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
80 changed files with 785 additions and 311 deletions

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š 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