Merge branch 'master' into glitch-soc/merge-upstream

Conflicts:
- config/locales/pl.yml
  Conflict caused by new upstream string too close to glitch-specific
  “flavour” string. Took both strings.
This commit is contained in:
Thibaut Girka 2019-04-08 15:57:56 +02:00
commit f5f6d23d55
80 changed files with 785 additions and 311 deletions

View File

@ -50,6 +50,7 @@ All notable changes to this project will be documented in this file.
- Change Webpack to not use @babel/preset-env to compile node_modules ([ykzts](https://github.com/tootsuite/mastodon/pull/10289))
- Change web UI to use new Web Share Target API ([gol-cha](https://github.com/tootsuite/mastodon/pull/9963))
- Change ActivityPub reports to have persistent URIs ([ThibG](https://github.com/tootsuite/mastodon/pull/10303))
- Change `tootctl accounts cull --dry-run` to list accounts that would be deleted ([BenLubar](https://github.com/tootsuite/mastodon/pull/10460))
### Removed
@ -75,6 +76,7 @@ All notable changes to this project will be documented in this file.
- Fix race condition when streaming out deleted statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10280))
- Fix performance of admin federation UI by caching account counts ([Gargron](https://github.com/tootsuite/mastodon/pull/10374))
- Fix JS error on pages that don't define a CSRF token ([hinaloe](https://github.com/tootsuite/mastodon/pull/10383))
- Fix `tootctl accounts cull` sometimes removing accounts that are temporarily unreachable ([BenLubar](https://github.com/tootsuite/mastodon/pull/10460))
## [2.7.4] - 2019-03-05
### Fixed

View File

@ -128,7 +128,7 @@ group :development do
gem 'letter_opener', '~> 1.7'
gem 'letter_opener_web', '~> 1.3'
gem 'memory_profiler'
gem 'rubocop', '~> 0.66', require: false
gem 'rubocop', '~> 0.67', require: false
gem 'brakeman', '~> 4.5', require: false
gem 'bundler-audit', '~> 0.6', require: false
gem 'scss_lint', '~> 0.57', require: false

View File

@ -392,7 +392,7 @@ GEM
paperclip-av-transcoder (0.6.4)
av (~> 0.9.0)
paperclip (>= 2.5.2)
parallel (1.14.0)
parallel (1.17.0)
parallel_tests (2.28.0)
parallel
parser (2.6.2.0)
@ -528,7 +528,7 @@ GEM
rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0)
rspec-support (3.8.0)
rubocop (0.66.0)
rubocop (0.67.1)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.5, != 2.5.1.1)
@ -750,7 +750,7 @@ DEPENDENCIES
rqrcode (~> 0.10)
rspec-rails (~> 3.8)
rspec-sidekiq (~> 3.0)
rubocop (~> 0.66)
rubocop (~> 0.67)
sanitize (~> 5.0)
scss_lint (~> 0.57)
sidekiq (~> 5.2)

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
module Admin
class PendingAccountsController < BaseController
before_action :set_accounts, only: :index
def index
@form = Form::AccountBatch.new
end
def update
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
# Do nothing
ensure
redirect_to admin_pending_accounts_path(current_params)
end
def approve_all
Form::AccountBatch.new(account_ids: User.pending.pluck(:account_id), action: 'approve').save
redirect_to admin_pending_accounts_path(current_params)
end
def reject_all
Form::AccountBatch.new(account_ids: User.pending.pluck(:account_id), action: 'reject').save
redirect_to admin_pending_accounts_path(current_params)
end
private
def set_accounts
@accounts = Account.joins(:user).merge(User.pending).page(params[:page])
end
def form_account_batch_params
params.require(:form_account_batch).permit(:action, account_ids: [])
end
def action_from_button
if params[:approve]
'approve'
elsif params[:reject]
'reject'
end
end
def current_params
params.slice(:page).permit(:page)
end
end
end

View File

@ -19,13 +19,17 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
end
def load_accounts
return [] if @account.user_hides_network? && current_account.id != @account.id
return [] if hide_results?
default_accounts.merge(paginated_follows).to_a
end
def hide_results?
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
end
def default_accounts
Account.without_blocking(current_account).includes(:active_relationships, :account_stat).references(:active_relationships)
Account.includes(:active_relationships, :account_stat).references(:active_relationships)
end
def paginated_follows

View File

@ -19,13 +19,17 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
end
def load_accounts
return [] if @account.user_hides_network? && current_account.id != @account.id
return [] if hide_results?
default_accounts.merge(paginated_follows).to_a
end
def hide_results?
(@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
end
def default_accounts
Account.without_blocking(current_account).includes(:passive_relationships, :account_stat).references(:passive_relationships)
Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
end
def paginated_follows

View File

@ -3,8 +3,6 @@
class Api::V1::Accounts::StatusesController < Api::BaseController
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :set_account
before_action :check_account_suspension
before_action :check_account_block
after_action :insert_pagination_headers
respond_to :json
@ -20,14 +18,6 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
@account = Account.find(params[:account_id])
end
def check_account_suspension
gone if @account.suspended?
end
def check_account_block
gone if current_account.present? && @account.blocking?(current_account)
end
def load_statuses
cached_account_statuses
end

View File

@ -10,7 +10,6 @@ class Api::V1::AccountsController < Api::BaseController
before_action :require_user!, except: [:show, :create]
before_action :set_account, except: [:create]
before_action :check_account_suspension, only: [:show]
before_action :check_account_block, only: [:show]
before_action :check_enabled_registrations, only: [:create]
respond_to :json
@ -76,10 +75,6 @@ class Api::V1::AccountsController < Api::BaseController
gone if @account.suspended?
end
def check_account_block
gone if current_account.present? && @account.blocking?(current_account)
end
def account_params
params.permit(:username, :email, :password, :agreement, :locale)
end

View File

@ -22,7 +22,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
def default_accounts
Account
.without_blocking(current_account)
.includes(:favourites, :account_stat)
.references(:favourites)
.where(favourites: { status_id: @status.id })

View File

@ -21,7 +21,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
end
def default_accounts
Account.without_blocking(current_account).includes(:statuses, :account_stat).references(:statuses)
Account.includes(:statuses, :account_stat).references(:statuses)
end
def paginated_statuses

View File

@ -56,7 +56,7 @@ class HomeController < ApplicationController
push_subscription: current_account.user.web_push_subscription(current_session),
current_account: current_account,
token: current_session.token,
admin: Account.find_local(Setting.site_contact_username),
admin: Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')),
}
end

View File

@ -22,7 +22,7 @@ class SharesController < ApplicationController
push_subscription: current_account.user.web_push_subscription(current_session),
current_account: current_account,
token: current_session.token,
admin: Account.find_local(Setting.site_contact_username),
admin: Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')),
text: text,
}
end

View File

@ -23,7 +23,7 @@ module StreamEntriesHelper
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')])
end
elsif !(account.memorial? || account.moved?)
link_to account_follow_path(account), class: 'button logo-button', data: { method: :post } do
link_to account_follow_path(account), class: "button logo-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post } do
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
end
end

View File

@ -351,7 +351,7 @@ class Status extends ImmutablePureComponent {
return (
<HotKeys handlers={handlers}>
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))} ref={this.handleRef}>
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
{prepend}
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}>

View File

@ -22,8 +22,6 @@ const messages = defineMessages({
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
block: { id: 'account.block', defaultMessage: 'Block @{name}' },
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
@ -111,7 +109,7 @@ class Header extends ImmutablePureComponent {
} else if (account.getIn(['relationship', 'requested'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
} else if (!account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
} else if (account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
}

View File

@ -14,14 +14,17 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl';
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
const emptyList = ImmutableList();
const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
const path = withReplies ? `${accountId}:with_replies` : accountId;
return {
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], ImmutableList()),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()),
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
};
};
@ -37,6 +40,7 @@ class AccountTimeline extends ImmutablePureComponent {
isLoading: PropTypes.bool,
hasMore: PropTypes.bool,
withReplies: PropTypes.bool,
blockedBy: PropTypes.bool,
};
componentWillMount () {
@ -44,9 +48,11 @@ class AccountTimeline extends ImmutablePureComponent {
this.props.dispatch(fetchAccount(accountId));
this.props.dispatch(fetchAccountIdentityProofs(accountId));
if (!withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(accountId));
}
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
}
@ -54,9 +60,11 @@ class AccountTimeline extends ImmutablePureComponent {
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
this.props.dispatch(fetchAccount(nextProps.params.accountId));
this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
if (!nextProps.withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId));
}
this.props.dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.params.withReplies }));
}
}
@ -66,7 +74,7 @@ class AccountTimeline extends ImmutablePureComponent {
}
render () {
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore } = this.props;
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy } = this.props;
if (!statusIds && isLoading) {
return (
@ -76,6 +84,8 @@ class AccountTimeline extends ImmutablePureComponent {
);
}
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;
return (
<Column>
<ColumnBackButton />
@ -84,13 +94,13 @@ class AccountTimeline extends ImmutablePureComponent {
prepend={<HeaderContainer accountId={this.props.params.accountId} />}
alwaysPrepend
scrollKey='account_timeline'
statusIds={statusIds}
statusIds={blockedBy ? emptyList : statusIds}
featuredStatusIds={featuredStatusIds}
isLoading={isLoading}
hasMore={hasMore}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />}
emptyMessage={emptyMessage}
/>
</Column>
);

View File

@ -20,6 +20,7 @@ import ScrollableList from '../../components/scrollable_list';
const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'followers', props.params.accountId, 'items']),
hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
});
export default @connect(mapStateToProps)
@ -31,6 +32,7 @@ class Followers extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
};
componentWillMount () {
@ -50,7 +52,7 @@ class Followers extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { shouldUpdateScroll, accountIds, hasMore } = this.props;
const { shouldUpdateScroll, accountIds, hasMore, blockedBy } = this.props;
if (!accountIds) {
return (
@ -60,7 +62,7 @@ class Followers extends ImmutablePureComponent {
);
}
const emptyMessage = <FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />;
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />;
return (
<Column>
@ -75,7 +77,7 @@ class Followers extends ImmutablePureComponent {
alwaysPrepend
emptyMessage={emptyMessage}
>
{accountIds.map(id =>
{blockedBy ? [] : accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />
)}
</ScrollableList>

View File

@ -20,6 +20,7 @@ import ScrollableList from '../../components/scrollable_list';
const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
});
export default @connect(mapStateToProps)
@ -31,6 +32,7 @@ class Following extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
};
componentWillMount () {
@ -50,7 +52,7 @@ class Following extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { shouldUpdateScroll, accountIds, hasMore } = this.props;
const { shouldUpdateScroll, accountIds, hasMore, blockedBy } = this.props;
if (!accountIds) {
return (
@ -60,7 +62,7 @@ class Following extends ImmutablePureComponent {
);
}
const emptyMessage = <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;
return (
<Column>
@ -75,7 +77,7 @@ class Following extends ImmutablePureComponent {
alwaysPrepend
emptyMessage={emptyMessage}
>
{accountIds.map(id =>
{blockedBy ? [] : accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />
)}
</ScrollableList>

View File

@ -442,7 +442,7 @@ class Status extends ImmutablePureComponent {
{ancestors}
<HotKeys handlers={handlers}>
<div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false, !status.get('hidden'))}>
<div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
<DetailedStatus
status={status}
onOpenVideo={this.handleOpenVideo}

View File

@ -117,6 +117,7 @@
"emoji_button.symbols": "Simbuli",
"emoji_button.travel": "Lochi è Viaghju",
"empty_column.account_timeline": "Nisun statutu quì!",
"empty_column.account_unavailable": "Prufile micca dispunibule",
"empty_column.blocks": "Per avà ùn avete bluccatu manc'un utilizatore.",
"empty_column.community": "Ùn c'hè nunda indè a linea lucale. Scrivete puru qualcosa!",
"empty_column.direct": "Ùn avete ancu nisun missaghju direttu. S'è voi mandate o ricevete unu, u vidarete quì.",

View File

@ -117,6 +117,7 @@
"emoji_button.symbols": "Symboly",
"emoji_button.travel": "Cestování a místa",
"empty_column.account_timeline": "Tady nejsou žádné tooty!",
"empty_column.account_unavailable": "Profil nedostupný",
"empty_column.blocks": "Ještě jste nezablokoval/a žádného uživatele.",
"empty_column.community": "Místní časová osa je prázdná. Napište něco veřejně a rozhýbejte to tu!",
"empty_column.direct": "Ještě nemáte žádné přímé zprávy. Pokud nějakou pošlete nebo dostanete, zobrazí se zde.",
@ -327,7 +328,7 @@
"status.more": "Více",
"status.mute": "Skrýt uživatele @{name}",
"status.mute_conversation": "Skrýt konverzaci",
"status.open": "Rozbalit tento toot",
"status.open": "Otevřít tento toot",
"status.pin": "Připnout na profil",
"status.pinned": "Připnutý toot",
"status.read_more": "Číst více",

View File

@ -117,6 +117,7 @@
"emoji_button.symbols": "Symbole",
"emoji_button.travel": "Reisen und Orte",
"empty_column.account_timeline": "Keine Beiträge!",
"empty_column.account_unavailable": "Konto nicht verfügbar",
"empty_column.blocks": "Du hast keine Profile blockiert.",
"empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe einen öffentlichen Beitrag, um den Ball ins Rollen zu bringen!",
"empty_column.direct": "Du hast noch keine Direktnachrichten erhalten. Wenn du eine sendest oder empfängst, wird sie hier zu sehen sein.",

View File

@ -203,26 +203,6 @@
},
{
"descriptors": [
{
"defaultMessage": "Moments remaining",
"id": "time_remaining.moments"
},
{
"defaultMessage": "{number, plural, one {# second} other {# seconds}} left",
"id": "time_remaining.seconds"
},
{
"defaultMessage": "{number, plural, one {# minute} other {# minutes}} left",
"id": "time_remaining.minutes"
},
{
"defaultMessage": "{number, plural, one {# hour} other {# hours}} left",
"id": "time_remaining.hours"
},
{
"defaultMessage": "{number, plural, one {# day} other {# days}} left",
"id": "time_remaining.days"
},
{
"defaultMessage": "Closed",
"id": "poll.closed"
@ -263,6 +243,26 @@
{
"defaultMessage": "{number}d",
"id": "relative_time.days"
},
{
"defaultMessage": "Moments remaining",
"id": "time_remaining.moments"
},
{
"defaultMessage": "{number, plural, one {# second} other {# seconds}} left",
"id": "time_remaining.seconds"
},
{
"defaultMessage": "{number, plural, one {# minute} other {# minutes}} left",
"id": "time_remaining.minutes"
},
{
"defaultMessage": "{number, plural, one {# hour} other {# hours}} left",
"id": "time_remaining.hours"
},
{
"defaultMessage": "{number, plural, one {# day} other {# days}} left",
"id": "time_remaining.days"
}
],
"path": "app/javascript/mastodon/components/relative_timestamp.json"
@ -552,8 +552,8 @@
{
"descriptors": [
{
"defaultMessage": "You are blocked",
"id": "empty_column.account_timeline_blocked"
"defaultMessage": "Profile unavailable",
"id": "empty_column.account_unavailable"
},
{
"defaultMessage": "No toots here!",
@ -1256,8 +1256,8 @@
{
"descriptors": [
{
"defaultMessage": "You are blocked",
"id": "empty_column.account_timeline_blocked"
"defaultMessage": "Profile unavailable",
"id": "empty_column.account_unavailable"
},
{
"defaultMessage": "No one follows this user yet.",
@ -1269,8 +1269,8 @@
{
"descriptors": [
{
"defaultMessage": "You are blocked",
"id": "empty_column.account_timeline_blocked"
"defaultMessage": "Profile unavailable",
"id": "empty_column.account_unavailable"
},
{
"defaultMessage": "This user doesn't follow anyone yet.",

View File

@ -121,7 +121,7 @@
"emoji_button.symbols": "Symbols",
"emoji_button.travel": "Travel & Places",
"empty_column.account_timeline": "No toots here!",
"empty_column.account_timeline_blocked": "You are blocked",
"empty_column.account_unavailable": "Profile unavailable",
"empty_column.blocks": "You haven't blocked any users yet.",
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
"empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",

View File

@ -77,8 +77,8 @@
"compose_form.placeholder": "今なにしてる?",
"compose_form.poll.add_option": "追加",
"compose_form.poll.duration": "アンケート期間",
"compose_form.poll.option_placeholder": "選択肢 {number}",
"compose_form.poll.remove_option": "この選択肢を削除",
"compose_form.poll.option_placeholder": "項目 {number}",
"compose_form.poll.remove_option": "この項目を削除",
"compose_form.publish": "トゥート",
"compose_form.publish_loud": "{publish}",
"compose_form.sensitive.marked": "メディアに閲覧注意が設定されています",
@ -121,7 +121,7 @@
"emoji_button.symbols": "記号",
"emoji_button.travel": "旅行と場所",
"empty_column.account_timeline": "トゥートがありません!",
"empty_column.account_timeline_blocked": "ブロックされています",
"empty_column.account_unavailable": "プロフィールは利用できません",
"empty_column.blocks": "まだ誰もブロックしていません。",
"empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!",
"empty_column.direct": "ダイレクトメッセージはまだありません。ダイレクトメッセージをやりとりすると、ここに表示されます。",

View File

@ -117,6 +117,7 @@
"emoji_button.symbols": "기호",
"emoji_button.travel": "여행과 장소",
"empty_column.account_timeline": "여긴 툿이 없어요!",
"empty_column.account_unavailable": "프로필 사용 불가",
"empty_column.blocks": "아직 아무도 차단하지 않았습니다.",
"empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!",
"empty_column.direct": "아직 다이렉트 메시지가 없습니다. 다이렉트 메시지를 보내거나 받은 경우, 여기에 표시 됩니다.",

View File

@ -71,10 +71,10 @@
"compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en kan de toots zien die je alleen aan jouw volgers hebt gericht.",
"compose_form.lock_disclaimer.lock": "besloten",
"compose_form.placeholder": "Wat wil je kwijt?",
"compose_form.poll.add_option": "Add a choice",
"compose_form.poll.duration": "Poll duration",
"compose_form.poll.option_placeholder": "Choice {number}",
"compose_form.poll.remove_option": "Remove this choice",
"compose_form.poll.add_option": "Keuze toevoegen",
"compose_form.poll.duration": "Duur van de poll",
"compose_form.poll.option_placeholder": "Keuze {number}",
"compose_form.poll.remove_option": "Deze keuze verwijderen",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "Media is als gevoelig gemarkeerd",
@ -83,7 +83,7 @@
"compose_form.spoiler.unmarked": "Tekst is niet verborgen",
"compose_form.spoiler_placeholder": "Waarschuwingstekst",
"confirmation_modal.cancel": "Annuleren",
"confirmations.block.block_and_report": "Block & Report",
"confirmations.block.block_and_report": "Blokkeren en rapporteren",
"confirmations.block.confirm": "Blokkeren",
"confirmations.block.message": "Weet je het zeker dat je {name} wilt blokkeren?",
"confirmations.delete.confirm": "Verwijderen",
@ -154,9 +154,9 @@
"home.column_settings.basic": "Algemeen",
"home.column_settings.show_reblogs": "Boosts tonen",
"home.column_settings.show_replies": "Reacties tonen",
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
"intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
"intervals.full.days": "{number, plural, one {# dag} other {# dagen}}",
"intervals.full.hours": "{number, plural, one {# uur} other {# uur}}",
"intervals.full.minutes": "{number, plural, one {# minuut} other {# minuten}}",
"introduction.federation.action": "Volgende",
"introduction.federation.federated.headline": "Globaal",
"introduction.federation.federated.text": "Openbare toots van mensen op andere servers in de fediverse verschijnen op de globale tijdlijn.",
@ -246,7 +246,7 @@
"notification.favourite": "{name} voegde jouw toot als favoriet toe",
"notification.follow": "{name} volgt jou nu",
"notification.mention": "{name} vermeldde jou",
"notification.poll": "A poll you have voted in has ended",
"notification.poll": "Een poll waaraan jij hebt meegedaan is beëindigd",
"notification.reblog": "{name} boostte jouw toot",
"notifications.clear": "Meldingen verwijderen",
"notifications.clear_confirmation": "Weet je het zeker dat je al jouw meldingen wilt verwijderen?",
@ -257,7 +257,7 @@
"notifications.column_settings.filter_bar.show": "Tonen",
"notifications.column_settings.follow": "Nieuwe volgers:",
"notifications.column_settings.mention": "Vermeldingen:",
"notifications.column_settings.poll": "Poll results:",
"notifications.column_settings.poll": "Pollresultaten:",
"notifications.column_settings.push": "Pushmeldingen",
"notifications.column_settings.reblog": "Boosts:",
"notifications.column_settings.show": "In kolom tonen",
@ -267,14 +267,14 @@
"notifications.filter.favourites": "Favorieten",
"notifications.filter.follows": "Die jij volgt",
"notifications.filter.mentions": "Vermeldingen",
"notifications.filter.polls": "Poll results",
"notifications.filter.polls": "Pollresultaten",
"notifications.group": "{count} meldingen",
"poll.closed": "Gesloten",
"poll.refresh": "Vernieuwen",
"poll.total_votes": "{count, plural, one {# stem} other {# stemmen}}",
"poll.vote": "Stemmen",
"poll_button.add_poll": "Add a poll",
"poll_button.remove_poll": "Remove poll",
"poll_button.add_poll": "Poll toevoegen",
"poll_button.remove_poll": "Poll verwijderen",
"privacy.change": "Zichtbaarheid toot aanpassen",
"privacy.direct.long": "Alleen aan vermelde gebruikers tonen",
"privacy.direct.short": "Direct",
@ -366,7 +366,7 @@
"upload_area.title": "Hierin slepen om te uploaden",
"upload_button.label": "Media toevoegen (JPEG, PNG, GIF, WebM, MP4, MOV)",
"upload_error.limit": "Uploadlimiet van bestand overschreden.",
"upload_error.poll": "File upload not allowed with polls.",
"upload_error.poll": "Het uploaden van bestanden is in polls niet toegestaan.",
"upload_form.description": "Omschrijf dit voor mensen met een visuele beperking",
"upload_form.focus": "Voorvertoning aanpassen",
"upload_form.undo": "Verwijderen",

View File

@ -121,6 +121,7 @@
"emoji_button.symbols": "Symbole",
"emoji_button.travel": "Podróże i miejsca",
"empty_column.account_timeline": "Brak wpisów tutaj!",
"empty_column.account_timeline_blocked": "Jesteś zablokowany(-a)",
"empty_column.blocks": "Nie zablokowałeś(-aś) jeszcze żadnego użytkownika.",
"empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby zagaić!",
"empty_column.direct": "Nie masz żadnych wiadomości bezpośrednich. Kiedy dostaniesz lub wyślesz jakąś, pojawi się ona tutaj.",
@ -251,7 +252,7 @@
"notification.favourite": "{name} dodał(a) Twój wpis do ulubionych",
"notification.follow": "{name} zaczął(-ęła) Cię śledzić",
"notification.mention": "{name} wspomniał(a) o tobie",
"notification.poll": "A poll you have voted in has ended",
"notification.poll": "Głosowanie w którym brałeś(-aś) udział zakończyła się",
"notification.reblog": "{name} podbił(a) Twój wpis",
"notifications.clear": "Wyczyść powiadomienia",
"notifications.clear_confirmation": "Czy na pewno chcesz bezpowrotnie usunąć wszystkie powiadomienia?",
@ -262,7 +263,7 @@
"notifications.column_settings.filter_bar.show": "Pokaż",
"notifications.column_settings.follow": "Nowi śledzący:",
"notifications.column_settings.mention": "Wspomnienia:",
"notifications.column_settings.poll": "Poll results:",
"notifications.column_settings.poll": "Wyniki głosowania:",
"notifications.column_settings.push": "Powiadomienia push",
"notifications.column_settings.reblog": "Podbicia:",
"notifications.column_settings.show": "Pokaż w kolumnie",

View File

@ -6,7 +6,7 @@
"account.blocked": "Blokovaný/á",
"account.direct": "Súkromná správa pre @{name}",
"account.domain_blocked": "Doména ukrytá",
"account.edit_profile": "Uprav profil",
"account.edit_profile": "Uprav profil",
"account.endorse": "Zobrazuj na profile",
"account.follow": "Následuj",
"account.followers": "Sledujúci",
@ -14,38 +14,38 @@
"account.follows": "Následuje",
"account.follows.empty": "Tento užívateľ ešte nikoho nenásleduje.",
"account.follows_you": "Následuje ťa",
"account.hide_reblogs": "Skryť povýšenia od @{name}",
"account.hide_reblogs": "Skry vyzdvihnutia od @{name}",
"account.link_verified_on": "Vlastníctvo tohto odkazu bolo skontrolované {date}",
"account.locked_info": "Stav súkromia pre tento účet je nastavený na zamknutý. Jeho vlastník sám prehodnocuje, kto ho môže sledovať.",
"account.media": "Médiá",
"account.mention": "Spomeň @{name}",
"account.moved_to": "{name} sa presunul/a na:",
"account.mute": "Ignorovať @{name}",
"account.mute_notifications": "Stĺm oboznámenia od @{name}",
"account.mute_notifications": "Stĺm oboznámenia od @{name}",
"account.muted": "Utíšený/á",
"account.posts": "Príspevky",
"account.posts_with_replies": "Príspevky aj s odpoveďami",
"account.report": "Nahlás @{name}",
"account.requested": "Čaká na schválenie. Kliknite pre zrušenie žiadosti",
"account.share": "Zdieľať @{name} profil",
"account.requested": "Čaká na schválenie. Klikni pre zrušenie žiadosti",
"account.share": "Zdieľaj @{name} profil",
"account.show_reblogs": "Ukáž vyzdvihnutia od @{name}",
"account.unblock": "Odblokuj @{name}",
"account.unblock_domain": "Prestaň skrývať {domain}",
"account.unendorse": "Nezobrazuj na profile",
"account.unfollow": "Prestaň následovať",
"account.unmute": "Prestaň ignorovať @{name}",
"account.unmute_notifications": "Zrušiť stlmenie oznámení od @{name}",
"account.unmute_notifications": "Zruš stĺmenie oboznámení od @{name}",
"alert.unexpected.message": "Vyskytla sa nečakaná chyba.",
"alert.unexpected.title": "Oops!",
"boost_modal.combo": "Nabudúce môžeš kliknúť {combo} pre preskočenie",
"bundle_column_error.body": "Pri načítaní tohto prvku nastala nejaká chyba.",
"bundle_column_error.retry": "Skús to znova",
"bundle_column_error.title": "Chyba siete",
"bundle_modal_error.close": "Zatvor",
"bundle_modal_error.close": "Zatvor",
"bundle_modal_error.message": "Nastala chyba pri načítaní tohto komponentu.",
"bundle_modal_error.retry": "Skúsiť znova",
"column.blocks": "Blokovaní užívatelia",
"column.community": "Lokálna časová os",
"column.community": "Miestna časová os",
"column.direct": "Súkromné správy",
"column.domain_blocks": "Skryté domény",
"column.favourites": "Obľúbené",
@ -64,11 +64,11 @@
"column_header.show_settings": "Ukáž nastavenia",
"column_header.unpin": "Odopnúť",
"column_subheading.settings": "Nastavenia",
"community.column_settings.media_only": "Iba media",
"community.column_settings.media_only": "Iba médiá",
"compose_form.direct_message_warning": "Tento príspevok bude videný výhradne iba spomenutými užívateľmi. Ber ale na vedomie že správci tvojej a všetkých iných zahrnutých instancií majú možnosť skontrolovať túto správu.",
"compose_form.direct_message_warning_learn_more": "Zistiť viac",
"compose_form.hashtag_warning": "Tento toot nebude zobrazený pod žiadným haštagom lebo nieje listovaný. Iba verejné tooty môžu byť nájdené podľa haštagu.",
"compose_form.lock_disclaimer": "Váš účet nie je {locked}. Ktokoľvek ťa môže nasledovať a vidieť tvoje správy pre sledujúcich.",
"compose_form.lock_disclaimer": "Tvoj účet nie je {locked}. Ktokoľvek ťa môže nasledovať a vidieť tvoje správy pre sledujúcich.",
"compose_form.lock_disclaimer.lock": "zamknutý",
"compose_form.placeholder": "Čo máš na mysli?",
"compose_form.poll.add_option": "Pridaj voľbu",
@ -90,29 +90,29 @@
"confirmations.delete.message": "Si si istý/á, že chceš vymazať túto správu?",
"confirmations.delete_list.confirm": "Vymaž",
"confirmations.delete_list.message": "Si si istý/á, že chceš natrvalo vymazať tento zoznam?",
"confirmations.domain_block.confirm": "Skryť celú doménu",
"confirmations.domain_block.message": "Si si naozaj istý, že chceš blokovať celú {domain}? Vo väčšine prípadov stačí blokovať alebo ignorovať pár konkrétnych užívateľov, čo sa doporučuje. Neuvidíš obsah z tejto domény v žiadnej verejnej časovej osi, ani v oznámeniach. Tvoji následovníci pochádzajúci z tejto domény budú odstránení.",
"confirmations.domain_block.confirm": "Skry celú doménu",
"confirmations.domain_block.message": "Si si naozaj istý, že chceš blokovať celú doménu {domain}? Vo väčšine prípadov stačí blokovať alebo ignorovať pár konkrétnych užívateľov, čo sa doporučuje. Neuvidíš obsah z tejto domény v žiadnej verejnej časovej osi, ani v oznámeniach. Tvoji následovníci pochádzajúci z tejto domény budú odstránení.",
"confirmations.mute.confirm": "Ignoruj",
"confirmations.mute.message": "Naozaj chcete ignorovať {name}?",
"confirmations.redraft.confirm": "Vyčistiť a prepísať",
"confirmations.mute.message": "Naozaj chceš ignorovať {name}?",
"confirmations.redraft.confirm": "Vyčisti a prepíš",
"confirmations.redraft.message": "Si si istý/á, že chceš premazať a prepísať tento príspevok? Jeho nadobudnuté vyzdvihnutia a obľúbenia, ale i odpovede na pôvodný príspevok budú odlúčené.",
"confirmations.reply.confirm": "Odpovedz",
"confirmations.reply.message": "Odpovedaním akurát teraz prepíšeš správu, ktorú máš práve rozpísanú. Si si istý/á, že chceš pokračovať?",
"confirmations.unfollow.confirm": "Nesledovať",
"confirmations.unfollow.message": "Naozaj chcete prestať sledovať {name}?",
"confirmations.unfollow.confirm": "Nesleduj",
"confirmations.unfollow.message": "Naozaj chceš prestať sledovať {name}?",
"embed.instructions": "Umiestni kód uvedený nižšie pre pridanie tohto statusu na tvoju web stránku.",
"embed.preview": "Tu je ako to bude vyzerať:",
"emoji_button.activity": "Aktivita",
"emoji_button.custom": "Vlastné",
"emoji_button.flags": "Vlajky",
"emoji_button.food": "Jedlá a nápoje",
"emoji_button.label": "Vlož emotikony",
"emoji_button.label": "Vlož emotikony",
"emoji_button.nature": "Prírodné",
"emoji_button.not_found": "Nie emotikony!! (╯°□°)╯︵ ┻━┻",
"emoji_button.objects": "Predmety",
"emoji_button.people": "Ľudia",
"emoji_button.recent": "Často používané",
"emoji_button.search": "Hľadať...",
"emoji_button.search": "Hľadaj...",
"emoji_button.search_results": "Nájdené",
"emoji_button.symbols": "Symboly",
"emoji_button.travel": "Cestovanie a miesta",
@ -158,16 +158,16 @@
"intervals.full.hours": "{number, plural, one {# hodina} few {# hodín} many {# hodín} other {# hodiny}}",
"intervals.full.minutes": "{number, plural, one {# minúta} few {# minút} many {# minút} other {# minúty}}",
"introduction.federation.action": "Ďalej",
"introduction.federation.federated.headline": "Federated",
"introduction.federation.federated.headline": "Federovaná",
"introduction.federation.federated.text": "Verejné príspevky z ostatných serverov vo fediverse budú zobrazenie vo federovanej časovej osi.",
"introduction.federation.home.headline": "Home",
"introduction.federation.home.headline": "Domov",
"introduction.federation.home.text": "Príspevky od ľudí ktorých následuješ sa zobrazia na tvojej domovskej nástenke. Môžeš následovať hocikoho na ktoromkoľvek serveri!",
"introduction.federation.local.headline": "Local",
"introduction.federation.local.headline": "Miestna",
"introduction.federation.local.text": "Verejné príspevky od ľudí v rámci toho istého serveru na akom si aj ty, budú zobrazované na miestnej časovej osi.",
"introduction.interactions.action": "Ukonči návod!",
"introduction.interactions.favourite.headline": "Obľúbené",
"introduction.interactions.favourite.text": "Obľúbením si môžeš príspevok uložiť na neskôr, a zároveň dať jeho autorovi vedieť, že sa ti páčil.",
"introduction.interactions.reblog.headline": "Povýš",
"introduction.interactions.reblog.headline": "Vyzdvihni",
"introduction.interactions.reblog.text": "Môžeš zdieľať príspevky iných ľudí s vašimi následovateľmi tým, že ich povýšiš.",
"introduction.interactions.reply.headline": "Odpovedz",
"introduction.interactions.reply.text": "Odpovedať môžeš na príspevky iných ľudí, aj na svoje vlastné, čím sa spolu prepoja do konverzácie.",
@ -210,7 +210,7 @@
"lightbox.previous": "Predchádzajúci",
"lists.account.add": "Pridať do zoznamu",
"lists.account.remove": "Odobrať zo zoznamu",
"lists.delete": "Vymazať list",
"lists.delete": "Vymaž list",
"lists.edit": "Uprav zoznam",
"lists.edit.submit": "Zmeň názov",
"lists.new.create": "Pridaj zoznam",
@ -259,7 +259,7 @@
"notifications.column_settings.mention": "Zmienenia:",
"notifications.column_settings.poll": "Výsledky ankiet:",
"notifications.column_settings.push": "Push notifikácie",
"notifications.column_settings.reblog": "Boosty:",
"notifications.column_settings.reblog": "Vyzdvihnutia:",
"notifications.column_settings.show": "Zobraziť v stĺpci",
"notifications.column_settings.sound": "Prehrať zvuk",
"notifications.filter.all": "Všetky",
@ -278,7 +278,7 @@
"privacy.change": "Uprav súkromie príspevku",
"privacy.direct.long": "Pošli iba spomenutým používateľom",
"privacy.direct.short": "Súkromne",
"privacy.private.long": "Poslať iba následovateľom",
"privacy.private.long": "Pošli iba následovateľom",
"privacy.private.short": "Iba pre sledujúcich",
"privacy.public.long": "Poslať všetkým verejne",
"privacy.public.short": "Verejné",
@ -311,9 +311,9 @@
"search_results.total": "{count, number} {count, plural, one {výsledok} many {výsledkov} other {výsledky}}",
"status.admin_account": "Otvor moderovacie rozhranie užívateľa @{name}",
"status.admin_status": "Otvor tento príspevok v moderovacom rozhraní",
"status.block": "Blokovať @{name}",
"status.block": "Blokuj @{name}",
"status.cancel_reblog_private": "Nezdieľaj",
"status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý",
"status.cannot_reblog": "Tento príspevok nemôže byť zdieľaný",
"status.copy": "Skopíruj odkaz na príspevok",
"status.delete": "Zmazať",
"status.detailed_status": "Podrobný náhľad celej konverzácie",
@ -325,14 +325,14 @@
"status.media_hidden": "Skryté médiá",
"status.mention": "Spomeň @{name}",
"status.more": "Viac",
"status.mute": "Utíš @{name}",
"status.mute_conversation": "Ignorovať konverzáciu",
"status.mute": "Utíš @{name}",
"status.mute_conversation": "Ignoruj konverzáciu",
"status.open": "Otvoriť tento status",
"status.pin": "Pripni na profil",
"status.pinned": "Pripnutý príspevok",
"status.read_more": "Čítaj ďalej",
"status.reblog": "Povýšiť",
"status.reblog_private": "Povýš k pôvodnému publiku",
"status.reblog_private": "Vyzdvihni k pôvodnému publiku",
"status.reblogged_by": "{name} povýšil/a",
"status.reblogs.empty": "Nikto ešte nepovýšil tento príspevok. Keď tak niekto urobí, bude to zobrazené práve tu.",
"status.redraft": "Vymaž a prepíš",
@ -341,7 +341,7 @@
"status.report": "Nahlásiť @{name}",
"status.sensitive_toggle": "Klikni pre zobrazenie",
"status.sensitive_warning": "Chúlostivý obsah",
"status.share": "Zdieľať",
"status.share": "Zdieľaj",
"status.show_less": "Zobraz menej",
"status.show_less_all": "Všetkým ukáž menej",
"status.show_more": "Ukáž viac",
@ -353,8 +353,8 @@
"suggestions.header": "Mohlo by ťa zaujímať…",
"tabs_bar.federated_timeline": "Federovaná",
"tabs_bar.home": "Domov",
"tabs_bar.local_timeline": "Lokálna",
"tabs_bar.notifications": "Notifikácie",
"tabs_bar.local_timeline": "Miestna",
"tabs_bar.notifications": "Oboznámenia",
"tabs_bar.search": "Hľadaj",
"time_remaining.days": "Zostáva {number, plural, one {# deň} few {# dní} many {# dni} other {# dni}}",
"time_remaining.hours": "Zostáva {number, plural, one {# hodina} few {# hodín} many {# hodín} other {# hodiny}}",
@ -371,13 +371,13 @@
"upload_form.focus": "Pozmeň náhľad",
"upload_form.undo": "Vymaž",
"upload_progress.label": "Nahráva sa...",
"video.close": "Zavri video",
"video.close": "Zavri video",
"video.exit_fullscreen": "Vpnúť zobrazenie na celú obrazovku",
"video.expand": "Zväčšiť video",
"video.expand": "Zväčši video",
"video.fullscreen": "Zobraziť na celú obrazovku",
"video.hide": "Skryť video",
"video.mute": "Vypnúť zvuk",
"video.pause": "Pauza",
"video.play": "Prehrať",
"video.unmute": "Zapnúť zvuk"
"video.play": "Prehraj",
"video.unmute": "Zapni zvuk"
}

View File

@ -46,7 +46,8 @@
}
}
&:disabled {
&:disabled,
&.disabled {
background-color: $ui-primary-color;
cursor: default;
}

View File

@ -109,6 +109,23 @@
}
}
&:disabled,
&.disabled {
svg path:last-child {
fill: $ui-primary-color;
}
&:active,
&:focus,
&:hover {
background: $ui-primary-color;
svg path:last-child {
fill: $ui-primary-color;
}
}
}
&.button--destructive {
&:active,
&:focus,

View File

@ -34,7 +34,7 @@ class ProofProvider::Keybase::ConfigSerializer < ActiveModel::Serializer
end
def username
{ min: 1, max: 30, re: '[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?' }
{ min: 1, max: 30, re: '[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?' }
end
def prefill_url

View File

@ -102,7 +102,6 @@ class Account < ApplicationRecord
scope :tagged_with, ->(tag) { joins(:accounts_tags).where(accounts_tags: { tag_id: tag }) }
scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc')) }
scope :popular, -> { order('account_stats.followers_count desc') }
scope :without_blocking, ->(account) { account.nil? ? all : where.not(id: Block.where(target_account_id: account.id).pluck(:account_id)) }
delegate :email,
:unconfirmed_email,

View File

@ -13,7 +13,7 @@ module AccountFinderConcern
end
def representative
find_local(Setting.site_contact_username.gsub(/\A@/, '')) || Account.local.find_by(suspended: false)
find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')) || Account.local.find_by(suspended: false)
end
def find_local(username)

View File

@ -22,7 +22,11 @@ class Export
end
def to_following_accounts_csv
to_csv account.following.select(:username, :domain)
CSV.generate(headers: ['Account address', 'Show boosts'], write_headers: true) do |csv|
account.active_relationships.includes(:target_account).reorder(id: :desc).each do |follow|
csv << [acct(follow.target_account), follow.show_reblogs]
end
end
end
def to_lists_csv

View File

@ -2,6 +2,7 @@
class Form::AccountBatch
include ActiveModel::Model
include Authorization
attr_accessor :account_ids, :action, :current_account
@ -13,6 +14,10 @@ class Form::AccountBatch
remove_from_followers!
when 'block_domains'
block_domains!
when 'approve'
approve!
when 'reject'
reject!
end
end
@ -57,4 +62,18 @@ class Form::AccountBatch
ActivityPub::DeliveryWorker.perform_async(json, current_account.id, follow.account.inbox_url)
end
def approve!
users = accounts.includes(:user).map(&:user)
users.each { |user| authorize(user, :approve?) }
.each(&:approve!)
end
def reject!
records = accounts.includes(:user)
records.each { |account| authorize(account.user, :reject?) }
.each { |account| SuspendAccountService.new.call(account, including_user: true, destroy: true, skip_distribution: true) }
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class AccountRelationshipsPresenter
attr_reader :following, :followed_by, :blocking,
attr_reader :following, :followed_by, :blocking, :blocked_by,
:muting, :requested, :domain_blocking,
:endorsed
@ -12,6 +12,7 @@ class AccountRelationshipsPresenter
@following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
@followed_by = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id))
@blocking = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id))
@blocked_by = cached[:blocked_by].merge(Account.blocked_by_map(@uncached_account_ids, @current_account_id))
@muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id))
@requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id))
@domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id))
@ -22,6 +23,7 @@ class AccountRelationshipsPresenter
@following.merge!(options[:following_map] || {})
@followed_by.merge!(options[:followed_by_map] || {})
@blocking.merge!(options[:blocking_map] || {})
@blocked_by.merge!(options[:blocked_by_map] || {})
@muting.merge!(options[:muting_map] || {})
@requested.merge!(options[:requested_map] || {})
@domain_blocking.merge!(options[:domain_blocking_map] || {})
@ -37,6 +39,7 @@ class AccountRelationshipsPresenter
following: {},
followed_by: {},
blocking: {},
blocked_by: {},
muting: {},
requested: {},
domain_blocking: {},
@ -64,6 +67,7 @@ class AccountRelationshipsPresenter
following: { account_id => following[account_id] },
followed_by: { account_id => followed_by[account_id] },
blocking: { account_id => blocking[account_id] },
blocked_by: { account_id => blocked_by[account_id] },
muting: { account_id => muting[account_id] },
requested: { account_id => requested[account_id] },
domain_blocking: { account_id => domain_blocking[account_id] },

View File

@ -13,7 +13,7 @@ class InstancePresenter
)
def contact_account
Account.find_local(Setting.site_contact_username.gsub(/\A@/, ''))
Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, ''))
end
def user_count

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class REST::RelationshipSerializer < ActiveModel::Serializer
attributes :id, :following, :showing_reblogs, :followed_by, :blocking,
attributes :id, :following, :showing_reblogs, :followed_by, :blocking, :blocked_by,
:muting, :muting_notifications, :requested, :domain_blocking,
:endorsed
@ -27,6 +27,10 @@ class REST::RelationshipSerializer < ActiveModel::Serializer
instance_options[:relationships].blocking[object.id] || false
end
def blocked_by
instance_options[:relationships].blocked_by[object.id] || false
end
def muting
instance_options[:relationships].muting[object.id] ? true : false
end

View File

@ -10,15 +10,7 @@ class AccountSearchService < BaseService
@options = options
@account = account
results = search_service_results
unless account.nil?
account_ids = results.map(&:id)
blocked_by_map = Account.blocked_by_map(account_ids, account.id)
results.reject! { |item| blocked_by_map[item.id] }
end
results
search_service_results
end
private

View File

@ -25,7 +25,7 @@ class ImportService < BaseService
def import_follows!
parse_import_data!(['Account address'])
import_relationships!('follow', 'unfollow', @account.following, follow_limit)
import_relationships!('follow', 'unfollow', @account.following, follow_limit, reblogs: 'Show boosts')
end
def import_blocks!
@ -35,7 +35,7 @@ class ImportService < BaseService
def import_mutes!
parse_import_data!(['Account address'])
import_relationships!('mute', 'unmute', @account.muting, ROWS_PROCESSING_LIMIT)
import_relationships!('mute', 'unmute', @account.muting, ROWS_PROCESSING_LIMIT, notifications: 'Hide notifications')
end
def import_domain_blocks!
@ -63,8 +63,8 @@ class ImportService < BaseService
end
end
def import_relationships!(action, undo_action, overwrite_scope, limit)
items = @data.take(limit).map { |row| [row['Account address']&.strip, row['Hide notifications']&.strip] }.reject { |(id, _)| id.blank? }
def import_relationships!(action, undo_action, overwrite_scope, limit, extra_fields = {})
items = @data.take(limit).map { |row| [row['Account address']&.strip, Hash[extra_fields.map { |key, header| [key, row[header]&.strip] }]] }.reject { |(id, _)| id.blank? }
if @import.overwrite?
presence_hash = items.each_with_object({}) { |(id, extra), mapping| mapping[id] = [true, extra] }
@ -73,7 +73,7 @@ class ImportService < BaseService
if presence_hash[target_account.acct]
items.delete(target_account.acct)
extra = presence_hash[target_account.acct][1]
Import::RelationshipWorker.perform_async(@account.id, target_account.acct, action, ActiveModel::Type::Boolean.new.cast(extra))
Import::RelationshipWorker.perform_async(@account.id, target_account.acct, action, extra)
else
Import::RelationshipWorker.perform_async(@account.id, target_account.acct, undo_action)
end
@ -81,7 +81,7 @@ class ImportService < BaseService
end
Import::RelationshipWorker.push_bulk(items) do |acct, extra|
[@account.id, acct, action, ActiveModel::Type::Boolean.new.cast(extra)]
[@account.id, acct, action, extra]
end
end

View File

@ -12,8 +12,6 @@ class SearchService < BaseService
default_results.tap do |results|
if url_query?
results.merge!(url_resource_results) unless url_resource.nil?
results[:accounts].reject! { |item| item.blocking?(@account) }
results[:statuses].reject! { |status| StatusFilter.new(status, @account).filtered? }
elsif @query.present?
results[:accounts] = perform_accounts_search! if account_searchable?
results[:statuses] = perform_statuses_search! if full_text_searchable?

View File

@ -5,16 +5,10 @@ class ExistingUsernameValidator < ActiveModel::EachValidator
return if value.blank?
if options[:multiple]
missing_usernames = value.split(',').map { |username| username unless Account.find_local(username) }.compact
missing_usernames = value.split(',').map { |username| username.strip.gsub(/\A@/, '') }.map { |username| username unless Account.find_local(username) }.compact
record.errors.add(attribute, I18n.t('existing_username_validator.not_found_multiple', usernames: missing_usernames.join(', '))) if missing_usernames.any?
else
record.errors.add(attribute, I18n.t('existing_username_validator.not_found')) unless Account.find_local(value)
record.errors.add(attribute, I18n.t('existing_username_validator.not_found')) unless Account.find_local(value.strip.gsub(/\A@/, ''))
end
end
private
def valid_html?(str)
Nokogiri::HTML.fragment(str).to_s == str
end
end

View File

@ -33,7 +33,9 @@
= active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account)
= active_link_to t('accounts.media'), short_account_media_url(@account)
- if @statuses.empty?
- if user_signed_in? && @account.blocking?(current_account)
.nothing-here.nothing-here--under-tabs= t('accounts.unavailable')
- elsif @statuses.empty?
= nothing_here 'nothing-here--under-tabs'
- else
.activity-stream

View File

@ -10,7 +10,7 @@
.filter-subset
%strong= t('admin.accounts.moderation.title')
%ul
%li= filter_link_to t('admin.accounts.moderation.pending'), pending: '1', silenced: nil, suspended: nil
%li= link_to safe_join([t('admin.accounts.moderation.pending'), "(#{number_with_delimiter(User.pending.count)})"], ' '), admin_pending_accounts_path
%li= filter_link_to t('admin.accounts.moderation.active'), silenced: nil, suspended: nil, pending: nil
%li= filter_link_to t('admin.accounts.moderation.silenced'), silenced: '1', suspended: nil, pending: nil
%li= filter_link_to t('admin.accounts.moderation.suspended'), suspended: '1', silenced: nil, pending: nil

View File

@ -0,0 +1,14 @@
.batch-table__row
%label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
= f.check_box :account_ids, { multiple: true, include_hidden: false }, account.id
.batch-table__row__content.batch-table__row__content--unpadded
%table.accounts-table
%tbody
%tr
%td
= account.user_email
= "(@#{account.username})"
%br/
= account.user_current_sign_in_ip
%td.accounts-table__count
= table_link_to 'pencil', t('admin.accounts.edit'), admin_account_path(account.id)

View File

@ -0,0 +1,33 @@
- content_for :page_title do
= t('admin.pending_accounts.title', count: User.pending.count)
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= form_for(@form, url: admin_pending_accounts_path, method: :patch) do |f|
= hidden_field_tag :page, params[:page] || 1
.batch-table
.batch-table__toolbar
%label.batch-table__toolbar__select.batch-checkbox-all
= check_box_tag :batch_checkbox_all, nil, false
.batch-table__toolbar__actions
= f.button safe_join([fa_icon('check'), t('admin.accounts.approve')]), name: :approve, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
= f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
.batch-table__body
- if @accounts.empty?
= nothing_here 'nothing-here--under-tabs'
- else
= render partial: 'account', collection: @accounts, locals: { f: f }
= paginate @accounts
%hr.spacer/
%div{ style: 'overflow: hidden' }
%div{ style: 'float: right' }
= link_to t('admin.accounts.reject_all'), reject_all_admin_pending_accounts_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive'
%div
= link_to t('admin.accounts.approve_all'), approve_all_admin_pending_accounts_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button'

View File

@ -9,6 +9,8 @@
- if @account.user_hides_network?
.nothing-here= t('accounts.network_hidden')
- elsif user_signed_in? && @account.blocking?(current_account)
.nothing-here= t('accounts.unavailable')
- elsif @follows.empty?
= nothing_here
- else

View File

@ -9,6 +9,8 @@
- if @account.user_hides_network?
.nothing-here= t('accounts.network_hidden')
- elsif user_signed_in? && @account.blocking?(current_account)
.nothing-here= t('accounts.unavailable')
- elsif @follows.empty?
= nothing_here
- else

View File

@ -5,15 +5,16 @@ class Import::RelationshipWorker
sidekiq_options queue: 'pull', retry: 8, dead: false
def perform(account_id, target_account_uri, relationship, extra = nil)
def perform(account_id, target_account_uri, relationship, options = {})
from_account = Account.find(account_id)
target_account = ResolveAccountService.new.call(target_account_uri)
options.symbolize_keys!
return if target_account.nil?
case relationship
when 'follow'
FollowService.new.call(from_account, target_account)
FollowService.new.call(from_account, target_account, options)
when 'unfollow'
UnfollowService.new.call(from_account, target_account)
when 'block'
@ -21,7 +22,7 @@ class Import::RelationshipWorker
when 'unblock'
UnblockService.new.call(from_account, target_account)
when 'mute'
MuteService.new.call(from_account, target_account, notifications: extra)
MuteService.new.call(from_account, target_account, options)
when 'unmute'
UnmuteService.new.call(from_account, target_account)
end

View File

@ -57,6 +57,10 @@ class Rack::Attack
req.authenticated_user_id if req.post? && req.path.start_with?('/api/v1/media')
end
throttle('throttle_media_proxy', limit: 30, period: 30.minutes) do |req|
req.ip if req.path.start_with?('/media_proxy')
end
throttle('throttle_api_sign_up', limit: 5, period: 30.minutes) do |req|
req.ip if req.post? && req.path == '/api/v1/accounts'
end

View File

@ -4,7 +4,7 @@ ja:
attributes:
poll:
expires_at: 期限
options: 選択肢
options: 項目
user:
email: メールアドレス
errors:

View File

@ -1,6 +1,9 @@
---
nl:
activerecord:
attributes:
status:
owned_poll: Poll
errors:
models:
account:

View File

@ -68,6 +68,7 @@ co:
admin: Amministratore
bot: Bot
moderator: Muderatore
unavailable: Prufile micca dispunibule
unfollow: Ùn siguità più
admin:
account_actions:
@ -80,6 +81,7 @@ co:
destroyed_msg: Nota di muderazione sguassata!
accounts:
approve: Appruvà
approve_all: Appruvà tutti
are_you_sure: Site sicuru·a?
avatar: Ritrattu di prufile
by_domain: Duminiu
@ -144,6 +146,7 @@ co:
push_subscription_expires: Spirata di labbunamentu PuSH
redownload: Mette à ghjornu u prufile
reject: Righjittà
reject_all: Righjittà tutti
remove_avatar: Toglie lavatar
remove_header: Toglie l'intistatura
resend_confirmation:
@ -330,6 +333,8 @@ co:
expired: Spirati
title: Filtrà
title: Invitazione
pending_accounts:
title: Conti in attesa (%{count})
relays:
add_new: Aghjustà un ripetitore
delete: Sguassà
@ -633,7 +638,7 @@ co:
all: Tuttu
changes_saved_msg: Cambiamenti salvati!
copy: Cupià
order_by: Urdinà per
order_by: Urdinà da
save_changes: Salvà e mudificazione
validation_errors:
one: Qualcosa ùn và bè! Verificate u prublemu quì sottu

View File

@ -23,7 +23,7 @@ cs:
federation_hint_html: S účtem na %{instance} můžete sledovat lidi na jakémkoliv serveru Mastodon a jiných službách.
generic_description: "%{domain} je jedním ze serverů v síti"
get_apps: Vyzkoušejte mobilní aplikaci
hosted_on: Server Mastodon na adrese %{domain}
hosted_on: Mastodon na adrese %{domain}
learn_more: Zjistit více
privacy_policy: Zásady soukromí
see_whats_happening: Podívejte se, co se děje
@ -72,6 +72,7 @@ cs:
admin: Administrátor
bot: Robot
moderator: Moderátor
unavailable: Profil nedostupný
unfollow: Přestat sledovat
admin:
account_actions:
@ -84,6 +85,7 @@ cs:
destroyed_msg: Moderátorská poznámka byla úspěšně zničena!
accounts:
approve: Schválit
approve_all: Schválit vše
are_you_sure: Jste si jistý/á?
avatar: Avatar
by_domain: Doména
@ -148,6 +150,7 @@ cs:
push_subscription_expires: Odebírání PuSH expiruje
redownload: Obnovit profil
reject: Zamítnout
reject_all: Zamítnout vše
remove_avatar: Odstranit avatar
remove_header: Odstranit záhlaví
resend_confirmation:
@ -336,6 +339,8 @@ cs:
expired: Vypršelé
title: Filtrovat
title: Pozvánky
pending_accounts:
title: Čekající účty (%{count})
relays:
add_new: Přidat nový most
delete: Smazat
@ -764,6 +769,7 @@ cs:
quadrillion: bld
thousand: tis
trillion: bil
unit: ''
pagination:
newer: Novější
next: Další
@ -1058,8 +1064,8 @@ cs:
edit_profile_action: Nastavit profil
edit_profile_step: Můžete si přizpůsobit svůj profil nahráním avataru a obrázku záhlaví, změnou zobrazovaného jména a dalších. Chcete-li posoudit nové sledující předtím, než vás mohou sledovat, můžete svůj účet uzamknout.
explanation: Zde je pár tipů na začátek
final_action: Začněte přispívat
final_step: 'Začněte psát! I když nemáte sledující, mohou vaše zprávy vidět jiní lidé, například na místní časové ose a mezi hashtagy. Můžete se ostatním představit pomocí hashtagu #introductions.'
final_action: Začít psát
final_step: 'Začněte psát! I když nemáte sledující, mohou vaše zprávy vidět jiní lidé, například na místní časové ose a v hashtazích. Můžete se ostatním představit pomocí hashtagu #introductions.'
full_handle: Vaše celá adresa profilu
full_handle_hint: Tohle je, co byste řekl/a svým přátelům, aby vám mohli posílat zprávy nebo vás sledovat z jiného serveru.
review_preferences_action: Změnit nastavení

View File

@ -3,7 +3,7 @@ de:
about:
about_hashtag_html: Dies sind öffentliche Beiträge, die mit <strong>#%{hashtag}</strong> getaggt wurden. Wenn du irgendwo im Fediversum ein Konto besitzt, kannst du mit ihnen interagieren.
about_mastodon_html: Mastodon ist ein soziales Netzwerk. Es basiert auf offenen Web-Protokollen und freier, quelloffener Software. Es ist dezentral (so wie E-Mail!).
about_this: Über diese Instanz
about_this: Über diesen Server
active_count_after: aktiv
active_footnote: Monatlich Aktive User (MAU)
administered_by: 'Administriert von:'
@ -11,7 +11,7 @@ de:
apps: Mobile Apps
apps_platforms: Benutze Mastodon auf iOS, Android und anderen Plattformen
browse_directory: Durchsuche ein Profilverzeichnis und filtere nach Interessen
browse_public_posts: Durchsuche einen Zeitleiste an öffentlichen Beiträgen auf Mastodon
browse_public_posts: Durchsuche eine Zeitleiste an öffentlichen Beiträgen auf Mastodon
contact: Kontakt
contact_missing: Nicht angegeben
contact_unavailable: N/A
@ -68,6 +68,7 @@ de:
admin: Admin
bot: Bot
moderator: Moderator
unavailable: Profil nicht verfügbar
unfollow: Entfolgen
admin:
account_actions:
@ -80,6 +81,7 @@ de:
destroyed_msg: Moderationsnotiz erfolgreich gelöscht!
accounts:
approve: Aktzeptieren
approve_all: Alle aktzeptieren
are_you_sure: Bist du sicher?
avatar: Profilbild
by_domain: Domain
@ -144,6 +146,7 @@ de:
push_subscription_expires: PuSH-Abonnement läuft aus
redownload: Profil neu laden
reject: Ablehnen
reject_all: Alle ablehnen
remove_avatar: Profilbild entfernen
remove_header: Header entfernen
resend_confirmation:
@ -329,6 +332,8 @@ de:
expired: Ausgelaufen
title: Filter
title: Einladungen
pending_accounts:
title: Ausstehende Konten (%{count})
relays:
add_new: Neues Relay hinzufügen
delete: Löschen
@ -422,17 +427,17 @@ de:
open: Jeder kann sich registrieren
title: Registrierungsmodus
show_known_fediverse_at_about_page:
desc_html: Wenn aktiviert, wird es alle Beiträge aus dem bereits bekannten Teil des Fediversums auf der Startseite anzeigen. Andernfalls werden lokale Beitrage der Instanz angezeigt.
desc_html: Wenn aktiviert, wird es alle Beiträge aus dem bereits bekannten Teil des Fediversums auf der Startseite anzeigen. Andernfalls werden lokale Beitrage des Servers angezeigt.
title: Verwende öffentliche Zeitleiste für die Vorschau
show_staff_badge:
desc_html: Zeige Mitarbeiter-Badge auf Benutzerseite
title: Zeige Mitarbeiter-Badge
site_description:
desc_html: Einleitungsabschnitt auf der Frontseite. Beschreibe, was diese Mastodon-Instanz ausmacht. Du kannst HTML-Tags benutzen, insbesondere <code>&lt;a&gt;</code> und <code>&lt;em&gt;</code>.
desc_html: Einleitungsabschnitt auf der Frontseite. Beschreibe, was diesen Mastodon-Server ausmacht. Du kannst HTML-Tags benutzen, insbesondere <code>&lt;a&gt;</code> und <code>&lt;em&gt;</code>.
title: Beschreibung des Servers
site_description_extended:
desc_html: Bietet sich für Verhaltenskodizes, Regeln, Richtlinien und weiteres an, was deinen Server auszeichnet. Du kannst HTML-Tags benutzen
title: Erweiterte Beschreibung der Instanz
title: Erweiterte Beschreibung des Servers
site_short_description:
desc_html: Wird angezeigt in der Seitenleiste und in Meta-Tags. Beschreibe in einem einzigen Abschnitt, was Mastodon ist und was diesen Server ausmacht. Falls leer, wird die Server-Beschreibung verwendet.
title: Kurze Server-Beschreibung
@ -446,7 +451,7 @@ de:
timeline_preview:
desc_html: Auf der Frontseite die öffentliche Zeitleiste anzeigen
title: Zeitleisten-Vorschau
title: Instanz-Einstellungen
title: Server-Einstellungen
statuses:
back_to_account: Zurück zum Konto
batch:
@ -530,7 +535,7 @@ de:
reset_password: Passwort zurücksetzen
security: Sicherheit
set_new_password: Neues Passwort setzen
trouble_logging_in: Schwierigkeiten beim anmelden?
trouble_logging_in: Schwierigkeiten beim Anmelden?
authorize_follow:
already_following: Du folgst diesem Konto bereits
error: Das Remote-Konto konnte nicht geladen werden
@ -632,6 +637,7 @@ de:
all: Alle
changes_saved_msg: Änderungen gespeichert!
copy: Kopieren
order_by: Sortieren nach
save_changes: Änderungen speichern
validation_errors:
one: Etwas ist noch nicht ganz richtig! Bitte korrigiere den Fehler
@ -773,6 +779,8 @@ de:
relationships:
activity: Kontoaktivität
dormant: Inaktiv
last_active: Zuletzt aktiv
most_recent: Neuste
moved: Umgezogen
mutual: Bekannt
primary: Primär
@ -982,7 +990,7 @@ de:
<h3 id="changes">Änderung an unserer Datenschutzerklärung</h3>
<p>Wenn wir uns entscheiden, Änderungen an unserer Datenschutzerklärung vorzunehmen, werden wird diese Änderungen auf dieser Seite bekannt gegeben.</p>
<p>Wenn wir uns entscheiden, Änderungen an unserer Datenschutzerklärung vorzunehmen, werden wir diese Änderungen auf dieser Seite bekannt gegeben.</p>
<p>Dies ist eine Übersetzung, Irrtümer und Übersetzungsfehler vorbehalten. Im Zweifelsfall gilt die englische Originalversion.</p>

View File

@ -8,10 +8,10 @@ fr:
failure:
already_authenticated: Vous êtes déjà connecté⋅e.
inactive: Votre compte nest pas encore activé.
invalid: "%{authentication_keys} invalide."
invalid: "%{authentication_keys} ou mot de passe invalide."
last_attempt: Vous avez droit à une tentative avant que votre compte ne soit verrouillé.
locked: Votre compte est verrouillé.
not_found_in_database: "%{authentication_keys} invalide."
not_found_in_database: "%{authentication_keys} ou mot de passe invalide."
timeout: Votre session a expiré. Veuillez vous reconnecter pour continuer.
unauthenticated: Vous devez vous connecter ou vous inscrire pour continuer.
unconfirmed: Vous devez valider votre compte pour continuer.
@ -20,17 +20,17 @@ fr:
action: Vérifier ladresse courriel
action_with_app: Confirmer et retourner à %{app}
explanation: Vous avez créé un compte sur %{host} avec cette adresse courriel. Vous êtes à un clic de lactiver. Si ce nétait pas vous, veuillez ignorer ce courriel.
extra_html: Merci de consultez également <a href="%{terms_path}">les règles de linstance</a> et <a href="%{policy_path}">nos conditions dutilisation</a>.
extra_html: Merci de consultez également <a href="%{terms_path}">les règles du serveur</a> et <a href="%{policy_path}">nos conditions dutilisation</a>.
subject: 'Mastodon : Merci de confirmer votre inscription sur %{instance}'
title: Vérifier ladresse courriel
email_changed:
explanation: 'Ladresse courriel de votre compte est en cours de modification pour devenir :'
extra: Si vous navez pas changé votre adresse courriel, il est probable que quelquun ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter ladministrateur·rice de linstance si vous êtes bloqué·e hors de votre compte.
extra: Si vous navez pas changé votre adresse courriel, il est probable que quelquun ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter ladministrateur·rice du serveur si vous êtes bloqué·e hors de votre compte.
subject: 'Mastodon : Courriel modifié'
title: Nouvelle adresse courriel
password_change:
explanation: Le mot de passe de votre compte a été changé.
extra: Si vous navez pas changé votre mot de passe, il est probable que quelquun ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter ladministrateur·rice de linstance si vous êtes bloqué·e hors de votre compte.
extra: Si vous navez pas changé votre mot de passe, il est probable que quelquun ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter ladministrateur·rice du serveur si vous êtes bloqué·e hors de votre compte.
subject: 'Mastodon : Votre mot de passe a été modifié avec succès'
title: Mot de passe modifié
reconfirmation_instructions:

View File

@ -13,6 +13,7 @@ nl:
last_attempt: Je hebt nog één poging over voordat jouw account wordt opgeschort.
locked: Jouw account is opgeschort.
not_found_in_database: "%{authentication_keys} of wachtwoord ongeldig."
pending: Jouw account moet nog steeds worden beoordeeld.
timeout: Jouw sessie is verlopen, log opnieuw in.
unauthenticated: Je dient in te loggen of te registreren.
unconfirmed: Je dient eerst jouw account te bevestigen.
@ -21,6 +22,7 @@ nl:
action: E-mailadres verifiëren
action_with_app: Bevestigen en naar %{app} teruggaan
explanation: Je hebt een account op %{host} aangemaakt en met één klik kun je deze activeren. Wanneer jij dit account niet hebt aangemaakt, mag je deze e-mail negeren.
explanation_when_pending: Je vroeg met dit e-mailadres een uitnodiging aan voor %{host}. Nadat je jouw e-mailadres hebt bevestigd, beoordelen we jouw aanvraag. Je kunt tot dan nog niet inloggen. Wanneer jouw aanvraag wordt afgekeurd, worden jouw gegevens verwijderd en hoef je daarna verder niets meer te doen. Wanneer jij dit niet was, kun je deze e-mail negeren.
extra_html: Bekijk ook de <a href="%{terms_path}">regels van de Mastodonserver</a> en <a href="%{policy_path}">onze gebruiksvoorwaarden</a>.
subject: 'Mastodon: E-mail bevestigen voor %{instance}'
title: E-mailadres verifiëren
@ -61,6 +63,7 @@ nl:
signed_up: Je bent geregistreerd.
signed_up_but_inactive: Je bent geregistreerd. Je kon alleen niet automatisch ingelogd worden omdat jouw account nog niet geactiveerd is.
signed_up_but_locked: Je bent ingeschreven. Je kon alleen niet automatisch ingelogd worden omdat jouw account is opgeschort.
signed_up_but_pending: Er is een bericht met een bevestigingslink naar jouw e-mailadres verzonden. Nadat je op deze link hebt geklikt nemen we jouw aanvraag in behandeling. Je wordt op de hoogte gesteld wanneer deze wordt goedgekeurd.
signed_up_but_unconfirmed: Je ontvangt via e-mail instructies hoe je jouw account kunt activeren. Kijk tussen je spam wanneer niks werd ontvangen.
update_needs_confirmation: Je hebt je e-mailadres succesvol gewijzigd, maar we moeten je nieuwe mailadres nog bevestigen. Controleer jouw e-mail en klik op de link in de mail om jouw e-mailadres te bevestigen. Kijk tussen je spam wanneer niks werd ontvangen.
updated: Jouw accountgegevens zijn opgeslagen.

View File

@ -12,6 +12,7 @@ pl:
last_attempt: Masz jeszcze jedną próbę; Twoje konto zostanie zablokowane jeśli się nie powiedzie.
locked: Twoje konto zostało zablokowane.
not_found_in_database: Nieprawidłowy %{authentication_keys} lub hasło.
pending: Twoje konto oczekuje na przegląd.
timeout: Twoja sesja wygasła. Zaloguj się ponownie, aby kontynuować..
unauthenticated: Zapisz się lub zaloguj, aby kontynuować.
unconfirmed: Zweryfikuj adres e-mail, aby kontynuować.
@ -20,6 +21,7 @@ pl:
action: Zweryfikuj adres e-mail
action_with_app: Potwierdź i wróć do %{app}
explanation: Utworzyłeś(-aś) konto na %{host} podając ten adres e-mail. Jedno kliknięcie dzieli Cię od aktywacji tego konta. Jeżeli to nie Ty, zignoruj ten e-mail.
explanation_when_pending: Poprosiłeś(-aś) o zaproszenie na %{host} używajac tego adresu e-mail. Kiedy potwierdzisz swój adres e-mail, przejrzymy Twoje podanie. Do tego czasu nie możesz się zalogować. Jeżeli Twoje podanie zostanie odrzucone, Twoje dane zostaną usunięte i nie będziesz musiał(-a) podejmować żadnych dodatkowych działań. Jeżeli to nie Ty, zignoruj ten e-mail.
extra_html: Przeczytaj też <a href="%{terms_path}">regulamin serwera</a> i <a href="%{policy_path}">nasze zasady użytkowania</a>.
subject: 'Mastodon: Instrukcje weryfikacji adresu e-mail na %{instance}'
title: Zweryfikuj adres e-mail
@ -60,6 +62,7 @@ pl:
signed_up: Witamy! Twoje konto zostało utworzone.
signed_up_but_inactive: Twoje konto zostało utworzone. Nie mogliśmy Cię jednak zalogować, ponieważ konto nie zostało jeszcze aktywowane.
signed_up_but_locked: Twoje konto zostało utworzone. Nie mogliśmy Cię jednak zalogować, ponieważ konto jest zablokowane.
signed_up_but_pending: Na Twój adres e-mail została wysłana wiadomosć z odnośnikiem potwierdzającym. Po kliknięciu w odnośnik, przejrzymy Twoje podanie. Zostaniesz poinformowany(-a), gdy zostanie ono przyjęte.
signed_up_but_unconfirmed: Na Twój adres e-mail została wysłana wiadomosć z odnośnikiem potwierdzającym. Kliknij w odnośnik, aby aktywować konto. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem.
update_needs_confirmation: Konto zostało zaktualizowane, musimy jednak zweryfikować Twój nowy adres e-mail. Została na niego wysłana wiadomość z odnośnikiem potwierdzającym. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem.
updated: Konto zostało zaktualizowane.

View File

@ -79,7 +79,7 @@ cs:
messages:
access_denied: Vlastník zdroje či autorizační server zamítl požadavek.
credential_flow_not_configured: Proud Resource Owner Password Credentials selhal, protože Doorkeeper.configure.resource_owner_from_credentials nebylo nakonfigurováno.
invalid_client: Ověření klienta selhalo kvůli neznámému klientovi, chybějící klientské autentikaci či nepodporované autentikační metodě.
invalid_client: Ověření klienta selhalo kvůli neznámému klientovi, chybějící klientské autentizaci či nepodporované autentizační metodě.
invalid_grant: Poskytnuté oprávnění je neplatné, vypršelé, zamítnuté, neshoduje se s URI přesměrování použitým v požadavku o autorizaci, nebo bylo uděleno jinému klientu.
invalid_redirect_uri: Přesměrovací URI není platné.
invalid_request: Požadavku chybí pžadovaný parametr, obsahuje nepodporovanou hodnotu parametru, či je jinak malformovaný.

View File

@ -68,6 +68,7 @@ en:
admin: Admin
bot: Bot
moderator: Mod
unavailable: Profile unavailable
unfollow: Unfollow
admin:
account_actions:
@ -80,6 +81,7 @@ en:
destroyed_msg: Moderation note successfully destroyed!
accounts:
approve: Approve
approve_all: Approve all
are_you_sure: Are you sure?
avatar: Avatar
by_domain: Domain
@ -144,6 +146,7 @@ en:
push_subscription_expires: PuSH subscription expires
redownload: Refresh profile
reject: Reject
reject_all: Reject all
remove_avatar: Remove avatar
remove_header: Remove header
resend_confirmation:
@ -330,6 +333,8 @@ en:
expired: Expired
title: Filter
title: Invites
pending_accounts:
title: Pending accounts (%{count})
relays:
add_new: Add new relay
delete: Delete
@ -499,7 +504,7 @@ en:
salutation: "%{name},"
settings: 'Change e-mail preferences: %{link}'
view: 'View:'
view_profile: View Profile
view_profile: View profile
view_status: View status
applications:
created: Application successfully created

View File

@ -15,7 +15,7 @@ fr:
<h3>Un bon endroit pour les règles</h3>
<p>La description étendue na pas été remplie.</p>
generic_description: "%{domain} est seulement un serveur du réseau"
hosted_on: Instance Mastodon hébergée par %{domain}
hosted_on: Serveur Mastodon hébergée par %{domain}
learn_more: En savoir plus
privacy_policy: Politique de vie privée
source_code: Code source
@ -317,7 +317,7 @@ fr:
relays:
add_new: Ajouter un nouveau relais
delete: Effacer
description_html: Un <strong>relai de fédération</strong> est un serveur intermédiaire qui échange de grandes quantités de pouets entre les serveurs qui publient dessus et ceux qui y sont abonnés. <strong>Il peut aider les petites et moyennes instances à découvrir du contenu sur le fediverse</strong>, ce qui normalement nécessiterait que les membres locaux suivent des gens inscrits sur des serveurs distants.
description_html: Un <strong>relai de fédération</strong> est un serveur intermédiaire qui échange de grandes quantités de pouets entre les serveurs qui publient dessus et ceux qui y sont abonnés. <strong>Il peut aider les petits et moyen serveurs à découvrir du contenu sur le fediverse</strong>, ce qui normalement nécessiterait que les membres locaux suivent des gens inscrits sur des serveurs distants.
disable: Désactiver
disabled: Désactivé
enable: Activé
@ -376,14 +376,14 @@ fr:
desc_html: Modifier l'apparence avec une CSS chargée sur chaque page
title: CSS personnalisé
hero:
desc_html: Affichée sur la page daccueil. Au moins 600x100px recommandé. Lorsquelle nest pas définie, se rabat sur la vignette de linstance
desc_html: Affichée sur la page daccueil. Au moins 600x100px recommandé. Lorsquelle nest pas définie, se rabat sur la vignette du serveur
title: Image den-tête
mascot:
desc_html: Affiché sur plusieurs pages. Au moins 293×205px recommandé. Lorsqu'il n'est pas défini, retombe à la mascotte par défaut
title: Image de la mascotte
peers_api_enabled:
desc_html: Noms des domaines que cette instance a découvert dans le fediverse
title: Publier la liste des instances découvertes
desc_html: Noms des domaines que ce serveur a découvert dans le fediverse
title: Publier la liste des serveurs découverts
preview_sensitive_media:
desc_html: Les liens de prévisualisation sur les autres sites web afficheront une vignette même si le média est sensible
title: Afficher les médias sensibles dans les prévisualisations OpenGraph
@ -401,31 +401,31 @@ fr:
disabled: Personne
title: Autoriser les invitations par
show_known_fediverse_at_about_page:
desc_html: Lorsque loption est activée, les pouets provenant de toutes les instances connues sont affichés dans la prévisualisation. Sinon, seuls les pouets locaux sont affichés.
desc_html: Lorsque loption est activée, les pouets provenant de toutes les serveurs connues sont affichés dans la prévisualisation. Sinon, seuls les pouets locaux sont affichés.
title: Afficher le fediverse connu dans la prévisualisation du fil
show_staff_badge:
desc_html: Montrer un badge de responsable sur une page utilisateur·ice
title: Montrer un badge de responsable
site_description:
desc_html: Paragraphe introductif sur la page daccueil. Décrivez ce qui rend spécifique ce serveur Mastodon et toute autre chose importante. Vous pouvez utiliser des balises HTML, en particulier <code>&lt;a&gt;</code> et <code>&lt;em&gt;</code>.
title: Description de l'instance
title: Description du serveur
site_description_extended:
desc_html: L'endroit idéal pour afficher votre code de conduite, les règles, les guides et autres choses qui rendent votre instance différente. Vous pouvez utiliser des balises HTML
title: Description étendue du site
desc_html: L'endroit idéal pour afficher votre code de conduite, les règles, les guides et autres choses qui rendent votre serveur différent. Vous pouvez utiliser des balises HTML
title: Description étendue du serveur
site_short_description:
desc_html: Affichée dans la barre latérale et dans les méta-tags. Décrivez ce qui rend spécifique cette instance Mastodon en un seul paragraphe. Si laissée vide, la description de linstance sera affiché par défaut.
title: Description courte de linstance
desc_html: Affichée dans la barre latérale et dans les méta-tags. Décrivez ce qui rend spécifique ce serveur Mastodon en un seul paragraphe. Si laissée vide, la description du serveur sera affiché par défaut.
title: Description courte du serveur
site_terms:
desc_html: Affichée sur la page des conditions dutilisation du site<br>Vous pouvez utiliser des balises HTML
title: Politique de confidentialité
site_title: Nom de l'instance
site_title: Nom du serveur
thumbnail:
desc_html: Utilisée pour les prévisualisations via OpenGraph et lAPI. 1200x630px recommandé
title: Vignette de linstance
title: Vignette du serveur
timeline_preview:
desc_html: Afficher le fil public sur la page daccueil
title: Prévisualisation du fil global
title: Paramètres du site
title: Paramètres du serveur
statuses:
back_to_account: Retour à la page du compte
batch:
@ -482,7 +482,7 @@ fr:
warning: Soyez prudent⋅e avec ces données. Ne les partagez pas!
your_token: Votre jeton daccès
auth:
agreement_html: En cliquant sur "S'inscrire" ci-dessous, vous souscrivez <a href="%{rules_path}">aux règles de linstance</a> et à <a href="%{terms_path}">nos conditions dutilisation</a>.
agreement_html: En cliquant sur "S'inscrire" ci-dessous, vous souscrivez <a href="%{rules_path}">aux règles du serveur</a> et à <a href="%{terms_path}">nos conditions dutilisation</a>.
change_password: Mot de passe
confirm_email: Confirmer mon adresse mail
delete_account: Supprimer le compte
@ -534,7 +534,7 @@ fr:
description_html: Cela va supprimer votre compte et le désactiver de manière <strong>permanente et irréversible</strong>. Votre nom dutilisateur⋅ice restera réservé afin déviter la confusion.
proceed: Supprimer compte
success_msg: Votre compte a été supprimé avec succès
warning_html: Seule la suppression du contenu depuis cette instance est garantie. Le contenu qui a été partagé est susceptible de laisser des traces. Les instances hors-ligne ainsi que ceux nétant plus abonnées à vos publications ne mettront pas leur base de données à jour.
warning_html: Seule la suppression du contenu depuis ce serveur est garantie. Le contenu qui a été partagé est susceptible de laisser des traces. Les serveurs hors-ligne ainsi que ceux nétant plus abonnées à vos publications ne mettront pas leur base de données à jour.
warning_title: Disponibilité du contenu disséminé
directories:
directory: Annuaire des profils
@ -610,11 +610,11 @@ fr:
merge_long: Garder les enregistrements existants et ajouter les nouveaux
overwrite: Réécrire
overwrite_long: Remplacer les enregistrements actuels par les nouveaux
preface: Vous pouvez importer certaines données que vous avez exporté d'une autre instance, comme une liste des personnes que vous suivez ou bloquez sur votre compte.
preface: Vous pouvez importer certaines données que vous avez exporté d'un autre serveur, comme une liste des personnes que vous suivez ou bloquez sur votre compte.
success: Vos données ont été importées avec succès et seront traitées en temps et en heure
types:
blocking: Liste dutilisateur⋅ice⋅s bloqué⋅e⋅s
domain_blocking: Liste des instances bloquées
domain_blocking: Liste des serveurs bloquées
following: Liste dutilisateur⋅ice⋅s suivi⋅e⋅s
muting: Liste dutilisateur⋅ice⋅s que vous masquez
upload: Importer
@ -636,7 +636,7 @@ fr:
one: 1 usage
other: "%{count} usages"
max_uses_prompt: Pas de limite
prompt: Générer et partager des liens avec les autres pour donner accès à cette instance
prompt: Générer et partager des liens avec les autres pour donner accès à ce serveur
table:
expires_at: Expire
uses: Utilise
@ -722,7 +722,7 @@ fr:
publishing: Publication
web: Web
remote_follow:
acct: Entrez ladresse profil@instance depuis laquelle vous voulez vous abonner
acct: Entrez ladresse profil@serveur depuis laquelle vous voulez vous abonner
missing_resource: LURL de redirection na pas pu être trouvée
no_account_html: Vous navez pas de compte? Vous pouvez <a href='%{sign_up_path}' target='_blank'>vous inscrire ici</a>
proceed: Confirmer labonnement
@ -978,7 +978,7 @@ fr:
final_action: Commencer à publier
final_step: 'Commencez à poster! Même sans abonné·e·s, vos messages publics peuvent être vus par dautres, par exemple sur le fil public local et dans les hashtags. Vous pouvez vous présenter sur le hashtag #introductions.'
full_handle: Votre identifiant complet
full_handle_hint: Cest ce que vous diriez à vos ami·e·s pour leur permettre de vous envoyer un message ou vous suivre à partir dune autre instance.
full_handle_hint: Cest ce que vous diriez à vos ami·e·s pour leur permettre de vous envoyer un message ou vous suivre à partir dun autre serveur.
review_preferences_action: Modifier les préférences
review_preferences_step: Assurez-vous de définir vos préférences, telles que les courriels que vous aimeriez recevoir ou le niveau de confidentialité auquel vous aimeriez que vos messages soient soumis par défaut. Si vous navez pas le mal des transports, vous pouvez choisir dactiver la lecture automatique des GIF.
subject: Bienvenue sur Mastodon

View File

@ -68,6 +68,7 @@ ja:
admin: Admin
bot: Bot
moderator: Mod
unavailable: プロフィールは利用できません
unfollow: フォロー解除
admin:
account_actions:
@ -80,6 +81,7 @@ ja:
destroyed_msg: モデレーションメモを削除しました!
accounts:
approve: 承認
approve_all: すべて承認
are_you_sure: 本当に実行しますか?
avatar: アイコン
by_domain: ドメイン
@ -125,7 +127,7 @@ ja:
moderation:
active: アクティブ
all: すべて
pending: 保留中
pending: 承認待ち
silenced: サイレンス済み
suspended: 停止済み
title: モデレーション
@ -135,7 +137,7 @@ ja:
no_limits_imposed: 制限なし
not_subscribed: 購読していない
outbox_url: Outbox URL
pending: 審査待ち
pending: 承認待ち
perform_full_suspension: 活動を完全に停止させる
profile_url: プロフィールURL
promote: 昇格
@ -144,6 +146,7 @@ ja:
push_subscription_expires: PuSH購読期限
redownload: プロフィールを更新
reject: 却下
reject_all: すべて却下
remove_avatar: アイコンを削除
remove_header: ヘッダーを削除
resend_confirmation:
@ -330,6 +333,8 @@ ja:
expired: 期限切れ
title: フィルター
title: 招待
pending_accounts:
title: 承認待ちアカウント (%{count})
relays:
add_new: リレーを追加
delete: 削除
@ -486,7 +491,7 @@ ja:
admin_mailer:
new_pending_account:
body: 新しいアカウントの詳細は以下の通りです。この申請を承認または却下することができます。
subject: "%{instance} で新しいアカウント (%{username}) が審査待ちです"
subject: "%{instance} で新しいアカウント (%{username}) が承認待ちです"
new_report:
body: "%{reporter} が %{target} を通報しました"
body_remote: "%{domain} の誰かが %{target} を通報しました"

View File

@ -68,6 +68,7 @@ ko:
admin: 관리자
bot:
moderator: 모더레이터
unavailable: 프로필 사용 불가
unfollow: 팔로우 해제
admin:
account_actions:
@ -80,6 +81,7 @@ ko:
destroyed_msg: 모더레이션 기록이 성공적으로 삭제되었습니다!
accounts:
approve: 승인
approve_all: 모두 승인
are_you_sure: 정말로 실행하시겠습니까?
avatar: 아바타
by_domain: 도메인
@ -144,6 +146,7 @@ ko:
push_subscription_expires: PuSH 구독 기간 만료
redownload: 프로필 업데이트
reject: 거부
reject_all: 모두 거부
remove_avatar: 아바타 지우기
remove_header: 헤더 삭제
resend_confirmation:
@ -332,6 +335,8 @@ ko:
expired: 만료됨
title: 필터
title: 초대
pending_accounts:
title: 대기중인 계정 (%{count})
relays:
add_new: 릴레이 추가
delete: 삭제

View File

@ -4,25 +4,36 @@ nl:
about_hashtag_html: Dit zijn openbare toots die getagged zijn met <strong>#%{hashtag}</strong>. Je kunt er op reageren of iets anders mee doen als je op Mastodon (of ergens anders in de fediverse) een account hebt.
about_mastodon_html: Mastodon is een sociaal netwerk dat gebruikt maakt van open webprotocollen en vrije software. Het is net zoals e-mail gedecentraliseerd.
about_this: Over deze server
active_count_after: actief
active_footnote: Actieve gebruikers per maand (MAU)
administered_by: 'Beheerd door:'
api: API
apps: Mobiele apps
apps_platforms: Gebruik Mastodon op iOS, Android en op andere platformen
browse_directory: Gebruikersgids doorbladeren en op interesses filteren
browse_public_posts: Livestream van openbare Mastodonberichten bekijken
contact: Contact
contact_missing: Niet ingesteld
contact_unavailable: n.v.t
discover_users: Gebruikers ontdekken
documentation: Documentatie
extended_description_html: |
<h3>Een goede plek voor richtlijnen</h3>
<p>De uitgebreide omschrijving is nog niet ingevuld.</p>
federation_hint_html: Met een account op %{instance} ben je in staat om mensen die zich op andere Mastodonservers (en op andere plekken) bevinden te volgen.
generic_description: "%{domain} is een server in het Mastodonnetwerk"
get_apps: Mobiele apps
hosted_on: Mastodon op %{domain}
learn_more: Meer leren
privacy_policy: Privacybeleid
see_whats_happening: Kijk wat er aan de hand is
server_stats: 'Serverstatistieken:'
source_code: Broncode
status_count_after:
one: toot
other: toots
status_count_before: Zij schreven
tagline: Vrienden volgen en nieuwe ontdekken
terms: Gebruiksvoorwaarden
user_count_after:
one: gebruiker
@ -68,6 +79,7 @@ nl:
delete: Verwijderen
destroyed_msg: Verwijderen van opmerking voor moderatoren geslaagd!
accounts:
approve: Goedkeuren
are_you_sure: Weet je het zeker?
avatar: Avatar
by_domain: Domein
@ -113,6 +125,7 @@ nl:
moderation:
active: Actief
all: Alles
pending: In afwachting
silenced: Genegeerd
suspended: Opgeschort
title: Moderatie
@ -122,6 +135,7 @@ nl:
no_limits_imposed: Geen limieten ingesteld
not_subscribed: Niet geabonneerd
outbox_url: Outbox-URL
pending: Moet nog beoordeeld worden
perform_full_suspension: Opschorten
profile_url: Profiel-URL
promote: Promoveren
@ -129,6 +143,7 @@ nl:
public: Openbaar
push_subscription_expires: PuSH-abonnement verloopt op
redownload: Profiel vernieuwen
reject: Afkeuren
remove_avatar: Avatar verwijderen
remove_header: Omslagfoto verwijderen
resend_confirmation:
@ -400,6 +415,12 @@ nl:
min_invite_role:
disabled: Niemand
title: Uitnodigingen toestaan door
registrations_mode:
modes:
approved: Goedkeuring vereist om te kunnen registreren
none: Niemand kan zich registreren
open: Iedereen kan zich registreren
title: Registratiemodus
show_known_fediverse_at_about_page:
desc_html: Wanneer ingeschakeld wordt de globale tijdlijn op de voorpagina getoond en wanneer uitgeschakeld de lokale tijdljn.
title: De globale tijdlijn op de voorpagina tonen
@ -462,6 +483,9 @@ nl:
edit_preset: Voorinstelling van waarschuwing bewerken
title: Voorinstellingen van waarschuwingen beheren
admin_mailer:
new_pending_account:
body: Zie hieronder de details van het nieuwe account. Je kunt de aanvraag goedkeuren of afkeuren.
subject: Er dient een nieuw account op %{instance} te worden beoordeeld (%{username})
new_report:
body: "%{reporter} heeft %{target} gerapporteerd"
body_remote: Iemand van %{domain} heeft %{target} gerapporteerd
@ -483,10 +507,12 @@ nl:
your_token: Jouw toegangscode
auth:
agreement_html: Wanneer je op registreren klikt ga je akkoord met het opvolgen van <a href="%{rules_path}">de regels van deze server</a> en <a href="%{terms_path}">onze gebruiksvoorwaarden</a>.
apply_for_account: Een uitnodiging aanvragen
change_password: Wachtwoord
checkbox_agreement_html: Ik ga akkoord met de <a href="%{rules_path}" target="_blank">regels van deze server</a> en de <a href="%{terms_path}" target="_blank">gebruiksvoorwaarden</a>
confirm_email: E-mail bevestigen
delete_account: Account verwijderen
delete_account_html: Wanneer je jouw account graag wilt verwijderen, kan je dat <a href="%{path}">hier doen</a>. We vragen jou daar om een bevestiging.
delete_account_html: Wanneer je jouw account graag wilt verwijderen, kun je dat <a href="%{path}">hier doen</a>. We vragen jou daar om een bevestiging.
didnt_get_confirmation: Geen bevestigingsinstructies ontvangen?
forgot_password: Wachtwoord vergeten?
invalid_reset_password_token: De code om jouw wachtwoord opnieuw in te stellen is verlopen. Vraag een nieuwe aan.
@ -499,10 +525,12 @@ nl:
cas: CAS
saml: SAML
register: Registreren
registration_closed: "%{instance} laat geen nieuwe gebruikers toe"
resend_confirmation: Verstuur de bevestigingsinstructies nogmaals
reset_password: Wachtwoord opnieuw instellen
security: Beveiliging
set_new_password: Nieuw wachtwoord instellen
trouble_logging_in: Problemen met inloggen?
authorize_follow:
already_following: Je volgt dit account al
error: Helaas, er is een fout opgetreden bij het opzoeken van de externe account
@ -598,12 +626,29 @@ nl:
more: Meer…
resources: Hulpmiddelen
generic:
all: Alles
changes_saved_msg: Wijzigingen succesvol opgeslagen!
copy: Kopiëren
save_changes: Wijzigingen opslaan
validation_errors:
one: Er is iets niet helemaal goed! Bekijk onderstaande fout
other: Er is iets niet helemaal goed! Bekijk onderstaande %{count} fouten
identity_proofs:
active: Actief
authorize: Ja, autoriseren
authorize_connection_prompt: Deze cryptografische verbinding autoriseren?
errors:
failed: De cryptografische verbinding is mislukt. Probeer het opnieuw vanaf %{provider}.
keybase:
invalid_token: Keybasetokens zijn hashes van handtekeningen en moeten een lengte hebben van 66 hexadecimale tekens
verification_failed: Keybase herkent deze token niet als een handtekening van Keybasegebruiker %{kb_username}. Probeer het opnieuw vanuit Keybase.
wrong_user: Er kan geen bewijs worden aangemaakt voor %{proving} terwijl je bent ingelogd als %{current}. Log in als %{proving} en probeer het opnieuw.
explanation_html: Hier kun je met behulp van cryptografie jouw andere identiteiten verbinden, zoals een Keybaseprofiel. Hiermee kunnen andere mensen jou versleutelde berichten sturen en inhoud die jij verstuurt vertrouwen.
i_am_html: Ik ben %{username} op %{service}.
identity: Identiteit
inactive: Inactief
status: Verificatiestatus
view_proof: Bekijk bewijs
imports:
modes:
merge: Samenvoegen
@ -721,6 +766,17 @@ nl:
other: Overig
publishing: Publiceren
web: Webapp
relationships:
activity: Accountactiviteit
dormant: Sluimerend
moved: Verhuisd
mutual: Wederzijds
primary: Primair
relationship: Relatie
remove_selected_domains: Alle volgers van de geselecteerde domeinen verwijderen
remove_selected_followers: Geselecteerde volgers verwijderen
remove_selected_follows: Geselecteerde gebruikers ontvolgen
status: Accountstatus
remote_follow:
acct: Geef jouw account@domein op die je wilt gebruiken
missing_resource: Kon vereiste doorverwijzings-URL voor jouw account niet vinden
@ -795,10 +851,12 @@ nl:
edit_profile: Profiel bewerken
export: Exporteren
featured_tags: Uitgelichte hashtags
identity_proofs: Identiteitsbewijzen
import: Importeren
migrate: Accountmigratie
notifications: Meldingen
preferences: Voorkeuren
relationships: Volgers en gevolgden
settings: Instellingen
two_factor_authentication: Tweestapsverificatie
your_apps: Jouw toepassingen
@ -944,7 +1002,7 @@ nl:
generate_recovery_codes: Herstelcodes genereren
instructions_html: "<strong>Scan deze QR-code in Google Authenticator of een soortgelijke app op jouw mobiele telefoon</strong>. Van nu af aan genereert deze app aanmeldcodes die je bij het inloggen moet invoeren."
lost_recovery_codes: Met herstelcodes kun je toegang tot jouw account krijgen wanneer je jouw telefoon bent kwijtgeraakt. Wanneer je jouw herstelcodes bent kwijtgeraakt, kan je ze hier opnieuw genereren. Jouw oude herstelcodes zijn daarna ongeldig.
manual_instructions: Voor het geval je de QR-code niet kunt scannen en het handmatig moet invoeren, vind je hieronder geheime code in platte tekst.
manual_instructions: Voor het geval je de QR-code niet kunt scannen en het handmatig moet invoeren, vind je hieronder de geheime code in platte tekst.
recovery_codes: Herstelcodes back-uppen
recovery_codes_regenerated: Opnieuw genereren herstelcodes geslaagd
recovery_instructions_html: Wanneer je ooit de toegang verliest tot jouw telefoon, kan je met behulp van een van de herstelcodes hieronder opnieuw toegang krijgen tot jouw account. <strong>Zorg ervoor dat je de herstelcodes op een veilige plek bewaard</strong>. Je kunt ze bijvoorbeeld printen en ze samen met andere belangrijke documenten bewaren.

View File

@ -4,20 +4,30 @@ pl:
about_hashtag_html: Znajdują się tu publiczne wpisy oznaczone hashtagiem <strong>#%{hashtag}</strong>. Możesz dołączyć do dyskusji, jeżeli posiadasz konto gdziekolwiek w Fediwersum.
about_mastodon_html: Mastodon jest wolną i otwartą siecią społecznościową, zdecentralizowaną alternatywą dla zamkniętych, komercyjnych platform.
about_this: O tej instancji
active_count_after: aktywni
active_footnote: Aktywni użytkownicy miesięcznie (MAU)
administered_by: 'Administrowana przez:'
api: API
apps: Aplikacje
apps_platforms: Korzystaj z Mastodona z poziomu iOS-a, Androida i innych
browse_directory: Przeglądaj katalog profilów i filtruj z uwzględnieniem zainteresowań
browse_public_posts: Przeglądaj strumień publicznych wpisów na Mastodonie na żywo
contact: Kontakt
contact_missing: Nie ustawiono
contact_unavailable: Nie dotyczy
discover_users: Odkrywaj użytkowników
documentation: Dokumentacja
extended_description_html: |
<h3>Dobre miejsce na zasady użytkowania</h3>
<p>Nie ustawiono jeszcze szczegółowego opisu</p>
federation_hint_html: Z kontem na %{instance}, możesz śledzić użytkowników każdego serwera Mastodona i nie tylko.
generic_description: "%{domain} jest jednym z serwerów sieci"
get_apps: Spróbuj aplikacji mobilnej
hosted_on: Mastodon uruchomiony na %{domain}
learn_more: Dowiedz się więcej
privacy_policy: Polityka prywatności
see_whats_happening: Zobacz co się dzieje
server_stats: 'Statystyki serwera:'
source_code: Kod źródłowy
status_count_after:
few: wpisów
@ -25,6 +35,7 @@ pl:
one: wpisu
other: wpisów
status_count_before: Są autorami
tagline: Śledź znajomych i poznawal nowych
terms: Zasady użytkowania
user_count_after:
few: użytkowników
@ -76,6 +87,7 @@ pl:
delete: Usuń
destroyed_msg: Pomyślnie usunięto notatkę moderacyjną!
accounts:
approve: Przyjmij
are_you_sure: Jesteś tego pewien?
avatar: Awatar
by_domain: Domena
@ -121,6 +133,7 @@ pl:
moderation:
active: Aktywne
all: Wszystkie
pending: Oczekujące
silenced: Wyciszone
suspended: Zawieszone
title: Moderacja
@ -130,6 +143,7 @@ pl:
no_limits_imposed: Nie nałożono ograniczeń
not_subscribed: Nie zasubskrybowano
outbox_url: Adres skrzynki nadawczej
pending: Oczekuje na przegląd
perform_full_suspension: Zawieś
profile_url: Adres profilu
promote: Podnieś uprawnienia
@ -137,6 +151,7 @@ pl:
public: Publiczne
push_subscription_expires: Subskrypcja PuSH wygasa
redownload: Odśwież profil
reject: Odrzuć
remove_avatar: Usun awatar
remove_header: Usuń nagłówek
resend_confirmation:
@ -238,6 +253,7 @@ pl:
feature_profile_directory: Katalog profilów
feature_registrations: Rejestracja
feature_relay: Przekazywanie federacji
feature_timeline_preview: Podgląd osi czasu
features: Możliwości
hidden_service: Federowanie z ukrytymi usługami
open_reports: otwarte zgłoszenia
@ -412,6 +428,12 @@ pl:
min_invite_role:
disabled: Nikt
title: Kto może zapraszać użytkowników
registrations_mode:
modes:
approved: Przyjęcie jest wymagane do rejestracji
none: Nikt nie może się zarejestrować
open: Każdy może się zarejestrować
title: Tryb rejestracji
show_known_fediverse_at_about_page:
desc_html: Jeśli włączone, podgląd instancji będzie wyświetlał wpisy z całego Fediwersum. W innym przypadku, będą wyświetlane tylko lokalne wpisy.
title: Pokazuj wszystkie znane wpisy na podglądzie instancji
@ -474,6 +496,9 @@ pl:
edit_preset: Edytuj szablon ostrzeżenia
title: Zarządzaj szablonami ostrzeżeń
admin_mailer:
new_pending_account:
body: Poniżej znajdują się szczegóły dotycząće nowego konta. Możesz przyjąć lub odrzucić to podanie.
subject: Nowe konto czeka na przegląd na %{instance} (%{username})
new_report:
body: Użytkownik %{reporter} zgłosił(a) %{target}
body_remote: Użytkownik instancji %{domain} zgłosił(a) %{target}
@ -495,7 +520,9 @@ pl:
your_token: Twój token dostępu
auth:
agreement_html: Rejestrując się, oświadczasz, że zapoznałeś(-aś) się z <a href="%{rules_path}">informacjami o serwerze</a> i <a href="%{terms_path}">zasadami korzystania z usługi</a>.
apply_for_account: Poproś o zaproszenie
change_password: Hasło
checkbox_agreement_html: Zgadzam się z <a href="%{rules_path}" target="_blank">regułami serwera</a> i <a href="%{terms_path}" target="_blank">zasadami korzystania z usługi</a>
confirm_email: Potwierdź adres e-mail
delete_account: Usunięcie konta
delete_account_html: Jeżeli chcesz usunąć konto, <a href="%{path}">przejdź tutaj</a>. Otrzymasz prośbę o potwierdzenie.
@ -511,10 +538,12 @@ pl:
cas: CAS
saml: SAML
register: Rejestracja
registration_closed: "%{instance} nie przyjmuje nowych członków"
resend_confirmation: Ponownie prześlij instrukcje weryfikacji
reset_password: Zresetuj hasło
security: Bezpieczeństwo
set_new_password: Ustaw nowe hasło
trouble_logging_in: Masz problem z zalogowaniem się?
authorize_follow:
already_following: Już śledzisz to konto
error: Niestety, podczas sprawdzania zdalnego konta wystąpił błąd
@ -572,6 +601,9 @@ pl:
content: Przepraszamy, coś poszło nie tak, po naszej stronie.
title: Ta strona jest nieprawidłowa
noscript_html: Aby korzystać z aplikacji Mastodon, włącz JavaScript. Możesz też skorzystać z jednej z <a href="%{apps_path}">natywnych aplikacji</a> obsługującej Twoje urządzenie.
existing_username_validator:
not_found: nie znaleziono lokalnego użytkownika o tej nazwie
not_found_multiple: nie znaleziono %{usernames}
exports:
archive_takeout:
date: Data
@ -612,8 +644,10 @@ pl:
more: Więcej…
resources: Zasoby
generic:
all: Wszystkie
changes_saved_msg: Ustawienia zapisane!
copy: Kopiuj
order_by: Uporządkuj według
save_changes: Zapisz zmiany
use_this: Użyj tego
validation_errors:
@ -621,6 +655,26 @@ pl:
many: Coś jest wciąż nie tak! Przejrzyj %{count} poniższych błędów
one: Coś jest wciąż nie tak! Przyjrzyj się poniższemu błędowi
other: Coś jest wciąż nie tak! Przejrzyj poniższe błędy (%{count})
html_validator:
invalid_markup: 'zawiera nieprawidłową składnię HTML: %{error}'
identity_proofs:
active: Aktywny
authorize: Tak, autoryzuj
authorize_connection_prompt: Czy chcesz autoryzować to połączenie kryptograficzne?
errors:
failed: Połączenioe kryptograficzne nie powiodło się. Spróbuj ponownie z poziomu %{provider}.
keybase:
invalid_token: Tokeny Keybase są hashami podpisów i musza składać się z 66 znaków heksadecymalnych
verification_failed: Keybase nie rozpoznaje tego tokenu jako podpisu użytkownika Keybase %{kb_username}. Spróbuj ponownie z poziomu Keybase.
wrong_user: Nie można utworzyć dowodu dla %{proving}, gdy jesteś zalogowany(-a) jako %{current}. Zaloguj się jako %{proving} i spróbuj ponownie.
explanation_html: Tutaj możesz połączyć kryptograficznie swoje inne tożsamości, takie jak profil Keybase. To pozwoli innym wysłać Ci szyfrowane wiadomości i zaufać zawartości którą im wysyłasz.
i_am_html: Jestem %{username} na %{service}.
identity: Tożsamość
inactive: Niekatywny
publicize_checkbox: 'I opublikuj to:'
publicize_toot: 'Udowodnione! Jestem %{username} na %{service}: %{url}'
status: Stan weryfikacji
view_proof: Wyświetl dowód
imports:
modes:
merge: Połącz
@ -744,6 +798,19 @@ pl:
other: Pozostałe
publishing: Publikowanie
web: Sieć
relationships:
activity: Aktywność konta
dormant: Uśpione
last_active: Ostatnia aktywność
most_recent: Ostatnie
moved: Przeniesione
mutual: Wspólna
primary: Jednostronna
relationship: Relacja
remove_selected_domains: Usuń wszystkich śledzących z zaznaczonych domen
remove_selected_followers: Usuń zaznaczonych śledzących
remove_selected_follows: Przestań śledzić zaznaczonych użytkowników
status: Stan konta
remote_follow:
acct: Podaj swój adres (nazwa@domena), z którego chcesz wykonać działanie
missing_resource: Nie udało się znaleźć adresu przekierowania z Twojej domeny
@ -819,10 +886,12 @@ pl:
export: Eksportowanie danych
featured_tags: Wyróżnione hashtagi
flavours: Odmiany
identity_proofs: Dowody tożsamości
import: Importowanie danych
migrate: Migracja konta
notifications: Powiadomienia
preferences: Preferencje
relationships: Śledzeni i śledzący
settings: Ustawienia
two_factor_authentication: Uwierzytelnianie dwuetapowe
your_apps: Twoje aplikacje

View File

@ -40,7 +40,7 @@ fr:
featured_tag:
name: 'Vous pourriez utiliser l''un d''entre eux :'
imports:
data: Un fichier CSV généré par une autre instance de Mastodon
data: Un fichier CSV généré par un autre serveur de Mastodon
sessions:
otp: 'Entrez le code dauthentification à deux facteurs généré par lapplication de votre téléphone ou utilisez un de vos codes de récupération:'
user:

View File

@ -34,7 +34,7 @@ SimpleNavigation::Configuration.run do |navigation|
primary.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), admin_reports_url, if: proc { current_user.staff? } do |admin|
admin.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url
admin.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports}
admin.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts}
admin.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts}
admin.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path
admin.item :tags, safe_join([fa_icon('tag fw'), t('admin.tags.title')]), admin_tags_path
admin.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url(limited: '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks}, if: -> { current_user.admin? }

View File

@ -217,6 +217,13 @@ Rails.application.routes.draw do
end
end
resources :pending_accounts, only: [:index, :update] do
collection do
post :approve_all
post :reject_all
end
end
resources :users, only: [] do
resource :two_factor_authentication, only: [:destroy]
end

View File

@ -219,12 +219,14 @@ module Mastodon
def cull
skip_threshold = 7.days.ago
culled = 0
dry_run_culled = []
skip_domains = Set.new
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
Account.remote.where(protocol: :activitypub).partitioned.find_each do |account|
next if account.updated_at >= skip_threshold || (account.last_webfingered_at.present? && account.last_webfingered_at >= skip_threshold)
code = 0
unless skip_domains.include?(account.domain)
begin
code = Request.new(:head, account.uri).perform(&:code)
@ -236,7 +238,11 @@ module Mastodon
end
if [404, 410].include?(code)
SuspendAccountService.new.call(account, destroy: true) unless options[:dry_run]
if options[:dry_run]
dry_run_culled << account.acct
else
SuspendAccountService.new.call(account, destroy: true)
end
culled += 1
say('+', :green, false)
else
@ -252,6 +258,11 @@ module Mastodon
say('The following servers were not available during the check:', :yellow)
skip_domains.each { |domain| say(' ' + domain) }
end
unless dry_run_culled.empty?
say('The following accounts would have been deleted:', :green)
dry_run_culled.each { |account| say(' ' + account) }
end
end
option :all, type: :boolean
@ -356,6 +367,104 @@ module Mastodon
say("OK, unfollowed target from #{processed} accounts, skipped #{failed}", :green)
end
option :follows, type: :boolean, default: false
option :followers, type: :boolean, default: false
desc 'reset-relationships USERNAME', 'Reset all follows and/or followers for a user'
long_desc <<-LONG_DESC
Reset all follows and/or followers for a user specified by USERNAME.
With the --follows option, the command unfollows everyone that the account follows,
and then re-follows the users that would be followed by a brand new account.
With the --followers option, the command removes all followers of the account.
LONG_DESC
def reset_relationships(username)
unless options[:follows] || options[:followers]
say('Please specify either --follows or --followers, or both', :red)
exit(1)
end
account = Account.find_local(username)
if account.nil?
say('No user with such username', :red)
exit(1)
end
if options[:follows]
processed = 0
failed = 0
say("Unfollowing #{account.username}'s followees, this might take a while...")
Account.where(id: ::Follow.where(account: account).select(:target_account_id)).find_each do |target_account|
begin
UnfollowService.new.call(account, target_account)
processed += 1
say('.', :green, false)
rescue StandardError
failed += 1
say('.', :red, false)
end
end
BootstrapTimelineWorker.perform_async(account.id)
say("OK, unfollowed #{processed} followees, skipped #{failed}", :green)
end
if options[:followers]
processed = 0
failed = 0
say("Removing #{account.username}'s followers, this might take a while...")
Account.where(id: ::Follow.where(target_account: account).select(:account_id)).find_each do |target_account|
begin
UnfollowService.new.call(target_account, account)
processed += 1
say('.', :green, false)
rescue StandardError
failed += 1
say('.', :red, false)
end
end
say("OK, removed #{processed} followers, skipped #{failed}", :green)
end
end
option :number, type: :numeric, aliases: [:n]
option :all, type: :boolean
desc 'approve [USERNAME]', 'Approve pending accounts'
long_desc <<~LONG_DESC
When registrations require review from staff, approve pending accounts,
either all of them with the --all option, or a specific number of them
specified with the --number (-n) option, or only a single specific
account identified by its username.
LONG_DESC
def approve(username = nil)
if options[:all]
User.pending.find_each(&:approve!)
say('OK', :green)
elsif options[:number]
User.pending.limit(options[:number]).each(&:approve!)
say('OK', :green)
elsif username.present?
account = Account.find_local(username)
if account.nil?
say('No such account', :red)
exit(1)
end
account.user&.approve!
say('OK', :green)
else
exit(1)
end
end
private
def rotate_keys_for_account(account, delay = 0)

View File

@ -66,6 +66,12 @@ module Mastodon
say("Imported #{imported}, skipped #{skipped}, failed to import #{failed}", color(imported, skipped, failed))
end
desc 'purge', 'Remove all custom emoji'
def purge
CustomEmoji.in_batches.destroy_all
say('OK', :green)
end
private
def color(green, _yellow, red)

View File

@ -13,7 +13,15 @@ module Mastodon
end
option :days, type: :numeric, default: 90
desc 'remove', 'Remove statuses'
desc 'remove', 'Remove unreferenced statuses'
long_desc <<~LONG_DESC
Remove statuses that are not referenced by local user activity, such as
ones that came from relays, or belonging to users that were once followed
by someone locally but no longer are.
This is a computationally heavy procedure that creates extra database
indicides before commencing, and removes them afterward.
LONG_DESC
def remove
say('Creating temporary database indices...')

View File

@ -7,40 +7,15 @@ describe Api::V1::Accounts::FollowerAccountsController do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
before do
Fabricate(:follow, target_account: user.account)
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
let(:simon) { Fabricate(:account, username: 'simon') }
let(:lewis) { Fabricate(:account, username: 'lewis') }
before do
simon.follow!(lewis)
end
it 'returns http success' do
get :index, params: { account_id: lewis.id, limit: 1 }
get :index, params: { account_id: user.account.id, limit: 1 }
expect(response).to have_http_status(200)
end
it 'returns JSON with correct data' do
get :index, params: { account_id: lewis.id, limit: 1 }
json = body_as_json
expect(json).to be_a Enumerable
expect(json.first[:username]).to eq 'simon'
end
it 'does not return accounts blocking you' do
simon.block!(user.account)
get :index, params: { account_id: lewis.id, limit: 1 }
json = body_as_json
expect(json).to be_a Enumerable
expect(json.size).to eq 0
end
end
end

View File

@ -7,40 +7,15 @@ describe Api::V1::Accounts::FollowingAccountsController do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
before do
Fabricate(:follow, account: user.account)
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
let(:simon) { Fabricate(:account, username: 'simon') }
let(:lewis) { Fabricate(:account, username: 'lewis') }
before do
lewis.follow!(simon)
end
it 'returns http success' do
get :index, params: { account_id: lewis.id, limit: 1 }
get :index, params: { account_id: user.account.id, limit: 1 }
expect(response).to have_http_status(200)
end
it 'returns JSON with correct data' do
get :index, params: { account_id: lewis.id, limit: 1 }
json = body_as_json
expect(json).to be_a Enumerable
expect(json.first[:username]).to eq 'simon'
end
it 'does not return accounts blocking you' do
simon.block!(user.account)
get :index, params: { account_id: lewis.id, limit: 1 }
json = body_as_json
expect(json).to be_a Enumerable
expect(json.size).to eq 0
end
end
end

View File

@ -11,7 +11,7 @@ describe Settings::Exports::FollowingAccountsController do
sign_in user, scope: :user
get :index, format: :csv
expect(response.body).to eq "username@domain\n"
expect(response.body).to eq "Account address,Show boosts\nusername@domain,true\n"
end
end
end

View File

@ -0,0 +1,4 @@
Account address,Show boosts
bob,true
eve@example.com,false

View File

@ -32,10 +32,11 @@ describe Export do
target_accounts.each(&account.method(:follow!))
export = Export.new(account).to_following_accounts_csv
results = export.strip.split
results = export.strip.split("\n")
expect(results.size).to eq 2
expect(results.first).to eq 'one@local.host'
expect(results.size).to eq 3
expect(results.first).to eq 'Account address,Show boosts'
expect(results.second).to eq 'one@local.host,true'
end
end

View File

@ -156,22 +156,5 @@ describe AccountSearchService, type: :service do
expect(results).to eq []
end
end
describe 'should not include accounts blocking the requester' do
let!(:blocked) { Fabricate(:account) }
let!(:blocker) { Fabricate(:account, username: 'exact') }
before do
blocker.block!(blocked)
end
it 'returns the fuzzy match first, and does not return suspended exacts' do
partial = Fabricate(:account, username: 'exactness')
results = subject.call('exact', blocked, limit: 10)
expect(results.size).to eq 1
expect(results).to eq [partial]
end
end
end
end

View File

@ -1,9 +1,9 @@
require 'rails_helper'
RSpec.describe ImportService, type: :service do
let!(:account) { Fabricate(:account) }
let!(:bob) { Fabricate(:account, username: 'bob') }
let!(:eve) { Fabricate(:account, username: 'eve', domain: 'example.com') }
let!(:account) { Fabricate(:account, locked: false) }
let!(:bob) { Fabricate(:account, username: 'bob', locked: false) }
let!(:eve) { Fabricate(:account, username: 'eve', domain: 'example.com', locked: false) }
context 'import old-style list of muted users' do
subject { ImportService.new }
@ -81,4 +81,89 @@ RSpec.describe ImportService, type: :service do
end
end
end
context 'import old-style list of followed users' do
subject { ImportService.new }
let(:csv) { attachment_fixture('mute-imports.txt') }
before do
allow(NotificationWorker).to receive(:perform_async)
end
describe 'when no accounts are followed' do
let(:import) { Import.create(account: account, type: 'following', data: csv) }
it 'follows the listed accounts, including boosts' do
subject.call(import)
expect(account.following.count).to eq 2
expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
end
end
describe 'when some accounts are already followed and overwrite is not set' do
let(:import) { Import.create(account: account, type: 'following', data: csv) }
it 'follows the listed accounts, including notifications' do
account.follow!(bob, reblogs: false)
subject.call(import)
expect(account.following.count).to eq 2
expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
end
end
describe 'when some accounts are already followed and overwrite is set' do
let(:import) { Import.create(account: account, type: 'following', data: csv, overwrite: true) }
it 'mutes the listed accounts, including notifications' do
account.follow!(bob, reblogs: false)
subject.call(import)
expect(account.following.count).to eq 2
expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
end
end
end
context 'import new-style list of followed users' do
subject { ImportService.new }
let(:csv) { attachment_fixture('new-following-imports.txt') }
before do
allow(NotificationWorker).to receive(:perform_async)
end
describe 'when no accounts are followed' do
let(:import) { Import.create(account: account, type: 'following', data: csv) }
it 'follows the listed accounts, respecting boosts' do
subject.call(import)
expect(account.following.count).to eq 2
expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false
end
end
describe 'when some accounts are already followed and overwrite is not set' do
let(:import) { Import.create(account: account, type: 'following', data: csv) }
it 'mutes the listed accounts, respecting notifications' do
account.follow!(bob, reblogs: true)
subject.call(import)
expect(account.following.count).to eq 2
expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false
end
end
describe 'when some accounts are already followed and overwrite is set' do
let(:import) { Import.create(account: account, type: 'following', data: csv, overwrite: true) }
it 'mutes the listed accounts, respecting notifications' do
account.follow!(bob, reblogs: true)
subject.call(import)
expect(account.following.count).to eq 2
expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false
end
end
end
end

View File

@ -3,8 +3,6 @@
require 'rails_helper'
describe SearchService, type: :service do
let(:current_account) { Fabricate(:user).account }
subject { described_class.new }
describe '#call' do
@ -12,7 +10,7 @@ describe SearchService, type: :service do
it 'returns empty results without searching' do
allow(AccountSearchService).to receive(:new)
allow(Tag).to receive(:search_for)
results = subject.call('', current_account, 10)
results = subject.call('', nil, 10)
expect(results).to eq(empty_results)
expect(AccountSearchService).not_to have_received(:new)
@ -29,33 +27,33 @@ describe SearchService, type: :service do
it 'returns the empty results' do
service = double(call: nil)
allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, current_account, 10)
results = subject.call(@query, nil, 10)
expect(service).to have_received(:call).with(@query, on_behalf_of: current_account)
expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results
end
end
context 'that finds an account' do
it 'includes the account in the results' do
account = Fabricate(:account)
account = Account.new
service = double(call: account)
allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, current_account, 10)
expect(service).to have_received(:call).with(@query, on_behalf_of: current_account)
results = subject.call(@query, nil, 10)
expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results.merge(accounts: [account])
end
end
context 'that finds a status' do
it 'includes the status in the results' do
status = Fabricate(:status)
status = Status.new
service = double(call: status)
allow(ResolveURLService).to receive(:new).and_return(service)
results = subject.call(@query, current_account, 10)
expect(service).to have_received(:call).with(@query, on_behalf_of: current_account)
results = subject.call(@query, nil, 10)
expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
expect(results).to eq empty_results.merge(statuses: [status])
end
end
@ -65,12 +63,12 @@ describe SearchService, type: :service do
context 'that matches an account' do
it 'includes the account in the results' do
query = 'username'
account = Fabricate(:account)
account = Account.new
service = double(call: [account])
allow(AccountSearchService).to receive(:new).and_return(service)
results = subject.call(query, current_account, 10)
expect(service).to have_received(:call).with(query, current_account, limit: 10, offset: 0, resolve: false)
results = subject.call(query, nil, 10)
expect(service).to have_received(:call).with(query, nil, limit: 10, offset: 0, resolve: false)
expect(results).to eq empty_results.merge(accounts: [account])
end
end
@ -81,7 +79,7 @@ describe SearchService, type: :service do
tag = Tag.new
allow(Tag).to receive(:search_for).with('tag', 10, 0).and_return([tag])
results = subject.call(query, current_account, 10)
results = subject.call(query, nil, 10)
expect(Tag).to have_received(:search_for).with('tag', 10, 0)
expect(results).to eq empty_results.merge(hashtags: [tag])
end
@ -89,7 +87,7 @@ describe SearchService, type: :service do
query = '@username'
allow(Tag).to receive(:search_for)
results = subject.call(query, current_account, 10)
results = subject.call(query, nil, 10)
expect(Tag).not_to have_received(:search_for)
expect(results).to eq empty_results
end