From ce0c4da86a189f8668fca6ca5c572cc808b6f2db Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 24 Nov 2024 00:42:52 +0100 Subject: [PATCH] Add terms of service --- .../terms_of_service/drafts_controller.rb | 36 +++++++ .../terms_of_service/generates_controller.rb | 37 +++++++ .../terms_of_service/histories_controller.rb | 8 ++ .../terms_of_service/previews_controller.rb | 29 ++++++ .../admin/terms_of_service_controller.rb | 8 ++ .../instances/terms_of_services_controller.rb | 16 +++ app/helpers/formatting_helper.rb | 4 + app/javascript/mastodon/api/instance.ts | 11 +++ app/javascript/mastodon/api_types/instance.ts | 9 ++ .../mastodon/features/about/index.jsx | 2 +- .../features/getting_started/index.jsx | 2 +- .../features/privacy_policy/index.jsx | 65 ------------ .../features/privacy_policy/index.tsx | 90 +++++++++++++++++ .../features/terms_of_service/index.tsx | 90 +++++++++++++++++ .../features/ui/components/compose_panel.jsx | 3 +- .../features/ui/components/link_footer.jsx | 95 ------------------ .../features/ui/components/link_footer.tsx | 95 ++++++++++++++++++ app/javascript/mastodon/features/ui/index.jsx | 2 + .../features/ui/util/async-components.js | 4 + app/javascript/mastodon/initial_state.js | 2 +- app/javascript/mastodon/locales/en.json | 3 +- app/javascript/styles/mailer.scss | 4 +- app/javascript/styles/mastodon/admin.scss | 77 +++++++++++++++ app/mailers/user_mailer.rb | 10 ++ app/models/admin/action_log_filter.rb | 1 + app/models/terms_of_service.rb | 34 +++++++ app/models/terms_of_service/generator.rb | 25 +++++ app/policies/terms_of_service_policy.rb | 23 +++++ .../admin/terms_of_service/_links.html.haml | 6 ++ .../terms_of_service/drafts/show.html.haml | 19 ++++ .../terms_of_service/generates/show.html.haml | 41 ++++++++ .../terms_of_service/histories/show.html.haml | 16 +++ .../admin/terms_of_service/index.html.haml | 39 ++++++++ .../terms_of_service/previews/show.html.haml | 20 ++++ app/views/auth/registrations/new.html.haml | 2 +- .../terms_of_service_changed.html.haml | 17 ++++ .../terms_of_service_changed.text.erb | 14 +++ ...te_terms_of_service_notification_worker.rb | 15 +++ config/locales/an.yml | 1 - config/locales/ar.yml | 1 - config/locales/ast.yml | 1 - config/locales/be.yml | 1 - config/locales/bg.yml | 1 - config/locales/ca.yml | 1 - config/locales/cs.yml | 1 - config/locales/cy.yml | 1 - config/locales/da.yml | 1 - config/locales/de.yml | 1 - config/locales/el.yml | 1 - config/locales/en-GB.yml | 1 - config/locales/en.yml | 42 +++++++- config/locales/eo.yml | 1 - config/locales/es-AR.yml | 1 - config/locales/es-MX.yml | 1 - config/locales/es.yml | 1 - config/locales/et.yml | 1 - config/locales/eu.yml | 1 - config/locales/fa.yml | 1 - config/locales/fi.yml | 1 - config/locales/fo.yml | 1 - config/locales/fr-CA.yml | 1 - config/locales/fr.yml | 1 - config/locales/fy.yml | 1 - config/locales/ga.yml | 1 - config/locales/gd.yml | 1 - config/locales/gl.yml | 1 - config/locales/he.yml | 1 - config/locales/hu.yml | 1 - config/locales/hy.yml | 1 - config/locales/ia.yml | 1 - config/locales/id.yml | 1 - config/locales/ie.yml | 1 - config/locales/io.yml | 1 - config/locales/is.yml | 1 - config/locales/it.yml | 1 - config/locales/ja.yml | 1 - config/locales/kab.yml | 1 - config/locales/ko.yml | 1 - config/locales/ku.yml | 1 - config/locales/lad.yml | 1 - config/locales/lt.yml | 1 - config/locales/lv.yml | 1 - config/locales/ms.yml | 1 - config/locales/my.yml | 1 - config/locales/nl.yml | 1 - config/locales/nn.yml | 1 - config/locales/no.yml | 1 - config/locales/oc.yml | 1 - config/locales/pl.yml | 1 - config/locales/pt-BR.yml | 1 - config/locales/pt-PT.yml | 1 - config/locales/ru.yml | 1 - config/locales/sco.yml | 1 - config/locales/simple_form.en.yml | 14 +++ config/locales/sl.yml | 1 - config/locales/sq.yml | 1 - config/locales/sr-Latn.yml | 1 - config/locales/sr.yml | 1 - config/locales/sv.yml | 1 - config/locales/th.yml | 1 - config/locales/tr.yml | 1 - config/locales/uk.yml | 1 - config/locales/vi.yml | 1 - config/locales/zh-CN.yml | 1 - config/locales/zh-HK.yml | 1 - config/locales/zh-TW.yml | 1 - config/navigation.rb | 1 + config/routes.rb | 5 +- config/routes/admin.rb | 13 +++ config/routes/api.rb | 1 + config/templates/terms-of-service.md | 99 +++++++++++++++++++ ...20241123224956_create_terms_of_services.rb | 14 +++ db/schema.rb | 49 ++++++--- .../terms_of_service_fabricator.rb | 7 ++ spec/mailers/previews/user_mailer_preview.rb | 5 + spec/models/terms_of_service_spec.rb | 7 ++ 116 files changed, 1045 insertions(+), 252 deletions(-) create mode 100644 app/controllers/admin/terms_of_service/drafts_controller.rb create mode 100644 app/controllers/admin/terms_of_service/generates_controller.rb create mode 100644 app/controllers/admin/terms_of_service/histories_controller.rb create mode 100644 app/controllers/admin/terms_of_service/previews_controller.rb create mode 100644 app/controllers/admin/terms_of_service_controller.rb create mode 100644 app/controllers/api/v1/instances/terms_of_services_controller.rb create mode 100644 app/javascript/mastodon/api/instance.ts create mode 100644 app/javascript/mastodon/api_types/instance.ts delete mode 100644 app/javascript/mastodon/features/privacy_policy/index.jsx create mode 100644 app/javascript/mastodon/features/privacy_policy/index.tsx create mode 100644 app/javascript/mastodon/features/terms_of_service/index.tsx delete mode 100644 app/javascript/mastodon/features/ui/components/link_footer.jsx create mode 100644 app/javascript/mastodon/features/ui/components/link_footer.tsx create mode 100644 app/models/terms_of_service.rb create mode 100644 app/models/terms_of_service/generator.rb create mode 100644 app/policies/terms_of_service_policy.rb create mode 100644 app/views/admin/terms_of_service/_links.html.haml create mode 100644 app/views/admin/terms_of_service/drafts/show.html.haml create mode 100644 app/views/admin/terms_of_service/generates/show.html.haml create mode 100644 app/views/admin/terms_of_service/histories/show.html.haml create mode 100644 app/views/admin/terms_of_service/index.html.haml create mode 100644 app/views/admin/terms_of_service/previews/show.html.haml create mode 100644 app/views/user_mailer/terms_of_service_changed.html.haml create mode 100644 app/views/user_mailer/terms_of_service_changed.text.erb create mode 100644 app/workers/admin/distribute_terms_of_service_notification_worker.rb create mode 100644 config/templates/terms-of-service.md create mode 100644 db/migrate/20241123224956_create_terms_of_services.rb create mode 100644 spec/fabricators/terms_of_service_fabricator.rb create mode 100644 spec/models/terms_of_service_spec.rb diff --git a/app/controllers/admin/terms_of_service/drafts_controller.rb b/app/controllers/admin/terms_of_service/drafts_controller.rb new file mode 100644 index 0000000000..5d32c0bd83 --- /dev/null +++ b/app/controllers/admin/terms_of_service/drafts_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class Admin::TermsOfService::DraftsController < Admin::BaseController + before_action :set_terms_of_service + + def show + authorize :terms_of_service, :create? + end + + def update + authorize @terms_of_service, :update? + + @terms_of_service.published_at = Time.now.utc if params[:action_type] == 'publish' + + if @terms_of_service.update(resource_params) + log_action(:publish, @terms_of_service) if @terms_of_service.published? + redirect_to @terms_of_service.published? ? admin_terms_of_service_index_path : admin_terms_of_service_draft_path + else + render :show + end + end + + private + + def set_terms_of_service + @terms_of_service = TermsOfService.draft.first || TermsOfService.new(text: current_terms_of_service&.text) + end + + def current_terms_of_service + TermsOfService.live.first + end + + def resource_params + params.require(:terms_of_service).permit(:text, :changelog) + end +end diff --git a/app/controllers/admin/terms_of_service/generates_controller.rb b/app/controllers/admin/terms_of_service/generates_controller.rb new file mode 100644 index 0000000000..28037674a3 --- /dev/null +++ b/app/controllers/admin/terms_of_service/generates_controller.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class Admin::TermsOfService::GeneratesController < Admin::BaseController + before_action :set_instance_presenter + + def show + authorize :terms_of_service, :create? + + @generator = TermsOfService::Generator.new( + domain: @instance_presenter.domain, + admin_email: @instance_presenter.contact.email + ) + end + + def create + authorize :terms_of_service, :create? + + @generator = TermsOfService::Generator.new(resource_params) + + if @generator.valid? + TermsOfService.create!(text: @generator.render) + redirect_to admin_terms_of_service_draft_path + else + render :show + end + end + + private + + def set_instance_presenter + @instance_presenter = InstancePresenter.new + end + + def resource_params + params.require(:terms_of_service_generator).permit(*TermsOfService::Generator::VARIABLES) + end +end diff --git a/app/controllers/admin/terms_of_service/histories_controller.rb b/app/controllers/admin/terms_of_service/histories_controller.rb new file mode 100644 index 0000000000..8f12341aea --- /dev/null +++ b/app/controllers/admin/terms_of_service/histories_controller.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class Admin::TermsOfService::HistoriesController < Admin::BaseController + def show + authorize :terms_of_service, :index? + @terms_of_service = TermsOfService.published.all + end +end diff --git a/app/controllers/admin/terms_of_service/previews_controller.rb b/app/controllers/admin/terms_of_service/previews_controller.rb new file mode 100644 index 0000000000..48f42b87b1 --- /dev/null +++ b/app/controllers/admin/terms_of_service/previews_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class Admin::TermsOfService::PreviewsController < Admin::BaseController + before_action :set_terms_of_service + + def show + authorize @terms_of_service, :distribute? + @user_count = @terms_of_service.scope_for_notification.count + end + + def test + authorize @terms_of_service, :distribute? + UserMailer.terms_of_service_changed(current_user, @terms_of_service).deliver_later! + redirect_to admin_terms_of_service_preview_path(@terms_of_service) + end + + def distribute + authorize @terms_of_service, :distribute? + @terms_of_service.touch(:notification_sent_at) + Admin::DistributeTermsOfServiceNotificationWorker.perform_async(@terms_of_service.id) + redirect_to admin_terms_of_service_index_path + end + + private + + def set_terms_of_service + @terms_of_service = TermsOfService.find(params[:terms_of_service_id]) + end +end diff --git a/app/controllers/admin/terms_of_service_controller.rb b/app/controllers/admin/terms_of_service_controller.rb new file mode 100644 index 0000000000..f70bfd2071 --- /dev/null +++ b/app/controllers/admin/terms_of_service_controller.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class Admin::TermsOfServiceController < Admin::BaseController + def index + authorize :terms_of_service, :index? + @terms_of_service = TermsOfService.live.first + end +end diff --git a/app/controllers/api/v1/instances/terms_of_services_controller.rb b/app/controllers/api/v1/instances/terms_of_services_controller.rb new file mode 100644 index 0000000000..e9e8e8ef55 --- /dev/null +++ b/app/controllers/api/v1/instances/terms_of_services_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class Api::V1::Instances::TermsOfServicesController < Api::V1::Instances::BaseController + before_action :set_terms_of_service + + def show + cache_even_if_authenticated! + render json: @terms_of_service, serializer: REST::PrivacyPolicySerializer + end + + private + + def set_terms_of_service + @terms_of_service = TermsOfService.live.first! + end +end diff --git a/app/helpers/formatting_helper.rb b/app/helpers/formatting_helper.rb index 9d5a2e2478..e827834975 100644 --- a/app/helpers/formatting_helper.rb +++ b/app/helpers/formatting_helper.rb @@ -64,6 +64,10 @@ module FormattingHelper end end + def markdown(text) + Redcarpet::Markdown.new(Redcarpet::Render::HTML, escape_html: true, no_images: true).render(text).html_safe # rubocop:disable Rails/OutputSafety + end + private def wrapped_status_content_format(status) diff --git a/app/javascript/mastodon/api/instance.ts b/app/javascript/mastodon/api/instance.ts new file mode 100644 index 0000000000..ec9146fb34 --- /dev/null +++ b/app/javascript/mastodon/api/instance.ts @@ -0,0 +1,11 @@ +import { apiRequestGet } from 'mastodon/api'; +import type { + ApiTermsOfServiceJSON, + ApiPrivacyPolicyJSON, +} from 'mastodon/api_types/instance'; + +export const apiGetTermsOfService = () => + apiRequestGet('v1/instance/terms_of_service'); + +export const apiGetPrivacyPolicy = () => + apiRequestGet('v1/instance/privacy_policy'); diff --git a/app/javascript/mastodon/api_types/instance.ts b/app/javascript/mastodon/api_types/instance.ts new file mode 100644 index 0000000000..ead9774515 --- /dev/null +++ b/app/javascript/mastodon/api_types/instance.ts @@ -0,0 +1,9 @@ +export interface ApiTermsOfServiceJSON { + updated_at: string; + content: string; +} + +export interface ApiPrivacyPolicyJSON { + updated_at: string; + content: string; +} diff --git a/app/javascript/mastodon/features/about/index.jsx b/app/javascript/mastodon/features/about/index.jsx index 65a36520d6..e7d357f4c6 100644 --- a/app/javascript/mastodon/features/about/index.jsx +++ b/app/javascript/mastodon/features/about/index.jsx @@ -18,7 +18,7 @@ import { Icon } from 'mastodon/components/icon'; import { ServerHeroImage } from 'mastodon/components/server_hero_image'; import { Skeleton } from 'mastodon/components/skeleton'; import Account from 'mastodon/containers/account_container'; -import LinkFooter from 'mastodon/features/ui/components/link_footer'; +import { LinkFooter} from 'mastodon/features/ui/components/link_footer'; const messages = defineMessages({ title: { id: 'column.about', defaultMessage: 'About' }, diff --git a/app/javascript/mastodon/features/getting_started/index.jsx b/app/javascript/mastodon/features/getting_started/index.jsx index 8d26115dfa..ece06953ea 100644 --- a/app/javascript/mastodon/features/getting_started/index.jsx +++ b/app/javascript/mastodon/features/getting_started/index.jsx @@ -25,7 +25,7 @@ import StarIcon from '@/material-icons/400-24px/star.svg?react'; import { fetchFollowRequests } from 'mastodon/actions/accounts'; import Column from 'mastodon/components/column'; import ColumnHeader from 'mastodon/components/column_header'; -import LinkFooter from 'mastodon/features/ui/components/link_footer'; +import { LinkFooter } from 'mastodon/features/ui/components/link_footer'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; import { canManageReports, canViewAdminDashboard } from 'mastodon/permissions'; diff --git a/app/javascript/mastodon/features/privacy_policy/index.jsx b/app/javascript/mastodon/features/privacy_policy/index.jsx deleted file mode 100644 index d420546e4f..0000000000 --- a/app/javascript/mastodon/features/privacy_policy/index.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; - -import { FormattedMessage, FormattedDate, injectIntl, defineMessages } from 'react-intl'; - -import { Helmet } from 'react-helmet'; - -import api from 'mastodon/api'; -import Column from 'mastodon/components/column'; -import { Skeleton } from 'mastodon/components/skeleton'; - -const messages = defineMessages({ - title: { id: 'privacy_policy.title', defaultMessage: 'Privacy Policy' }, -}); - -class PrivacyPolicy extends PureComponent { - - static propTypes = { - intl: PropTypes.object, - multiColumn: PropTypes.bool, - }; - - state = { - content: null, - lastUpdated: null, - isLoading: true, - }; - - componentDidMount () { - api().get('/api/v1/instance/privacy_policy').then(({ data }) => { - this.setState({ content: data.content, lastUpdated: data.updated_at, isLoading: false }); - }).catch(() => { - this.setState({ isLoading: false }); - }); - } - - render () { - const { intl, multiColumn } = this.props; - const { isLoading, content, lastUpdated } = this.state; - - return ( - -
-
-

-

: }} />

-
- -
-
- - - {intl.formatMessage(messages.title)} - - - - ); - } - -} - -export default injectIntl(PrivacyPolicy); diff --git a/app/javascript/mastodon/features/privacy_policy/index.tsx b/app/javascript/mastodon/features/privacy_policy/index.tsx new file mode 100644 index 0000000000..79314347ec --- /dev/null +++ b/app/javascript/mastodon/features/privacy_policy/index.tsx @@ -0,0 +1,90 @@ +import { useState, useEffect } from 'react'; + +import { + FormattedMessage, + FormattedDate, + useIntl, + defineMessages, +} from 'react-intl'; + +import { Helmet } from 'react-helmet'; + +import { apiGetPrivacyPolicy } from 'mastodon/api/instance'; +import type { ApiPrivacyPolicyJSON } from 'mastodon/api_types/instance'; +import Column from 'mastodon/components/column'; +import { Skeleton } from 'mastodon/components/skeleton'; + +const messages = defineMessages({ + title: { id: 'privacy_policy.title', defaultMessage: 'Privacy Policy' }, +}); + +const PrivacyPolicy: React.FC<{ + multiColumn: boolean; +}> = ({ multiColumn }) => { + const intl = useIntl(); + const [response, setResponse] = useState(); + const [loading, setLoading] = useState(true); + + useEffect(() => { + apiGetPrivacyPolicy() + .then((data) => { + setResponse(data); + setLoading(false); + return ''; + }) + .catch(() => { + setLoading(false); + }); + }, []); + + return ( + +
+
+

+ +

+

+ + ) : ( + + ), + }} + /> +

+
+ + {response && ( +
+ )} +
+ + + {intl.formatMessage(messages.title)} + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default PrivacyPolicy; diff --git a/app/javascript/mastodon/features/terms_of_service/index.tsx b/app/javascript/mastodon/features/terms_of_service/index.tsx new file mode 100644 index 0000000000..ebdd4864b0 --- /dev/null +++ b/app/javascript/mastodon/features/terms_of_service/index.tsx @@ -0,0 +1,90 @@ +import { useState, useEffect } from 'react'; + +import { + FormattedMessage, + FormattedDate, + useIntl, + defineMessages, +} from 'react-intl'; + +import { Helmet } from 'react-helmet'; + +import { apiGetTermsOfService } from 'mastodon/api/instance'; +import type { ApiTermsOfServiceJSON } from 'mastodon/api_types/instance'; +import Column from 'mastodon/components/column'; +import { Skeleton } from 'mastodon/components/skeleton'; + +const messages = defineMessages({ + title: { id: 'terms_of_service.title', defaultMessage: 'Terms of Service' }, +}); + +const TermsOfService: React.FC<{ + multiColumn: boolean; +}> = ({ multiColumn }) => { + const intl = useIntl(); + const [response, setResponse] = useState(); + const [loading, setLoading] = useState(true); + + useEffect(() => { + apiGetTermsOfService() + .then((data) => { + setResponse(data); + setLoading(false); + return ''; + }) + .catch(() => { + setLoading(false); + }); + }, []); + + return ( + +
+
+

+ +

+

+ + ) : ( + + ), + }} + /> +

+
+ + {response && ( +
+ )} +
+ + + {intl.formatMessage(messages.title)} + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default TermsOfService; diff --git a/app/javascript/mastodon/features/ui/components/compose_panel.jsx b/app/javascript/mastodon/features/ui/components/compose_panel.jsx index 18321cbe63..b085b2dc2a 100644 --- a/app/javascript/mastodon/features/ui/components/compose_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/compose_panel.jsx @@ -7,10 +7,9 @@ import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/ import ServerBanner from 'mastodon/components/server_banner'; import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container'; import SearchContainer from 'mastodon/features/compose/containers/search_container'; +import { LinkFooter } from 'mastodon/features/ui/components/link_footer'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; -import LinkFooter from './link_footer'; - class ComposePanel extends PureComponent { static propTypes = { identity: identityContextPropShape, diff --git a/app/javascript/mastodon/features/ui/components/link_footer.jsx b/app/javascript/mastodon/features/ui/components/link_footer.jsx deleted file mode 100644 index 49b21c2e48..0000000000 --- a/app/javascript/mastodon/features/ui/components/link_footer.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; - -import { FormattedMessage, injectIntl } from 'react-intl'; - -import { Link } from 'react-router-dom'; - -import { connect } from 'react-redux'; - -import { openModal } from 'mastodon/actions/modal'; -import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; -import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state'; -import { PERMISSION_INVITE_USERS } from 'mastodon/permissions'; - -const mapDispatchToProps = (dispatch) => ({ - onLogout () { - dispatch(openModal({ modalType: 'CONFIRM_LOG_OUT' })); - - }, -}); - -class LinkFooter extends PureComponent { - static propTypes = { - identity: identityContextPropShape, - multiColumn: PropTypes.bool, - onLogout: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - }; - - handleLogoutClick = e => { - e.preventDefault(); - e.stopPropagation(); - - this.props.onLogout(); - - return false; - }; - - render () { - const { signedIn, permissions } = this.props.identity; - const { multiColumn } = this.props; - - const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS); - const canProfileDirectory = profileDirectory; - - const DividingCircle = {' · '}; - - return ( -
-

- {domain}: - {' '} - - {statusPageUrl && ( - <> - {DividingCircle} - - - )} - {canInvite && ( - <> - {DividingCircle} - - - )} - {canProfileDirectory && ( - <> - {DividingCircle} - - - )} - {DividingCircle} - -

- -

- Mastodon: - {' '} - - {DividingCircle} - - {DividingCircle} - - {DividingCircle} - - {DividingCircle} - v{version} -

-
- ); - } - -} - -export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter))); diff --git a/app/javascript/mastodon/features/ui/components/link_footer.tsx b/app/javascript/mastodon/features/ui/components/link_footer.tsx new file mode 100644 index 0000000000..28b361a7e4 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/link_footer.tsx @@ -0,0 +1,95 @@ +import { FormattedMessage } from 'react-intl'; + +import { Link } from 'react-router-dom'; + +import { + domain, + version, + source_url, + statusPageUrl, + profile_directory as canProfileDirectory, +} from 'mastodon/initial_state'; + +const DividingCircle: React.FC = () => {' · '}; + +export const LinkFooter: React.FC<{ + multiColumn: boolean; +}> = ({ multiColumn }) => { + return ( +
+

+ {domain}:{' '} + + + + {statusPageUrl && ( + <> + + + + + + )} + {canProfileDirectory && ( + <> + + + + + + )} + + + + + + + + +

+ +

+ Mastodon:{' '} + + + + + + + + + + + + + + + + + v{version} +

+
+ ); +}; diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index daa4585ead..f2e28932ee 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -70,6 +70,7 @@ import { Onboarding, About, PrivacyPolicy, + TermsOfService, } from './util/async-components'; import { ColumnsContextProvider } from './util/columns_context'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; @@ -197,6 +198,7 @@ class SwitchingColumnsArea extends PureComponent { + diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index 5a85c856d2..bff27bd0ec 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -202,6 +202,10 @@ export function PrivacyPolicy () { return import(/*webpackChunkName: "features/privacy_policy" */'../../privacy_policy'); } +export function TermsOfService () { + return import(/*webpackChunkName: "features/terms_of_service" */'../../terms_of_service'); +} + export function NotificationRequests () { return import(/*webpackChunkName: "features/notifications/requests" */'../../notifications/requests'); } diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js index 60b35cb31a..2610d001de 100644 --- a/app/javascript/mastodon/initial_state.js +++ b/app/javascript/mastodon/initial_state.js @@ -43,6 +43,7 @@ * @property {boolean=} use_pending_items * @property {string} version * @property {string} sso_redirect + * @property {string} status_page_url */ /** @@ -115,7 +116,6 @@ export const usePendingItems = getMeta('use_pending_items'); export const version = getMeta('version'); export const languages = initialState?.languages; export const criticalUpdatesPending = initialState?.critical_updates_pending; -// @ts-expect-error export const statusPageUrl = getMeta('status_page_url'); export const sso_redirect = getMeta('sso_redirect'); diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 142b7f1770..6c3473989d 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -355,11 +355,11 @@ "footer.about": "About", "footer.directory": "Profiles directory", "footer.get_app": "Get the app", - "footer.invite": "Invite people", "footer.keyboard_shortcuts": "Keyboard shortcuts", "footer.privacy_policy": "Privacy policy", "footer.source_code": "View source code", "footer.status": "Status", + "footer.terms_of_service": "Terms of service", "generic.saved": "Saved", "getting_started.heading": "Getting started", "hashtag.column_header.tag_mode.all": "and {additional}", @@ -875,6 +875,7 @@ "subscribed_languages.target": "Change subscribed languages for {target}", "tabs_bar.home": "Home", "tabs_bar.notifications": "Notifications", + "terms_of_service.title": "Terms of Service", "time_remaining.days": "{number, plural, one {# day} other {# days}} left", "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", diff --git a/app/javascript/styles/mailer.scss b/app/javascript/styles/mailer.scss index f46160889a..1f3310877a 100644 --- a/app/javascript/styles/mailer.scss +++ b/app/javascript/styles/mailer.scss @@ -173,7 +173,9 @@ table + p { } .email-prose { - p { + p, + ul, + ol { color: #17063b; font-size: 14px; line-height: 20px; diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index b5f8570ae2..611cb2884a 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -253,6 +253,10 @@ $content-width: 840px; .time-period { padding: 0 10px; } + + .back-link { + margin-bottom: 0; + } } h2 small { @@ -1940,3 +1944,76 @@ a.sparkline { } } } + +.admin { + &__terms-of-service { + &__container { + background: var(--surface-background-color); + border-radius: 8px; + border: 1px solid var(--background-border-color); + overflow: hidden; + + &__header { + padding: 16px; + font-size: 14px; + line-height: 20px; + color: $secondary-text-color; + display: flex; + align-items: center; + gap: 12px; + } + + &__body { + background: var(--background-color); + padding: 16px; + overflow-y: scroll; + height: 30vh; + } + } + + &__history { + & > li { + border-bottom: 1px solid var(--background-border-color); + + &:last-child { + border-bottom: 0; + } + } + + &__item { + padding: 16px 0; + padding-bottom: 8px; + + h5 { + font-size: 14px; + line-height: 20px; + font-weight: 600; + margin-bottom: 16px; + } + } + } + } +} + +.dot-indicator { + display: inline-flex; + align-items: center; + gap: 8px; + font-weight: 500; + + &__indicator { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: $dark-text-color; + } + + &.success { + color: $valid-value-color; + + .dot-indicator__indicator { + background-color: $valid-value-color; + } + } +} diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 5c9e5c96d9..b02c462217 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -209,6 +209,16 @@ class UserMailer < Devise::Mailer end end + def terms_of_service_changed(user, terms_of_service) + @resource = user + @terms_of_service = terms_of_service + @markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, escape_html: true, no_images: true) + + I18n.with_locale(locale) do + mail subject: default_i18n_subject + end + end + private def default_devise_subject diff --git a/app/models/admin/action_log_filter.rb b/app/models/admin/action_log_filter.rb index dfb7fd00ed..fd6b4289ce 100644 --- a/app/models/admin/action_log_filter.rb +++ b/app/models/admin/action_log_filter.rb @@ -57,6 +57,7 @@ class Admin::ActionLogFilter enable_relay: { target_type: 'Relay', action: 'enable' }.freeze, memorialize_account: { target_type: 'Account', action: 'memorialize' }.freeze, promote_user: { target_type: 'User', action: 'promote' }.freeze, + publish_terms_of_service: { target_type: 'TermsOfService', action: 'publish' }.freeze, remove_avatar_user: { target_type: 'User', action: 'remove_avatar' }.freeze, reopen_report: { target_type: 'Report', action: 'reopen' }.freeze, resend_user: { target_type: 'User', action: 'resend' }.freeze, diff --git a/app/models/terms_of_service.rb b/app/models/terms_of_service.rb new file mode 100644 index 0000000000..2db47111f2 --- /dev/null +++ b/app/models/terms_of_service.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: terms_of_services +# +# id :bigint(8) not null, primary key +# changelog :text default(""), not null +# notification_sent_at :datetime +# published_at :datetime +# text :text default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# +class TermsOfService < ApplicationRecord + scope :published, -> { where.not(published_at: nil).order(published_at: :desc) } + scope :live, -> { published.limit(1) } + scope :draft, -> { where(published_at: nil).order(id: :desc).limit(1) } + + validates :text, presence: true + validates :changelog, presence: true, if: -> { published? } + + def published? + published_at.present? + end + + def notified? + notification_sent_at.present? + end + + def scope_for_notification + User.confirmed.joins(:account).merge(Account.without_suspended).where(created_at: (..published_at)) + end +end diff --git a/app/models/terms_of_service/generator.rb b/app/models/terms_of_service/generator.rb new file mode 100644 index 0000000000..dd9f359ed5 --- /dev/null +++ b/app/models/terms_of_service/generator.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class TermsOfService::Generator + include ActiveModel::Model + + TEMPLATE = Rails.root.join('config', 'templates', 'terms-of-service.md').read + + VARIABLES = %i( + domain + admin_email + dmca_email + dmca_address + arbitration_address + arbitration_website + jurisdiction + ).freeze + + attr_accessor(*VARIABLES) + + validates(*VARIABLES, presence: true) + + def render + format(TEMPLATE, VARIABLES.index_with { |key| public_send(key) }) + end +end diff --git a/app/policies/terms_of_service_policy.rb b/app/policies/terms_of_service_policy.rb new file mode 100644 index 0000000000..0cbdb6a2e1 --- /dev/null +++ b/app/policies/terms_of_service_policy.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class TermsOfServicePolicy < ApplicationPolicy + def index? + role.can?(:manage_settings) + end + + def create? + role.can?(:manage_settings) + end + + def distribute? + record.published? && !record.notified? && role.can?(:manage_settings) + end + + def update? + !record.published? && role.can?(:manage_settings) + end + + def destroy? + !record.published? && role.can?(:manage_settings) + end +end diff --git a/app/views/admin/terms_of_service/_links.html.haml b/app/views/admin/terms_of_service/_links.html.haml new file mode 100644 index 0000000000..aaf0f2c7b7 --- /dev/null +++ b/app/views/admin/terms_of_service/_links.html.haml @@ -0,0 +1,6 @@ +.content__heading__tabs + = render_navigation renderer: :links do |primary| + :ruby + primary.item :current, safe_join([material_symbol('description'), t('admin.terms_of_service.current')]), admin_terms_of_service_index_path + primary.item :draft, safe_join([material_symbol('description'), t('admin.terms_of_service.draft')]), admin_terms_of_service_draft_path + primary.item :previous, safe_join([material_symbol('history'), t('admin.terms_of_service.history')]), admin_terms_of_service_history_path diff --git a/app/views/admin/terms_of_service/drafts/show.html.haml b/app/views/admin/terms_of_service/drafts/show.html.haml new file mode 100644 index 0000000000..7a9a6fd3c4 --- /dev/null +++ b/app/views/admin/terms_of_service/drafts/show.html.haml @@ -0,0 +1,19 @@ +- content_for :page_title do + = t('admin.terms_of_service.title') + +- content_for :heading do + %h2= t('admin.terms_of_service.title') + = render partial: 'admin/terms_of_service/links' + += simple_form_for @terms_of_service, url: admin_terms_of_service_draft_path, method: :put do |form| + = render 'shared/error_messages', object: @terms_of_service + + .fields-group + = form.input :text, wrapper: :with_block_label, input_html: { rows: 8 } + + .fields-group + = form.input :changelog, wrapper: :with_block_label, input_html: { rows: 8 } + + .actions + = form.button :button, t('admin.terms_of_service.save_draft'), type: :submit, name: :action_type, value: :save_draft, class: 'button button-secondary' + = form.button :button, t('admin.terms_of_service.publish'), type: :submit, name: :action_type, value: :publish diff --git a/app/views/admin/terms_of_service/generates/show.html.haml b/app/views/admin/terms_of_service/generates/show.html.haml new file mode 100644 index 0000000000..c1ddd1a556 --- /dev/null +++ b/app/views/admin/terms_of_service/generates/show.html.haml @@ -0,0 +1,41 @@ +- content_for :page_title do + = t('admin.terms_of_service.generates.title') + +- content_for :heading_actions do + .back-link + = link_to admin_terms_of_service_index_path do + = material_symbol 'chevron_left' + = t('admin.terms_of_service.back') + +%p.lead= t('admin.terms_of_service.generates.explanation_html') + +%hr.spacer/ + += simple_form_for @generator, url: admin_terms_of_service_generate_path, method: :post do |form| + = render 'shared/error_messages', object: @generator + + .fields-group + = form.input :domain, wrapper: :with_label + + .fields-group + = form.input :admin_email, wrapper: :with_label + + .fields-group + = form.input :jurisdiction, wrapper: :with_label + + .fields-group + = form.input :dmca_address, wrapper: :with_label + + .fields-group + = form.input :dmca_email, wrapper: :with_label + + .fields-group + = form.input :arbitration_address, wrapper: :with_label + + .fields-group + = form.input :arbitration_website, wrapper: :with_label + + .actions + = form.button :button, t('admin.terms_of_service.generates.action'), type: :submit + + %p.hint.subtle-hint= t('admin.terms_of_service.generates.chance_to_review') diff --git a/app/views/admin/terms_of_service/histories/show.html.haml b/app/views/admin/terms_of_service/histories/show.html.haml new file mode 100644 index 0000000000..8b7b8eb82e --- /dev/null +++ b/app/views/admin/terms_of_service/histories/show.html.haml @@ -0,0 +1,16 @@ +- content_for :page_title do + = t('admin.terms_of_service.history') + +- content_for :heading do + %h2= t('admin.terms_of_service.title') + = render partial: 'admin/terms_of_service/links' + +- if @terms_of_service.empty? + %p= t('admin.terms_of_service.no_history') +- else + %ol.admin__terms-of-service__history + - @terms_of_service.each do |terms_of_service| + %li + .admin__terms-of-service__history__item + %h5= l(terms_of_service.published_at) + .prose= markdown(terms_of_service.changelog) diff --git a/app/views/admin/terms_of_service/index.html.haml b/app/views/admin/terms_of_service/index.html.haml new file mode 100644 index 0000000000..69b4e4221e --- /dev/null +++ b/app/views/admin/terms_of_service/index.html.haml @@ -0,0 +1,39 @@ +- content_for :page_title do + = t('admin.terms_of_service.title') + +- content_for :heading do + %h2= t('admin.terms_of_service.title') + = render partial: 'links' + +- if @terms_of_service.present? + .admin__terms-of-service__container + .admin__terms-of-service__container__header + .dot-indicator.success + .dot-indicator__indicator + %span= t('admin.terms_of_service.live') + · + %span + = t('admin.terms_of_service.published_on_html', date: content_tag(:time, l(@terms_of_service.published_at.to_date), class: 'formatted', date: @terms_of_service.published_at.to_date.iso8601)) + · + - if @terms_of_service.notified? + %span + = t('admin.terms_of_service.notified_on_html', date: content_tag(:time, l(@terms_of_service.notification_sent_at.to_date), class: 'formatted', date: @terms_of_service.notification_sent_at.to_date.iso8601)) + - else + = link_to t('admin.terms_of_service.notify_users'), admin_terms_of_service_preview_path(@terms_of_service), class: 'link-button' + + .admin__terms-of-service__container__body + .prose + = markdown(@terms_of_service.text) + + %hr.spacer/ + + %h3= t('admin.terms_of_service.changelog') + + .prose + = markdown(@terms_of_service.changelog) +- else + %p.lead= t('admin.terms_of_service.no_terms_of_service_html') + + .content__heading__actions + = link_to t('admin.terms_of_service.create'), admin_terms_of_service_draft_path, class: 'button' + = link_to t('admin.terms_of_service.generate'), admin_terms_of_service_generate_path, class: 'button button-secondary' diff --git a/app/views/admin/terms_of_service/previews/show.html.haml b/app/views/admin/terms_of_service/previews/show.html.haml new file mode 100644 index 0000000000..9f295393fe --- /dev/null +++ b/app/views/admin/terms_of_service/previews/show.html.haml @@ -0,0 +1,20 @@ +- content_for :page_title do + = t('admin.terms_of_service.preview.title') + +- content_for :heading_actions do + .back-link + = link_to admin_terms_of_service_index_path do + = material_symbol 'chevron_left' + = t('admin.terms_of_service.back') + +%p.lead + = t('admin.terms_of_service.preview.explanation_html', count: @user_count, display_count: number_with_delimiter(@user_count), date: l(@terms_of_service.published_at.to_date)) + +.prose + = markdown(@terms_of_service.changelog) + +%hr.spacer/ + +.content__heading__actions + = link_to t('admin.terms_of_service.preview.send_preview', email: current_user.email), test_admin_terms_of_service_preview_path(@terms_of_service), method: :post, class: 'button button-secondary' + = link_to t('admin.terms_of_service.preview.send_to_all', count: @user_count, display_count: number_with_delimiter(@user_count)), distribute_admin_terms_of_service_preview_path(@terms_of_service), method: :post, class: 'button', data: { confirm: t('admin.reports.are_you_sure') } diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index d58f1ccf4d..d3671b4977 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -71,7 +71,7 @@ .fields-group = f.input :agreement, as: :boolean, - label: t('auth.privacy_policy_agreement_html', rules_path: about_more_path, privacy_policy_path: privacy_policy_path), + label: t('auth.user_agreement_html', privacy_policy_path: privacy_policy_path, terms_of_service_path: terms_of_service_path), required: true, wrapper: :with_label diff --git a/app/views/user_mailer/terms_of_service_changed.html.haml b/app/views/user_mailer/terms_of_service_changed.html.haml new file mode 100644 index 0000000000..95cc976418 --- /dev/null +++ b/app/views/user_mailer/terms_of_service_changed.html.haml @@ -0,0 +1,17 @@ += content_for :heading do + = render 'application/mailer/heading', + image_url: frontend_asset_url('images/mailer-new/heading/user.png'), + subtitle: t('user_mailer.terms_of_service_changed.subtitle', domain: site_hostname), + title: t('user_mailer.terms_of_service_changed.title') +%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-body-padding-td + %table.email-inner-card-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-inner-card-td.email-prose + %p= t('user_mailer.terms_of_service_changed.description_html', path: terms_of_service_url, domain: site_hostname) + %p + %strong= t('user_mailer.terms_of_service_changed.changelog') + = markdown(@terms_of_service.changelog) + %p= t('user_mailer.terms_of_service_changed.agreement', domain: site_hostname) + %p= t('user_mailer.terms_of_service_changed.sign_off', domain: site_hostname) diff --git a/app/views/user_mailer/terms_of_service_changed.text.erb b/app/views/user_mailer/terms_of_service_changed.text.erb new file mode 100644 index 0000000000..8416572f0a --- /dev/null +++ b/app/views/user_mailer/terms_of_service_changed.text.erb @@ -0,0 +1,14 @@ +<%= t('user_mailer.terms_of_service_changed.title') %> + +=== + +<%= t('user_mailer.terms_of_service_changed.description', domain: site_hostname) %> + +=> <%= terms_of_service_url %> + +<%= t('user_mailer.terms_of_service_changed.changelog') %> + +<%= @terms_of_service.changelog %> +<%= t('user_mailer.terms_of_service_changed.agreement', domain: site_hostname) %> + +<%= t('user_mailer.terms_of_service_changed.sign_off', domain: site_hostname) %> diff --git a/app/workers/admin/distribute_terms_of_service_notification_worker.rb b/app/workers/admin/distribute_terms_of_service_notification_worker.rb new file mode 100644 index 0000000000..7370ee87e8 --- /dev/null +++ b/app/workers/admin/distribute_terms_of_service_notification_worker.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class Admin::DistributeTermsOfServiceNotificationWorker + include Sidekiq::Worker + + def perform(terms_of_service_id) + terms_of_service = TermsOfService.find(terms_of_service_id) + + terms_of_service.scope_for_notification.find_each do |user| + UserMailer.terms_of_service_changed(user, terms_of_service).deliver_later! + end + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/config/locales/an.yml b/config/locales/an.yml index f941651438..012d37bd95 100644 --- a/config/locales/an.yml +++ b/config/locales/an.yml @@ -919,7 +919,6 @@ an: migrate_account: Mudar-se a unatra cuenta migrate_account_html: Si deseyas reendrezar esta cuenta a unatra distinta, puetz configurar-lo aquí. or_log_in_with: U inicia sesión con - privacy_policy_agreement_html: He leyiu y accepto la politica de privacidat providers: cas: CAS saml: SAML diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 832f60795f..fd275e6684 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -1118,7 +1118,6 @@ ar: migrate_account: الانتقال إلى حساب مختلف migrate_account_html: إن كنت ترغب في تحويل هذا الحساب نحو حساب آخَر، يُمكِنُك إعداده هنا. or_log_in_with: أو قم بتسجيل الدخول بواسطة - privacy_policy_agreement_html: لقد قرأتُ وأوافق على سياسة الخصوصية progress: confirm: تأكيد عنوان البريد الإلكتروني details: تفاصيلك diff --git a/config/locales/ast.yml b/config/locales/ast.yml index a3310f0d46..d7dc3701a1 100644 --- a/config/locales/ast.yml +++ b/config/locales/ast.yml @@ -459,7 +459,6 @@ ast: logout: Zarrar la sesión migrate_account: Cambéu de cuenta migrate_account_html: Si quies redirixir esta cuenta a otra diferente, pues configurar esta opción equí. - privacy_policy_agreement_html: Lleí y acepto la política de privacidá providers: cas: CAS saml: SAML diff --git a/config/locales/be.yml b/config/locales/be.yml index c97547b8b9..536af45277 100644 --- a/config/locales/be.yml +++ b/config/locales/be.yml @@ -1134,7 +1134,6 @@ be: migrate_account: Пераехаць на іншы ўліковы запіс migrate_account_html: Калі вы хочаце перанакіраваць гэты ўліковы запіс на іншы, то можаце наладзіць яго тут. or_log_in_with: Або ўвайсці з дапамогай - privacy_policy_agreement_html: Я азнаёміўся і пагаджаюся з палітыкай канфідэнцыйнасці progress: confirm: Пацвердзіць email details: Вашы дадзеныя diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 20ee1f6c68..c11a9ba2cc 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -1082,7 +1082,6 @@ bg: migrate_account: Преместване в различен акаунт migrate_account_html: Ако желаете да пренасочите този акаунт към друг, можете да настроите това тук. or_log_in_with: Или влизане с помощта на - privacy_policy_agreement_html: Прочетох и има съгласието ми за политиката за поверителност progress: details: Вашите подробности review: Нашият преглед diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 28caa1d1fc..09b0b7fdb0 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -1132,7 +1132,6 @@ ca: migrate_account: Mou a un compte diferent migrate_account_html: Si vols redirigir aquest compte a un altre diferent, el pots configurar aquí. or_log_in_with: O inicia sessió amb - privacy_policy_agreement_html: He llegit i estic d'acord amb la política de privacitat progress: confirm: Confirmar email details: Els teus detalls diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 19a6372224..e93169abac 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -1099,7 +1099,6 @@ cs: migrate_account: Přesunout se na jiný účet migrate_account_html: Zde můžete nastavit přesměrování tohoto účtu na jiný. or_log_in_with: Nebo se přihlaste pomocí - privacy_policy_agreement_html: Četl jsem a souhlasím se zásadami ochrany osobních údajů progress: details: Vaše údaje review: Naše hodnocení diff --git a/config/locales/cy.yml b/config/locales/cy.yml index 0daa793f97..3e7a5e5891 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -1197,7 +1197,6 @@ cy: migrate_account: Symud i gyfrif gwahanol migrate_account_html: Os hoffech chi ailgyfeirio'r cyfrif hwn at un gwahanol, mae modd ei ffurfweddu yma. or_log_in_with: Neu mewngofnodwch gyda - privacy_policy_agreement_html: Rwyf wedi darllen ac yn cytuno i'r polisi preifatrwydd progress: confirm: Cadarnhau'r e-bost details: Eich manylion diff --git a/config/locales/da.yml b/config/locales/da.yml index 3a82278b93..5dfe37a5f3 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1132,7 +1132,6 @@ da: migrate_account: Flyt til en anden konto migrate_account_html: Ønsker du at omdirigere denne konto til en anden, kan du opsætte dette hér. or_log_in_with: Eller log ind med - privacy_policy_agreement_html: Jeg accepterer privatlivspolitikken progress: confirm: Bekræft e-mail details: Dine detaljer diff --git a/config/locales/de.yml b/config/locales/de.yml index 26ccc8f3ee..3cc8db7823 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1132,7 +1132,6 @@ de: migrate_account: Zu einem anderen Konto umziehen migrate_account_html: Wenn du dieses Konto auf ein anderes weiterleiten möchtest, kannst du es hier konfigurieren. or_log_in_with: Oder anmelden mit - privacy_policy_agreement_html: Ich habe die Datenschutzerklärung gelesen und stimme ihr zu progress: confirm: E-Mail bestätigen details: Deine Daten diff --git a/config/locales/el.yml b/config/locales/el.yml index 98ef5f18b7..423a251c5c 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -1085,7 +1085,6 @@ el: migrate_account: Μεταφορά σε διαφορετικό λογαριασμό migrate_account_html: Αν θέλεις να ανακατευθύνεις αυτό τον λογαριασμό σε έναν διαφορετικό, μπορείς να το διαμορφώσεις εδώ. or_log_in_with: Ή συνδέσου με - privacy_policy_agreement_html: Έχω διαβάσει και συμφωνώ με την πολιτική απορρήτου progress: confirm: Επιβεβαίωση email details: Τα στοιχεία σας diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 76a16aa9db..99cac6f362 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -1116,7 +1116,6 @@ en-GB: migrate_account: Move to a different account migrate_account_html: If you wish to redirect this account to a different one, you can configure it here. or_log_in_with: Or log in with - privacy_policy_agreement_html: I have read and agree to the privacy policy progress: confirm: Confirm email details: Your details diff --git a/config/locales/en.yml b/config/locales/en.yml index 2971fe1f25..ba0716c480 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -214,6 +214,7 @@ en: enable_user: Enable User memorialize_account: Memorialize Account promote_user: Promote User + publish_terms_of_service: Publish Terms of Service reject_appeal: Reject Appeal reject_user: Reject User remove_avatar_user: Remove Avatar @@ -278,6 +279,7 @@ en: enable_user_html: "%{name} enabled login for user %{target}" memorialize_account_html: "%{name} turned %{target}'s account into a memoriam page" promote_user_html: "%{name} promoted user %{target}" + publish_terms_of_service_html: "%{name} published updates to the terms of service" reject_appeal_html: "%{name} rejected moderation decision appeal from %{target}" reject_user_html: "%{name} rejected sign-up from %{target}" remove_avatar_user_html: "%{name} removed %{target}'s avatar" @@ -925,6 +927,35 @@ en: search: Search title: Hashtags updated_msg: Hashtag settings updated successfully + terms_of_service: + back: Back to terms of service + changelog: What's changed + create: Use your own + current: Current + draft: Draft + generate: Use template + generates: + action: Generate + chance_to_review: The generated terms of service will not be published automatically. You will have a chance to review the results. + explanation_html: You can use Mastodon's template terms of service. To proceed, you will need to fill in a few necessary pieces of information. + title: Terms of Service Setup + history: History + live: Live + no_history: There are no recorded changes of the terms of service yet. + no_terms_of_service_html: You don't currently have any terms of service configured. Unlike the privacy policy, terms of service are optional for online services, however, they are meant to protect you from potential liabilities in disputes with your users. + notified_on_html: Users notified on %{date} + notify_users: Notify users + preview: + explanation_html: 'The email will be sent to %{display_count} users who have signed up before %{date}. The following text will be included in the e-mail:' + send_preview: Send preview to %{email} + send_to_all: + one: Send %{display_count} email + other: Send %{display_count} emails + title: Preview terms of service notification + publish: Publish + published_on_html: Published on %{date} + save_draft: Save draft + title: Terms of Service title: Administration trends: allow: Allow @@ -1132,7 +1163,6 @@ en: migrate_account: Move to a different account migrate_account_html: If you wish to redirect this account to a different one, you can configure it here. or_log_in_with: Or log in with - privacy_policy_agreement_html: I have read and agree to the privacy policy progress: confirm: Confirm email details: Your details @@ -1178,6 +1208,7 @@ en: view_strikes: View past strikes against your account too_fast: Form submitted too fast, try again. use_security_key: Use security key + user_agreement_html: I have read and agree to the terms of service and privacy policy author_attribution: example_title: Sample text hint_html: Are you writing news or blog articles outside of Mastodon? Control how you get credited when they are shared on Mastodon. @@ -1900,6 +1931,15 @@ en: further_actions_html: If this wasn't you, we recommend that you %{action} immediately and enable two-factor authentication to keep your account secure. subject: Your account has been accessed from a new IP address title: A new sign-in + terms_of_service_changed: + agreement: By continuing to use %{domain}, you are agreeing to these terms. If you disagree with the updated terms, you may terminate your agreement with %{domain} at any time by deleting your account. + changelog: 'At a glance, here is what this update means for you:' + description: 'You are receiving this e-mail because we''re making some changes to our terms of service at %{domain}. We encourage you to review the updated terms in full here:' + description_html: You are receiving this e-mail because we're making some changes to our terms of service at %{domain}. We encourage you to review the updated terms in full here. + sign_off: The %{domain} team + subject: Updates to our terms of service + subtitle: The terms of service of %{domain} are changing + title: Important update warning: appeal: Submit an appeal appeal_description: If you believe this is an error, you can submit an appeal to the staff of %{instance}. diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 120df142b7..6ddcd03059 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -1091,7 +1091,6 @@ eo: migrate_account: Movi al alia konto migrate_account_html: Se vi deziras alidirekti ĉi tiun konton al alia, vi povas agordi ĝin ĉi tie. or_log_in_with: Aŭ saluti per - privacy_policy_agreement_html: Mi legis kaj konsentis pri privatpolitiko progress: confirm: Konfirmi retadreson details: Viaj detaloj diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index 09163c2e4a..98605ba983 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -1132,7 +1132,6 @@ es-AR: migrate_account: Mudarse a otra cuenta migrate_account_html: Si querés redireccionar esta cuenta a otra distinta, podés configurar eso acá. or_log_in_with: O iniciar sesión con - privacy_policy_agreement_html: Leí y acepto la política de privacidad progress: confirm: Confirmar correo electrónico details: Tus detalles diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index 53557c091a..6242e2ec35 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -1132,7 +1132,6 @@ es-MX: migrate_account: Mudarse a otra cuenta migrate_account_html: Si deseas redireccionar esta cuenta a otra distinta, puedes configurarlo aquí. or_log_in_with: O inicia sesión con - privacy_policy_agreement_html: He leído y acepto la política de privacidad progress: confirm: Confirmar dirección de correo details: Tus detalles diff --git a/config/locales/es.yml b/config/locales/es.yml index 921db752ab..08eaa6834d 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1132,7 +1132,6 @@ es: migrate_account: Mudarse a otra cuenta migrate_account_html: Si deseas redireccionar esta cuenta a otra distinta, puedes configurarlo aquí. or_log_in_with: O inicia sesión con - privacy_policy_agreement_html: He leído y acepto la política de privacidad progress: confirm: Confirmar dirección de correo details: Tus detalles diff --git a/config/locales/et.yml b/config/locales/et.yml index 40db2eede0..69f3f2817f 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -1117,7 +1117,6 @@ et: migrate_account: Teisele kontole ära kolimine migrate_account_html: Kui soovid konto siit ära kolida, saad seda teha siin. or_log_in_with: Või logi sisse koos - privacy_policy_agreement_html: Olen tutvunud isikuandmete kaitse põhimõtetega ja nõustun nendega progress: confirm: E-posti kinnitamine details: Sinu üksikasjad diff --git a/config/locales/eu.yml b/config/locales/eu.yml index dc064ebc7b..a733d3bb7f 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -1041,7 +1041,6 @@ eu: migrate_account: Migratu beste kontu batera migrate_account_html: Kontu hau beste batera birbideratu nahi baduzu, hemen konfiguratu dezakezu. or_log_in_with: Edo hasi saioa honekin - privacy_policy_agreement_html: Pribatutasun politika irakurri dut eta ados nago progress: details: Zure xehetasunak review: Gure berrikuspena diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 7cb0714c28..6a5bb49833 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -978,7 +978,6 @@ fa: migrate_account: نقل مکان به یک حساب دیگر migrate_account_html: اگر می‌خواهید این حساب را به حساب دیگری منتقل کنید، این‌جا را کلیک کنید. or_log_in_with: یا ورود به وسیلهٔ - privacy_policy_agreement_html: سیاست محرمانگی را خوانده و پذیرفته‌ام progress: confirm: تأیید رایانامه details: جزئیات شما diff --git a/config/locales/fi.yml b/config/locales/fi.yml index d7755d23b8..76b860f86e 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -1132,7 +1132,6 @@ fi: migrate_account: Muuta toiseen tiliin migrate_account_html: Jos haluat ohjata tämän tilin toiseen, voit asettaa toisen tilin tästä. or_log_in_with: Tai käytä kirjautumiseen - privacy_policy_agreement_html: Olen lukenut ja hyväksyn tietosuojakäytännön progress: confirm: Vahvista sähköpostiosoite details: Omat tietosi diff --git a/config/locales/fo.yml b/config/locales/fo.yml index 502c027b5d..be21dbd91d 100644 --- a/config/locales/fo.yml +++ b/config/locales/fo.yml @@ -1132,7 +1132,6 @@ fo: migrate_account: Flyt til eina aðra kontu migrate_account_html: Ynskir tú at víðaribeina hesa kontuna til eina aðra, so kanst tú seta tað upp her. or_log_in_with: Ella innrita við - privacy_policy_agreement_html: Eg havi lisið og taki undir við privatlívspolitikkinum progress: confirm: Vátta teldupost details: Tínir smálutir diff --git a/config/locales/fr-CA.yml b/config/locales/fr-CA.yml index 9c2bf3398e..58d4863e32 100644 --- a/config/locales/fr-CA.yml +++ b/config/locales/fr-CA.yml @@ -1120,7 +1120,6 @@ fr-CA: migrate_account: Déménager vers un compte différent migrate_account_html: Si vous voulez rediriger ce compte vers un autre, vous pouvez le configurer ici. or_log_in_with: Ou authentifiez-vous avec - privacy_policy_agreement_html: J’ai lu et j’accepte la politique de confidentialité progress: confirm: Confirmation de l'adresse mail details: Vos infos diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 650147eb5d..233b71f253 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1120,7 +1120,6 @@ fr: migrate_account: Déménager vers un compte différent migrate_account_html: Si vous voulez rediriger ce compte vers un autre, vous pouvez le configurer ici. or_log_in_with: Ou authentifiez-vous avec - privacy_policy_agreement_html: J’ai lu et j’accepte la politique de confidentialité progress: confirm: Confirmation de l'adresse mail details: Vos infos diff --git a/config/locales/fy.yml b/config/locales/fy.yml index 24be51d9d7..8731056ff5 100644 --- a/config/locales/fy.yml +++ b/config/locales/fy.yml @@ -1117,7 +1117,6 @@ fy: migrate_account: Nei in oar account ferhúzje migrate_account_html: Wannear’t jo dizze account nei in oare account trochferwize wolle, kinne jo dit hjir ynstelle. or_log_in_with: Of oanmelde mei - privacy_policy_agreement_html: Ik haw it privacybelied lêzen en gean dêrmei akkoard progress: confirm: E-mailadres werhelje details: Jo gegevens diff --git a/config/locales/ga.yml b/config/locales/ga.yml index deb9986691..9f00fcdb99 100644 --- a/config/locales/ga.yml +++ b/config/locales/ga.yml @@ -1186,7 +1186,6 @@ ga: migrate_account: Bog chuig cuntas eile migrate_account_html: Más mian leat an cuntas seo a atreorú chuig ceann eile, is féidir leat é a chumrú anseo. or_log_in_with: Nó logáil isteach le - privacy_policy_agreement_html: Léigh mé agus aontaím leis an polasaí príobháideachais progress: confirm: Deimhnigh ríomhphost details: Do chuid sonraí diff --git a/config/locales/gd.yml b/config/locales/gd.yml index 7581f6c856..1e2f4d565b 100644 --- a/config/locales/gd.yml +++ b/config/locales/gd.yml @@ -1168,7 +1168,6 @@ gd: migrate_account: Imrich gu cunntas eile migrate_account_html: Nam bu mhiann leat an cunntas seo ath-stiùireadh gu fear eile, ’s urrainn dhut a rèiteachadh an-seo. or_log_in_with: No clàraich a-steach le - privacy_policy_agreement_html: Leugh mi is tha mi ag aontachadh ris a’ phoileasaidh prìobhaideachd progress: confirm: Dearbh am post-d details: Am fiosrachadh agad diff --git a/config/locales/gl.yml b/config/locales/gl.yml index feda673621..bd735c14ea 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1132,7 +1132,6 @@ gl: migrate_account: Mover a unha conta diferente migrate_account_html: Se queres redirixir esta conta hacia outra diferente, podes facelo aquí. or_log_in_with: Ou accede con - privacy_policy_agreement_html: Lin e acepto a política de privacidade progress: confirm: Confirmar correo details: Detalles diff --git a/config/locales/he.yml b/config/locales/he.yml index 5243cf4768..2673748242 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -1168,7 +1168,6 @@ he: migrate_account: מעבר לחשבון אחר migrate_account_html: אם ברצונך להכווין את החשבון לעבר חשבון אחר, ניתן להגדיר זאת כאן. or_log_in_with: או התחבר באמצעות - privacy_policy_agreement_html: קראתי והסכמתי למדיניות הפרטיות progress: confirm: אימות כתובת הדואל details: הפרטים שלך diff --git a/config/locales/hu.yml b/config/locales/hu.yml index c3420205c8..88a3edc1a5 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -1125,7 +1125,6 @@ hu: migrate_account: Felhasználói fiók költöztetése migrate_account_html: Ha át szeretnéd irányítani ezt a fiókodat egy másikra, akkor itt állíthatod be. or_log_in_with: Vagy jelentkezz be ezzel - privacy_policy_agreement_html: Elolvastam és egyetértek az adatvédemi nyilatkozattal progress: confirm: E-mail megerősítése details: Saját adatok diff --git a/config/locales/hy.yml b/config/locales/hy.yml index 3b4aaf3629..8cf1ad594d 100644 --- a/config/locales/hy.yml +++ b/config/locales/hy.yml @@ -462,7 +462,6 @@ hy: logout: Դուրս գալ migrate_account: Տեղափոխուել այլ հաշիւ or_log_in_with: Կամ մուտք գործել օգտագործելով՝ - privacy_policy_agreement_html: Ես կարդացել եւ ընդունել եմ գաղնիութեան քաղաքականութիւնը progress: details: Ձեր տուեալները review: Վաւերացում diff --git a/config/locales/ia.yml b/config/locales/ia.yml index 54401b1c5a..a658ada953 100644 --- a/config/locales/ia.yml +++ b/config/locales/ia.yml @@ -1132,7 +1132,6 @@ ia: migrate_account: Migrar a un altere conto migrate_account_html: Si tu vole rediriger iste conto a un altere, tu pote configurar lo hic. or_log_in_with: O aperi session con - privacy_policy_agreement_html: Io ha legite e accepta le politica de confidentialitate progress: confirm: Confirmar e-mail details: Tu detalios diff --git a/config/locales/id.yml b/config/locales/id.yml index 34b690828d..fa939485d1 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -903,7 +903,6 @@ id: migrate_account: Pindah ke akun berbeda migrate_account_html: Jika Anda ingin mengalihkan akun ini ke akun lain, Anda dapat mengaturnya di sini. or_log_in_with: Atau masuk dengan - privacy_policy_agreement_html: Saya telah membaca dan menerima kebijakan privasi providers: cas: CAS saml: SAML diff --git a/config/locales/ie.yml b/config/locales/ie.yml index f006f2c8ad..d1b888ecf7 100644 --- a/config/locales/ie.yml +++ b/config/locales/ie.yml @@ -1039,7 +1039,6 @@ ie: migrate_account: Mover te a un conto diferent migrate_account_html: Si tu vole redirecter ti-ci conto a un altri, tu posse configurar it ci. or_log_in_with: O intrar med - privacy_policy_agreement_html: Yo leet e consenti li politica pri privatie progress: details: Tui detallies review: Nor revise diff --git a/config/locales/io.yml b/config/locales/io.yml index 83c92484ff..470c4b6fb6 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -1014,7 +1014,6 @@ io: migrate_account: Transferez a diferanta konto migrate_account_html: Se vu volas ridirektar ca konto a diferanto, vu povas ajustar hike. or_log_in_with: O eniras per - privacy_policy_agreement_html: Me lektis e konsentis privatesguidilo progress: details: Vua detali review: Nia revuo diff --git a/config/locales/is.yml b/config/locales/is.yml index cad01257fd..9efb702821 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -1136,7 +1136,6 @@ is: migrate_account: Færa á annan notandaaðgang migrate_account_html: Ef þú vilt endurbeina þessum aðgangi á einhvern annan, geturðu stillt það hér. or_log_in_with: Eða skráðu inn með - privacy_policy_agreement_html: Ég hef lesið og samþykkt persónuverndarstefnuna progress: confirm: Staðfesta tölvupóstfang details: Nánari upplýsingar þínar diff --git a/config/locales/it.yml b/config/locales/it.yml index 3b36bd3f7a..42b6753c00 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1134,7 +1134,6 @@ it: migrate_account: Sposta ad un account differente migrate_account_html: Se vuoi che questo account sia reindirizzato a uno diverso, puoi configurarlo qui. or_log_in_with: Oppure accedi con - privacy_policy_agreement_html: Ho letto e accetto l'informativa sulla privacy progress: confirm: Conferma l'e-mail details: I tuoi dettagli diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 810de2b227..93063849d2 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1114,7 +1114,6 @@ ja: migrate_account: 別のアカウントに引っ越す migrate_account_html: 引っ越し先を明記したい場合はこちらで設定できます。 or_log_in_with: または次のサービスでログイン - privacy_policy_agreement_html: プライバシーポリシーを読み、同意します progress: confirm: メールアドレスの確認 details: ユーザー情報 diff --git a/config/locales/kab.yml b/config/locales/kab.yml index 1f5c5ded79..1f3b99315a 100644 --- a/config/locales/kab.yml +++ b/config/locales/kab.yml @@ -495,7 +495,6 @@ kab: logout: Ffeɣ migrate_account: Gujj ɣer umiḍan nniḍen or_log_in_with: Neɣ eqqen s - privacy_policy_agreement_html: Ɣriɣ yerna qebleɣ tasertit n tbaḍnit progress: confirm: Sentem imayl details: Isalli-inek diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 3394433758..da3a863d2d 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1116,7 +1116,6 @@ ko: migrate_account: 계정 옮기기 migrate_account_html: 이 계정을 다른 계정으로 리디렉션 하길 원하는 경우 여기에서 설정할 수 있습니다. or_log_in_with: 다른 방법으로 로그인 하려면 - privacy_policy_agreement_html: 개인정보처리방침을 읽고 동의합니다 progress: confirm: 이메일 확인 details: 세부사항 diff --git a/config/locales/ku.yml b/config/locales/ku.yml index 5337610992..719276f6e7 100644 --- a/config/locales/ku.yml +++ b/config/locales/ku.yml @@ -916,7 +916,6 @@ ku: migrate_account: Livandin bo ajimêreke din migrate_account_html: Ku tu dixwazî ev ajimêr li ajimêreke cuda beralî bikî, tu dikarî ji vir de saz bike. or_log_in_with: An têketinê bike bi riya - privacy_policy_agreement_html: Min Politîka taybetiyê xwend û dipejirînim providers: cas: CAS saml: SAML diff --git a/config/locales/lad.yml b/config/locales/lad.yml index 2612ae2131..88c6f161f1 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -1097,7 +1097,6 @@ lad: migrate_account: Transferate a otro kuento migrate_account_html: Si keres readresar este kuento a otra distinta, puedes konfigurarlo aki. or_log_in_with: O konektate kon tu kuento kon - privacy_policy_agreement_html: Tengo meldado i acheto la politika de privasita progress: confirm: Konfirma posta details: Tus detalyos diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 65bd4456ae..d88307a2b7 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -795,7 +795,6 @@ lt: migrate_account: Persikelti prie kitos paskyros migrate_account_html: Jei nori šią paskyrą nukreipti į kitą, gali sukonfigūruoti ją čia. or_log_in_with: Arba prisijungti su - privacy_policy_agreement_html: Perskaičiau ir sutinku su privatumo politika progress: details: Tavo duomenys review: Mūsų peržiūra diff --git a/config/locales/lv.yml b/config/locales/lv.yml index cc4f4d9cfc..fb13cf8f55 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -1108,7 +1108,6 @@ lv: migrate_account: Pāriešana uz citu kontu migrate_account_html: Ja vēlies novirzīt šo kontu uz citu, tu vari to konfigurēt šeit. or_log_in_with: Vai piesakies ar - privacy_policy_agreement_html: Esmu izlasījis un piekrītu privātuma politikai progress: confirm: Apstiprināt e-pasta adresi details: Tavi dati diff --git a/config/locales/ms.yml b/config/locales/ms.yml index 948ad56a54..fef2da5b46 100644 --- a/config/locales/ms.yml +++ b/config/locales/ms.yml @@ -1001,7 +1001,6 @@ ms: migrate_account: Pindah kepada akaun lain migrate_account_html: Jika anda ingin mengubah hala akaun ini kepada akaun lain, anda boleh konfigurasikannya di sini. or_log_in_with: Atau daftar masuk dengan - privacy_policy_agreement_html: Saya telah membaca dan bersetuju menerima dasar privasi progress: details: Maklumat anda review: Ulasan kami diff --git a/config/locales/my.yml b/config/locales/my.yml index 4428843855..31e937946f 100644 --- a/config/locales/my.yml +++ b/config/locales/my.yml @@ -994,7 +994,6 @@ my: migrate_account: အခြားအကောင့်တစ်ခုသို့ ရွှေ့ရန် migrate_account_html: ဤအကောင့်ကို အခြားအကောင့်သို့ ပြန်ညွှန်းလိုပါက ဤနေရာတွင် စီစဉ်သတ်မှတ်နိုင်သည်။ or_log_in_with: သို့မဟုတ် အကောင့်ဖြင့် ဝင်ရောက်ပါ - privacy_policy_agreement_html: ကိုယ်ရေးအချက်အလက်မူဝါဒ ကို ဖတ်ပြီး သဘောတူလိုက်ပါပြီ progress: details: သင့်အသေးစိတ်အချက်အလက်များ review: ကျွန်ုပ်တို့၏သုံးသပ်ချက် diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 048214eca3..acba41f9b5 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -1122,7 +1122,6 @@ nl: migrate_account: Naar een ander account verhuizen migrate_account_html: Wanneer je dit account naar een ander account wilt doorverwijzen, kun je dit hier instellen. or_log_in_with: Of inloggen met - privacy_policy_agreement_html: Ik heb het privacybeleid gelezen en ga daarmee akkoord progress: confirm: E-mailadres bevestigen details: Jouw gegevens diff --git a/config/locales/nn.yml b/config/locales/nn.yml index dbb8b6c693..338b3657d9 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -1132,7 +1132,6 @@ nn: migrate_account: Flytt til ein annan konto migrate_account_html: Om du vil visa denne kontoen til ein anna, kan du skipe det her. or_log_in_with: Eller logg inn med - privacy_policy_agreement_html: Jeg har lest og godtar retningslinjer for personvern progress: confirm: Stadfest e-post details: Opplysingane dine diff --git a/config/locales/no.yml b/config/locales/no.yml index 8bf5daad0e..152d6270cd 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -1033,7 +1033,6 @@ migrate_account: Flytt til en annen konto migrate_account_html: Hvis du ønsker å henvise denne kontoen til en annen, kan du konfigurere det her. or_log_in_with: Eller logg inn med - privacy_policy_agreement_html: Jeg har lest og godtar retningslinjer for personvern progress: details: Dine opplysninger review: Vår gjennomgang diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 5dec5ebb77..d47e0d857a 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -481,7 +481,6 @@ oc: migrate_account: Mudar endacòm mai migrate_account_html: Se volètz mandar los visitors d’aqueste compte a un autre, podètz o configurar aquí. or_log_in_with: O autentificatz-vos amb - privacy_policy_agreement_html: Ai legit e accepti la politica de confidencialitat providers: cas: CAS saml: SAML diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 3592ea53f6..3fe62d4255 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -1168,7 +1168,6 @@ pl: migrate_account: Przenieś konto migrate_account_html: Jeżeli chcesz skonfigurować przekierowanie z obecnego konta na inne, możesz zrobić to tutaj. or_log_in_with: Lub zaloguj się z użyciem - privacy_policy_agreement_html: Przeczytałem/am i akceptuję politykę prywatności progress: confirm: Potwierdź adres e-mail details: Twoje dane diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 537dee5f74..251e846579 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1132,7 +1132,6 @@ pt-BR: migrate_account: Mudar-se para outra conta migrate_account_html: Se você quer redirecionar essa conta para uma outra você pode configurar isso aqui. or_log_in_with: Ou entre com - privacy_policy_agreement_html: Eu li e concordo com a política de privacidade progress: confirm: Confirmar e-mail details: Suas informações diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index cceceb0534..23ee280b56 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -1113,7 +1113,6 @@ pt-PT: migrate_account: Mudar para uma conta diferente migrate_account_html: Se deseja redirecionar esta conta para uma outra pode configurar isso aqui. or_log_in_with: Ou iniciar sessão com - privacy_policy_agreement_html: Eu li e concordo com a política de privacidade progress: confirm: Confirmar e-mail details: Os seus dados diff --git a/config/locales/ru.yml b/config/locales/ru.yml index d9bf96c625..e09ec39e9f 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1161,7 +1161,6 @@ ru: migrate_account: Перенос учётной записи migrate_account_html: Завели новую учётную запись? Перенаправьте подписчиков на неё — настройте перенаправление тут. or_log_in_with: Или войти с помощью - privacy_policy_agreement_html: Мной прочитана и принята политика конфиденциальности progress: confirm: Подтвердите электронную почту details: Ваши данные diff --git a/config/locales/sco.yml b/config/locales/sco.yml index ff3a730b17..9374ef1742 100644 --- a/config/locales/sco.yml +++ b/config/locales/sco.yml @@ -909,7 +909,6 @@ sco: migrate_account: Uise a different accoont migrate_account_html: Gin ye'r wantin fir tae redireck this accoont tae a different ane, ye kin configure it here. or_log_in_with: Or log in wi - privacy_policy_agreement_html: A'v read an A agree tae the privacy policy providers: cas: CAS saml: SAML diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index f451c780b0..bac166faf8 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -129,6 +129,9 @@ en: show_application: You will always be able to see which app published your post regardless. tag: name: You can only change the casing of the letters, for example, to make it more readable + terms_of_service: + changelog: Can be structured with Markdown syntax. + text: Can be structured with Markdown syntax. user: chosen_languages: When checked, only posts in selected languages will be displayed in public timelines role: The role controls which permissions the user has. @@ -317,6 +320,17 @@ en: name: Hashtag trendable: Allow this hashtag to appear under trends usable: Allow posts to use this hashtag locally + terms_of_service: + changelog: What's changed? + text: Terms of Service + terms_of_service_generator: + admin_email: Email address for legal notices + arbitration_address: Physical address for arbitration notices + arbitration_website: Website for submitting arbitration notices + dmca_address: Physical address for DMCA notices + dmca_email: Email address for DMCA notices + domain: Domain + jurisdiction: Legal jurisdiction user: role: Role time_zone: Time zone diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 909b89e5a9..be5a60cdd5 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -1141,7 +1141,6 @@ sl: migrate_account: Premakni se na drug račun migrate_account_html: Če želite ta račun preusmeriti na drugega, ga lahko nastavite tukaj. or_log_in_with: Ali se prijavite z - privacy_policy_agreement_html: Prebral_a sem in se strinjam s pravilnikom o zasebnosti. progress: confirm: Potrdi e-pošto details: Vaši podatki diff --git a/config/locales/sq.yml b/config/locales/sq.yml index e9247ef365..9740d6f74e 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -1126,7 +1126,6 @@ sq: migrate_account: Kaloni në një tjetër llogari migrate_account_html: Nëse doni ta ridrejtoni këtë llogari te një tjetër, këtë mund ta formësoni këtu. or_log_in_with: Ose bëni hyrjen me - privacy_policy_agreement_html: I kam lexuar dhe pajtohem me rregullat e privatësisë progress: confirm: Ripohoni email-in details: Hollësitë tuaja diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 0b3043c47b..b206d4e12f 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -1060,7 +1060,6 @@ sr-Latn: migrate_account: Premeštanje u drugi nalog migrate_account_html: Ako želite da preusmerite ovaj nalog na neki drugi, možete to podesiti ovde. or_log_in_with: Ili se prijavite sa - privacy_policy_agreement_html: Pročitao/-la sam i saglasan/-a sam sa politikom privatnosti progress: details: Vaši detalji review: Naš pregled diff --git a/config/locales/sr.yml b/config/locales/sr.yml index e077a40eee..d12541ce81 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -1090,7 +1090,6 @@ sr: migrate_account: Премештање у други налог migrate_account_html: Ако желите да преусмерите овај налог на неки други, можете то подесити овде. or_log_in_with: Или се пријавите са - privacy_policy_agreement_html: Прочитао/-ла сам и сагласан/-а сам са политиком приватности progress: details: Ваши детаљи review: Наш преглед diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 173dd24c88..b5f8d4a4a2 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -1132,7 +1132,6 @@ sv: migrate_account: Flytta till ett annat konto migrate_account_html: Om du vill omdirigera detta konto till ett annat, kan du konfigurera det här. or_log_in_with: Eller logga in med - privacy_policy_agreement_html: Jag har läst och godkänner integritetspolicyn progress: confirm: Bekräfta e-postadress details: Dina uppgifter diff --git a/config/locales/th.yml b/config/locales/th.yml index 6834f8ac26..3daefdff53 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -1114,7 +1114,6 @@ th: migrate_account: ย้ายไปยังบัญชีอื่น migrate_account_html: หากคุณต้องการเปลี่ยนเส้นทางบัญชีนี้ไปยังบัญชีอื่น คุณสามารถ กำหนดค่าบัญชีที่นี่ or_log_in_with: หรือเข้าสู่ระบบด้วย - privacy_policy_agreement_html: ฉันได้อ่านและเห็นด้วยกับ นโยบายความเป็นส่วนตัว progress: confirm: ยืนยันอีเมล details: รายละเอียดของคุณ diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 48f6c5e240..80adc6012b 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1132,7 +1132,6 @@ tr: migrate_account: Farklı bir hesaba taşıyın migrate_account_html: Bu hesabı başka bir hesaba yönlendirmek istiyorsan, buradan yapılandırabilirsin. or_log_in_with: 'Veya şununla oturum açın:' - privacy_policy_agreement_html: Gizlilik politikasını okudum ve kabul ettim progress: confirm: E-postanızı onaylayın details: Ayrıntılarınız diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 68ca10c0cc..72743f0da7 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -1153,7 +1153,6 @@ uk: migrate_account: Переїхати на інший обліковий запис migrate_account_html: Якщо ви бажаєте переспрямувати цей обліковий запис на інший, ви можете налаштувати це тут. or_log_in_with: Або увійдіть з - privacy_policy_agreement_html: Мною прочитано і я погоджуюся з політикою приватності progress: confirm: Підтвердити електронну адресу details: Ваші дані diff --git a/config/locales/vi.yml b/config/locales/vi.yml index acc69f96cd..f9203cddcc 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1114,7 +1114,6 @@ vi: migrate_account: Chuyển sang tài khoản khác migrate_account_html: Nếu bạn muốn bỏ tài khoản này để dùng một tài khoản khác, bạn có thể thiết lập tại đây. or_log_in_with: Hoặc đăng nhập bằng - privacy_policy_agreement_html: Tôi đã đọc và đồng ý chính sách bảo mật progress: confirm: Xác nhận email details: Điền thông tin diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index d2170d3e11..76d6f3a47d 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -1114,7 +1114,6 @@ zh-CN: migrate_account: 迁移到另一个账户 migrate_account_html: 如果你希望引导他人关注另一个账号,请点击这里进行设置。 or_log_in_with: 或通过外部服务登录 - privacy_policy_agreement_html: 我已阅读并同意 隐私政策 progress: confirm: 确认邮箱 details: 你的详情 diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 752c9b5cbf..79855588e2 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -1021,7 +1021,6 @@ zh-HK: migrate_account: 轉移到另一個帳號 migrate_account_html: 想要將這個帳號指向另一個帳號可到這裡設定。 or_log_in_with: 或登入於 - privacy_policy_agreement_html: 我已閱讀且同意私隱政策 progress: details: 你的資料 review: 我們的審核 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index d14bfee1b4..4299cde808 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1116,7 +1116,6 @@ zh-TW: migrate_account: 轉移至另一個帳號 migrate_account_html: 如果您希望引導他人跟隨另一個帳號,請至這裡設定。 or_log_in_with: 或透過其他方式登入 - privacy_policy_agreement_html: 我已閱讀且同意 隱私權政策 progress: confirm: 驗證電子郵件地址 details: 您的個人資料 diff --git a/config/navigation.rb b/config/navigation.rb index bdda569092..de9e530f58 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -66,6 +66,7 @@ SimpleNavigation::Configuration.run do |navigation| n.item :admin, safe_join([material_symbol('manufacturing'), t('admin.title')]), nil, if: -> { current_user.can?(:view_dashboard, :manage_settings, :manage_rules, :manage_announcements, :manage_custom_emojis, :manage_webhooks, :manage_federation) && !self_destruct } do |s| s.item :dashboard, safe_join([material_symbol('speed'), t('admin.dashboard.title')]), admin_dashboard_path, if: -> { current_user.can?(:view_dashboard) } s.item :settings, safe_join([material_symbol('manufacturing'), t('admin.settings.title')]), admin_settings_path, if: -> { current_user.can?(:manage_settings) }, highlights_on: %r{/admin/settings} + s.item :terms_of_service, safe_join([material_symbol('description'), t('admin.terms_of_service.title')]), admin_terms_of_service_index_path, highlights_on: %r{/admin/terms_of_service}, if: -> { current_user.can?(:manage_rules) } s.item :rules, safe_join([material_symbol('gavel'), t('admin.rules.title')]), admin_rules_path, highlights_on: %r{/admin/rules}, if: -> { current_user.can?(:manage_rules) } s.item :warning_presets, safe_join([material_symbol('warning'), t('admin.warning_presets.title')]), admin_warning_presets_path, highlights_on: %r{/admin/warning_presets}, if: -> { current_user.can?(:manage_settings) } s.item :roles, safe_join([material_symbol('contact_mail'), t('admin.roles.title')]), admin_roles_path, highlights_on: %r{/admin/roles}, if: -> { current_user.can?(:manage_roles) } diff --git a/config/routes.rb b/config/routes.rb index 0f4df757da..564864c0f4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -234,8 +234,9 @@ Rails.application.routes.draw do get '/about', to: 'about#show' get '/about/more', to: redirect('/about') - get '/privacy-policy', to: 'privacy#show', as: :privacy_policy - get '/terms', to: redirect('/privacy-policy') + get '/privacy-policy', to: 'privacy#show', as: :privacy_policy + get '/terms-of-service', to: 'privacy#show', as: :terms_of_service + get '/terms', to: redirect('/terms-of-service') match '/', via: [:post, :put, :patch, :delete], to: 'application#raise_not_found', format: false match '*unmatched_route', via: :all, to: 'application#raise_not_found', format: false diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 2afe570236..126e9b43ba 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -33,6 +33,19 @@ namespace :admin do resources :action_logs, only: [:index] resources :warning_presets, except: [:new, :show] + namespace :terms_of_service do + resource :generate, only: [:show, :create] + resource :history, only: [:show] + resource :draft, only: [:show, :update] + end + + resources :terms_of_service, only: [:index, :create, :update] do + resource :preview, only: [:show], controller: 'terms_of_service/previews' do + post :test + post :distribute + end + end + resources :announcements, except: [:show] do member do post :publish diff --git a/config/routes/api.rb b/config/routes/api.rb index 86e41a2abe..34a267b35d 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -116,6 +116,7 @@ namespace :api, format: false do resources :rules, only: [:index] resources :domain_blocks, only: [:index] resource :privacy_policy, only: [:show] + resource :terms_of_service, only: [:show] resource :extended_description, only: [:show] resource :translation_languages, only: [:show] resource :languages, only: [:show] diff --git a/config/templates/terms-of-service.md b/config/templates/terms-of-service.md new file mode 100644 index 0000000000..e7d74f0e8d --- /dev/null +++ b/config/templates/terms-of-service.md @@ -0,0 +1,99 @@ +## Introduction + +These terms of service (the "Terms") cover your access and use of Server Operator’s ("Administrator" we," or "us") instance, located at %{domain} (the "Instance").  These Terms apply solely to your use of the Instance as operated by the Administrator. Please note that we have no affiliation with Mastodon gGmbH (“Mastodon”) and these Terms do not contain any representations or warranties or other promises from Mastodon about your use of the Instance. If you would like to contact us for any reason, please direct all questions, comments, concerns and notices to us by following the instructions provided in the Notice section below. + +Please read these Terms carefully before using the Instance as they contain important information about your interactions with the Instance. We may have other policies that apply to your use of the Instance and that are incorporated into these Terms. You should also read these policies before using the Instance. + +## Age Requirements and Responsibility of Parents and Legal Guardians + +By accessing the Instance, you signify that you are at least thirteen years old and that you meet the minimum age required by the laws in your country. If you are old enough to access the Instance in your country, but are not old enough to have the legal authority to consent to our Terms, please ask your parent or legal guardian to read these Terms with you, as they must agree to the Terms on your behalf. If you are a parent or legal guardian who has accepted these terms on your child’s behalf, these terms apply to you and you are responsible for your child’s activities on the Instance. + +## Prohibited Uses + +You are fully responsible for your activities while using the Instance, including any content, information or other materials you post or upload to the Instance, and you bear all risks associated with use of the Instance.  By agreeing to these Terms, you agree to comply with all applicable federal, state, and local laws and regulations in connection with your use of the Instance.  You also agree not to use the Instance to engage in any prohibited conduct, or to assist any other person or entity in engaging in any prohibited conduct. + +We reserve the right (but do not have the obligation) in our sole discretion to: (1) monitor the Instance for violations of these Terms; (2) take appropriate legal action against anyone who uses or accesses the Instance in a manner that we believe violates the law or these Terms, including without limitation, reporting such user to law enforcement authorities; (3) deny access to the Instance or any features of the Instance to anyone who violates these Terms or who we believe interferes with the ability of others to enjoy our Instance or infringes the rights of others; and (4) otherwise manage the Instance in a manner designed to protect our rights and property and to facilitate the proper functioning of the Instance. + +You are prohibited from using the Instance for the commission of harmful or illegal activities.  Accordingly, you may not, or assist any other person to (or attempt to): + +- Violate these Terms or other policies and terms posted on, or otherwise applicable to, the Instance; +- Upload any material, program, or software that contains any virus, worm, spyware, Trojan horse or other program or code designed to interrupt, destroy or limit the functionality of the Instance, launch a denial of service attack, or in any other way attempt to interfere with the functioning and availability of the Instance; +- Except as may be the result of standard search engine or Internet browser usage, use, launch, develop, or distribute any automated system, including without limitation, any spider, robot, cheat utility, scraper, offline reader, or any data mining or similar data gathering extraction tools to access the Instance, or use or launch any unauthorized script or other software; +- Interfere with, disable, vandalize or disrupt the Instance or servers or networks connected to the Instance; +- Hack into, penetrate, disable, or otherwise circumvent the security measures of the Instance or servers or networks connected to the Instance; or otherwise use the Instance in any way that violates any applicable national, federal, state, local or international law or regulation. + +## Intellectual Property + +The Instance contains content provided by its users, including you, such as text, photos, videos, audio, links, and streams (“Content”). When you submit Content to the Instance, you represent and warrant that you have all of the rights, power, and authority necessary to grant the rights to the Content contained within these Terms. Because you alone are responsible for the Content that you submit to the Instance, you may expose yourself to liability from third parties if you post or share such Content without all necessary rights. + +You retain all ownership rights you have in the Content that you submit to the Instance, but you grant us a limited, non-exclusive, irrevocable, transferable, royalty-free, perpetual license to use, copy, store, display, share, distribute, communicate and transfer the Content in ways that are consistent with your use of the Instance. To the fullest extent possible, you agree to waive or promise not to assert against the Administrator all moral rights you may have in the Content to the extent those rights are necessary for the Administrator to host the Content on the Instance. + +## DMCA Copyright Infringement Notice + +We have implemented the procedures described in the Digital Millennium Copyright Act of 1998 ("DMCA"), 17 U.S.C. § 512 , regarding the reporting of alleged copyright infringement and the removal of or disabling access to infringing material.  If you have a good faith belief that copyrighted material on the Instance is being used in a way that infringes a copyright over which you are authorized to act, you may make a Notice of Infringing Material. If you have a good faith belief that copyrighted material that was removed or access to which was disabled was a result of a mistake or misidentification, then you may make a Notice of Counter-Notification. + +Before serving a Notice of Infringing Material or Counter-Notification, you may wish to contact a lawyer to better understand your rights and obligations under the DMCA and other applicable laws. For example, if your Notice or Counter-Notifications fails to comply with all requirements of sections 512(c)(3) or 512(g)(3), respectively, your Notice or Counter-Notification may not be effective. + +### Termination of Repeat Infringers + +We will terminate or disable your use of the Instance in appropriate circumstances if you are deemed by us to be a repeat copyright infringer. + +### Notices and Counter-Notifications must be sent to: + +DMCA Agent: Copyright Manager + +Address: %{dmca_address} + +Email:  %{dmca_email} + +## Disclaimer + +Administrator reserves the right in our sole discretion to modify or discontinue, temporarily or permanently, the Instance (or any part thereof) with or without notice to you.  You agree that Administrator will not be liable to you or to any third party for any modification or discontinuance of the Instance, except as set forth in the "Limitation of Liability" section below. + +You understand that we are not responsible for any activities or legal consequences of your use of the Instance.  Users are responsible for using the Instance in compliance with all applicable laws and regulations of the jurisdictions in which such users are domiciled, reside, or are located at the time of such access or use, as well as these Terms.  Any violation of these Terms may result in the suspension or termination by us, in our sole discretion, of your access to and use of the Instance. + +## Limitation of Liability + +In no event will Administrator’s total liability to you for all damages, losses, or causes of action exceed one hundred dollars ($100). If you are dissatisfied with the Instance or with these Terms, your sole remedy is to discontinue your use of the Instance. + +## Links to and From Other Websites + +You may gain access to other websites and Instances via links on the Instance.  These Terms apply to the Instance only and do not apply to Mastodon, other Instances, or other parties' websites.  Similarly, you may have come to the Instance via a link from another website or Instance.  The terms of use of other websites and Instances do not apply to the Instance.  Administrator assumes no responsibility for any terms of use or material outside of the Instance accessed via any link.  You are free to establish a hypertext link to the Instance so long as the link does not state or imply any sponsorship of your website, instance or service by Administrator or the Instance.  Unless expressly agreed to by us in writing, reference to any of our products, services, processes or other information, by trade name, trademark, logo, or otherwise by you or any third party does not constitute or imply endorsement, sponsorship or recommendation thereof by us.  You may not, without our prior written permission, scrape the Instance or incorporate into another website or other service any of our material, content or intellectual property, unless you are otherwise permitted by us to do so in accordance with a license or subject to separate terms. + +## Dispute Resolution by Binding Arbitration + +### Agreement to Arbitrate: + +This Dispute Resolution by Binding Arbitration section is referred to in these Terms as the “Arbitration Agreement.” You and the Instance agree that any and all disputes, claims, demands, or causes of action (“Claims”) that have arisen or may arise between you and us, whether arising out of or relating to these Terms, the website, or any aspect of the relationship or transactions between us, will be resolved exclusively through final and binding arbitration before a neutral arbitrator, rather than in a court by a judge or jury, in accordance with the terms of this Arbitration Agreement, except that, where available, you or we may (but are not required to) assert individual Claims in small claims court if such Claims are within the scope of such court’s jurisdiction. Further, this Arbitration Agreement does not preclude you from bringing issues to the attention of federal, state/provincial, or local agencies, and such agencies can, if the law allows, seek relief against us on your behalf. You agree that, by entering into these Terms, you and we are each waiving the right to a trial by jury or to participate in a class action and that our respective rights will be determined by a neutral arbitrator, not a judge or jury. + +### Prohibition of Class and Representative Actions and Non-Individualized Relief + +You and we agree that each of us may bring claims against the other only on an individual basis and not as a plaintiff or class member in any purported class or representative action or proceeding. + +### Pre-Arbitration Dispute Resolution + +Before commencing any arbitration (or suit in small claims court, if available), you agree to provide the Instance with a written notice of Claim, and the Instance agrees to provide you with a written notice of Claim to the extent reasonably possible based on the availability of your contact information to the Instance (“Notice”). The Notice to the Instance shall be sent to %{arbitration_website} with a paper copy to %{arbitration_address}. Where the Instance has your contact information, the Instance will send its Notice to you using the last email address we have on file for you if you have provided us with an email address (each, a “Notice Address”). The Notice must (i) describe the nature and basis of the Claim in sufficient detail to evaluate the merits of the claiming party’s Claim and (ii) set forth the specific relief sought, including the amount of money (if any) that is demanded and the means by which the demanding party calculated the claimed amount. Both parties agree that they will attempt to resolve a Claim through informal negotiation within sixty (60) calendar days from the date the Notice is received. If the Claim is not resolved within sixty (60) calendar days after the Notice is received, you or we may commence an arbitration proceeding. Each party agrees that %{jurisdiction} courts may enter injunctive relief to enforce the pre-filing requirements of this paragraph, including an injunction to stay an arbitration that has been commenced in violation of this paragraph. + +### Arbitration Procedures + +The relevant arbitration rules of %{jurisdiction} fully applies to the Arbitration Agreement. The arbitration will be conducted by a neutral arbitrator in accordance with %{jurisdiction} rules (the “Rules”), as modified by this Arbitration Agreement. If there is any inconsistency between any term of the Rules and any term of this Arbitration Agreement, the applicable terms of this Arbitration Agreement will control. The arbitrator must also follow the provisions of these Terms as a court would. Except as set forth above, all issues are for the arbitrator to decide, including, but not limited to, threshold issues relating to the scope, enforceability, and arbitrability of this Arbitration Agreement and issues relating to (a) whether the terms of these Terms (or any aspect thereof) are enforceable, unconscionable, or illusory and (b) any defense to arbitration, including waiver, delay, laches, or estoppel. Regardless of the manner in which the arbitration is conducted, the arbitrator will issue a reasoned written decision sufficient to explain the essential findings and conclusions on which the award is based. Payment of all filing, administration and arbitrator fees (collectively, the “Arbitration Fees”) will be governed by the Rules unless otherwise provided in this Arbitration Agreement. + +### Small Claims Court + +Subject to applicable jurisdictional requirements, either party may elect to pursue a Claim in a local small claims court rather than through arbitration so long as the matter remains in a small claims court and proceeds only on an individual basis. + +## Choice of Law  + +Any and all claims related to or arising out of your use of, or access to the Instance shall be governed by internal substantive laws of New York in all respects, without regard for the jurisdiction or forum in which you are domiciled, reside, or located at the time of such access or use. + +## Waiver and Severability  + +If you do not comply with a portion of these Terms and we do not take action right away, this does not mean we are giving up any of our rights under these Terms.  If any part of these Terms is determined to be invalid or unenforceable by a court of competent jurisdiction or arbitrator, the remainder of the Terms shall be enforced to the maximum extent permitted by law. + +## Notices + +All notices to Administrator under these Terms, unless otherwise specified shall be sent to %{admin_email}.  Service of any notice will be deemed given on the date of receipt delivered by email. + +## Changes to these Terms + +We may change or modify these Terms by posting a revised version on the Instance, or by otherwise providing notice to you, and will state at the top of the revised Terms the date they were last revised.  Changes will not apply retroactively and will become effective no earlier than fourteen (14) calendar days after they are posted, except for changes addressing changes made for legal reasons, which will be effective immediately.  Your continued use of the Instance after any change means you agree to the new Terms. diff --git a/db/migrate/20241123224956_create_terms_of_services.rb b/db/migrate/20241123224956_create_terms_of_services.rb new file mode 100644 index 0000000000..dda2b0647c --- /dev/null +++ b/db/migrate/20241123224956_create_terms_of_services.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateTermsOfServices < ActiveRecord::Migration[7.2] + def change + create_table :terms_of_services do |t| + t.text :text, null: false, default: '' + t.text :changelog, null: false, default: '' + t.datetime :published_at + t.datetime :notification_sent_at + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 30b16a8a82..de7d84cc78 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do +ActiveRecord::Schema[7.2].define(version: 2024_11_23_224956) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -191,8 +191,8 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do t.boolean "hide_collections" t.integer "avatar_storage_schema_version" t.integer "header_storage_schema_version" - t.datetime "sensitized_at", precision: nil t.integer "suspension_origin" + t.datetime "sensitized_at", precision: nil t.boolean "trendable" t.datetime "reviewed_at", precision: nil t.datetime "requested_review_at", precision: nil @@ -556,12 +556,12 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do end create_table "ip_blocks", force: :cascade do |t| - t.inet "ip", default: "0.0.0.0", null: false - t.integer "severity", default: 0, null: false - t.datetime "expires_at", precision: nil - t.text "comment", default: "", null: false t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false + t.datetime "expires_at", precision: nil + t.inet "ip", default: "0.0.0.0", null: false + t.integer "severity", default: 0, null: false + t.text "comment", default: "", null: false t.index ["ip"], name: "index_ip_blocks_on_ip", unique: true end @@ -1080,6 +1080,15 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do t.index ["tag_id"], name: "index_tag_follows_on_tag_id" end + create_table "tag_trends", force: :cascade do |t| + t.bigint "tag_id", null: false + t.float "score", default: 0.0, null: false + t.integer "rank", default: 0, null: false + t.boolean "allowed", default: false, null: false + t.string "language" + t.index ["tag_id", "language"], name: "index_tag_trends_on_tag_id_and_language", unique: true + end + create_table "tags", force: :cascade do |t| t.string "name", default: "", null: false t.datetime "created_at", precision: nil, null: false @@ -1096,6 +1105,15 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do t.index "lower((name)::text) text_pattern_ops", name: "index_tags_on_name_lower_btree", unique: true end + create_table "terms_of_services", force: :cascade do |t| + t.text "text", default: "", null: false + t.text "changelog", default: "", null: false + t.datetime "published_at" + t.datetime "notification_sent_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "tombstones", force: :cascade do |t| t.bigint "account_id" t.string "uri", null: false @@ -1343,6 +1361,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do add_foreign_key "statuses_tags", "tags", name: "fk_3081861e21", on_delete: :cascade add_foreign_key "tag_follows", "accounts", on_delete: :cascade add_foreign_key "tag_follows", "tags", on_delete: :cascade + add_foreign_key "tag_trends", "tags", on_delete: :cascade add_foreign_key "tombstones", "accounts", on_delete: :cascade add_foreign_key "user_invite_requests", "users", on_delete: :cascade add_foreign_key "users", "accounts", name: "fk_50500f500d", on_delete: :cascade @@ -1380,9 +1399,9 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do add_index "instances", ["domain"], name: "index_instances_on_domain", unique: true create_view "user_ips", sql_definition: <<-SQL - SELECT user_id, - ip, - max(used_at) AS used_at + SELECT t0.user_id, + t0.ip, + max(t0.used_at) AS used_at FROM ( SELECT users.id AS user_id, users.sign_up_ip AS ip, users.created_at AS used_at @@ -1399,7 +1418,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do login_activities.created_at FROM login_activities WHERE (login_activities.success = true)) t0 - GROUP BY user_id, ip; + GROUP BY t0.user_id, t0.ip; SQL create_view "account_summaries", materialized: true, sql_definition: <<-SQL SELECT accounts.id AS account_id, @@ -1420,9 +1439,9 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do add_index "account_summaries", ["account_id"], name: "index_account_summaries_on_account_id", unique: true create_view "global_follow_recommendations", materialized: true, sql_definition: <<-SQL - SELECT account_id, - sum(rank) AS rank, - array_agg(reason) AS reason + SELECT t0.account_id, + sum(t0.rank) AS rank, + array_agg(t0.reason) AS reason FROM ( SELECT account_summaries.account_id, ((count(follows.id))::numeric / (1.0 + (count(follows.id))::numeric)) AS rank, 'most_followed'::text AS reason @@ -1446,8 +1465,8 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_04_082851) do WHERE (follow_recommendation_suppressions.account_id = statuses.account_id))))) GROUP BY account_summaries.account_id HAVING (sum((status_stats.reblogs_count + status_stats.favourites_count)) >= (5)::numeric)) t0 - GROUP BY account_id - ORDER BY (sum(rank)) DESC; + GROUP BY t0.account_id + ORDER BY (sum(t0.rank)) DESC; SQL add_index "global_follow_recommendations", ["account_id"], name: "index_global_follow_recommendations_on_account_id", unique: true diff --git a/spec/fabricators/terms_of_service_fabricator.rb b/spec/fabricators/terms_of_service_fabricator.rb new file mode 100644 index 0000000000..f32d269578 --- /dev/null +++ b/spec/fabricators/terms_of_service_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:terms_of_service) do + text 'MyText' + changelog 'MyText' + notification_sent_at '2024-11-23 23:49:56' +end diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb index 2722538e1a..e677a24df2 100644 --- a/spec/mailers/previews/user_mailer_preview.rb +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -98,4 +98,9 @@ class UserMailerPreview < ActionMailer::Preview def failed_2fa UserMailer.failed_2fa(User.first, '127.0.0.1', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0', Time.now.utc) end + + # Preview this email at http://localhost:3000/rails/mailers/user_mailer/terms_of_service_changed + def terms_of_service_changed + UserMailer.terms_of_service_changed(User.first, TermsOfService.live.first) + end end diff --git a/spec/models/terms_of_service_spec.rb b/spec/models/terms_of_service_spec.rb new file mode 100644 index 0000000000..a4b2f536af --- /dev/null +++ b/spec/models/terms_of_service_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe TermsOfService do + pending "add some examples to (or delete) #{__FILE__}" +end