mirror of
https://github.com/mastodon/mastodon.git
synced 2024-12-28 05:55:04 +01:00
Add terms of service (#33055)
This commit is contained in:
parent
7a2a345c08
commit
30aa0df88c
@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::TermsOfService::DistributionsController < Admin::BaseController
|
||||
before_action :set_terms_of_service
|
||||
|
||||
def create
|
||||
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
|
36
app/controllers/admin/terms_of_service/drafts_controller.rb
Normal file
36
app/controllers/admin/terms_of_service/drafts_controller.rb
Normal file
@ -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
|
@ -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
|
@ -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
|
@ -0,0 +1,16 @@
|
||||
# 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
|
||||
|
||||
private
|
||||
|
||||
def set_terms_of_service
|
||||
@terms_of_service = TermsOfService.find(params[:terms_of_service_id])
|
||||
end
|
||||
end
|
17
app/controllers/admin/terms_of_service/tests_controller.rb
Normal file
17
app/controllers/admin/terms_of_service/tests_controller.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::TermsOfService::TestsController < Admin::BaseController
|
||||
before_action :set_terms_of_service
|
||||
|
||||
def create
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
def set_terms_of_service
|
||||
@terms_of_service = TermsOfService.find(params[:terms_of_service_id])
|
||||
end
|
||||
end
|
8
app/controllers/admin/terms_of_service_controller.rb
Normal file
8
app/controllers/admin/terms_of_service_controller.rb
Normal file
@ -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
|
@ -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
|
11
app/controllers/terms_of_service_controller.rb
Normal file
11
app/controllers/terms_of_service_controller.rb
Normal file
@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TermsOfServiceController < ApplicationController
|
||||
include WebAppControllerConcern
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def show
|
||||
expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in?
|
||||
end
|
||||
end
|
@ -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)
|
||||
|
11
app/javascript/mastodon/api/instance.ts
Normal file
11
app/javascript/mastodon/api/instance.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { apiRequestGet } from 'mastodon/api';
|
||||
import type {
|
||||
ApiTermsOfServiceJSON,
|
||||
ApiPrivacyPolicyJSON,
|
||||
} from 'mastodon/api_types/instance';
|
||||
|
||||
export const apiGetTermsOfService = () =>
|
||||
apiRequestGet<ApiTermsOfServiceJSON>('v1/instance/terms_of_service');
|
||||
|
||||
export const apiGetPrivacyPolicy = () =>
|
||||
apiRequestGet<ApiPrivacyPolicyJSON>('v1/instance/privacy_policy');
|
9
app/javascript/mastodon/api_types/instance.ts
Normal file
9
app/javascript/mastodon/api_types/instance.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export interface ApiTermsOfServiceJSON {
|
||||
updated_at: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ApiPrivacyPolicyJSON {
|
||||
updated_at: string;
|
||||
content: string;
|
||||
}
|
@ -18,7 +18,7 @@ import Column from 'mastodon/components/column';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { ServerHeroImage } from 'mastodon/components/server_hero_image';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
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' },
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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 (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
<h3><FormattedMessage id='privacy_policy.title' defaultMessage='Privacy Policy' /></h3>
|
||||
<p><FormattedMessage id='privacy_policy.last_updated' defaultMessage='Last updated {date}' values={{ date: isLoading ? <Skeleton width='10ch' /> : <FormattedDate value={lastUpdated} year='numeric' month='short' day='2-digit' /> }} /></p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='privacy-policy__body prose'
|
||||
dangerouslySetInnerHTML={{ __html: content }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='all' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(PrivacyPolicy);
|
90
app/javascript/mastodon/features/privacy_policy/index.tsx
Normal file
90
app/javascript/mastodon/features/privacy_policy/index.tsx
Normal file
@ -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<ApiPrivacyPolicyJSON>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
apiGetPrivacyPolicy()
|
||||
.then((data) => {
|
||||
setResponse(data);
|
||||
setLoading(false);
|
||||
return '';
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Column
|
||||
bindToDocument={!multiColumn}
|
||||
label={intl.formatMessage(messages.title)}
|
||||
>
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id='privacy_policy.title'
|
||||
defaultMessage='Privacy Policy'
|
||||
/>
|
||||
</h3>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='privacy_policy.last_updated'
|
||||
defaultMessage='Last updated {date}'
|
||||
values={{
|
||||
date: loading ? (
|
||||
<Skeleton width='10ch' />
|
||||
) : (
|
||||
<FormattedDate
|
||||
value={response?.updated_at}
|
||||
year='numeric'
|
||||
month='short'
|
||||
day='2-digit'
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{response && (
|
||||
<div
|
||||
className='privacy-policy__body prose'
|
||||
dangerouslySetInnerHTML={{ __html: response.content }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='all' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default PrivacyPolicy;
|
95
app/javascript/mastodon/features/terms_of_service/index.tsx
Normal file
95
app/javascript/mastodon/features/terms_of_service/index.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
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';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
|
||||
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<ApiTermsOfServiceJSON>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
apiGetTermsOfService()
|
||||
.then((data) => {
|
||||
setResponse(data);
|
||||
setLoading(false);
|
||||
return '';
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!loading && !response) {
|
||||
return <BundleColumnError multiColumn={multiColumn} errorType='routing' />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column
|
||||
bindToDocument={!multiColumn}
|
||||
label={intl.formatMessage(messages.title)}
|
||||
>
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id='terms_of_service.title'
|
||||
defaultMessage='Terms of Service'
|
||||
/>
|
||||
</h3>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='privacy_policy.last_updated'
|
||||
defaultMessage='Last updated {date}'
|
||||
values={{
|
||||
date: loading ? (
|
||||
<Skeleton width='10ch' />
|
||||
) : (
|
||||
<FormattedDate
|
||||
value={response?.updated_at}
|
||||
year='numeric'
|
||||
month='short'
|
||||
day='2-digit'
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{response && (
|
||||
<div
|
||||
className='privacy-policy__body prose'
|
||||
dangerouslySetInnerHTML={{ __html: response.content }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='all' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default TermsOfService;
|
@ -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,
|
||||
|
@ -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 = <span aria-hidden>{' · '}</span>;
|
||||
|
||||
return (
|
||||
<div className='link-footer'>
|
||||
<p>
|
||||
<strong>{domain}</strong>:
|
||||
{' '}
|
||||
<Link to='/about' target={multiColumn ? '_blank' : undefined}><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
|
||||
{statusPageUrl && (
|
||||
<>
|
||||
{DividingCircle}
|
||||
<a href={statusPageUrl} target='_blank' rel='noopener'><FormattedMessage id='footer.status' defaultMessage='Status' /></a>
|
||||
</>
|
||||
)}
|
||||
{canInvite && (
|
||||
<>
|
||||
{DividingCircle}
|
||||
<a href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
|
||||
</>
|
||||
)}
|
||||
{canProfileDirectory && (
|
||||
<>
|
||||
{DividingCircle}
|
||||
<Link to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
|
||||
</>
|
||||
)}
|
||||
{DividingCircle}
|
||||
<Link to='/privacy-policy' target={multiColumn ? '_blank' : undefined}><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Mastodon</strong>:
|
||||
{' '}
|
||||
<a href='https://joinmastodon.org' target='_blank'><FormattedMessage id='footer.about' defaultMessage='About' /></a>
|
||||
{DividingCircle}
|
||||
<a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='footer.get_app' defaultMessage='Get the app' /></a>
|
||||
{DividingCircle}
|
||||
<Link to='/keyboard-shortcuts'><FormattedMessage id='footer.keyboard_shortcuts' defaultMessage='Keyboard shortcuts' /></Link>
|
||||
{DividingCircle}
|
||||
<a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a>
|
||||
{DividingCircle}
|
||||
<span className='version'>v{version}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter)));
|
105
app/javascript/mastodon/features/ui/components/link_footer.tsx
Normal file
105
app/javascript/mastodon/features/ui/components/link_footer.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
domain,
|
||||
version,
|
||||
source_url,
|
||||
statusPageUrl,
|
||||
profile_directory as canProfileDirectory,
|
||||
termsOfServiceEnabled,
|
||||
} from 'mastodon/initial_state';
|
||||
|
||||
const DividingCircle: React.FC = () => <span aria-hidden>{' · '}</span>;
|
||||
|
||||
export const LinkFooter: React.FC<{
|
||||
multiColumn: boolean;
|
||||
}> = ({ multiColumn }) => {
|
||||
return (
|
||||
<div className='link-footer'>
|
||||
<p>
|
||||
<strong>{domain}</strong>:{' '}
|
||||
<Link to='/about' target={multiColumn ? '_blank' : undefined}>
|
||||
<FormattedMessage id='footer.about' defaultMessage='About' />
|
||||
</Link>
|
||||
{statusPageUrl && (
|
||||
<>
|
||||
<DividingCircle />
|
||||
<a href={statusPageUrl} target='_blank' rel='noopener noreferrer'>
|
||||
<FormattedMessage id='footer.status' defaultMessage='Status' />
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
{canProfileDirectory && (
|
||||
<>
|
||||
<DividingCircle />
|
||||
<Link to='/directory'>
|
||||
<FormattedMessage
|
||||
id='footer.directory'
|
||||
defaultMessage='Profiles directory'
|
||||
/>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
<DividingCircle />
|
||||
<Link
|
||||
to='/privacy-policy'
|
||||
target={multiColumn ? '_blank' : undefined}
|
||||
rel='privacy-policy'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='footer.privacy_policy'
|
||||
defaultMessage='Privacy policy'
|
||||
/>
|
||||
</Link>
|
||||
{termsOfServiceEnabled && (
|
||||
<>
|
||||
<DividingCircle />
|
||||
<Link
|
||||
to='/terms-of-service'
|
||||
target={multiColumn ? '_blank' : undefined}
|
||||
rel='terms-of-service'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='footer.terms_of_service'
|
||||
defaultMessage='Terms of service'
|
||||
/>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Mastodon</strong>:{' '}
|
||||
<a href='https://joinmastodon.org' target='_blank' rel='noreferrer'>
|
||||
<FormattedMessage id='footer.about' defaultMessage='About' />
|
||||
</a>
|
||||
<DividingCircle />
|
||||
<a
|
||||
href='https://joinmastodon.org/apps'
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
>
|
||||
<FormattedMessage id='footer.get_app' defaultMessage='Get the app' />
|
||||
</a>
|
||||
<DividingCircle />
|
||||
<Link to='/keyboard-shortcuts'>
|
||||
<FormattedMessage
|
||||
id='footer.keyboard_shortcuts'
|
||||
defaultMessage='Keyboard shortcuts'
|
||||
/>
|
||||
</Link>
|
||||
<DividingCircle />
|
||||
<a href={source_url} rel='noopener noreferrer' target='_blank'>
|
||||
<FormattedMessage
|
||||
id='footer.source_code'
|
||||
defaultMessage='View source code'
|
||||
/>
|
||||
</a>
|
||||
<DividingCircle />
|
||||
<span className='version'>v{version}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -71,6 +71,7 @@ import {
|
||||
Explore,
|
||||
About,
|
||||
PrivacyPolicy,
|
||||
TermsOfService,
|
||||
} from './util/async-components';
|
||||
import { ColumnsContextProvider } from './util/columns_context';
|
||||
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
|
||||
@ -198,6 +199,7 @@ class SwitchingColumnsArea extends PureComponent {
|
||||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
||||
<WrappedRoute path='/about' component={About} content={children} />
|
||||
<WrappedRoute path='/privacy-policy' component={PrivacyPolicy} content={children} />
|
||||
<WrappedRoute path='/terms-of-service' component={TermsOfService} content={children} />
|
||||
|
||||
<WrappedRoute path={['/home', '/timelines/home']} component={HomeTimeline} content={children} />
|
||||
<Redirect from='/timelines/public' to='/public' exact />
|
||||
|
@ -198,6 +198,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');
|
||||
}
|
||||
|
@ -43,6 +43,8 @@
|
||||
* @property {boolean=} use_pending_items
|
||||
* @property {string} version
|
||||
* @property {string} sso_redirect
|
||||
* @property {string} status_page_url
|
||||
* @property {boolean} terms_of_service_enabled
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -115,10 +117,9 @@ 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');
|
||||
|
||||
export const termsOfServiceEnabled = getMeta('terms_of_service_enabled');
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
|
@ -359,11 +359,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.admin_moderation": "Open moderation interface for #{name}",
|
||||
@ -857,6 +857,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",
|
||||
|
@ -173,7 +173,9 @@ table + p {
|
||||
}
|
||||
|
||||
.email-prose {
|
||||
p {
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
color: #17063b;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
34
app/models/terms_of_service.rb
Normal file
34
app/models/terms_of_service.rb
Normal file
@ -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 notification_sent?
|
||||
notification_sent_at.present?
|
||||
end
|
||||
|
||||
def scope_for_notification
|
||||
User.confirmed.joins(:account).merge(Account.without_suspended).where(created_at: (..published_at))
|
||||
end
|
||||
end
|
25
app/models/terms_of_service/generator.rb
Normal file
25
app/models/terms_of_service/generator.rb
Normal file
@ -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(
|
||||
admin_email
|
||||
arbitration_address
|
||||
arbitration_website
|
||||
dmca_address
|
||||
dmca_email
|
||||
domain
|
||||
jurisdiction
|
||||
).freeze
|
||||
|
||||
attr_accessor(*VARIABLES)
|
||||
|
||||
validates(*VARIABLES, presence: true)
|
||||
|
||||
def render
|
||||
format(TEMPLATE, VARIABLES.index_with { |key| public_send(key) })
|
||||
end
|
||||
end
|
23
app/policies/terms_of_service_policy.rb
Normal file
23
app/policies/terms_of_service_policy.rb
Normal file
@ -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.notification_sent? && role.can?(:manage_settings)
|
||||
end
|
||||
|
||||
def update?
|
||||
!record.published? && role.can?(:manage_settings)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
!record.published? && role.can?(:manage_settings)
|
||||
end
|
||||
end
|
@ -109,6 +109,7 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||
trends_as_landing_page: Setting.trends_as_landing_page,
|
||||
trends_enabled: Setting.trends,
|
||||
version: instance_presenter.version,
|
||||
terms_of_service_enabled: TermsOfService.live.exists?,
|
||||
}
|
||||
end
|
||||
|
||||
|
6
app/views/admin/terms_of_service/_links.html.haml
Normal file
6
app/views/admin/terms_of_service/_links.html.haml
Normal file
@ -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
|
19
app/views/admin/terms_of_service/drafts/show.html.haml
Normal file
19
app/views/admin/terms_of_service/drafts/show.html.haml
Normal file
@ -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
|
41
app/views/admin/terms_of_service/generates/show.html.haml
Normal file
41
app/views/admin/terms_of_service/generates/show.html.haml
Normal file
@ -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')
|
||||
|
||||
%p.lead= t('admin.terms_of_service.generates.chance_to_review_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 :jurisdiction, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= form.input :admin_email, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= form.input :dmca_email, wrapper: :with_label
|
||||
|
||||
.fields-group
|
||||
= form.input :dmca_address, 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
|
16
app/views/admin/terms_of_service/histories/show.html.haml
Normal file
16
app/views/admin/terms_of_service/histories/show.html.haml
Normal file
@ -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)
|
39
app/views/admin/terms_of_service/index.html.haml
Normal file
39
app/views/admin/terms_of_service/index.html.haml
Normal file
@ -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: 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.notification_sent?
|
||||
%span
|
||||
= t('admin.terms_of_service.notified_on_html', date: 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'
|
20
app/views/admin/terms_of_service/previews/show.html.haml
Normal file
20
app/views/admin/terms_of_service/previews/show.html.haml
Normal file
@ -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), admin_terms_of_service_test_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)), admin_terms_of_service_distribution_path(@terms_of_service), method: :post, class: 'button', data: { confirm: t('admin.reports.are_you_sure') }
|
@ -72,7 +72,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: false,
|
||||
wrapper: :with_label
|
||||
|
||||
|
6
app/views/terms_of_service/show.html.haml
Normal file
6
app/views/terms_of_service/show.html.haml
Normal file
@ -0,0 +1,6 @@
|
||||
- content_for :page_title, t('terms_of_service.title')
|
||||
|
||||
- content_for :header_tags do
|
||||
= render partial: 'shared/og'
|
||||
|
||||
= render 'shared/web_app'
|
17
app/views/user_mailer/terms_of_service_changed.html.haml
Normal file
17
app/views/user_mailer/terms_of_service_changed.html.haml
Normal file
@ -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)
|
14
app/views/user_mailer/terms_of_service_changed.text.erb
Normal file
14
app/views/user_mailer/terms_of_service_changed.text.erb
Normal file
@ -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) %>
|
@ -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
|
@ -919,7 +919,6 @@ an:
|
||||
migrate_account: Mudar-se a unatra cuenta
|
||||
migrate_account_html: Si deseyas reendrezar esta cuenta a unatra distinta, puetz <a href="%{path}">configurar-lo aquí</a>.
|
||||
or_log_in_with: U inicia sesión con
|
||||
privacy_policy_agreement_html: He leyiu y accepto la <a href="%{privacy_policy_path}" target="_blank">politica de privacidat</a>
|
||||
providers:
|
||||
cas: CAS
|
||||
saml: SAML
|
||||
|
@ -1118,7 +1118,6 @@ ar:
|
||||
migrate_account: الانتقال إلى حساب مختلف
|
||||
migrate_account_html: إن كنت ترغب في تحويل هذا الحساب نحو حساب آخَر، يُمكِنُك <a href="%{path}">إعداده هنا</a>.
|
||||
or_log_in_with: أو قم بتسجيل الدخول بواسطة
|
||||
privacy_policy_agreement_html: لقد قرأتُ وأوافق على سياسة الخصوصية <a href="%{privacy_policy_path}" target="_blank"></a>
|
||||
progress:
|
||||
confirm: تأكيد عنوان البريد الإلكتروني
|
||||
details: تفاصيلك
|
||||
|
@ -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 <a href="%{path}">configurar esta opción equí</a>.
|
||||
privacy_policy_agreement_html: Lleí y acepto la <a href="%{privacy_policy_path}" target="_blank">política de privacidá</a>
|
||||
providers:
|
||||
cas: CAS
|
||||
saml: SAML
|
||||
|
@ -1134,7 +1134,6 @@ be:
|
||||
migrate_account: Пераехаць на іншы ўліковы запіс
|
||||
migrate_account_html: Калі вы хочаце перанакіраваць гэты ўліковы запіс на іншы, то можаце <a href="%{path}">наладзіць яго тут</a>.
|
||||
or_log_in_with: Або ўвайсці з дапамогай
|
||||
privacy_policy_agreement_html: Я азнаёміўся і пагаджаюся з <a href="%{privacy_policy_path}" target="_blank">палітыкай канфідэнцыйнасці</a>
|
||||
progress:
|
||||
confirm: Пацвердзіць email
|
||||
details: Вашы дадзеныя
|
||||
|
@ -1121,7 +1121,6 @@ bg:
|
||||
migrate_account: Преместване в различен акаунт
|
||||
migrate_account_html: Ако желаете да пренасочите този акаунт към друг, можете да <a href="%{path}">настроите това тук</a>.
|
||||
or_log_in_with: Или влизане с помощта на
|
||||
privacy_policy_agreement_html: Прочетох и има съгласието ми за <a href="%{privacy_policy_path}" target="_blank">политиката за поверителност</a>
|
||||
progress:
|
||||
details: Вашите подробности
|
||||
review: Нашият преглед
|
||||
|
@ -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 <a href="%{path}">configurar aquí</a>.
|
||||
or_log_in_with: O inicia sessió amb
|
||||
privacy_policy_agreement_html: He llegit i estic d'acord amb la <a href="%{privacy_policy_path}" target="_blank">política de privacitat</a>
|
||||
progress:
|
||||
confirm: Confirmar email
|
||||
details: Els teus detalls
|
||||
|
@ -1099,7 +1099,6 @@ cs:
|
||||
migrate_account: Přesunout se na jiný účet
|
||||
migrate_account_html: Zde můžete <a href="%{path}">nastavit přesměrování tohoto účtu na jiný</a>.
|
||||
or_log_in_with: Nebo se přihlaste pomocí
|
||||
privacy_policy_agreement_html: Četl jsem a souhlasím se zásadami <a href="%{privacy_policy_path}" target="_blank">ochrany osobních údajů</a>
|
||||
progress:
|
||||
details: Vaše údaje
|
||||
review: Naše hodnocení
|
||||
|
@ -1204,7 +1204,6 @@ cy:
|
||||
migrate_account: Symud i gyfrif gwahanol
|
||||
migrate_account_html: Os hoffech chi ailgyfeirio'r cyfrif hwn at un gwahanol, mae modd <a href="%{path}">ei ffurfweddu yma</a>.
|
||||
or_log_in_with: Neu mewngofnodwch gyda
|
||||
privacy_policy_agreement_html: Rwyf wedi darllen ac yn cytuno i'r <a href="%{privacy_policy_path}" target="_blank">polisi preifatrwydd</a>
|
||||
progress:
|
||||
confirm: Cadarnhau'r e-bost
|
||||
details: Eich manylion
|
||||
|
@ -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 <a href="%{path}">opsætte dette hér</a>.
|
||||
or_log_in_with: Eller log ind med
|
||||
privacy_policy_agreement_html: Jeg accepterer <a href="%{privacy_policy_path}" target="_blank">privatlivspolitikken</a>
|
||||
progress:
|
||||
confirm: Bekræft e-mail
|
||||
details: Dine detaljer
|
||||
|
@ -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 <a href="%{path}">hier konfigurieren</a>.
|
||||
or_log_in_with: Oder anmelden mit
|
||||
privacy_policy_agreement_html: Ich habe die <a href="%{privacy_policy_path}" target="_blank">Datenschutzerklärung</a> gelesen und stimme ihr zu
|
||||
progress:
|
||||
confirm: E-Mail bestätigen
|
||||
details: Deine Daten
|
||||
|
@ -1132,7 +1132,6 @@ el:
|
||||
migrate_account: Μεταφορά σε διαφορετικό λογαριασμό
|
||||
migrate_account_html: Αν θέλεις να ανακατευθύνεις αυτό τον λογαριασμό σε έναν διαφορετικό, μπορείς να το <a href="%{path}">διαμορφώσεις εδώ</a>.
|
||||
or_log_in_with: Ή συνδέσου με
|
||||
privacy_policy_agreement_html: Έχω διαβάσει και συμφωνώ με την <a href="%{privacy_policy_path}" target="_blank">πολιτική απορρήτου</a>
|
||||
progress:
|
||||
confirm: Επιβεβαίωση email
|
||||
details: Τα στοιχεία σας
|
||||
|
@ -1132,7 +1132,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 <a href="%{path}">configure it here</a>.
|
||||
or_log_in_with: Or log in with
|
||||
privacy_policy_agreement_html: I have read and agree to the <a href="%{privacy_policy_path}" target="_blank">privacy policy</a>
|
||||
progress:
|
||||
confirm: Confirm email
|
||||
details: Your details
|
||||
|
@ -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_html: "<strong>The generated terms of service will not be published automatically.</strong> You will have a chance to review the results. Please fill in the necessary details to proceed."
|
||||
explanation_html: The terms of service template provided is for informational purposes only, and should not be construed as legal advice on any subject matter. Please consult with your own legal counsel on your situation and specific legal questions you have.
|
||||
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. Terms of service are meant to provide clarity and 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 <strong>%{display_count} users</strong> 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 <a href="%{path}">configure it here</a>.
|
||||
or_log_in_with: Or log in with
|
||||
privacy_policy_agreement_html: I have read and agree to the <a href="%{privacy_policy_path}" target="_blank">privacy policy</a>
|
||||
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 <a href="%{terms_of_service_path}" target="_blank">terms of service</a> and <a href="%{privacy_policy_path}" target="_blank">privacy policy</a>
|
||||
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.
|
||||
@ -1840,6 +1871,8 @@ en:
|
||||
too_late: It is too late to appeal this strike
|
||||
tags:
|
||||
does_not_match_previous_name: does not match the previous name
|
||||
terms_of_service:
|
||||
title: Terms of Service
|
||||
themes:
|
||||
contrast: Mastodon (High contrast)
|
||||
default: Mastodon (Dark)
|
||||
@ -1900,6 +1933,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 <a href="%{path}" target="_blank">updated terms in full here</a>.
|
||||
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}.
|
||||
|
@ -1132,7 +1132,6 @@ eo:
|
||||
migrate_account: Movi al alia konto
|
||||
migrate_account_html: Se vi deziras alidirekti ĉi tiun konton al alia, vi povas <a href="%{path}">agordi ĝin ĉi tie</a>.
|
||||
or_log_in_with: Aŭ saluti per
|
||||
privacy_policy_agreement_html: Mi legis kaj konsentis pri <a href="%{privacy_policy_path}" target="_blank">privatpolitiko</a>
|
||||
progress:
|
||||
confirm: Konfirmi retadreson
|
||||
details: Viaj detaloj
|
||||
|
@ -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 <a href="%{path}">configurar eso acá</a>.
|
||||
or_log_in_with: O iniciar sesión con
|
||||
privacy_policy_agreement_html: Leí y acepto la <a href="%{privacy_policy_path}" target="_blank">política de privacidad</a>
|
||||
progress:
|
||||
confirm: Confirmar correo electrónico
|
||||
details: Tus detalles
|
||||
|
@ -1132,7 +1132,6 @@ es-MX:
|
||||
migrate_account: Mudarse a otra cuenta
|
||||
migrate_account_html: Si deseas redireccionar esta cuenta a otra distinta, puedes <a href="%{path}">configurarlo aquí</a>.
|
||||
or_log_in_with: O inicia sesión con
|
||||
privacy_policy_agreement_html: He leído y acepto la <a href="%{privacy_policy_path}" target="_blank">política de privacidad</a>
|
||||
progress:
|
||||
confirm: Confirmar dirección de correo
|
||||
details: Tus detalles
|
||||
|
@ -1132,7 +1132,6 @@ es:
|
||||
migrate_account: Mudarse a otra cuenta
|
||||
migrate_account_html: Si deseas redireccionar esta cuenta a otra distinta, puedes <a href="%{path}">configurarlo aquí</a>.
|
||||
or_log_in_with: O inicia sesión con
|
||||
privacy_policy_agreement_html: He leído y acepto la <a href="%{privacy_policy_path}" target="_blank">política de privacidad</a>
|
||||
progress:
|
||||
confirm: Confirmar dirección de correo
|
||||
details: Tus detalles
|
||||
|
@ -1117,7 +1117,6 @@ et:
|
||||
migrate_account: Teisele kontole ära kolimine
|
||||
migrate_account_html: Kui soovid konto siit ära kolida, <a href="%{path}">saad seda teha siin</a>.
|
||||
or_log_in_with: Või logi sisse koos
|
||||
privacy_policy_agreement_html: Olen tutvunud <a href="%{privacy_policy_path}" target="_blank">isikuandmete kaitse põhimõtetega</a> ja nõustun nendega
|
||||
progress:
|
||||
confirm: E-posti kinnitamine
|
||||
details: Sinu üksikasjad
|
||||
|
@ -1041,7 +1041,6 @@ eu:
|
||||
migrate_account: Migratu beste kontu batera
|
||||
migrate_account_html: Kontu hau beste batera birbideratu nahi baduzu, <a href="%{path}">hemen konfiguratu</a> dezakezu.
|
||||
or_log_in_with: Edo hasi saioa honekin
|
||||
privacy_policy_agreement_html: <a href="%{privacy_policy_path}" target="_blank">Pribatutasun politika</a> irakurri dut eta ados nago
|
||||
progress:
|
||||
details: Zure xehetasunak
|
||||
review: Gure berrikuspena
|
||||
|
@ -978,7 +978,6 @@ fa:
|
||||
migrate_account: نقل مکان به یک حساب دیگر
|
||||
migrate_account_html: اگر میخواهید این حساب را به حساب دیگری منتقل کنید، <a href="%{path}">اینجا را کلیک کنید</a>.
|
||||
or_log_in_with: یا ورود به وسیلهٔ
|
||||
privacy_policy_agreement_html: <a href="%{privacy_policy_path}" target="_blank">سیاست محرمانگی</a> را خوانده و پذیرفتهام
|
||||
progress:
|
||||
confirm: تأیید رایانامه
|
||||
details: جزئیات شما
|
||||
|
@ -1132,7 +1132,6 @@ fi:
|
||||
migrate_account: Muuta toiseen tiliin
|
||||
migrate_account_html: Jos haluat ohjata tämän tilin toiseen, voit <a href="%{path}">asettaa toisen tilin tästä</a>.
|
||||
or_log_in_with: Tai käytä kirjautumiseen
|
||||
privacy_policy_agreement_html: Olen lukenut ja hyväksyn <a href="%{privacy_policy_path}" target="_blank">tietosuojakäytännön</a>
|
||||
progress:
|
||||
confirm: Vahvista sähköpostiosoite
|
||||
details: Omat tietosi
|
||||
|
@ -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ú <a href="%{path}">seta tað upp her</a>.
|
||||
or_log_in_with: Ella innrita við
|
||||
privacy_policy_agreement_html: Eg havi lisið og taki undir við <a href="%{privacy_policy_path}" target="_blank">privatlívspolitikkinum</a>
|
||||
progress:
|
||||
confirm: Vátta teldupost
|
||||
details: Tínir smálutir
|
||||
|
@ -1135,7 +1135,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 <a href="%{path}">configurer ici</a>.
|
||||
or_log_in_with: Ou authentifiez-vous avec
|
||||
privacy_policy_agreement_html: J’ai lu et j’accepte la <a href="%{privacy_policy_path}" target="_blank">politique de confidentialité</a>
|
||||
progress:
|
||||
confirm: Confirmation de l'adresse mail
|
||||
details: Vos infos
|
||||
|
@ -1135,7 +1135,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 <a href="%{path}">configurer ici</a>.
|
||||
or_log_in_with: Ou authentifiez-vous avec
|
||||
privacy_policy_agreement_html: J’ai lu et j’accepte la <a href="%{privacy_policy_path}" target="_blank">politique de confidentialité</a>
|
||||
progress:
|
||||
confirm: Confirmation de l'adresse mail
|
||||
details: Vos infos
|
||||
|
@ -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 <a href="%{path}">dit hjir ynstelle</a>.
|
||||
or_log_in_with: Of oanmelde mei
|
||||
privacy_policy_agreement_html: Ik haw it <a href="%{privacy_policy_path}" target="_blank">privacybelied</a> lêzen en gean dêrmei akkoard
|
||||
progress:
|
||||
confirm: E-mailadres werhelje
|
||||
details: Jo gegevens
|
||||
|
@ -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 href="%{path}">é a chumrú anseo</a>.
|
||||
or_log_in_with: Nó logáil isteach le
|
||||
privacy_policy_agreement_html: Léigh mé agus aontaím leis an <a href="%{privacy_policy_path}" target="_blank">polasaí príobháideachais</a>
|
||||
progress:
|
||||
confirm: Deimhnigh ríomhphost
|
||||
details: Do chuid sonraí
|
||||
|
@ -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 href="%{path}">a rèiteachadh an-seo</a>.
|
||||
or_log_in_with: No clàraich a-steach le
|
||||
privacy_policy_agreement_html: Leugh mi is tha mi ag aontachadh ris a’ <a href="%{privacy_policy_path}" target="_blank">phoileasaidh prìobhaideachd</a>
|
||||
progress:
|
||||
confirm: Dearbh am post-d
|
||||
details: Am fiosrachadh agad
|
||||
|
@ -1132,7 +1132,6 @@ gl:
|
||||
migrate_account: Mover a unha conta diferente
|
||||
migrate_account_html: Se queres redirixir esta conta hacia outra diferente, podes <a href="%{path}">facelo aquí</a>.
|
||||
or_log_in_with: Ou accede con
|
||||
privacy_policy_agreement_html: Lin e acepto a <a href="%{privacy_policy_path}" target="_blank">política de privacidade</a>
|
||||
progress:
|
||||
confirm: Confirmar correo
|
||||
details: Detalles
|
||||
|
@ -1168,7 +1168,6 @@ he:
|
||||
migrate_account: מעבר לחשבון אחר
|
||||
migrate_account_html: אם ברצונך להכווין את החשבון לעבר חשבון אחר, ניתן <a href="%{path}">להגדיר זאת כאן</a>.
|
||||
or_log_in_with: או התחבר באמצעות
|
||||
privacy_policy_agreement_html: קראתי והסכמתי ל<a href="%{privacy_policy_path}" target="_blank">מדיניות הפרטיות</a>
|
||||
progress:
|
||||
confirm: אימות כתובת הדואל
|
||||
details: הפרטים שלך
|
||||
|
@ -1132,7 +1132,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 <a href="%{path}">itt állíthatod be</a>.
|
||||
or_log_in_with: Vagy jelentkezz be ezzel
|
||||
privacy_policy_agreement_html: Elolvastam és egyetértek az <a href="%{privacy_policy_path}" target="_blank">adatvédemi nyilatkozattal</a>
|
||||
progress:
|
||||
confirm: E-mail megerősítése
|
||||
details: Saját adatok
|
||||
|
@ -462,7 +462,6 @@ hy:
|
||||
logout: Դուրս գալ
|
||||
migrate_account: Տեղափոխուել այլ հաշիւ
|
||||
or_log_in_with: Կամ մուտք գործել օգտագործելով՝
|
||||
privacy_policy_agreement_html: Ես կարդացել եւ ընդունել եմ <a href="%{privacy_policy_path}" target="_blank">գաղնիութեան քաղաքականութիւնը</a>
|
||||
progress:
|
||||
details: Ձեր տուեալները
|
||||
review: Վաւերացում
|
||||
|
@ -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 <a href="%{path}">configurar lo hic</a>.
|
||||
or_log_in_with: O aperi session con
|
||||
privacy_policy_agreement_html: Io ha legite e accepta le <a href="%{privacy_policy_path}" target="_blank">politica de confidentialitate</a>
|
||||
progress:
|
||||
confirm: Confirmar e-mail
|
||||
details: Tu detalios
|
||||
|
@ -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 <a href="%{path}">mengaturnya di sini</a>.
|
||||
or_log_in_with: Atau masuk dengan
|
||||
privacy_policy_agreement_html: Saya telah membaca dan menerima <a href="%{privacy_policy_path}" target="_blank">kebijakan privasi</a>
|
||||
providers:
|
||||
cas: CAS
|
||||
saml: SAML
|
||||
|
@ -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 <a href="%{path}">configurar it ci</a>.
|
||||
or_log_in_with: O intrar med
|
||||
privacy_policy_agreement_html: Yo leet e consenti li <a href="%{privacy_policy_path}" target="_blank">politica pri privatie</a>
|
||||
progress:
|
||||
details: Tui detallies
|
||||
review: Nor revise
|
||||
|
@ -1014,7 +1014,6 @@ io:
|
||||
migrate_account: Transferez a diferanta konto
|
||||
migrate_account_html: Se vu volas ridirektar ca konto a diferanto, vu povas <a href="%{path}">ajustar hike</a>.
|
||||
or_log_in_with: O eniras per
|
||||
privacy_policy_agreement_html: Me lektis e konsentis <a href="%{privacy_policy_path}" target="_blank">privatesguidilo</a>
|
||||
progress:
|
||||
details: Vua detali
|
||||
review: Nia revuo
|
||||
|
@ -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 <a href="%{path}">stillt það hér</a>.
|
||||
or_log_in_with: Eða skráðu inn með
|
||||
privacy_policy_agreement_html: Ég hef lesið og samþykkt <a href="%{privacy_policy_path}" target="_blank">persónuverndarstefnuna</a>
|
||||
progress:
|
||||
confirm: Staðfesta tölvupóstfang
|
||||
details: Nánari upplýsingar þínar
|
||||
|
@ -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 <a href="%{path}">configurarlo qui</a>.
|
||||
or_log_in_with: Oppure accedi con
|
||||
privacy_policy_agreement_html: Ho letto e accetto l'<a href="%{privacy_policy_path}" target="_blank">informativa sulla privacy</a>
|
||||
progress:
|
||||
confirm: Conferma l'e-mail
|
||||
details: I tuoi dettagli
|
||||
|
@ -1114,7 +1114,6 @@ ja:
|
||||
migrate_account: 別のアカウントに引っ越す
|
||||
migrate_account_html: 引っ越し先を明記したい場合は<a href="%{path}">こちら</a>で設定できます。
|
||||
or_log_in_with: または次のサービスでログイン
|
||||
privacy_policy_agreement_html: <a href="%{privacy_policy_path}" target="_blank">プライバシーポリシー</a>を読み、同意します
|
||||
progress:
|
||||
confirm: メールアドレスの確認
|
||||
details: ユーザー情報
|
||||
|
@ -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ɣ <a href="%{privacy_policy_path}" target="_blank">tasertit n tbaḍnit</a>
|
||||
progress:
|
||||
confirm: Sentem imayl
|
||||
details: Isalli-inek
|
||||
|
@ -1116,7 +1116,6 @@ ko:
|
||||
migrate_account: 계정 옮기기
|
||||
migrate_account_html: 이 계정을 다른 계정으로 리디렉션 하길 원하는 경우 <a href="%{path}">여기</a>에서 설정할 수 있습니다.
|
||||
or_log_in_with: 다른 방법으로 로그인 하려면
|
||||
privacy_policy_agreement_html: <a href="%{privacy_policy_path}" target="_blank">개인정보처리방침</a>을 읽고 동의합니다
|
||||
progress:
|
||||
confirm: 이메일 확인
|
||||
details: 세부사항
|
||||
|
@ -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î <a href="%{path}">ji vir de saz bike</a>.
|
||||
or_log_in_with: An têketinê bike bi riya
|
||||
privacy_policy_agreement_html: Min <a href="%{privacy_policy_path}" target="_blank">Politîka taybetiyê</a> xwend û dipejirînim
|
||||
providers:
|
||||
cas: CAS
|
||||
saml: SAML
|
||||
|
@ -1097,7 +1097,6 @@ lad:
|
||||
migrate_account: Transferate a otro kuento
|
||||
migrate_account_html: Si keres readresar este kuento a otra distinta, puedes <a href="%{path}">konfigurarlo aki</a>.
|
||||
or_log_in_with: O konektate kon tu kuento kon
|
||||
privacy_policy_agreement_html: Tengo meldado i acheto la <a href="%{privacy_policy_path}" target="_blank">politika de privasita</a>
|
||||
progress:
|
||||
confirm: Konfirma posta
|
||||
details: Tus detalyos
|
||||
|
@ -795,7 +795,6 @@ lt:
|
||||
migrate_account: Persikelti prie kitos paskyros
|
||||
migrate_account_html: Jei nori šią paskyrą nukreipti į kitą, gali <a href="%{path}">sukonfigūruoti ją čia</a>.
|
||||
or_log_in_with: Arba prisijungti su
|
||||
privacy_policy_agreement_html: Perskaičiau ir sutinku su <a href="%{privacy_policy_path}" target="_blank">privatumo politika</a>
|
||||
progress:
|
||||
details: Tavo duomenys
|
||||
review: Mūsų peržiūra
|
||||
|
@ -1110,7 +1110,6 @@ lv:
|
||||
migrate_account: Pāriešana uz citu kontu
|
||||
migrate_account_html: Ja vēlies novirzīt šo kontu uz citu, tu vari <a href="%{path}">to konfigurēt šeit</a>.
|
||||
or_log_in_with: Vai piesakies ar
|
||||
privacy_policy_agreement_html: Esmu izlasījis un piekrītu <a href="%{privacy_policy_path}" target="_blank">privātuma politikai</a>
|
||||
progress:
|
||||
confirm: Apstiprināt e-pasta adresi
|
||||
details: Tavi dati
|
||||
|
@ -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 <a href="%{path}">konfigurasikannya di sini</a>.
|
||||
or_log_in_with: Atau daftar masuk dengan
|
||||
privacy_policy_agreement_html: Saya telah membaca dan bersetuju menerima <a href="%{privacy_policy_path}" target="_blank">dasar privasi</a>
|
||||
progress:
|
||||
details: Maklumat anda
|
||||
review: Ulasan kami
|
||||
|
@ -994,7 +994,6 @@ my:
|
||||
migrate_account: အခြားအကောင့်တစ်ခုသို့ ရွှေ့ရန်
|
||||
migrate_account_html: ဤအကောင့်ကို အခြားအကောင့်သို့ ပြန်ညွှန်းလိုပါက <a href="%{path}">ဤနေရာတွင် စီစဉ်သတ်မှတ်နိုင်သည်</a>။
|
||||
or_log_in_with: သို့မဟုတ် အကောင့်ဖြင့် ဝင်ရောက်ပါ
|
||||
privacy_policy_agreement_html: <a href="%{privacy_policy_path}" target="_blank">ကိုယ်ရေးအချက်အလက်မူဝါဒ</a> ကို ဖတ်ပြီး သဘောတူလိုက်ပါပြီ
|
||||
progress:
|
||||
details: သင့်အသေးစိတ်အချက်အလက်များ
|
||||
review: ကျွန်ုပ်တို့၏သုံးသပ်ချက်
|
||||
|
@ -1132,7 +1132,6 @@ nl:
|
||||
migrate_account: Naar een ander account verhuizen
|
||||
migrate_account_html: Wanneer je dit account naar een ander account wilt doorverwijzen, kun je <a href="%{path}">dit hier instellen</a>.
|
||||
or_log_in_with: Of inloggen met
|
||||
privacy_policy_agreement_html: Ik heb het <a href="%{privacy_policy_path}" target="_blank">privacybeleid</a> gelezen en ga daarmee akkoord
|
||||
progress:
|
||||
confirm: E-mailadres bevestigen
|
||||
details: Jouw gegevens
|
||||
|
@ -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 <a href="%{path}">skipe det her</a>.
|
||||
or_log_in_with: Eller logg inn med
|
||||
privacy_policy_agreement_html: Jeg har lest og godtar <a href="%{privacy_policy_path}" target="_blank">retningslinjer for personvern</a>
|
||||
progress:
|
||||
confirm: Stadfest e-post
|
||||
details: Opplysingane dine
|
||||
|
@ -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 <a href="%{path}">konfigurere det her</a>.
|
||||
or_log_in_with: Eller logg inn med
|
||||
privacy_policy_agreement_html: Jeg har lest og godtar <a href="%{privacy_policy_path}" target="_blank">retningslinjer for personvern</a>
|
||||
progress:
|
||||
details: Dine opplysninger
|
||||
review: Vår gjennomgang
|
||||
|
@ -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<a href="%{path}"> o configurar aquí</a>.
|
||||
or_log_in_with: O autentificatz-vos amb
|
||||
privacy_policy_agreement_html: Ai legit e accepti la <a href="%{privacy_policy_path}" target="_blank">politica de confidencialitat</a>
|
||||
providers:
|
||||
cas: CAS
|
||||
saml: SAML
|
||||
|
@ -1168,7 +1168,6 @@ pl:
|
||||
migrate_account: Przenieś konto
|
||||
migrate_account_html: Jeżeli chcesz skonfigurować przekierowanie z obecnego konta na inne, możesz <a href="%{path}">zrobić to tutaj</a>.
|
||||
or_log_in_with: Lub zaloguj się z użyciem
|
||||
privacy_policy_agreement_html: Przeczytałem/am i akceptuję <a href="%{privacy_policy_path}" target="_blank">politykę prywatności</a>
|
||||
progress:
|
||||
confirm: Potwierdź adres e-mail
|
||||
details: Twoje dane
|
||||
|
@ -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 <a href="%{path}">configurar isso aqui</a>.
|
||||
or_log_in_with: Ou entre com
|
||||
privacy_policy_agreement_html: Eu li e concordo com a <a href="%{privacy_policy_path}" target="_blank">política de privacidade</a>
|
||||
progress:
|
||||
confirm: Confirmar e-mail
|
||||
details: Suas informações
|
||||
|
@ -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 <a href="%{path}">configurar isso aqui</a>.
|
||||
or_log_in_with: Ou iniciar sessão com
|
||||
privacy_policy_agreement_html: Eu li e concordo com a <a href="%{privacy_policy_path}" target="_blank">política de privacidade</a>
|
||||
progress:
|
||||
confirm: Confirmar e-mail
|
||||
details: Os seus dados
|
||||
|
@ -1168,7 +1168,6 @@ ru:
|
||||
migrate_account: Перенос учётной записи
|
||||
migrate_account_html: Завели новую учётную запись? Перенаправьте подписчиков на неё — <a href="%{path}">настройте перенаправление тут</a>.
|
||||
or_log_in_with: Или войти с помощью
|
||||
privacy_policy_agreement_html: Мной прочитана и принята <a href="%{privacy_policy_path}" target="_blank">политика конфиденциальности</a>
|
||||
progress:
|
||||
confirm: Подтвердите электронную почту
|
||||
details: Ваши данные
|
||||
|
@ -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 <a href="%{path}">configure it here</a>.
|
||||
or_log_in_with: Or log in wi
|
||||
privacy_policy_agreement_html: A'v read an A agree tae the <a href="%{privacy_policy_path}" target="_blank">privacy policy</a>
|
||||
providers:
|
||||
cas: CAS
|
||||
saml: SAML
|
||||
|
@ -130,6 +130,17 @@ 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.
|
||||
terms_of_service_generator:
|
||||
admin_email: Legal notices include counternotices, court orders, takedown requests, and law enforcement requests.
|
||||
arbitration_address: Can be the same as Physical address above, or “N/A” if using email
|
||||
arbitration_website: Can be a web form, or “N/A” if using email
|
||||
dmca_address: For US operators, use the address registered in the DMCA Designated Agent Directory. A P.O. Box listing is available upon direct request, use the DMCA Designated Agent Post Office Box Waiver Request to email the Copyright Office and describe that you are a home-based content moderator who fears revenge or retribution for your actions and need to use a P.O. Box to remove your home address from public view.
|
||||
dmca_email: Can be the same email used for “Email address for legal notices” above
|
||||
domain: Unique identification of the online service you are providing.
|
||||
jurisdiction: List the country where whoever pays the bills lives. If it’s a company or other entity, list the country where it’s incorporated, and the city, region, territory or state as appropriate.
|
||||
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.
|
||||
@ -319,6 +330,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/copyright notices
|
||||
dmca_email: Email address for DMCA/copyright notices
|
||||
domain: Domain
|
||||
jurisdiction: Legal jurisdiction
|
||||
user:
|
||||
role: Role
|
||||
time_zone: Time zone
|
||||
|
@ -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 <a href="%{path}">nastavite tukaj</a>.
|
||||
or_log_in_with: Ali se prijavite z
|
||||
privacy_policy_agreement_html: Prebral_a sem in se strinjam s <a href="%{privacy_policy_path}" target="_blank">pravilnikom o zasebnosti</a>.
|
||||
progress:
|
||||
confirm: Potrdi e-pošto
|
||||
details: Vaši podatki
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user