Merge branch 'main' into feature/require-mfa-by-admin

This commit is contained in:
FredysFonseca 2025-07-30 13:27:35 -04:00 committed by GitHub
commit 6a40447759
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 1016 additions and 324 deletions

View File

@ -6,7 +6,7 @@ module Admin
def index
authorize :audit_log, :index?
@auditable_accounts = Account.auditable.select(:id, :username)
@auditable_accounts = Account.auditable.select(:id, :username).order(username: :asc)
end
private

View File

@ -18,7 +18,7 @@ class Admin::Disputes::AppealsController < Admin::BaseController
end
def reject
authorize @appeal, :approve?
authorize @appeal, :reject?
log_action :reject, @appeal
@appeal.reject!(current_account)
UserMailer.appeal_rejected(@appeal.account.user, @appeal).deliver_later

View File

@ -36,7 +36,7 @@ module Admin
end
def edit
authorize :domain_block, :create?
authorize :domain_block, :update?
end
def create
@ -129,7 +129,7 @@ module Admin
end
def requires_confirmation?
@domain_block.valid? && (@domain_block.new_record? || @domain_block.severity_changed?) && @domain_block.severity.to_s == 'suspend' && !params[:confirm]
@domain_block.valid? && (@domain_block.new_record? || @domain_block.severity_changed?) && @domain_block.suspend? && !params[:confirm]
end
end
end

View File

@ -11,6 +11,7 @@ class StatusesController < ApplicationController
before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_status
before_action :redirect_to_original, only: :show
before_action :verify_embed_allowed, only: :embed
after_action :set_link_headers
@ -40,8 +41,6 @@ class StatusesController < ApplicationController
end
def embed
return not_found if @status.hidden? || @status.reblog?
expires_in 180, public: true
response.headers.delete('X-Frame-Options')
@ -50,6 +49,10 @@ class StatusesController < ApplicationController
private
def verify_embed_allowed
not_found if @status.hidden? || @status.reblog?
end
def set_link_headers
response.headers['Link'] = LinkHeader.new(
[[ActivityPub::TagManager.instance.uri_for(@status), [%w(rel alternate), %w(type application/activity+json)]]]

View File

@ -0,0 +1,63 @@
import { useState, useRef, useCallback, useId } from 'react';
import { FormattedMessage } from 'react-intl';
import Overlay from 'react-overlays/Overlay';
export const LearnMoreLink: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const accessibilityId = useId();
const [open, setOpen] = useState(false);
const triggerRef = useRef(null);
const handleClick = useCallback(() => {
setOpen(!open);
}, [open, setOpen]);
return (
<>
<button
className='link-button'
ref={triggerRef}
onClick={handleClick}
aria-expanded={open}
aria-controls={accessibilityId}
>
<FormattedMessage
id='learn_more_link.learn_more'
defaultMessage='Learn more'
/>
</button>
<Overlay
show={open}
rootClose
onHide={handleClick}
offset={[5, 5]}
placement='bottom-end'
target={triggerRef}
>
{({ props }) => (
<div
{...props}
role='region'
id={accessibilityId}
className='account__domain-pill__popout learn-more__popout dropdown-animation'
>
<div className='learn-more__popout__content'>{children}</div>
<div>
<button className='link-button' onClick={handleClick}>
<FormattedMessage
id='learn_more_link.got_it'
defaultMessage='Got it'
/>
</button>
</div>
</div>
)}
</Overlay>
</>
);
};

View File

@ -138,6 +138,16 @@ class StatusContent extends PureComponent {
onCollapsedToggle(collapsed);
}
// Remove quote fallback link from the DOM so it doesn't
// mess with paragraph margins
if (!!status.get('quote')) {
const inlineQuote = node.querySelector('.quote-inline');
if (inlineQuote) {
inlineQuote.remove();
}
}
}
handleMouseEnter = ({ currentTarget }) => {

View File

@ -3,19 +3,15 @@ import { useEffect, useMemo } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import type { Map as ImmutableMap } from 'immutable';
import ArticleIcon from '@/material-icons/400-24px/article.svg?react';
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
import { Icon } from 'mastodon/components/icon';
import { LearnMoreLink } from 'mastodon/components/learn_more_link';
import StatusContainer from 'mastodon/containers/status_container';
import type { Status } from 'mastodon/models/status';
import type { RootState } from 'mastodon/store';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import QuoteIcon from '../../images/quote.svg?react';
import { fetchStatus } from '../actions/statuses';
import { makeGetStatus } from '../selectors';
@ -31,7 +27,6 @@ const QuoteWrapper: React.FC<{
'status__quote--error': isError,
})}
>
<Icon id='quote' icon={QuoteIcon} className='status__quote-icon' />
{children}
</div>
);
@ -45,27 +40,20 @@ const NestedQuoteLink: React.FC<{
accountId ? state.accounts.get(accountId) : undefined,
);
const quoteAuthorName = account?.display_name_html;
const quoteAuthorName = account?.acct;
if (!quoteAuthorName) {
return null;
}
const quoteAuthorElement = (
<span dangerouslySetInnerHTML={{ __html: quoteAuthorName }} />
);
const quoteUrl = `/@${account.get('acct')}/${status.get('id') as string}`;
return (
<Link to={quoteUrl} className='status__quote-author-button'>
<div className='status__quote-author-button'>
<FormattedMessage
id='status.quote_post_author'
defaultMessage='Post by {name}'
values={{ name: quoteAuthorElement }}
defaultMessage='Quoted a post by @{name}'
values={{ name: quoteAuthorName }}
/>
<Icon id='chevron_right' icon={ChevronRightIcon} />
<Icon id='article' icon={ArticleIcon} />
</Link>
</div>
);
};
@ -112,39 +100,42 @@ export const QuotedStatus: React.FC<{
defaultMessage='Hidden due to one of your filters'
/>
);
} else if (quoteState === 'deleted') {
quoteError = (
<FormattedMessage
id='status.quote_error.removed'
defaultMessage='This post was removed by its author.'
/>
);
} else if (quoteState === 'unauthorized') {
quoteError = (
<FormattedMessage
id='status.quote_error.unauthorized'
defaultMessage='This post cannot be displayed as you are not authorized to view it.'
/>
);
} else if (quoteState === 'pending') {
quoteError = (
<FormattedMessage
id='status.quote_error.pending_approval'
defaultMessage='This post is pending approval from the original author.'
/>
<>
<FormattedMessage
id='status.quote_error.pending_approval'
defaultMessage='Post pending'
/>
<LearnMoreLink>
<h6>
<FormattedMessage
id='status.quote_error.pending_approval_popout.title'
defaultMessage='Pending quote? Remain calm'
/>
</h6>
<p>
<FormattedMessage
id='status.quote_error.pending_approval_popout.body'
defaultMessage='Quotes shared across the Fediverse may take time to display, as different servers have different protocols.'
/>
</p>
</LearnMoreLink>
</>
);
} else if (quoteState === 'rejected' || quoteState === 'revoked') {
} else if (
!status ||
!quotedStatusId ||
quoteState === 'deleted' ||
quoteState === 'rejected' ||
quoteState === 'revoked' ||
quoteState === 'unauthorized'
) {
quoteError = (
<FormattedMessage
id='status.quote_error.rejected'
defaultMessage='This post cannot be displayed as the original author does not allow it to be quoted.'
/>
);
} else if (!status || !quotedStatusId) {
quoteError = (
<FormattedMessage
id='status.quote_error.not_found'
defaultMessage='This post cannot be displayed.'
id='status.quote_error.not_available'
defaultMessage='Post unavailable'
/>
);
}
@ -168,7 +159,7 @@ export const QuotedStatus: React.FC<{
isQuotedPost
id={quotedStatusId}
contextType={contextType}
avatarSize={40}
avatarSize={32}
>
{canRenderChildQuote && (
<QuotedStatus

View File

@ -24,6 +24,11 @@ export const RefreshController: React.FC<{
const refresh = useAppSelector(
(state) => state.contexts.refreshing[statusId],
);
const autoRefresh = useAppSelector(
(state) =>
!state.contexts.replies[statusId] ||
state.contexts.replies[statusId].length === 0,
);
const dispatch = useAppDispatch();
const intl = useIntl();
const [ready, setReady] = useState(false);
@ -39,6 +44,11 @@ export const RefreshController: React.FC<{
dispatch(completeContextRefresh({ statusId }));
if (result.async_refresh.result_count > 0) {
if (autoRefresh) {
void dispatch(fetchContext({ statusId }));
return '';
}
setReady(true);
}
} else {
@ -57,7 +67,7 @@ export const RefreshController: React.FC<{
return () => {
clearTimeout(timeoutId);
};
}, [dispatch, setReady, statusId, refresh]);
}, [dispatch, setReady, statusId, refresh, autoRefresh]);
const handleClick = useCallback(() => {
setLoading(true);

View File

@ -1,7 +1,7 @@
{
"about.blocks": "Moderated servers",
"about.contact": "Contact:",
"about.default_locale": "Varsayılan",
"about.default_locale": "Default",
"about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Reason not available",
"about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the Fediverse. These are the exceptions that have been made on this particular server.",
@ -9,11 +9,11 @@
"about.domain_blocks.silenced.title": "Limited",
"about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
"about.domain_blocks.suspended.title": "Suspended",
"about.language_label": "Dil",
"about.language_label": "Language",
"about.not_available": "This information has not been made available on this server.",
"about.powered_by": "Decentralised social media powered by {mastodon}",
"about.rules": "Server rules",
"account.account_note_header": "Kişisel not",
"account.account_note_header": "Personal note",
"account.add_or_remove_from_list": "Add or Remove from lists",
"account.badges.bot": "Automated",
"account.badges.group": "Group",
@ -845,8 +845,6 @@
"status.bookmark": "Bookmark",
"status.cancel_reblog_private": "Unboost",
"status.cannot_reblog": "This post cannot be boosted",
"status.context.load_new_replies": "Yeni yanıtlar geldi.",
"status.context.loading": "Daha fazla yanıt kontrol ediliyor",
"status.continued_thread": "Continued thread",
"status.copy": "Copy link to status",
"status.delete": "Delete",

View File

@ -498,6 +498,8 @@
"keyboard_shortcuts.translate": "to translate a post",
"keyboard_shortcuts.unfocus": "Unfocus compose textarea/search",
"keyboard_shortcuts.up": "Move up in the list",
"learn_more_link.got_it": "Got it",
"learn_more_link.learn_more": "Learn more",
"lightbox.close": "Close",
"lightbox.next": "Next",
"lightbox.previous": "Previous",
@ -873,12 +875,11 @@
"status.open": "Expand this post",
"status.pin": "Pin on profile",
"status.quote_error.filtered": "Hidden due to one of your filters",
"status.quote_error.not_found": "This post cannot be displayed.",
"status.quote_error.pending_approval": "This post is pending approval from the original author.",
"status.quote_error.rejected": "This post cannot be displayed as the original author does not allow it to be quoted.",
"status.quote_error.removed": "This post was removed by its author.",
"status.quote_error.unauthorized": "This post cannot be displayed as you are not authorized to view it.",
"status.quote_post_author": "Post by {name}",
"status.quote_error.not_available": "Post unavailable",
"status.quote_error.pending_approval": "Post pending",
"status.quote_error.pending_approval_popout.body": "Quotes shared across the Fediverse may take time to display, as different servers have different protocols.",
"status.quote_error.pending_approval_popout.title": "Pending quote? Remain calm",
"status.quote_post_author": "Quoted a post by @{name}",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost with original visibility",

View File

@ -845,6 +845,8 @@
"status.bookmark": "Järjehoidja",
"status.cancel_reblog_private": "Lõpeta jagamine",
"status.cannot_reblog": "Seda postitust ei saa jagada",
"status.context.load_new_replies": "Leidub uusi vastuseid",
"status.context.loading": "Kontrollin täiendavate vastuste olemasolu",
"status.continued_thread": "Jätkatud lõim",
"status.copy": "Kopeeri postituse link",
"status.delete": "Kustuta",

View File

@ -845,6 +845,8 @@
"status.bookmark": "Blêdwizer tafoegje",
"status.cancel_reblog_private": "Net langer booste",
"status.cannot_reblog": "Dit berjocht kin net boost wurde",
"status.context.load_new_replies": "Nije reaksjes beskikber",
"status.context.loading": "Op nije reaksjes oan it kontrolearjen",
"status.continued_thread": "Ferfolgje it petear",
"status.copy": "Copy link to status",
"status.delete": "Fuortsmite",

View File

@ -346,7 +346,7 @@
"featured_carousel.post": "הודעה",
"featured_carousel.previous": "הקודם",
"featured_carousel.slide": "{index} מתוך {total}",
"filter_modal.added.context_mismatch_explanation": "קטגוריית המסנן הזאת לא חלה על ההקשר שממנו הגעת אל ההודעה הזו. אם תרצה/י שההודעה תסונן גם בהקשר זה, תצטרך/י לערוך את הסנן.",
"filter_modal.added.context_mismatch_explanation": "קטגוריית הסנן הזאת לא חלה על ההקשר שממנו הגעת אל ההודעה הזו. אם תרצה/י שההודעה תסונן גם בהקשר זה, תצטרך/י לערוך את הסנן.",
"filter_modal.added.context_mismatch_title": "אין התאמה להקשר!",
"filter_modal.added.expired_explanation": "פג תוקפה של קטגוריית הסינון הזו, יש צורך לשנות את תאריך התפוגה כדי שהסינון יוחל.",
"filter_modal.added.expired_title": "פג תוקף המסנן!",

View File

@ -513,7 +513,7 @@
"lists.add_to_lists": "리스트에 {name} 추가",
"lists.create": "생성",
"lists.create_a_list_to_organize": "새 리스트를 만들어 홈 피드를 정리하세요",
"lists.create_list": "리스트 생성",
"lists.create_list": "리스트 만들기",
"lists.delete": "리스트 삭제",
"lists.done": "완료",
"lists.edit": "리스트 편집",

View File

@ -12,6 +12,8 @@ body {
--background-color: #fff;
--background-color-tint: rgba(255, 255, 255, 80%);
--background-filter: blur(10px);
--surface-variant-background-color: #f1ebfb;
--surface-border-color: #cac4d0;
--on-surface-color: #{color.adjust($ui-base-color, $alpha: -0.65)};
--rich-text-container-color: rgba(255, 216, 231, 100%);
--rich-text-text-color: rgba(114, 47, 83, 100%);

View File

@ -1433,10 +1433,6 @@ body > [data-popper-placement] {
}
}
.status--has-quote .quote-inline {
display: none;
}
.status {
padding: 16px;
min-height: 54px;
@ -1470,10 +1466,6 @@ body > [data-popper-placement] {
margin-top: 16px;
}
&--is-quote {
border: none;
}
&--in-thread {
--thread-margin: calc(46px + 8px);
@ -1860,79 +1852,99 @@ body > [data-popper-placement] {
// --status-gutter-width is currently only set inside of
// .notification-ungrouped, so everywhere else this will fall back
// to the pixel values
--quote-margin: var(--status-gutter-width, 36px);
--quote-margin: var(--status-gutter-width);
position: relative;
margin-block-start: 16px;
margin-inline-start: calc(var(--quote-margin) + var(--thread-margin, 0px));
border-radius: 8px;
border-radius: 12px;
color: var(--nested-card-text);
background: var(--nested-card-background);
border: var(--nested-card-border);
@container (width > 460px) {
--quote-margin: var(--status-gutter-width, 56px);
}
border: 1px solid var(--surface-border-color);
}
.status__quote--error {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 12px;
font-size: 15px;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.25px;
min-height: 56px;
.link-button {
font-size: inherit;
line-height: inherit;
letter-spacing: inherit;
}
}
.status__quote-author-button {
position: relative;
overflow: hidden;
display: inline-flex;
width: auto;
margin-block-start: 10px;
padding: 5px 12px;
display: flex;
margin-top: 8px;
padding: 8px 12px;
align-items: center;
gap: 6px;
font-family: inherit;
font-size: 14px;
font-weight: 700;
line-height: normal;
letter-spacing: 0;
text-decoration: none;
color: $highlight-text-color;
background: var(--nested-card-background);
border: var(--nested-card-border);
border-radius: 4px;
&:active,
&:focus,
&:hover {
border-color: lighten($highlight-text-color, 4%);
color: lighten($highlight-text-color, 4%);
}
&:focus-visible {
outline: $ui-button-icon-focus-outline;
}
font-weight: 400;
line-height: 20px;
letter-spacing: 0.25px;
color: $darker-text-color;
background: var(--surface-variant-background-color);
border-radius: 8px;
cursor: default;
}
.status__quote-icon {
position: absolute;
inset-block-start: 18px;
inset-inline-start: -40px;
display: block;
width: 26px;
height: 26px;
padding: 5px;
color: #6a49ba;
z-index: 10;
.status--is-quote {
border: none;
padding: 12px;
.status__quote--error & {
inset-block-start: 50%;
transform: translateY(-50%);
.status__info {
padding-bottom: 8px;
}
@container (width > 460px) {
inset-inline-start: -50px;
.display-name,
.status__relative-time {
font-size: 14px;
line-height: 20px;
letter-spacing: 0.1px;
}
.display-name__account {
font-size: 12px;
line-height: 16px;
letter-spacing: 0.5px;
}
.status__content {
display: -webkit-box;
font-size: 14px;
letter-spacing: 0.25px;
line-height: 20px;
-webkit-line-clamp: 4;
line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
p {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
}
.media-gallery,
.video-player,
.audio-player,
.attachment-list,
.poll {
margin-top: 8px;
}
}
@ -2152,6 +2164,27 @@ body > [data-popper-placement] {
}
}
.learn-more__popout {
gap: 8px;
&__content {
display: flex;
flex-direction: column;
gap: 4px;
}
h6 {
font-size: inherit;
font-weight: 500;
line-height: inherit;
letter-spacing: 0.1px;
}
.link-button {
font-weight: 500;
}
}
.account__wrapper {
display: flex;
gap: 10px;

View File

@ -16,6 +16,7 @@
--surface-background-color: #{darken($ui-base-color, 4%)};
--surface-variant-background-color: #{$ui-base-color};
--surface-variant-active-background-color: #{lighten($ui-base-color, 4%)};
--surface-border-color: #{lighten($ui-base-color, 8%)};
--on-surface-color: #{color.adjust($ui-base-color, $alpha: -0.5)};
--avatar-border-radius: 8px;
--media-outline-color: #{rgba(#fcf8ff, 0.15)};

View File

@ -206,8 +206,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
@quote.save
embedded_quote = safe_prefetched_embed(@account, @status_parser.quoted_object, @json['context'])
ActivityPub::VerifyQuoteService.new.call(@quote, fetchable_quoted_uri: @quote_uri, prefetched_quoted_object: embedded_quote, request_id: @options[:request_id])
rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
ActivityPub::VerifyQuoteService.new.call(@quote, fetchable_quoted_uri: @quote_uri, prefetched_quoted_object: embedded_quote, request_id: @options[:request_id], depth: @options[:depth])
rescue Mastodon::RecursionLimitExceededError, Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS
ActivityPub::RefetchAndVerifyQuoteWorker.perform_in(rand(30..600).seconds, @quote.id, @quote_uri, { 'request_id' => @options[:request_id] })
end

View File

@ -40,7 +40,7 @@ class Admin::Metrics::Dimension::SpaceUsageDimension < Admin::Metrics::Dimension
def media_size
value = [
MediaAttachment.sum(Arel.sql('COALESCE(file_file_size, 0) + COALESCE(thumbnail_file_size, 0)')),
MediaAttachment.sum(MediaAttachment.combined_media_file_size),
CustomEmoji.sum(:image_file_size),
PreviewCard.sum(:image_file_size),
Account.sum(Arel.sql('COALESCE(avatar_file_size, 0) + COALESCE(header_file_size, 0)')),

View File

@ -29,7 +29,7 @@ class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics:
def perform_total_query
domain = params[:domain]
domain = Instance.by_domain_and_subdomains(params[:domain]).select(:domain) if params[:include_subdomains]
MediaAttachment.joins(:account).merge(Account.where(domain: domain)).sum('COALESCE(file_file_size, 0) + COALESCE(thumbnail_file_size, 0)')
MediaAttachment.joins(:account).merge(Account.where(domain: domain)).sum(MediaAttachment.combined_media_file_size)
end
def perform_previous_total_query
@ -44,7 +44,7 @@ class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics:
<<~SQL.squish
SELECT axis.*, (
WITH new_media_attachments AS (
SELECT COALESCE(media_attachments.file_file_size, 0) + COALESCE(media_attachments.thumbnail_file_size, 0) AS size
SELECT #{media_size_total} AS size
FROM media_attachments
INNER JOIN accounts ON accounts.id = media_attachments.account_id
WHERE date_trunc('day', media_attachments.created_at)::date = axis.period
@ -58,6 +58,10 @@ class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics:
SQL
end
def media_size_total
MediaAttachment.combined_media_file_size.to_sql
end
def params
@params.permit(:domain, :include_subdomains)
end

View File

@ -30,8 +30,10 @@ class StatusReachFinder
[
replied_to_account_id,
reblog_of_account_id,
quote_of_account_id,
mentioned_account_ids,
reblogs_account_ids,
quotes_account_ids,
favourites_account_ids,
replies_account_ids,
].tap do |arr|
@ -46,6 +48,10 @@ class StatusReachFinder
@status.in_reply_to_account_id if distributable?
end
def quote_of_account_id
@status.quote&.quoted_account_id
end
def reblog_of_account_id
@status.reblog.account_id if @status.reblog?
end
@ -54,6 +60,11 @@ class StatusReachFinder
@status.mentions.pluck(:account_id)
end
# Beware: Quotes can be created without the author having had access to the status
def quotes_account_ids
@status.quotes.pluck(:account_id) if distributable? || unsafe?
end
# Beware: Reblogs can be created without the author having had access to the status
def reblogs_account_ids
@status.reblogs.rewhere(deleted_at: [nil, @status.deleted_at]).pluck(:account_id) if distributable? || unsafe?

View File

@ -26,6 +26,7 @@ class AccountSuggestions::FriendsOfFriendsSource < AccountSuggestions::Source
AND NOT EXISTS (SELECT 1 FROM mutes m WHERE m.target_account_id = follows.target_account_id AND m.account_id = :id)
AND (accounts.domain IS NULL OR NOT EXISTS (SELECT 1 FROM account_domain_blocks b WHERE b.account_id = :id AND b.domain = accounts.domain))
AND NOT EXISTS (SELECT 1 FROM follows f WHERE f.target_account_id = follows.target_account_id AND f.account_id = :id)
AND NOT EXISTS (SELECT 1 FROM follow_requests f WHERE f.target_account_id = follows.target_account_id AND f.account_id = :id)
AND follows.target_account_id <> :id
AND accounts.discoverable
AND accounts.suspended_at IS NULL

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
module User::Activity
extend ActiveSupport::Concern
# The home and list feeds will be stored for this amount of time, and status
# fan-out to followers will include only people active within this time frame.
#
# Lowering the duration may improve performance if many people sign up, but
# most will not check their feed every day. Raising the duration reduces the
# amount of background processing that happens when people become active.
ACTIVE_DURATION = ENV.fetch('USER_ACTIVE_DAYS', 7).to_i.days
included do
scope :signed_in_recently, -> { where(current_sign_in_at: ACTIVE_DURATION.ago..) }
scope :not_signed_in_recently, -> { where(current_sign_in_at: ...ACTIVE_DURATION.ago) }
end
def signed_in_recently?
current_sign_in_at.present? && current_sign_in_at >= ACTIVE_DURATION.ago
end
private
def inactive_since_duration?
last_sign_in_at < ACTIVE_DURATION.ago
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
module User::Confirmation
extend ActiveSupport::Concern
included do
scope :confirmed, -> { where.not(confirmed_at: nil) }
scope :unconfirmed, -> { where(confirmed_at: nil) }
def confirm
wrap_email_confirmation { super }
end
end
def confirmed?
confirmed_at.present?
end
def unconfirmed?
!confirmed?
end
end

View File

@ -298,6 +298,10 @@ class MediaAttachment < ApplicationRecord
IMAGE_FILE_EXTENSIONS + VIDEO_FILE_EXTENSIONS + AUDIO_FILE_EXTENSIONS
end
def combined_media_file_size
arel_table.coalesce(arel_table[:file_file_size], 0) + arel_table.coalesce(arel_table[:thumbnail_file_size], 0)
end
private
def file_styles(attachment)

View File

@ -78,6 +78,7 @@ class Status < ApplicationRecord
has_many :mentions, dependent: :destroy, inverse_of: :status
has_many :mentioned_accounts, through: :mentions, source: :account, class_name: 'Account'
has_many :media_attachments, dependent: :nullify
has_many :quotes, foreign_key: 'quoted_status_id', inverse_of: :quoted_status, dependent: :nullify
# The `dependent` option is enabled by the initial `mentions` association declaration
has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status # rubocop:disable Rails/HasManyOrHasOneDependent

View File

@ -58,20 +58,13 @@ class User < ApplicationRecord
include LanguagesHelper
include Redisable
include User::Activity
include User::Confirmation
include User::HasSettings
include User::LdapAuthenticable
include User::Omniauthable
include User::PamAuthenticable
# The home and list feeds will be stored in Redis for this amount
# of time, and status fan-out to followers will include only people
# within this time frame. Lowering the duration may improve performance
# if lots of people sign up, but not a lot of them check their feed
# every day. Raising the duration reduces the amount of expensive
# RegenerationWorker jobs that need to be run when those people come
# to check their feed
ACTIVE_DURATION = ENV.fetch('USER_ACTIVE_DAYS', 7).to_i.days.freeze
devise :two_factor_authenticatable,
otp_secret_length: 32
@ -118,13 +111,9 @@ class User < ApplicationRecord
scope :recent, -> { order(id: :desc) }
scope :pending, -> { where(approved: false) }
scope :approved, -> { where(approved: true) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
scope :unconfirmed, -> { where(confirmed_at: nil) }
scope :enabled, -> { where(disabled: false) }
scope :disabled, -> { where(disabled: true) }
scope :active, -> { confirmed.signed_in_recently.account_not_suspended }
scope :signed_in_recently, -> { where(current_sign_in_at: ACTIVE_DURATION.ago..) }
scope :not_signed_in_recently, -> { where(current_sign_in_at: ...ACTIVE_DURATION.ago) }
scope :matches_email, ->(value) { where(arel_table[:email].matches("#{value}%")) }
scope :matches_ip, ->(value) { left_joins(:ips).merge(IpBlock.contained_by(value)).group(users: [:id]) }
@ -143,7 +132,10 @@ class User < ApplicationRecord
delegate :can?, to: :role
attr_reader :invite_code, :date_of_birth
attr_writer :external, :bypass_registration_checks, :current_account
attr_writer :current_account
attribute :external, :boolean, default: false
attribute :bypass_registration_checks, :boolean, default: false
def self.those_who_can(*any_of_privileges)
matching_role_ids = UserRole.that_can(*any_of_privileges).map(&:id)
@ -178,14 +170,6 @@ class User < ApplicationRecord
end
end
def signed_in_recently?
current_sign_in_at.present? && current_sign_in_at >= ACTIVE_DURATION.ago
end
def confirmed?
confirmed_at.present?
end
def invited?
invite_id.present?
end
@ -210,12 +194,6 @@ class User < ApplicationRecord
account_id
end
def confirm
wrap_email_confirmation do
super
end
end
# Mark current email as confirmed, bypassing Devise
def mark_email_as_confirmed!
wrap_email_confirmation do
@ -231,16 +209,11 @@ class User < ApplicationRecord
end
def update_sign_in!(new_sign_in: false)
old_current = current_sign_in_at
new_current = Time.now.utc
self.last_sign_in_at = old_current || new_current
self.last_sign_in_at = current_sign_in_at || new_current
self.current_sign_in_at = new_current
if new_sign_in
self.sign_in_count ||= 0
self.sign_in_count += 1
end
increment(:sign_in_count) if new_sign_in
save(validate: false) unless new_record?
prepare_returning_user!
@ -262,10 +235,6 @@ class User < ApplicationRecord
confirmed? && approved? && !disabled? && !account.unavailable? && !account.memorial?
end
def unconfirmed?
!confirmed?
end
def unconfirmed_or_pending?
unconfirmed? || pending?
end
@ -507,14 +476,6 @@ class User < ApplicationRecord
Setting.registrations_mode == 'open'
end
def external?
!!@external
end
def bypass_registration_checks?
@bypass_registration_checks
end
def sanitize_role
self.role = nil if role.present? && role.everyone?
end
@ -531,7 +492,7 @@ class User < ApplicationRecord
return unless confirmed?
ActivityTracker.record('activity:logins', id)
regenerate_feed! if needs_feed_update?
regenerate_feed! if inactive_since_duration?
end
def notify_staff_about_pending_account!
@ -550,10 +511,6 @@ class User < ApplicationRecord
RegenerationWorker.perform_async(account_id)
end
def needs_feed_update?
last_sign_in_at < ACTIVE_DURATION.ago
end
def validate_email_dns?
email_changed? && !external? && !self.class.skip_mx_check?
end

View File

@ -8,9 +8,10 @@ class ActivityPub::FetchRemoteStatusService < BaseService
DISCOVERIES_PER_REQUEST = 1000
# Should be called when uri has already been checked for locality
def call(uri, prefetched_body: nil, on_behalf_of: nil, expected_actor_uri: nil, request_id: nil)
def call(uri, prefetched_body: nil, on_behalf_of: nil, expected_actor_uri: nil, request_id: nil, depth: nil)
return if domain_not_allowed?(uri)
@depth = depth || 0
@request_id = request_id || "#{Time.now.utc.to_i}-status-#{uri}"
@json = if prefetched_body.nil?
fetch_status(uri, true, on_behalf_of)
@ -52,7 +53,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService
return nil if discoveries > DISCOVERIES_PER_REQUEST
end
ActivityPub::Activity.factory(activity_json, actor, request_id: @request_id).perform
ActivityPub::Activity.factory(activity_json, actor, request_id: @request_id, depth: @depth).perform
end
private

View File

@ -3,9 +3,12 @@
class ActivityPub::VerifyQuoteService < BaseService
include JsonLdHelper
MAX_SYNCHRONOUS_DEPTH = 2
# Optionally fetch quoted post, and verify the quote is authorized
def call(quote, fetchable_quoted_uri: nil, prefetched_quoted_object: nil, prefetched_approval: nil, request_id: nil)
def call(quote, fetchable_quoted_uri: nil, prefetched_quoted_object: nil, prefetched_approval: nil, request_id: nil, depth: nil)
@request_id = request_id
@depth = depth || 0
@quote = quote
@fetching_error = nil
@ -72,10 +75,12 @@ class ActivityPub::VerifyQuoteService < BaseService
return if uri.nil? || @quote.quoted_status.present?
status = ActivityPub::TagManager.instance.uri_to_resource(uri, Status)
status ||= ActivityPub::FetchRemoteStatusService.new.call(uri, on_behalf_of: @quote.account.followers.local.first, prefetched_body:, request_id: @request_id)
raise Mastodon::RecursionLimitExceededError if @depth > MAX_SYNCHRONOUS_DEPTH && status.nil?
status ||= ActivityPub::FetchRemoteStatusService.new.call(uri, on_behalf_of: @quote.account.followers.local.first, prefetched_body:, request_id: @request_id, depth: @depth + 1)
@quote.update(quoted_status: status) if status.present?
rescue Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS => e
rescue Mastodon::RecursionLimitExceededError, Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS => e
@fetching_error = e
end
@ -90,7 +95,7 @@ class ActivityPub::VerifyQuoteService < BaseService
# It's not safe to fetch if the inlined object is cross-origin or doesn't match expectations
return if object['id'] != uri || non_matching_uri_hosts?(@quote.approval_uri, object['id'])
status = ActivityPub::FetchRemoteStatusService.new.call(object['id'], prefetched_body: object, on_behalf_of: @quote.account.followers.local.first, request_id: @request_id)
status = ActivityPub::FetchRemoteStatusService.new.call(object['id'], prefetched_body: object, on_behalf_of: @quote.account.followers.local.first, request_id: @request_id, depth: @depth)
if status.present?
@quote.update(quoted_status: status)

View File

@ -86,9 +86,7 @@ class ActivityPub::FetchAllRepliesWorker
return if root_status_body.nil?
@batch.within do
FetchReplyWorker.perform_async(root_status_uri, { **options.deep_stringify_keys, 'prefetched_body' => root_status_body })
end
FetchReplyWorker.perform_async(root_status_uri, { **options.deep_stringify_keys.except('batch_id'), 'prefetched_body' => root_status_body })
get_replies(root_status_body, MAX_PAGES, options)
end

View File

@ -190,6 +190,7 @@ da:
create_relay: Opret Videresendelse
create_unavailable_domain: Opret Utilgængeligt Domæne
create_user_role: Opret rolle
create_username_block: Opret brugernavn-regel
demote_user: Degradér bruger
destroy_announcement: Slet bekendtgørelse
destroy_canonical_email_block: Slet e-mailblokering
@ -203,6 +204,7 @@ da:
destroy_status: Slet indlæg
destroy_unavailable_domain: Slet Utilgængeligt Domæne
destroy_user_role: Ødelæg rolle
destroy_username_block: Slet brugernavn-regel
disable_2fa_user: Deaktivér 2FA
disable_custom_emoji: Deaktivér tilpasset emoji
disable_relay: Deaktivér Videresendelse
@ -237,6 +239,7 @@ da:
update_report: Opdatér anmeldelse
update_status: Opdatér indlæg
update_user_role: Opdatér rolle
update_username_block: Opdatér brugernavn-regel
actions:
approve_appeal_html: "%{name} godkendte moderationsafgørelsesappellen fra %{target}"
approve_user_html: "%{name} godkendte tilmeldingen fra %{target}"
@ -255,6 +258,7 @@ da:
create_relay_html: "%{name} oprettede videresendelsen %{target}"
create_unavailable_domain_html: "%{name} stoppede levering til domænet %{target}"
create_user_role_html: "%{name} oprettede %{target}-rolle"
create_username_block_html: "%{name} tilføjede regel for brugernavne indeholdende %{target}"
demote_user_html: "%{name} degraderede brugeren %{target}"
destroy_announcement_html: "%{name} slettede bekendtgørelsen %{target}"
destroy_canonical_email_block_html: "%{name} afblokerede e-mailen med hash'et %{target}"
@ -268,6 +272,7 @@ da:
destroy_status_html: "%{name} fjernede indlægget fra %{target}"
destroy_unavailable_domain_html: "%{name} genoptog levering til domænet %{target}"
destroy_user_role_html: "%{name} slettede %{target}-rolle"
destroy_username_block_html: "%{name} fjernede regel for brugernavne indeholdende %{target}"
disable_2fa_user_html: "%{name} deaktiverede tofaktorkravet for brugeren %{target}"
disable_custom_emoji_html: "%{name} deaktiverede emojien %{target}"
disable_relay_html: "%{name} deaktiverede videresendelsen %{target}"
@ -302,6 +307,7 @@ da:
update_report_html: "%{name} opdaterede rapporten %{target}"
update_status_html: "%{name} opdaterede indlægget fra %{target}"
update_user_role_html: "%{name} ændrede %{target}-rolle"
update_username_block_html: "%{name} opdaterede regel for brugernavne indeholdende %{target}"
deleted_account: slettet konto
empty: Ingen logger fundet.
filter_by_action: Filtrér efter handling
@ -1085,6 +1091,25 @@ da:
other: Brugt af %{count} personer den seneste uge
title: Anbefalinger og trends
trending: Trender
username_blocks:
add_new: Tilføj ny
block_registrations: Blokér registreringer
comparison:
contains: Indeholder
equals: Er lig
contains_html: Indeholder %{string}
created_msg: Brugernavn-regel er hermed oprettet
delete: Slet
edit:
title: Redigér brugernavn-regel
matches_exactly_html: Er lig med %{string}
new:
create: Opret regel
title: Opret ny brugernavn-regel
no_username_block_selected: Ingen brugernavn-regler ændret (ingen var valgt)
not_permitted: Ikke tilladt
title: Brugernavn-regler
updated_msg: Brugernavn-regel opdateret
warning_presets:
add_new: Tilføj ny
delete: Slet

View File

@ -190,6 +190,7 @@ de:
create_relay: Relay erstellen
create_unavailable_domain: Nicht verfügbare Domain erstellen
create_user_role: Rolle erstellen
create_username_block: Regel für Profilnamen erstellen
demote_user: Benutzer*in herabstufen
destroy_announcement: Ankündigung löschen
destroy_canonical_email_block: E-Mail-Sperre entfernen
@ -203,6 +204,7 @@ de:
destroy_status: Beitrag entfernen
destroy_unavailable_domain: Nicht-verfügbare Domain entfernen
destroy_user_role: Rolle entfernen
destroy_username_block: Regel für Profilnamen löschen
disable_2fa_user: 2FA deaktivieren
disable_custom_emoji: Eigenes Emoji deaktivieren
disable_relay: Relay deaktivieren
@ -237,6 +239,7 @@ de:
update_report: Meldung aktualisieren
update_status: Beitrag aktualisieren
update_user_role: Rolle bearbeiten
update_username_block: Regel für Profilnamen aktualisieren
actions:
approve_appeal_html: "%{name} hat den Einspruch gegen eine Moderationsentscheidung von %{target} genehmigt"
approve_user_html: "%{name} genehmigte die Registrierung von %{target}"
@ -255,6 +258,7 @@ de:
create_relay_html: "%{name} erstellte ein Relay %{target}"
create_unavailable_domain_html: "%{name} beendete die Zustellung an die Domain %{target}"
create_user_role_html: "%{name} erstellte die Rolle %{target}"
create_username_block_html: "%{name} erstellte eine Regel für Profilnamen mit %{target}"
demote_user_html: "%{name} stufte %{target} herunter"
destroy_announcement_html: "%{name} löschte die Ankündigung %{target}"
destroy_canonical_email_block_html: "%{name} entsperrte die E-Mail mit dem Hash %{target}"
@ -268,6 +272,7 @@ de:
destroy_status_html: "%{name} entfernte einen Beitrag von %{target}"
destroy_unavailable_domain_html: "%{name} nahm die Zustellung an die Domain %{target} wieder auf"
destroy_user_role_html: "%{name} löschte die Rolle %{target}"
destroy_username_block_html: "%{name} entfernte eine Regel für Profilnamen mit %{target}"
disable_2fa_user_html: "%{name} deaktivierte die Zwei-Faktor-Authentisierung für %{target}"
disable_custom_emoji_html: "%{name} deaktivierte das Emoji %{target}"
disable_relay_html: "%{name} deaktivierte das Relay %{target}"
@ -302,6 +307,7 @@ de:
update_report_html: "%{name} überarbeitete die Meldung %{target}"
update_status_html: "%{name} überarbeitete einen Beitrag von %{target}"
update_user_role_html: "%{name} änderte die Rolle von %{target}"
update_username_block_html: "%{name} aktualisierte eine Regel für Profilnamen mit %{target}"
deleted_account: gelöschtes Konto
empty: Protokolle nicht gefunden.
filter_by_action: Nach Aktion filtern
@ -1085,6 +1091,25 @@ de:
other: In den vergangenen 7 Tagen von %{count} Profilen verwendet
title: Empfehlungen & Trends
trending: Angesagt
username_blocks:
add_new: Neue Regel
block_registrations: Registrierungen sperren
comparison:
contains: Enthält
equals: Entspricht
contains_html: Enthält %{string}
created_msg: Regel für Profilnamen erfolgreich erstellt
delete: Löschen
edit:
title: Regel für Profilnamen bearbeiten
matches_exactly_html: Entspricht %{string}
new:
create: Regel erstellen
title: Neue Regel für Profilnamen erstellen
no_username_block_selected: Keine Regeln für Profilnamen wurden geändert, weil keine ausgewählt wurde(n)
not_permitted: Nicht gestattet
title: Regeln für Profilnamen
updated_msg: Regel für Profilnamen erfolgreich aktualisiert
warning_presets:
add_new: Neu hinzufügen
delete: Löschen

View File

@ -190,6 +190,7 @@ es-AR:
create_relay: Crear relé
create_unavailable_domain: Crear dominio no disponible
create_user_role: Crear rol
create_username_block: Crear regla de nombre de usuario
demote_user: Descender usuario
destroy_announcement: Eliminar anuncio
destroy_canonical_email_block: Eliminar bloqueo de correo electrónico
@ -203,6 +204,7 @@ es-AR:
destroy_status: Eliminar mensaje
destroy_unavailable_domain: Eliminar dominio no disponible
destroy_user_role: Destruir rol
destroy_username_block: Eliminar regla de nombre de usuario
disable_2fa_user: Deshabilitar 2FA
disable_custom_emoji: Deshabilitar emoji personalizado
disable_relay: Deshabilitar relé
@ -237,6 +239,7 @@ es-AR:
update_report: Actualizar denuncia
update_status: Actualizar mensaje
update_user_role: Actualizar rol
update_username_block: Actualizar regla de nombre de usuario
actions:
approve_appeal_html: "%{name} aprobó la solicitud de moderación de %{target}"
approve_user_html: "%{name} aprobó el registro de %{target}"
@ -255,6 +258,7 @@ es-AR:
create_relay_html: "%{name} creó el relé %{target}"
create_unavailable_domain_html: "%{name} detuvo la entrega al dominio %{target}"
create_user_role_html: "%{name} creó el rol %{target}"
create_username_block_html: "%{name} agregó una regla para los nombres de usuario que contienen %{target}"
demote_user_html: "%{name} bajó de nivel al usuario %{target}"
destroy_announcement_html: "%{name} eliminó el anuncio %{target}"
destroy_canonical_email_block_html: "%{name} desbloqueó el correo electrónico con el hash %{target}"
@ -268,6 +272,7 @@ es-AR:
destroy_status_html: "%{name} eliminó el mensaje de %{target}"
destroy_unavailable_domain_html: "%{name} reanudó la entrega al dominio %{target}"
destroy_user_role_html: "%{name} eliminó el rol %{target}"
destroy_username_block_html: "%{name} eliminó una regla para los nombres de usuario que contienen %{target}"
disable_2fa_user_html: "%{name} deshabilitó el requerimiento de dos factores para el usuario %{target}"
disable_custom_emoji_html: "%{name} deshabilitó el emoji %{target}"
disable_relay_html: "%{name} deshabilitó el relé %{target}"
@ -302,6 +307,7 @@ es-AR:
update_report_html: "%{name} actualizó la denuncia %{target}"
update_status_html: "%{name} actualizó el mensaje de %{target}"
update_user_role_html: "%{name} cambió el rol %{target}"
update_username_block_html: "%{name} actualizó una regla para los nombres de usuario que contienen %{target}"
deleted_account: cuenta eliminada
empty: No se encontraron registros.
filter_by_action: Filtrar por acción
@ -1085,6 +1091,25 @@ es-AR:
other: Usada por %{count} personas durante la última semana
title: Recomendaciones y tendencias
trending: En tendencia
username_blocks:
add_new: Agregar nueva
block_registrations: Bloquear registros
comparison:
contains: Contiene
equals: Es igual a
contains_html: Contiene %{string}
created_msg: Regla de nombre de usuario creada exitosamente
delete: Eliminar
edit:
title: Editar regla de nombre de usuario
matches_exactly_html: Es igual a %{string}
new:
create: Crear regla
title: Crear nueva regla de nombre de usuario
no_username_block_selected: No se cambiaron las reglas de nombre de usuario, ya que no se seleccionó ninguna
not_permitted: No permitido
title: Reglas de nombre de usuario
updated_msg: Regla de nombre de usuario actualizada exitosamente
warning_presets:
add_new: Agregar nuevo
delete: Eliminar

View File

@ -190,6 +190,7 @@ es-MX:
create_relay: Crear Relé
create_unavailable_domain: Crear Dominio No Disponible
create_user_role: Crear rol
create_username_block: Crear regla de nombre de usuario
demote_user: Degradar Usuario
destroy_announcement: Eliminar Anuncio
destroy_canonical_email_block: Eliminar bloqueo de correo electrónico
@ -203,6 +204,7 @@ es-MX:
destroy_status: Eliminar Publicación
destroy_unavailable_domain: Eliminar Dominio No Disponible
destroy_user_role: Destruir Rol
destroy_username_block: Eliminar regla de nombre de usuario
disable_2fa_user: Deshabilitar 2FA
disable_custom_emoji: Deshabilitar Emoji Personalizado
disable_relay: Desactivar Relé
@ -237,6 +239,7 @@ es-MX:
update_report: Actualizar informe
update_status: Actualizar Publicación
update_user_role: Actualizar Rol
update_username_block: Actualizar regla de nombre de usuario
actions:
approve_appeal_html: "%{name} aprobó la solicitud de moderación de %{target}"
approve_user_html: "%{name} aprobó el registro de %{target}"
@ -255,6 +258,7 @@ es-MX:
create_relay_html: "%{name} creó un relé %{target}"
create_unavailable_domain_html: "%{name} detuvo las entregas al dominio %{target}"
create_user_role_html: "%{name} creó el rol %{target}"
create_username_block_html: "%{name} añadió regla para nombres de usuario que contienen %{target}"
demote_user_html: "%{name} degradó al usuario %{target}"
destroy_announcement_html: "%{name} eliminó el anuncio %{target}"
destroy_canonical_email_block_html: "%{name} ha desbloqueado el correo electrónico con el hash %{target}"
@ -268,6 +272,7 @@ es-MX:
destroy_status_html: "%{name} eliminó la publicación por %{target}"
destroy_unavailable_domain_html: "%{name} reanudó las entregas al dominio %{target}"
destroy_user_role_html: "%{name} eliminó el rol %{target}"
destroy_username_block_html: "%{name} eliminó la regla para los nombres de usuario que contienen %{target}"
disable_2fa_user_html: "%{name} desactivó el requisito de dos factores para el usuario %{target}"
disable_custom_emoji_html: "%{name} desactivó el emoji %{target}"
disable_relay_html: "%{name} desactivó el relé %{target}"
@ -302,6 +307,7 @@ es-MX:
update_report_html: "%{name} actualizó el informe %{target}"
update_status_html: "%{name} actualizó la publicación de %{target}"
update_user_role_html: "%{name} cambió el rol %{target}"
update_username_block_html: "%{name} actualizó una regla para los nombres de usuario que contienen %{target}"
deleted_account: cuenta eliminada
empty: No se encontraron registros.
filter_by_action: Filtrar por acción
@ -1085,6 +1091,25 @@ es-MX:
other: Usada por %{count} personas durante la última semana
title: Recomendaciones y Tendencias
trending: En tendencia
username_blocks:
add_new: Añadir nuevo
block_registrations: Bloquear registros
comparison:
contains: Contiene
equals: Igual
contains_html: Contiene %{string}
created_msg: Regla de nombre de usuario creada correctamente
delete: Eliminar
edit:
title: Editar regla de nombre de usuario
matches_exactly_html: Es igual a %{string}
new:
create: Crear regla
title: Crear nueva regla de nombre de usuario
no_username_block_selected: No se modificaron las reglas de nombre de usuario, ya que no se seleccionó ninguna
not_permitted: No permitido
title: Reglas para el nombre de usuario
updated_msg: Regla de nombre de usuario actualizada correctamente
warning_presets:
add_new: Añadir nuevo
delete: Borrar

View File

@ -190,6 +190,7 @@ es:
create_relay: Crear Relé
create_unavailable_domain: Crear Dominio No Disponible
create_user_role: Crear Rol
create_username_block: Crear regla de nombre de usuario
demote_user: Degradar Usuario
destroy_announcement: Eliminar Anuncio
destroy_canonical_email_block: Eliminar Bloqueo de Correo Electrónico
@ -203,6 +204,7 @@ es:
destroy_status: Eliminar Publicación
destroy_unavailable_domain: Eliminar Dominio No Disponible
destroy_user_role: Destruir Rol
destroy_username_block: Eliminar regla de nombre de usuario
disable_2fa_user: Deshabilitar 2FA
disable_custom_emoji: Deshabilitar Emoji Personalizado
disable_relay: Desactivar Relé
@ -237,6 +239,7 @@ es:
update_report: Actualizar informe
update_status: Actualizar Publicación
update_user_role: Actualizar Rol
update_username_block: Actualizar regla de nombre de usuario
actions:
approve_appeal_html: "%{name} aprobó la solicitud de moderación de %{target}"
approve_user_html: "%{name} aprobó el registro de %{target}"
@ -255,6 +258,7 @@ es:
create_relay_html: "%{name} creó un relé %{target}"
create_unavailable_domain_html: "%{name} detuvo las entregas al dominio %{target}"
create_user_role_html: "%{name} creó el rol %{target}"
create_username_block_html: "%{name} agregó una regla para los nombres de usuario que contienen %{target}"
demote_user_html: "%{name} degradó al usuario %{target}"
destroy_announcement_html: "%{name} eliminó el anuncio %{target}"
destroy_canonical_email_block_html: "%{name} desbloqueó el correo electrónico con el hash %{target}"
@ -268,6 +272,7 @@ es:
destroy_status_html: "%{name} eliminó la publicación de %{target}"
destroy_unavailable_domain_html: "%{name} reanudó las entregas al dominio %{target}"
destroy_user_role_html: "%{name} eliminó el rol %{target}"
destroy_username_block_html: "%{name} eliminó una regla para los nombres de usuario que contienen %{target}"
disable_2fa_user_html: "%{name} desactivó el requisito de dos factores para el usuario %{target}"
disable_custom_emoji_html: "%{name} desactivó el emoji %{target}"
disable_relay_html: "%{name} desactivó el relé %{target}"
@ -302,6 +307,7 @@ es:
update_report_html: "%{name} actualizó el informe %{target}"
update_status_html: "%{name} actualizó la publicación de %{target}"
update_user_role_html: "%{name} cambió el rol %{target}"
update_username_block_html: "%{name} actualizó una regla para los nombres de usuario que contienen %{target}"
deleted_account: cuenta eliminada
empty: No se encontraron registros.
filter_by_action: Filtrar por acción
@ -1085,6 +1091,25 @@ es:
other: Usada por %{count} personas durante la última semana
title: Recomendaciones y Tendencias
trending: En tendencia
username_blocks:
add_new: Añadir nueva
block_registrations: Bloquear registros
comparison:
contains: Contiene
equals: Es igual a
contains_html: Contiene %{string}
created_msg: Regla de nombre de usuario creada correctamente
delete: Eliminar
edit:
title: Editar regla de nombre de usuario
matches_exactly_html: Es igual a %{string}
new:
create: Crear regla
title: Crear nueva regla de nombre de usuario
no_username_block_selected: No se cambiaron las reglas de nombre de usuario, ya que no se seleccionó ninguno
not_permitted: No permitido
title: Reglas de nombre de usuario
updated_msg: Regla de nombre de usuario actualizada correctamente
warning_presets:
add_new: Añadir nuevo
delete: Borrar
@ -1872,7 +1897,7 @@ es:
edited_at_html: Editado %{date}
errors:
in_reply_not_found: La publicación a la que intentas responder no existe.
quoted_status_not_found: La publicación que estás intentando citar no existe.
quoted_status_not_found: La publicación que estás intentando citar no parece existir.
over_character_limit: Límite de caracteres de %{max} superado
pin_errors:
direct: Las publicaciones que son visibles solo para los usuarios mencionados no pueden fijarse

View File

@ -190,6 +190,7 @@ et:
create_relay: Loo sõnumivahendusserver
create_unavailable_domain: Kättesaamatu domeeni lisamine
create_user_role: Loo roll
create_username_block: Lisa kasutajanime reegel
demote_user: Alandas kasutaja
destroy_announcement: Eemaldas teadaande
destroy_canonical_email_block: Kustuta e-posti blokeering
@ -203,6 +204,7 @@ et:
destroy_status: Kustuta postitus
destroy_unavailable_domain: Kättesaamatu domeeni kustutamine
destroy_user_role: Rolli kustutamine
destroy_username_block: Kustuta kasutajanime reegel
disable_2fa_user: Keela 2FA
disable_custom_emoji: Keelas kohandatud emotikoni
disable_relay: Lülita sõnumivahendusserver välja
@ -237,6 +239,7 @@ et:
update_report: Uuendamise raport
update_status: Uuenda postitust
update_user_role: Uuenda rolli
update_username_block: Uuenda kasutajanime reeglit
actions:
approve_appeal_html: "%{name} kiitis heaks modereerimise otsuse vaidlustuse %{target} poolt"
approve_user_html: "%{name} kiitis heaks registreerimise %{target} poolt"
@ -255,6 +258,7 @@ et:
create_relay_html: "%{name} lõi sõnumivahendusserveri: %{target}"
create_unavailable_domain_html: "%{name} lõpetas edastamise domeeni %{target}"
create_user_role_html: "%{name} lõi rolli %{target}"
create_username_block_html: "%{name} lisas kasutajanime reegli, milles sisaldub %{target}"
demote_user_html: "%{name} alandas kasutajat %{target}"
destroy_announcement_html: "%{name} kustutas teadaande %{target}"
destroy_canonical_email_block_html: "%{name} eemaldas blokeeringu e-postilt räsiga %{target}"
@ -268,6 +272,7 @@ et:
destroy_status_html: "%{name} kustutas %{target} postituse"
destroy_unavailable_domain_html: "%{name} taastas edastamise domeeni %{target}"
destroy_user_role_html: "%{name} kustutas %{target} rolli"
destroy_username_block_html: "%{name} eemaldas kasutajanime reegli, milles sisaldub %{target}"
disable_2fa_user_html: "%{name} eemaldas kasutaja %{target} kahe etapise nõude"
disable_custom_emoji_html: "%{name} keelas emotikooni %{target}"
disable_relay_html: "%{name} eemaldas sõnumivahendusserveri kasutuselt: %{target}"
@ -302,6 +307,7 @@ et:
update_report_html: "%{name} uuendas raportit %{target}"
update_status_html: "%{name} muutis %{target} postitust"
update_user_role_html: "%{name} muutis %{target} rolli"
update_username_block_html: "%{name} uuendas kasutajanime reeglit, milles sisaldub %{target}"
deleted_account: kustutatud konto
empty: Logisi ei leitud.
filter_by_action: Filtreeri tegevuse järgi
@ -806,6 +812,8 @@ et:
preamble: Täpsem kirjeldus, kuidas serverit käitatakse, modereeritakse ja rahastatakse.
rules_hint: Reeglitele, millega kasutajad peavad nõustuma, on vastav piirkond.
title: Teave
allow_referrer_origin:
title: Luba välistel serveritel näha Mastodoni teenust linkide viitajana
appearance:
preamble: Kohanda Mastodoni veebiliidest.
title: Välimus
@ -993,6 +1001,7 @@ et:
other: Saada %{display_count} e-kirja
publish: Postita
published_on_html: Postitatud %{date}
save_draft: Salvesta kavandina
title: Kasutustingimused
title: Administreerimine
trends:
@ -1069,6 +1078,25 @@ et:
other: Kasutatud %{count} kasutaja poolt viimase nädala jooksul
title: Soovitused ja trendid
trending: Trendid
username_blocks:
add_new: Lisa uus
block_registrations: Blokeeri registreerimisi
comparison:
contains: Sisaldab
equals: Võrdub
contains_html: 'Sisaldab: %{string}'
created_msg: Kasutajanime reegli lisamine õnnestus
delete: Kustuta
edit:
title: Muuda kasutajanime reeglit
matches_exactly_html: 'Võrdub: %{string}'
new:
create: Lisa reegel
title: Lisa uus kasutajanime reegel
no_username_block_selected: Ühtegi kasutajanimereeglit ei muudetud, kuna midagi polnud valitud
not_permitted: Ei ole lubatud
title: Kasutajanime reeglid
updated_msg: Kasutajanime reegli uuendamine õnnestus
warning_presets:
add_new: Lisa uus
delete: Kustuta
@ -1331,6 +1359,10 @@ et:
basic_information: Põhiinfo
hint_html: "<strong>Kohanda, mida inimesed näevad su avalikul profiilil ja postituste kõrval.</strong> Inimesed alustavad tõenäolisemalt sinu jälgimist ja interakteeruvad sinuga, kui sul on täidetud profiil ja profiilipilt."
other: Muu
emoji_styles:
auto: Automaatne
native: Rakenduseomane
twemoji: Twemoji
errors:
'400': Esitatud päring oli vigane või valesti vormindatud.
'403': Sul puudub õigus seda lehte vaadata.
@ -1850,6 +1882,7 @@ et:
edited_at_html: Muudetud %{date}
errors:
in_reply_not_found: Postitus, millele üritad vastata, ei näi enam eksisteerivat.
quoted_status_not_found: Postitus, mida üritad tsiteerida, ei näi enam eksisteerivat.
over_character_limit: tähtmärkide limiit %{max} ületatud
pin_errors:
direct: Ei saa kinnitada postitusi, mis on nähtavad vaid mainitud kasutajatele

View File

@ -190,6 +190,7 @@ fo:
create_relay: Stovna reiðlag
create_unavailable_domain: Stovna navnaøki, sum ikki er tøkt
create_user_role: Stovna leiklut
create_username_block: Stovna brúkaranavnsreglu
demote_user: Lækka brúkara í tign
destroy_announcement: Strika kunngerð
destroy_canonical_email_block: Strika t-postablokk
@ -203,6 +204,7 @@ fo:
destroy_status: Strika post
destroy_unavailable_domain: Strika navnaøki, sum ikki er tøkt
destroy_user_role: Bein burtur leiklut
destroy_username_block: Strika brúkaranavnsreglu
disable_2fa_user: Ger 2FA óvirkið
disable_custom_emoji: Ger serligt kenslutekn óvirkið
disable_relay: Ger reiðlag óvirkið
@ -237,6 +239,7 @@ fo:
update_report: Dagfør frágreiðing
update_status: Dagfør Uppslag
update_user_role: Dagfør Leiklut
update_username_block: Dagfør brúkaranavnsreglu
actions:
approve_appeal_html: "%{name} góðkendi umsjónaráheitan frá %{target}"
approve_user_html: "%{name} góðtók umsókn frá %{target}"
@ -255,6 +258,7 @@ fo:
create_relay_html: "%{name} gjørdi eitt reiðlag %{target}"
create_unavailable_domain_html: "%{name} steðgaði veiting til navnaøkið %{target}"
create_user_role_html: "%{name} stovnaði %{target} leiklutin"
create_username_block_html: "%{name} legði reglu afturat fyri brúkaranøvn, sum innihalda %{target}"
demote_user_html: "%{name} lækkaði tignina hjá brúkaranum %{target}"
destroy_announcement_html: "%{name} strikaðar fráboðanir %{target}"
destroy_canonical_email_block_html: "%{name} strikaði blokeringina av teldupostin við hashkodu %{target}"
@ -268,6 +272,7 @@ fo:
destroy_status_html: "%{name} slettaði upplegg hjá %{target}"
destroy_unavailable_domain_html: "%{name} tók upp aftir veiting til navnaøkið %{target}"
destroy_user_role_html: "%{name} slettaði leiklutin hjá %{target}"
destroy_username_block_html: "%{name} strikaði reglu fyri brúkaranøvn, sum innihalda %{target}"
disable_2fa_user_html: "%{name} slepti kravið um váttan í tveimum stigum fyri brúkaran %{target}"
disable_custom_emoji_html: "%{name} gjørdi kensluteknið %{target} óvirkið"
disable_relay_html: "%{name} gjørdi reiðlagið %{target} óvirkið"
@ -302,6 +307,7 @@ fo:
update_report_html: "%{name} dagførdi meldingina %{target}"
update_status_html: "%{name} dagførdi postin hjá %{target}"
update_user_role_html: "%{name} broyttir %{target} leiklutir"
update_username_block_html: "%{name} dagførdi reglu fyri brúkaranøvn, sum innihalda %{target}"
deleted_account: strikað konta
empty: Eingir loggar funnir.
filter_by_action: Filtrera eftir atgerð
@ -1085,6 +1091,25 @@ fo:
other: Brúkt av %{count} brúkarum seinastu vikuna
title: Tilmæli & rák
trending: Vælumtókt
username_blocks:
add_new: Legg afturat
block_registrations: Blokera skrásetingar
comparison:
contains: Inniheldur
equals: Er tað sama sum
contains_html: Inniheldur %{string}
created_msg: Eydnaðist at leggja brúkaranavnsreglu afturat
delete: Strika
edit:
title: Broyt brúkaranavnsreglu
matches_exactly_html: Tað sama sum %{string}
new:
create: Stovna reglu
title: Stovna brúkaranavnsreglu
no_username_block_selected: Ongar brúkaranavnsreglur vóru broyttar, tí ongar vóru valdar
not_permitted: Ikki loyvt
title: Brúkaranavnsreglur
updated_msg: Eydnaðist at dagføra brúkaranavnsreglu
warning_presets:
add_new: Legg afturat
delete: Strika

View File

@ -190,6 +190,7 @@ fy:
create_relay: Relay oanmeitsje
create_unavailable_domain: Net beskikber domein oanmeitsje
create_user_role: Rol oanmeitsje
create_username_block: Brûkersnammeregel oanmeitsje
demote_user: Brûker degradearje
destroy_announcement: Meidieling fuortsmite
destroy_canonical_email_block: E-mailblokkade fuortsmite
@ -203,6 +204,7 @@ fy:
destroy_status: Toot fuortsmite
destroy_unavailable_domain: Net beskikber domein fuortsmite
destroy_user_role: Rol permanint fuortsmite
destroy_username_block: Brûkersnammeregel fuortsmite
disable_2fa_user: Twa-stapsferifikaasje útskeakelje
disable_custom_emoji: Lokale emoji útskeakelje
disable_relay: Relay útskeakelje
@ -237,6 +239,7 @@ fy:
update_report: Rapportaazje bywurkje
update_status: Berjocht bywurkje
update_user_role: Rol bywurkje
update_username_block: Brûkersnammeregel bywurkje
actions:
approve_appeal_html: "%{name} hat it beswier tsjin de moderaasjemaatregel fan %{target} goedkard"
approve_user_html: "%{name} hat de registraasje fan %{target} goedkard"
@ -255,6 +258,7 @@ fy:
create_relay_html: "%{name} hat in relay oanmakke %{target}"
create_unavailable_domain_html: "%{name} hat de besoarging foar domein %{target} beëinige"
create_user_role_html: "%{name} hat de rol %{target} oanmakke"
create_username_block_html: "%{name} hat in brûkersnammeregel mei %{target} tafoege"
demote_user_html: Brûker %{target} is troch %{name} degradearre
destroy_announcement_html: "%{name} hat de meidieling %{target} fuortsmiten"
destroy_canonical_email_block_html: "%{name} hat it e-mailberjocht mei de hash %{target} deblokkearre"
@ -268,6 +272,7 @@ fy:
destroy_status_html: Berjocht fan %{target} is troch %{name} fuortsmiten
destroy_unavailable_domain_html: "%{name} hat de besoarging foar domein %{target} opnij starte"
destroy_user_role_html: "%{name} hat de rol %{target} fuortsmiten"
destroy_username_block_html: "%{name} hat in brûkersnammeregel mei %{target} fuortsmiten"
disable_2fa_user_html: De fereaske twa-stapsferifikaasje foar %{target} is troch %{name} útskeakele
disable_custom_emoji_html: Emoji %{target} is troch %{name} útskeakele
disable_relay_html: "%{name} hat de relay %{target} útskeakele"
@ -302,6 +307,7 @@ fy:
update_report_html: Rapportaazje %{target} is troch %{name} bywurke
update_status_html: "%{name} hat de berjochten %{target} bywurke"
update_user_role_html: "%{name} hat de rol %{target} wizige"
update_username_block_html: "%{name} hat in brûkersnammeregel mei %{target} bywurke"
deleted_account: fuortsmiten account
empty: Gjin lochboeken fûn.
filter_by_action: Op aksje filterje
@ -1085,6 +1091,25 @@ fy:
other: Dizze wike troch %{count} persoanen brûkt
title: Oanrekommandaasjes & trends
trending: Trending
username_blocks:
add_new: Nije tafoegje
block_registrations: Registraasjes blokkearje
comparison:
contains: Befettet
equals: Is lyk oan
contains_html: Befettet %{string}
created_msg: Brûkersnammeregel mei sukses oanmakke
delete: Fuortsmite
edit:
title: Brûkersnammeregel bywurkje
matches_exactly_html: Is lyk oan %{string}
new:
create: Regel oanmeitsje
title: Brûkersnammeregel oanmeitsje
no_username_block_selected: Der binne gjin brûkersnammeregels wizige, omdat der gjin ien selektearre waard
not_permitted: Net tastien
title: Brûkersnammeregels
updated_msg: Brûkersnammeregel mei sukses bywurke
warning_presets:
add_new: Nije tafoegje
delete: Fuortsmite
@ -1872,6 +1897,7 @@ fy:
edited_at_html: Bewurke op %{date}
errors:
in_reply_not_found: It berjocht wêrop jo probearje te reagearjen liket net te bestean.
quoted_status_not_found: It berjocht dyt jo probearje te sitearjen liket net te bestean.
over_character_limit: Oer de limyt fan %{max} tekens
pin_errors:
direct: Berjochten dyt allinnich sichtber binne foar fermelde brûkers kinne net fêstset wurde

View File

@ -190,6 +190,7 @@ gl:
create_relay: Crear Repetidor
create_unavailable_domain: Crear dominio Non dispoñible
create_user_role: Crear Rol
create_username_block: Crear regra para Identificadores
demote_user: Degradar usuaria
destroy_announcement: Eliminar anuncio
destroy_canonical_email_block: Eliminar bloqueo do correo
@ -203,6 +204,7 @@ gl:
destroy_status: Eliminar publicación
destroy_unavailable_domain: Eliminar dominio Non dispoñible
destroy_user_role: Eliminar Rol
destroy_username_block: Eliminar regra para Identificadores
disable_2fa_user: Desactivar 2FA
disable_custom_emoji: Desactivar emoticona personalizada
disable_relay: Desactivar Repetidor
@ -237,6 +239,7 @@ gl:
update_report: Actualización da denuncia
update_status: Actualizar publicación
update_user_role: Actualizar Rol
update_username_block: Actualizar regra para Identificadores
actions:
approve_appeal_html: "%{name} aprobou a apelación da decisión da moderación de %{target}"
approve_user_html: "%{name} aprobou o rexistro de %{target}"
@ -255,6 +258,7 @@ gl:
create_relay_html: "%{name} creou un repetidor en %{target}"
create_unavailable_domain_html: "%{name} deixou de interactuar co dominio %{target}"
create_user_role_html: "%{name} creou o rol %{target}"
create_username_block_html: "%{name} engadiu unha regra para identificadores que contén %{target}"
demote_user_html: "%{name} degradou a usuaria %{target}"
destroy_announcement_html: "%{name} eliminou o anuncio %{target}"
destroy_canonical_email_block_html: "%{name} desbloqueou o correo con suma de comprobación %{target}"
@ -268,6 +272,7 @@ gl:
destroy_status_html: "%{name} eliminou a publicación de %{target}"
destroy_unavailable_domain_html: "%{name} retomou a interacción co dominio %{target}"
destroy_user_role_html: "%{name} eliminou o rol %{target}"
destroy_username_block_html: "%{name} retirou unha regra para identificadores que contén %{target}"
disable_2fa_user_html: "%{name} desactivou o requerimento do segundo factor para a usuaria %{target}"
disable_custom_emoji_html: "%{name} desactivou o emoji %{target}"
disable_relay_html: "%{name} desactivou o repetidor %{target}"
@ -302,6 +307,7 @@ gl:
update_report_html: "%{name} actualizou a denuncia %{target}"
update_status_html: "%{name} actualizou a publicación de %{target}"
update_user_role_html: "%{name} cambiou o rol %{target}"
update_username_block_html: "%{name} actualizou a regra para identificadores que contén %{target}"
deleted_account: conta eliminada
empty: Non se atoparon rexistros.
filter_by_action: Filtrar por acción
@ -1085,6 +1091,25 @@ gl:
other: Utilizado por %{count} persoas na última semana
title: Recomendacións e Suxestións
trending: Popularidade
username_blocks:
add_new: Engadir nova
block_registrations: Bloqueo dos rexistros
comparison:
contains: Contén
equals: É igual a
contains_html: Contén %{string}
created_msg: Creouse correctamente a regra para identificadores
delete: Eliminar
edit:
title: Editar regra para identificadores
matches_exactly_html: É igual a %{string}
new:
create: Crear regra
title: Crear nova regra para identificadores
no_username_block_selected: Non se cambiou ningunha regra porque non se seleccionou ningunha
not_permitted: Non permitido
title: Regras para identificadores
updated_msg: Actualizouse correctamente a regra
warning_presets:
add_new: Engadir novo
delete: Eliminar

View File

@ -196,6 +196,7 @@ he:
create_relay: יצירת ממסר
create_unavailable_domain: יצירת דומיין בלתי זמין
create_user_role: יצירת תפקיד
create_username_block: יצירת חוק שמות משתמש
demote_user: הורדת משתמש בדרגה
destroy_announcement: מחיקת הכרזה
destroy_canonical_email_block: מחיקת חסימת דואל
@ -209,6 +210,7 @@ he:
destroy_status: מחיקת הודעה
destroy_unavailable_domain: מחיקת דומיין בלתי זמין
destroy_user_role: מחיקת תפקיד
destroy_username_block: מחיקת חוק שמות משתמש
disable_2fa_user: השעיית זיהוי דו-גורמי
disable_custom_emoji: השעיית אמוג'י מיוחד
disable_relay: השבתת ממסר
@ -243,6 +245,7 @@ he:
update_report: עדכון דו"ח עבירה
update_status: סטטוס עדכון
update_user_role: עדכון תפקיד
update_username_block: עדכון חוק שמות משתמש
actions:
approve_appeal_html: "%{name} אישר/ה ערעור על החלטת מנהלי הקהילה מ-%{target}"
approve_user_html: "%{name} אישר/ה הרשמה מ-%{target}"
@ -261,6 +264,7 @@ he:
create_relay_html: "%{name} יצרו את הממסר %{target}"
create_unavailable_domain_html: "%{name} הפסיק/ה משלוח לדומיין %{target}"
create_user_role_html: "%{name} יצר את התפקיד של %{target}"
create_username_block_html: "%{name} הוסיפו חוק לשמות משתמש המכילים %{target}"
demote_user_html: "%{name} הוריד/ה בדרגה את המשתמש %{target}"
destroy_announcement_html: "%{name} מחק/ה את ההכרזה %{target}"
destroy_canonical_email_block_html: "%{name} הסירו חסימה מדואל %{target}"
@ -274,6 +278,7 @@ he:
destroy_status_html: ההודעה של %{target} הוסרה ע"י %{name}
destroy_unavailable_domain_html: "%{name} התחיל/ה מחדש משלוח לדומיין %{target}"
destroy_user_role_html: "%{name} ביטל את התפקיד של %{target}"
destroy_username_block_html: "%{name} הסירו חוק לשמות משתמש המכילים %{target}"
disable_2fa_user_html: "%{name} ביטל/ה את הדרישה לאימות דו-גורמי למשתמש %{target}"
disable_custom_emoji_html: "%{name} השבית/ה את האמוג'י %{target}"
disable_relay_html: "%{name} השביתו את הממסר %{target}"
@ -308,6 +313,7 @@ he:
update_report_html: '%{name} עדכן/ה דו"ח %{target}'
update_status_html: "%{name} עדכן/ה הודעה של %{target}"
update_user_role_html: "%{name} שינה את התפקיד של %{target}"
update_username_block_html: "%{name} עידכנו חוק לשמות משתמש המכילים %{target}"
deleted_account: חשבון מחוק
empty: לא נמצאו יומנים.
filter_by_action: סינון לפי פעולה
@ -1121,6 +1127,25 @@ he:
two: הוצגה על ידי %{count} משתמשים במשך השבוע שעבר
title: המלצות ונושאים חמים
trending: נושאים חמים
username_blocks:
add_new: הוספת חדש
block_registrations: חסימת הרשמות
comparison:
contains: מכיל
equals: שווה
contains_html: מכיל %{string}
created_msg: חוק שמות משתמשים נוצר בהצלחה
delete: מחיקה
edit:
title: עריכת חוק שמות משתמש
matches_exactly_html: מתאים ל־%{string}
new:
create: יצירת כלל
title: יצירת חוק חדש לשמות משתמש
no_username_block_selected: לא בוצעו שינויים בחוקי בחירת שמות שכן לא נבחרו כאלו
not_permitted: שמות אסורים
title: חוקי שמות
updated_msg: חוק שמות משתמשים עודכן בהצלחה
warning_presets:
add_new: הוספת חדש
delete: למחוק

View File

@ -190,6 +190,7 @@ is:
create_relay: Búa til endurvarpa
create_unavailable_domain: Útbúa lén sem ekki er tiltækt
create_user_role: Útbúa hlutverk
create_username_block: Búa til notandanafnsreglu
demote_user: Lækka notanda í tign
destroy_announcement: Eyða tilkynningu
destroy_canonical_email_block: Eyða útilokunarblokk tölvupósts
@ -203,6 +204,7 @@ is:
destroy_status: Eyða færslu
destroy_unavailable_domain: Eyða léni sem ekki er tiltækt
destroy_user_role: Eyða hlutverki
destroy_username_block: Eyða notandanafnsreglu
disable_2fa_user: Gera tveggja-þátta auðkenningu óvirka
disable_custom_emoji: Gera sérsniðið tjáningartákn óvirkt
disable_relay: Gera endurvarpa óvirkan
@ -237,6 +239,7 @@ is:
update_report: Uppfæra kæru
update_status: Uppfæra færslu
update_user_role: Uppfæra hlutverk
update_username_block: Uppfæra notandanafnsreglu
actions:
approve_appeal_html: "%{name} samþykkti áfrýjun á ákvörðun umsjónarmanns frá %{target}"
approve_user_html: "%{name} samþykkti nýskráningu frá %{target}"
@ -255,6 +258,7 @@ is:
create_relay_html: "%{name} bjó til endurvarpa %{target}"
create_unavailable_domain_html: "%{name} stöðvaði afhendingu til lénsins %{target}"
create_user_role_html: "%{name} útbjó %{target} hlutverk"
create_username_block_html: "%{name} bætti við reglu varðandi notendanöfn sem innihalda %{target}"
demote_user_html: "%{name} lækkaði notandann %{target} í tign"
destroy_announcement_html: "%{name} eyddi tilkynninguni %{target}"
destroy_canonical_email_block_html: "%{name} tók af útilokun á tölvupósti með tætigildið %{target}"
@ -268,6 +272,7 @@ is:
destroy_status_html: "%{name} fjarlægði færslu frá %{target}"
destroy_unavailable_domain_html: "%{name} hóf aftur afhendingu til lénsins %{target}"
destroy_user_role_html: "%{name} eyddi hlutverki %{target}"
destroy_username_block_html: "%{name} fjarlægði reglu varðandi notendanöfn sem innihalda %{target}"
disable_2fa_user_html: "%{name} gerði kröfu um tveggja-þátta innskráningu óvirka fyrir notandann %{target}"
disable_custom_emoji_html: "%{name} gerði tjáningartáknið %{target} óvirkt"
disable_relay_html: "%{name} gerði endurvarpann %{target} óvirkan"
@ -302,6 +307,7 @@ is:
update_report_html: "%{name} uppfærði kæru %{target}"
update_status_html: "%{name} uppfærði færslu frá %{target}"
update_user_role_html: "%{name} breytti hlutverki %{target}"
update_username_block_html: "%{name} uppfærði reglu varðandi notendanöfn sem innihalda %{target}"
deleted_account: eyddur notandaaðgangur
empty: Engar atvikaskrár fundust.
filter_by_action: Sía eftir aðgerð
@ -1087,6 +1093,25 @@ is:
other: Notað af %{count} aðilum síðustu vikuna
title: Meðmæli og vinsælt
trending: Vinsælt
username_blocks:
add_new: Bæta við nýju
block_registrations: Loka á nýskráningar
comparison:
contains: Inniheldur
equals: Er jafnt og
contains_html: Inniheldur %{string}
created_msg: Tókst að útbúa notandanafnsreglu
delete: Eyða
edit:
title: Breyta notandanafnsreglu
matches_exactly_html: Er jafnt og %{string}
new:
create: Búa til reglu
title: Búa til nýja notandanafnsreglu
no_username_block_selected: Engum notandanafnsreglum var breytt þar sem engar voru valdar
not_permitted: Ekki leyft
title: Notendanafnareglur
updated_msg: Tókst að uppfæra notandanafnsreglu
warning_presets:
add_new: Bæta við nýju
delete: Eyða

View File

@ -181,7 +181,7 @@ ko:
create_canonical_email_block: 이메일 차단 생성
create_custom_emoji: 커스텀 에모지 생성
create_domain_allow: 도메인 허용 생성
create_domain_block: 도메인 차단 추가
create_domain_block: 도메인 차단 만들기
create_email_domain_block: 이메일 도메인 차단 생성
create_ip_block: IP 규칙 만들기
create_relay: 릴레이 생성
@ -1069,6 +1069,12 @@ ko:
other: 지난 주 동안 %{count} 명의 사람들이 사용했습니다
title: 추천과 유행
trending: 유행 중
username_blocks:
add_new: 새로 추가
new:
create: 규칙 만들기
title: 새 유저네임 규칙 만들기
not_permitted: 허용하지 않음
warning_presets:
add_new: 새로 추가
delete: 삭제

View File

@ -160,6 +160,10 @@ da:
name: Offentligt rollennavn, hvis rollen er opsat til fremstå som et badge
permissions_as_keys: Brugere med denne rolle vil kunne tilgå...
position: Højere rolle bestemmer konfliktløsning i visse situationer. Visse handlinger kan kun udføres på roller med lavere prioritet
username_block:
allow_with_approval: I stedet for at forhindre tilmelding helt, vil matchende tilmeldinger kræve din godkendelse
comparison: Vær opmærksom på Scunthorpe-problemet ved blokering af delvise match
username: Matches uanset minuskel/majuskel-brug og alm. homoglyffer, såsom "4" for "a" eller "3" for "e"
webhook:
events: Vælg begivenheder at sende
template: Skriv din egen JSON nyttelast ved hjælp af variabel interpolation. Lad feltet stå tomt for standard JSON.
@ -371,6 +375,10 @@ da:
name: Navn
permissions_as_keys: Tilladelser
position: Prioritet
username_block:
allow_with_approval: Tillad registreringer med godkendelse
comparison: Sammenligningsmetode
username: Ord, som skal matches
webhook:
events: Aktive begivenheder
template: Payload skabelon

View File

@ -160,6 +160,10 @@ de:
name: Name der Rolle, der auch öffentlich als Badge angezeigt wird, sofern dies unten aktiviert ist
permissions_as_keys: Benutzer*innen mit dieser Rolle haben Zugriff auf...
position: Höhere Rollen entscheiden über Konfliktlösungen zu gewissen Situationen. Bestimmte Aktionen können nur mit geringfügigeren Rollen durchgeführt werden
username_block:
allow_with_approval: Anstatt Registrierungen komplett zu verhindern, benötigen übereinstimmende Treffer eine Genehmigung
comparison: Bitte beachte das Scunthorpe-Problem, wenn teilweise übereinstimmende Treffer gesperrt werden
username: Abgleich erfolgt unabhängig der Groß- und Kleinschreibung sowie gängiger Homoglyphen („4“ für „a“ oder „3“ für „e“)
webhook:
events: Zu sendende Ereignisse auswählen
template: Erstelle deine eigenen JSON-Nutzdaten mit Hilfe von Variablen-Interpolation. Leer lassen für Standard-JSON.
@ -371,6 +375,10 @@ de:
name: Name
permissions_as_keys: Berechtigungen
position: Priorität
username_block:
allow_with_approval: Registrierungen mit Genehmigung zulassen
comparison: Vergleichsmethode
username: Übereinstimmendes Wort
webhook:
events: Aktivierte Ereignisse
template: Nutzdaten-Vorlage

View File

@ -160,6 +160,10 @@ es-AR:
name: Nombre público del rol, si el rol se establece para que se muestre como una insignia
permissions_as_keys: Los usuarios con este rol tendrán acceso a…
position: Un rol más alto decide la resolución de conflictos en ciertas situaciones. Ciertas acciones sólo pueden llevarse a cabo en roles con prioridad inferior
username_block:
allow_with_approval: En lugar de impedir el registro total, los registros coincidentes requerirán tu aprobación
comparison: Por favor, tené en cuenta el Problema de Scunthorpe al bloquear coincidencias parciales
username: Coincidirá sin importar la capitalización de letras y los homoglifos comunes como «4» para «a», o «3» para «e»
webhook:
events: Seleccionar eventos para enviar
template: Creá tu propio archivo JSON usando interpolación variable. Dejalo en blanco para usar el archivo JSON predeterminado.
@ -371,6 +375,10 @@ es-AR:
name: Nombre
permissions_as_keys: Permisos
position: Prioridad
username_block:
allow_with_approval: Permitir registros con aprobación
comparison: Método de comparación
username: Palabra a coincidir
webhook:
events: Eventos habilitados
template: Plantilla de carga

View File

@ -160,6 +160,10 @@ es-MX:
name: Nombre público del rol, si el rol se establece para que se muestre como una insignia
permissions_as_keys: Los usuarios con este rol tendrán acceso a...
position: Un rol superior decide la resolución de conflictos en ciertas situaciones. Ciertas acciones sólo pueden llevarse a cabo en roles con menor prioridad
username_block:
allow_with_approval: En lugar de impedir directamente el registro, los registros coincidentes requerirán tu aprobación
comparison: Por favor ten en cuenta el problema de Scunthorpe al bloquear coincidencias parciales
username: Se emparejará independientemente de las mayúsculas y minúsculas y de los homógrafos comunes como «4» por «a» o «3» por «e»
webhook:
events: Seleccionar eventos para enviar
template: Crea tu propio JSON usando interpolación variable. Déjalo en blanco para el JSON predeterminado.
@ -371,6 +375,10 @@ es-MX:
name: Nombre
permissions_as_keys: Permisos
position: Prioridad
username_block:
allow_with_approval: Permitir registros con aprobación previa
comparison: Método de comparación
username: Palabra a coincidir
webhook:
events: Eventos habilitados
template: Plantilla de carga

View File

@ -160,6 +160,10 @@ es:
name: Nombre público del rol, si el rol se establece para que se muestre como una insignia
permissions_as_keys: Los usuarios con este rol tendrán acceso a...
position: Un rol superior decide la resolución de conflictos en ciertas situaciones. Ciertas acciones sólo pueden llevarse a cabo en roles con menor prioridad
username_block:
allow_with_approval: En lugar de impedir directamente el registro, los registros coincidentes requerirán tu aprobación
comparison: Por favor, ten en cuenta el problema de Scunthorpe al bloquear coincidencias parciales
username: Se emparejará independientemente de la mayúscula o minúscula y de homógrafos comunes como «4» por «a» o «3» por «e»
webhook:
events: Seleccionar eventos para enviar
template: Crea tu propio JSON usando interpolación variable. Déjalo en blanco para el JSON predeterminado.
@ -371,6 +375,10 @@ es:
name: Nombre
permissions_as_keys: Permisos
position: Prioridad
username_block:
allow_with_approval: Permitir registros con aprobación
comparison: Método de comparación
username: Palabra a coincidir
webhook:
events: Eventos habilitados
template: Plantilla de carga

View File

@ -10,6 +10,7 @@ et:
indexable: Sinu avalikud postitused võivad ilmuda Mastodoni otsingutulemustes. Inimesed, kes on sinu postitustele reageerinud, saavad neid otsida nii või naa.
note: 'Saad @mainida teisi inimesi või #silte.'
show_collections: Inimesed saavad sirvida su jälgijaid ja jälgitavaid. Inimesed, keda sa jälgid, näevad seda sõltumata häälestuse valikust.
unlocked: Teised kasutajad saavad sind jälgima hakata nõusolekut küsimata. Eemalda märge, kui soovid jälgimistaotlusi üle vaadata ja valida, kas nõustuda või keelduda uute jälgijatega.
account_alias:
acct: Sisesta konto kasutajanimi@domeen, mille soovid siia ümber kolida
account_migration:
@ -59,6 +60,7 @@ et:
setting_display_media_default: Peida tundlikuks märgitud meedia
setting_display_media_hide_all: Alati peida kõik meedia
setting_display_media_show_all: Alati näita tundlikuks märgistatud meedia
setting_system_scrollbars_ui: Kehtib vaid Safaril ja Chrome'il põhinevatel tavaarvuti veebibrauserite puhul
setting_use_blurhash: Värvid põhinevad peidetud visuaalidel, kuid hägustavad igasuguseid detaile
setting_use_pending_items: Voo automaatse kerimise asemel peida ajajoone uuendused kliki taha
username: Võid kasutada ladina tähti, numbreid ja allkriipsu

View File

@ -160,6 +160,10 @@ fo:
name: Almenna navnið á leiklutinum, um leikluturin er settur at verða vístur sum eitt tignarmerki
permissions_as_keys: Brúkarar við hesum leiklutinum fara at fáa atgongd til...
position: Hægri leiklutur er avgerandi fyri loysn av ósemjum í ávísum støðum. Ávísar atgerðir kunnu einans verða gjørdar móti leiklutum, sum hava eina lægri raðfesting
username_block:
allow_with_approval: Í staðin fyri at forða heilt fyri skráseting, fara samsvarandi skrásetingar at krevja, at tú góðkennir tær
comparison: Vinarliga gev Scunthorpe-trupulleikanum gætur, tá tú blokerar lutvís samsvar
username: Samsvar vera funnin óansæð stórar og smáar bókstavir og vanligar homoglyffir, sosum "4" fyri "a" og "3" fyri "e"
webhook:
events: Vel hendingar at senda
template: Samanset títt egna JSON farm við at brúka variabul-interpolasjón. Lat vera blankt fyri sjálvgaldandi JSON.
@ -371,6 +375,10 @@ fo:
name: Navn
permissions_as_keys: Loyvi
position: Raðfesting
username_block:
allow_with_approval: Loyv skrásetingum við góðkenning
comparison: Samanberingarmetoda
username: Orð, sum skal samsvara
webhook:
events: Virknar hendingar
template: Farmaformur

View File

@ -160,6 +160,10 @@ gl:
name: Nome público do rol, se o rol se mostra como unha insignia
permissions_as_keys: As usuarias con este rol terán acceso a...
position: O rol superior decide nos conflitos en certas situacións. Algunhas accións só poden aplicarse sobre roles cunha prioridade menor
username_block:
allow_with_approval: No lugar de evitar a cración directa de contas, as contas mediante regras van precisar a túa aprobación
comparison: Ten en conta o Sunthorpe Problem cando se bloquean coincidencias parciais
username: Vai crear unha coincidencia sen importar se son maiúsculas ou minúsculas e homoglifos como «4» por «a» ou «3» por «e».
webhook:
events: Escoller eventos a enviar
template: Crea o teu propio JSON interpolando variables. Déixao en branco para usar o JSON predeterminado.
@ -371,6 +375,10 @@ gl:
name: Nome
permissions_as_keys: Permisos
position: Prioridade
username_block:
allow_with_approval: Permitir crear contas con aprobación
comparison: Método de comparación
username: Palabra a comparar
webhook:
events: Eventos activados
template: Modelo de carga

View File

@ -162,6 +162,10 @@ he:
name: שם ציבורי של התפקיד, במידה והתפקיד מוגדר ככזה שמופיע כתג
permissions_as_keys: למשתמשים בתפקיד זה תהיה גישה ל...
position: תפקיד גבוה יותר מכריע בחילוקי דעות במצבים מסוימים. פעולות מסוימות יכולות להתבצע רק על תפקידים בדרגה נמוכה יותר
username_block:
allow_with_approval: במקום למנוע הרשמה לחלוטין, הרשמות חדשות יצטרכו לחכות לאישורך
comparison: יש להזהר מחסימת חלקי שמות קצרים מדי כדי להמנע מהתופעה הידועה בשם Scunthorpe problem
username: ההתאמה תתבצע ללא קשר לגודל אותיות או להומוגליפים נפוצים כגון "4" במקום "a" או "3" במקום "e"
webhook:
events: בחר אירועים לשליחה
template: ניתן להרכיב מטען JSON משלך בשימוש בשילוב משתנים. יש להשאיר ריק בשביל JSON ברירת המחדל.
@ -373,6 +377,10 @@ he:
name: שם
permissions_as_keys: הרשאות
position: עדיפות
username_block:
allow_with_approval: הרשאת הרשמה לאחר אישור
comparison: שיטת השוואה
username: מילה להתאמה
webhook:
events: אירועים מאופשרים
template: תבנית מטען

View File

@ -160,6 +160,10 @@ is:
name: Opinbert heiti hlutverks, ef birta á hlutverk sem merki
permissions_as_keys: Notendur með þetta hlutverk munu hafa aðgang að...
position: Rétthærra hlutverk ákvarðar lausn árekstra í ákveðnum tilfellum. Sumar aðgerðir er aðeins hægt að framkvæma á hlutverk með lægri forgangi
username_block:
allow_with_approval: Í stað þess að loka alfarið á nýskráningar, munu samsvarandi nýskráningar þurfa samþykki þitt
comparison: Hafðu í huga Scunthorpe-vandamálið (Scunthorpe inniheldur orð sem ýmsar síur reyna að banna) þegar þú útilokar samsvarandi orðhluta
username: Mun samsvara án tillits til stafstöðu eða algengra táknlíkinga á borð við "4" í stað "a" eða "3" í stað "e"
webhook:
events: Veldu atburði sem á að senda
template: Sníddu eigin JSON með breytilegri brúun. Skiljið eftir autt fyrir sjálfgefið JSON.
@ -371,6 +375,10 @@ is:
name: Nafn
permissions_as_keys: Heimildir
position: Forgangur
username_block:
allow_with_approval: Leyfa skráningar með samþykki
comparison: Aðferð við samanburð
username: Orð sem samsvara
webhook:
events: Virkjaðir atburðir
template: Sniðmát gagna

View File

@ -159,6 +159,10 @@ vi:
name: Tên công khai của vai trò, nếu vai trò được đặt để hiển thị dưới dạng huy hiệu
permissions_as_keys: Người có vai trò này sẽ có quyền truy cập vào...
position: Vai trò cao hơn sẽ có quyền quyết định xung đột trong các tình huống. Các vai trò có mức độ ưu tiên thấp hơn chỉ có thể thực hiện một số hành động nhất định
username_block:
allow_with_approval: Thay vì cấm đăng ký ngay lập tức, bạn sẽ duyệt phù hợp trước khi đăng ký
comparison: Xin hãy lưu ý đến Vấn đề Scunthorpe khi chặn các từ trùng khớp một phần
username: Sẽ được khớp bất kể chữ hoa và chữ tượng hình phổ biến như "4" cho "a" hoặc "3" cho "e"
webhook:
events: Chọn sự kiện để gửi
template: Soạn JSON payload của riêng bạn bằng phép nội suy biến. Để trống để dùng JSON mặc định.
@ -370,6 +374,10 @@ vi:
name: Tên
permissions_as_keys: Quyền
position: Mức độ ưu tiên
username_block:
allow_with_approval: Cho đăng ký nhưng duyệt thủ công
comparison: Phương pháp so sánh
username: Khớp từ
webhook:
events: Những sự kiện đã bật
template: Mẫu payload

View File

@ -159,6 +159,10 @@ zh-TW:
name: 角色的公開名稱,如果角色設定為顯示為徽章
permissions_as_keys: 有此角色的使用者將有權存取...
position: 某些情況下,衝突的解決方式由更高階的角色決定。某些動作只能由優先程度較低的角色執行
username_block:
allow_with_approval: 不直接禁止註冊,符合規則之註冊將需要您的審核
comparison: 當您封鎖部分匹配時,請留心 Scunthorpe 問題
username: 將匹配不限大小寫與常見同形異義字如以「4」代替「A」或以「3」代替「E」
webhook:
events: 請選擇要傳送的事件
template: 使用變數代換組合您自己的 JSON payload。留白以使用預設 JSON 。
@ -370,6 +374,10 @@ zh-TW:
name: 名稱
permissions_as_keys: 權限
position: 優先權
username_block:
allow_with_approval: 經審核後可註冊
comparison: 比較方式
username: 字詞匹配
webhook:
events: 已啟用的事件
template: Payload 樣板

View File

@ -1072,6 +1072,11 @@ uk:
other: Використали %{count} людей за минулий тиждень
title: Рекомендації та тренди
trending: Популярне
username_blocks:
comparison:
contains: Містить
equals: Дорівнює
delete: Видалити
warning_presets:
add_new: Додати новий
delete: Видалити

View File

@ -187,6 +187,7 @@ vi:
create_relay: Tạo relay
create_unavailable_domain: Bỏ liên hợp
create_user_role: Tạo vai trò
create_username_block: Tạo quy tắc tên người dùng
demote_user: Hạ vai trò
destroy_announcement: Xóa thông báo
destroy_canonical_email_block: Bỏ chặn địa chỉ email biến thể
@ -200,6 +201,7 @@ vi:
destroy_status: Xóa tút
destroy_unavailable_domain: Cho phép liên hợp
destroy_user_role: Xóa vai trò
destroy_username_block: Xóa quy tắc tên người dùng
disable_2fa_user: Vô hiệu hóa 2FA
disable_custom_emoji: Vô hiệu hóa emoji
disable_relay: Tắt relay
@ -234,6 +236,7 @@ vi:
update_report: Cập nhật báo cáo
update_status: Cập nhật tút
update_user_role: Cập nhật vai trò
update_username_block: Cập nhật quy tắc tên người dùng
actions:
approve_appeal_html: "%{name} đã chấp nhận khiếu nại từ %{target}"
approve_user_html: "%{name} đã chấp nhận đăng ký từ %{target}"
@ -252,6 +255,7 @@ vi:
create_relay_html: "%{name} đã tạo relay %{target}"
create_unavailable_domain_html: "%{name} đã bỏ liên hợp với máy chủ %{target}"
create_user_role_html: "%{name} đã tạo vai trò %{target}"
create_username_block_html: "%{name} thêm quy tắc cho tên người dùng chứa %{target}"
demote_user_html: "%{name} đã hạ vai trò của %{target}"
destroy_announcement_html: "%{name} đã xóa thông báo %{target}"
destroy_canonical_email_block_html: "%{name} đã bỏ chặn địa chỉ email biến thể %{target}"
@ -265,6 +269,7 @@ vi:
destroy_status_html: "%{name} đã xóa tút của %{target}"
destroy_unavailable_domain_html: "%{name} tiếp tục liên hợp với máy chủ %{target}"
destroy_user_role_html: "%{name} đã xóa vai trò %{target}"
destroy_username_block_html: "%{name} xóa quy tắc cho tên người dùng chứa %{target}"
disable_2fa_user_html: "%{name} đã vô hiệu hóa xác thực 2 bước của %{target}"
disable_custom_emoji_html: "%{name} đã ẩn emoji %{target}"
disable_relay_html: "%{name} đã tắt relay %{target}"
@ -299,6 +304,7 @@ vi:
update_report_html: "%{name} đã cập nhật báo cáo %{target}"
update_status_html: "%{name} đã cập nhật tút của %{target}"
update_user_role_html: "%{name} đã cập nhật vai trò %{target}"
update_username_block_html: "%{name} đã cập nhật quy tắc cho tên người dùng chứa %{target}"
deleted_account: tài khoản đã xóa
empty: Không tìm thấy bản ghi.
filter_by_action: Theo hành động
@ -1067,6 +1073,25 @@ vi:
other: "%{count} người dùng tuần rồi"
title: Đề xuất & Xu hướng
trending: Xu hướng
username_blocks:
add_new: Thêm mới
block_registrations: Cấm đăng ký
comparison:
contains: Có chứa
equals: Tương tự
contains_html: Có chứa %{string}
created_msg: Đã tạo quy tắc tên người dùng thành công
delete: Xóa
edit:
title: Sửa quy tắc tên người dùng
matches_exactly_html: Có chứa %{string}
new:
create: Tạo quy tắc
title: Tạo quy tắc tên người dùng mới
no_username_block_selected: Chưa chọn quy tắc tên người dùng nào
not_permitted: Cấm
title: Quy tắc tên người dùng
updated_msg: Đã cập nhật quy tắc tên người dùng thành công
warning_presets:
add_new: Thêm mới
delete: Xóa bỏ

View File

@ -187,6 +187,7 @@ zh-TW:
create_relay: 新增中繼
create_unavailable_domain: 新增無法存取的網域
create_user_role: 新增角色
create_username_block: 新增使用者名稱規則
demote_user: 將用戶降級
destroy_announcement: 刪除公告
destroy_canonical_email_block: 刪除電子郵件封鎖
@ -200,6 +201,7 @@ zh-TW:
destroy_status: 刪除狀態
destroy_unavailable_domain: 刪除無法存取的網域
destroy_user_role: 移除角色
destroy_username_block: 刪除使用者名稱規則
disable_2fa_user: 停用兩階段驗證
disable_custom_emoji: 停用自訂 emoji 表情符號
disable_relay: 停用中繼
@ -234,6 +236,7 @@ zh-TW:
update_report: 更新檢舉報告
update_status: 更新狀態
update_user_role: 更新角色
update_username_block: 變更使用者名稱規則
actions:
approve_appeal_html: "%{name} 已批准來自 %{target} 的審核決定申訴"
approve_user_html: "%{name} 已批准自 %{target} 而來的註冊"
@ -252,6 +255,7 @@ zh-TW:
create_relay_html: "%{name} 已新增中繼 %{target}"
create_unavailable_domain_html: "%{name} 停止發送至網域 %{target}"
create_user_role_html: "%{name} 已新增 %{target} 角色"
create_username_block_html: "%{name} 已新增使用者名稱包含 %{target} 之規則"
demote_user_html: "%{name} 將使用者 %{target} 降級"
destroy_announcement_html: "%{name} 已刪除公告 %{target}"
destroy_canonical_email_block_html: "%{name} 已解除封鎖 hash 為 %{target} 之電子郵件"
@ -265,6 +269,7 @@ zh-TW:
destroy_status_html: "%{name} 已刪除 %{target} 的嘟文"
destroy_unavailable_domain_html: "%{name} 已恢復對網域 %{target} 的發送"
destroy_user_role_html: "%{name} 已刪除 %{target} 角色"
destroy_username_block_html: "%{name} 已移除使用者名稱包含 %{target} 之規則"
disable_2fa_user_html: "%{name} 已停用使用者 %{target} 的兩階段驗證 (2FA) "
disable_custom_emoji_html: "%{name} 已停用自訂 emoji 表情符號 %{target}"
disable_relay_html: "%{name} 已停用中繼 %{target}"
@ -299,6 +304,7 @@ zh-TW:
update_report_html: "%{name} 已更新 %{target} 的檢舉"
update_status_html: "%{name} 已更新 %{target} 的嘟文"
update_user_role_html: "%{name} 已變更 %{target} 角色"
update_username_block_html: "%{name} 已變更使用者名稱包含 %{target} 之規則"
deleted_account: 已刪除帳號
empty: 找不到 log
filter_by_action: 按動作過濾
@ -1069,6 +1075,25 @@ zh-TW:
other: 上週被 %{count} 個人使用
title: 推薦內容與熱門趨勢
trending: 熱門
username_blocks:
add_new: 新增
block_registrations: 禁止註冊
comparison:
contains: 包含
equals: 等於
contains_html: 包含 %{string}
created_msg: 已成功新增使用者名稱規則
delete: 刪除
edit:
title: 編輯使用者名稱規則
matches_exactly_html: 等於 %{string}
new:
create: 新增規則
title: 新增使用者名稱規則
no_username_block_selected: 因未選取任何使用者名稱規則,所以什麼事都沒發生
not_permitted: 不允許
title: 使用者名稱規則
updated_msg: 已成功變更使用者名稱規則
warning_presets:
add_new: 新增
delete: 刪除

View File

@ -14,6 +14,7 @@ module Mastodon
class InvalidParameterError < Error; end
class SignatureVerificationError < Error; end
class MalformedHeaderError < Error; end
class RecursionLimitExceededError < Error; end
class UnexpectedResponseError < Error
attr_reader :response

View File

@ -313,9 +313,7 @@ module Mastodon::CLI
end
def combined_media_sum
Arel.sql(<<~SQL.squish)
COALESCE(file_file_size, 0) + COALESCE(thumbnail_file_size, 0)
SQL
MediaAttachment.combined_media_file_size
end
def preload_records_from_mixed_objects(objects)

View File

@ -63,7 +63,7 @@
"cocoon-js-vanilla": "^1.5.1",
"color-blend": "^4.0.0",
"core-js": "^3.30.2",
"cross-env": "^7.0.3",
"cross-env": "^10.0.0",
"detect-passive-events": "^2.0.3",
"emoji-mart": "npm:emoji-mart-lazyload@latest",
"emojibase": "^16.0.0",
@ -168,7 +168,7 @@
"eslint-import-resolver-typescript": "^4.2.5",
"eslint-plugin-formatjs": "^5.3.1",
"eslint-plugin-import": "~2.31.0",
"eslint-plugin-jsdoc": "^51.0.0",
"eslint-plugin-jsdoc": "^52.0.0",
"eslint-plugin-jsx-a11y": "~6.10.2",
"eslint-plugin-promise": "~7.2.1",
"eslint-plugin-react": "^7.37.4",

View File

@ -16,10 +16,12 @@ RSpec.describe AccountSuggestions::FriendsOfFriendsSource do
let!(:jerk) { Fabricate(:account, discoverable: true, hide_collections: false) }
let!(:larry) { Fabricate(:account, discoverable: true, hide_collections: false) }
let!(:morty) { Fabricate(:account, discoverable: true, hide_collections: false, memorial: true) }
let!(:joyce) { Fabricate(:account, discoverable: true, hide_collections: false) }
context 'with follows and blocks' do
before do
bob.block!(jerk)
bob.request_follow!(joyce)
FollowRecommendationMute.create!(account: bob, target_account: neil)
# bob follows eugen, alice and larry
@ -28,8 +30,8 @@ RSpec.describe AccountSuggestions::FriendsOfFriendsSource do
# alice follows eve and mallory
[john, mallory].each { |account| alice.follow!(account) }
# eugen follows eve, john, jerk, larry, neil and morty
[eve, mallory, jerk, larry, neil, morty].each { |account| eugen.follow!(account) }
# eugen follows eve, john, jerk, larry, neil, morty and joyce
[eve, mallory, jerk, larry, neil, morty, joyce].each { |account| eugen.follow!(account) }
end
it 'returns eligible accounts', :aggregate_failures do
@ -55,6 +57,9 @@ RSpec.describe AccountSuggestions::FriendsOfFriendsSource do
# morty is not included because his account is in memoriam
expect(results).to_not include([morty.id, :friends_of_friends])
# joyce is not included because there is already a pending follow request
expect(results).to_not include([joyce.id, :friends_of_friends])
end
end

View File

@ -313,6 +313,12 @@ RSpec.describe MediaAttachment, :attachment_processing do
end
end
describe '.combined_media_file_size' do
subject { described_class.combined_media_file_size }
it { is_expected.to be_an(Arel::Nodes::Grouping) }
end
private
def media_metadata

View File

@ -10,6 +10,8 @@ RSpec.describe User do
let(:account) { Fabricate(:account, username: 'alice') }
it_behaves_like 'two_factor_backupable'
it_behaves_like 'User::Activity'
it_behaves_like 'User::Confirmation'
describe 'otp_secret' do
it 'encrypts the saved value' do
@ -65,34 +67,6 @@ RSpec.describe User do
end
end
describe 'confirmed' do
it 'returns an array of users who are confirmed' do
Fabricate(:user, confirmed_at: nil)
confirmed_user = Fabricate(:user, confirmed_at: Time.zone.now)
expect(described_class.confirmed).to contain_exactly(confirmed_user)
end
end
describe 'signed_in_recently' do
it 'returns a relation of users who have signed in during the recent period' do
recent_sign_in_user = Fabricate(:user, current_sign_in_at: within_duration_window_days.ago)
Fabricate(:user, current_sign_in_at: exceed_duration_window_days.ago)
expect(described_class.signed_in_recently)
.to contain_exactly(recent_sign_in_user)
end
end
describe 'not_signed_in_recently' do
it 'returns a relation of users who have not signed in during the recent period' do
no_recent_sign_in_user = Fabricate(:user, current_sign_in_at: exceed_duration_window_days.ago)
Fabricate(:user, current_sign_in_at: within_duration_window_days.ago)
expect(described_class.not_signed_in_recently)
.to contain_exactly(no_recent_sign_in_user)
end
end
describe 'account_not_suspended' do
it 'returns with linked accounts that are not suspended' do
suspended_account = Fabricate(:account, suspended_at: 10.days.ago)
@ -127,14 +101,6 @@ RSpec.describe User do
expect(described_class.matches_ip('2160:2160::/32')).to contain_exactly(user1)
end
end
def exceed_duration_window_days
described_class::ACTIVE_DURATION + 2.days
end
def within_duration_window_days
described_class::ACTIVE_DURATION - 2.days
end
end
describe 'email domains denylist integration' do
@ -197,12 +163,13 @@ RSpec.describe User do
describe '#update_sign_in!' do
context 'with an existing user' do
let!(:user) { Fabricate :user, last_sign_in_at: 10.days.ago, current_sign_in_at: 1.hour.ago, sign_in_count: 123 }
let!(:user) { Fabricate :user, last_sign_in_at: 10.days.ago, current_sign_in_at:, sign_in_count: 123 }
let(:current_sign_in_at) { 1.hour.ago }
context 'with new sign in false' do
it 'updates timestamps but not counts' do
expect { user.update_sign_in!(new_sign_in: false) }
.to change(user, :last_sign_in_at)
.to change(user, :last_sign_in_at).to(current_sign_in_at)
.and change(user, :current_sign_in_at)
.and not_change(user, :sign_in_count)
end
@ -211,11 +178,22 @@ RSpec.describe User do
context 'with new sign in true' do
it 'updates timestamps and counts' do
expect { user.update_sign_in!(new_sign_in: true) }
.to change(user, :last_sign_in_at)
.to change(user, :last_sign_in_at).to(current_sign_in_at)
.and change(user, :current_sign_in_at)
.and change(user, :sign_in_count).by(1)
end
end
context 'when the user does not have a current_sign_in_at value' do
let(:current_sign_in_at) { nil }
before { travel_to(1.minute.ago) }
it 'updates last sign in to now' do
expect { user.update_sign_in! }
.to change(user, :last_sign_in_at).to(Time.now.utc)
end
end
end
context 'with a new user' do
@ -228,79 +206,6 @@ RSpec.describe User do
end
end
describe '#confirmed?' do
it 'returns true when a confirmed_at is set' do
user = Fabricate.build(:user, confirmed_at: Time.now.utc)
expect(user.confirmed?).to be true
end
it 'returns false if a confirmed_at is nil' do
user = Fabricate.build(:user, confirmed_at: nil)
expect(user.confirmed?).to be false
end
end
describe '#confirm' do
subject { user.confirm }
let(:new_email) { 'new-email@example.com' }
before do
allow(TriggerWebhookWorker).to receive(:perform_async)
end
context 'when the user is already confirmed' do
let!(:user) { Fabricate(:user, confirmed_at: Time.now.utc, approved: true, unconfirmed_email: new_email) }
it 'sets email to unconfirmed_email and does not trigger web hook' do
expect { subject }.to change { user.reload.email }.to(new_email)
expect(TriggerWebhookWorker).to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
end
end
context 'when the user is a new user' do
let(:user) { Fabricate(:user, confirmed_at: nil, unconfirmed_email: new_email) }
context 'when the user is already approved' do
before do
Setting.registrations_mode = 'approved'
user.approve!
end
it 'sets email to unconfirmed_email and triggers `account.approved` web hook' do
expect { subject }.to change { user.reload.email }.to(new_email)
expect(TriggerWebhookWorker).to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
end
end
context 'when the user does not require explicit approval' do
before do
Setting.registrations_mode = 'open'
end
it 'sets email to unconfirmed_email and triggers `account.approved` web hook' do
expect { subject }.to change { user.reload.email }.to(new_email)
expect(TriggerWebhookWorker).to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
end
end
context 'when the user requires explicit approval but is not approved' do
before do
Setting.registrations_mode = 'approved'
end
it 'sets email to unconfirmed_email and does not trigger web hook' do
expect { subject }.to change { user.reload.email }.to(new_email)
expect(TriggerWebhookWorker).to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
end
end
end
end
describe '#approve!' do
subject { user.approve! }

View File

@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.shared_examples 'User::Activity' do
before { stub_const 'User::ACTIVE_DURATION', 7.days }
describe 'Scopes' do
let!(:recent_sign_in_user) { Fabricate(:user, current_sign_in_at: 2.days.ago) }
let!(:no_recent_sign_in_user) { Fabricate(:user, current_sign_in_at: 10.days.ago) }
describe '.signed_in_recently' do
it 'returns users who have signed in during the recent period' do
expect(described_class.signed_in_recently)
.to contain_exactly(recent_sign_in_user)
end
end
describe '.not_signed_in_recently' do
it 'returns users who have not signed in during the recent period' do
expect(described_class.not_signed_in_recently)
.to contain_exactly(no_recent_sign_in_user)
end
end
end
describe '#signed_in_recently?' do
subject { Fabricate.build :user, current_sign_in_at: }
context 'when current_sign_in_at is nil' do
let(:current_sign_in_at) { nil }
it { is_expected.to_not be_signed_in_recently }
end
context 'when current_sign_in_at is before the threshold' do
let(:current_sign_in_at) { 10.days.ago }
it { is_expected.to_not be_signed_in_recently }
end
context 'when current_sign_in_at is after the threshold' do
let(:current_sign_in_at) { 2.days.ago }
it { is_expected.to be_signed_in_recently }
end
end
end

View File

@ -0,0 +1,114 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.shared_examples 'User::Confirmation' do
describe 'Scopes' do
let!(:unconfirmed_user) { Fabricate :user, confirmed_at: nil }
let!(:confirmed_user) { Fabricate :user, confirmed_at: Time.now.utc }
describe '.confirmed' do
it 'returns users who are confirmed' do
expect(described_class.confirmed)
.to contain_exactly(confirmed_user)
end
end
describe '.unconfirmed' do
it 'returns users who are not confirmed' do
expect(described_class.unconfirmed)
.to contain_exactly(unconfirmed_user)
end
end
end
describe '#confirmed?' do
subject { Fabricate.build(:user, confirmed_at:) }
context 'when confirmed_at is set' do
let(:confirmed_at) { Time.now.utc }
it { is_expected.to be_confirmed }
end
context 'when confirmed_at is not set' do
let(:confirmed_at) { nil }
it { is_expected.to_not be_confirmed }
end
end
describe '#unconfirmed?' do
subject { Fabricate.build(:user, confirmed_at:) }
context 'when confirmed_at is set' do
let(:confirmed_at) { Time.now.utc }
it { is_expected.to_not be_unconfirmed }
end
context 'when confirmed_at is not set' do
let(:confirmed_at) { nil }
it { is_expected.to be_unconfirmed }
end
end
describe '#confirm' do
subject { user.confirm }
let(:new_email) { 'new-email@host.example' }
before { allow(TriggerWebhookWorker).to receive(:perform_async) }
context 'when the user is already confirmed' do
let!(:user) { Fabricate(:user, confirmed_at: Time.now.utc, approved: true, unconfirmed_email: new_email) }
it 'sets email to unconfirmed_email and does not trigger web hook' do
expect { subject }
.to change { user.reload.email }.to(new_email)
expect(TriggerWebhookWorker)
.to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
end
end
context 'when the user is a new user' do
let(:user) { Fabricate(:user, confirmed_at: nil, unconfirmed_email: new_email) }
context 'when the user does not require explicit approval' do
before { Setting.registrations_mode = 'open' }
it 'sets email to unconfirmed_email and triggers `account.approved` web hook' do
expect { subject }
.to change { user.reload.email }.to(new_email)
expect(TriggerWebhookWorker)
.to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
end
end
context 'when registrations mode is approved' do
before { Setting.registrations_mode = 'approved' }
context 'when the user is already approved' do
before { user.approve! }
it 'sets email to unconfirmed_email and triggers `account.approved` web hook' do
expect { subject }
.to change { user.reload.email }.to(new_email)
expect(TriggerWebhookWorker)
.to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
end
end
context 'when the user is not approved' do
it 'sets email to unconfirmed_email and does not trigger web hook' do
expect { subject }
.to change { user.reload.email }.to(new_email)
expect(TriggerWebhookWorker)
.to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
end
end
end
end
end
end

View File

@ -57,6 +57,30 @@ RSpec.describe 'blocking domains through the moderation interface' do
end
end
context 'when suspending an already suspended domain and using a lower severity' do
before { Fabricate :domain_block, domain: 'example.com', severity: 'silence' }
it 'warns about downgrade and does not update' do
visit new_admin_domain_block_path
submit_domain_block('example.com', 'noop')
expect(page)
.to have_content(/You have already imposed stricter limits on example.com/)
end
end
context 'when failing to provide a domain value' do
it 'provides an error about the missing value' do
visit new_admin_domain_block_path
submit_domain_block('', 'noop')
expect(page)
.to have_content(/review the error below/)
end
end
context 'when suspending a subdomain of an already-silenced domain' do
it 'presents a confirmation screen before suspending the domain' do
domain_block = Fabricate(:domain_block, domain: 'example.com', severity: 'silence')

View File

@ -1939,6 +1939,13 @@ __metadata:
languageName: node
linkType: hard
"@epic-web/invariant@npm:^1.0.0":
version: 1.0.0
resolution: "@epic-web/invariant@npm:1.0.0"
checksum: 10c0/72dbeb026e4e4eb3bc9c65739b91408ca77ab7d603a2494fa2eff3790ec22892c4caba751cffdf30f5ccf0e7ba79c1e9c96cf0a357404b9432bf1365baac23ca
languageName: node
linkType: hard
"@es-joy/jsdoccomment@npm:~0.52.0":
version: 0.52.0
resolution: "@es-joy/jsdoccomment@npm:0.52.0"
@ -2665,7 +2672,7 @@ __metadata:
cocoon-js-vanilla: "npm:^1.5.1"
color-blend: "npm:^4.0.0"
core-js: "npm:^3.30.2"
cross-env: "npm:^7.0.3"
cross-env: "npm:^10.0.0"
detect-passive-events: "npm:^2.0.3"
emoji-mart: "npm:emoji-mart-lazyload@latest"
emojibase: "npm:^16.0.0"
@ -2676,7 +2683,7 @@ __metadata:
eslint-import-resolver-typescript: "npm:^4.2.5"
eslint-plugin-formatjs: "npm:^5.3.1"
eslint-plugin-import: "npm:~2.31.0"
eslint-plugin-jsdoc: "npm:^51.0.0"
eslint-plugin-jsdoc: "npm:^52.0.0"
eslint-plugin-jsx-a11y: "npm:~6.10.2"
eslint-plugin-promise: "npm:~7.2.1"
eslint-plugin-react: "npm:^7.37.4"
@ -6104,19 +6111,20 @@ __metadata:
languageName: node
linkType: hard
"cross-env@npm:^7.0.3":
version: 7.0.3
resolution: "cross-env@npm:7.0.3"
"cross-env@npm:^10.0.0":
version: 10.0.0
resolution: "cross-env@npm:10.0.0"
dependencies:
cross-spawn: "npm:^7.0.1"
"@epic-web/invariant": "npm:^1.0.0"
cross-spawn: "npm:^7.0.6"
bin:
cross-env: src/bin/cross-env.js
cross-env-shell: src/bin/cross-env-shell.js
checksum: 10c0/f3765c25746c69fcca369655c442c6c886e54ccf3ab8c16847d5ad0e91e2f337d36eedc6599c1227904bf2a228d721e690324446876115bc8e7b32a866735ecf
cross-env: dist/bin/cross-env.js
cross-env-shell: dist/bin/cross-env-shell.js
checksum: 10c0/d16ffc3734106577d57b6253d81ab50294623bd59f96e161033eaf99c1c308ffbaba8463c23a6c0f72e841eff467cb7007a0a551f27554fcf2bbf6598cd828f9
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.6":
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.6":
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
dependencies:
@ -7040,9 +7048,9 @@ __metadata:
languageName: node
linkType: hard
"eslint-plugin-jsdoc@npm:^51.0.0":
version: 51.3.4
resolution: "eslint-plugin-jsdoc@npm:51.3.4"
"eslint-plugin-jsdoc@npm:^52.0.0":
version: 52.0.1
resolution: "eslint-plugin-jsdoc@npm:52.0.1"
dependencies:
"@es-joy/jsdoccomment": "npm:~0.52.0"
are-docs-informative: "npm:^0.0.2"
@ -7056,7 +7064,7 @@ __metadata:
spdx-expression-parse: "npm:^4.0.0"
peerDependencies:
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
checksum: 10c0/59e5aa972bdd1bd4e2ca2796ed4455dff1069044abc028621e107aa4b0cbb62ce09554c8e7c2ff3a44a1cbd551e54b6970adc420ba3a89adc6236b94310a81ff
checksum: 10c0/0dac74a5ea1db8d15eea5a29a6dadd1361029230f414794abfd143b8cd0129d1cd862481d31eb0374b215d0841f41209fb8b4e36ac69b758094d7c77052432c0
languageName: node
linkType: hard