Merge commit 'e1d7efadc04dd0826c6bcfe43325688566e13881' into glitch-soc/merge-upstream

Conflicts:
- `app/models/status.rb`:
  Upstream refactored `visibility` handling to a concern, while glitch-soc had
  custom code adjacent to some of the changed lines.
  Applied upstream's changes.
- `lib/mastodon/version.rb`:
  Upstream moved some definitions to `config/mastodon.yml`, while glitch-soc's
  default github repository had been modified.
  Applied upstream's changes and updated `config/mastodon.yml` accordingly.
This commit is contained in:
Claire 2025-01-14 20:38:32 +01:00
commit ab152ebed4
84 changed files with 1448 additions and 261 deletions

View file

@ -153,6 +153,7 @@ RUN \
libpq-dev \
libssl-dev \
libtool \
libyaml-dev \
meson \
nasm \
pkg-config \

View file

@ -94,7 +94,7 @@ gem 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2023'
gem 'webauthn', '~> 3.0'
gem 'webpacker', '~> 5.4'
gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9'
gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913'
gem 'json-ld'
gem 'json-ld-preloaded', '~> 3.2'
@ -125,7 +125,7 @@ group :test do
gem 'flatware-rspec'
# Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab
gem 'rspec-github', '~> 2.4', require: false
gem 'rspec-github', '~> 3.0', require: false
# RSpec helpers for email specs
gem 'email_spec'

View file

@ -1,9 +1,9 @@
GIT
remote: https://github.com/ClearlyClaire/webpush.git
revision: f14a4d52e201128b1b00245d11b6de80d6cfdcd9
ref: f14a4d52e201128b1b00245d11b6de80d6cfdcd9
remote: https://github.com/mastodon/webpush.git
revision: 9631ac63045cfabddacc69fc06e919b4c13eb913
ref: 9631ac63045cfabddacc69fc06e919b4c13eb913
specs:
webpush (0.3.8)
webpush (1.1.0)
hkdf (~> 0.2)
jwt (~> 2.0)
@ -555,7 +555,7 @@ GEM
opentelemetry-api (~> 1.0)
orm_adapter (0.5.0)
ostruct (0.6.1)
ox (2.14.19)
ox (2.14.20)
bigdecimal (>= 3.0)
parallel (1.26.3)
parser (3.3.6.0)
@ -690,7 +690,7 @@ GEM
rspec-expectations (3.13.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-github (2.4.0)
rspec-github (3.0.0)
rspec-core (~> 3.0)
rspec-mocks (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
@ -994,7 +994,7 @@ DEPENDENCIES
redis (~> 4.5)
redis-namespace (~> 1.10)
rqrcode (~> 2.2)
rspec-github (~> 2.4)
rspec-github (~> 3.0)
rspec-rails (~> 7.0)
rspec-sidekiq (~> 5.0)
rubocop

View file

@ -49,7 +49,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def collection_presenter
ActivityPub::CollectionPresenter.new(
id: account_collection_url(@account, params[:id]),
id: ActivityPub::TagManager.instance.collection_uri_for(@account, params[:id]),
type: @type,
size: @size,
items: @items

View file

@ -41,12 +41,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
end
end
def outbox_url(**)
if params[:account_username].present?
account_outbox_url(@account, **)
else
instance_actor_outbox_url(**)
end
def outbox_url(...)
ActivityPub::TagManager.instance.outbox_uri_for(@account, ...)
end
def next_page

View file

@ -21,6 +21,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
endpoint: subscription_params[:endpoint],
key_p256dh: subscription_params[:keys][:p256dh],
key_auth: subscription_params[:keys][:auth],
standard: subscription_params[:standard] || false,
data: data_params,
user_id: current_user.id,
access_token_id: doorkeeper_token.id
@ -55,7 +56,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
end
def subscription_params
params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
params.require(:subscription).permit(:endpoint, :standard, keys: [:auth, :p256dh])
end
def data_params

View file

@ -66,7 +66,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
end
def subscription_params
@subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
@subscription_params ||= params.require(:subscription).permit(:standard, :endpoint, keys: [:auth, :p256dh])
end
def web_push_subscription_params
@ -76,6 +76,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
endpoint: subscription_params[:endpoint],
key_auth: subscription_params[:keys][:auth],
key_p256dh: subscription_params[:keys][:p256dh],
standard: subscription_params[:standard] || false,
user_id: active_session.user_id,
}
end

View file

@ -46,7 +46,7 @@ class FollowerAccountsController < ApplicationController
end
def page_url(page)
account_followers_url(@account, page: page) unless page.nil?
ActivityPub::TagManager.instance.followers_uri_for(@account, page: page) unless page.nil?
end
def next_page_url

View file

@ -49,7 +49,9 @@ module ThemeHelper
end
def cached_custom_css_digest
Rails.cache.read(:setting_digest_custom_css)
Rails.cache.fetch(:setting_digest_custom_css) do
Setting.custom_css&.then { |content| Digest::SHA256.hexdigest(content) }
end
end
def theme_color_for(theme)

View file

@ -33,7 +33,7 @@ const unsubscribe = ({ registration, subscription }) =>
subscription ? subscription.unsubscribe().then(() => registration) : registration;
const sendSubscriptionToBackend = (subscription) => {
const params = { subscription };
const params = { subscription: { ...subscription.toJSON(), standard: true } };
if (me) {
const data = pushNotificationsSetting.get(me);

View file

@ -19,7 +19,7 @@ export interface BaseApiAccountJSON {
avatar_static: string;
bot: boolean;
created_at: string;
discoverable: boolean;
discoverable?: boolean;
indexable: boolean;
display_name: string;
emojis: ApiCustomEmojiJSON[];

View file

@ -12,6 +12,7 @@ import AddPhotoAlternateIcon from '@/material-icons/400-24px/add_photo_alternate
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
import { updateAccount } from 'mastodon/actions/accounts';
import { closeOnboarding } from 'mastodon/actions/onboarding';
import { Button } from 'mastodon/components/button';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
@ -58,7 +59,9 @@ export const Profile: React.FC<{
);
const [avatar, setAvatar] = useState<File>();
const [header, setHeader] = useState<File>();
const [discoverable, setDiscoverable] = useState(true);
const [discoverable, setDiscoverable] = useState(
account?.discoverable ?? true,
);
const [isSaving, setIsSaving] = useState(false);
const [errors, setErrors] = useState<ApiAccountErrors>();
const avatarFileRef = createRef<HTMLInputElement>();
@ -132,6 +135,7 @@ export const Profile: React.FC<{
)
.then(() => {
history.push('/start/follows');
dispatch(closeOnboarding());
return '';
})
// eslint-disable-next-line @typescript-eslint/use-unknown-in-catch-callback-variable

View file

@ -92,6 +92,7 @@ const mapStateToProps = state => ({
hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']),
firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION,
newAccount: !state.getIn(['accounts', me, 'note']) && !state.getIn(['accounts', me, 'bot']) && state.getIn(['accounts', me, 'following_count'], 0) === 0 && state.getIn(['accounts', me, 'statuses_count'], 0) === 0,
username: state.getIn(['accounts', me, 'username']),
});
@ -135,6 +136,7 @@ class SwitchingColumnsArea extends PureComponent {
children: PropTypes.node,
location: PropTypes.object,
singleColumn: PropTypes.bool,
forceOnboarding: PropTypes.bool,
};
UNSAFE_componentWillMount () {
@ -165,14 +167,16 @@ class SwitchingColumnsArea extends PureComponent {
};
render () {
const { children, singleColumn } = this.props;
const { children, singleColumn, forceOnboarding } = this.props;
const { signedIn } = this.props.identity;
const pathName = this.props.location.pathname;
let redirect;
if (signedIn) {
if (singleColumn) {
if (forceOnboarding) {
redirect = <Redirect from='/' to='/start' exact />;
} else if (singleColumn) {
redirect = <Redirect from='/' to='/home' exact />;
} else {
redirect = <Redirect from='/' to='/deck/getting-started' exact />;
@ -276,6 +280,7 @@ class UI extends PureComponent {
intl: PropTypes.object.isRequired,
layout: PropTypes.string.isRequired,
firstLaunch: PropTypes.bool,
newAccount: PropTypes.bool,
username: PropTypes.string,
...WithRouterPropTypes,
};
@ -568,7 +573,7 @@ class UI extends PureComponent {
render () {
const { draggingOver } = this.state;
const { children, isComposing, location, layout } = this.props;
const { children, isComposing, location, layout, firstLaunch, newAccount } = this.props;
const handlers = {
help: this.handleHotkeyToggleHelp,
@ -597,7 +602,7 @@ class UI extends PureComponent {
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef}>
<Header />
<SwitchingColumnsArea identity={this.props.identity} location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
<SwitchingColumnsArea identity={this.props.identity} location={location} singleColumn={layout === 'mobile' || layout === 'single-column'} forceOnboarding={firstLaunch && newAccount}>
{children}
</SwitchingColumnsArea>

View file

@ -104,10 +104,11 @@
"annual_report.summary.most_used_hashtag.none": "Žádné",
"annual_report.summary.new_posts.new_posts": "nové příspěvky",
"annual_report.summary.percentile.text": "<topLabel>To vás umisťuje do vrcholu</topLabel><percentage></percentage><bottomLabel>{domain} uživatelů.</bottomLabel>",
"annual_report.summary.percentile.we_wont_tell_bernie": "To, že jste zdejší smetánka zůstane mezi námi ;).",
"annual_report.summary.thanks": "Děkujeme, že jste součástí Mastodonu!",
"attachments_list.unprocessed": "(nezpracováno)",
"audio.hide": "Skrýt zvuk",
"block_modal.remote_users_caveat": "Požádáme server {domain}, aby respektoval vaše rozhodnutí. Úplné dodržování nastavení však není zaručeno, protože některé servery mohou řešit blokování různě. Veřejné příspěvky mohou stále být viditelné pro nepřihlášené uživatele.",
"block_modal.remote_users_caveat": "Požádáme server {domain}, aby respektoval vaše rozhodnutí. Úplné dodržování nastavení však není zaručeno, protože některé servery mohou řešit blokování různě. Veřejné příspěvky mohou být stále viditelné pro nepřihlášené uživatele.",
"block_modal.show_less": "Zobrazit méně",
"block_modal.show_more": "Zobrazit více",
"block_modal.they_cant_mention": "Nemůže vás zmiňovat ani sledovat.",
@ -180,6 +181,7 @@
"compose_form.poll.duration": "Doba trvání ankety",
"compose_form.poll.multiple": "Výběr z více možností",
"compose_form.poll.option_placeholder": "Volba {number}",
"compose_form.poll.single": "Jediná volba",
"compose_form.poll.switch_to_multiple": "Povolit u ankety výběr více voleb",
"compose_form.poll.switch_to_single": "Povolit u ankety výběr pouze jedné volby",
"compose_form.poll.type": "Styl",
@ -204,6 +206,7 @@
"confirmations.edit.message": "Editovat teď znamená přepsání zprávy, kterou právě tvoříte. Opravdu chcete pokračovat?",
"confirmations.edit.title": "Přepsat příspěvek?",
"confirmations.follow_to_list.confirm": "Sledovat a přidat do seznamu",
"confirmations.follow_to_list.message": "Musíte {name} sledovat, abyste je přidali do seznamu.",
"confirmations.follow_to_list.title": "Sledovat uživatele?",
"confirmations.logout.confirm": "Odhlásit se",
"confirmations.logout.message": "Opravdu se chcete odhlásit?",
@ -236,19 +239,25 @@
"disabled_account_banner.text": "Váš účet {disabledAccount} je momentálně deaktivován.",
"dismissable_banner.community_timeline": "Toto jsou nejnovější veřejné příspěvky od lidí, jejichž účty hostuje {domain}.",
"dismissable_banner.dismiss": "Zavřít",
"dismissable_banner.explore_links": "Tyto zprávy jsou dnes nejvíce sdíleny ve fediversu. Novější novinky publikované více různými lidmi jsou v pořadí vyšší.",
"dismissable_banner.explore_statuses": "Tyto příspěvky napříč fediversem dnes získávají na popularitě. Novější příspěvky s více boosty a oblíbenými jsou výše v pořadí.",
"dismissable_banner.explore_tags": "Tyto hashtagy dnes na fediversu získávají na popularitě. Hashtagy, které používá více různých lidí, jsou řazeny výše.",
"dismissable_banner.public_timeline": "Toto jsou nejnovější veřejné příspěvky od lidí na fediversu, které lidé na {domain} sledují.",
"domain_block_modal.block": "Blokovat server",
"domain_block_modal.block_account_instead": "Raději blokovat @{name}",
"domain_block_modal.they_can_interact_with_old_posts": "Lidé z tohoto serveru mohou interagovat s vašimi starými příspěvky.",
"domain_block_modal.they_cant_follow": "Nikdo z tohoto serveru vás nemůže sledovat.",
"domain_block_modal.they_wont_know": "Nebude vědět, že je zablokován*a.",
"domain_block_modal.title": "Blokovat doménu?",
"domain_block_modal.you_will_lose_num_followers": "Ztratíte {followersCount, plural, one {{followersCountDisplay} sledujícího} few {{followersCountDisplay} sledující} many {{followersCountDisplay} sledujících} other {{followersCountDisplay} sledujících}} a {followingCount, plural, one {{followingCountDisplay} sledovaného} few {{followingCountDisplay} sledované} many {{followingCountDisplay} sledovaných} other {{followingCountDisplay} sledovaných}}.",
"domain_block_modal.you_will_lose_relationships": "Ztratíte všechny sledující a lidi, které sledujete z tohoto serveru.",
"domain_block_modal.you_wont_see_posts": "Neuvidíte příspěvky ani upozornění od uživatelů z tohoto serveru.",
"domain_pill.activitypub_lets_connect": "Umožňuje vám spojit se a komunikovat s lidmi nejen na Mastodonu, ale i s dalšími sociálními aplikacemi.",
"domain_pill.activitypub_like_language": "ActivityPub je jako jazyk, kterým Mastodon mluví s jinými sociálními sítěmi.",
"domain_pill.server": "Server",
"domain_pill.their_handle": "Handle:",
"domain_pill.their_server": "Digitální domov, kde žijí všechny příspěvky.",
"domain_pill.their_username": "Jedinečný identikátor na serveru. Je možné najít uživatele se stejným uživatelským jménem na různých serverech.",
"domain_pill.their_server": "Jejich digitální domov, kde žijí jejich všechny příspěvky.",
"domain_pill.their_username": "Jejich jedinečný identikátor na jejich serveru. Je možné najít uživatele se stejným uživatelským jménem na jiných serverech.",
"domain_pill.username": "Uživatelské jméno",
"domain_pill.whats_in_a_handle": "Co obsahuje handle?",
"domain_pill.who_they_are": "Protože handle říkají kdo je kdo a také kde, je možné interagovat s lidmi napříč sociálními weby <button>platforem postavených na ActivityPub</button>.",
@ -322,6 +331,7 @@
"filter_modal.select_filter.title": "Filtrovat tento příspěvek",
"filter_modal.title.status": "Filtrovat příspěvek",
"filter_warning.matches_filter": "Odpovídá filtru “<span>{title}</span>”",
"filtered_notifications_banner.pending_requests": "Od {count, plural, =0 {nikoho, koho možná znáte} one {člověka, kterého možná znáte} few {#, které možná znáte} many {#, které možná znáte} other {#, které možná znáte}}",
"filtered_notifications_banner.title": "Filtrovaná oznámení",
"firehose.all": "Vše",
"firehose.local": "Tento server",
@ -329,14 +339,14 @@
"follow_request.authorize": "Autorizovat",
"follow_request.reject": "Zamítnout",
"follow_requests.unlocked_explanation": "Přestože váš účet není uzamčen, personál {domain} usoudil, že byste mohli chtít tyto požadavky na sledování zkontrolovat ručně.",
"follow_suggestions.curated_suggestion": "Výběr personálů",
"follow_suggestions.curated_suggestion": "Výběr personálu",
"follow_suggestions.dismiss": "Znovu nezobrazovat",
"follow_suggestions.featured_longer": "Ručně vybráno týmem {domain}",
"follow_suggestions.friends_of_friends_longer": "Populární mezi lidmi, které sledujete",
"follow_suggestions.hints.featured": "Tento profil byl ručně vybrán týmem {domain}.",
"follow_suggestions.hints.friends_of_friends": "Tento profil je populární mezi lidmi, které sledujete.",
"follow_suggestions.hints.most_followed": "Tento profil je jedním z nejvíce sledovaných na {domain}.",
"follow_suggestions.hints.most_interactions": "Tento profil nedávno dostalo velkou pozornost na {domain}.",
"follow_suggestions.hints.most_followed": "Tento profil je jedním z nejsledovanějších na {domain}.",
"follow_suggestions.hints.most_interactions": "Tomuto profilu se nedávno dostalo velké pozornosti na {domain}.",
"follow_suggestions.hints.similar_to_recently_followed": "Tento profil je podobný profilům, které jste nedávno sledovali.",
"follow_suggestions.personalized_suggestion": "Přizpůsobený návrh",
"follow_suggestions.popular_suggestion": "Populární návrh",
@ -355,6 +365,7 @@
"footer.terms_of_service": "Obchodní podmínky",
"generic.saved": "Uloženo",
"getting_started.heading": "Začínáme",
"hashtag.admin_moderation": "Otevřít moderátorské rozhraní pro #{name}",
"hashtag.column_header.tag_mode.all": "a {additional}",
"hashtag.column_header.tag_mode.any": "nebo {additional}",
"hashtag.column_header.tag_mode.none": "bez {additional}",
@ -370,9 +381,13 @@
"hashtag.follow": "Sledovat hashtag",
"hashtag.unfollow": "Přestat sledovat hashtag",
"hashtags.and_other": "…a {count, plural, one {# další} few {# další} other {# dalších}}",
"hints.profiles.followers_may_be_missing": "Sledující mohou pro tento profil chybět.",
"hints.profiles.follows_may_be_missing": "Sledování mohou pro tento profil chybět.",
"hints.profiles.posts_may_be_missing": "Některé příspěvky z tohoto profilu mohou chybět.",
"hints.profiles.see_more_followers": "Zobrazit více sledujících na {domain}",
"hints.profiles.see_more_follows": "Zobrazit další sledování na {domain}",
"hints.profiles.see_more_posts": "Zobrazit další příspěvky na {domain}",
"hints.threads.replies_may_be_missing": "Odpovědi z jiných serverů mohou chybět.",
"hints.threads.see_more": "Zobrazit další odpovědi na {domain}",
"home.column_settings.show_reblogs": "Zobrazit boosty",
"home.column_settings.show_replies": "Zobrazit odpovědi",
@ -381,7 +396,22 @@
"home.pending_critical_update.link": "Zobrazit aktualizace",
"home.pending_critical_update.title": "K dispozici je kritická bezpečnostní aktualizace!",
"home.show_announcements": "Zobrazit oznámení",
"ignore_notifications_modal.disclaimer": "Mastodon nemůže informovat uživatele, že jste ignorovali jejich oznámení. Ignorování oznámení nezabrání odesílání zpráv samotných.",
"ignore_notifications_modal.filter_instead": "Místo toho filtrovat",
"ignore_notifications_modal.filter_to_act_users": "Stále budete moci přijmout, odmítnout nebo nahlásit uživatele",
"ignore_notifications_modal.filter_to_avoid_confusion": "Filtrování pomáhá vyhnout se možným nejasnostem",
"ignore_notifications_modal.filter_to_review_separately": "Filtrovaná oznámení můžete zkontrolovat samostatně",
"ignore_notifications_modal.ignore": "Ignorovat oznámení",
"ignore_notifications_modal.limited_accounts_title": "Ignorovat oznámení z moderovaných účtů?",
"ignore_notifications_modal.new_accounts_title": "Ignorovat oznámení z nových účtů?",
"ignore_notifications_modal.not_followers_title": "Ignorovat oznámení od lidí, kteří vás nesledují?",
"ignore_notifications_modal.not_following_title": "Ignorovat oznámení od lidí, které nesledujete?",
"ignore_notifications_modal.private_mentions_title": "Ignorovat oznámení z nevyžádaných soukromých zmínek?",
"interaction_modal.action.favourite": "Chcete-li pokračovat, musíte oblíbit z vašeho účtu.",
"interaction_modal.action.follow": "Chcete-li pokračovat, musíte sledovat z vašeho účtu.",
"interaction_modal.action.reblog": "Chcete-li pokračovat, musíte dát boost z vašeho účtu.",
"interaction_modal.action.reply": "Chcete-li pokračovat, musíte odpovědět z vašeho účtu.",
"interaction_modal.action.vote": "Chcete-li pokračovat, musíte hlasovat z vašeho účtu.",
"interaction_modal.go": "Přejít",
"interaction_modal.no_account_yet": "Ještě nemáte účet?",
"interaction_modal.on_another_server": "Na jiném serveru",
@ -433,20 +463,27 @@
"lightbox.close": "Zavřít",
"lightbox.next": "Další",
"lightbox.previous": "Předchozí",
"lightbox.zoom_in": "Přiblížit na skutečnou velikost",
"lightbox.zoom_out": "Přizpůsobit velikost",
"limited_account_hint.action": "Přesto profil zobrazit",
"limited_account_hint.title": "Tento profil byl skryt moderátory {domain}.",
"link_preview.author": "Podle {name}",
"link_preview.more_from_author": "Více od {name}",
"link_preview.shares": "{count, plural, one {{counter} příspěvek} few {{counter} příspěvky} many {{counter} příspěvků} other {{counter} příspěvků}}",
"lists.add_member": "Přidat",
"lists.add_to_list": "Přidat do seznamu",
"lists.add_to_lists": "Přidat {name} do seznamů",
"lists.create": "Vytvořit",
"lists.create_a_list_to_organize": "Vytvořte nový seznam pro organizaci vašeho domovského kanálu",
"lists.create_list": "Vytvořit seznam",
"lists.delete": "Smazat seznam",
"lists.done": "Hotovo",
"lists.edit": "Upravit seznam",
"lists.exclusive": "Skrýt členy na domovském kanálu",
"lists.exclusive_hint": "Pokud je někdo na tomto seznamu, skryjte jej ve vašem domovském kanálu, abyste se vyhnuli dvojímu vidění jejich příspěvků.",
"lists.find_users_to_add": "Najít uživatele, které chcete přidat",
"lists.list_members": "Členové seznamu",
"lists.list_members_count": "{count, plural, one {# člen} few {# členové} many {# členů} other {# členů}}",
"lists.list_name": "Název seznamu",
"lists.new_list_name": "Název nového seznamu",
"lists.no_lists_yet": "Zatím žádné seznamy.",
@ -458,6 +495,7 @@
"lists.replies_policy.none": "Nikomu",
"lists.save": "Uložit",
"lists.search": "Hledat",
"lists.show_replies_to": "Zahrnout odpovědi od členů seznamu pro",
"load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položek} other {# nových položek}}",
"loading_indicator.label": "Načítání…",
"media_gallery.hide": "Skrýt",
@ -500,14 +538,25 @@
"navigation_bar.security": "Zabezpečení",
"not_signed_in_indicator.not_signed_in": "Pro přístup k tomuto zdroji se musíte přihlásit.",
"notification.admin.report": "Uživatel {name} nahlásil {target}",
"notification.admin.report_account": "{name} nahlásil {count, plural, one {jeden příspěvek} few {# příspěvky} many {# příspěvků} other {# příspěvků}} od {target} za {category}",
"notification.admin.report_account_other": "{name} nahlásil {count, plural, one {jeden příspěvek} few {# příspěvky} many {# příspěvků} other {# příspěvků}} od {target}",
"notification.admin.report_statuses": "{name} nahlásil {target} za {category}",
"notification.admin.report_statuses_other": "{name} nahlásil {target}",
"notification.admin.sign_up": "Uživatel {name} se zaregistroval",
"notification.admin.sign_up.name_and_others": "{name} a {count, plural, one {# další} few {# další} many {# dalších} other {# dalších}} se zaregistrovali",
"notification.annual_report.message": "Váš #Wrapstodon {year} na Vás čeká! Podívejte se, jak vypadal tento Váš rok na Mastodonu!",
"notification.annual_report.view": "Zobrazit #Wrapstodon",
"notification.favourite": "Uživatel {name} si oblíbil váš příspěvek",
"notification.favourite.name_and_others_with_link": "{name} a {count, plural, one {<a># další</a> si oblíbil} few {<a># další</a> si oblíbili} other {<a># dalších</a> si oblíbilo}} Váš příspěvek",
"notification.favourite_pm": "{name} si oblíbil vaši soukromou zmínku",
"notification.favourite_pm.name_and_others_with_link": "{name} a {count, plural, one {<a># další</a> si oblíbil} few {<a># další</a> si oblíbili} other {<a># dalších</a> si oblíbilo}} Vaši soukromou zmínku",
"notification.follow": "Uživatel {name} vás začal sledovat",
"notification.follow.name_and_others": "{name} a {count, plural, one {<a># další</a> Vás začal sledovat} few {<a># další</a> Vás začali sledovat} other {<a># dalších</a> Vás začalo sledovat}}",
"notification.follow_request": "Uživatel {name} požádal o povolení vás sledovat",
"notification.follow_request.name_and_others": "{name} a {count, plural, one {# další Vám poslal žádost o sledování} few {# další Vám poslali žádost o sledování} other {# dalších Vám poslalo žádost o sledování}}",
"notification.label.mention": "Zmínka",
"notification.label.private_mention": "Soukromá zmínka",
"notification.label.private_reply": "Privátní odpověď",
"notification.label.reply": "Odpověď",
"notification.mention": "Zmínka",
"notification.mentioned_you": "{name} vás zmínil",
@ -523,6 +572,7 @@
"notification.own_poll": "Vaše anketa skončila",
"notification.poll": "Anketa, ve které jste hlasovali, skončila",
"notification.reblog": "Uživatel {name} boostnul váš příspěvek",
"notification.reblog.name_and_others_with_link": "{name} a {count, plural, one {<a># další</a> boostnul} few {<a># další</a> boostnuli} other {<a># dalších</a> boostnulo}} Váš příspěvek",
"notification.relationships_severance_event": "Kontakt ztracen s {name}",
"notification.relationships_severance_event.account_suspension": "Administrátor z {from} pozastavil {target}, což znamená, že již od nich nemůžete přijímat aktualizace nebo s nimi interagovat.",
"notification.relationships_severance_event.domain_block": "Administrátor z {from} pozastavil {target}, včetně {followersCount} z vašich sledujících a {followingCount, plural, one {# účet, který sledujete} few {# účty, které sledujete} many {# účtů, které sledujete} other {# účtů, které sledujete}}.",
@ -531,10 +581,19 @@
"notification.status": "Uživatel {name} právě přidal příspěvek",
"notification.update": "Uživatel {name} upravil příspěvek",
"notification_requests.accept": "Přijmout",
"notification_requests.accept_multiple": "{count, plural, one {Schválit # požadavek…} few {Schválit # požadavky…} other {Schválit # požadavků…}}",
"notification_requests.confirm_accept_multiple.button": "{count, plural, one {Schválit požadavek} other {Schválit požadavky}}",
"notification_requests.confirm_accept_multiple.message": "Chystáte se schválit {count, plural, one {jeden požadavek} few {# požadavky} other {# požadavků}} na oznámení. Opravdu chcete pokračovat?",
"notification_requests.confirm_accept_multiple.title": "Přijmout žádosti o oznámení?",
"notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Zamítnout požadavek} other {Zamítnout požadavky}}",
"notification_requests.confirm_dismiss_multiple.message": "Chystáte se zamítnout {count, plural, one {jeden požadavek} few {# požadavky} many {# požadavků} other {# požadavků}} na oznámení. Poté k {count, plural, one {němu} other {něm}} již nebudete mít snadný přístup. Opravdu chcete pokračovat?",
"notification_requests.confirm_dismiss_multiple.title": "Zamítnout požadavky na oznámení?",
"notification_requests.dismiss": "Zamítnout",
"notification_requests.dismiss_multiple": "Zamítnout {count, plural, one {# požadavek} few {# požadavky} many {# požadavků} other {# požadavků}}…",
"notification_requests.edit_selection": "Upravit",
"notification_requests.exit_selection": "Hotovo",
"notification_requests.explainer_for_limited_account": "Oznámení z tohoto účtu byla filtrována, protože tento účet byl omezen moderátorem.",
"notification_requests.explainer_for_limited_remote_account": "Oznámení z tohoto účtu byla filtrována, protože tento účet nebo jeho server byl omezen moderátorem.",
"notification_requests.maximize": "Maximalizovat",
"notification_requests.minimize_banner": "Minimalizovat banner filtrovaných oznámení",
"notification_requests.notifications_from": "Oznámení od {name}",
@ -578,6 +637,7 @@
"notifications.policy.accept": "Přijmout",
"notifications.policy.accept_hint": "Zobrazit v oznámeních",
"notifications.policy.drop": "Ignorovat",
"notifications.policy.drop_hint": "Permanentně odstranit, aby již nikdy nespatřil světlo světa",
"notifications.policy.filter": "Filtrovat",
"notifications.policy.filter_hint": "Odeslat do filtrované schránky oznámení",
"notifications.policy.filter_limited_accounts_hint": "Omezeno moderátory serveru",

View file

@ -407,6 +407,13 @@
"ignore_notifications_modal.not_followers_title": "An dtugann tú aird ar fhógraí ó dhaoine nach leanann tú?",
"ignore_notifications_modal.not_following_title": "An ndéanann tú neamhaird de fhógraí ó dhaoine nach leanann tú?",
"ignore_notifications_modal.private_mentions_title": "An dtugann tú aird ar fhógraí ó Luaintí Príobháideacha gan iarraidh?",
"interaction_modal.action.favourite": "Chun leanúint ar aghaidh, ní mór duit an ceann is fearr leat ó do chuntas.",
"interaction_modal.action.follow": "Chun leanúint ar aghaidh, ní mór duit leanúint ó do chuntas.",
"interaction_modal.action.reblog": "Chun leanúint ar aghaidh, ní mór duit athbhlagáil ó do chuntas.",
"interaction_modal.action.reply": "Chun leanúint ar aghaidh, ní mór duit freagra a thabhairt ó do chuntas.",
"interaction_modal.action.vote": "Chun leanúint ar aghaidh, ní mór duit vótáil ó do chuntas.",
"interaction_modal.go": "Téigh",
"interaction_modal.no_account_yet": "Níl cuntas agat fós?",
"interaction_modal.on_another_server": "Ar freastalaí eile",
"interaction_modal.on_this_server": "Ar an freastalaí seo",
"interaction_modal.title.favourite": "An postáil {name} is fearr leat",
@ -414,6 +421,7 @@
"interaction_modal.title.reblog": "Mol postáil de chuid {name}",
"interaction_modal.title.reply": "Freagair postáil {name}",
"interaction_modal.title.vote": "Vótáil i vótaíocht {name}",
"interaction_modal.username_prompt": "M.sh. {example}",
"intervals.full.days": "{number, plural, one {# lá} other {# lá}}",
"intervals.full.hours": "{number, plural, one {# uair} other {# uair}}",
"intervals.full.minutes": "{number, plural, one {# nóiméad} other {# nóiméad}}",
@ -449,6 +457,7 @@
"keyboard_shortcuts.toggle_hidden": "Taispeáin/folaigh an téacs taobh thiar de CW",
"keyboard_shortcuts.toggle_sensitivity": "Taispeáin / cuir i bhfolach meáin",
"keyboard_shortcuts.toot": "Cuir tús le postáil nua",
"keyboard_shortcuts.translate": "post a aistriú",
"keyboard_shortcuts.unfocus": "Unfocus cum textarea/search",
"keyboard_shortcuts.up": "Bog suas ar an liosta",
"lightbox.close": "Dún",
@ -687,6 +696,8 @@
"privacy_policy.title": "Polasaí príobháideachais",
"recommended": "Molta",
"refresh": "Athnuaigh",
"regeneration_indicator.please_stand_by": "Fan i do sheasamh, le do thoil.",
"regeneration_indicator.preparing_your_home_feed": "Ag ullmhú do bheatha baile…",
"relative_time.days": "{number}l",
"relative_time.full.days": "{number, plural, one {# lá} other {# lá}} ó shin",
"relative_time.full.hours": "{number, plural, one {# uair} other {# uair}} ó shin",
@ -826,6 +837,7 @@
"status.reblogs.empty": "Níor mhol éinne an phostáil seo fós. Nuair a mholfaidh duine éigin í, taispeánfar anseo é sin.",
"status.redraft": "Scrios ⁊ athdhréachtaigh",
"status.remove_bookmark": "Bain leabharmharc",
"status.remove_favourite": "Bain ó cheanáin",
"status.replied_in_thread": "D'fhreagair sa snáithe",
"status.replied_to": "D'fhreagair {name}",
"status.reply": "Freagair",

View file

@ -803,7 +803,7 @@
"status.bookmark": "Adder al marcapaginas",
"status.cancel_reblog_private": "Disfacer impulso",
"status.cannot_reblog": "Iste message non pote esser impulsate",
"status.continued_thread": "Discussion continuate",
"status.continued_thread": "Continuation del discussion",
"status.copy": "Copiar ligamine a message",
"status.delete": "Deler",
"status.detailed_status": "Vista detaliate del conversation",

View file

@ -431,11 +431,11 @@
"keyboard_shortcuts.column": "Focalizza alla colonna",
"keyboard_shortcuts.compose": "Focalizza l'area di composizione testuale",
"keyboard_shortcuts.description": "Descrizione",
"keyboard_shortcuts.direct": "per aprire la colonna menzioni private",
"keyboard_shortcuts.direct": "Apre la colonna \"menzioni private\"",
"keyboard_shortcuts.down": "Scorri in basso nell'elenco",
"keyboard_shortcuts.enter": "Apre il post",
"keyboard_shortcuts.favourite": "Contrassegna il post come preferito",
"keyboard_shortcuts.favourites": "Apri l'elenco dei preferiti",
"keyboard_shortcuts.favourites": "Apre l'elenco dei preferiti",
"keyboard_shortcuts.federated": "Apre la cronologia federata",
"keyboard_shortcuts.heading": "Scorciatoie da tastiera",
"keyboard_shortcuts.home": "Apre la cronologia domestica",
@ -457,6 +457,7 @@
"keyboard_shortcuts.toggle_hidden": "Mostra/Nasconde il testo dietro CW",
"keyboard_shortcuts.toggle_sensitivity": "Mostra/Nasconde media",
"keyboard_shortcuts.toot": "Crea un nuovo post",
"keyboard_shortcuts.translate": "Traduce un post",
"keyboard_shortcuts.unfocus": "Rimuove il focus sull'area di composizione testuale/ricerca",
"keyboard_shortcuts.up": "Scorre in su nell'elenco",
"lightbox.close": "Chiudi",

View file

@ -407,6 +407,13 @@
"ignore_notifications_modal.not_followers_title": "本当に「フォローされていないアカウントからの通知」を無視するようにしますか?",
"ignore_notifications_modal.not_following_title": "本当に「フォローしていないアカウントからの通知」を無視するようにしますか?",
"ignore_notifications_modal.private_mentions_title": "本当に「外部からの非公開の返信」を無視するようにしますか?",
"interaction_modal.action.favourite": "お気に入り登録はあなたのアカウントがあるサーバーで行う必要があります。",
"interaction_modal.action.follow": "ユーザーをフォローするには、あなたのアカウントがあるサーバーからフォローする必要があります。",
"interaction_modal.action.reblog": "投稿をブーストするには、あなたのアカウントがあるサーバーでブーストする必要があります。",
"interaction_modal.action.reply": "リプライを送るには、あなたのアカウントがあるサーバーから送る必要があります。",
"interaction_modal.action.vote": "票を入れるには、あなたのアカウントがあるサーバーから投票する必要があります。",
"interaction_modal.go": "サーバーに移動",
"interaction_modal.no_account_yet": "アカウントを持っていない場合は:",
"interaction_modal.on_another_server": "別のサーバー",
"interaction_modal.on_this_server": "このサーバー",
"interaction_modal.title.favourite": "{name}さんの投稿をお気に入り登録",
@ -414,6 +421,7 @@
"interaction_modal.title.reblog": "{name}さんの投稿をブースト",
"interaction_modal.title.reply": "{name}さんの投稿にリプライ",
"interaction_modal.title.vote": "{name}さんのアンケートに投票",
"interaction_modal.username_prompt": "例: {example}",
"intervals.full.days": "{number}日",
"intervals.full.hours": "{number}時間",
"intervals.full.minutes": "{number}分",

View file

@ -345,7 +345,7 @@
"hints.profiles.see_more_followers": "Skatīt vairāk sekotāju {domain}",
"hints.profiles.see_more_follows": "Skatīt vairāk sekojumu {domain}",
"hints.profiles.see_more_posts": "Skatīt vairāk ierakstu {domain}",
"hints.threads.replies_may_be_missing": "Var trūkt atbildes no citiem serveriem.",
"hints.threads.replies_may_be_missing": "Var trūkt atbilžu no citiem serveriem.",
"hints.threads.see_more": "Skatīt vairāk atbilžu {domain}",
"home.column_settings.show_reblogs": "Rādīt pastiprinātos ierakstus",
"home.column_settings.show_replies": "Rādīt atbildes",

View file

@ -126,9 +126,35 @@
"bundle_column_error.network.title": "網路錯誤",
"bundle_column_error.retry": "Koh試",
"bundle_column_error.return": "Tńg去頭頁",
"bundle_column_error.routing.body": "Tshuē bô所要求ê頁面。Lí kám確定地址liâu-á ê URL正確",
"bundle_column_error.routing.title": "404",
"bundle_modal_error.close": "關",
"bundle_modal_error.message": "Tī載入tsit ê畫面ê時起錯誤。",
"bundle_modal_error.retry": "Koh試",
"column.create_list": "建立列單",
"column.direct": "私人ê提起",
"column.directory": "瀏覽個人資料",
"column.domain_blocks": "封鎖ê域名",
"column.edit_list": "編輯列單",
"column.favourites": "Siōng kah意",
"column.firehose": "Tsit-má ê動態",
"column.follow_requests": "跟tuè請求",
"column.home": "頭頁",
"column_header.pin": "釘",
"column_header.show_settings": "顯示設定",
"column_header.unpin": "Pak掉",
"column_search.cancel": "取消",
"column_subheading.settings": "設定",
"community.column_settings.local_only": "Kan-ta展示本地ê",
"community.column_settings.media_only": "Kan-ta展示媒體",
"community.column_settings.remote_only": "Kan-ta展示遠距離ê",
"compose.language.change": "換語言",
"compose.language.search": "Tshiau-tshuē語言……",
"compose.published.body": "成功PO文。",
"compose.published.open": "開",
"compose.saved.body": "PO文儲存ah。",
"compose_form.direct_message_warning_learn_more": "詳細資訊",
"compose_form.encryption_warning": "Mastodon ê PO文無點tuì點加密。M̄通用Mastodon分享任何敏感ê資訊。",
"confirmations.follow_to_list.confirm": "跟tuè加入kàu列單",
"notification.favourite_pm": "{name} kah意lí ê私人提起",
"notification.favourite_pm.name_and_others_with_link": "{name} kap<a>{count, plural, other {另外 # ê lâng}}</a>kah意lí ê私人提起",

View file

@ -47,11 +47,13 @@
"account.mutual": "आपसी",
"account.no_bio": "कुनै विवरण प्रदान गरिएको छैन।",
"account.posts": "पोस्टहरू",
"account.posts_with_replies": "पोस्ट र जवाफहरू",
"account.report": "@{name}लाई रिपोर्ट गर्नुहोस्",
"account.requested": "स्वीकृतिको पर्खाइमा। फलो अनुरोध रद्द गर्न क्लिक गर्नुहोस्",
"account.requested_follow": "{name} ले तपाईंलाई फलो गर्न अनुरोध गर्नुभएको छ",
"account.share": "@{name} को प्रोफाइल सेयर गर्नुहोस्",
"account.show_reblogs": "@{name} को बूस्टहरू देखाउनुहोस्",
"account.statuses_counter": "{count, plural, one {{counter} पोस्ट} other {{counter} पोस्टहरू}}",
"account.unblock": "@{name} लाई अनब्लक गर्नुहोस्",
"account.unblock_domain": "{domain} डोमेनलाई अनब्लक गर्नुहोस्",
"account.unblock_short": "अनब्लक गर्नुहोस्",
@ -67,14 +69,18 @@
"alert.unexpected.message": "एउटा अनपेक्षित त्रुटि भयो।",
"announcement.announcement": "घोषणा",
"annual_report.summary.followers.followers": "फलोअरहरु",
"annual_report.summary.highlighted_post.by_reblogs": "सबैभन्दा बढि बूस्ट गरिएको पोस्ट",
"annual_report.summary.new_posts.new_posts": "नयाँ पोस्टहरू",
"block_modal.remote_users_caveat": "हामी सर्भर {domain} लाई तपाईंको निर्णयको सम्मान गर्न सोध्नेछौं। तर, हामी अनुपालनको ग्यारेन्टी दिन सक्दैनौं किनभने केही सर्भरहरूले ब्लकहरू फरक रूपमा ह्यान्डल गर्न सक्छन्। सार्वजनिक पोस्टहरू लग इन नभएका प्रयोगकर्ताहरूले देख्न सक्छन्।",
"block_modal.show_less": "कम देखाउनुहोस्",
"block_modal.show_more": "थप देखाउनुहोस्",
"block_modal.title": "प्रयोगकर्तालाई ब्लक गर्ने हो?",
"block_modal.title": "प्रयोगकर्तालाई ब्लक गर्ने?",
"boost_modal.reblog": "पोस्ट बुस्ट गर्ने?",
"boost_modal.undo_reblog": "पोस्ट अनबुस्ट गर्ने?",
"bundle_column_error.copy_stacktrace": "त्रुटि रिपोर्ट प्रतिलिपि गर्नुहोस्",
"bundle_column_error.network.title": "नेटवर्क त्रुटि",
"bundle_column_error.retry": "पुन: प्रयास गर्नुहोस्",
"bundle_column_error.routing.title": "४०४",
"bundle_modal_error.close": "बन्द गर्नुहोस्",
"bundle_modal_error.retry": "Try again",
"closed_registrations.other_server_instructions": "Mastodon विकेन्द्रीकृत भएकोले, तपाइँ अर्को सर्भरमा खाता खोल्न सक्नुहुन्छ र पनि यो सर्भरसँग अन्तरक्रिया गर्न सक्नुहुन्छ।",
@ -82,23 +88,54 @@
"closed_registrations_modal.find_another_server": "अर्को सर्भर खोज्नुहोस्",
"closed_registrations_modal.title": "Mastodon मा साइन अप गर्दै",
"column.blocks": "ब्लक गरिएको प्रयोगकर्ताहरु",
"column.bookmarks": "बुकमार्कहरू",
"column.create_list": "सूची बनाउनुहोस्",
"column.direct": "निजी उल्लेखहरू",
"column.directory": "प्रोफाइल ब्राउज गर्नुहोस्",
"column.domain_blocks": "ब्लक गरिएको डोमेन",
"column.edit_list": "सूची सम्पादन गर्नुहोस्",
"column.follow_requests": "फलो अनुरोधहरू",
"column.home": "गृहपृष्ठ",
"column.lists": "सूचीहरू",
"column.mutes": "म्यूट गरिएका प्रयोगकर्ताहरू",
"column.notifications": "सूचनाहरू",
"column.pins": "पिन गरिएका पोस्टहरू",
"column_header.hide_settings": "सेटिङ्हरू लुकाउनुहोस्",
"column_header.pin": "पिन गर्नुहोस्",
"column_header.unpin": "अनपिन गर्नुहोस्",
"column_search.cancel": "रद्द गर्नुहोस्",
"column_subheading.settings": "सेटिङहरू",
"community.column_settings.media_only": "मिडिया मात्र",
"compose.language.change": "भाषा परिवर्तन गर्नुहोस्",
"compose.language.search": "भाषाहरू खोज्नुहोस्...",
"compose.published.body": "पोस्ट प्रकाशित भयो।",
"compose.published.open": "खोल्नुहोस्",
"compose.saved.body": "पोस्ट सेभ गरियो।",
"compose_form.direct_message_warning_learn_more": "थप जान्नुहोस्",
"compose_form.placeholder": "तपाईको मनमा के छ?",
"compose_form.publish": "पोस्ट गर्नुहोस्",
"compose_form.publish_form": "नयाँ पोस्ट",
"compose_form.reply": "जवाफ दिनुहोस्",
"compose_form.save_changes": "अपडेट गर्नुहोस्",
"confirmations.delete.message": "के तपाइँ पक्का हुनुहुन्छ कि तपाईं यो पोष्ट मेटाउन चाहनुहुन्छ?",
"confirmations.delete.title": "पोस्ट मेटाउने?",
"confirmations.delete_list.message": "के तपाइँ पक्का हुनुहुन्छ कि तपाईं यो सूची स्थायी रूपमा मेटाउन चाहनुहुन्छ?",
"confirmations.delete_list.title": "सूची मेटाउने?",
"confirmations.edit.confirm": "सम्पादन गर्नुहोस्",
"confirmations.edit.message": "अहिले सम्पादन गर्नाले तपाईंले हाल लेखिरहनुभएको सन्देश अधिलेखन हुनेछ। के तपाईं अगाडि बढ्न चाहनुहुन्छ?",
"confirmations.edit.title": "पोस्ट अधिलेखन गर्ने?",
"confirmations.follow_to_list.confirm": "फलो गर्नुहोस र सूचीमा थप्नुहोस्",
"confirmations.follow_to_list.message": "सूचीमा {name}लाई थप्नको लागि तपाईंले तिनीहरूलाई फलो गरेको हुनुपर्छ।",
"confirmations.follow_to_list.title": "प्रयोगकर्तालाई फलो गर्ने हो?",
"confirmations.follow_to_list.title": "प्रयोगकर्तालाई फलो गर्ने?",
"confirmations.logout.message": "के तपाइँ पक्का हुनुहुन्छ कि तपाइँ लाई लग आउट गर्न चाहनुहुन्छ?",
"confirmations.logout.title": "लग आउट गर्ने?",
"confirmations.redraft.title": "पोस्ट मेटाएर पुन: ड्राफ्ट गर्ने?",
"confirmations.reply.message": "अहिले जवाफ दिनाले तपाईंले हाल लेखिरहनुभएको सन्देश अधिलेखन हुनेछ। के तपाईं अगाडि बढ्न चाहनुहुन्छ?",
"confirmations.reply.title": "पोस्ट अधिलेखन गर्ने?",
"confirmations.unfollow.confirm": "अनफलो गर्नुहोस्",
"confirmations.unfollow.message": "के तपाइँ पक्का हुनुहुन्छ कि तपाइँ {name}लाई अनफलो गर्न चाहनुहुन्छ?",
"confirmations.unfollow.title": "प्रयोगकर्तालाई अनफलो गर्ने हो?",
"confirmations.unfollow.title": "प्रयोगकर्तालाई अनफलो गर्ने?",
"disabled_account_banner.account_settings": "खाता सेटिङहरू",
"empty_column.follow_requests": "तपाईंले अहिलेसम्म कुनै पनि फलो अनुरोधहरू प्राप्त गर्नुभएको छैन। तपाईंले कुनै प्राप्त गरेपछि त्यो यहाँ देखिनेछ।",
"empty_column.followed_tags": "तपाईंले अहिलेसम्म कुनै पनि ह्यासट्यागहरू फलो गर्नुभएको छैन। तपाईंले ह्यासट्याग फलो गरेपछि तिनीहरू यहाँ देखिनेछन्।",
"follow_suggestions.dismiss": "फेरि नदेखाउनुहोस्",
@ -111,15 +148,35 @@
"followed_tags": "फलो गरिएका ह्यासट्यागहरू",
"hashtag.follow": "ह्यासट्याग फलो गर्नुहोस्",
"hashtag.unfollow": "ह्यासट्याग अनफलो गर्नुहोस्",
"home.column_settings.show_reblogs": "बूस्टहरू देखाउनुहोस्",
"interaction_modal.no_account_yet": "अहिलेसम्म खाता छैन?",
"interaction_modal.title.follow": "{name} लाई फलो गर्नुहोस्",
"interaction_modal.title.reblog": "{name} को पोस्ट बुस्ट गर्नुहोस्",
"keyboard_shortcuts.boost": "पोस्ट बुस्ट गर्नुहोस्",
"mute_modal.they_wont_know": "उनीहरूलाई म्यूट गरिएको बारे थाहा हुँदैन।",
"mute_modal.title": "प्रयोगकर्तालाई म्युट गर्ने हो?",
"mute_modal.title": "प्रयोगकर्तालाई म्युट गर्ने?",
"navigation_bar.blocks": "ब्लक गरिएको प्रयोगकर्ताहरु",
"navigation_bar.follow_requests": "फलो अनुरोधहरू",
"navigation_bar.followed_tags": "फलो गरिएका ह्यासट्यागहरू",
"notification.reblog": "{name} ले तपाईंको पोस्ट बूस्ट गर्नुभयो",
"notification_requests.confirm_accept_multiple.title": "सूचना अनुरोधहरू स्वीकार गर्ने?",
"notification_requests.confirm_dismiss_multiple.title": "सूचना अनुरोधहरू खारेज गर्ने?",
"notifications.clear_title": "सूचनाहरू खाली गर्ने?",
"notifications.column_settings.reblog": "बूस्टहरू:",
"notifications.filter.boosts": "बूस्टहरू",
"report.comment.title": "के हामीले थाहा पाउनुपर्ने अरू केही छ जस्तो लाग्छ?",
"report.forward_hint": "यो खाता अर्को सर्भरबाट हो। त्यहाँ पनि रिपोर्टको गुमनाम प्रतिलिपि पठाउने हो?",
"report.rules.title": "कुन नियमहरू उल्लङ्घन भइरहेका छन्?",
"report.statuses.title": "के यस रिपोर्टलाई समर्थन गर्ने कुनै पोस्टहरू छन्?",
"report.thanks.title": "यो हेर्न चाहनुहुन्न?",
"report.unfollow": "@{name} लाई अनफलो गर्नुहोस्",
"search_results.hashtags": "ह्यासट्यागहरू",
"status.cancel_reblog_private": "अनबुस्ट गर्नुहोस्",
"status.cannot_reblog": "यो पोस्टलाई बुस्ट गर्न सकिँदैन",
"status.mute": "@{name}लाई म्यूट गर्नुहोस्",
"status.mute_conversation": "कुराकानी म्यूट गर्नुहोस्",
"status.reblog": "बूस्ट गर्नुहोस्",
"status.reblogged_by": "{name} ले बूस्ट गर्नुभएको",
"status.reblogs": "{count, plural, one {बूस्ट} other {बूस्टहरू}}",
"status.unmute_conversation": "कुराकानी अनम्यूट गर्नुहोस्"
}

View file

@ -416,6 +416,7 @@
"interaction_modal.title.reblog": "Podbij wpis {name}",
"interaction_modal.title.reply": "Odpowiedz na post {name}",
"interaction_modal.title.vote": "Weź udział w głosowaniu {name}",
"interaction_modal.username_prompt": "Np. {example}",
"intervals.full.days": "{number, plural, one {# dzień} few {# dni} many {# dni} other {# dni}}",
"intervals.full.hours": "{number, plural, one {# godzina} few {# godziny} many {# godzin} other {# godzin}}",
"intervals.full.minutes": "{number, plural, one {# minuta} few {# minuty} many {# minut} other {# minut}}",
@ -828,6 +829,7 @@
"status.reblogs.empty": "Nikt nie podbił jeszcze tego wpisu. Gdy ktoś to zrobi, pojawi się tutaj.",
"status.redraft": "Usuń i przeredaguj",
"status.remove_bookmark": "Usuń zakładkę",
"status.remove_favourite": "Usuń z ulubionych",
"status.replied_in_thread": "Odpowiedź w wątku",
"status.replied_to": "Odpowiedź do wpisu użytkownika {name}",
"status.reply": "Odpowiedz",

View file

@ -108,7 +108,7 @@
"annual_report.summary.thanks": "Obrigada por fazer parte do Mastodon!",
"attachments_list.unprocessed": "(não processado)",
"audio.hide": "Ocultar áudio",
"block_modal.remote_users_caveat": "Pediremos ao servidor {domínio} que respeite sua decisão. No entanto, a conformidade não é garantida pois alguns servidores podem lidar com os blocos de maneira diferente. As postagens públicas ainda podem estar visíveis para usuários não logados.",
"block_modal.remote_users_caveat": "Pediremos ao servidor {domain} que respeite sua decisão. No entanto, a conformidade não é garantida, já que alguns servidores podem lidar com bloqueios de maneira diferente. As postagens públicas ainda podem estar visíveis para usuários não logados.",
"block_modal.show_less": "Mostrar menos",
"block_modal.show_more": "Mostrar mais",
"block_modal.they_cant_mention": "Eles não podem mencionar ou seguir você.",
@ -259,9 +259,9 @@
"domain_pill.their_server": "Sua casa digital, onde ficam todas as suas postagens.",
"domain_pill.their_username": "Seu identificador exclusivo em seu servidor. É possível encontrar usuários com o mesmo nome de usuário em servidores diferentes.",
"domain_pill.username": "Nome de usuário",
"domain_pill.whats_in_a_handle": "O que há em uma alça?",
"domain_pill.who_they_are": "Como os identificadores indicam quem alguém é e onde está, você pode interagir com pessoas na web social de <button>plataformas alimentadas pelo ActivityPub</button>.",
"domain_pill.who_you_are": "Como seu identificador indica quem você é e onde está, as pessoas podem interagir com você nas redes sociais das <button>plataformas alimentadas pelo ActivityPub</button>.",
"domain_pill.whats_in_a_handle": "O que há em um identificador?",
"domain_pill.who_they_are": "Como os identificadores indicam quem alguém é e onde está, você pode interagir com pessoas na rede de <button>plataformas alimentadas pelo ActivityPub</button>.",
"domain_pill.who_you_are": "Como seu identificador indica quem você é e onde está, as pessoas podem interagir com você na rede de <button>plataformas alimentadas pelo ActivityPub</button>.",
"domain_pill.your_handle": "Seu identificador:",
"domain_pill.your_server": "Sua casa digital, onde ficam todas as suas postagens. Não gosta deste? Transfira servidores a qualquer momento e traga seus seguidores também.",
"domain_pill.your_username": "Seu identificador exclusivo neste servidor. É possível encontrar usuários com o mesmo nome de usuário em servidores diferentes.",

View file

@ -1,7 +1,7 @@
{
"about.blocks": "Servidores moderados",
"about.contact": "Contacto:",
"about.disclaimer": "O Mastodon é um software livre, de código aberto e uma marca registada do Mastodon gGmbH.",
"about.disclaimer": "O Mastodon é um software livre, de código aberto e uma marca registada de Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Motivo não disponível",
"about.domain_blocks.preamble": "O Mastodon geralmente permite ver e interagir com o conteúdo de utilizadores de qualquer outra instância no fediverso. Estas são as exceções desta instância em específico.",
"about.domain_blocks.silenced.explanation": "Normalmente não verás perfis e conteúdos deste servidor, a não ser que os procures explicitamente ou optes por segui-los.",
@ -723,7 +723,7 @@
"report.category.title_account": "perfil",
"report.category.title_status": "publicação",
"report.close": "Concluído",
"report.comment.title": "Há algo mais que pensa que devemos saber?",
"report.comment.title": "Há mais alguma coisa que devamos saber?",
"report.forward": "Reencaminhar para {target}",
"report.forward_hint": "A conta pertence a outro servidor. Enviar uma cópia anónima da denúncia para esse servidor também?",
"report.mute": "Ocultar",
@ -739,15 +739,15 @@
"report.reasons.spam": "É spam",
"report.reasons.spam_description": "Hiperligações maliciosas, contactos falsos ou respostas repetitivas",
"report.reasons.violation": "Viola as regras do servidor",
"report.reasons.violation_description": "Está ciente de que infringe regras específicas",
"report.rules.subtitle": "Selecione tudo o que se aplicar",
"report.reasons.violation_description": "Infringe regras específicas",
"report.rules.subtitle": "Seleciona tudo o que se aplicar",
"report.rules.title": "Que regras estão a ser violadas?",
"report.statuses.subtitle": "Selecione tudo o que se aplicar",
"report.statuses.subtitle": "Seleciona tudo o que se aplicar",
"report.statuses.title": "Existe alguma publicação que suporte esta denúncia?",
"report.submit": "Enviar",
"report.target": "A denunciar {target}",
"report.thanks.take_action": "Aqui estão as suas opções para controlar o que vê no Mastodon:",
"report.thanks.take_action_actionable": "Enquanto revemos a sua denúncia, pode tomar medidas contra @{name}:",
"report.thanks.take_action_actionable": "Enquanto revemos a tua denúncia, podes tomar medidas contra @{name}:",
"report.thanks.title": "Não quer ver isto?",
"report.thanks.title_actionable": "Obrigado por nos informares, vamos analisar a situação.",
"report.unfollow": "Deixar de seguir @{name}",
@ -758,7 +758,7 @@
"report_notification.categories.other": "Outro",
"report_notification.categories.other_sentence": "outro",
"report_notification.categories.spam": "Spam",
"report_notification.categories.spam_sentence": "spam",
"report_notification.categories.spam_sentence": "publicidade indesejada / spam",
"report_notification.categories.violation": "Violação de regra",
"report_notification.categories.violation_sentence": "violação de regra",
"report_notification.open": "Abrir denúncia",
@ -813,7 +813,7 @@
"status.edited": "Última edição em {date}",
"status.edited_x_times": "Editado {count, plural,one {{count} vez} other {{count} vezes}}",
"status.embed": "Obter código de incorporação",
"status.favourite": "Assinalar como favorito",
"status.favourite": "Adicionar aos favoritos",
"status.favourites": "{count, plural, one {favorito} other {favoritos}}",
"status.filter": "Filtrar esta publicação",
"status.history.created": "{name} criado em {date}",

View file

@ -530,6 +530,7 @@
"notification.status": "{name} uverejňuje niečo nové",
"notification.update": "{name} upravuje príspevok",
"notification_requests.accept": "Prijať",
"notification_requests.confirm_accept_multiple.title": "Priať požiadavku o oboznámenia?",
"notification_requests.dismiss": "Zamietnuť",
"notification_requests.edit_selection": "Uprav",
"notification_requests.exit_selection": "Hotovo",
@ -624,6 +625,7 @@
"privacy_policy.title": "Pravidlá ochrany súkromia",
"recommended": "Odporúčané",
"refresh": "Obnoviť",
"regeneration_indicator.preparing_your_home_feed": "Pripravuje sa tvoj domáci kanál…",
"relative_time.days": "{number} dní",
"relative_time.full.days": "Pred {number, plural, one {# dňom} other {# dňami}}",
"relative_time.full.hours": "Pred {number, plural, one {# hodinou} other {# hodinami}}",

View file

@ -550,7 +550,7 @@
"notification.favourite.name_and_others_with_link": "{name} và <a>{count, plural, other {# người khác}}</a> đã thích tút của bạn",
"notification.favourite_pm": "{name} đã thích lượt nhắn riêng của bạn",
"notification.favourite_pm.name_and_others_with_link": "{name} và <a>{count, plural, other {# người khác}}</a> đã thích lượt nhắn riêng của bạn",
"notification.follow": "{name} theo dõi bạn",
"notification.follow": "{name} đã theo dõi bạn",
"notification.follow.name_and_others": "{name} và <a>{count, plural, other {# người khác}}</a> theo dõi bạn",
"notification.follow_request": "{name} yêu cầu theo dõi bạn",
"notification.follow_request.name_and_others": "{name} và {count, plural, other {# người khác}} đã yêu cầu theo dõi bạn",
@ -764,7 +764,7 @@
"report_notification.open": "Mở báo cáo",
"search.no_recent_searches": "Gần đây chưa tìm gì",
"search.placeholder": "Tìm kiếm",
"search.quick_action.account_search": "Người tên {x}",
"search.quick_action.account_search": "Người tên {x}",
"search.quick_action.go_to_account": "Xem trang {x}",
"search.quick_action.go_to_hashtag": "Xem hashtag {x}",
"search.quick_action.open_url": "Mở liên kết trong Mastodon",

View file

@ -6815,6 +6815,8 @@ a.status-card {
}
&--layout-3 {
min-height: calc(64px * 2 + 8px);
& > .media-gallery__item:nth-child(1) {
border-end-end-radius: 0;
border-start-end-radius: 0;
@ -6834,6 +6836,8 @@ a.status-card {
}
&--layout-4 {
min-height: calc(64px * 2 + 8px);
& > .media-gallery__item:nth-child(1) {
border-end-end-radius: 0;
border-start-end-radius: 0;

View file

@ -3,6 +3,7 @@
class ActivityPub::Activity::Announce < ActivityPub::Activity
def perform
return reject_payload! if delete_arrived_first?(@json['id']) || !related_to_local_activity?
return reject_payload! if @object.nil?
with_redis_lock("announce:#{value_or_id(@object)}") do
original_status = status_from_object

View file

@ -86,8 +86,34 @@ class ActivityPub::TagManager
account_status_shares_url(target.account, target)
end
def followers_uri_for(target)
target.local? ? account_followers_url(target) : target.followers_url.presence
def following_uri_for(target, ...)
raise ArgumentError, 'target must be a local account' unless target.local?
account_following_index_url(target, ...)
end
def followers_uri_for(target, ...)
return target.followers_url.presence unless target.local?
account_followers_url(target, ...)
end
def collection_uri_for(target, ...)
raise NotImplementedError unless target.local?
account_collection_url(target, ...)
end
def inbox_uri_for(target)
raise NotImplementedError unless target.local?
target.instance_actor? ? instance_actor_inbox_url : account_inbox_url(target)
end
def outbox_uri_for(target, ...)
raise NotImplementedError unless target.local?
target.instance_actor? ? instance_actor_outbox_url(...) : account_outbox_url(target, ...)
end
# Primary audience of a status
@ -99,7 +125,7 @@ class ActivityPub::TagManager
when 'public'
[COLLECTIONS[:public]]
when 'unlisted', 'private'
[account_followers_url(status.account)]
[followers_uri_for(status.account)]
when 'direct', 'limited'
if status.account.silenced?
# Only notify followers if the account is locally silenced
@ -133,7 +159,7 @@ class ActivityPub::TagManager
case status.visibility
when 'public'
cc << account_followers_url(status.account)
cc << followers_uri_for(status.account)
when 'unlisted'
cc << COLLECTIONS[:public]
end

View file

@ -2,7 +2,8 @@
class WebPushRequest
SIGNATURE_ALGORITHM = 'p256ecdsa'
AUTH_HEADER = 'WebPush'
LEGACY_AUTH_HEADER = 'WebPush'
STANDARD_AUTH_HEADER = 'vapid'
PAYLOAD_EXPIRATION = 24.hours
JWT_ALGORITHM = 'ES256'
JWT_TYPE = 'JWT'
@ -10,6 +11,7 @@ class WebPushRequest
attr_reader :web_push_subscription
delegate(
:standard,
:endpoint,
:key_auth,
:key_p256dh,
@ -24,20 +26,36 @@ class WebPushRequest
@audience ||= Addressable::URI.parse(endpoint).normalized_site
end
def authorization_header
[AUTH_HEADER, encoded_json_web_token].join(' ')
def legacy_authorization_header
[LEGACY_AUTH_HEADER, encoded_json_web_token].join(' ')
end
def crypto_key_header
[SIGNATURE_ALGORITHM, vapid_key.public_key_for_push_header].join('=')
end
def encrypt(payload)
def legacy_encrypt(payload)
Webpush::Legacy::Encryption.encrypt(payload, key_p256dh, key_auth)
end
def standard_authorization_header
[STANDARD_AUTH_HEADER, standard_vapid_value].join(' ')
end
def standard_encrypt(payload)
Webpush::Encryption.encrypt(payload, key_p256dh, key_auth)
end
def legacy
!standard
end
private
def standard_vapid_value
"t=#{encoded_json_web_token},k=#{vapid_key.public_key_for_push_header}"
end
def encoded_json_web_token
JWT.encode(
web_token_payload,

View file

@ -0,0 +1,47 @@
# frozen_string_literal: true
module Status::Visibility
extend ActiveSupport::Concern
included do
enum :visibility,
{ public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 },
suffix: :visibility,
validate: true
scope :distributable_visibility, -> { where(visibility: %i(public unlisted)) }
scope :list_eligible_visibility, -> { where(visibility: %i(public unlisted private)) }
scope :not_direct_visibility, -> { where.not(visibility: :direct) }
validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog?
before_validation :set_visibility, unless: :visibility?
end
class_methods do
def selectable_visibilities
visibilities.keys - %w(direct limited)
end
end
def hidden?
!distributable?
end
def distributable?
public_visibility? || unlisted_visibility?
end
alias sign? distributable?
private
def set_visibility
self.visibility ||= reblog.visibility if reblog?
self.visibility ||= visibility_from_account
end
def visibility_from_account
account.locked? ? :private : :public
end
end

View file

@ -21,7 +21,7 @@ class DomainBlock < ApplicationRecord
include DomainNormalizable
include DomainMaterializable
enum :severity, { silence: 0, suspend: 1, noop: 2 }
enum :severity, { silence: 0, suspend: 1, noop: 2 }, validate: true
validates :domain, presence: true, uniqueness: true, domain: true

View file

@ -31,7 +31,7 @@ class Invite < ApplicationRecord
validates :comment, length: { maximum: COMMENT_SIZE_LIMIT }
before_validation :set_code
before_validation :set_code, on: :create
def valid_for_use?
(max_uses.nil? || uses < max_uses) && !expired? && user&.functional?

View file

@ -24,7 +24,7 @@ class IpBlock < ApplicationRecord
sign_up_requires_approval: 5000,
sign_up_block: 5500,
no_access: 9999,
}, prefix: true
}, prefix: true, validate: true
validates :ip, :severity, presence: true
validates :ip, uniqueness: true

View file

@ -40,6 +40,7 @@ class Status < ApplicationRecord
include Status::SearchConcern
include Status::SnapshotConcern
include Status::ThreadingConcern
include Status::Visibility
MEDIA_ATTACHMENTS_LIMIT = 4
@ -54,8 +55,6 @@ class Status < ApplicationRecord
update_index('statuses', :proper)
update_index('public_statuses', :proper)
enum :visibility, { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, suffix: :visibility, validate: true
belongs_to :application, class_name: 'Doorkeeper::Application', optional: true
belongs_to :account, inverse_of: :statuses
@ -100,7 +99,6 @@ class Status < ApplicationRecord
validates_with StatusLengthValidator
validates_with DisallowedHashtagsValidator
validates :reblog, uniqueness: { scope: :account }, if: :reblog?
validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog?
validates :content_type, inclusion: { in: %w(text/plain text/markdown text/html) }, allow_nil: true
accepts_nested_attributes_for :poll
@ -128,9 +126,6 @@ class Status < ApplicationRecord
scope :tagged_with_none, lambda { |tag_ids|
where('NOT EXISTS (SELECT * FROM statuses_tags forbidden WHERE forbidden.status_id = statuses.id AND forbidden.tag_id IN (?))', tag_ids)
}
scope :distributable_visibility, -> { where(visibility: %i(public unlisted)) }
scope :list_eligible_visibility, -> { where(visibility: %i(public unlisted private)) }
scope :not_direct_visibility, -> { where.not(visibility: :direct) }
scope :not_local_only, -> { where(local_only: [false, nil]) }
@ -145,7 +140,6 @@ class Status < ApplicationRecord
before_validation :prepare_contents, if: :local?
before_validation :set_reblog
before_validation :set_visibility
before_validation :set_conversation
before_validation :set_local
@ -249,16 +243,6 @@ class Status < ApplicationRecord
PreviewCardsStatus.where(status_id: id).delete_all
end
def hidden?
!distributable?
end
def distributable?
public_visibility? || unlisted_visibility?
end
alias sign? distributable?
def with_media?
ordered_media_attachments.any?
end
@ -358,10 +342,6 @@ class Status < ApplicationRecord
end
class << self
def selectable_visibilities
visibilities.keys - %w(direct limited)
end
def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil)
# direct timeline is mix of direct message from_me and to_me.
# 2 queries are executed with pagination.
@ -488,11 +468,6 @@ class Status < ApplicationRecord
update_column(:poll_id, poll.id) if association(:poll).loaded? && poll.present?
end
def set_visibility
self.visibility = reblog.visibility if reblog? && visibility.nil?
self.visibility = (account.locked? ? :private : :public) if visibility.nil?
end
def set_local_only
return unless account.domain.nil? && !attribute_changed?(:local_only)

View file

@ -16,7 +16,7 @@ class Trends::Links < Trends::Base
class Query < Trends::Query
def to_arel
scope = PreviewCard.joins(:trend).reorder(score: :desc)
scope = scope.merge(language_order_clause) if preferred_languages.present?
scope = scope.reorder(language_order_clause, score: :desc) if preferred_languages.present?
scope = scope.merge(PreviewCardTrend.allowed) if @allowed
scope = scope.offset(@offset) if @offset.present?
scope = scope.limit(@limit) if @limit.present?
@ -25,8 +25,8 @@ class Trends::Links < Trends::Base
private
def language_order_clause
language_order_for(PreviewCardTrend)
def trend_class
PreviewCardTrend
end
end

View file

@ -94,11 +94,14 @@ class Trends::Query
to_arel.to_a
end
def language_order_for(trend_class)
def language_order_clause
Arel::Nodes::Case.new.when(language_is_preferred).then(1).else(0).desc
end
def language_is_preferred
trend_class
.reorder(nil)
.in_order_of(:language, [preferred_languages], filter: false)
.order(score: :desc)
.arel_table[:language]
.in(preferred_languages)
end
def preferred_languages

View file

@ -15,7 +15,7 @@ class Trends::Statuses < Trends::Base
class Query < Trends::Query
def to_arel
scope = Status.joins(:trend).reorder(score: :desc)
scope = scope.merge(language_order_clause) if preferred_languages.present?
scope = scope.reorder(language_order_clause, score: :desc) if preferred_languages.present?
scope = scope.merge(StatusTrend.allowed) if @allowed
scope = scope.not_excluded_by_account(@account).not_domain_blocked_by_account(@account) if @account.present?
scope = scope.offset(@offset) if @offset.present?
@ -25,8 +25,8 @@ class Trends::Statuses < Trends::Base
private
def language_order_clause
language_order_for(StatusTrend)
def trend_class
StatusTrend
end
end

View file

@ -16,7 +16,7 @@ class Trends::Tags < Trends::Base
class Query < Trends::Query
def to_arel
scope = Tag.joins(:trend).reorder(score: :desc)
scope = scope.merge(language_order_clause) if preferred_languages.present?
scope = scope.reorder(language_order_clause, score: :desc) if preferred_languages.present?
scope = scope.merge(TagTrend.allowed) if @allowed
scope = scope.offset(@offset) if @offset.present?
scope = scope.limit(@limit) if @limit.present?
@ -25,8 +25,8 @@ class Trends::Tags < Trends::Base
private
def language_order_clause
language_order_for(TagTrend)
def trend_class
TagTrend
end
end

View file

@ -5,10 +5,11 @@
# Table name: web_push_subscriptions
#
# id :bigint(8) not null, primary key
# endpoint :string not null
# key_p256dh :string not null
# key_auth :string not null
# data :json
# endpoint :string not null
# key_auth :string not null
# key_p256dh :string not null
# standard :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# access_token_id :bigint(8)

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class LanguagePresenter < ActiveModelSerializers::Model
attributes :code, :name, :native_name
attributes :code, :name
def initialize(code)
super()
@ -13,8 +13,4 @@ class LanguagePresenter < ActiveModelSerializers::Model
def name
@item[0]
end
def native_name
@item[1]
end
end

View file

@ -44,7 +44,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
delegate :suspended?, :instance_actor?, to: :object
def id
object.instance_actor? ? instance_actor_url : account_url(object)
ActivityPub::TagManager.instance.uri_for(object)
end
def type
@ -60,27 +60,27 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
end
def following
account_following_index_url(object)
ActivityPub::TagManager.instance.following_uri_for(object)
end
def followers
account_followers_url(object)
ActivityPub::TagManager.instance.followers_uri_for(object)
end
def inbox
object.instance_actor? ? instance_actor_inbox_url : account_inbox_url(object)
ActivityPub::TagManager.instance.inbox_uri_for(object)
end
def outbox
object.instance_actor? ? instance_actor_outbox_url : account_outbox_url(object)
ActivityPub::TagManager.instance.outbox_uri_for(object)
end
def featured
account_collection_url(object, :featured)
ActivityPub::TagManager.instance.collection_uri_for(object, :featured)
end
def featured_tags
account_collection_url(object, :tags)
ActivityPub::TagManager.instance.collection_uri_for(object, :tags)
end
def endpoints

View file

@ -38,6 +38,6 @@ class ActivityPub::AddSerializer < ActivityPub::Serializer
end
def target
account_collection_url(object.account, :featured)
ActivityPub::TagManager.instance.collection_uri_for(object.account, :featured)
end
end

View file

@ -38,6 +38,6 @@ class ActivityPub::RemoveSerializer < ActivityPub::Serializer
end
def target
account_collection_url(object.account, :featured)
ActivityPub::TagManager.instance.collection_uri_for(object.account, :featured)
end
end

View file

@ -1,7 +1,9 @@
# frozen_string_literal: true
class REST::WebPushSubscriptionSerializer < ActiveModel::Serializer
attributes :id, :endpoint, :alerts, :server_key, :policy
attributes :id, :endpoint, :standard, :alerts, :server_key, :policy
delegate :standard, to: :object
def alerts
(object.data&.dig('alerts') || {}).each_with_object({}) { |(k, v), h| h[k] = ActiveModel::Type::Boolean.new.cast(v) }

View file

@ -13,7 +13,7 @@ class WebfingerSerializer < ActiveModel::Serializer
if object.instance_actor?
[instance_actor_url]
else
[short_account_url(object), account_url(object)]
[short_account_url(object), ActivityPub::TagManager.instance.uri_for(object)]
end
end
@ -43,6 +43,6 @@ class WebfingerSerializer < ActiveModel::Serializer
end
def self_href
object.instance_actor? ? instance_actor_url : account_url(object)
ActivityPub::TagManager.instance.uri_for(object)
end
end

View file

@ -19,7 +19,19 @@ class Web::PushNotificationWorker
# in the meantime, so we have to double-check before proceeding
return unless @notification.activity.present? && @subscription.pushable?(@notification)
payload = web_push_request.encrypt(push_notification_json)
if web_push_request.legacy
perform_legacy_request
else
perform_standard_request
end
rescue ActiveRecord::RecordNotFound
true
end
private
def perform_legacy_request
payload = web_push_request.legacy_encrypt(push_notification_json)
request_pool.with(web_push_request.audience) do |http_client|
request = Request.new(:post, web_push_request.endpoint, body: payload.fetch(:ciphertext), http_client: http_client)
@ -31,28 +43,48 @@ class Web::PushNotificationWorker
'Content-Encoding' => 'aesgcm',
'Encryption' => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}",
'Crypto-Key' => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{web_push_request.crypto_key_header}",
'Authorization' => web_push_request.authorization_header,
'Authorization' => web_push_request.legacy_authorization_header,
'Unsubscribe-URL' => subscription_url
)
request.perform do |response|
# If the server responds with an error in the 4xx range
# that isn't about rate-limiting or timeouts, we can
# assume that the subscription is invalid or expired
# and must be removed
if (400..499).cover?(response.code) && ![408, 429].include?(response.code)
@subscription.destroy!
elsif !(200...300).cover?(response.code)
raise Mastodon::UnexpectedResponseError, response
end
end
send(request)
end
rescue ActiveRecord::RecordNotFound
true
end
private
def perform_standard_request
payload = web_push_request.standard_encrypt(push_notification_json)
request_pool.with(web_push_request.audience) do |http_client|
request = Request.new(:post, web_push_request.endpoint, body: payload, http_client: http_client)
request.add_headers(
'Content-Type' => 'application/octet-stream',
'Ttl' => TTL.to_s,
'Urgency' => URGENCY,
'Content-Encoding' => 'aes128gcm',
'Authorization' => web_push_request.standard_authorization_header,
'Unsubscribe-URL' => subscription_url,
'Content-Length' => payload.length.to_s
)
send(request)
end
end
def send(request)
request.perform do |response|
# If the server responds with an error in the 4xx range
# that isn't about rate-limiting or timeouts, we can
# assume that the subscription is invalid or expired
# and must be removed
if (400..499).cover?(response.code) && ![408, 429].include?(response.code)
@subscription.destroy!
elsif !(200...300).cover?(response.code)
raise Mastodon::UnexpectedResponseError, response
end
end
end
def web_push_request
@web_push_request || WebPushRequest.new(@subscription)

View file

@ -15,9 +15,17 @@ cs:
user/invite_request:
text: Důvod
errors:
attributes:
domain:
invalid: není platný název domény
messages:
invalid_domain_on_line: "%{value} není platný název domény"
too_many_lines: překročil limit %{limit} řádků
models:
account:
attributes:
fields:
fields_with_values_missing_labels: obsahuje hodnoty s chybějícími popisky
username:
invalid: musí obsahovat pouze písmena, číslice a podtržítka
reserved: je vyhrazeno
@ -33,6 +41,11 @@ cs:
attributes:
data:
malformed: je chybný
list_account:
attributes:
account_id:
taken: již je na seznamu
must_be_following: musí být sledovaný účet
status:
attributes:
reblog:

View file

@ -24,6 +24,8 @@ ga:
models:
account:
attributes:
fields:
fields_with_values_missing_labels: ina bhfuil luachanna a bhfuil lipéid in easnamh orthu
username:
invalid: ní mór go mbeadh litreacha, uimhreacha agus pointí béime amháin
reserved: in áirithe

View file

@ -24,6 +24,8 @@ it:
models:
account:
attributes:
fields:
fields_with_values_missing_labels: contiene valori con label mancanti
username:
invalid: deve contenere solo lettere, numeri e trattini bassi
reserved: è riservato

View file

@ -1 +1,35 @@
---
ne:
activerecord:
attributes:
user:
agreement: सेवा सम्झौता
email: ईमेल ठेगाना
password: पासवर्ड
user/account:
username: प्रयोगकर्ता नाम
user/invite_request:
text: कारण
errors:
attributes:
domain:
invalid: मान्य डोमेन नाम होइन
messages:
invalid_domain_on_line: "%{value} मान्य डोमेन नाम होइन"
models:
account:
attributes:
username:
invalid: अक्षर, संख्या र अन्डरस्कोर मात्र हुनु पर्छ
admin/webhook:
attributes:
url:
invalid: मान्य URL होइन
doorkeeper/application:
attributes:
website:
invalid: मान्य URL होइन
list_account:
attributes:
account_id:
taken: पहिले नै सूचीमा छ

View file

@ -27,7 +27,7 @@ pt-PT:
fields:
fields_with_values_missing_labels: contém valores com etiquetas em falta
username:
invalid: deve conter apenas letras, números e traços inferiores
invalid: deve conter apenas letras, números e traços inferiores (_)
reserved: está reservado
admin/webhook:
attributes:

View file

@ -937,6 +937,7 @@ ca:
generates:
action: Generar
chance_to_review_html: "<strong>Les condicions de servei generades no es publicaran automàticament.</strong> Tindreu l'oportunitat de revisar-ne els resultats. Empleneu els detalls necessaris per a procedir."
explanation_html: La plantilla de condicions de servei proveïda ho és només a títol informatiu i no s'ha d'interpretar com a consell jurídic en cap cas. Consulteu el vostre propi servei legal sobre la vostra situació i les qüestions legals específiques que tingueu.
title: Configuració de les condicions de servei
history: Historial
live: En ús
@ -1892,6 +1893,8 @@ ca:
title: Un inici de sessió nou
terms_of_service_changed:
sign_off: L'equip de %{domain}
subject: Actualitzacions de les condicions de servei
subtitle: Les condicions de servei de %{domain} canvien
title: Actualització important
warning:
appeal: Envia una apel·lació

View file

@ -78,10 +78,10 @@ cs:
enabled: Povoleno
enabled_msg: Účet %{username} byl úspěšně rozmrazen
followers: Sledující
follows: Sledované
follows: Sledovaní
header: Záhlaví
inbox_url: URL příchozí schránky
invite_request_text: Důvody založení
invite_request_text: Důvody pro připojení
invited_by: Pozván uživatelem
ip: IP adresa
joined: Uživatelem od
@ -187,29 +187,40 @@ cs:
confirm_user: Potvrdit uživatele
create_account_warning: Vytvořit varování
create_announcement: Nové oznámení
create_canonical_email_block: Vytvořit blok e-mailu
create_custom_emoji: Vytvořit vlastní emoji
create_domain_allow: Vytvořit povolení domény
create_domain_block: Vytvořit blokaci domény
create_email_domain_block: Vytvořit blok e-mailové domény
create_ip_block: Vytvořit IP pravidlo
create_relay: Vytvořit relay
create_unavailable_domain: Vytvořit nedostupnou doménu
create_user_role: Vytvořit roli
demote_user: Snížit roli uživatele
destroy_announcement: Odstranit oznámení
destroy_canonical_email_block: Odblokovat email
destroy_custom_emoji: Odstranit vlastní emoji
destroy_domain_allow: Odstranit povolení domény
destroy_domain_block: Odstranit blokaci domény
destroy_email_domain_block: Smazat blokaci e-mailové domény
destroy_instance: Odmazat doménu
destroy_ip_block: Smazat IP pravidlo
destroy_relay: Smazat relay
destroy_status: Odstranit Příspěvek
destroy_unavailable_domain: Smazat nedostupnou doménu
destroy_user_role: Zničit roli
disable_2fa_user: Vypnout 2FA
disable_custom_emoji: Zakázat vlastní emoji
disable_relay: Deaktivovat relay
disable_sign_in_token_auth_user: Zrušit uživatelovo ověřování e-mailovým tokenem
disable_user: Deaktivovat uživatele
enable_custom_emoji: Povolit vlastní emoji
enable_relay: Aktivovat relay
enable_sign_in_token_auth_user: Povolit uživatelovo ověřování e-mailovým tokenem
enable_user: Povolit uživatele
memorialize_account: Změna na „in memoriam“
promote_user: Povýšit uživatele
publish_terms_of_service: Zveřejnit smluvní podmínky
reject_appeal: Zamítnout odvolání
reject_user: Odmítnout uživatele
remove_avatar_user: Odstranit avatar
@ -236,36 +247,50 @@ cs:
approve_appeal_html: Uživatel %{name} schválil odvolání proti rozhodnutí moderátora %{target}
approve_user_html: "%{name} schválil registraci od %{target}"
assigned_to_self_report_html: Uživatel %{name} si přidělil hlášení %{target}
change_email_user_html: "%{name} změnil*a e-mailovou adresu %{target}"
change_role_user_html: "%{name} změnil roli %{target}"
confirm_user_html: Uživatel %{name} potvrdil e-mailovou adresu uživatele %{target}
create_account_warning_html: Uživatel %{name} poslal %{target} varování
create_announcement_html: Uživatel %{name} vytvořil nové oznámení %{target}
create_canonical_email_block_html: "%{name} zablokoval e-mail s hashem %{target}"
create_custom_emoji_html: Uživatel %{name} nahrál nové emoji %{target}
create_domain_allow_html: Uživatel %{name} povolil federaci s doménou %{target}
create_domain_block_html: Uživatel %{name} zablokoval doménu %{target}
create_email_domain_block_html: Uživatel %{name} zablokoval e-mailovou doménu %{target}
create_ip_block_html: Uživatel %{name} vytvořil pravidlo pro IP %{target}
create_relay_html: "%{name} vytvořil*a relay %{target}"
create_unavailable_domain_html: "%{name} zastavil doručování na doménu %{target}"
create_user_role_html: "%{name} vytvořil %{target} roli"
demote_user_html: Uživatel %{name} degradoval uživatele %{target}
destroy_announcement_html: Uživatel %{name} odstranil oznámení %{target}
destroy_canonical_email_block_html: "%{name} odblokoval*a e-mail s hashem %{target}"
destroy_custom_emoji_html: "%{name} odstranil emoji %{target}"
destroy_domain_allow_html: Uživatel %{name} zakázal federaci s doménou %{target}
destroy_domain_block_html: Uživatel %{name} odblokoval doménu %{target}
destroy_email_domain_block_html: "%{name} odblokoval*a e-mailovou doménu %{target}"
destroy_instance_html: Uživatel %{name} odmazal doménu %{target}
destroy_ip_block_html: Uživatel %{name} odstranil pravidlo pro IP %{target}
destroy_relay_html: "%{name} odstranil*a relay %{target}"
destroy_status_html: Uživatel %{name} odstranil příspěvek uživatele %{target}
destroy_unavailable_domain_html: "%{name} obnovil doručování na doménu %{target}"
destroy_user_role_html: "%{name} odstranil %{target} roli"
disable_2fa_user_html: Uživatel %{name} vypnul dvoufázové ověřování pro uživatele %{target}
disable_custom_emoji_html: Uživatel %{name} zakázal emoji %{target}
disable_relay_html: "%{name} deaktivoval*a relay %{target}"
disable_sign_in_token_auth_user_html: "%{name} deaktivoval*a ověřování e-mailovým tokenem pro %{target}"
disable_user_html: Uživatel %{name} zakázal přihlašování pro uživatele %{target}
enable_custom_emoji_html: Uživatel %{name} povolil emoji %{target}
enable_relay_html: "%{name} aktivoval*a relay %{target}"
enable_sign_in_token_auth_user_html: "%{name} aktivoval*a ověřování e-mailovým tokenem pro %{target}"
enable_user_html: Uživatel %{name} povolil přihlašování pro uživatele %{target}
memorialize_account_html: Uživatel %{name} změnil účet %{target} na „in memoriam“ stránku
promote_user_html: Uživatel %{name} povýšil uživatele %{target}
publish_terms_of_service_html: "%{name} zveřejnil*a aktualizace podmínek služby"
reject_appeal_html: Uživatel %{name} zamítl odvolání proti rozhodnutí moderátora %{target}
reject_user_html: "%{name} odmítl registraci od %{target}"
remove_avatar_user_html: Uživatel %{name} odstranil avatar uživatele %{target}
reopen_report_html: Uživatel %{name} znovu otevřel hlášení %{target}
resend_user_html: "%{name} znovu odeslal*a potvrzovací e-mail pro %{target}"
reset_password_user_html: Uživatel %{name} obnovil heslo uživatele %{target}
resolve_report_html: Uživatel %{name} vyřešil hlášení %{target}
sensitive_account_html: "%{name} označil média účtu %{target} jako citlivá"
@ -436,6 +461,7 @@ cs:
many: "%{count} pokusů o registraci za poslední týden"
one: "%{count} pokus o registraci za poslední týden"
other: "%{count} pokusů o registraci za poslední týden"
created_msg: E-mailová doména úspěšně zablokována
delete: Smazat
dns:
types:
@ -445,7 +471,9 @@ cs:
create: Přidat doménu
resolve: Přeložit doménu
title: Blokovat novou e-mailovou doménu
no_email_domain_block_selected: Žádné blokace e-mailové domény nebyly změněny, protože žádné nebyly vybrány
not_permitted: Nepovoleno
resolved_dns_records_hint_html: Doménové jméno vede na následující MX domény, které mají nakonec na starost přijímání e-mailů. Blokování MX domény zablokuje registrace z jakékoliv e-mailové adresy, která používá stejnou MX doménu, i když je viditelné doménové jméno jiné. <strong>Dejte si pozor, abyste nezablokovali velké e-mailové poskytovatele.</strong>
resolved_through_html: Přeložena přes %{domain}
title: Blokované e-mailové domény
export_domain_allows:
@ -609,7 +637,9 @@ cs:
resolve_description_html: Nebudou učiněny žádné kroky proti nahlášenému účtu, žádný prohřešek zaznamenán a hlášení bude uzavřeno.
silence_description_html: Účet bude viditelný pouze těm, kdo jej již sledují nebo si jej ručně vyhledají, což výrazně omezí jeho dosah. Vždy lze vrátit zpět. Uzavře všechna hlášení proti tomuto účtu.
suspend_description_html: Účet a veškerý jeho obsah se znepřístupní a bude nakonec smazán, interakce s ním nebude možná. Lze vrátit zpět do 30 dnů. Uzavře všechna hlášení proti tomuto účtu.
actions_description_html: Rozhodněte, který krok učinit pro vyřešení tohoto hlášení. Pokud podniknete kárný krok proti nahlášenému účtu, bude mu zasláno e-mailové oznámení, s výjimkou případu, kdy je zvolena kategorie <strong>Spam</strong>.
actions_description_remote_html: Rozhodněte, co podniknout pro vyřešení tohoto hlášení. Toto ovlivní pouze to, jak <strong>váš</strong> server komunikuje s tímto vzdáleným účtem, a zpracuje jeho obsah.
actions_no_posts: Toto hlášení nemá žádné související příspěvky k odstranění
add_to_report: Přidat do hlášení další
already_suspended_badges:
local: Již pozastaveno na tomto serveru
@ -673,6 +703,7 @@ cs:
delete_data_html: Odstranit profil a obsah <strong>@%{acct}</strong> ode dneška po 30 dní, pokud mezitím nebude zrušeno jeho pozastavení
preview_preamble_html: "<strong>@%{acct}</strong> obdrží varování s následujícím obsahem:"
record_strike_html: Zaznamenat prohřešek <strong>@%{acct}</strong> pro pomoc s řešením budoucích přestupků z tohoto účtu
send_email_html: Poslat varovný e-mail pro <strong>@%{acct}</strong>
warning_placeholder: Volitelné další odůvodnění moderační akce.
target_origin: Původ nahlášeného účtu
title: Hlášení
@ -716,6 +747,7 @@ cs:
manage_appeals: Spravovat odvolání
manage_appeals_description: Umožňuje uživatelům posuzovat odvolání proti moderátorským zásahům
manage_blocks: Spravovat blokace
manage_blocks_description: Umožňuje uživatelům blokovat poskytovatele e-mailů a IP adresy
manage_custom_emojis: Spravovat vlastní emoji
manage_custom_emojis_description: Umožňuje uživatelům spravovat vlastní emoji na serveru
manage_federation: Spravovat federaci
@ -733,6 +765,7 @@ cs:
manage_taxonomies: Správa taxonomií
manage_taxonomies_description: Umožňuje uživatelům zkontrolovat populární obsah a aktualizovat nastavení hashtag
manage_user_access: Spravovat uživatelské přístupy
manage_user_access_description: Umožňuje uživatelům rušit jiným uživatelům dvoufázové ověřování, měnit jejich e-mailovou adresu a obnovovat jim hesla
manage_users: Spravovat uživatele
manage_users_description: Umožňuje uživatelům zobrazit podrobnosti ostatních uživatelů a provádět moderování proti nim
manage_webhooks: Spravovat webhooky
@ -807,6 +840,7 @@ cs:
destroyed_msg: Upload stránky byl úspěšně smazán!
software_updates:
critical_update: Kritické — aktualizujte, prosím, co nejdříve
description: Doporučuje se udržovat vaši instalaci Mastodonu aktuální, aby se využily nejnovější opravy a funkce. Kromě toho je někdy velmi důležité včas aktualizovat Mastodon, aby se předešlo bezpečnostním problémům. Z těchto důvodů Mastodon kontroluje aktualizace každých 30 minut a bude vás informovat podle nastavení vašeho e-mailového oznámení.
documentation_link: Zjistit více
release_notes: Poznámky k vydání
title: Dostupné aktualizace
@ -822,6 +856,7 @@ cs:
back_to_account: Zpět na stránku účtu
back_to_report: Zpět na stránku hlášení
batch:
add_to_report: 'Přidat do hlášení #%{id}'
remove_from_report: Odebrat z hlášení
report: Nahlásit
contents: Obsah
@ -833,12 +868,17 @@ cs:
media:
title: Média
metadata: Metadata
no_history: Tento příspěvek nebyl upraven
no_status_selected: Nebyly změněny žádné příspěvky, neboť žádné nebyly vybrány
open: Otevřít příspěvek
original_status: Původní příspěvek
reblogs: Boosty
replied_to_html: Odpověděl %{acct_link}
status_changed: Příspěvek změněn
status_title: Příspěvek od @%{name}
title: Příspěvky na účtu - @%{name}
trending: Populární
view_publicly: Zobrazit veřejně
visibility: Viditelnost
with_media: S médii
strikes:
@ -880,6 +920,9 @@ cs:
message_html: Nedefinovali jste žádná pravidla serveru.
sidekiq_process_check:
message_html: Pro %{value} frontu/fronty neběží žádný Sidekiq proces. Zkontrolujte prosím svou Sidekiq konfiguraci
software_version_check:
action: Zobrazit dostupné aktualizace
message_html: K dispozici je aktualizace Mastodonu.
software_version_critical_check:
action: Zobrazit dostupné aktualizace
message_html: K dispozici je kritická aktualizace Mastodonu, prosím aktualizujte co nejrychleji.
@ -906,19 +949,57 @@ cs:
name: Název
newest: Nejnovější
oldest: Nejstarší
open: Zobrazit veřejně
reset: Resetovat
review: Stav posouzení
search: Hledat
title: Hashtagy
updated_msg: Nastavení hashtagů bylo úspěšně aktualizováno
terms_of_service:
back: Zpět na podmínky služby
changelog: Co se změnilo
create: Použít vlastní
current: Aktuální
draft: Koncept
generate: Použít šablonu
generates:
action: Vygenerovat
chance_to_review_html: "<strong>Vygenerované podmínky služby nebudou zveřejněny automaticky.</strong> Výsledky budete mít možnost zkontrolovat. Pro pokračování vyplňte potřebné podrobnosti."
explanation_html: Poskytovaná šablona služeb je určena pouze pro informační účely a neměla by být vykládána jako právní poradenství v jakékoli věci. Prosíme, konzultujte Vaši situaci a konkrétní právní otázky s Vaším pravním zástupcem.
title: Nastavení podmínek služby
history: Historie
live: Živě
no_history: Zatím nejsou zaznamenány žádné změny podmínek služby.
no_terms_of_service_html: Momentálně nemáte nakonfigurovány žádné podmínky služby. Podmínky služby jsou určeny k zajištění jasnosti a ochránit Vás před případnými právními závazky ve sporech s vašimi uživateli.
notified_on_html: Uživatelé upozorněni dne %{date}
notify_users: Upozornit uživatele
preview:
explanation_html: 'E-mail bude poslán <strong>%{display_count} uživatelům</strong>, kteří se zaregistrovali před datem %{date}. Následující text bude obsažen v onom e-mailu:'
send_preview: Poslat náhled na %{email}
send_to_all:
few: Poslat %{display_count} emaily
many: Poslat %{display_count} emailů
one: Poslat %{display_count} email
other: Poslat %{display_count} emailů
title: Náhled oznámení o podmínkách služby
publish: Zveřejnit
published_on_html: Zveřejněno %{date}
save_draft: Uložit koncept
title: Podmínky služby
title: Administrace
trends:
allow: Povolit
approved: Schválené
confirm_allow: Opravdu chcete povolit vybrané štítky?
confirm_disallow: Opravdu chcete zakázat vybrané štítky?
disallow: Zakázat
links:
allow: Povolit odkaz
allow_provider: Povolit vydavatele
confirm_allow: Jste si jist, že chcete povolit vybrané odkazy?
confirm_allow_provider: Opravdu chcete povolit vybrané poskytovatele?
confirm_disallow: Opravdu chcete zakázat vybrané odkazy?
confirm_disallow_provider: Opravdu chcete zakázat vybrané poskytovatele?
description_html: Toto jsou odkazy, které jsou momentálně hojně sdíleny účty, jejichž příspěvky váš server vidí. To může pomoct vašim uživatelům zjistit, co se děje ve světě. Žádné odkazy se nezobrazují veřejně, dokud neschválíte vydavatele. Můžete také povolit nebo zamítnout jednotlivé odkazy.
disallow: Zakázat odkaz
disallow_provider: Zakázat vydavatele
@ -944,6 +1025,10 @@ cs:
statuses:
allow: Povolit příspěvek
allow_account: Povolit autora
confirm_allow: Opravdu chcete povolit vybrané tooty?
confirm_allow_account: Opravdu chcete povolit vybrané účty?
confirm_disallow: Opravdu chcete zakázat vybrané tooty?
confirm_disallow_account: Opravdu chcete zakázat vybrané účty?
description_html: Toto jsou příspěvky, o kterých váš server ví, že jsou momentálně hodně sdíleny a oblibovány. To může pomoci vašim novým i vracejícím se uživatelům najít další lidi ke sledování. Žádné příspěvky se nezobrazují veřejně, dokud neschválíte autora a tento autor nepovolí navrhování svého účtu ostatním. Můžete také povolit či zamítnout jednotlivé příspěvky.
disallow: Zakázat příspěvek
disallow_account: Zakázat autora
@ -980,6 +1065,7 @@ cs:
many: Použit %{count} lidmi za poslední týden
one: Použit jedním člověkem za poslední týden
other: Použit %{count} lidmi za poslední týden
title: Doporučení & Trendy
trending: Populární
warning_presets:
add_new: Přidat nové
@ -1066,7 +1152,9 @@ cs:
guide_link_text: Zapojit se může každý.
sensitive_content: Citlivý obsah
application_mailer:
notification_preferences: Změnit předvolby e-mailu
salutation: "%{name},"
settings: 'Změnit předvolby e-mailu: %{link}'
unsubscribe: Přestat odebírat
view: 'Zobrazit:'
view_profile: Zobrazit profil
@ -1086,6 +1174,7 @@ cs:
hint_html: Ještě jedna věc! Musíme potvrdit, že jste člověk (to proto, abychom drželi stranou spam!). Vyřešte CAPTCHA níže a klikněte na "Pokračovat".
title: Bezpečnostní kontrola
confirmations:
awaiting_review: Vaše e-mailová adresa je potvrzena! Personál %{domain} nyní kontrolují vaši registraci. Pokud váš účet schválí, obdržíte e-mail!
awaiting_review_title: Vaše registrace se ověřuje
clicking_this_link: kliknutím na tento odkaz
login_link: přihlásit se
@ -1093,6 +1182,7 @@ cs:
redirect_to_app_html: Měli byste být přesměrováni do aplikace <strong>%{app_name}</strong>. Pokud se tak nestalo, zkuste %{clicking_this_link} nebo ručně se vrátit do aplikace.
registration_complete: Vaše registrace na %{domain} je hotová!
welcome_title: Vítám uživatele %{name}!
wrong_email_hint: Pokud není tento e-mail správný, můžete si ho změnit v nastavení účtu.
delete_account: Odstranit účet
delete_account_html: Chcete-li odstranit svůj účet, <a href="%{path}">pokračujte zde</a>. Budete požádáni o potvrzení.
description:
@ -1112,6 +1202,7 @@ cs:
migrate_account_html: Zde můžete <a href="%{path}">nastavit přesměrování tohoto účtu na jiný</a>.
or_log_in_with: Nebo se přihlaste pomocí
progress:
confirm: Potvrdit e-mail
details: Vaše údaje
review: Naše hodnocení
rules: Přijmout pravidla
@ -1133,22 +1224,37 @@ cs:
security: Zabezpečení
set_new_password: Nastavit nové heslo
setup:
email_below_hint_html: Zkontrolujte složku se spamem, nebo požádejte o další. Svou e-mailovou adresu si můžete opravit, pokud je špatně.
email_settings_hint_html: Kliknutím na odkaz, který jsme poslali do %{email}, začnete používat Mastodon. Budeme tu čekat.
link_not_received: Nedostali jste odkaz?
new_confirmation_instructions_sent: Za několik minut obdržíte nový e-mail s potvrzovacím odkazem!
title: Zkontrolujte doručenou poštu
sign_in:
preamble_html: Přihlaste se svými <strong>%{domain}</strong> údaji. Pokud je váš účet hostován na jiném serveru, přihlásit se zde nemůžete.
title: Přihlásit se k %{domain}
sign_up:
manual_review: Registrace na %{domain} procházejí manuálním hodnocením od našich moderátorů. Abyste nám pomohli zpracovat Vaši registraci, napište trochu o sobě a proč chcete účet na %{domain}.
preamble: S účtem na tomto Mastodon serveru budete moci sledovat jakoukoli jinou osobu na fediversu, bez ohledu na to, kde je jejich účet hostován.
title: Pojďme vás nastavit na %{domain}.
status:
account_status: Stav účtu
confirming: Čekáme na dokončení potvrzení e-mailu.
functional: Váš účet je plně funkční.
pending: Vaše žádost čeká na posouzení naším personálem. To může nějakou dobu trvat. Pokud bude váš požadavek schválen, obdržíte e-mail.
redirecting_to: Váš účet je neaktivní, protože je právě přesměrován na účet %{acct}.
self_destruct: Protože %{domain} končí, budete mít k účtu jen omezený přístup.
view_strikes: Zobrazit minulé prohřešky vašeho účtu
too_fast: Formulář byl odeslán příliš rychle, zkuste to znovu.
use_security_key: Použít bezpečnostní klíč
user_agreement_html: Přečetl jsem si a souhlasím s <a href="%{terms_of_service_path}" target="_blank">podmínkami služby</a> a <a href="%{privacy_policy_path}" target="_blank">ochranou osobních údajů</a>
author_attribution:
example_title: Ukázkový text
hint_html: Píšete novinové články nebo blog mimo Mastodon? Kontrolujte, jak Vám bude připisováno autorství, když jsou sdíleny na Mastodonu.
instructions: 'Ujistěte se, že tento kód je v HTML vašeho článku:'
more_from_html: Více od %{name}
s_blog: Blog %{name}
then_instructions: Poté přidejte název domény publikace do níže uvedeného pole.
title: Připisování autorství
challenge:
confirm: Pokračovat
hint_html: "<strong>Tip:</strong> Po dobu jedné hodiny vás o heslo nebudeme znovu žádat."
@ -1185,6 +1291,9 @@ cs:
before: 'Před pokračováním si prosím pečlivě přečtěte tyto poznámky:'
caches: Obsah, který byl uložen do cache jiných serverů, nemusí být smazán
data_removal: Vaše příspěvky a další data budou trvale smazána
email_change_html: Můžete <a href="%{path}">změnit svou e-mailovou adresu</a> bez odstranění svého účtu
email_contact_html: Pokud stále nedorazí, můžete poslat e-mail <a href="mailto:%{email}">%{email}</a> pro pomoc
email_reconfirmation_html: Pokud neobdržíte potvrzovací e-mail, můžete si ho <a href="%{path}">vyžádat znovu</a>
irreversible: Váš účet nebude možné obnovit ani znovu aktivovat
more_details_html: Podrobnosti najdete v <a href="%{terms_path}">zásadách ochrany osobních údajů</a>.
username_available: Vaše uživatelské jméno bude opět dostupné
@ -1356,6 +1465,68 @@ cs:
merge_long: Ponechat existující záznamy a přidat nové
overwrite: Přepsat
overwrite_long: Nahradit aktuální záznamy novými
overwrite_preambles:
blocking_html:
few: Chystáte se <strong>nahradit váš seznam bloků</strong> s <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
many: Chystáte se <strong>nahradit váš seznam bloků</strong> s <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
one: Chystáte se <strong>nahradit váš seznam bloků</strong> s <strong>%{count} účtem</strong> z <strong>%{filename}</strong>.
other: Chystáte se <strong>nahradit váš seznam bloků</strong> s <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
bookmarks_html:
few: Chystáte se <strong>nahradit své záložky</strong> s <strong>%{count} příspěvky</strong> z <strong>%{filename}</strong>.
many: Chystáte se <strong>nahradit své záložky</strong> s <strong>%{count} příspěvky</strong> z <strong>%{filename}</strong>.
one: Chystáte se <strong>nahradit své záložky</strong> s <strong>%{count} příspěvkem</strong> z <strong>%{filename}</strong>.
other: Chystáte se <strong>nahradit své záložky</strong> s <strong>%{count} příspěvky</strong> z <strong>%{filename}</strong>.
domain_blocking_html:
few: Chystáte se <strong>nahradit Váš seznam zablokovaných domén</strong> s <strong>%{count} stránky</strong> z <strong>%{filename}</strong>.
many: Chystáte se <strong>nahradit Váš seznam zablokovaných domén</strong> s <strong>%{count} stránky</strong> z <strong>%{filename}</strong>.
one: Chystáte se <strong>nahradit Váš seznam zablokovaných domén</strong> s <strong>%{count} stránkou</strong> z <strong>%{filename}</strong>.
other: Chystáte se <strong>nahradit Váš seznam zablokovaných domén</strong> s <strong>%{count} stránky</strong> z <strong>%{filename}</strong>.
following_html:
few: Chystáte se <strong>sledovat</strong> až <strong>%{count} účty</strong> z <strong>%{filename}</strong> a <strong>přestanete sledovat kohokoliv jiného</strong>.
many: Chystáte se <strong>sledovat</strong> až <strong>%{count} účtů</strong> z <strong>%{filename}</strong> a <strong>přestanete sledovat kohokoliv jiného</strong>.
one: Chystáte se <strong>sledovat</strong> až <strong>%{count} účet</strong> z <strong>%{filename}</strong> a <strong>přestanete sledovat kohokoliv jiného</strong>.
other: Chystáte se <strong>sledovat</strong> až <strong>%{count} účtů</strong> z <strong>%{filename}</strong> a <strong>přestanete sledovat kohokoliv jiného</strong>.
lists_html:
few: Chystáte se <strong>nahradit své seznamy</strong> obsahem <strong>%{filename}</strong>. Až <strong>%{count} účty</strong> budou přidány do nových seznamů.
many: Chystáte se <strong>nahradit své seznamy</strong> obsahem <strong>%{filename}</strong>. Až <strong>%{count} účtů</strong> bude přidáno do nových seznamů.
one: Chystáte se <strong>nahradit své seznamy</strong> obsahem <strong>%{filename}</strong>. Až <strong>%{count} účet</strong> bude přidán do nových seznamů.
other: Chystáte se <strong>nahradit své seznamy</strong> obsahem <strong>%{filename}</strong>. Až <strong>%{count} účtů</strong> bude přidáno do nových seznamů.
muting_html:
few: Chystáte se <strong>nahradit svůj seznam ztišených účtů</strong> s <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
many: Chystáte se <strong>nahradit svůj seznam ztišených účtů</strong> s <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
one: Chystáte se <strong>nahradit svůj seznam ztišených účtů</strong> s <strong>%{count} účtem</strong> z <strong>%{filename}</strong>.
other: Chystáte se <strong>nahradit svůj seznam ztišených účtů</strong> s <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
preambles:
blocking_html:
few: Chystáte se <strong>zablokovat</strong> až <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
many: Chystáte se <strong>zablokovat</strong> až <strong>%{count} účtů</strong> z <strong>%{filename}</strong>.
one: Chystáte se <strong>zablokovat</strong> až <strong>%{count} účet</strong> z <strong>%{filename}</strong>.
other: Chystáte se <strong>zablokovat</strong> až <strong>%{count} účtů</strong> z <strong>%{filename}</strong>.
bookmarks_html:
few: Chystáte se přidat až <strong>%{count} příspěvky</strong> z <strong>%{filename}</strong> do vašich <strong>záložek</strong>.
many: Chystáte se přidat až <strong>%{count} příspěvků</strong> z <strong>%{filename}</strong> do vašich <strong>záložek</strong>.
one: Chystáte se přidat až <strong>%{count} příspěvek</strong> z <strong>%{filename}</strong> do vašich <strong>záložek</strong>.
other: Chystáte se přidat až <strong>%{count} příspěvků</strong> z <strong>%{filename}</strong> do vašich <strong>záložek</strong>.
domain_blocking_html:
few: Chystáte se <strong>zablokovat</strong> až <strong>%{count} domény</strong> z <strong>%{filename}</strong>.
many: Chystáte se <strong>zablokovat</strong> až <strong>%{count} domén</strong> z <strong>%{filename}</strong>.
one: Chystáte se <strong>zablokovat</strong> až <strong>%{count} doménu</strong> z <strong>%{filename}</strong>.
other: Chystáte se <strong>zablokovat</strong> až <strong>%{count} domén</strong> z <strong>%{filename}</strong>.
following_html:
few: Chystáte se <strong>sledovat</strong> až <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
many: Chystáte se <strong>sledovat</strong> až <strong>%{count} účtů</strong> z <strong>%{filename}</strong>.
one: Chystáte se <strong>sledovat</strong> až <strong>%{count} účet</strong> z <strong>%{filename}</strong>.
other: Chystáte se <strong>sledovat</strong> až <strong>%{count} účtů</strong> z <strong>%{filename}</strong>.
lists_html:
few: Chystáte se přidat <strong>%{count} účty</strong> z <strong>%{filename}</strong> do vaších <strong>seznamů</strong>. Nové seznamy budou vytvořeny, pokud neexistuje žádný seznam, do kterého by je bylo možné přidat.
many: Chystáte se přidat <strong>%{count} účtů</strong> z <strong>%{filename}</strong> do vaších <strong>seznamů</strong>. Nové seznamy budou vytvořeny, pokud neexistuje žádný seznam, do kterého by je bylo možné přidat.
one: Chystáte se přidat <strong>%{count} účet</strong> z <strong>%{filename}</strong> do vaších <strong>seznamů</strong>. Nové seznamy budou vytvořeny, pokud neexistuje žádný seznam, do kterého by jej bylo možné přidat.
other: Chystáte se přidat <strong>%{count} účtů</strong> z <strong>%{filename}</strong> do vaších <strong>seznamů</strong>. Nové seznamy budou vytvořeny, pokud neexistuje žádný seznam, do kterého by je bylo možné přidat.
muting_html:
few: Chystáte se <strong>ztišit</strong> až <strong>%{count} účty</strong> z <strong>%{filename}</strong>.
many: Chystáte se <strong>ztišit</strong> až <strong>%{count} účtů</strong> z <strong>%{filename}</strong>.
one: Chystáte se <strong>ztišit</strong> až <strong>%{count} účet</strong> z <strong>%{filename}</strong>.
other: Chystáte se <strong>ztišit</strong> až <strong>%{count} účtů</strong> z <strong>%{filename}</strong>.
preface: Můžete importovat data, která jste exportovali z jiného serveru, jako například seznam lidí, které sledujete či blokujete.
recent_imports: Nedávné importy
states:
@ -1417,6 +1588,7 @@ cs:
authentication_methods:
otp: aplikací pro dvoufaktorové ověření
password: heslem
sign_in_token: bezpečnostní kód e-mailu
webauthn: bezpečnostními klíči
description_html: Pokud vidíte aktivitu, kterou nepoznáváte, zvažte změnu hesla a zapnutí dvoufaktorového ověřování.
empty: Není k dispozici žádná historie přihlášení
@ -1427,10 +1599,21 @@ cs:
unsubscribe:
action: Ano, odeberte odběr
complete: Odběr byl odhlášen
confirmation_html: Jste si jisti, že chcete odhlásit odběr %{type} pro Mastodon na %{domain} na váš e-mail %{email}? Vždy se můžete znovu přihlásit ve svém nastavení <a href="%{settings_path}">e-mailových oznámení</a>.
emails:
notification_emails:
favourite: e-mailové oznámení při oblíbení
follow: e-mailové oznámení při sledování
follow_request: e-mail při žádost o sledování
mention: e-mailové oznámení při zmínění
reblog: e-mailové oznámení při boostu
resubscribe_html: Pokud jste se odhlásili omylem, můžete se znovu přihlásit ve svých nastavení <a href="%{settings_path}">e-mailových oznámení</a>.
success_html: Již nebudete dostávat %{type} pro Mastodon na %{domain} na vaši e-mailovou adresu %{email}.
title: Odhlásit odběr
media_attachments:
validations:
images_and_video: K příspěvku, který již obsahuje obrázky, nelze připojit video
not_found: Média %{ids} nebyla nalezena nebo již byla připojena k jinému příspěvku
not_ready: Nelze připojit soubory před jejich zpracováním. Zkuste to znovu za chvíli!
too_many: Nelze připojit více než 4 soubory
migrations:
@ -1507,6 +1690,8 @@ cs:
update:
subject: Uživatel %{name} upravil příspěvek
notifications:
administration_emails: E-mailová oznámení administrátora
email_events: Události pro e-mailová oznámení
email_events_hint: 'Vyberte události, pro které chcete dostávat oznámení:'
number:
human:
@ -1600,6 +1785,7 @@ cs:
scheduled_statuses:
over_daily_limit: Pro dnešek jste překročili limit %{limit} naplánovaných příspěvků
over_total_limit: Překročili jste limit %{limit} naplánovaných příspěvků
too_soon: datum musí být v budoucnu
self_destruct:
lead_html: "<strong>%{domain}</strong> bohužel končí nadobro. Pokud jste tam měli účet, nebudete jej moci dále používat, ale stále si můžete vyžádat zálohu vašich dat."
title: Tento server končí
@ -1659,10 +1845,12 @@ cs:
delete: Smazání účtu
development: Vývoj
edit_profile: Upravit profil
export: Export
featured_tags: Zvýrazněné hashtagy
import: Import
import_and_export: Import a export
migrate: Přesun účtu
notifications: Emailové oznámení
preferences: Předvolby
profile: Profil
relationships: Sledovaní a sledující
@ -1768,6 +1956,8 @@ cs:
too_late: Na odvolání proti tomuto prohřešku už je pozdě
tags:
does_not_match_previous_name: se neshoduje s předchozím názvem
terms_of_service:
title: Podmínky služby
themes:
contrast: Mastodon (vysoký kontrast)
default: Mastodon (tmavý)
@ -1828,6 +2018,15 @@ cs:
further_actions_html: Pokud jste to nebyli vy, doporučujeme pro zabezpečení vašeho účtu okamžitě %{action} a zapnout dvoufázové ověřování.
subject: Váš účet byl použit z nové IP adresy
title: Nové přihlášení
terms_of_service_changed:
agreement: Pokračováním v používání %{domain} souhlasíte s těmito podmínkami. Pokud nesouhlasíte s aktualizovanými podmínkami, můžete svůj souhlas s %{domain} kdykoliv ukončit odstraněním vašeho účtu.
changelog: 'Ve zkratce, zde je to, co tato změna znamená pro vás:'
description: 'Tento e-mail jste obdrželi, protože na %{domain} provádíme určité změny našich smluvních podmínek. Doporučujeme vám zkontrolovat aktualizované podmínky v plném znění zde:'
description_html: Tento e-mail jste obdrželi, protože na %{domain} provádíme určité změny našich smluvních podmínek. Doporučujeme Vám zkontrolovat <a href="%{path}" target="_blank">aktualizované termíny v plném znění zde</a>.
sign_off: Tým %{domain}
subject: Aktualizace našich podmínek služby
subtitle: Podmínky služby %{domain} se mění
title: Důležitá aktualizace
warning:
appeal: Podat odvolání
appeal_description: Pokud se domníváte, že se jedná o chybu, můžete podat odvolání personálu %{instance}.
@ -1908,6 +2107,7 @@ cs:
invalid_otp_token: Neplatný kód pro dvoufázové ověřování
otp_lost_help_html: Pokud jste ztratili přístup k oběma, spojte se s %{email}
rate_limited: Příliš mnoho pokusů o ověření, zkuste to znovu později.
seamless_external_login: Jste přihlášeni přes externí službu, nastavení hesla a e-mailu proto nejsou dostupná.
signed_in_as: 'Přihlášeni jako:'
verification:
extra_instructions_html: <strong>Tip:</strong> Odkaz na vaší webové stránce může být neviditelný. Důležitou součástí je <code>rel="me"</code>, která brání proti napodování vás na webových stránkách s obsahem vytvořeným uživatelem. Můžete dokonce použít <code>odkaz</code> v záhlaví stránky místo <code>a</code>, ale HTML musí být přístupné bez spuštění JavaScript.
@ -1916,6 +2116,7 @@ cs:
instructions_html: Zkopírujte a vložte níže uvedený kód do HTML vašeho webu. Poté přidejte adresu vašeho webu do jednoho z extra políček na vašem profilu na záložce "Upravit profil" a uložte změny.
verification: Ověření
verified_links: Vaše ověřené odkazy
website_verification: Ověření webové stránky
webauthn_credentials:
add: Přidat nový bezpečnostní klíč
create:

View file

@ -937,9 +937,13 @@ eo:
generates:
action: Generi
title: Agordo de kondiĉoj de uzado
history: Historio
live: Antaŭmontro
no_history: Ankoraŭ ne estas registritaj ŝanĝoj de la kondiĉoj de la servo.
no_terms_of_service_html: Vi nuntempe ne havas iujn ajn kondiĉojn de la servo agordita. La kondiĉoj de la servo celas doni klarecon kaj protekti vin kontraŭ eblaj respondecoj en disputoj kun viaj uzantoj.
notify_users: Informu uzantojn
preview:
send_preview: Sendu antaŭrigardon al %{email}
send_to_all:
one: Sendi %{display_count} retpoŝton
other: Sendi %{display_count} retpoŝtojn
@ -1926,6 +1930,8 @@ eo:
subject: Via konto estas alirita de nova IP-adreso
title: Nova saluto
terms_of_service_changed:
description: 'Vi ricevas ĉi tiun retmesaĝon ĉar ni faras iujn ŝanĝojn al niaj servokondiĉoj ĉe %{domain}. Ni instigas vin revizii la ĝisdatigitajn kondiĉojn tute ĉi tie:'
description_html: Vi ricevas ĉi tiun retmesaĝon ĉar ni faras iujn ŝanĝojn al niaj servokondiĉoj ĉe %{domain}. Ni instigas vin revizii la <a href="%{path}" target="_blank">ĝisdatigitajn kondiĉojn plene ĉi tie</a>.
sign_off: La teamo de %{domain}
subject: Ĝisdatigoj al niaj kondiĉoj de servo
subtitle: La kondiĉoj de la servo de %{domain} ŝanĝiĝas

View file

@ -956,6 +956,8 @@ pl:
updated_msg: Pomyślnie uaktualniono ustawienia hashtagów
terms_of_service:
draft: Szkic
generate: Użyj szablonu
save_draft: Zapisz wersję roboczą
title: Administracja
trends:
allow: Zezwól
@ -1214,6 +1216,7 @@ pl:
view_strikes: Zobacz dawne ostrzeżenia nałożone na twoje konto
too_fast: Zbyt szybko przesłano formularz, spróbuj ponownie.
use_security_key: Użyj klucza bezpieczeństwa
user_agreement_html: Przeczytałem i akceptuję <a href="%{terms_of_service_path}" target="_blank">warunki korzystania z usługi</a> oraz <a href="%{privacy_policy_path}" target="_blank">politykę prywatności</a>
author_attribution:
example_title: Przykładowy tekst
hint_html: Piszesz wiadomości albo bloga poza Mastodonem? Kontroluj jak będą ci przypisywane podczas dizielenia się nimi na Mastodonie.

View file

@ -352,7 +352,7 @@ pt-PT:
not_permitted: Não estás autorizado a executar esta ação
overwrite: Substituir
shortcode: Código de atalho
shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e traços inferiores
shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e traços inferiores (_)
title: Emojis personalizados
uncategorized: Não categorizados
unlist: Não listar
@ -1826,8 +1826,8 @@ pt-PT:
private_long: Mostrar só aos seguidores
public: Público
public_long: Todos podem ver
unlisted: Não inventariado
unlisted_long: Todos podem ver, mas não será inventariado nas cronologias públicas
unlisted: Não listado
unlisted_long: Todos podem ver, mas não aparecerá nas cronologias públicas
statuses_cleanup:
enabled: Eliminar publicações antigas automaticamente
enabled_hint: Elimina automaticamente as tuas publicações assim que atingirem um certo limite de tempo, a não ser que correspondam a uma das seguintes exceções
@ -1945,7 +1945,7 @@ pt-PT:
appeal: Submeter uma contestação
appeal_description: Se achas que isto é um erro, podes submeter uma contestação para a equipa de %{instance}.
categories:
spam: Spam
spam: Publicidade indesejada / spam
violation: O conteúdo infringe as seguintes diretrizes da comunidade
explanation:
delete_statuses: Algumas das tuas mensagens foram consideradas como violando uma ou mais diretrizes da comunidade e foram subsequentemente removidas pelos moderadores do %{instance}.

View file

@ -130,6 +130,9 @@ ca:
show_application: Sempre podràs veure quina aplicació ha publicat els teus tuts.
tag:
name: Només pots canviar la caixa de les lletres, per exemple, per fer-la més llegible
terms_of_service_generator:
domain: Identificació única del servei en línia que oferiu.
jurisdiction: Indiqueu el país on resideix qui paga les factures. Si és una empresa o una altra entitat, indiqueu el país en què està registrada, així com la ciutat, regió, territori o estat, segons calqui.
user:
chosen_languages: Quan estigui marcat, només es mostraran els tuts de les llengües seleccionades en les línies de temps públiques
role: El rol controla quins permisos té l'usuari.

View file

@ -3,12 +3,14 @@ cs:
simple_form:
hints:
account:
attribution_domains_as_text: Jeden na řádek. Chrání před falešným připisování autorství.
discoverable: Vaše veřejné příspěvky a profil mohou být zobrazeny nebo doporučeny v různých oblastech Mastodonu a váš profil může být navrhován ostatním uživatelům.
display_name: Vaše celé jméno nebo přezdívka.
fields: Vaše domovská stránka, zájmena, věk, cokoliv chcete.
indexable: Vaše veřejné příspěvky se mohou objevit ve výsledcích vyhledávání na Mastodonu. Lidé, kteří s vašimi příspěvky interagovaly, je mohou stále vyhledávat.
note: 'Můžete @zmínit jiné osoby nebo #hashtagy.'
show_collections: Lidé budou moci procházet skrz sledující. Lidé, které sledujete, uvidí, že je sledujete bezohledně.
unlocked: Lidé vás budou moci sledovat, aniž by vás žádali o schválení. Zrušte zaškrtnutí, pokud chcete kontrolovat požadavky o sledování a vybírat si, zda přijmete nebo odmítnete nové sledující.
account_alias:
acct: Zadejte svůj účet, ze kterého se chcete přesunout jinam, ve formátu přezdívka@doména
account_migration:
@ -58,6 +60,7 @@ cs:
setting_display_media_default: Skrývat média označená jako citlivá
setting_display_media_hide_all: Vždy skrývat média
setting_display_media_show_all: Vždy zobrazovat média
setting_system_scrollbars_ui: Platí pouze pro desktopové prohlížeče založené na Safari nebo Chrome
setting_use_blurhash: Gradienty jsou vytvořeny na základě barvev skrytých médií, ale zakrývají veškeré detaily
setting_use_pending_items: Aktualizovat časovou osu až po kliknutí namísto automatického rolování kanálu
username: Pouze písmena, číslice a podtržítka
@ -130,8 +133,17 @@ cs:
terms_of_service:
changelog: Může být strukturováno pomocí Markdown syntaxu.
text: Může být strukturováno pomocí Markdown syntaxu.
terms_of_service_generator:
admin_email: Právní oznámení zahrnují protioznámení, soudní příkazy, žádosti o stáhnutí příspěvků a žádosti od právních vymahačů.
arbitration_address: Může být stejné jako výše uvedená fyzická adresa, nebo „N/A“, pokud používáte e-mail
arbitration_website: Může být webový formulář nebo „N/A“, pokud používáte e-mail
dmca_address: V případě provozovatelů v USA použijte adresu zapsanou v DMCA Designated Agent Directory. Na přímou žádost je možné použít namísto domovské adresy PO box, použijte DMCA Designated Agent Post Office Box Waiver Request k e-mailovaní Copyright Office a řekněte jim, že jste malým moderátorem obsahu, který se obavá pomsty nabo odplaty za Vaši práci a potřebujete použít PO box, abyste schovali Vaši domovskou adresu před širokou veřejností.
dmca_email: Může být stejný e-mail použitý pro „E-mail adresy pro právní upozornění“
domain: Jedinečná identifikace online služby, kterou poskytujete.
jurisdiction: Uveďte zemi, kde žije ten, kdo platí účty. Pokud je to společnost nebo jiný subjekt, uveďte zemi, v níž je zapsán do obchodního rejstříku, a město, region, území, případně stát, pokud je to povinné.
user:
chosen_languages: Po zaškrtnutí budou ve veřejných časových osách zobrazeny pouze příspěvky ve zvolených jazycích
role: Role určuje, která oprávnění uživatel má.
user_role:
color: Barva, která má být použita pro roli v celém UI, jako RGB v hex formátu
highlighted: Toto roli učiní veřejně viditelnou
@ -144,6 +156,7 @@ cs:
url: Kam budou události odesílány
labels:
account:
attribution_domains_as_text: Webové stránky s povolením Vám připsat autorství
discoverable: Zobrazovat profil a příspěvky ve vyhledávacích algoritmech
fields:
name: Označení
@ -222,6 +235,7 @@ cs:
setting_hide_network: Skrýt mou síť
setting_reduce_motion: Omezit pohyb v animacích
setting_system_font_ui: Použít výchozí písmo systému
setting_system_scrollbars_ui: Použít výchozí posuvník systému
setting_theme: Vzhled stránky
setting_trends: Zobrazit dnešní trendy
setting_unfollow_modal: Před zrušením sledování zobrazovat potvrzovací okno

View file

@ -140,6 +140,7 @@ eo:
dmca_address: Por tenantoj en Usono, uzu la adreson registritan en la DMCA Designated Agenŭ Directory. Registrolibro de poŝtskatoloj haveblas per direkta postulo, uzu la DMCA Designated Agent Post Office Box Waiver Request por retpoŝti la Ofico de Kopirajto kaj priskribu, ke vi estas hejm-trovigita administranto por enhavo kaj devas uzi Poŝtskatolon por forigi vian hejmadreson de publika vido.
dmca_email: Povas esti la sama retpoŝtadreso uzita por "Retpoŝtadreso por legalaj sciigoj" supre
domain: Unika identigilo de la retaj servicoj, ke vi provizas.
jurisdiction: Enlistigu la landon, kie loĝas kiu pagas la fakturojn. Se ĝi estas kompanio aŭ alia ento, listigu la landon, kie ĝi estas enkorpigita, kaj la urbon, regionon, teritorion aŭ ŝtaton laŭeble.
user:
chosen_languages: Kun tio markita nur mesaĝoj en elektitaj lingvoj aperos en publikaj tempolinioj
role: La rolo kontrolas kiujn permesojn la uzanto havas.
@ -333,7 +334,13 @@ eo:
changelog: Kio ŝanĝiĝis?
text: Kondiĉoj de uzado
terms_of_service_generator:
admin_email: Retpoŝtadreso por laŭleĝaj avizoj
arbitration_address: Fizika adreso por arbitraciaj avizoj
arbitration_website: Retejo por sendi arbitraciajn avizojn
dmca_address: Fizika adreso por DMCA/kopirajto-avizoj
dmca_email: Retpoŝtadreso por DMCA/kopirajto-avizoj
domain: Domajno
jurisdiction: Laŭleĝa jurisdikcio
user:
role: Rolo
time_zone: Horzono

View file

@ -63,7 +63,7 @@ pt-PT:
setting_system_scrollbars_ui: Aplica-se apenas a navegadores de desktop baseados no Safari e Chrome
setting_use_blurhash: Os gradientes são baseados nas cores das imagens escondidas, mas ofuscam quaisquer pormenores
setting_use_pending_items: Ocultar as atualizações da cronologia após um clique em vez de percorrer automaticamente a cronologia
username: Pode utilizar letras, números e sublinhados
username: Pode utilizar letras, números e traços inferiores (_)
whole_word: Quando a palavra-chave ou expressão-chave é somente alfanumérica, ela só será aplicada se corresponder à palavra completa
domain_allow:
domain: Este domínio será capaz de obter dados desta instância e os dados dele recebidos serão processados e armazenados

View file

@ -2,6 +2,10 @@
shared:
self_destruct_value: <%= ENV.fetch('SELF_DESTRUCT', nil) %>
software_update_url: <%= ENV.fetch('UPDATE_CHECK_URL', 'https://api.joinmastodon.org/update-check') %>
source:
base_url: <%= ENV.fetch('SOURCE_BASE_URL', nil) %>
repository: <%= ENV.fetch('GITHUB_REPOSITORY', 'glitch-soc/mastodon') %>
tag: <%= ENV.fetch('SOURCE_TAG', nil) %>
version:
metadata: <%= ['glitch', ENV.fetch('MASTODON_VERSION_METADATA', nil)].compact_blank.join('.') %>
prerelease: <%= ENV.fetch('MASTODON_VERSION_PRERELEASE', nil) %>

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddStandardToPushSubscription < ActiveRecord::Migration[8.0]
def change
add_column :web_push_subscriptions, :standard, :boolean, null: false, default: false
end
end

View file

@ -10,9 +10,9 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2024_12_16_224825) do
ActiveRecord::Schema[8.0].define(version: 2025_01_08_111200) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "pg_catalog.plpgsql"
create_table "account_aliases", force: :cascade do |t|
t.bigint "account_id", null: false
@ -1205,6 +1205,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_12_16_224825) do
t.datetime "updated_at", precision: nil, null: false
t.bigint "access_token_id"
t.bigint "user_id"
t.boolean "standard", default: false, null: false
t.index ["access_token_id"], name: "index_web_push_subscriptions_on_access_token_id", where: "(access_token_id IS NOT NULL)"
t.index ["user_id"], name: "index_web_push_subscriptions_on_user_id"
end

View file

@ -50,16 +50,16 @@ module Mastodon
end
def repository
ENV.fetch('GITHUB_REPOSITORY', 'glitch-soc/mastodon')
source_configuration[:repository]
end
def source_base_url
ENV.fetch('SOURCE_BASE_URL', "https://github.com/#{repository}")
source_configuration[:base_url] || "https://github.com/#{repository}"
end
# specify git tag or commit hash here
def source_tag
ENV.fetch('SOURCE_TAG', nil)
source_configuration[:tag]
end
def source_url
@ -79,7 +79,15 @@ module Mastodon
end
def version_configuration
Rails.configuration.x.mastodon.version
mastodon_configuration.version
end
def source_configuration
mastodon_configuration.source
end
def mastodon_configuration
Rails.configuration.x.mastodon
end
end
end

View file

@ -80,18 +80,37 @@ RSpec.describe ThemeHelper do
end
describe '#custom_stylesheet' do
let(:custom_css) { 'body {}' }
let(:custom_digest) { Digest::SHA256.hexdigest(custom_css) }
before do
Setting.custom_css = custom_css
end
context 'when custom css setting value digest is present' do
before { Rails.cache.write(:setting_digest_custom_css, '1a2s3d4f1a2s3d4f') }
before { Rails.cache.write(:setting_digest_custom_css, custom_digest) }
it 'returns value from settings' do
expect(custom_stylesheet)
.to match('/css/custom-1a2s3d4f.css')
.to match("/css/custom-#{custom_digest[...8]}.css")
end
end
context 'when custom css setting value digest is not present' do
context 'when custom css setting value digest is expired' do
before { Rails.cache.delete(:setting_digest_custom_css) }
it 'returns value from settings' do
expect(custom_stylesheet)
.to match("/css/custom-#{custom_digest[...8]}.css")
end
end
context 'when custom css setting is not present' do
before do
Setting.custom_css = nil
Rails.cache.delete(:setting_digest_custom_css)
end
it 'returns default value' do
expect(custom_stylesheet)
.to be_blank

View file

@ -7,17 +7,50 @@ RSpec.describe ActivityPub::TagManager do
subject { described_class.instance }
let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" }
describe '#public_collection?' do
it 'returns true for the special public collection and common shorthands' do
expect(subject.public_collection?('https://www.w3.org/ns/activitystreams#Public')).to be true
expect(subject.public_collection?('as:Public')).to be true
expect(subject.public_collection?('Public')).to be true
end
it 'returns false for other URIs' do
expect(subject.public_collection?('https://example.com/foo/bar')).to be false
end
end
describe '#url_for' do
it 'returns a string' do
it 'returns a string starting with web domain' do
account = Fabricate(:account)
expect(subject.url_for(account)).to be_a String
expect(subject.url_for(account)).to be_a(String)
.and start_with(domain)
end
end
describe '#uri_for' do
it 'returns a string' do
it 'returns a string starting with web domain' do
account = Fabricate(:account)
expect(subject.uri_for(account)).to be_a String
expect(subject.uri_for(account)).to be_a(String)
.and start_with(domain)
end
end
describe '#activity_uri_for' do
context 'when given an account' do
it 'raises an exception' do
account = Fabricate(:account)
expect { subject.activity_uri_for(account) }.to raise_error(ArgumentError)
end
end
context 'when given a local activity' do
it 'returns a string starting with web domain' do
status = Fabricate(:status)
expect(subject.uri_for(status)).to be_a(String)
.and start_with(domain)
end
end
end

View file

@ -5,6 +5,29 @@ require 'rails_helper'
RSpec.describe Invite do
include_examples 'Expireable'
describe 'Associations' do
it { is_expected.to belong_to(:user).inverse_of(:invites) }
it { is_expected.to have_many(:users).inverse_of(:invite) }
end
describe 'Validations' do
it { is_expected.to validate_length_of(:comment).is_at_most(described_class::COMMENT_SIZE_LIMIT) }
end
describe 'Scopes' do
describe '.available' do
let!(:no_expires) { Fabricate :invite, expires_at: nil }
let!(:past_expires) { Fabricate :invite, expires_at: 2.days.ago }
let!(:future_expires) { Fabricate :invite, expires_at: 2.days.from_now }
it 'returns future and non-epiring records' do
expect(described_class.available)
.to include(no_expires, future_expires)
.and not_include(past_expires)
end
end
end
describe '#valid_for_use?' do
it 'returns true when there are no limitations' do
invite = Fabricate(:invite, max_uses: nil, expires_at: nil)
@ -37,4 +60,26 @@ RSpec.describe Invite do
expect(invite.valid_for_use?).to be false
end
end
describe 'Callbacks' do
describe 'Setting the invite code' do
context 'when creating a new record' do
subject { Fabricate.build :invite }
it 'sets a code value' do
expect { subject.save }
.to change(subject, :code).from(be_blank).to(be_present)
end
end
context 'when updating a record' do
subject { Fabricate :invite }
it 'does not change the code value' do
expect { subject.update(max_uses: 123_456) }
.to not_change(subject, :code)
end
end
end
end
end

View file

@ -12,6 +12,8 @@ RSpec.describe IpBlock do
it { is_expected.to validate_presence_of(:severity) }
it { is_expected.to validate_uniqueness_of(:ip) }
it { is_expected.to allow_values(:sign_up_requires_approval, :sign_up_block, :no_access).for(:severity) }
end
describe '#to_log_human_identifier' do

View file

@ -9,6 +9,8 @@ RSpec.describe Status do
let(:bob) { Fabricate(:account, username: 'bob') }
let(:other) { Fabricate(:status, account: bob, text: 'Skulls for the skull god! The enemy\'s gates are sideways!') }
include_examples 'Status::Visibility'
describe '#local?' do
it 'returns true when no remote URI is set' do
expect(subject.local?).to be true
@ -84,36 +86,6 @@ RSpec.describe Status do
end
end
describe '#hidden?' do
context 'when private_visibility?' do
it 'returns true' do
subject.visibility = :private
expect(subject.hidden?).to be true
end
end
context 'when direct_visibility?' do
it 'returns true' do
subject.visibility = :direct
expect(subject.hidden?).to be true
end
end
context 'when public_visibility?' do
it 'returns false' do
subject.visibility = :public
expect(subject.hidden?).to be false
end
end
context 'when unlisted_visibility?' do
it 'returns false' do
subject.visibility = :unlisted
expect(subject.hidden?).to be false
end
end
end
describe '#content' do
it 'returns the text of the status if it is not a reblog' do
expect(subject.content).to eql subject.text

View file

@ -24,6 +24,20 @@ RSpec.describe Trends::Links do
.to eq([lower_score.preview_card, higher_score.preview_card])
end
end
context 'when account has chosen languages' do
let!(:lang_match_higher_score) { Fabricate :preview_card_trend, score: 10, language: 'is' }
let!(:lang_match_lower_score) { Fabricate :preview_card_trend, score: 1, language: 'da' }
let(:user) { Fabricate :user, chosen_languages: %w(da is) }
let(:account) { Fabricate :account, user: user }
before { subject.filtered_for!(account) }
it 'returns results' do
expect(subject.records)
.to eq([lang_match_higher_score.preview_card, lang_match_lower_score.preview_card, higher_score.preview_card, lower_score.preview_card])
end
end
end
end
end

View file

@ -66,6 +66,20 @@ RSpec.describe Trends::Statuses do
.to eq([lower_score.status, higher_score.status])
end
end
context 'when account has chosen languages' do
let!(:lang_match_higher_score) { Fabricate :status_trend, score: 10, language: 'is' }
let!(:lang_match_lower_score) { Fabricate :status_trend, score: 1, language: 'da' }
let(:user) { Fabricate :user, chosen_languages: %w(da is) }
let(:account) { Fabricate :account, user: user }
before { subject.filtered_for!(account) }
it 'returns results' do
expect(subject.records)
.to eq([lang_match_higher_score.status, lang_match_lower_score.status, higher_score.status, lower_score.status])
end
end
end
end
end

View file

@ -50,6 +50,20 @@ RSpec.describe Trends::Tags do
.to eq([lower_score.tag, higher_score.tag])
end
end
context 'when account has chosen languages' do
let!(:lang_match_higher_score) { Fabricate :tag_trend, score: 10, language: 'is' }
let!(:lang_match_lower_score) { Fabricate :tag_trend, score: 1, language: 'da' }
let(:user) { Fabricate :user, chosen_languages: %w(da is) }
let(:account) { Fabricate :account, user: user }
before { subject.filtered_for!(account) }
it 'returns results' do
expect(subject.records)
.to eq([lang_match_higher_score.tag, lang_match_lower_score.tag, higher_score.tag, lower_score.tag])
end
end
end
end
end

View file

@ -68,6 +68,7 @@ RSpec.describe InstancePresenter do
context 'with the GITHUB_REPOSITORY env variable set' do
around do |example|
ClimateControl.modify GITHUB_REPOSITORY: 'other/repo' do
reload_configuration
example.run
end
end
@ -80,6 +81,7 @@ RSpec.describe InstancePresenter do
context 'without the GITHUB_REPOSITORY env variable set' do
around do |example|
ClimateControl.modify GITHUB_REPOSITORY: nil do
reload_configuration
example.run
end
end
@ -88,6 +90,10 @@ RSpec.describe InstancePresenter do
expect(instance_presenter.source_url).to eq('https://github.com/glitch-soc/mastodon')
end
end
def reload_configuration
Rails.configuration.x.mastodon = Rails.application.config_for(:mastodon)
end
end
describe '#thumbnail' do

View file

@ -217,6 +217,19 @@ RSpec.describe 'Domain Blocks' do
.to start_with('application/json')
end
end
context 'when severity is invalid' do
let(:params) { { domain: 'bar.com', severity: :bar } }
it 'returns http unprocessable entity' do
subject
expect(response).to have_http_status(422)
expect(response.content_type)
.to start_with('application/json')
expect(response.parsed_body[:error]).to eq('Validation failed: Severity is not included in the list')
end
end
end
describe 'PUT /api/v1/admin/domain_blocks/:id' do

View file

@ -187,6 +187,16 @@ RSpec.describe 'IP Blocks' do
.to start_with('application/json')
end
end
context 'when the given severity is invalid' do
let(:params) { { ip: '151.0.32.55', severity: 'invalid' } }
it 'returns http unprocessable entity' do
subject
expect(response).to have_http_status(422)
end
end
end
describe 'PUT /api/v1/admin/ip_blocks/:id' do

View file

@ -9,10 +9,21 @@ RSpec.describe 'Languages' do
end
it 'returns http success and includes supported languages' do
expect(response).to have_http_status(200)
expect(response)
.to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
expect(response.parsed_body.pluck(:code)).to match_array LanguagesHelper::SUPPORTED_LOCALES.keys.map(&:to_s)
expect(response.parsed_body)
.to match_array(supported_locale_expectations)
end
def supported_locale_expectations
LanguagesHelper::SUPPORTED_LOCALES.map do |key, values|
include(
code: key.to_s,
name: values.first
)
end
end
end
end

View file

@ -0,0 +1,184 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.shared_examples 'Status::Visibility' do
describe 'Validations' do
context 'when status is a reblog' do
subject { Fabricate.build :status, reblog: Fabricate(:status) }
it { is_expected.to allow_values('public', 'unlisted', 'private').for(:visibility) }
it { is_expected.to_not allow_values('direct', 'limited').for(:visibility) }
end
context 'when status is not reblog' do
subject { Fabricate.build :status, reblog_of_id: nil }
it { is_expected.to allow_values('public', 'unlisted', 'private', 'direct', 'limited').for(:visibility) }
end
end
describe 'Scopes' do
let!(:direct_status) { Fabricate :status, visibility: :direct }
let!(:limited_status) { Fabricate :status, visibility: :limited }
let!(:private_status) { Fabricate :status, visibility: :private }
let!(:public_status) { Fabricate :status, visibility: :public }
let!(:unlisted_status) { Fabricate :status, visibility: :unlisted }
describe '.list_eligible_visibility' do
it 'returns appropriate records' do
expect(Status.list_eligible_visibility)
.to include(
private_status,
public_status,
unlisted_status
)
.and not_include(direct_status)
.and not_include(limited_status)
end
end
describe '.distributable_visibility' do
it 'returns appropriate records' do
expect(Status.distributable_visibility)
.to include(
public_status,
unlisted_status
)
.and not_include(private_status)
.and not_include(direct_status)
.and not_include(limited_status)
end
end
describe '.not_direct_visibility' do
it 'returns appropriate records' do
expect(Status.not_direct_visibility)
.to include(
limited_status,
private_status,
public_status,
unlisted_status
)
.and not_include(direct_status)
end
end
end
describe 'Callbacks' do
describe 'Setting visibility in before validation' do
subject { Fabricate.build :status, visibility: nil }
context 'when explicit value is set' do
before { subject.visibility = :public }
it 'does not change' do
expect { subject.valid? }
.to_not change(subject, :visibility)
end
end
context 'when status is a reblog' do
before { subject.reblog = Fabricate(:status, visibility: :public) }
it 'changes to match the reblog' do
expect { subject.valid? }
.to change(subject, :visibility).to('public')
end
end
context 'when account is locked' do
before { subject.account = Fabricate.build(:account, locked: true) }
it 'changes to private' do
expect { subject.valid? }
.to change(subject, :visibility).to('private')
end
end
context 'when account is not locked' do
before { subject.account = Fabricate.build(:account, locked: false) }
it 'changes to public' do
expect { subject.valid? }
.to change(subject, :visibility).to('public')
end
end
end
end
describe '.selectable_visibilities' do
it 'returns options available for default privacy selection' do
expect(Status.selectable_visibilities)
.to match(%w(public unlisted private))
end
end
describe '#hidden?' do
subject { Status.new }
context 'when visibility is private' do
before { subject.visibility = :private }
it { is_expected.to be_hidden }
end
context 'when visibility is direct' do
before { subject.visibility = :direct }
it { is_expected.to be_hidden }
end
context 'when visibility is limited' do
before { subject.visibility = :limited }
it { is_expected.to be_hidden }
end
context 'when visibility is public' do
before { subject.visibility = :public }
it { is_expected.to_not be_hidden }
end
context 'when visibility is unlisted' do
before { subject.visibility = :unlisted }
it { is_expected.to_not be_hidden }
end
end
describe '#distributable?' do
subject { Status.new }
context 'when visibility is public' do
before { subject.visibility = :public }
it { is_expected.to be_distributable }
end
context 'when visibility is unlisted' do
before { subject.visibility = :unlisted }
it { is_expected.to be_distributable }
end
context 'when visibility is private' do
before { subject.visibility = :private }
it { is_expected.to_not be_distributable }
end
context 'when visibility is direct' do
before { subject.visibility = :direct }
it { is_expected.to_not be_distributable }
end
context 'when visibility is limited' do
before { subject.visibility = :limited }
it { is_expected.to_not be_distributable }
end
end
end

View file

@ -5,20 +5,49 @@ require 'rails_helper'
RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do
let(:current_user) { Fabricate(:admin_user) }
before do
sign_in current_user
end
before { sign_in current_user }
describe 'Performing batch updates' do
before do
visit admin_trends_links_preview_card_providers_path
end
context 'without selecting any records' do
it 'displays a notice about selection' do
visit admin_trends_links_preview_card_providers_path
click_on button_for_allow
expect(page).to have_content(selection_error_text)
expect(page)
.to have_content(selection_error_text)
end
end
context 'with providers that are not trendable' do
let!(:provider) { Fabricate :preview_card_provider, trendable: false }
it 'allows the providers' do
visit admin_trends_links_preview_card_providers_path
check_item
expect { click_on button_for_allow }
.to change { provider.reload.trendable? }.from(false).to(true)
end
end
context 'with providers that are trendable' do
let!(:provider) { Fabricate :preview_card_provider, trendable: true }
it 'disallows the providers' do
visit admin_trends_links_preview_card_providers_path
check_item
expect { click_on button_for_disallow }
.to change { provider.reload.trendable? }.from(true).to(false)
end
end
def check_item
within '.batch-table__row' do
find('input[type=checkbox]').check
end
end
@ -26,6 +55,10 @@ RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do
I18n.t('admin.trends.allow')
end
def button_for_disallow
I18n.t('admin.trends.disallow')
end
def selection_error_text
I18n.t('admin.trends.links.publishers.no_publisher_selected')
end

View file

@ -5,20 +5,77 @@ require 'rails_helper'
RSpec.describe 'Admin::Trends::Links' do
let(:current_user) { Fabricate(:admin_user) }
before do
sign_in current_user
end
before { sign_in current_user }
describe 'Performing batch updates' do
before do
visit admin_trends_links_path
end
context 'without selecting any records' do
it 'displays a notice about selection' do
visit admin_trends_links_path
click_on button_for_allow
expect(page).to have_content(selection_error_text)
expect(page)
.to have_content(selection_error_text)
end
end
context 'with links that are not trendable' do
let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, trendable: false) }
it 'allows the links' do
visit admin_trends_links_path
check_item
expect { click_on button_for_allow }
.to change { preview_card_trend.preview_card.reload.trendable? }.from(false).to(true)
end
end
context 'with links whose providers are not trendable' do
let(:preview_card_provider) { Fabricate :preview_card_provider, trendable: false }
let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, url: "https://#{preview_card_provider.domain}/page") }
it 'allows the providers of the links' do
visit admin_trends_links_path
check_item
expect { click_on button_for_allow_providers }
.to change { preview_card_trend.preview_card.provider.reload.trendable? }.from(false).to(true)
end
end
context 'with links that are trendable' do
let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, trendable: true) }
it 'disallows the links' do
visit admin_trends_links_path
check_item
expect { click_on button_for_disallow }
.to change { preview_card_trend.preview_card.reload.trendable? }.from(true).to(false)
end
end
context 'with links whose providers are trendable' do
let(:preview_card_provider) { Fabricate :preview_card_provider, trendable: true }
let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, url: "https://#{preview_card_provider.domain}/page") }
it 'disallows the links' do
visit admin_trends_links_path
check_item
expect { click_on button_for_disallow_providers }
.to change { preview_card_trend.preview_card.provider.reload.trendable? }.from(true).to(false)
end
end
def check_item
within '.batch-table__row' do
find('input[type=checkbox]').check
end
end
@ -26,6 +83,18 @@ RSpec.describe 'Admin::Trends::Links' do
I18n.t('admin.trends.links.allow')
end
def button_for_allow_providers
I18n.t('admin.trends.links.allow_provider')
end
def button_for_disallow
I18n.t('admin.trends.links.disallow')
end
def button_for_disallow_providers
I18n.t('admin.trends.links.disallow_provider')
end
def selection_error_text
I18n.t('admin.trends.links.no_link_selected')
end

View file

@ -5,20 +5,75 @@ require 'rails_helper'
RSpec.describe 'Admin::Trends::Statuses' do
let(:current_user) { Fabricate(:admin_user) }
before do
sign_in current_user
end
before { sign_in current_user }
describe 'Performing batch updates' do
before do
visit admin_trends_statuses_path
end
context 'without selecting any records' do
it 'displays a notice about selection' do
visit admin_trends_statuses_path
click_on button_for_allow
expect(page).to have_content(selection_error_text)
expect(page)
.to have_content(selection_error_text)
end
end
context 'with statuses that are not trendable' do
let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, trendable: false) }
it 'allows the statuses' do
visit admin_trends_statuses_path
check_item
expect { click_on button_for_allow }
.to change { status_trend.status.reload.trendable? }.from(false).to(true)
end
end
context 'with statuses whose accounts are not trendable' do
let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, account: Fabricate(:account, trendable: false)) }
it 'allows the accounts of the statuses' do
visit admin_trends_statuses_path
check_item
expect { click_on button_for_allow_accounts }
.to change { status_trend.status.account.reload.trendable? }.from(false).to(true)
end
end
context 'with statuses that are trendable' do
let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, trendable: true) }
it 'disallows the statuses' do
visit admin_trends_statuses_path
check_item
expect { click_on button_for_disallow }
.to change { status_trend.status.reload.trendable? }.from(true).to(false)
end
end
context 'with statuses whose accounts are trendable' do
let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, account: Fabricate(:account, trendable: true)) }
it 'disallows the statuses' do
visit admin_trends_statuses_path
check_item
expect { click_on button_for_disallow_accounts }
.to change { status_trend.status.reload.trendable? }.from(true).to(false)
end
end
def check_item
within '.batch-table__row' do
find('input[type=checkbox]').check
end
end
@ -26,6 +81,18 @@ RSpec.describe 'Admin::Trends::Statuses' do
I18n.t('admin.trends.statuses.allow')
end
def button_for_allow_accounts
I18n.t('admin.trends.statuses.allow_account')
end
def button_for_disallow
I18n.t('admin.trends.statuses.disallow')
end
def button_for_disallow_accounts
I18n.t('admin.trends.statuses.disallow_account')
end
def selection_error_text
I18n.t('admin.trends.statuses.no_status_selected')
end

View file

@ -5,27 +5,59 @@ require 'rails_helper'
RSpec.describe 'Admin::Trends::Tags' do
let(:current_user) { Fabricate(:admin_user) }
before do
sign_in current_user
end
before { sign_in current_user }
describe 'Performing batch updates' do
before do
visit admin_trends_tags_path
end
context 'without selecting any records' do
it 'displays a notice about selection' do
visit admin_trends_tags_path
click_on button_for_allow
expect(page).to have_content(selection_error_text)
end
end
context 'with tags that are not trendable' do
let!(:tag_trend) { Fabricate :tag_trend, tag: Fabricate(:tag, trendable: false) }
it 'allows the tags' do
visit admin_trends_tags_path
check_item
expect { click_on button_for_allow }
.to change { tag_trend.tag.reload.trendable? }.from(false).to(true)
end
end
context 'with tags that are trendable' do
let!(:tag_trend) { Fabricate :tag_trend, tag: Fabricate(:tag, trendable: true) }
it 'disallows the tags' do
visit admin_trends_tags_path
check_item
expect { click_on button_for_disallow }
.to change { tag_trend.tag.reload.trendable? }.from(true).to(false)
end
end
def check_item
within '.batch-table__row' do
find('input[type=checkbox]').check
end
end
def button_for_allow
I18n.t('admin.trends.allow')
end
def button_for_disallow
I18n.t('admin.trends.disallow')
end
def selection_error_text
I18n.t('admin.trends.tags.no_tag_selected')
end

View file

@ -5,21 +5,36 @@ require 'rails_helper'
RSpec.describe Web::PushNotificationWorker do
subject { described_class.new }
let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' }
let(:auth) { 'Q2BoAjC09xH3ywDLNJr-dA==' }
let(:endpoint) { 'https://updates.push.services.mozilla.com/push/v1/subscription-id' }
let(:user) { Fabricate(:user) }
let(:notification) { Fabricate(:notification) }
let(:subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: p256dh, key_auth: auth, endpoint: endpoint, data: { alerts: { notification.type => true } }) }
let(:vapid_public_key) { 'BB37UCyc8LLX4PNQSe-04vSFvpUWGrENubUaslVFM_l5TxcGVMY0C3RXPeUJAQHKYlcOM2P4vTYmkoo0VZGZTM4=' }
let(:vapid_private_key) { 'OPrw1Sum3gRoL4-DXfSCC266r-qfFSRZrnj8MgIhRHg=' }
let(:vapid_key) { Webpush::VapidKey.from_keys(vapid_public_key, vapid_private_key) }
let(:contact_email) { 'sender@example.com' }
let(:ciphertext) { "+\xB8\xDBT}\x13\xB6\xDD.\xF9\xB0\xA7\xC8\xD2\x80\xFD\x99#\xF7\xAC\x83\xA4\xDB,\x1F\xB5\xB9w\x85>\xF7\xADr" }
let(:salt) { "X\x97\x953\xE4X\xF8_w\xE7T\x95\xC51q\xFE" }
let(:server_public_key) { "\x04\b-RK9w\xDD$\x16lFz\xF9=\xB4~\xC6\x12k\xF3\xF40t\xA9\xC1\fR\xC3\x81\x80\xAC\f\x7F\xE4\xCC\x8E\xC2\x88 n\x8BB\xF1\x9C\x14\a\xFA\x8D\xC9\x80\xA1\xDDyU\\&c\x01\x88#\x118Ua" }
let(:shared_secret) { "\t\xA7&\x85\t\xC5m\b\xA8\xA7\xF8B{1\xADk\xE1y'm\xEDE\xEC\xDD\xEDj\xB3$s\xA9\xDA\xF0" }
let(:payload) { { ciphertext: ciphertext, salt: salt, server_public_key: server_public_key, shared_secret: shared_secret } }
# Legacy values
let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' }
let(:auth) { 'Q2BoAjC09xH3ywDLNJr-dA==' }
let(:legacy_subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: p256dh, key_auth: auth, endpoint: endpoint, data: { alerts: { notification.type => true } }) }
let(:legacy_payload) do
{
ciphertext: "+\xB8\xDBT}\x13\xB6\xDD.\xF9\xB0\xA7\xC8\xD2\x80\xFD\x99#\xF7\xAC\x83\xA4\xDB,\x1F\xB5\xB9w\x85>\xF7\xADr",
salt: "X\x97\x953\xE4X\xF8_w\xE7T\x95\xC51q\xFE",
server_public_key: "\x04\b-RK9w\xDD$\x16lFz\xF9=\xB4~\xC6\x12k\xF3\xF40t\xA9\xC1\fR\xC3\x81\x80\xAC\f\x7F\xE4\xCC\x8E\xC2\x88 n\x8BB\xF1\x9C\x14\a\xFA\x8D\xC9\x80\xA1\xDDyU\\&c\x01\x88#\x118Ua",
shared_secret: "\t\xA7&\x85\t\xC5m\b\xA8\xA7\xF8B{1\xADk\xE1y'm\xEDE\xEC\xDD\xEDj\xB3$s\xA9\xDA\xF0",
}
end
# Standard values, from RFC8291
let(:std_p256dh) { 'BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4' }
let(:std_auth) { 'BTBZMqHH6r4Tts7J_aSIgg' }
let(:std_as_public) { 'BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8' }
let(:std_as_private) { 'yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw' }
let(:std_salt) { 'DGv6ra1nlYgDCS1FRnbzlw' }
let(:std_subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: std_p256dh, key_auth: std_auth, endpoint: endpoint, standard: true, data: { alerts: { notification.type => true } }) }
let(:std_input) { 'When I grow up, I want to be a watermelon' }
let(:std_ciphertext) { 'DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPTpK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN' }
describe 'perform' do
around do |example|
@ -35,20 +50,40 @@ RSpec.describe Web::PushNotificationWorker do
before do
Setting.site_contact_email = contact_email
allow(Webpush::Encryption).to receive(:encrypt).and_return(payload)
allow(JWT).to receive(:encode).and_return('jwt.encoded.payload')
stub_request(:post, endpoint).to_return(status: 201, body: '')
end
it 'calls the relevant service with the correct headers' do
subject.perform(subscription.id, notification.id)
it 'Legacy push calls the relevant service with the legacy headers' do
allow(Webpush::Legacy::Encryption).to receive(:encrypt).and_return(legacy_payload)
expect(web_push_endpoint_request)
subject.perform(legacy_subscription.id, notification.id)
expect(legacy_web_push_endpoint_request)
.to have_been_made
end
def web_push_endpoint_request
# We allow subject stub to encrypt the same input than the RFC8291 example
# rubocop:disable RSpec/SubjectStub
it 'Standard push calls the relevant service with the standard headers' do
# Mock server keys to match RFC example
allow(OpenSSL::PKey::EC).to receive(:generate).and_return(std_as_keys)
# Mock the random salt to match RFC example
rand = Random.new
allow(Random).to receive(:new).and_return(rand)
allow(rand).to receive(:bytes).and_return(Webpush.decode64(std_salt))
# Mock input to match RFC example
allow(subject).to receive(:push_notification_json).and_return(std_input)
subject.perform(std_subscription.id, notification.id)
expect(standard_web_push_endpoint_request)
.to have_been_made
end
# rubocop:enable RSpec/SubjectStub
def legacy_web_push_endpoint_request
a_request(
:post,
endpoint
@ -66,5 +101,28 @@ RSpec.describe Web::PushNotificationWorker do
body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr"
)
end
def standard_web_push_endpoint_request
a_request(
:post,
endpoint
).with(
headers: {
'Content-Encoding' => 'aes128gcm',
'Content-Type' => 'application/octet-stream',
'Ttl' => '172800',
'Urgency' => 'normal',
'Authorization' => "vapid t=jwt.encoded.payload,k=#{vapid_public_key.delete('=')}",
'Unsubscribe-URL' => %r{/api/web/push_subscriptions/},
},
body: Webpush.decode64(std_ciphertext)
)
end
def std_as_keys
# VapidKey contains a method to retrieve EC keypair from
# B64 raw keys, the keypair is stored in curve field
Webpush::VapidKey.from_keys(std_as_public, std_as_private).curve
end
end
end

View file

@ -238,10 +238,10 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.25.0, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.8.0":
version: 7.25.9
resolution: "@babel/helper-plugin-utils@npm:7.25.9"
checksum: 10c0/483066a1ba36ff16c0116cd24f93de05de746a603a777cd695ac7a1b034928a65a4ecb35f255761ca56626435d7abdb73219eba196f9aa83b6c3c3169325599d
"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.25.0, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.26.5, @babel/helper-plugin-utils@npm:^7.8.0":
version: 7.26.5
resolution: "@babel/helper-plugin-utils@npm:7.26.5"
checksum: 10c0/cdaba71d4b891aa6a8dfbe5bac2f94effb13e5fa4c2c487667fdbaa04eae059b78b28d85a885071f45f7205aeb56d16759e1bed9c118b94b16e4720ef1ab0f65
languageName: node
linkType: hard
@ -935,13 +935,13 @@ __metadata:
linkType: hard
"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.25.9"
version: 7.26.6
resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.26.6"
dependencies:
"@babel/helper-plugin-utils": "npm:^7.25.9"
"@babel/helper-plugin-utils": "npm:^7.26.5"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/eb623db5be078a1c974afe7c7797b0309ba2ea9e9237c0b6831ade0f56d8248bb4ab3432ab34495ff8c877ec2fe412ff779d1e9b3c2b8139da18e1753d950bc3
checksum: 10c0/574d6db7cbc5c092db5d1dece8ce26195e642b9c40dbfeaf3082058a78ad7959c1c333471cdd45f38b784ec488850548075d527b178c5010ee9bff7aa527cc7a
languageName: node
linkType: hard
@ -12356,12 +12356,12 @@ __metadata:
languageName: node
linkType: hard
"nanoid@npm:^3.3.7":
version: 3.3.7
resolution: "nanoid@npm:3.3.7"
"nanoid@npm:^3.3.8":
version: 3.3.8
resolution: "nanoid@npm:3.3.8"
bin:
nanoid: bin/nanoid.cjs
checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3
checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120
languageName: node
linkType: hard
@ -13330,14 +13330,14 @@ __metadata:
linkType: hard
"pino-http@npm:^10.0.0":
version: 10.3.0
resolution: "pino-http@npm:10.3.0"
version: 10.4.0
resolution: "pino-http@npm:10.4.0"
dependencies:
get-caller-file: "npm:^2.0.5"
pino: "npm:^9.0.0"
pino-std-serializers: "npm:^7.0.0"
process-warning: "npm:^4.0.0"
checksum: 10c0/da95d93e1176c02201f9b9bb0af53ad737105c5772acbb077dcad0f52ebce2438e0e9fc8216cd96396d1305d0ecf1f1d23142c7a50110a701ea093b2ee999ea7
checksum: 10c0/64144e2c94e939070f56ad82dfb012b6a98d21582e0660cf821e7cee64d4e06f7724aa40bc5bf9cd1254d58ab7cbd972dec287b7989eba647d384f6edd8d95fd
languageName: node
linkType: hard
@ -14233,13 +14233,13 @@ __metadata:
linkType: hard
"postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.49":
version: 8.4.49
resolution: "postcss@npm:8.4.49"
version: 8.5.1
resolution: "postcss@npm:8.5.1"
dependencies:
nanoid: "npm:^3.3.7"
nanoid: "npm:^3.3.8"
picocolors: "npm:^1.1.1"
source-map-js: "npm:^1.2.1"
checksum: 10c0/f1b3f17aaf36d136f59ec373459f18129908235e65dbdc3aee5eef8eba0756106f52de5ec4682e29a2eab53eb25170e7e871b3e4b52a8f1de3d344a514306be3
checksum: 10c0/c4d90c59c98e8a0c102b77d3f4cac190f883b42d63dc60e2f3ed840f16197c0c8e25a4327d2e9a847b45a985612317dc0534178feeebd0a1cf3eb0eecf75cae4
languageName: node
linkType: hard
@ -15647,8 +15647,8 @@ __metadata:
linkType: hard
"sass@npm:^1.62.1":
version: 1.83.1
resolution: "sass@npm:1.83.1"
version: 1.83.4
resolution: "sass@npm:1.83.4"
dependencies:
"@parcel/watcher": "npm:^2.4.1"
chokidar: "npm:^4.0.0"
@ -15659,7 +15659,7 @@ __metadata:
optional: true
bin:
sass: sass.js
checksum: 10c0/9772506cd8290df7b5e800055098e91a8a65100840fd9e90c660deb74b248b3ddbbd1a274b8f7f09777d472d2c873575357bd87939a40fb5a80bdf654985486f
checksum: 10c0/6f27f0eebfeb50222b14baaeef548ef58a05daf8abd9797e6c499334ed7ad40541767056c8693780d06ca83d8836348ea7396a923d3be439b133507993ca78be
languageName: node
linkType: hard