diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 030f31110..bc66bc4ad 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -45,48 +45,6 @@ Metrics/PerceivedComplexity:
RSpec/ExampleLength:
Max: 22
-RSpec/LetSetup:
- Exclude:
- - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb'
- - 'spec/controllers/api/v1/filters_controller_spec.rb'
- - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
- - 'spec/controllers/api/v2/filters/keywords_controller_spec.rb'
- - 'spec/controllers/api/v2/filters/statuses_controller_spec.rb'
- - 'spec/controllers/auth/confirmations_controller_spec.rb'
- - 'spec/controllers/auth/passwords_controller_spec.rb'
- - 'spec/controllers/auth/sessions_controller_spec.rb'
- - 'spec/controllers/follower_accounts_controller_spec.rb'
- - 'spec/controllers/following_accounts_controller_spec.rb'
- - 'spec/controllers/oauth/authorized_applications_controller_spec.rb'
- - 'spec/controllers/oauth/tokens_controller_spec.rb'
- - 'spec/controllers/settings/imports_controller_spec.rb'
- - 'spec/lib/activitypub/activity/delete_spec.rb'
- - 'spec/lib/vacuum/applications_vacuum_spec.rb'
- - 'spec/lib/vacuum/preview_cards_vacuum_spec.rb'
- - 'spec/models/account_spec.rb'
- - 'spec/models/account_statuses_cleanup_policy_spec.rb'
- - 'spec/models/canonical_email_block_spec.rb'
- - 'spec/models/status_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/services/account_statuses_cleanup_service_spec.rb'
- - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb'
- - 'spec/services/activitypub/fetch_remote_status_service_spec.rb'
- - 'spec/services/activitypub/process_account_service_spec.rb'
- - 'spec/services/activitypub/process_collection_service_spec.rb'
- - 'spec/services/batched_remove_status_service_spec.rb'
- - 'spec/services/block_domain_service_spec.rb'
- - 'spec/services/bulk_import_service_spec.rb'
- - 'spec/services/delete_account_service_spec.rb'
- - 'spec/services/import_service_spec.rb'
- - 'spec/services/notify_service_spec.rb'
- - 'spec/services/remove_status_service_spec.rb'
- - 'spec/services/report_service_spec.rb'
- - 'spec/services/resolve_account_service_spec.rb'
- - 'spec/services/suspend_account_service_spec.rb'
- - 'spec/services/unallow_domain_service_spec.rb'
- - 'spec/services/unsuspend_account_service_spec.rb'
- - 'spec/workers/scheduler/user_cleanup_scheduler_spec.rb'
-
RSpec/MultipleExpectations:
Max: 8
diff --git a/Gemfile.lock b/Gemfile.lock
index 15bdb9b4f..9b78ca733 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -220,7 +220,7 @@ GEM
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
date (3.3.4)
- debug (1.9.0)
+ debug (1.9.1)
irb (~> 1.10)
reline (>= 0.3.8)
debug_inspector (1.1.0)
@@ -376,8 +376,8 @@ GEM
rainbow (>= 2.2.2, < 4.0)
terminal-table (>= 1.5.1)
idn-ruby (0.1.5)
- io-console (0.6.0)
- irb (1.10.1)
+ io-console (0.7.1)
+ irb (1.11.0)
rdoc
reline (>= 0.3.8)
jmespath (1.6.2)
@@ -540,7 +540,7 @@ GEM
activesupport (>= 7.0.0)
rack
railties (>= 7.0.0)
- psych (5.1.1.1)
+ psych (5.1.2)
stringio
public_suffix (5.0.4)
puma (6.4.0)
@@ -614,7 +614,7 @@ GEM
link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.6.1)
rdf (~> 3.2)
- rdoc (6.6.1)
+ rdoc (6.6.2)
psych (>= 4.0.0)
redcarpet (3.6.0)
redis (4.8.1)
diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx
index 29b46cb43..97f68c221 100644
--- a/app/javascript/mastodon/features/account/components/header.jsx
+++ b/app/javascript/mastodon/features/account/components/header.jsx
@@ -35,6 +35,8 @@ import FollowRequestNoteContainer from '../containers/follow_request_note_contai
const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
follow: { id: 'account.follow', defaultMessage: 'Follow' },
+ followBack: { id: 'account.follow_back', defaultMessage: 'Follow back' },
+ mutual: { id: 'account.mutual', defaultMessage: 'Mutual' },
cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Withdraw follow request' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
@@ -82,6 +84,20 @@ const titleFromAccount = account => {
return `${prefix} (@${acct})`;
};
+const messageForFollowButton = relationship => {
+ if(!relationship) return messages.follow;
+
+ if (relationship.get('following') && relationship.get('followed_by')) {
+ return messages.mutual;
+ } else if (!relationship.get('following') && relationship.get('followed_by')) {
+ return messages.followBack;
+ } else if (relationship.get('following')) {
+ return messages.unfollow;
+ } else {
+ return messages.follow;
+ }
+};
+
const dateFormatOptions = {
month: 'short',
day: 'numeric',
@@ -253,9 +269,7 @@ class Header extends ImmutablePureComponent {
let info = [];
let menu = [];
- if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
- info.push();
- } else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
+ if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
info.push();
}
@@ -281,7 +295,7 @@ class Header extends ImmutablePureComponent {
} else if (account.getIn(['relationship', 'requested'])) {
actionBtn = ;
} else if (!account.getIn(['relationship', 'blocking'])) {
- actionBtn = ;
+ actionBtn = ;
} else if (account.getIn(['relationship', 'blocking'])) {
actionBtn = ;
}
diff --git a/app/javascript/mastodon/features/onboarding/profile.jsx b/app/javascript/mastodon/features/onboarding/profile.jsx
index 09e6b2c6c..daaef6065 100644
--- a/app/javascript/mastodon/features/onboarding/profile.jsx
+++ b/app/javascript/mastodon/features/onboarding/profile.jsx
@@ -26,6 +26,8 @@ const messages = defineMessages({
uploadAvatar: { id: 'onboarding.profile.upload_avatar', defaultMessage: 'Upload profile picture' },
});
+const nullIfMissing = path => path.endsWith('missing.png') ? null : path;
+
export const Profile = () => {
const account = useAppSelector(state => state.getIn(['accounts', me]));
const [displayName, setDisplayName] = useState(account.get('display_name'));
@@ -61,8 +63,8 @@ export const Profile = () => {
setHeader(e.target?.files?.[0]);
}, [setHeader]);
- const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : account.get('avatar'), [avatar, account]);
- const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : account.get('header'), [header, account]);
+ const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : nullIfMissing(account.get('avatar')), [avatar, account]);
+ const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : nullIfMissing(account.get('header')), [header, account]);
const handleSubmit = useCallback(() => {
setIsSaving(true);
diff --git a/app/javascript/mastodon/locales/af.json b/app/javascript/mastodon/locales/af.json
index d3cc40c60..6c37cdf5c 100644
--- a/app/javascript/mastodon/locales/af.json
+++ b/app/javascript/mastodon/locales/af.json
@@ -30,7 +30,6 @@
"account.followers.empty": "Hierdie gebruiker het nog nie volgers nie.",
"account.following": "Volg",
"account.follows.empty": "Die gebruiker volg nog niemand.",
- "account.follows_you": "Volg jou",
"account.go_to_profile": "Gaan na profiel",
"account.hide_reblogs": "Versteek plasings wat deur @{name} aangestuur is",
"account.joined_short": "Aangesluit",
diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json
index b2134551b..23899b100 100644
--- a/app/javascript/mastodon/locales/an.json
+++ b/app/javascript/mastodon/locales/an.json
@@ -35,7 +35,6 @@
"account.following": "Seguindo",
"account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Seguindo}}",
"account.follows.empty": "Este usuario encara no sigue a dengún.",
- "account.follows_you": "Te sigue",
"account.go_to_profile": "Ir ta lo perfil",
"account.hide_reblogs": "Amagar retutz de @{name}",
"account.joined_short": "S'unió",
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 979bc9a70..c0d07cc64 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -37,7 +37,6 @@
"account.following": "الاشتراكات",
"account.following_counter": "{count, plural, zero{لا يُتابِع أحدًا} one {يُتابِعُ واحد} two{يُتابِعُ اِثنان} few{يُتابِعُ {counter}} many{يُتابِعُ {counter}} other {يُتابِعُ {counter}}}",
"account.follows.empty": "لا يُتابع هذا المُستخدمُ أيَّ أحدٍ حتى الآن.",
- "account.follows_you": "يُتابِعُك",
"account.go_to_profile": "اذهب إلى الملف الشخصي",
"account.hide_reblogs": "إخفاء المعاد نشرها مِن @{name}",
"account.in_memoriam": "في الذكرى.",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index 98f622c29..c4238edcd 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -38,7 +38,6 @@
"account.following": "Siguiendo",
"account.following_counter": "{count, plural,one {Sigue a {counter}} other {Sigue a {counter}}}",
"account.follows.empty": "Esti perfil nun sigue a naide.",
- "account.follows_you": "Síguete",
"account.go_to_profile": "Dir al perfil",
"account.hide_reblogs": "Anubrir los artículos compartíos de @{name}",
"account.in_memoriam": "N'alcordanza.",
diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json
index f94a8b79f..773af4034 100644
--- a/app/javascript/mastodon/locales/be.json
+++ b/app/javascript/mastodon/locales/be.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Няма допісаў",
"account.featured_tags.title": "Тэгі, выбраныя {name}",
"account.follow": "Падпісацца",
+ "account.follow_back": "Падпісацца ў адказ",
"account.followers": "Падпісчыкі",
"account.followers.empty": "Ніхто пакуль не падпісаны на гэтага карыстальніка.",
"account.followers_counter": "{count, plural, one {{counter} падпісчык} few {{counter} падпісчыкі} many {{counter} падпісчыкаў} other {{counter} падпісчыка}}",
"account.following": "Падпіскі",
"account.following_counter": "{count, plural, one {{counter} падпіска} few {{counter} падпіскі} many {{counter} падпісак} other {{counter} падпіскі}}",
"account.follows.empty": "Карыстальнік ні на каго не падпісаны.",
- "account.follows_you": "Падпісаны на вас",
"account.go_to_profile": "Перайсці да профілю",
"account.hide_reblogs": "Схаваць пашырэнні ад @{name}",
"account.in_memoriam": "У памяць.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Не апавяшчаць",
"account.mute_short": "Ігнараваць",
"account.muted": "Ігнаруецца",
+ "account.mutual": "Узаемныя",
"account.no_bio": "Апісанне адсутнічае.",
"account.open_original_page": "Адкрыць арыгінальную старонку",
"account.posts": "Допісы",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index f57d868c7..8a6d6040d 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -38,7 +38,6 @@
"account.following": "Последвано",
"account.following_counter": "{count, plural, one {{counter} последван} other {{counter} последвани}}",
"account.follows.empty": "Потребителят още никого не следва.",
- "account.follows_you": "Следва ви",
"account.go_to_profile": "Към профила",
"account.hide_reblogs": "Скриване на подсилвания от @{name}",
"account.in_memoriam": "В памет на.",
diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json
index b6e4fbb96..fe3d2a627 100644
--- a/app/javascript/mastodon/locales/bn.json
+++ b/app/javascript/mastodon/locales/bn.json
@@ -37,7 +37,6 @@
"account.following": "অনুসরণ করা হচ্ছে",
"account.following_counter": "{count, plural,one {{counter} জনকে অনুসরণ} other {{counter} জনকে অনুসরণ}}",
"account.follows.empty": "এই সদস্য কাউকে এখনো ফলো করেন না.",
- "account.follows_you": "আপনাকে ফলো করে",
"account.go_to_profile": "প্রোফাইলে যান",
"account.hide_reblogs": "@{name}'র সমর্থনগুলি লুকিয়ে ফেলুন",
"account.in_memoriam": "স্মৃতিতে.",
diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json
index 39cd73241..bea8b27b7 100644
--- a/app/javascript/mastodon/locales/br.json
+++ b/app/javascript/mastodon/locales/br.json
@@ -36,7 +36,6 @@
"account.following": "Koumanantoù",
"account.following_counter": "{count, plural, one{{counter} C'houmanant} two{{counter} Goumanant} other {{counter} a Goumanant}}",
"account.follows.empty": "An implijer·ez-mañ na heul den ebet.",
- "account.follows_you": "Ho heuilh",
"account.go_to_profile": "Gwelet ar profil",
"account.hide_reblogs": "Kuzh skignadennoù gant @{name}",
"account.joined_short": "Amañ abaoe",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 5ae49325f..86f1fb476 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "No hi ha tuts",
"account.featured_tags.title": "etiquetes destacades de {name}",
"account.follow": "Segueix",
+ "account.follow_back": "Segueix",
"account.followers": "Seguidors",
"account.followers.empty": "A aquest usuari encara no el segueix ningú.",
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} Seguidors}}",
"account.following": "Seguint",
"account.following_counter": "{count, plural, other {{counter} Seguint-ne}}",
"account.follows.empty": "Aquest usuari encara no segueix ningú.",
- "account.follows_you": "Et segueix",
"account.go_to_profile": "Vés al perfil",
"account.hide_reblogs": "Amaga els impulsos de @{name}",
"account.in_memoriam": "En Memòria.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Silencia les notificacions",
"account.mute_short": "Silencia",
"account.muted": "Silenciat",
+ "account.mutual": "Mutu",
"account.no_bio": "No s'ha proporcionat cap descripció.",
"account.open_original_page": "Obre la pàgina original",
"account.posts": "Tuts",
diff --git a/app/javascript/mastodon/locales/ckb.json b/app/javascript/mastodon/locales/ckb.json
index 7e9641832..f1cafd1ac 100644
--- a/app/javascript/mastodon/locales/ckb.json
+++ b/app/javascript/mastodon/locales/ckb.json
@@ -36,7 +36,6 @@
"account.following": "بەدوادا",
"account.following_counter": "{count, plural, one {{counter} شوێنکەوتوو} other {{counter} شوێنکەوتوو}}",
"account.follows.empty": "ئەم بەکارهێنەرە تا ئێستا شوێن کەس نەکەوتووە.",
- "account.follows_you": "شوێنت دەکەوێت",
"account.go_to_profile": "بڕۆ بۆ پڕۆفایلی",
"account.hide_reblogs": "داشاردنی بووستەکان لە @{name}",
"account.joined_short": "بەشداری کردووە",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index d4bb2f82b..9f90c3d21 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -19,7 +19,6 @@
"account.followers_counter": "{count, plural, one {{counter} Abbunatu} other {{counter} Abbunati}}",
"account.following_counter": "{count, plural, one {{counter} Abbunamentu} other {{counter} Abbunamenti}}",
"account.follows.empty": "St'utilizatore ùn seguita nisunu.",
- "account.follows_you": "Vi seguita",
"account.hide_reblogs": "Piattà spartere da @{name}",
"account.link_verified_on": "A prupietà di stu ligame hè stata verificata u {date}",
"account.locked_info": "U statutu di vita privata di u contu hè chjosu. U pruprietariu esamina manualmente e dumande d'abbunamentu.",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index 16bf5020c..e18cabcec 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -38,7 +38,6 @@
"account.following": "Sledujete",
"account.following_counter": "{count, plural, one {{counter} Sledovaný} few {{counter} Sledovaní} many {{counter} Sledovaných} other {{counter} Sledovaných}}",
"account.follows.empty": "Tento uživatel zatím nikoho nesleduje.",
- "account.follows_you": "Sleduje vás",
"account.go_to_profile": "Přejít na profil",
"account.hide_reblogs": "Skrýt boosty od @{name}",
"account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index 4ecf48735..17133631a 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -37,7 +37,6 @@
"account.following": "Yn dilyn",
"account.following_counter": "{count, plural, one {Yn dilyn: {counter}} other {Yn dilyn: {counter}}}",
"account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
- "account.follows_you": "Yn eich dilyn chi",
"account.go_to_profile": "Mynd i'r proffil",
"account.hide_reblogs": "Cuddio hybiau gan @{name}",
"account.in_memoriam": "Er Cof.",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 04fc43734..3bc830ad2 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Ingen indlæg",
"account.featured_tags.title": "{name}s fremhævede hashtags",
"account.follow": "Følg",
+ "account.follow_back": "Følg tilbage",
"account.followers": "Følgere",
"account.followers.empty": "Ingen følger denne bruger endnu.",
"account.followers_counter": "{count, plural, one {{counter} Følger} other {{counter} Følgere}}",
"account.following": "Følger",
"account.following_counter": "{count, plural, one {{counter} Følges} other {{counter} Følges}}",
"account.follows.empty": "Denne bruger følger ikke nogen endnu.",
- "account.follows_you": "Følger dig",
"account.go_to_profile": "Gå til profil",
"account.hide_reblogs": "Skjul boosts fra @{name}",
"account.in_memoriam": "Til minde om.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Slå lyden fra for notifikationer",
"account.mute_short": "Skjul (mute)",
"account.muted": "Skjult (muted)",
+ "account.mutual": "Fælles",
"account.no_bio": "Ingen beskrivelse til rådighed.",
"account.open_original_page": "Åbn oprindelig side",
"account.posts": "Indlæg",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index cf545e48c..ddcbc58ce 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Keine Beiträge",
"account.featured_tags.title": "Von {name} vorgestellte Hashtags",
"account.follow": "Folgen",
+ "account.follow_back": "Ebenfalls folgen",
"account.followers": "Follower",
"account.followers.empty": "Diesem Profil folgt noch niemand.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}",
"account.following": "Folge ich",
"account.following_counter": "{count, plural, one {{counter} Folge ich} other {{counter} Folge ich}}",
"account.follows.empty": "Dieses Profil folgt noch niemandem.",
- "account.follows_you": "Folgt dir",
"account.go_to_profile": "Profil aufrufen",
"account.hide_reblogs": "Geteilte Beiträge von @{name} ausblenden",
"account.in_memoriam": "Zum Andenken.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Benachrichtigungen stummschalten",
"account.mute_short": "Stummschalten",
"account.muted": "Stummgeschaltet",
+ "account.mutual": "Gegenseitig",
"account.no_bio": "Keine Beschreibung verfügbar.",
"account.open_original_page": "Ursprüngliche Seite öffnen",
"account.posts": "Beiträge",
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index 02ce7120a..83a986ba4 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -36,7 +36,6 @@
"account.following": "Ακολουθείτε",
"account.following_counter": "{count, plural, one {{counter} Ακολουθεί} other {{counter} Ακολουθούν}}",
"account.follows.empty": "Αυτός ο χρήστης δεν ακολουθεί κανέναν ακόμα.",
- "account.follows_you": "Σε ακολουθεί",
"account.go_to_profile": "Μετάβαση στο προφίλ",
"account.hide_reblogs": "Απόκρυψη ενισχύσεων από @{name}",
"account.in_memoriam": "Εις μνήμην.",
diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json
index 1a67fecb6..37e5efa5b 100644
--- a/app/javascript/mastodon/locales/en-GB.json
+++ b/app/javascript/mastodon/locales/en-GB.json
@@ -38,7 +38,6 @@
"account.following": "Following",
"account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.",
- "account.follows_you": "Follows you",
"account.go_to_profile": "Go to profile",
"account.hide_reblogs": "Hide boosts from @{name}",
"account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 05d7d1656..be7209d04 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "No posts",
"account.featured_tags.title": "{name}'s featured hashtags",
"account.follow": "Follow",
+ "account.follow_back": "Follow back",
"account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following": "Following",
"account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.",
- "account.follows_you": "Follows you",
"account.go_to_profile": "Go to profile",
"account.hide_reblogs": "Hide boosts from @{name}",
"account.in_memoriam": "In Memoriam.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Mute notifications",
"account.mute_short": "Mute",
"account.muted": "Muted",
+ "account.mutual": "Mutual",
"account.no_bio": "No description provided.",
"account.open_original_page": "Open original page",
"account.posts": "Posts",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 2678d83a5..4daa699dc 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -38,7 +38,6 @@
"account.following": "Sekvatoj",
"account.following_counter": "{count, plural, one {{counter} Sekvato} other {{counter} Sekvatoj}}",
"account.follows.empty": "La uzanto ankoraŭ ne sekvas iun ajn.",
- "account.follows_you": "Sekvas vin",
"account.go_to_profile": "Iri al profilo",
"account.hide_reblogs": "Kaŝi diskonigojn de @{name}",
"account.in_memoriam": "Memore.",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index 4573f4ab9..eb6c0f3e4 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Sin mensajes",
"account.featured_tags.title": "Etiquetas destacadas de {name}",
"account.follow": "Seguir",
+ "account.follow_back": "Seguir también",
"account.followers": "Seguidores",
"account.followers.empty": "Todavía nadie sigue a este usuario.",
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
"account.following": "Siguiendo",
"account.following_counter": "{count, plural, other {Siguiendo a {counter}}}",
"account.follows.empty": "Todavía este usuario no sigue a nadie.",
- "account.follows_you": "Te sigue",
"account.go_to_profile": "Ir al perfil",
"account.hide_reblogs": "Ocultar adhesiones de @{name}",
"account.in_memoriam": "Cuenta conmemorativa.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Silenciar notificaciones",
"account.mute_short": "Silenciar",
"account.muted": "Silenciado",
+ "account.mutual": "Mutuo",
"account.no_bio": "Sin descripción provista.",
"account.open_original_page": "Abrir página original",
"account.posts": "Mensajes",
diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json
index 0d26afef2..fab5f6ec9 100644
--- a/app/javascript/mastodon/locales/es-MX.json
+++ b/app/javascript/mastodon/locales/es-MX.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Sin publicaciones",
"account.featured_tags.title": "Etiquetas destacadas de {name}",
"account.follow": "Seguir",
+ "account.follow_back": "Seguir también",
"account.followers": "Seguidores",
"account.followers.empty": "Todavía nadie sigue a este usuario.",
"account.followers_counter": "{count, plural, one {{counter} Seguidor} other {{counter} Seguidores}}",
"account.following": "Siguiendo",
"account.following_counter": "{count, plural, other {{counter} Siguiendo}}",
"account.follows.empty": "Este usuario todavía no sigue a nadie.",
- "account.follows_you": "Te sigue",
"account.go_to_profile": "Ir al perfil",
"account.hide_reblogs": "Ocultar retoots de @{name}",
"account.in_memoriam": "En memoria.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Silenciar notificaciones",
"account.mute_short": "Silenciar",
"account.muted": "Silenciado",
+ "account.mutual": "Mutuo",
"account.no_bio": "Sin biografía.",
"account.open_original_page": "Abrir página original",
"account.posts": "Publicaciones",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index bbc8bcc75..0d6149f5f 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Sin publicaciones",
"account.featured_tags.title": "Etiquetas destacadas de {name}",
"account.follow": "Seguir",
+ "account.follow_back": "Seguir también",
"account.followers": "Seguidores",
"account.followers.empty": "Todavía nadie sigue a este usuario.",
"account.followers_counter": "{count, plural, one {{counter} Seguidor} other {{counter} Seguidores}}",
"account.following": "Siguiendo",
"account.following_counter": "{count, plural, other {Siguiendo a {counter}}}",
"account.follows.empty": "Este usuario todavía no sigue a nadie.",
- "account.follows_you": "Te sigue",
"account.go_to_profile": "Ir al perfil",
"account.hide_reblogs": "Ocultar impulsos de @{name}",
"account.in_memoriam": "Cuenta conmemorativa.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Silenciar notificaciones",
"account.mute_short": "Silenciar",
"account.muted": "Silenciado",
+ "account.mutual": "Mutuo",
"account.no_bio": "Sin biografía.",
"account.open_original_page": "Abrir página original",
"account.posts": "Publicaciones",
diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json
index a41aa02f8..c39733410 100644
--- a/app/javascript/mastodon/locales/et.json
+++ b/app/javascript/mastodon/locales/et.json
@@ -38,7 +38,6 @@
"account.following": "Jälgib",
"account.following_counter": "{count, plural, one {{counter} jälgitav} other {{counter} jälgitavat}}",
"account.follows.empty": "See kasutaja ei jälgi veel kedagi.",
- "account.follows_you": "Jälgib sind",
"account.go_to_profile": "Mine profiilile",
"account.hide_reblogs": "Peida @{name} jagamised",
"account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index 26ed7add1..c75f697e4 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Bidalketarik ez",
"account.featured_tags.title": "{name} erabiltzailearen nabarmendutako traolak",
"account.follow": "Jarraitu",
+ "account.follow_back": "Jarraitu bueltan",
"account.followers": "Jarraitzaileak",
"account.followers.empty": "Ez du inork erabiltzaile hau jarraitzen oraindik.",
"account.followers_counter": "{count, plural, one {Jarraitzaile {counter}} other {{counter} jarraitzaile}}",
"account.following": "Jarraitzen",
"account.following_counter": "{count, plural, one {{counter} jarraitzen} other {{counter} jarraitzen}}",
"account.follows.empty": "Erabiltzaile honek ez du inor jarraitzen oraindik.",
- "account.follows_you": "Jarraitzen dizu",
"account.go_to_profile": "Joan profilera",
"account.hide_reblogs": "Ezkutatu @{name} erabiltzailearen bultzadak",
"account.in_memoriam": "Oroimenezkoa.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Mututu jakinarazpenak",
"account.mute_short": "Mututu",
"account.muted": "Mutututa",
+ "account.mutual": "Elkarrekikoa",
"account.no_bio": "Ez da deskribapenik eman.",
"account.open_original_page": "Ireki jatorrizko orria",
"account.posts": "Bidalketa",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 8e8930bfe..a93d0f661 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -38,7 +38,6 @@
"account.following": "پی میگیرید",
"account.following_counter": "{count, plural, one {{counter} پیگرفته} other {{counter} پیگرفته}}",
"account.follows.empty": "این کاربر هنوز پیگیر کسی نیست.",
- "account.follows_you": "پیگیرتان است",
"account.go_to_profile": "رفتن به نمایه",
"account.hide_reblogs": "نهفتن تقویتهای @{name}",
"account.in_memoriam": "به یادبود.",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index e99de4d03..dab7eac1e 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Ei julkaisuja",
"account.featured_tags.title": "Käyttäjän {name} esillä pidettävät aihetunnisteet",
"account.follow": "Seuraa",
+ "account.follow_back": "Seuraa takaisin",
"account.followers": "Seuraajat",
"account.followers.empty": "Kukaan ei seuraa tätä käyttäjää vielä.",
"account.followers_counter": "{count, plural, one {{counter} seuraaja} other {{counter} seuraajaa}}",
"account.following": "Seuratut",
"account.following_counter": "{count, plural, one {{counter} seurattu} other {{counter} seurattua}}",
"account.follows.empty": "Tämä käyttäjä ei vielä seuraa ketään.",
- "account.follows_you": "Seuraa sinua",
"account.go_to_profile": "Avaa profiili",
"account.hide_reblogs": "Piilota käyttäjän @{name} tehostukset",
"account.in_memoriam": "Muistoissamme.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Mykistä ilmoitukset",
"account.mute_short": "Mykistä",
"account.muted": "Mykistetty",
+ "account.mutual": "Molemmat",
"account.no_bio": "Kuvausta ei ole annettu.",
"account.open_original_page": "Avaa alkuperäinen sivu",
"account.posts": "Julkaisut",
diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json
index bc7635887..ec27c8f60 100644
--- a/app/javascript/mastodon/locales/fil.json
+++ b/app/javascript/mastodon/locales/fil.json
@@ -31,7 +31,6 @@
"account.followers.empty": "Wala pang sumusunod sa tagagamit na ito.",
"account.following": "Sinusundan",
"account.follows.empty": "Wala pang sinusundan ang tagagamit na ito.",
- "account.follows_you": "Sinusunod ka",
"account.go_to_profile": "Pumunta sa profile",
"account.hide_reblogs": "Itago ang mga pagpapalakas mula sa {name}",
"account.in_memoriam": "Sa Alaala Ni.",
diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json
index 45fafd15d..9259f2015 100644
--- a/app/javascript/mastodon/locales/fo.json
+++ b/app/javascript/mastodon/locales/fo.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Einki uppslag",
"account.featured_tags.title": "Tvíkrossar hjá {name}",
"account.follow": "Fylg",
+ "account.follow_back": "Fylg aftur",
"account.followers": "Fylgjarar",
"account.followers.empty": "Ongar fylgjarar enn.",
"account.followers_counter": "{count, plural, one {{counter} Fylgjari} other {{counter} Fylgjarar}}",
"account.following": "Fylgir",
"account.following_counter": "{count, plural, one {{counter} fylgir} other {{counter} fylgja}}",
"account.follows.empty": "Hesin brúkari fylgir ongum enn.",
- "account.follows_you": "Fylgir tær",
"account.go_to_profile": "Far til vanga",
"account.hide_reblogs": "Fjal lyft frá @{name}",
"account.in_memoriam": "In memoriam.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Sløkk fráboðanir",
"account.mute_short": "Doyv",
"account.muted": "Sløkt/ur",
+ "account.mutual": "Sínamillum",
"account.no_bio": "Lýsing vantar.",
"account.open_original_page": "Opna upprunasíðuna",
"account.posts": "Uppsløg",
diff --git a/app/javascript/mastodon/locales/fr-QC.json b/app/javascript/mastodon/locales/fr-QC.json
index e2067cc46..f2d99412d 100644
--- a/app/javascript/mastodon/locales/fr-QC.json
+++ b/app/javascript/mastodon/locales/fr-QC.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Aucune publication",
"account.featured_tags.title": "Hashtags inclus de {name}",
"account.follow": "Suivre",
+ "account.follow_back": "S'abonner en retour",
"account.followers": "abonné·e·s",
"account.followers.empty": "Personne ne suit ce compte pour l'instant.",
"account.followers_counter": "{count, plural, one {{counter} Abonné·e} other {{counter} Abonné·e·s}}",
"account.following": "Abonné·e",
"account.following_counter": "{count, plural, one {{counter} Abonnement} other {{counter} Abonnements}}",
"account.follows.empty": "Ce compte ne suit personne présentement.",
- "account.follows_you": "Vous suit",
"account.go_to_profile": "Voir ce profil",
"account.hide_reblogs": "Masquer les boosts de @{name}",
"account.in_memoriam": "En souvenir de",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Rendre les notifications muettes",
"account.mute_short": "Rendre muet",
"account.muted": "Masqué·e",
+ "account.mutual": "Mutuel",
"account.no_bio": "Description manquante.",
"account.open_original_page": "Ouvrir la page d'origine",
"account.posts": "Publications",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 7db4bf7bc..774702f98 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Aucun message",
"account.featured_tags.title": "Les hashtags en vedette de {name}",
"account.follow": "Suivre",
+ "account.follow_back": "S'abonner en retour",
"account.followers": "Abonné·e·s",
"account.followers.empty": "Personne ne suit cet·te utilisateur·rice pour l’instant.",
"account.followers_counter": "{count, plural, one {{counter} Abonné·e} other {{counter} Abonné·e·s}}",
"account.following": "Abonnements",
"account.following_counter": "{count, plural, one {{counter} Abonnement} other {{counter} Abonnements}}",
"account.follows.empty": "Cet·te utilisateur·rice ne suit personne pour l’instant.",
- "account.follows_you": "Vous suit",
"account.go_to_profile": "Aller au profil",
"account.hide_reblogs": "Masquer les partages de @{name}",
"account.in_memoriam": "En mémoire de.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Désactiver les alertes",
"account.mute_short": "Mettre en sourdine",
"account.muted": "Masqué·e",
+ "account.mutual": "Mutuel",
"account.no_bio": "Aucune description fournie.",
"account.open_original_page": "Ouvrir la page d'origine",
"account.posts": "Messages",
diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json
index 2ec4a53bf..ea42ef91c 100644
--- a/app/javascript/mastodon/locales/fy.json
+++ b/app/javascript/mastodon/locales/fy.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Gjin berjochten",
"account.featured_tags.title": "Utljochte hashtags fan {name}",
"account.follow": "Folgje",
+ "account.follow_back": "Weromfolgje",
"account.followers": "Folgers",
"account.followers.empty": "Noch net ien folget dizze brûker.",
"account.followers_counter": "{count, plural, one {{counter} folger} other {{counter} folgers}}",
"account.following": "Folgjend",
"account.following_counter": "{count, plural, one {{counter} folgjend} other {{counter} folgjend}}",
"account.follows.empty": "Dizze brûker folget noch net ien.",
- "account.follows_you": "Folget jo",
"account.go_to_profile": "Gean nei profyl",
"account.hide_reblogs": "Boosts fan @{name} ferstopje",
"account.in_memoriam": "Yn memoriam.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Meldingen negearje",
"account.mute_short": "Negearje",
"account.muted": "Negearre",
+ "account.mutual": "Jimme folgje inoar",
"account.no_bio": "Gjin omskriuwing opjûn.",
"account.open_original_page": "Orizjinele side iepenje",
"account.posts": "Berjochten",
diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json
index ee6b44c88..f0f9c5554 100644
--- a/app/javascript/mastodon/locales/ga.json
+++ b/app/javascript/mastodon/locales/ga.json
@@ -35,7 +35,6 @@
"account.following": "Ag leanúint",
"account.following_counter": "{count, plural, one {Ag leanúint cúntas amháin} other {Ag leanúint {counter} cúntas}}",
"account.follows.empty": "Ní leanann an t-úsáideoir seo duine ar bith fós.",
- "account.follows_you": "Do do leanúint",
"account.go_to_profile": "Téigh go dtí próifíl",
"account.hide_reblogs": "Folaigh moltaí ó @{name}",
"account.in_memoriam": "Cuimhneachán.",
diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json
index 91333c1a0..11d83d6ce 100644
--- a/app/javascript/mastodon/locales/gd.json
+++ b/app/javascript/mastodon/locales/gd.json
@@ -37,7 +37,6 @@
"account.following": "A’ leantainn",
"account.following_counter": "{count, plural, one {A’ leantainn {counter}} two {A’ leantainn {counter}} few {A’ leantainn {counter}} other {A’ leantainn {counter}}}",
"account.follows.empty": "Chan eil an cleachdaiche seo a’ leantainn neach sam bith fhathast.",
- "account.follows_you": "Gad leantainn",
"account.go_to_profile": "Tadhail air a’ phròifil",
"account.hide_reblogs": "Falaich na brosnachaidhean o @{name}",
"account.in_memoriam": "Mar chuimhneachan.",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 08d7d4977..bfd20a467 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -38,7 +38,6 @@
"account.following": "Seguindo",
"account.following_counter": "{count, plural, one {{counter} Seguindo} other {{counter} Seguindo}}",
"account.follows.empty": "Esta usuaria aínda non segue a ninguén.",
- "account.follows_you": "Séguete",
"account.go_to_profile": "Ir ao perfil",
"account.hide_reblogs": "Agochar promocións de @{name}",
"account.in_memoriam": "Lembranzas.",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index dda92c5c9..658e16b85 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "אין חצרוצים",
"account.featured_tags.title": "התגיות המועדפות של {name}",
"account.follow": "לעקוב",
+ "account.follow_back": "החזרת עוקב",
"account.followers": "עוקבים",
"account.followers.empty": "אף אחד לא עוקב אחר המשתמש הזה עדיין.",
"account.followers_counter": "{count, plural,one {עוקב אחד} other {{counter} עוקבים}}",
"account.following": "נעקבים",
"account.following_counter": "{count, plural,one {עוקב אחרי {counter}}other {עוקב אחרי {counter}}}",
"account.follows.empty": "משתמש זה עדיין לא עוקב אחרי אף אחד.",
- "account.follows_you": "במעקב אחריך",
"account.go_to_profile": "מעבר לפרופיל",
"account.hide_reblogs": "להסתיר הידהודים מאת @{name}",
"account.in_memoriam": "פרופיל זכרון.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "השתקת התראות",
"account.mute_short": "השתקה",
"account.muted": "מושתק",
+ "account.mutual": "הדדיים",
"account.no_bio": "לא סופק תיאור.",
"account.open_original_page": "לפתיחת העמוד המקורי",
"account.posts": "פוסטים",
diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json
index 387941b5e..76d7c1d38 100644
--- a/app/javascript/mastodon/locales/hi.json
+++ b/app/javascript/mastodon/locales/hi.json
@@ -37,7 +37,6 @@
"account.following": "फॉलोइंग",
"account.following_counter": "{count, plural, one {{counter} निम्नलिखित} other {{counter} निम्नलिखित}}",
"account.follows.empty": "यह यूज़र् अभी तक किसी को फॉलो नहीं करता है।",
- "account.follows_you": "आपको फॉलो करता है",
"account.go_to_profile": "प्रोफाइल में जाएँ",
"account.hide_reblogs": "@{name} के बूस्ट छुपाएं",
"account.in_memoriam": "याद में",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 6da7d6cd8..654991a0f 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -28,7 +28,6 @@
"account.following": "Pratim",
"account.following_counter": "{count, plural, one {{counter} praćeni} few{{counter} praćena} other {{counter} praćenih}}",
"account.follows.empty": "Korisnik/ca još ne prati nikoga.",
- "account.follows_you": "Prati te",
"account.go_to_profile": "Idi na profil",
"account.hide_reblogs": "Sakrij boostove od @{name}",
"account.in_memoriam": "U sjećanje.",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 386b15811..0d50e36fe 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Nincs bejegyzés",
"account.featured_tags.title": "{name} kiemelt hashtagjei",
"account.follow": "Követés",
+ "account.follow_back": "Viszontkövetés",
"account.followers": "Követő",
"account.followers.empty": "Ezt a felhasználót még senki sem követi.",
"account.followers_counter": "{count, plural, one {{counter} Követő} other {{counter} Követő}}",
"account.following": "Követve",
"account.following_counter": "{count, plural, one {{counter} Követett} other {{counter} Követett}}",
"account.follows.empty": "Ez a felhasználó még senkit sem követ.",
- "account.follows_you": "Követ téged",
"account.go_to_profile": "Ugrás a profilhoz",
"account.hide_reblogs": "@{name} megtolásainak elrejtése",
"account.in_memoriam": "Emlékünkben.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Értesítések némítása",
"account.mute_short": "Némítás",
"account.muted": "Némítva",
+ "account.mutual": "Kölcsönös",
"account.no_bio": "Leírás nincs megadva.",
"account.open_original_page": "Eredeti oldal megnyitása",
"account.posts": "Bejegyzések",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index f2548c7d3..835105218 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -32,7 +32,6 @@
"account.following": "Հետեւած",
"account.following_counter": "{count, plural, one {{counter} Հետեւած} other {{counter} Հետեւած}}",
"account.follows.empty": "Այս օգտատէրը դեռ ոչ մէկի չի հետեւում։",
- "account.follows_you": "Հետեւում է քեզ",
"account.go_to_profile": "Գնալ անձնական հաշիւ",
"account.hide_reblogs": "Թաքցնել @{name}֊ի տարածածները",
"account.joined_short": "Միացել է",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 8ecf36125..5af20a97f 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -37,7 +37,6 @@
"account.following": "Mengikuti",
"account.following_counter": "{count, plural, other {{counter} Mengikuti}}",
"account.follows.empty": "Pengguna ini belum mengikuti siapa pun.",
- "account.follows_you": "Mengikuti Anda",
"account.go_to_profile": "Buka profil",
"account.hide_reblogs": "Sembunyikan boosts dari @{name}",
"account.in_memoriam": "Mengenang.",
diff --git a/app/javascript/mastodon/locales/ie.json b/app/javascript/mastodon/locales/ie.json
index 9f91bf2ce..a7cf1caab 100644
--- a/app/javascript/mastodon/locales/ie.json
+++ b/app/javascript/mastodon/locales/ie.json
@@ -38,7 +38,6 @@
"account.following": "Sequent",
"account.following_counter": "{count, plural, one {{counter} Sequent} other {{counter} Sequent}}",
"account.follows.empty": "Ti-ci usator ancor ne seque quemcunc.",
- "account.follows_you": "Seque te",
"account.go_to_profile": "Ear a profil",
"account.hide_reblogs": "Celar boosts de @{name}",
"account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/ig.json b/app/javascript/mastodon/locales/ig.json
index c24d28eea..f163567f1 100644
--- a/app/javascript/mastodon/locales/ig.json
+++ b/app/javascript/mastodon/locales/ig.json
@@ -6,7 +6,6 @@
"account.follow": "Soro",
"account.followers": "Ndị na-eso",
"account.following": "Na-eso",
- "account.follows_you": "Na-eso gị",
"account.mute": "Mee ogbi @{name}",
"account.unfollow": "Kwụsị iso",
"account_note.placeholder": "Click to add a note",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index ba4440893..233b76845 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -37,7 +37,6 @@
"account.following": "Sequata",
"account.following_counter": "{count, plural, one {{counter} Sequas} other {{counter} Sequanti}}",
"account.follows.empty": "Ca uzanto ne sequa irgu til nun.",
- "account.follows_you": "Sequas tu",
"account.go_to_profile": "Irez al profilo",
"account.hide_reblogs": "Celez repeti de @{name}",
"account.in_memoriam": "Memorige.",
diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json
index 0df1a5e75..46e83b4db 100644
--- a/app/javascript/mastodon/locales/is.json
+++ b/app/javascript/mastodon/locales/is.json
@@ -38,7 +38,6 @@
"account.following": "Fylgist með",
"account.following_counter": "{count, plural, one {Fylgist með: {counter}} other {Fylgist með: {counter}}}",
"account.follows.empty": "Þessi notandi fylgist ennþá ekki með neinum.",
- "account.follows_you": "Fylgir þér",
"account.go_to_profile": "Fara í notandasnið",
"account.hide_reblogs": "Fela endurbirtingar fyrir @{name}",
"account.in_memoriam": "Minning.",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index fa659506a..4fb4d88cb 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Nessun post",
"account.featured_tags.title": "Hashtag in evidenza di {name}",
"account.follow": "Segui",
+ "account.follow_back": "Segui a tua volta",
"account.followers": "Follower",
"account.followers.empty": "Ancora nessuno segue questo utente.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}",
"account.following": "Seguiti",
"account.following_counter": "{count, plural, one {{counter} Seguiti} other {{counter} Seguiti}}",
"account.follows.empty": "Questo utente non segue ancora nessuno.",
- "account.follows_you": "Ti segue",
"account.go_to_profile": "Vai al profilo",
"account.hide_reblogs": "Nascondi potenziamenti da @{name}",
"account.in_memoriam": "In memoria.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Silenzia notifiche",
"account.mute_short": "Silenzia",
"account.muted": "Mutato",
+ "account.mutual": "Reciproco",
"account.no_bio": "Nessuna descrizione fornita.",
"account.open_original_page": "Apri la pagina originale",
"account.posts": "Post",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 5ad9d6dc9..324641575 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -38,7 +38,6 @@
"account.following": "フォロー中",
"account.following_counter": "{counter} フォロー",
"account.follows.empty": "まだ誰もフォローしていません。",
- "account.follows_you": "フォローされています",
"account.go_to_profile": "プロフィールページへ",
"account.hide_reblogs": "@{name}さんからのブーストを非表示",
"account.in_memoriam": "故人を偲んで。",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 875ac3c19..9d977e933 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -15,7 +15,6 @@
"account.featured_tags.last_status_never": "პოსტები არ არის",
"account.follow": "გაყოლა",
"account.followers": "მიმდევრები",
- "account.follows_you": "მოგყვებათ",
"account.hide_reblogs": "დაიმალოს ბუსტები @{name}-სგან",
"account.media": "მედია",
"account.mention": "ასახელეთ @{name}",
diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json
index e9d4b57de..08c70a940 100644
--- a/app/javascript/mastodon/locales/kab.json
+++ b/app/javascript/mastodon/locales/kab.json
@@ -22,7 +22,6 @@
"account.followers_counter": "{count, plural, one {{count} n umeḍfar} other {{count} n imeḍfaren}}",
"account.following_counter": "{count, plural, one {{counter} yettwaḍfaren} other {{counter} yettwaḍfaren}}",
"account.follows.empty": "Ar tura, amseqdac-agi ur yeṭṭafaṛ yiwen.",
- "account.follows_you": "Yeṭṭafaṛ-ik",
"account.hide_reblogs": "Ffer ayen i ibeṭṭu @{name}",
"account.link_verified_on": "Taɣara n useɣwen-a tettwasenqed ass n {date}",
"account.locked_info": "Amiḍan-agi uslig isekweṛ. D bab-is kan i izemren ad yeǧǧ, s ufus-is, win ara t-iḍefṛen.",
diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json
index 189d792e3..e0e047ba1 100644
--- a/app/javascript/mastodon/locales/kk.json
+++ b/app/javascript/mastodon/locales/kk.json
@@ -34,7 +34,6 @@
"account.following": "Жазылым",
"account.following_counter": "{count, plural, one {{counter} жазылым} other {{counter} жазылым}}",
"account.follows.empty": "Бұл қолданушы әлі ешкімге жазылмаған.",
- "account.follows_you": "Сізге жазылған",
"account.go_to_profile": "Профиліне өту",
"account.hide_reblogs": "@{name} бустарын жасыру",
"account.joined_short": "Қосылған",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 4606916c1..5746ab67a 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "게시물 없음",
"account.featured_tags.title": "{name} 님의 추천 해시태그",
"account.follow": "팔로우",
+ "account.follow_back": "맞팔로우",
"account.followers": "팔로워",
"account.followers.empty": "아직 아무도 이 사용자를 팔로우하고 있지 않습니다.",
"account.followers_counter": "{counter} 팔로워",
"account.following": "팔로잉",
"account.following_counter": "{counter} 팔로잉",
"account.follows.empty": "이 사용자는 아직 아무도 팔로우하고 있지 않습니다.",
- "account.follows_you": "나를 팔로우합니다",
"account.go_to_profile": "프로필로 이동",
"account.hide_reblogs": "@{name}의 부스트를 숨기기",
"account.in_memoriam": "고인의 계정입니다.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "알림 뮤트",
"account.mute_short": "뮤트",
"account.muted": "뮤트됨",
+ "account.mutual": "상호 팔로우",
"account.no_bio": "제공된 설명이 없습니다.",
"account.open_original_page": "원본 페이지 열기",
"account.posts": "게시물",
diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json
index b94054267..7d8603cae 100644
--- a/app/javascript/mastodon/locales/ku.json
+++ b/app/javascript/mastodon/locales/ku.json
@@ -36,7 +36,6 @@
"account.following": "Dişopîne",
"account.following_counter": "{count, plural, one {{counter} Dişopîne} other {{counter} Dişopîne}}",
"account.follows.empty": "Ev bikarhêner hin kesekî heya niha neşopandiye.",
- "account.follows_you": "Te dişopîne",
"account.go_to_profile": "Biçe bo profîlê",
"account.hide_reblogs": "Bilindkirinên ji @{name} veşêre",
"account.in_memoriam": "Di bîranînê de.",
diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json
index ca08ca836..8f384fe12 100644
--- a/app/javascript/mastodon/locales/kw.json
+++ b/app/javascript/mastodon/locales/kw.json
@@ -20,7 +20,6 @@
"account.followers_counter": "{count, plural, one {{counter} Holyer} other {{counter} Holyer}}",
"account.following_counter": "{count, plural, one {Ow holya {counter}} other {Ow holya {counter}}}",
"account.follows.empty": "Ny wra'n devnydhyer ma holya nagonan hwath.",
- "account.follows_you": "Y'th hol",
"account.hide_reblogs": "Kudha kenerthow a @{name}",
"account.link_verified_on": "Perghenogeth an kolm ma a veu checkys dhe {date}",
"account.locked_info": "Studh privetter an akont ma yw alhwedhys. An perghen a wra dasweles dre leuv piw a yll aga holya.",
diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json
index 1b501c710..c54ed2a7e 100644
--- a/app/javascript/mastodon/locales/lad.json
+++ b/app/javascript/mastodon/locales/lad.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "\"No ay publikasyones",
"account.featured_tags.title": "Etiketas avaliadas de {name}",
"account.follow": "Sige",
+ "account.follow_back": "Sige tamyen",
"account.followers": "Suivantes",
"account.followers.empty": "Por agora dingun no sige a este utilizador.",
"account.followers_counter": "{count, plural, one {{counter} suivante} other {{counter} suivantes}}",
"account.following": "Sigiendo",
"account.following_counter": "{count, plural, other {Sigiendo a {counter}}}",
"account.follows.empty": "Este utilizador ainda no sige a ningun.",
- "account.follows_you": "Te sige",
"account.go_to_profile": "Va al profil",
"account.hide_reblogs": "Eskonde repartajasyones de @{name}",
"account.in_memoriam": "De bendicha memoria.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Silensia avizos de @{name}",
"account.mute_short": "Silensia",
"account.muted": "Silensiado",
+ "account.mutual": "Mutual",
"account.no_bio": "No ay deskripsion.",
"account.open_original_page": "Avre pajina orijnala",
"account.posts": "Publikasyones",
@@ -77,6 +78,10 @@
"admin.dashboard.retention.average": "Media",
"admin.dashboard.retention.cohort": "Mez de enrejistrasyon",
"admin.dashboard.retention.cohort_size": "Muevos utilizadores",
+ "admin.impact_report.instance_accounts": "Profiles de kuentos esto efasaria",
+ "admin.impact_report.instance_followers": "Suivantes a los kualos nuestros utilizadores perderian",
+ "admin.impact_report.instance_follows": "Suivantes a los kualos sus utilizadores perderian",
+ "admin.impact_report.title": "Rezumen de impakto",
"alert.rate_limited.message": "Por favor aprova dempues de {retry_time, time, medium}.",
"alert.rate_limited.title": "Trafiko limitado",
"alert.unexpected.message": "Afito un yerro no asperado.",
@@ -220,6 +225,7 @@
"emoji_button.search_results": "Rizultados de bushkeda",
"emoji_button.symbols": "Simbolos",
"emoji_button.travel": "Viajes i lugares",
+ "empty_column.account_hides_collections": "Este utilizador desidio no mostrar esta enformasyon",
"empty_column.account_suspended": "Kuento suspendido",
"empty_column.account_timeline": "No ay publikasyones aki!",
"empty_column.account_unavailable": "Profil no desponivle",
@@ -294,6 +300,8 @@
"hashtag.column_settings.tag_mode.any": "Kualsekera de estos",
"hashtag.column_settings.tag_mode.none": "Dinguno de estos",
"hashtag.column_settings.tag_toggle": "Inkluir etiketas adisionalas en esta kolumna",
+ "hashtag.counter_by_accounts": "{count, plural, one {{counter} partisipante} other {{counter} partisipantes}}",
+ "hashtag.counter_by_uses": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}} oy",
"hashtag.follow": "Segir etiketa",
"hashtag.unfollow": "Desegir etiketa",
@@ -303,18 +311,23 @@
"home.column_settings.basic": "Opsyones bazikas",
"home.column_settings.show_reblogs": "Amostrar repartajasyones",
"home.column_settings.show_replies": "Amostrar repuestas",
+ "home.explore_prompt.body": "Tu linya prinsipala es una mikstura de publikasyones kon etiketas a las kualas eskojites a segir, la djente a la kuala eskojites a segir i las publikasyones ke eyos repartajan. Si esta demaziado trankila, puedes:",
+ "home.explore_prompt.title": "Esta es tu baza prinsipala en Mastodon.",
"home.hide_announcements": "Eskonde pregones",
"home.pending_critical_update.body": "Por favor aktualiza tu sirvidor de Mastodon pishin!",
"home.pending_critical_update.link": "Ve aktualizasyones",
+ "home.pending_critical_update.title": "Aktualizasyon de seguridad kritika esta desponivle!",
"home.show_announcements": "Amostra pregones",
"interaction_modal.description.favourite": "Kon un kuento en Mastodon, puedes markar esta publikasyon komo favorita para ke el autor sepa ke te plaze i para guadrarla para dempues.",
"interaction_modal.description.follow": "Kon un kuento en Mastodon, puedes segir a {name} para risivir sus publikasyones en tu linya temporal prinsipala.",
"interaction_modal.description.reblog": "Kon un kuento en Mastodon, puedes repartajar esta publikasyon para amostrarla a tus suivantes.",
"interaction_modal.description.reply": "Kon un kuento en Mastodon, puedes arispondir a esta publikasyon.",
+ "interaction_modal.login.action": "Va a tu sirvidor",
"interaction_modal.login.prompt": "Domeno del sirvidor de tu kuento, por enshemplo mastodon.social",
"interaction_modal.no_account_yet": "No tyenes kuento de Mastodon?",
"interaction_modal.on_another_server": "En otro sirvidor",
"interaction_modal.on_this_server": "En este sirvidor",
+ "interaction_modal.sign_in": "No estas konektado kon este sirvidor. Ande tyenes tu kuento?",
"interaction_modal.title.favourite": "Endika ke te plaze publikasyon de {name}",
"interaction_modal.title.follow": "Sige a {name}",
"interaction_modal.title.reblog": "Repartaja publikasyon de {name}",
@@ -369,6 +382,7 @@
"lists.delete": "Efasa lista",
"lists.edit": "Edita lista",
"lists.edit.submit": "Troka titolo",
+ "lists.exclusive": "Eskonder estas publikasyones de linya prinsipala",
"lists.new.create": "Adjusta lista",
"lists.new.title_placeholder": "Titolo de mueva lista",
"lists.replies_policy.followed": "Kualseker utilizardo segido",
@@ -403,6 +417,7 @@
"navigation_bar.lists": "Listas",
"navigation_bar.logout": "Salir",
"navigation_bar.mutes": "Utilizadores silensiados",
+ "navigation_bar.opened_in_classic_interface": "Publikasyones, kuentos i otras pajinas espesifikas se avren kon preferensyas predeterminadas en la enterfaz web klasika.",
"navigation_bar.personal": "Personal",
"navigation_bar.pins": "Publikasyones fiksadas",
"navigation_bar.preferences": "Preferensyas",
@@ -460,17 +475,25 @@
"notifications_permission_banner.title": "Nunka te piedres niente",
"onboarding.action.back": "Va atras",
"onboarding.actions.back": "Va atras",
+ "onboarding.actions.go_to_explore": "Va a los trendes",
+ "onboarding.actions.go_to_home": "Va a tu linya prinsipala",
"onboarding.compose.template": "Ke haber, #Mastodon?",
"onboarding.follows.title": "Personaliza tu linya prinsipala",
+ "onboarding.profile.discoverable": "Faz ke mi profil apareska en bushkedas",
"onboarding.profile.display_name": "Nombre amostrado",
"onboarding.profile.display_name_hint": "Tu nombre para amostrar.",
"onboarding.profile.note": "Tu deskripsyon",
+ "onboarding.profile.note_hint": "Puedes @enmentar a otra djente o #etiketas…",
"onboarding.profile.save_and_continue": "Guadra i kontinua",
"onboarding.profile.title": "Konfigurasyon de profil",
"onboarding.profile.upload_avatar": "Karga imaje de profil",
"onboarding.profile.upload_header": "Karga kavesera de profil",
+ "onboarding.share.message": "Soy {username} en #Mastodon! Segidme en {url}",
+ "onboarding.share.next_steps": "Posivles sigientes pasos:",
"onboarding.share.title": "Partaja tu profil",
"onboarding.start.skip": "No nesesitas ayudo para ampesar?",
+ "onboarding.start.title": "Lo logrates!",
+ "onboarding.steps.follow_people.body": "El buto de Mastodon es segir a djente interesante.",
"onboarding.steps.follow_people.title": "Personaliza tu linya prinsipala",
"onboarding.steps.publish_status.title": "Eskrive tu primera publikasyon",
"onboarding.steps.setup_profile.title": "Personaliza tu profil",
@@ -515,6 +538,7 @@
"reply_indicator.cancel": "Anula",
"report.block": "Bloka",
"report.block_explanation": "No veras sus publikasyones. No podra ver tus publikasyones ni segirte. Podra saver ke le blokates.",
+ "report.categories.legal": "Legal",
"report.categories.other": "Otros",
"report.categories.spam": "Spam",
"report.categories.violation": "El kontenido viola una o mas reglas del sirvidor",
@@ -533,6 +557,7 @@
"report.reasons.dislike": "No me plaze",
"report.reasons.dislike_description": "\"No es algo ke kero ver",
"report.reasons.legal": "Es ilegal",
+ "report.reasons.legal_description": "Kreyes ke esta violando la ley de tu paiz o el paiz del sirvidor",
"report.reasons.other": "Es otra koza",
"report.reasons.other_description": "El problem no es de las otras kategorias",
"report.reasons.spam": "Es spam",
@@ -552,6 +577,7 @@
"report.unfollow": "Desegir a @{name}",
"report.unfollow_explanation": "Estas sigiendo este kuento. Para no ver sus publikasyones en tu linya de tiempo, puedes deshar de segirlo.",
"report_notification.attached_statuses": "{count, plural, one {{count} publikasyon} other {{count} publikasyones}} atadas",
+ "report_notification.categories.legal": "Legal",
"report_notification.categories.other": "Otros",
"report_notification.categories.spam": "Spam",
"report_notification.categories.violation": "Violasion de reglas",
@@ -570,6 +596,7 @@
"search_popout.options": "Opsyones de bushkeda",
"search_popout.quick_actions": "Aksiones rapidas",
"search_popout.recent": "Bushkedas resientes",
+ "search_popout.specific_date": "dato espesifiko",
"search_popout.user": "utilizador",
"search_results.accounts": "Profiles",
"search_results.all": "Todos",
@@ -587,6 +614,7 @@
"sign_in_banner.create_account": "Kriya kuento",
"sign_in_banner.sign_in": "Konektate",
"sign_in_banner.sso_redirect": "Konektate o enrejistrate",
+ "sign_in_banner.text": "Konektate para segir prefiles o etiketas, partajar publikasyones, arispondir a eyas i markar ke te plazen. Puedes tambyen enteraktuar dizde tu kuento en un sirvidor desferente.",
"status.admin_account": "Avre la enterfaz de moderasyon para @{name}",
"status.admin_domain": "Avre la enterfaz de moderasyon para @{domain}",
"status.admin_status": "Avre esto en la enterfaz de moderasyon",
@@ -596,10 +624,12 @@
"status.cannot_reblog": "Esta publikasyon no se puede repartajar",
"status.copy": "Kopia atadijo de publikasyon",
"status.delete": "Efasa",
+ "status.detailed_status": "Vista de konversasyon detalyada",
"status.direct": "Enmenta a @{name} en privado",
"status.direct_indicator": "Enmentadura privada",
"status.edit": "Edita",
"status.edited": "Editado {date}",
+ "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} vezes}}",
"status.embed": "Inkrusta",
"status.favourite": "Te plaze",
"status.filter": "Filtra esta publikasyon",
@@ -637,6 +667,7 @@
"status.show_more": "Amostra mas",
"status.show_more_all": "Amostra mas para todo",
"status.show_original": "Amostra orijinal",
+ "status.title.with_attachments": "{user} publiko {attachmentCount, plural, one {un anekso} other {{attachmentCount} aneksos}}",
"status.translate": "Trezlada",
"status.translated_from_with": "Trezladado dizde {lang} kon {provider}",
"status.uncached_media_warning": "Vista previa no desponivle",
@@ -685,6 +716,7 @@
"upload_modal.preview_label": "Vista previa ({ratio})",
"upload_progress.label": "Kargando...",
"upload_progress.processing": "Prosesando…",
+ "username.taken": "Akel nombre de utilizador ya esta en uzo. Aprova otruno",
"video.close": "Serra video",
"video.download": "Abasha dosya",
"video.exit_fullscreen": "Sal de ekran kompleto",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index 58c80e411..82f1669b1 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Nėra įrašų",
"account.featured_tags.title": "{name} rekomenduojamos grotažymės",
"account.follow": "Sekti",
+ "account.follow_back": "Sekti atgal",
"account.followers": "Sekėjai",
"account.followers.empty": "Šio naudotojo dar niekas neseka.",
"account.followers_counter": "{count, plural, one {{counter} sekėjas (-a)} few {{counter} sekėjai} many {{counter} sekėjo} other {{counter} sekėjų}}",
"account.following": "Seka",
"account.following_counter": "{count, plural, one {{counter} Seka} few {{counter} Seka} many {{counter} Seka} other {{counter} Seka}}",
"account.follows.empty": "Šis (-i) naudotojas (-a) dar nieko neseka.",
- "account.follows_you": "Seka tave",
"account.go_to_profile": "Eiti į profilį",
"account.hide_reblogs": "Slėpti pakėlimus iš @{name}",
"account.in_memoriam": "Atminimui.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Nutildyti pranešimus",
"account.mute_short": "Nutildyti",
"account.muted": "Nutildytas",
+ "account.mutual": "Abipusis",
"account.no_bio": "Nėra pateikto aprašymo.",
"account.open_original_page": "Atidaryti originalinį puslapį",
"account.posts": "Įrašai",
diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json
index 63ec6275b..c06a1d936 100644
--- a/app/javascript/mastodon/locales/lv.json
+++ b/app/javascript/mastodon/locales/lv.json
@@ -37,7 +37,6 @@
"account.following": "Seko",
"account.following_counter": "{count, plural, one {{counter} sekojamais} other {{counter} sekojamie}}",
"account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.",
- "account.follows_you": "Seko tev",
"account.go_to_profile": "Doties uz profilu",
"account.hide_reblogs": "Slēpt @{name} izceltas ziņas",
"account.in_memoriam": "Piemiņai.",
diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json
index 371218bbf..bdef3f4a5 100644
--- a/app/javascript/mastodon/locales/mk.json
+++ b/app/javascript/mastodon/locales/mk.json
@@ -25,7 +25,6 @@
"account.followers": "Следбеници",
"account.followers.empty": "Никој не го следи овој корисник сеуште.",
"account.follows.empty": "Корисникот не следи никој сеуште.",
- "account.follows_you": "Те следи тебе",
"account.hide_reblogs": "Сокриј буст од @{name}",
"account.link_verified_on": "Сопстевноста на овај линк беше проверен на {date}",
"account.locked_info": "Статусот на приватност на овај корисник е сетиран како заклучен. Корисникот одлучува кој можи да го следи него.",
diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json
index b00cedc6f..11636646b 100644
--- a/app/javascript/mastodon/locales/ml.json
+++ b/app/javascript/mastodon/locales/ml.json
@@ -26,7 +26,6 @@
"account.following": "പിന്തുടരുന്നു",
"account.following_counter": "{count, plural, one {{counter} പിന്തുടരുന്നു} other {{counter} പിന്തുടരുന്നു}}",
"account.follows.empty": "ഈ ഉപയോക്താവ് ആരേയും ഇതുവരെ പിന്തുടരുന്നില്ല.",
- "account.follows_you": "നിങ്ങളെ പിന്തുടരുന്നു",
"account.go_to_profile": "പ്രൊഫൈലിലേക്ക് പോകാം",
"account.hide_reblogs": "@{name} ബൂസ്റ്റ് ചെയ്തവ മറയ്കുക",
"account.joined_short": "ജോയിൻ ചെയ്തിരിക്കുന്നു",
diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json
index 75b75375b..7f5b7d652 100644
--- a/app/javascript/mastodon/locales/mr.json
+++ b/app/javascript/mastodon/locales/mr.json
@@ -35,7 +35,6 @@
"account.following": "अनुसरण",
"account.following_counter": "{count, plural, one {{counter} following} other {{counter} following}}",
"account.follows.empty": "हा वापरकर्ता अजूनपर्यंत कोणाचा अनुयायी नाही.",
- "account.follows_you": "तुमचा अनुयायी आहे",
"account.go_to_profile": "प्रोफाइल वर जा",
"account.hide_reblogs": "@{name} पासून सर्व बूस्ट लपवा",
"account.joined_short": "सामील झाले",
diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json
index 724e07ae7..50a48db1e 100644
--- a/app/javascript/mastodon/locales/ms.json
+++ b/app/javascript/mastodon/locales/ms.json
@@ -37,7 +37,6 @@
"account.following": "Mengikuti",
"account.following_counter": "{count, plural, one {{counter} Diikuti} other {{counter} Diikuti}}",
"account.follows.empty": "Pengguna ini belum mengikuti sesiapa.",
- "account.follows_you": "Mengikuti anda",
"account.go_to_profile": "Pergi ke profil",
"account.hide_reblogs": "Sembunyikan galakan daripada @{name}",
"account.in_memoriam": "Dalam Memoriam.",
diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json
index 917419d17..3ca03b616 100644
--- a/app/javascript/mastodon/locales/my.json
+++ b/app/javascript/mastodon/locales/my.json
@@ -38,7 +38,6 @@
"account.following": "စောင့်ကြည့်နေသည်",
"account.following_counter": "{count, plural, one {စောင့်ကြည့်ထားသူ {counter}} other {စောင့်ကြည့်ထားသူများ {counter}}}",
"account.follows.empty": "ဤသူသည် မည်သူ့ကိုမျှ စောင့်ကြည့်ခြင်း မရှိသေးပါ။",
- "account.follows_you": "သင့်ကို စောင့်ကြည့်နေသည်",
"account.go_to_profile": "ပရိုဖိုင်းသို့ သွားရန်",
"account.hide_reblogs": "@{name} ၏ မျှဝေမှုကို ဝှက်ထားရန်",
"account.in_memoriam": "အမှတ်တရ",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 295b420fd..86617d4a5 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Geen berichten",
"account.featured_tags.title": "Uitgelichte hashtags van {name}",
"account.follow": "Volgen",
+ "account.follow_back": "Terugvolgen",
"account.followers": "Volgers",
"account.followers.empty": "Deze gebruiker heeft nog geen volgers of heeft deze verborgen.",
"account.followers_counter": "{count, plural, one {{counter} volger} other {{counter} volgers}}",
"account.following": "Volgend",
"account.following_counter": "{count, plural, one {{counter} volgend} other {{counter} volgend}}",
"account.follows.empty": "Deze gebruiker volgt nog niemand of heeft deze verborgen.",
- "account.follows_you": "Volgt jou",
"account.go_to_profile": "Ga naar profiel",
"account.hide_reblogs": "Boosts van @{name} verbergen",
"account.in_memoriam": "In memoriam.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Meldingen negeren",
"account.mute_short": "Negeren",
"account.muted": "Genegeerd",
+ "account.mutual": "Jullie volgen elkaar",
"account.no_bio": "Geen beschrijving opgegeven.",
"account.open_original_page": "Originele pagina openen",
"account.posts": "Berichten",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index 4750b1adc..3ef2f80ea 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Ingen innlegg",
"account.featured_tags.title": "{name} sine framheva emneknaggar",
"account.follow": "Fylg",
+ "account.follow_back": "Følg tilbake",
"account.followers": "Fylgjarar",
"account.followers.empty": "Ingen fylgjer denne brukaren enno.",
"account.followers_counter": "{count, plural, one {{counter} fylgjar} other {{counter} fylgjarar}}",
"account.following": "Fylgjer",
"account.following_counter": "{count, plural, one {Fylgjer {counter}} other {Fylgjer {counter}}}",
"account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.",
- "account.follows_you": "Fylgjer deg",
"account.go_to_profile": "Gå til profil",
"account.hide_reblogs": "Skjul framhevingar frå @{name}",
"account.in_memoriam": "Til minne om.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Demp varslingar",
"account.mute_short": "Demp",
"account.muted": "Målbunden",
+ "account.mutual": "Felles",
"account.no_bio": "Inga skildring er gjeven.",
"account.open_original_page": "Opne originalsida",
"account.posts": "Tut",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 1739d4aa3..29eaeddff 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -38,7 +38,6 @@
"account.following": "Følger",
"account.following_counter": "{count, plural, one {{counter} som følges} other {{counter} som følges}}",
"account.follows.empty": "Denne brukeren følger ikke noen enda.",
- "account.follows_you": "Følger deg",
"account.go_to_profile": "Gå til profil",
"account.hide_reblogs": "Skjul fremhevinger fra @{name}",
"account.in_memoriam": "Til minne om.",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 3812057fb..833bfe6ac 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -34,7 +34,6 @@
"account.following": "Abonat",
"account.following_counter": "{count, plural, one {{counter} Abonaments} other {{counter} Abonaments}}",
"account.follows.empty": "Aqueste utilizaire sèc pas degun pel moment.",
- "account.follows_you": "Vos sèc",
"account.go_to_profile": "Anar al perfil",
"account.hide_reblogs": "Rescondre los partatges de @{name}",
"account.in_memoriam": "En Memòria.",
diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json
index 708d8c3e9..132b695cd 100644
--- a/app/javascript/mastodon/locales/pa.json
+++ b/app/javascript/mastodon/locales/pa.json
@@ -18,7 +18,6 @@
"account.followers.empty": "ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਹਾਲੇ ਕੋਈ ਫ਼ਾਲੋ ਨਹੀਂ ਕਰਦਾ ਹੈ।",
"account.following": "ਫ਼ਾਲੋ ਕੀਤਾ",
"account.follows.empty": "ਇਹ ਵਰਤੋਂਕਾਰ ਹਾਲੇ ਕਿਸੇ ਨੂੰ ਫ਼ਾਲੋ ਨਹੀਂ ਕਰਦਾ ਹੈ।",
- "account.follows_you": "ਤੁਹਾਨੂੰ ਫ਼ਾਲੋ ਕਰੋ",
"account.media": "ਮੀਡੀਆ",
"account.muted": "ਮੌਨ ਕੀਤੀਆਂ",
"account.posts": "ਪੋਸਟਾਂ",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index dc77f8f4e..9a3710da7 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Brak postów",
"account.featured_tags.title": "Polecane hasztagi {name}",
"account.follow": "Obserwuj",
+ "account.follow_back": "Obserwuj wzajemnie",
"account.followers": "Obserwujący",
"account.followers.empty": "Nikt jeszcze nie obserwuje tego użytkownika.",
"account.followers_counter": "{count, plural, one {{counter} obserwujący} few {{counter} obserwujących} many {{counter} obserwujących} other {{counter} obserwujących}}",
"account.following": "Obserwowani",
"account.following_counter": "{count, plural, one {{counter} obserwowany} few {{counter} obserwowanych} many {{counter} obserwowanych} other {{counter} obserwowanych}}",
"account.follows.empty": "Ten użytkownik nie obserwuje jeszcze nikogo.",
- "account.follows_you": "Obserwuje Cię",
"account.go_to_profile": "Przejdź do profilu",
"account.hide_reblogs": "Ukryj podbicia od @{name}",
"account.in_memoriam": "Ku pamięci.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Wycisz powiadomienia",
"account.mute_short": "Wycisz",
"account.muted": "Wyciszony",
+ "account.mutual": "Przyjaciele",
"account.no_bio": "Brak opisu.",
"account.open_original_page": "Otwórz stronę oryginalną",
"account.posts": "Wpisy",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index e7975dd76..482cc8ee7 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -38,7 +38,6 @@
"account.following": "Seguindo",
"account.following_counter": "{count, plural, one {segue {counter}} other {segue {counter}}}",
"account.follows.empty": "Nada aqui.",
- "account.follows_you": "te segue",
"account.go_to_profile": "Ir ao perfil",
"account.hide_reblogs": "Ocultar boosts de @{name}",
"account.in_memoriam": "Em memória.",
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index d5055b0dc..a6d0ffee9 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Sem publicações",
"account.featured_tags.title": "#Etiquetas destacadas por {name}",
"account.follow": "Seguir",
+ "account.follow_back": "Seguir de volta",
"account.followers": "Seguidores",
"account.followers.empty": "Ainda ninguém segue este utilizador.",
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
"account.following": "A seguir",
"account.following_counter": "{count, plural, other {A seguir {counter}}}",
"account.follows.empty": "Este utilizador ainda não segue ninguém.",
- "account.follows_you": "Segue-te",
"account.go_to_profile": "Ir para o perfil",
"account.hide_reblogs": "Esconder partilhas de @{name}",
"account.in_memoriam": "Em Memória.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Silenciar notificações",
"account.mute_short": "Silenciar",
"account.muted": "Silenciada",
+ "account.mutual": "Mútuo",
"account.no_bio": "Nenhuma descrição fornecida.",
"account.open_original_page": "Abrir a página original",
"account.posts": "Publicações",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index 5355f9935..88dbfa4b8 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -36,7 +36,6 @@
"account.following": "Urmăriți",
"account.following_counter": "{count, plural, one {Un abonament} few {{counter} abonamente} other {{counter} de abonamente}}",
"account.follows.empty": "Momentan acest utilizator nu are niciun abonament.",
- "account.follows_you": "Este abonat la tine",
"account.go_to_profile": "Mergi la profil",
"account.hide_reblogs": "Ascunde distribuirile de la @{name}",
"account.joined_short": "Înscris",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index f0c48236b..37a8328c9 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -38,7 +38,6 @@
"account.following": "Подписки",
"account.following_counter": "{count, plural, one {{counter} подписка} many {{counter} подписок} other {{counter} подписки}}",
"account.follows.empty": "Этот пользователь пока ни на кого не подписался.",
- "account.follows_you": "Подписан(а) на вас",
"account.go_to_profile": "Перейти к профилю",
"account.hide_reblogs": "Скрыть продвижения от @{name}",
"account.in_memoriam": "В Памяти.",
diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json
index 59379343b..051ec5d6f 100644
--- a/app/javascript/mastodon/locales/sa.json
+++ b/app/javascript/mastodon/locales/sa.json
@@ -36,7 +36,6 @@
"account.following": "अनुसरति",
"account.following_counter": "{count, plural, one {{counter} अनुसृतः} two {{counter} अनुसृतौ} other {{counter} अनुसृताः}}",
"account.follows.empty": "न कोऽप्यनुसृतो वर्तते",
- "account.follows_you": "त्वामनुसरति",
"account.go_to_profile": "प्रोफायिलं गच्छ",
"account.hide_reblogs": "@{name} मित्रस्य प्रकाशनानि छिद्यन्ताम्",
"account.in_memoriam": "स्मृत्याम्",
diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json
index 7f29525e7..89e951f77 100644
--- a/app/javascript/mastodon/locales/sc.json
+++ b/app/javascript/mastodon/locales/sc.json
@@ -30,7 +30,6 @@
"account.following": "Sighende",
"account.following_counter": "{count, plural, one {Sighende a {counter}} other {Sighende a {counter}}}",
"account.follows.empty": "Custa persone non sighit ancora a nemos.",
- "account.follows_you": "Ti sighit",
"account.hide_reblogs": "Cua is cumpartziduras de @{name}",
"account.in_memoriam": "In memoriam.",
"account.joined_short": "At aderidu",
diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json
index 28dac9c2a..0378cd292 100644
--- a/app/javascript/mastodon/locales/sco.json
+++ b/app/javascript/mastodon/locales/sco.json
@@ -35,7 +35,6 @@
"account.following": "Follaein",
"account.following_counter": "{count, plural, one {{counter} Follaein} other {{counter} Follaein}}",
"account.follows.empty": "This uiser disnae follae oniebody yit.",
- "account.follows_you": "Follaes ye",
"account.go_to_profile": "Gang tae profile",
"account.hide_reblogs": "Dinnae shaw heezes fae @{name}",
"account.joined_short": "Jynt",
diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json
index 835f699b8..c2d2a41cc 100644
--- a/app/javascript/mastodon/locales/si.json
+++ b/app/javascript/mastodon/locales/si.json
@@ -26,7 +26,6 @@
"account.following": "අනුගමන",
"account.following_counter": "{count, plural, one {අනුගමන {counter}} other {අනුගමන {counter}}}",
"account.follows.empty": "තවමත් කිසිවෙක් අනුගමනය නොකරයි.",
- "account.follows_you": "ඔබව අනුගමනය කරයි",
"account.go_to_profile": "පැතිකඩට යන්න",
"account.joined_short": "එක් වූ දිනය",
"account.link_verified_on": "මෙම සබැඳියේ අයිතිය {date} දී පරීක්ෂා කෙරිණි",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 0eb404198..02425d73e 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -38,7 +38,6 @@
"account.following": "Sledujem",
"account.following_counter": "{count, plural, one {{counter} Sledovaných} other {{counter} Sledujúcich}}",
"account.follows.empty": "Tento používateľ ešte nikoho nesleduje.",
- "account.follows_you": "Sleduje ťa",
"account.go_to_profile": "Prejdi na profil",
"account.hide_reblogs": "Skry zdieľania od @{name}",
"account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 4ef2681f9..b3998b911 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Ni objav",
"account.featured_tags.title": "Izpostavljeni ključniki {name}",
"account.follow": "Sledi",
+ "account.follow_back": "Sledi nazaj",
"account.followers": "Sledilci",
"account.followers.empty": "Nihče ne sledi temu uporabniku.",
"account.followers_counter": "{count, plural, one {ima {counter} sledilca} two {ima {counter} sledilca} few {ima {counter} sledilce} other {ima {counter} sledilcev}}",
"account.following": "Sledim",
"account.following_counter": "{count, plural, one {sledi {count} osebi} two {sledi {count} osebama} few {sledi {count} osebam} other {sledi {count} osebam}}",
"account.follows.empty": "Ta uporabnik še ne sledi nikomur.",
- "account.follows_you": "Vam sledi",
"account.go_to_profile": "Pojdi na profil",
"account.hide_reblogs": "Skrij izpostavitve od @{name}",
"account.in_memoriam": "V spomin.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Utišaj obvestila",
"account.mute_short": "Utišaj",
"account.muted": "Utišan",
+ "account.mutual": "Vzajemno",
"account.no_bio": "Ni opisa.",
"account.open_original_page": "Odpri izvirno stran",
"account.posts": "Objave",
diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json
index 710224e1c..48eac1487 100644
--- a/app/javascript/mastodon/locales/sq.json
+++ b/app/javascript/mastodon/locales/sq.json
@@ -38,7 +38,6 @@
"account.following": "Ndjekje",
"account.following_counter": "{count, plural, one {{counter} i Ndjekur} other {{counter} të Ndjekur}}",
"account.follows.empty": "Ky përdorues ende s’ndjek kënd.",
- "account.follows_you": "Ju ndjek",
"account.go_to_profile": "Kalo te profili",
"account.hide_reblogs": "Fshih përforcime nga @{name}",
"account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 35bb8f992..59ad0ae84 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -38,7 +38,6 @@
"account.following": "Prati",
"account.following_counter": "{count, plural, one {{counter} prati} few {{counter} prati} other {{counter} prati}}",
"account.follows.empty": "Ovaj korisnik još uvek nikog ne prati.",
- "account.follows_you": "Prati vas",
"account.go_to_profile": "Idi na profil",
"account.hide_reblogs": "Sakrij podržavanja @{name}",
"account.in_memoriam": "U znak sećanja na.",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 552c04d13..79786b8d4 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -38,7 +38,6 @@
"account.following": "Прати",
"account.following_counter": "{count, plural, one {{counter} прати} few {{counter} прати} other {{counter} прати}}",
"account.follows.empty": "Овај корисник још увек никог не прати.",
- "account.follows_you": "Прати вас",
"account.go_to_profile": "Иди на профил",
"account.hide_reblogs": "Сакриј подржавања од @{name}",
"account.in_memoriam": "У знак сећања на.",
@@ -53,6 +52,7 @@
"account.mute_notifications_short": "Искључи обавештења",
"account.mute_short": "Искључи",
"account.muted": "Игнорисан",
+ "account.mutual": "Заједнички",
"account.no_bio": "Нема описа.",
"account.open_original_page": "Отвори оригиналну страницу",
"account.posts": "Објаве",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 8a07da72d..d3ca776bd 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "Inga inlägg",
"account.featured_tags.title": "{name}s utvalda hashtaggar",
"account.follow": "Följ",
+ "account.follow_back": "Följ tillbaka",
"account.followers": "Följare",
"account.followers.empty": "Ingen följer denna användare än.",
"account.followers_counter": "{count, plural, one {{counter} följare} other {{counter} följare}}",
"account.following": "Följer",
"account.following_counter": "{count, plural, one {{counter} följd} other {{counter} följda}}",
"account.follows.empty": "Denna användare följer inte någon än.",
- "account.follows_you": "Följer dig",
"account.go_to_profile": "Gå till profilen",
"account.hide_reblogs": "Dölj boostar från @{name}",
"account.in_memoriam": "Till minne av.",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Stäng av aviseringsljud",
"account.mute_short": "Tysta",
"account.muted": "Tystad",
+ "account.mutual": "Ömsesidig",
"account.no_bio": "Ingen beskrivning angiven.",
"account.open_original_page": "Öppna den ursprungliga sidan",
"account.posts": "Inlägg",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index ce9042e62..6b2332d5b 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -25,7 +25,6 @@
"account.following": "பின்தொடரும்",
"account.following_counter": "{count, plural,one {{counter} சந்தா} other {{counter} சந்தாக்கள்}}",
"account.follows.empty": "இந்த பயனர் இதுவரை யாரையும் பின்தொடரவில்லை.",
- "account.follows_you": "உங்களைப் பின்தொடர்கிறார்",
"account.hide_reblogs": "இருந்து ஊக்கியாக மறை @{name}",
"account.link_verified_on": "இந்த இணைப்பை உரிமையாளர் சரிபார்க்கப்பட்டது {date}",
"account.locked_info": "இந்தக் கணக்கு தனியுரிமை நிலை பூட்டப்பட்டுள்ளது. அவர்களைப் பின்தொடர்பவர் யார் என்பதை உரிமையாளர் கைமுறையாக மதிப்பாய்வு செய்கிறார்.",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index f21c0ef57..3c231871f 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -12,7 +12,6 @@
"account.followers": "అనుచరులు",
"account.followers.empty": "ఈ వినియోగదారుడిని ఇంకా ఎవరూ అనుసరించడంలేదు.",
"account.follows.empty": "ఈ వినియోగదారి ఇంకా ఎవరినీ అనుసరించడంలేదు.",
- "account.follows_you": "మిమ్మల్ని అనుసరిస్తున్నారు",
"account.hide_reblogs": "@{name} నుంచి బూస్ట్ లను దాచిపెట్టు",
"account.link_verified_on": "ఈ లంకె యొక్క యాజమాన్యం {date}న పరీక్షించబడింది",
"account.locked_info": "ఈ ఖాతా యొక్క గోప్యత స్థితి లాక్ చేయబడి వుంది. ఈ ఖాతాను ఎవరు అనుసరించవచ్చో యజమానే నిర్ణయం తీసుకుంటారు.",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 67b920393..d14e37517 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "ไม่มีโพสต์",
"account.featured_tags.title": "แฮชแท็กที่น่าสนใจของ {name}",
"account.follow": "ติดตาม",
+ "account.follow_back": "ติดตามกลับ",
"account.followers": "ผู้ติดตาม",
"account.followers.empty": "ยังไม่มีใครติดตามผู้ใช้นี้",
"account.followers_counter": "{count, plural, other {{counter} ผู้ติดตาม}}",
"account.following": "กำลังติดตาม",
"account.following_counter": "{count, plural, other {{counter} กำลังติดตาม}}",
"account.follows.empty": "ผู้ใช้นี้ยังไม่ได้ติดตามใคร",
- "account.follows_you": "ติดตามคุณ",
"account.go_to_profile": "ไปยังโปรไฟล์",
"account.hide_reblogs": "ซ่อนการดันจาก @{name}",
"account.in_memoriam": "เพื่อระลึกถึง",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 8eb09bb7c..e85db817b 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -32,20 +32,20 @@
"account.featured_tags.last_status_never": "Gönderi yok",
"account.featured_tags.title": "{name} kişisinin öne çıkan etiketleri",
"account.follow": "Takip et",
+ "account.follow_back": "Geri takip et",
"account.followers": "Takipçi",
"account.followers.empty": "Henüz kimse bu kullanıcıyı takip etmiyor.",
"account.followers_counter": "{count, plural, one {{counter} Takipçi} other {{counter} Takipçi}}",
"account.following": "Takip Ediliyor",
"account.following_counter": "{count, plural, one {{counter} Takip Edilen} other {{counter} Takip Edilen}}",
"account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.",
- "account.follows_you": "Seni takip ediyor",
"account.go_to_profile": "Profile git",
"account.hide_reblogs": "@{name} kişisinin boostlarını gizle",
"account.in_memoriam": "Hatırasına.",
"account.joined_short": "Katıldı",
"account.languages": "Abone olunan dilleri değiştir",
"account.link_verified_on": "Bu bağlantının sahipliği {date} tarihinde denetlendi",
- "account.locked_info": "Bu hesabın gizlilik durumu gizli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini manuel olarak onaylıyor.",
+ "account.locked_info": "Bu hesabın gizlilik durumu gizli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini elle onaylıyor.",
"account.media": "Medya",
"account.mention": "@{name} kişisinden bahset",
"account.moved_to": "{name} yeni hesabının artık şu olduğunu belirtti:",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "Bildirimleri sessize al",
"account.mute_short": "Sessize al",
"account.muted": "Susturuldu",
+ "account.mutual": "Karşılıklı",
"account.no_bio": "Herhangi bir açıklama belirtilmedi.",
"account.open_original_page": "Asıl sayfayı aç",
"account.posts": "Gönderiler",
@@ -345,7 +346,7 @@
"keyboard_shortcuts.down": "Listede aşağıya inmek için",
"keyboard_shortcuts.enter": "gönderiyi aç",
"keyboard_shortcuts.favourite": "Gönderiyi favorilerine ekle",
- "keyboard_shortcuts.favourites": "Favoriler listeni aç",
+ "keyboard_shortcuts.favourites": "Gözde listeni aç",
"keyboard_shortcuts.federated": "Federe akışı aç",
"keyboard_shortcuts.heading": "Klavye kısayolları",
"keyboard_shortcuts.home": "Ana akışı aç",
diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json
index 6727f3e59..47fe60bd2 100644
--- a/app/javascript/mastodon/locales/tt.json
+++ b/app/javascript/mastodon/locales/tt.json
@@ -35,7 +35,6 @@
"account.following": "Язылулар",
"account.following_counter": "{count, plural, one {{counter} язылу} other {{counter} язылу}}",
"account.follows.empty": "Беркемгә дә язылмаган әле.",
- "account.follows_you": "Сезгә язылган",
"account.go_to_profile": "Профильгә күчү",
"account.hide_reblogs": "Скрывать көчен нче @{name}",
"account.in_memoriam": "Истәлегенә.",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 58a14c0ed..92eacaad1 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -38,7 +38,6 @@
"account.following": "Ви стежите",
"account.following_counter": "{count, plural, one {{counter} підписка} few {{counter} підписки} many {{counter} підписок} other {{counter} підписки}}",
"account.follows.empty": "Цей користувач ще ні на кого не підписався.",
- "account.follows_you": "Підписується на вас",
"account.go_to_profile": "Перейти до профілю",
"account.hide_reblogs": "Сховати поширення від @{name}",
"account.in_memoriam": "Пам'ятник.",
@@ -53,6 +52,7 @@
"account.mute_notifications_short": "Не сповіщати",
"account.mute_short": "Ігнорувати",
"account.muted": "Приховується",
+ "account.mutual": "Взаємно",
"account.no_bio": "Немає опису.",
"account.open_original_page": "Відкрити оригінальну сторінку",
"account.posts": "Дописи",
diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index 8fc3aff03..563b2dedf 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -32,7 +32,6 @@
"account.following": "فالو کر رہے ہیں",
"account.following_counter": "{count, plural, one {{counter} پیروی کر رہے ہیں} other {{counter} پیروی کر رہے ہیں}}",
"account.follows.empty": "\"یہ صارف ہنوز کسی کی پیروی نہیں کرتا ہے\".",
- "account.follows_you": "آپ کا پیروکار ہے",
"account.go_to_profile": "پروفائل پر جائیں",
"account.hide_reblogs": "@{name} سے فروغ چھپائیں",
"account.in_memoriam": "یادگار میں۔",
diff --git a/app/javascript/mastodon/locales/uz.json b/app/javascript/mastodon/locales/uz.json
index 026cc115c..8eeee42a5 100644
--- a/app/javascript/mastodon/locales/uz.json
+++ b/app/javascript/mastodon/locales/uz.json
@@ -35,7 +35,6 @@
"account.following": "Kuzatish",
"account.following_counter": "{count, plural, one {{counter} ga Muxlis} other {{counter} larga muxlis}}",
"account.follows.empty": "Bu foydalanuvchi hali hech kimni kuzatmagan.",
- "account.follows_you": "Sizga obuna",
"account.go_to_profile": "Profilga o'tish",
"account.hide_reblogs": "@{name} dan boostlarni yashirish",
"account.joined_short": "Qo'shilgan",
diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json
index 9ac90b407..721c7cd4a 100644
--- a/app/javascript/mastodon/locales/vi.json
+++ b/app/javascript/mastodon/locales/vi.json
@@ -38,7 +38,6 @@
"account.following": "Đang theo dõi",
"account.following_counter": "{count, plural, one {{counter} Theo dõi} other {{counter} Theo dõi}}",
"account.follows.empty": "Người này chưa theo dõi ai.",
- "account.follows_you": "Đang theo dõi bạn",
"account.go_to_profile": "Xem hồ sơ",
"account.hide_reblogs": "Ẩn tút @{name} đăng lại",
"account.in_memoriam": "Tưởng Niệm.",
diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json
index 5896a25b0..008a9636d 100644
--- a/app/javascript/mastodon/locales/zgh.json
+++ b/app/javascript/mastodon/locales/zgh.json
@@ -12,7 +12,6 @@
"account.edit_profile": "ⵙⵏⴼⵍ ⵉⴼⵔⵙ",
"account.follow": "ⴹⴼⵕ",
"account.followers": "ⵉⵎⴹⴼⴰⵕⵏ",
- "account.follows_you": "ⴹⴼⵕⵏ ⴽⵯⵏ",
"account.media": "ⴰⵙⵏⵖⵎⵉⵙ",
"account.mute": "ⵥⵥⵉⵥⵏ @{name}",
"account.muted": "ⵉⵜⵜⵓⵥⵉⵥⵏ",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 1678fd729..575e0c7ae 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "暂无嘟文",
"account.featured_tags.title": "{name} 的精选标签",
"account.follow": "关注",
+ "account.follow_back": "回关",
"account.followers": "关注者",
"account.followers.empty": "目前无人关注此用户。",
"account.followers_counter": "被 {counter} 人关注",
"account.following": "正在关注",
"account.following_counter": "正在关注 {counter} 人",
"account.follows.empty": "此用户目前未关注任何人。",
- "account.follows_you": "关注了你",
"account.go_to_profile": "前往个人资料页",
"account.hide_reblogs": "隐藏来自 @{name} 的转嘟",
"account.in_memoriam": "谨此悼念。",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "关闭通知",
"account.mute_short": "隐藏",
"account.muted": "已隐藏",
+ "account.mutual": "互相关注",
"account.no_bio": "未提供描述。",
"account.open_original_page": "打开原始页面",
"account.posts": "嘟文",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 263c70799..cd0845b6e 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "暫無文章",
"account.featured_tags.title": "{name} 的精選標籤",
"account.follow": "關注",
+ "account.follow_back": "追蹤對方",
"account.followers": "追蹤者",
"account.followers.empty": "尚未有人追蹤這位使用者。",
"account.followers_counter": "有 {count, plural,one {{counter} 個} other {{counter} 個}}追蹤者",
"account.following": "正在追蹤",
"account.following_counter": "正在追蹤 {count, plural,one {{counter}}other {{counter} 人}}",
"account.follows.empty": "這位使用者尚未追蹤任何人。",
- "account.follows_you": "追蹤你",
"account.go_to_profile": "前往個人檔案",
"account.hide_reblogs": "隱藏 @{name} 的轉推",
"account.in_memoriam": "謹此悼念。",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "靜音通知",
"account.mute_short": "靜音",
"account.muted": "靜音",
+ "account.mutual": "互相追蹤",
"account.no_bio": "未提供描述。",
"account.open_original_page": "打開原始頁面",
"account.posts": "帖文",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 8484e00c8..998393695 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -32,13 +32,13 @@
"account.featured_tags.last_status_never": "沒有嘟文",
"account.featured_tags.title": "{name} 的推薦主題標籤",
"account.follow": "跟隨",
+ "account.follow_back": "跟隨回去",
"account.followers": "跟隨者",
"account.followers.empty": "尚未有人跟隨這位使用者。",
"account.followers_counter": "被 {count, plural, other {{counter} 人}}跟隨",
"account.following": "跟隨中",
"account.following_counter": "正在跟隨 {count,plural,other {{counter} 人}}",
"account.follows.empty": "這位使用者尚未跟隨任何人。",
- "account.follows_you": "跟隨了您",
"account.go_to_profile": "前往個人檔案",
"account.hide_reblogs": "隱藏來自 @{name} 的轉嘟",
"account.in_memoriam": "謹此悼念。",
@@ -53,6 +53,7 @@
"account.mute_notifications_short": "靜音推播通知",
"account.mute_short": "靜音",
"account.muted": "已靜音",
+ "account.mutual": "互相跟隨",
"account.no_bio": "無個人檔案描述",
"account.open_original_page": "檢視原始頁面",
"account.posts": "嘟文",
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index fedfa39de..f6262899a 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -110,6 +110,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def process_status_params
@status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: @account.followers_url)
+ attachment_ids = process_attachments.take(4).map(&:id)
+
@params = {
uri: @status_parser.uri,
url: @status_parser.url || @status_parser.uri,
@@ -125,7 +127,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
visibility: @status_parser.visibility,
thread: replied_to_status,
conversation: conversation_from_uri(@object['conversation']),
- media_attachment_ids: process_attachments.take(4).map(&:id),
+ media_attachment_ids: attachment_ids,
+ ordered_media_attachment_ids: attachment_ids,
poll: process_poll,
}
end
diff --git a/app/models/account_suggestions/friends_of_friends_source.rb b/app/models/account_suggestions/friends_of_friends_source.rb
index 77d4f635a..28d0ab99b 100644
--- a/app/models/account_suggestions/friends_of_friends_source.rb
+++ b/app/models/account_suggestions/friends_of_friends_source.rb
@@ -16,7 +16,7 @@ class AccountSuggestions::FriendsOfFriendsSource < AccountSuggestions::Source
JOIN account_stats ON account_stats.account_id = accounts.id
LEFT OUTER JOIN follow_recommendation_mutes ON follow_recommendation_mutes.target_account_id = accounts.id AND follow_recommendation_mutes.account_id = :id
WHERE follows.account_id IN (SELECT * FROM first_degree)
- AND follows.target_account_id NOT IN (SELECT * FROM first_degree)
+ AND NOT EXISTS (SELECT 1 FROM follows f WHERE f.target_account_id = follows.target_account_id AND f.account_id = :id)
AND follows.target_account_id <> :id
AND accounts.discoverable
AND accounts.suspended_at IS NULL
diff --git a/app/models/status.rb b/app/models/status.rb
index 4bdadae06..3faa50700 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -277,7 +277,9 @@ class Status < ApplicationRecord
def ordered_media_attachments
if ordered_media_attachment_ids.nil?
- media_attachments
+ # NOTE: sort Ruby-side to avoid hitting the database when the status is
+ # not persisted to database yet
+ media_attachments.sort_by(&:id)
else
map = media_attachments.index_by(&:id)
ordered_media_attachment_ids.filter_map { |media_attachment_id| map[media_attachment_id] }
diff --git a/config/locales/devise.ie.yml b/config/locales/devise.ie.yml
index cee5357c2..0cf0fbe1f 100644
--- a/config/locales/devise.ie.yml
+++ b/config/locales/devise.ie.yml
@@ -2,8 +2,16 @@
ie:
devise:
failure:
+ already_authenticated: Tu ha ja intrat.
+ inactive: Tui conto ancor ne ha esset activat.
invalid: Ínvalid %{authentication_keys} o passa-parol.
+ last_attempt: Hay solmen un prova ante que tui conto deveni serrat.
+ locked: Tui conto es serrat.
not_found_in_database: Ínvalid %{authentication_keys} o passa-parol.
+ pending: Tui conto es ancor sub revision.
+ timeout: Tui session ha expirat. Ples reintrar denov por continuar.
+ unauthenticated: Tu deve intrar o registrar te ante continuar.
+ unconfirmed: Tu deve confirmar tui e-posta ante continuar.
mailer:
email_changed:
extra: Si tu ne changeat tui email-adresse, it es probabil que alqui ha ganiat accesse a tui conto. Ples changear tui passa-parol strax o contacter li administrator del servitor si tu ne posse intrar tui conto.
@@ -20,6 +28,8 @@ ie:
title: Reiniciar passa-parol
two_factor_disabled:
explanation: 2-factor autentication por tui conto ha esset desactivisat. Aperter session nu es possibil solmen per email-adresse e passa-parol.
+ omniauth_callbacks:
+ failure: Ne posset autenticar te de %{kind} pro "%{reason}".
passwords:
no_token: Tu ne posse accessar ti-ci págine sin venir de un email pri reiniciar li passa-parol. Si tu ha venit de un email pri reiniciar li passa-parol, ples far cert que tu usat li complet URL providet.
send_instructions: Si tui email-adresse existe in nor database, tu va reciver un ligament por recuperar li passa-parol a tui email-adresse in quelc minutes. Ples vider tui spam-emails si tu ne recivet ti email.
diff --git a/config/locales/ie.yml b/config/locales/ie.yml
index 94df7d145..6013923e3 100644
--- a/config/locales/ie.yml
+++ b/config/locales/ie.yml
@@ -329,6 +329,7 @@ ie:
title: Adjunter nov customisat emoji
no_emoji_selected: Null emoji esset changeat pro que null esset selectet
not_permitted: Tu ne es permisset far ti action
+ overwrite: Remplazzar
shortcode_hint: Adminim 2 carácteres, solmen lítteres, ciffres e sublineas
title: Customisat emoji
uncategorized: Íncategorisat
@@ -433,6 +434,7 @@ ie:
title: Bloccar nov email-dominia
no_email_domain_block_selected: Null email-dominia-bloccas esset changeat pro que null esset selectet
not_permitted: Ne permisset
+ resolved_through_html: Resoluet per %{domain}
title: Bloccat email-dominias
export_domain_allows:
new:
@@ -459,6 +461,7 @@ ie:
unsuppress: Restaurar seque-recomandation
instances:
availability:
+ no_failures_recorded: Null fallimentes registrat.
title: Disponibilitá
warning: Li ultim prova conexer a ti servitor ha esset ínsuccessosi
back_to_all: Omni
@@ -482,8 +485,16 @@ ie:
instance_followers_measure: nor sequitores ta
instance_follows_measure: lor sequitores ci
instance_languages_dimension: Max grand lingues
+ instance_statuses_measure: salvat postas
delivery:
+ all: Omni
+ clear: Aclarar errores de liveration
+ failing: Fallint
+ restart: Recomensar liveration
+ stop: Haltar liveration
unavailable: Índisponibil
+ delivery_available: Liveration es disponibil
+ delivery_error_days: Dies de errores de liveration
delivery_error_hint: Si liveration ne es possibil durant %{count} dies, it va esser marcat automaticmen quam ínliverabil.
destroyed_msg: Data de %{domain} es nu in li linea por iminent deletion.
empty: Null dominias trovat.
@@ -561,6 +572,9 @@ ie:
other_description_html: Vider plu optiones por controlar li conduida del conto e customisar comunication al raportat conto.
resolve_description_html: Null action va esser fat contra li raportat conto, null admoniment registrat, e li raporte va esser cludet.
silence_description_html: Li conto va esser visibil nur a tis qui ja seque it o qui sercha it manualmen, limitante severimen su atingement. Ti sempre posse esser revertet. Ti clude omni raportes contra ti conto.
+ suspend_description_html: Ti-ci conto e omni contenete va esser ínaccessibil e finalmen deletet, e interacter con it va esser ínpossibil. Reversibil til que 30 dies ha passat. Clude omni raportes contra ti conto.
+ actions_description_html: Decider quel action a far por soluer ti raporte. Si tu fa un punitiv action contra li raportat conto, li usator va reciver un notification per email, except in li casu que li Spam categorie es selectet.
+ actions_description_remote_html: Decider quel action a far por soluer ti raporte. It va afecter solmen qualmen vor servitor comunica con ti lontan conto e tractar su contenete.
add_to_report: Adjunter plu al raporte
are_you_sure: Es tu cert?
assign_to_self: Assignar it a me
@@ -576,6 +590,9 @@ ie:
confirm_action: Confirmar moderatori action contra @%{acct}
created_at: Raportat
delete_and_resolve: Deleter postas
+ forwarded: Reinviat
+ forwarded_replies_explanation: Ti-ci raporte es de un lontan usator e pri lontan contenete. It ha esset reinviat a vos pro que li raportat contenete es un response a un de tui usatores.
+ forwarded_to: Misset anc a %{domain}
mark_as_resolved: Marcar quam soluet
mark_as_sensitive: Marcar quam sensitiv
mark_as_unresolved: Marcar quam ínsoluet
@@ -600,72 +617,161 @@ ie:
skip_to_actions: Ear rect al actiones
status: Statu
statuses: Contenete raportat
+ statuses_description_html: Ofensiv contenete va esser citat in comunication con li raportat conto
summary:
+ action_preambles:
+ delete_html: 'Tu va remover alcun postas de @%{acct}''. To va:'
+ mark_as_sensitive_html: 'Tu va marcar alcun postas de @%{acct} quam sensitiv. To va:'
+ silence_html: 'Tu va limitar li conto de @%{acct}. To va:'
+ suspend_html: 'Tu va suspender li conto de @%{acct}. To va:'
+ actions:
+ delete_html: Remover li ofendent postas
+ mark_as_sensitive_html: Marcar li medie del ofendent postas quam sensitiv
+ silence_html: Severimen limitar li atingement de @%{acct} per far su profil e contenete visibil solmen a persones qui ja seque ilu o qui sercha su profil manualmen
+ suspend_html: Suspender @%{acct}, fante su profil e contenete ínaccessibil e ínpossibil con quel interacter
close_report: 'Marcar raporte #%{id} quam resoluet'
+ close_reports_html: Marcar omni raportes contra @%{acct} quam soluet
+ delete_data_html: Deleter li profil e contenete de @%{acct} 30 dies pos nu, except si ilu es dessuspendet ante tande
+ preview_preamble_html: "@%{acct} va reciver un admoniment con li sequent contenete:"
+ record_strike_html: Registrar un admoniment contra @%{acct} por auxiliar vos tractar futur violationes de ti-ci conto
+ send_email_html: Misser un admoniment-email a @%{acct}
+ warning_placeholder: Ínobligatori additional rason por li moderatori action.
+ target_origin: Orígine del conto raportat
title: Raportes
+ unassign: Ínassignar
+ unknown_action_msg: 'Ínconosset action: %{action}'
+ unresolved: Ínresoluet
+ updated_at: Actualisat
+ view_profile: Vider profil
roles:
+ add_new: Adjunter un rol
+ assigned_users:
+ one: "%{count} usator"
+ other: "%{count} usatores"
categories:
+ administration: Administration
devops: DevOps
invites: Invitationes
moderation: Moderation
special: Special
delete: Deleter
+ description_html: Con roles por usatores, tu posse customisar li functiones e locs de Mastodon in queles tui usatores posse accesser.
+ edit: Modificar rol '%{name}'
+ everyone: Permissiones predefinit
+ permissions_count:
+ one: "%{count} permission"
+ other: "%{count} permissiones"
privileges:
administrator: Administrator
delete_user_data: Deleter Data de Usator
delete_user_data_description: Possibilisa que usatores mey deleter li data de altri usatores strax
invite_users: Invitar Usatores
+ invite_users_description: Permisse que usatores invita novones al servitor
manage_announcements: Tractar proclamationes
manage_announcements_description: Permisse usatores tractar proclamationes sur li servitor
manage_appeals: Gerer Apelles
+ manage_blocks: Gerer Bloccas
+ manage_blocks_description: Permisse que usatores blocca provisores de e-posta e IP-adresses
+ manage_custom_emojis: Gerer Customisat Emojis
+ manage_custom_emojis_description: Permisse que usatores gere customisat emojis sur li servitor
manage_federation: Gerer Federation
+ manage_federation_description: Permisse que usatores sive blocca sive permisse federation con altri domenes, e controla liverabilitá
manage_invites: Gerer Invitationes
manage_reports: Gerer Raportes
manage_roles: Gerer Roles
+ manage_rules: Gerer Regules
+ manage_rules_description: Permisse que usatores changea regules del servitor
+ manage_settings: Gerer Parametres
+ manage_settings_description: Permisse que usatores changea parametres del situ
manage_taxonomies: Gerer Taxonomies
+ manage_user_access: Gerer Usator-Accesse
manage_user_access_description: Permisse usatores desactivisar li 2-factor autentication de altri usatores, changear lor email-adresses, e reiniciar lor passa-paroles
manage_users: Gerer usatores
+ manage_webhooks: Gerer Web-crocs
+ view_audit_log_description: Permisse que usatores vide li historie de administrativ actiones sur li servitor
+ view_devops: DevOps
title: Roles
rules:
add_new: Adjunter un regule
+ delete: Deleter
+ edit: Redacter regul
+ empty: Ancor null regules de servitor ha esset definit.
title: Regules del servitor
settings:
about:
+ manage_rules: Gerer regules de servitor
title: Pri
appearance:
+ preamble: Customisar li interfacie web de Mastodon.
title: Aspecte
discovery:
+ follow_recommendations: Seque-recomandationes
+ preamble: Exposir interessant contenete es importantissim por incorporar nov usatores qui fórsan conosse nequi che Mastodon. Decider qualmen diferent utensiles de decovrition functiona che vor servitor.
profile_directory: Profilarium
public_timelines: Public témpor-lineas
+ publish_discovered_servers: Publicar decovrit servitores
+ publish_statistics: Publicar statisticas
+ title: Decovriment
+ trends: Tendenties
domain_blocks:
+ all: Ad omnes
+ disabled: A necun
users: A local usatores qui ha initiat session
registrations:
+ preamble: Decider qui posse crear un conto che vor servitor.
title: Registrationes
registrations_mode:
modes:
+ approved: Aprobation besonat por adhesion
none: Nequi posse registrar se
open: Quicunc posse registrar se
+ security:
+ authorized_fetch: Postular autentication de federat servitores
+ authorized_fetch_hint: Postular autentication de federat servitores possibilisa plu strict infortiament de ambi usatori e servitori bloccas. Támen, ti fórsan va limitar li potentie de vor servitor, reducter li atingement de vor responses, e possibilmen introducter problemas de compatibilitá con quelc federat servicies. Additionalmen, ti ne va preventer dedicat actores de accesser vor public postas e contos.
+ authorized_fetch_overridden_hint: Tu actualmen ne posse changear ti parametre pro que it es controlat de un environmental variabile.
+ federation_authentication: Infortiation de federational autentication
+ title: Parametres del servitor
site_uploads:
delete: Deleter cargat file
destroyed_msg: Cargat file successosimen deletet!
software_updates:
+ critical_update: Critic — ples actualisar rapidmen
+ description: On recomanda que vu actualisa vor Mastodon-servitor regularimen por profiter del max recent fixes e facultates. In plu, quelcvez it es critic actualisar Mastodon promptmen por evitar problemas de securitá. Pro ti rasones, Mastodon questiona chascun 30 minutes ca hay actualisationes, e va notificar vos secun vor parametres pri email-notificationes.
+ documentation_link: Aprender plu
+ title: Actualisationes disponibil
type: Specie
version: Version
statuses:
account: Autor
+ application: Aplication
+ back_to_account: Retornar al págine del conto
+ back_to_report: Retornar al págine del raporte
batch:
remove_from_report: Remover de raporte
report: Raportar
deleted: Deletet
favourites: Favorites
+ history: Historie de versiones
in_reply_to: Respondent a
language: Lingue
+ media:
+ title: Medie
metadata: Metadata
+ no_status_selected: Null postas esset changeat pro que null esset selectet
open: Aperter posta
original_status: Original posta
+ reblogs: Boosts
+ status_changed: Posta modificat
+ title: Postas del conto
+ trending: Populari
visibility: Visibilitá
+ with_media: Con medie
strikes:
actions:
+ delete_statuses: "%{name} deletet li postas de %{target}"
+ disable: "%{name} gelat li conto de %{target}"
+ mark_statuses_as_sensitive: "%{name} marcat li postas de %{target} quam sensitiv"
+ none: "%{name} misset un admoniment a %{target}"
sensitive: "%{name} marcat li conto de %{target} quam sensitiv"
silence: "%{name} limitat li conto de %{target}"
suspend: "%{name} suspendet li conto de %{target}"
@@ -679,20 +785,81 @@ ie:
action: Vider li documentation
elasticsearch_preset_single_node:
action: Vider li documentation
+ rules_check:
+ action: Gerer regules de servitor
+ software_version_critical_check:
+ message_html: Un critical actualisation por Mastodon es disposibil, ples actualisar tam rapidmen possibil.
+ software_version_patch_check:
+ action: Vider actualisationes disponibil
title: Administration
trends:
+ allow: Permisser
approved: Aprobat
+ disallow: Despermisser
+ links:
+ allow: Permisser ligament
+ allow_provider: Permisser editor
+ description_html: Ci hay ligamentes actualmen partit per mult contos sur queles tui servitor vide postas. It posse auxiliar tui usatores saver to quo eveni in li munde. Null ligamentes es monstrat publicmen ante que tu aproba li publicator. Tu posse anc aprobar o rejecter índividual ligamentes.
+ disallow: Despermisser ligament
+ disallow_provider: Despermisser editor
+ no_link_selected: Null ligamentes esset changeat pro que null esset selectet
+ publishers:
+ no_publisher_selected: Null editores esset changeat pro que necun esset selectet
+ title: Ligamentes in tendentie
+ usage_comparison: Partit %{today} vezes hodie, in comparation a %{yesterday} yer
+ not_allowed_to_trend: Ne permisset esser in tendentie
+ pending_review: Sub inspection
+ preview_card_providers:
+ allowed: Ligamentes de ti-ci editor posse esser in tendentie
+ description_html: Tis es dominias de queles ligamentes es sovente distribuet che vor servitor. Ligamentes ne va intrar li tendenties publicmen except si li dominia del ligament es aprobat. Vor aprobation (o rejection) aplica anc a subdominias.
+ rejected: Ligamentes de ti publicator ne va intrar li tendenties
+ title: Editores
+ rejected: Rejectet
statuses:
+ allow: Permisser posta
+ allow_account: Permisser autor
+ description_html: Tis es postas queles vor servitor conosse queles on actualmen distribue e favoritisa mult. It posse auxiliar vor nov e retornant usatores por trovar plu persones por sequer. Null postas es monstrat publicmen til que vu aproba li autor, e li autor permisse que su conto es suggestet a altres. Vu anc posse permisser o rejecter individual postas.
+ disallow: Despermisser posta
+ disallow_account: Despermisser autor
+ no_status_selected: Null populari postas esset changeat pro que null esset selectet
+ not_discoverable: Autor ne ha consentit esser decovribil
shared_by:
one: Partit o favoritisat un vez
other: Partit e favoritisat %{friendly_count} vezes
+ title: Populari postas
tags:
+ current_score: Actual puntes %{score}
+ dashboard:
+ tag_accounts_measure: unic usationes
+ tag_languages_dimension: Max usat lingues
+ tag_servers_dimension: Max populari servitores
+ tag_servers_measure: diferent servitores
+ tag_uses_measure: total usationes
+ description_html: Vi hashtags actualmen aparient in mult postas queles vor servitor vide. It posse auxiliar vor usatores decovrir pri quel gente parla nu. Null hashtags es monstrat til que vu aproba les.
listable: Suggestibil
+ no_tag_selected: Null hashtags esset changeat pro que null esset selectet
not_listable: Ne suggestibil
+ not_trendable: Ne va aparir in tendenties
not_usable: Prohibit
+ peaked_on_and_decaying: Atinget su cime ye %{date}, nu deperient
+ title: Populari hashtags
+ trendable: Posse aparir in tendenties
+ trending_rank: 'Tendentie #%{rank}'
usable: Permisset
usage_comparison: 'Usat hodie: %{today} vez(es), yer: %{yesterday}'
+ used_by_over_week:
+ one: Usat de un person durant li ultim semane
+ other: Usat de %{count} persones durant li ultim semane
+ title: Tendenties
+ trending: Populari
+ warning_presets:
+ add_new: Adjunter un nov
+ delete: Deleter
+ edit_preset: Modificar prefiguration de avise
+ empty: Vu ancor ha definit null prefigurationes de avise.
+ title: Modificar prefigurationes de avise
webhooks:
+ enabled: Activ
events: Evenimentes
status: Statu
admin_mailer:
@@ -703,6 +870,7 @@ ie:
title: Populari postas
new_trending_tags:
title: Populari hashtags
+ subject: Nov tendenties a inspecter sur %{instance}
aliases:
add_new: Crear alias
created_msg: Successosimen creat un nov alias. Tu nu posse initiar li movement del antiqui conto.
@@ -1115,6 +1283,7 @@ ie:
validations:
images_and_video: On ne posse atachar un video a un posta quel ja contene images
migrations:
+ acct: Translocat a
set_redirect: Configurar un redirection
move_handler:
carry_mutes_over_text: Ti-ci usator movet se de %{acct}, quel tu hat silentiat.
@@ -1142,6 +1311,13 @@ ie:
email_events: Evenimentes por email-notificationes
email_events_hint: 'Selecte li evenimentes pri queles tu vole reciver notificationes:'
other_settings: Parametres pri altri notificationes
+ number:
+ human:
+ decimal_units:
+ units:
+ billion: B
+ million: M
+ thousand: m
otp_authentication:
code_hint: Inmetter li code generat de tui aplication de autentication por confirmar
description_html: Si tu activisa 2-factor autentication per un aplication de autentication, aperter un session va postular que tu have possession de tui telefon, quel va generar codes por que tu mey inmetter les.
@@ -1302,6 +1478,7 @@ ie:
boosted_from_html: Boostat de %{acct_link}
default_language: Sam quam li lingue del interfacie
edited_at_html: Modificat ye %{date}
+ open_in_web: Aperter in web
pin_errors:
direct: On ne posse pinglar postas queles es visibil solmen a mentionat usatores
limit: Tu ja ha pinglat li maxim númere de postas
@@ -1315,16 +1492,30 @@ ie:
one: "%{count} vote"
other: "%{count} votes"
vote: Votar
+ show_more: Monstrar plu
+ show_newer: Monstrar plu nov
+ show_older: Monstrar plu old
title: "%{name}: «%{quote}»"
visibilities:
+ private_long: Monstrar solmen a sequitores
public: Public
+ public_long: Omnes posse vider
unlisted: Delistat
+ unlisted_long: Omnes posse vider, ma ne listat sur public témpor-lineas
statuses_cleanup:
+ enabled: Automaticmen deleter old postas
+ enabled_hint: Deleter automaticmen tui postas quande ili atinge un cert etá, except si ili concorda con un del exceptiones ci infra
exceptions: Exceptiones
+ explanation: Deletion de postas es un operation expensiv, e pro to es efectuat lentmen quande li servitor ne es ocupat. Pro to, on posse deleter tui postas un cert témpor pos atinger un cert etá.
+ ignore_favs: Ignorar favorites
+ interaction_exceptions: Exceptiones basat sur interactiones
+ keep_direct: Retener missages direct
+ keep_direct_hint: Ne delete quelcunc de tui direct missages
keep_pinned: Conservar pinglat postas
keep_pinned_hint: Delete null de tui pinglat postas
keep_polls: Conservar balotationes
keep_polls_hint: Delete null de tui balotationes
+ keep_self_fav: Retener postas favorit de te
min_age:
'1209600': 2 semanes
'31556952': 1 annu
@@ -1404,11 +1595,28 @@ ie:
mark_statuses_as_sensitive: Tui postas che %{acct} ha esset marcat quam sensitiv
none: Admoniment por %{acct}
sensitive: Tui postas che %{acct} ve esser marcat quam sensitiv pos nu
+ title:
+ delete_statuses: Postas efaciat
+ sensitive: Conto marcat quam sensitiv
+ silence: Conto limitat
+ suspend: Conto suspendet
welcome:
+ edit_profile_action: Configuration de profil
explanation: Vi quelc suggestiones por que tu mey comensar
+ final_action: Comensar postar
subject: Benevenit a Mastodon
+ title: Benevenit, %{name}!
users:
seamless_external_login: Tu ha intrat per un servicie external, dunc parametres pri tui passa-parol e email-adresse ne es disponibil.
verification:
extra_instructions_html: 'Nota: Li ligament in tui websitu posse esser ínvisibil. Li important parte es rel="me"
quel prevente fals self-identification in websitus con contenete generat de usatores. Tu posse mem usar un link
element in li cap-section del págine vice a
, ma li HTML code deve esser accessibil sin executer JavaScript.'
+ here_is_how: Vide qualmen
verification: Verification
+ verified_links: Tui verificat ligamentes
+ webauthn_credentials:
+ add: Adjunter nov clave de securitá
+ create:
+ error: Un problema evenit durant li adjuntion de tui clave de securitá. Ples provar denov.
+ success: Tui clave de securitá esset adjuntet con successe.
+ delete: Deleter
+ delete_confirmation: Vole tu vermen deleter ti-ci clave de securitá?
diff --git a/config/locales/lad.yml b/config/locales/lad.yml
index afa6d2930..471b830a3 100644
--- a/config/locales/lad.yml
+++ b/config/locales/lad.yml
@@ -306,6 +306,7 @@ lad:
unpublish: Retirar publikasyon
unpublished_msg: Pregon retirado kon sukseso!
updated_msg: Pregon aktualizado kon sukseso!
+ critical_update_pending: Aktualizasyon kritika esta asperando
custom_emojis:
assign_category: Asinyar kategoria
by_domain: Domeno
@@ -331,6 +332,7 @@ lad:
no_emoji_selected: No se troko dingun emoji porke no eskojites dinguno
not_permitted: No tienes permiso para realizar esta aksyon
overwrite: Sobreskrive
+ shortcode: Kodiche kurto
title: Emojis personalizados
uncategorized: No kategorizado
unlist: No lista
@@ -380,6 +382,7 @@ lad:
confirm_suspension:
cancel: Anula
confirm: Suspende
+ title: Konfirma bloko de domeno para %{domain}
created_msg: El bloko de domeno esta siendo prosesado
destroyed_msg: El bloko de domeno se dezizo
domain: Domeno
@@ -702,6 +705,7 @@ lad:
manage_users: Administra utilizadores
manage_users_description: Permete a los utilizadores ver los peratim de otros utilizadores i realizar aksyones de moderasyon kontra eyos
manage_webhooks: Administrar webhooks
+ view_dashboard: Ve pano
view_devops: DevOps
title: Rolos
rules:
@@ -714,11 +718,19 @@ lad:
manage_rules: Administra reglas del sirvidor
title: Sovre esto
appearance:
+ preamble: Personaliza la enterfaz web de Mastodon.
title: Aparensya
+ branding:
+ title: Marka
+ content_retention:
+ title: Retensyon de kontenido
discovery:
follow_recommendations: Rekomendasyones de kuentos
profile_directory: Katalogo de profiles
public_timelines: Linyas de tiempo publikas
+ publish_discovered_servers: Publika sirvidores diskuviertos
+ publish_statistics: Publika estatistikas
+ title: Diskuvrimiento
trends: Trendes
domain_blocks:
all: A todos
@@ -737,9 +749,13 @@ lad:
delete: Efasa dosya kargada
destroyed_msg: Dosya supremida kon sukseso!
software_updates:
+ critical_update: Kritiko – por favor aktualiza pishin
documentation_link: Ambezate mas
+ release_notes: Notas sovre la versyon
title: Aktualizasyones desponivles
type: Tipo
+ types:
+ major: Versyon prinsipala
version: Versyon
statuses:
account: Autor
@@ -795,6 +811,10 @@ lad:
message_html: No tienes definido dinguna regla del sirvidor.
sidekiq_process_check:
message_html: No ay dingun prosedura Sidekiq en egzekusion para la(s) kola(s) %{value}. Por favor, reviza tu konfigurasyon de Sidekiq
+ software_version_critical_check:
+ action: Amostra aktualizasyones desponivles
+ software_version_patch_check:
+ action: Amostra aktualizasyones desponivles
upload_check_privacy_error:
message_html: "Tu sirvidor de web es mal konfigurado. La privasita de tus utilizadores esta en riziko."
upload_check_privacy_error_object_storage:
@@ -852,7 +872,9 @@ lad:
listable: Pueden ser rekomendadas
no_tag_selected: No se troko dinguna etiketa al no eskojer dinguna
not_listable: No seran rekomendadas
+ not_trendable: No aperesera en trendes
not_usable: No se pueden uzar
+ title: Etiketas en trend
trendable: Pueden apareser en trendes
trending_rank: Trend n.º %{rank}
usable: Pueden uzarse
@@ -901,6 +923,8 @@ lad:
body: "%{target} esta apelando a una solisitasyon de moderasyon de %{action_taken_by} el %{date}, del tipo %{type}. Eyos eskrivieron:"
next_steps: Puedes achetar la apelasyon para dezazer la dechizyon de moderasyon, o ignorarla.
subject: "%{username} esta apelando a una dechizyon de moderasyon en %{instance}"
+ new_critical_software_updates:
+ subject: Ay aktualizasyones kritikas de Mastodon desponivles para %{instance}!
new_pending_account:
body: Los peratim del muevo kuento estan abashos. Puedes achetar o refuzar esta aplikasyon.
subject: Muevo kuento para revizion en %{instance} (%{username})
@@ -908,6 +932,9 @@ lad:
body: "%{reporter} tiene raportado a %{target}"
body_remote: Alguno de %{domain} a raportado a %{target}
subject: Muevo raporto para la %{instance} (#%{id})
+ new_software_updates:
+ body: Ay mueva versyon de Mastodon, kizas keras aktualizar!
+ subject: Ay muevas versyones de Mastodon desponivles para %{instance}!
new_trends:
body: 'Los sigientes elementos nesesitan una revizion antes de ke se puedan amostrar publikamente:'
new_trending_links:
@@ -916,10 +943,20 @@ lad:
title: Publikasyones en trend
new_trending_tags:
title: Etiketas en trend
+ aliases:
+ add_new: Kriya un alias
+ empty: No tienes aliases.
+ remove: Dezata alias
appearance:
advanced_web_interface: Enterfaz web avanzada
+ animations_and_accessibility: Animasyones i aksesivilita
+ confirmation_dialogs: Dialogos de konfirmasyon
+ discovery: Diskuvrimiento
+ localization:
+ guide_link_text: Todos pueden kontribuir.
sensitive_content: Kontenido sensivle
application_mailer:
+ notification_preferences: Troka preferensyas de posta
salutation: "%{name},"
settings: 'Troka preferensyas de posta: %{link}'
unsubscribe: Dezabona
@@ -936,8 +973,13 @@ lad:
your_token: Tu token de akseso
auth:
apply_for_account: Solisita un kuento
+ captcha_confirmation:
+ title: Kontrolo de sigurita
confirmations:
+ awaiting_review_title: Estamos revizando tu enrejistramiento
+ clicking_this_link: klikando en este atadijo
login_link: konektate kon kuento
+ proceed_to_login_html: Agora puedes ir a %{login_link}.
welcome_title: Bienvenido, %{name}!
wrong_email_hint: Si este adreso de posta es inkorekto, puedes trokarlo en las preferensyas del kuento.
delete_account: Efasa kuento
@@ -962,6 +1004,7 @@ lad:
progress:
confirm: Konfirma posta
details: Tus detalyos
+ review: Muestra revizyon
rules: Acheta reglas
providers:
cas: CAS
@@ -975,10 +1018,12 @@ lad:
back: Atras
preamble: Estas son establesidas i aplikadas por los moderadores de %{domain}.
title: Algunas reglas bazikas.
+ title_invited: Fuites envitado.
security: Sigurita
set_new_password: Establese muevo kod
setup:
email_below_hint_html: Mira en tu kuti de spam o solisita de muevo. Si el adreso de posta elektronika ke aparese aki es yerrado, puedes trokarlo aki.
+ link_not_received: No risivites un atadijo?
sign_in:
preamble_html: Konektate kon tus kredensiales de %{domain}. Si tu kuento esta balabayado en otruno servidor, no puedras konektarte aki.
title: Konektate kon %{domain}
@@ -1023,10 +1068,15 @@ lad:
x_seconds: "%{count} s"
deletes:
proceed: Efasa kuento
+ success_msg: Tu kuento fue efasado kon reusho
+ warning:
+ before: 'Antes de kontinuar, por favor melda kon atensyon las sigientes notas:'
+ username_unavailable: Tu nombre de utilizador no estara desponivle
disputes:
strikes:
action_taken: Aksyon tomada
appeal: Apela
+ appeal_rejected: La apelasyon fue refuzada
appeal_submitted_at: Apelasyon embiada
appealed_msg: Tu apelasyon fue embiada. Si la achetamos, se te avizara.
appeals:
@@ -1054,6 +1104,7 @@ lad:
domain_validator:
invalid_domain: no es un nombre de domeno valido
edit_profile:
+ basic_information: Enformasyon bazika
other: Otros
errors:
'400': La solisitasyon ke enviates no fue valida o fue malformada.
@@ -1090,17 +1141,35 @@ lad:
storage: Magazinaje de multimedia
featured_tags:
add_new: Adjusta muevo
+ errors:
+ limit: Tienes alkansado el karar maksimo de etiketas
filters:
contexts:
account: Profiles
+ home: Prinsipyo i listas
notifications: Avizos
public: Linyas de tiempo publikas
+ thread: Konversasyones
edit:
add_keyword: Adjusta biervo yave
keywords: Biervos yaves
statuses: Publikasyones individualas
+ title: Edita filtro
index:
+ contexts: Filtros en %{contexts}
delete: Efasa
+ empty: No tyenes filtros.
+ expires_in: Kaduka en %{distance}
+ expires_on: Kaduka en %{date}
+ keywords:
+ one: "%{count} biervo yave"
+ other: "%{count} biervos yave"
+ statuses:
+ one: "%{count} publikasyon"
+ other: "%{count} publikasyones"
+ statuses_long:
+ one: "%{count} publikasyon individuala eskondida"
+ other: "%{count} publikasyones individualas eskondidas"
title: Filtros
new:
save: Guadra muevo filtro
@@ -1109,17 +1178,23 @@ lad:
back_to_filter: Retorna al filtro
batch:
remove: Kita del filtro
+ index:
+ title: Publikasyones filtradas
generic:
all: Todos
cancel: Anula
+ changes_saved_msg: Trokamientos guadrados kon reusho!
confirm: Konfirma
copy: Kopia
delete: Efasa
+ deselect: Deseleksyonar todo
none: Dinguno
+ order_by: Ordena por
save_changes: Guadra trokamientos
today: oy
imports:
errors:
+ empty: Dosya CSV vaziya
invalid_csv_file: 'Dosya CSV no valida. Yerro: %{error}'
over_rows_processing_limit: kontiene mas de %{count} filas
too_large: Dosya es mas grande
@@ -1130,19 +1205,36 @@ lad:
overwrite: Sobreskrive
overwrite_long: Mete muevos rejistros en vez de los aktuales
preface: Puedes importar siertos datos, komo todas las personas a las kualas estas sigiendo o blokando en tu kuento en esta instansya, dizde dosyas eksportadas de otra instansya.
+ recent_imports: Importasyones resyentes
states:
finished: Finalizado
scheduled: Programado
unconfirmed: Sin konfirmasyon
status: Estado
success: Tus datos se tienen kargado djustamente i seran prosesados pishin
+ time_started: Ampesado el
+ titles:
+ blocking: Importando kuentos blokados
+ bookmarks: Importando markadores
+ domain_blocking: Importando domenos blokados
+ following: Importando kuentos segidos
+ lists: Importando listas
+ muting: Importando kuentos silensyados
+ type: Tipo de importasyon
+ type_groups:
+ constructive: Segidores i markadores
+ destructive: Blokos i silensyos
types:
blocking: Lista de blokos
bookmarks: Markadores
+ domain_blocking: Lista de domenos blokados
+ following: Lista de segidos
lists: Listas
+ muting: Lista de silensyados
upload: Karga
invites:
delete: Dezaktiva
+ expired: Kadukado
expires_in:
'1800': 30 minutos
'21600': 6 oras
@@ -1151,6 +1243,9 @@ lad:
'604800': 1 semana
'86400': 1 diya
expires_in_prompt: Nunkua
+ generate: Djenera un atadijo de envitasyon
+ invalid: Esta envitasyon no es valida
+ invited_by: 'Fuites envitado por:'
max_uses:
one: 1 uzo
other: "%{count} uzos"
@@ -1175,7 +1270,15 @@ lad:
title: Estoria de autentifikasyon
mail_subscriptions:
unsubscribe:
+ action: Si, dezabona
complete: Dezabonado
+ emails:
+ notification_emails:
+ favourite: avizos de favoritos por posta
+ follow: avizos de segidores por posta
+ follow_request: avizos de solisitasyones de segimyento por posta
+ mention: avizos de enmentaduras por posta
+ reblog: avizos de repartajasyones por posta
title: Dezabona
media_attachments:
validations:
@@ -1214,12 +1317,43 @@ lad:
moderation:
title: Moderasyon
notification_mailer:
+ admin:
+ report:
+ subject: "%{name} embio un raporto"
+ sign_up:
+ subject: "%{name} se enrejistro"
+ favourite:
+ body: 'Tu publikasyon fue favoritada por %{name}:'
+ subject: A %{name} le plaze tu publikasyon
+ title: Muevo favorito
follow:
+ body: "%{name} te esta sigiendo!"
+ subject: "%{name} te esta sigiendo"
title: Muevo suivante
follow_request:
+ action: Administra solisitudes de segimiento
+ body: "%{name} tiene solisitado segirte"
+ subject: 'Segidor esta asperando: %{name}'
title: Mueva solisitud de segimiento
mention:
action: Arisponde
+ body: 'Fuites enmentado por %{name} en:'
+ subject: Fuites enmentado por %{name}
+ title: Mueva enmentadura
+ poll:
+ subject: Una anketa de %{name} eskapo
+ reblog:
+ body: 'Tu publikasyon fue repartajada por %{name}:'
+ subject: "%{name} repartajo tu publikasyon"
+ title: Mueva repartajasyon
+ status:
+ subject: "%{name} publiko algo"
+ update:
+ subject: "%{name} edito una publikasyon"
+ notifications:
+ administration_emails: Avizos de administrasyon por posta
+ email_events: Evenimyentos para avizos por posta
+ other_settings: Otras preferensyas de avizos
number:
human:
decimal_units:
@@ -1227,7 +1361,9 @@ lad:
units:
billion: MM
million: M
+ quadrillion: Ku
thousand: K
+ trillion: T
otp_authentication:
enable: Kapasita
instructions_html: "Eskanea este kodiche QR dizde Google Authenticator o una aplikasyon similar en tu telefon. A partir de agora, esta aplikasyon djenerara kodiches ke tendras ke ingresar kuando keras konektarte kon tu kuento."
@@ -1249,6 +1385,7 @@ lad:
expired: La anketa ya tiene eskapado
invalid_choice: La opsyon de voto eskojida no egziste
over_character_limit: no puede trespasar %{max} karakteres kada uno
+ self_vote: No puedes votar en tus propias anketas
too_few_options: deve tener mas de un elemento
too_many_options: no puede kontener mas de %{max} elementos
preferences:
@@ -1281,11 +1418,19 @@ lad:
mutual: Mutual
primary: Prinsipal
relationship: Relasyon
+ remove_selected_followers: Kita a los suivantes eskojidos
+ remove_selected_follows: Desige a los utilizadores eskojidos
status: Estado del kuento
rss:
content_warning: 'Avertensya de kontenido:'
+ descriptions:
+ account: Publikasyones publikas de @%{acct}
+ tag: 'Publikasyones publikas etiketadas kon #%{hashtag}'
+ self_destruct:
+ title: Este sirvidor esta serrando
sessions:
activity: Ultima aktivita
+ browser: Navigador
browsers:
alipay: Alipay
blackberry: BlackBerry
@@ -1293,6 +1438,7 @@ lad:
edge: Microsoft Edge
electron: Electron
firefox: Firefox
+ generic: Navigador deskonosido
huawei_browser: Huawei Browser
ie: Internet Explorer
micro_messenger: MicroMessenger
@@ -1303,8 +1449,10 @@ lad:
qq: QQ Browser
safari: Safari
uc_browser: UC Browser
+ unknown_browser: Navigador deskonosido
weibo: Weibo
current_session: Sesyon aktuala
+ description: "%{browser} en %{platform}"
ip: IP
platforms:
adobe_air: Adobe Air
@@ -1316,6 +1464,7 @@ lad:
kai_os: KaiOS
linux: Linux
mac: macOS
+ unknown_platform: Platforma deskonesida
windows: Windows
windows_mobile: Windows Mobile
windows_phone: Windows Phone
@@ -1373,10 +1522,44 @@ lad:
direct: Las publikasyones ke son vizivles solo para los utilizadores enmentados no pueden fiksarse
limit: Ya tienes fiksado el numero maksimo de publikasyones
ownership: La publikasyon de otra persona no puede fiksarse
+ reblog: No se puede fixar una repartajasyon
poll:
+ total_people:
+ one: "%{count} persona"
+ other: "%{count} personas"
+ total_votes:
+ one: "%{count} voto"
+ other: "%{count} votos"
vote: Vota
show_more: Amostra mas
+ show_newer: Amostra mas muevos
+ show_older: Amostra mas viejos
+ show_thread: Amostra diskusyon
+ title: '%{name}: "%{quote}"'
+ visibilities:
+ direct: Direkto
+ private: Solo suivantes
+ private_long: Solo amostra a tus segidores
+ public: Publiko
+ public_long: Todos pueden ver
+ unlisted: No listado
statuses_cleanup:
+ enabled: Otomatikamente efasa publikasyones viejas
+ exceptions: Eksepsiones
+ ignore_favs: Ignora favoritos
+ ignore_reblogs: Ignora repartajasyones
+ keep_direct: Manten enmentaduras privadas
+ keep_direct_hint: No efasa dingunos mesajes privados
+ keep_media: Manten publikasyones kon atamientos
+ keep_media_hint: No efasa tus publikasyones kon atamientos de multimedia
+ keep_pinned: Manten publikasyones fiksadas
+ keep_pinned_hint: No efasa tus publikasyones fiksadas
+ keep_polls: Manten anketas
+ keep_polls_hint: No efasa tus anketas
+ keep_self_bookmark: Manten publikasyones kon markadores
+ keep_self_bookmark_hint: No efasa tus publikasyones kon tus markadores
+ keep_self_fav: Manten publikasyones ke te plazen
+ keep_self_fav_hint: No efasa las tus publikasyones kualas markates komo favoritas
min_age:
'1209600': 2 semanas
'15778476': 6 mezes
@@ -1386,6 +1569,8 @@ lad:
'604800': 1 semana
'63113904': 2 anyos
'7889238': 3 mezes
+ min_favs: Manten publikasyones favoritadas a lo manko
+ min_reblogs: Manten publikasyones repartajadas a lo manko
stream_entries:
sensitive_content: Kontenido sensivle
strikes:
@@ -1451,17 +1636,43 @@ lad:
mark_statuses_as_sensitive: Algunas de tus publikasyones an sido markados komo sensivles por los moderadores de %{instance}. Esto sinyifika ke la djente tendra ke pulsar los dosyas multimedia en las publikasyones antes de ke se amostre una vista previa. Puedes markar los dosyas multimedia komo sensivles tu mezmo kuando publikes en el avenir.
sensitive: A partir de agora todas los dosyas multimedia ke subas seran markados komo sensivles i eskondidos tras una avertensya.
reason: 'Razon:'
+ statuses: 'Publikasyones relevantes:'
+ subject:
+ delete_statuses: Tus publikasyones en %{acct} tienen sido efasadas
+ disable: Tu kuento %{acct} tiene sido konjelado
+ mark_statuses_as_sensitive: Tus publikasyones en %{acct} tienen sido markadas komo sensivles
+ none: Avertensya para %{acct}
+ sensitive: Tus publikasyones en %{acct} seran markadas komo sensivles dizde agora
+ silence: Tu kuento %{acct} tiene sido limitado
+ suspend: Tu kuento %{acct} tiene sido suspendido
title:
+ delete_statuses: Publikasyones efasadas
+ disable: Kuento konjelado
+ mark_statuses_as_sensitive: Publikasyones markadas komo sensivles
none: Avertensya
+ sensitive: Kuento markado komo sensivle
silence: Kuento limitado
suspend: Kuento suspendido
+ welcome:
+ edit_profile_action: Konfigurasyon de profil
+ final_action: Ampesa a publikar
+ subject: Bienvenido a Mastodon
+ title: Bienvenido, %{name}!
users:
+ follow_limit_reached: No puedes segir a mas de %{limit} personas
+ invalid_otp_token: Kodiche de dos pasos no valido
signed_in_as: 'Konektado komo:'
verification:
verification: Verifikasyon
+ verified_links: Tus atadijos verifikados
webauthn_credentials:
add: Adjusta mueva yave de sigurita
+ create:
+ success: Tu yave de sigurita fue adjustada kon sukseso.
delete: Efasa
+ delete_confirmation: Estas siguro ke keres efasar esta yave de sigurita?
+ destroy:
+ success: Tu yave de sigurita fue efasada kon sukseso.
invalid_credential: Yave de sigurita no valida
nickname_hint: Introduska el sovrenombre de tu mueva yave de sigurita
not_enabled: Ainda no tienes aktivado WebAuthn
diff --git a/config/locales/simple_form.ie.yml b/config/locales/simple_form.ie.yml
index a94903b66..672ff9d00 100644
--- a/config/locales/simple_form.ie.yml
+++ b/config/locales/simple_form.ie.yml
@@ -4,6 +4,9 @@ ie:
hints:
account:
unlocked: Persones va posser sequer te sin petir aprobation. Desselecte si tu vole manualmen tractar petitiones de sequer e decider ca acceptar o rejecter nov sequitores.
+ account_warning_preset:
+ text: Tu posse usar posta-sintaxe, quam URLs, hashtags e mentiones
+ title: Ínobligatori. Ne visibil al recipiente
admin_account_action:
send_email_notification: Li usator va reciver un explication de ti quel evenit con su conto
text_html: Ínobligatori. Tu posse usar posta-sintaxe. Tu posse adjunter preconfigurationes de avises por sparar témpor
@@ -47,19 +50,31 @@ ie:
setting_display_media_show_all: Sempre monstrar medie
form_admin_settings:
bootstrap_timeline_accounts: Ti-ci contos va esser pinglat al parte superiori del recomandationes por nov usatores.
+ site_contact_username: Qualmen li gente posse atinger te sur Mastodon.
theme: Li dessine quel ínregistrat visitantes e nov usatores vide.
timeline_preview: Ínregistrat visitantes va posser vider li max recent public postas disponibil che li servitor.
trends_as_landing_page: Monstrar populari contenete a ínregistrat visitantes vice un description del servitor. Besona que tendenties es activisat.
form_challenge:
current_password: Tu nu intra un area secur
+ ip_block:
+ severities:
+ sign_up_block: Nov registrationes ne va esser possibil
+ sign_up_requires_approval: Nov registrationes va besonar tui aprobation
+ webhook:
+ events: Selecter evenimentes a misser
+ url: Ad u misser evenimentes
labels:
account:
fields:
value: Contenete
+ indexable: Includer public postas in resultates de sercha
account_warning_preset:
+ text: Textu prefigurat
title: Titul
admin_account_action:
+ send_email_notification: Notificar li usator per e-posta
type: Action
+ warning_preset_id: Usar un prefiguration de avise
announcement:
all_day: Eveniment del tot die
ends_at: Fine del eveniment
@@ -67,15 +82,37 @@ ie:
starts_at: Comense del eveniment
text: Proclamation
defaults:
+ chosen_languages: Filtrar lingues
confirm_new_password: Confirmar nov passa-parol
confirm_password: Confirmar passa-parol
+ context: Filtrar contextus
current_password: Actual passa-parol
+ data: Data
+ display_name: Nómine a monstrar
+ email: E-posta
+ expires_in: Expirar pos
honeypot: "%{label} (ne plenar)"
locale: Lingue del interfacie
new_password: Nov passa-parol
note: Biografie
password: Passa-parol
+ setting_default_language: Lingue in quel postar
+ setting_default_privacy: Privatie de postada
+ setting_default_sensitive: Sempre marcar medie quam sensitiv
+ setting_display_media_default: Predefinitiones
+ setting_display_media_hide_all: Celar omno
+ setting_display_media_show_all: Monstrar omno
+ setting_theme: Tema de situ
+ setting_trends: Monstrar li hodial tendenties
+ setting_unfollow_modal: Monstrar dialog de confirmation ante dessequer alquem
+ setting_use_pending_items: Mode lent
form_admin_settings:
registrations_mode: Qui posse registrar se
+ theme: Predefenit tema
+ trends: Possibilisar tendenties
+ trends_as_landing_page: Usar tendenties quam frontispicie
notification_emails:
follow_request: Alqui petit sequer te
+ trending_tag: Nov tendentie besonant inspection
+ tag:
+ trendable: Permisse que ti-ci hashtag apari sub tendenties
diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml
index 9ad432350..731379835 100644
--- a/config/locales/simple_form.it.yml
+++ b/config/locales/simple_form.it.yml
@@ -6,7 +6,7 @@ it:
discoverable: I tuoi post pubblici e il tuo profilo potrebbero essere presenti o consigliati in varie aree di Mastodon e il tuo profilo potrebbe essere suggerito ad altri utenti.
display_name: Il tuo nome completo o il tuo soprannome.
fields: La tua homepage, i pronomi, l'età, tutto quello che vuoi.
- indexable: I tuoi post pubblici potrebbero apparire nei risultati di ricerca su Mastodon. Le persone che hanno interagito con i tuoi post potrebbero essere in grado di cercarli indipendentemente.
+ indexable: I tuoi post pubblici potrebbero apparire nei risultati di ricerca su Mastodon. Le persone che hanno interagito con i tuoi post potrebbero essere in grado di cercarli anche se non hai attivato questa impostazione.
note: 'Puoi @menzionare altre persone o usare gli #hashtags.'
show_collections: Le persone saranno in grado di navigare attraverso i tuoi seguaci e seguaci. Le persone che segui vedranno che li seguirai indipendentemente dalle tue impostazioni.
unlocked: Le persone saranno in grado di seguirti senza richiedere l'approvazione. Deseleziona se vuoi controllare le richieste di seguirti e scegli se accettare o rifiutare nuovi follower.
@@ -122,7 +122,7 @@ it:
webauthn: Se si tratta di una chiavetta USB assicurati di inserirla e, se necessario, toccarla.
settings:
indexable: La pagina del tuo profilo potrebbe apparire nei risultati di ricerca su Google, Bing e altri.
- show_application: Sarai sempre in grado di vedere quale app ha pubblicato il tuo post indipendentemente.
+ show_application: Tu sarai sempre in grado di vedere quale app ha pubblicato il tuo post anche se hai attivato questa impostazione.
tag:
name: Puoi cambiare solo il minuscolo/maiuscolo delle lettere, ad esempio, per renderlo più leggibile
user:
diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml
index 33fe877db..aeac5f1c5 100644
--- a/config/locales/simple_form.lad.yml
+++ b/config/locales/simple_form.lad.yml
@@ -4,6 +4,9 @@ lad:
hints:
account:
display_name: Tu nombre para amostrar.
+ fields: Tu deskripsyon, pronombres, edad, todo lo ke keras.
+ note: 'Puedes @enmentar a otra djente o #etiketas.'
+ show_collections: Otra djente podra ver tus segidos i suivantes. Personas a las kualas siges siempre podran ver que las estas sigiendo.
account_alias:
acct: Espesifika tu nombre de utilizador@domeno del kuento de ande keres migrar
account_migration:
@@ -55,6 +58,7 @@ lad:
setting_display_media_show_all: Amostra siempre el kontenido de multimedia
setting_use_blurhash: Los gradientes se bazan en los kolores de las imajes eskondidas pero faziendo velados los peratim
setting_use_pending_items: Eskonde muevas publikasyones detras de un klik en lugar de desplazar otomatikamente la linya
+ username: Solo puedes uzar letras, shifras i sulinyados
whole_word: Kuando el biervo yave o fraza es solo alfanumerika, solo sera aplikado si konkorda kon todo el biervo
domain_allow:
domain: Este domeno podra obtener datos de este sirvidor i los datos entrantes seran prosesados i archivados
@@ -113,6 +117,8 @@ lad:
sessions:
otp: 'Introduse el kodiche de autentifikasyon de dos pasos djenerado por tu aplikasyon de telefon o uza uno de tus kodiches de recuperasyon:'
webauthn: Si es una yave USB, asigurete de insertarla y, si es necesario, pulsala.
+ settings:
+ show_application: Tu siempre podras ver dizde kuala aplikasyon publikates tu publikasyon.
tag:
name: Solo se puede trokar la kapitalizasyon de las letras, por enshemplo, para ke sea mas meldable
user:
@@ -132,6 +138,9 @@ lad:
fields:
name: Etiketa
value: Kontenido
+ indexable: Inkluye publikasyones publikas en los rezultados de bushkeda
+ show_collections: Amostra tus segidos i suivantes en el profil
+ unlocked: Otomatikamente acheta muevos suivantes
account_alias:
acct: Alias del kuento viejo
account_migration:
@@ -168,19 +177,30 @@ lad:
confirm_password: Konfirma kod
context: Filtra kontekstos
current_password: Kod aktual
+ data: Datos
display_name: Nombre amostrado
email: Adreso de posta elektronika
expires_in: Kaduka dempues de
+ fields: Datos adisyonales
header: Imaje de kavesera
+ locale: Lingua de enterfaz
+ max_uses: Maksimo numero de uzos
new_password: Muevo kod
note: Deskripsyon
otp_attempt: Kodiche de dos pasos
password: Kod
+ phrase: Biervo yave o fraza
+ setting_aggregate_reblogs: Agrupa repartajasyones en linyas
+ setting_always_send_emails: Siempre embia avizos por posta
+ setting_auto_play_gif: Siempre reproduse los GIFs animados
setting_default_language: Lingua de publikasyones
setting_default_privacy: Privasita de publikasyones
setting_display_media_default: Predeterminado
setting_display_media_hide_all: Eskonde todo
setting_display_media_show_all: Amostra todo
+ setting_reduce_motion: Reduse el movimyento en animasyones
+ setting_system_font_ui: Uza el font predeterminado del sistem
+ setting_trends: Amostra los trendes de oy
setting_use_blurhash: Amostra gradientes koloridos para kontenido multimedia eskondido
setting_use_pending_items: Modo lento
severity: Severita
@@ -243,12 +263,23 @@ lad:
sign_up_requires_approval: Limita enrejistrasyones
severity: Regla
notification_emails:
+ favourite: A alguno le plaze tu publikasyon
follow: Alguno te ampeso a segir
follow_request: Alguno tiene solisitado segirte
mention: Alguno te enmento
+ pending_account: Muevo kuento nesesita revizyon
reblog: Alguno repartajo tu publikasyon
+ report: Muevo raporto fue embiado
+ software_updates:
+ all: Avizame de kada aktualizasyon
+ critical: Avizame solo de aktualizasyones kritikas
+ label: Mueva version de Mastodon esta desponivle
+ none: Nunkua avizame de aktualizasyones (no rekomendado)
+ patch: Avizame de aktualizasyones de yerros
rule:
text: Regla
+ settings:
+ indexable: Inkluye la pajina de profil en los bushkadores
tag:
name: Etiketa
user:
@@ -259,8 +290,11 @@ lad:
position: Priorita
webhook:
events: Evenimientos kapasitados
+ url: URL de Endpoint
'no': 'No'
+ not_recommended: No rekomendado
recommended: Rekomendado
required:
mark: "*"
+ text: rekerido
'yes': Si
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index 098719eb7..568607e70 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -1272,7 +1272,7 @@ tr:
remove: Filtreden kaldır
index:
hint: Bu filtre diğer ölçütlerden bağımsız olarak tekil gönderileri seçmek için uygulanıyor. Web arayüzünü kullanarak bu filtreye daha fazla gönderi ekleyebilirsiniz.
- title: Filtrelenmiş gönderiler
+ title: Süzgeçlenmiş gönderiler
generic:
all: Tümü
all_items_on_page_selected_html:
diff --git a/config/routes/api.rb b/config/routes/api.rb
index ebfa60a69..0fe9f69ab 100644
--- a/config/routes/api.rb
+++ b/config/routes/api.rb
@@ -301,10 +301,6 @@ namespace :api, format: false do
resources :statuses, only: [:show, :destroy]
end
- namespace :accounts do
- resources :relationships, only: :index
- end
-
namespace :admin do
resources :accounts, only: [:index]
end
diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb
index 98067c6e3..7b3a9852a 100644
--- a/lib/mastodon/cli/maintenance.rb
+++ b/lib/mastodon/cli/maintenance.rb
@@ -40,6 +40,10 @@ module Mastodon::CLI
class BulkImport < ApplicationRecord; end
class SoftwareUpdate < ApplicationRecord; end
+ class DomainBlock < ApplicationRecord
+ scope :by_severity, -> { order(Arel.sql('(CASE severity WHEN 0 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 0 END), domain')) }
+ end
+
class PreviewCard < ApplicationRecord
self.inheritance_column = false
end
@@ -249,19 +253,7 @@ module Mastodon::CLI
say 'Deduplicating user records…'
- # Deduplicating email
- ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users GROUP BY email HAVING count(*) > 1").each do |row|
- users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse
- ref_user = users.shift
- say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow
- say "e-mail will be disabled for the following accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow
- say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow
-
- users.each_with_index do |user, index|
- user.update!(email: "#{index} " + user.email)
- end
- end
-
+ deduplicate_users_process_email
deduplicate_users_process_confirmation_token
deduplicate_users_process_remember_token
deduplicate_users_process_password_token
@@ -280,6 +272,20 @@ module Mastodon::CLI
ActiveRecord::Base.connection.execute('REINDEX INDEX index_users_on_unconfirmed_email;') if migrator_version >= 2023_07_02_151753
end
+ def deduplicate_users_process_email
+ ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users GROUP BY email HAVING count(*) > 1").each do |row|
+ users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse
+ ref_user = users.shift
+ say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow
+ say "e-mail will be disabled for the following accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow
+ say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow
+
+ users.each_with_index do |user, index|
+ user.update!(email: "#{index} " + user.email)
+ end
+ end
+ end
+
def deduplicate_users_process_confirmation_token
ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE confirmation_token IS NOT NULL GROUP BY confirmation_token HAVING count(*) > 1").each do |row|
users = User.where(id: row['ids'].split(',')).sort_by(&:created_at).reverse.drop(1)
@@ -571,7 +577,7 @@ module Mastodon::CLI
say 'Deduplicating webhooks…'
ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM webhooks GROUP BY url HAVING count(*) > 1").each do |row|
- Webhooks.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
+ Webhook.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
end
say 'Restoring webhooks indexes…'
@@ -604,11 +610,7 @@ module Mastodon::CLI
say 'Please chose the one to keep unchanged, other ones will be automatically renamed.'
- ref_id = ask('Account to keep unchanged:') do |q|
- q.required true
- q.default 0
- q.convert :int
- end
+ ref_id = ask('Account to keep unchanged:', required: true, default: 0).to_i
accounts.delete_at(ref_id)
diff --git a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
index 0e4fa9301..df71e94ac 100644
--- a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
@@ -10,11 +10,11 @@ describe Api::V1::Accounts::StatusesController do
before do
allow(controller).to receive(:doorkeeper_token) { token }
- Fabricate(:status, account: user.account)
end
describe 'GET #index' do
it 'returns expected headers', :aggregate_failures do
+ Fabricate(:status, account: user.account)
get :index, params: { account_id: user.account.id, limit: 1 }
expect(response).to have_http_status(200)
@@ -30,7 +30,6 @@ describe Api::V1::Accounts::StatusesController do
end
context 'with exclude replies' do
- let!(:older_statuses) { user.account.statuses.destroy_all }
let!(:status) { Fabricate(:status, account: user.account) }
let!(:status_self_reply) { Fabricate(:status, account: user.account, thread: status) }
diff --git a/spec/controllers/api/v1/filters_controller_spec.rb b/spec/controllers/api/v1/filters_controller_spec.rb
index 8d5408cf5..b0f64ccf4 100644
--- a/spec/controllers/api/v1/filters_controller_spec.rb
+++ b/spec/controllers/api/v1/filters_controller_spec.rb
@@ -15,10 +15,15 @@ RSpec.describe Api::V1::FiltersController do
describe 'GET #index' do
let(:scopes) { 'read:filters' }
let!(:filter) { Fabricate(:custom_filter, account: user.account) }
+ let!(:custom_filter_keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
it 'returns http success' do
get :index
expect(response).to have_http_status(200)
+ expect(body_as_json)
+ .to contain_exactly(
+ include(id: custom_filter_keyword.id.to_s)
+ )
end
end
diff --git a/spec/controllers/api/v2/admin/accounts_controller_spec.rb b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
index 18b395014..349f4d252 100644
--- a/spec/controllers/api/v2/admin/accounts_controller_spec.rb
+++ b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
@@ -34,28 +34,56 @@ RSpec.describe Api::V2::Admin::AccountsController do
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
- [
- [{ status: 'active', origin: 'local', permissions: 'staff' }, [:admin_account]],
- [{ by_domain: 'example.org', origin: 'remote' }, [:remote_account]],
- [{ status: 'suspended' }, [:suspended_remote, :suspended_account]],
- [{ status: 'disabled' }, [:disabled_account]],
- [{ status: 'pending' }, [:pending_account]],
- ].each do |params, expected_results|
- context "when called with #{params.inspect}" do
- let(:params) { params }
+ context 'when called with status active and origin local and permissions staff' do
+ let(:params) { { status: 'active', origin: 'local', permissions: 'staff' } }
- it "returns the correct accounts (#{expected_results.inspect})" do
- expect(response).to have_http_status(200)
-
- expect(body_json_ids).to eq(expected_results.map { |symbol| send(symbol).id })
- end
-
- def body_json_ids
- body_as_json.map { |a| a[:id].to_i }
- end
+ it 'returns the correct accounts' do
+ expect(response).to have_http_status(200)
+ expect(body_json_ids).to eq([admin_account.id])
end
end
+ context 'when called with by_domain value and origin remote' do
+ let(:params) { { by_domain: 'example.org', origin: 'remote' } }
+
+ it 'returns the correct accounts' do
+ expect(response).to have_http_status(200)
+ expect(body_json_ids).to include(remote_account.id)
+ expect(body_json_ids).to_not include(other_remote_account.id)
+ end
+ end
+
+ context 'when called with status suspended' do
+ let(:params) { { status: 'suspended' } }
+
+ it 'returns the correct accounts' do
+ expect(response).to have_http_status(200)
+ expect(body_json_ids).to include(suspended_remote.id, suspended_account.id)
+ end
+ end
+
+ context 'when called with status disabled' do
+ let(:params) { { status: 'disabled' } }
+
+ it 'returns the correct accounts' do
+ expect(response).to have_http_status(200)
+ expect(body_json_ids).to include(disabled_account.id)
+ end
+ end
+
+ context 'when called with status pending' do
+ let(:params) { { status: 'pending' } }
+
+ it 'returns the correct accounts' do
+ expect(response).to have_http_status(200)
+ expect(body_json_ids).to include(pending_account.id)
+ end
+ end
+
+ def body_json_ids
+ body_as_json.map { |a| a[:id].to_i }
+ end
+
context 'with limit param' do
let(:params) { { limit: 1 } }
diff --git a/spec/controllers/api/v2/filters/keywords_controller_spec.rb b/spec/controllers/api/v2/filters/keywords_controller_spec.rb
index 5321f787a..9f25237bc 100644
--- a/spec/controllers/api/v2/filters/keywords_controller_spec.rb
+++ b/spec/controllers/api/v2/filters/keywords_controller_spec.rb
@@ -22,6 +22,10 @@ RSpec.describe Api::V2::Filters::KeywordsController do
it 'returns http success' do
get :index, params: { filter_id: filter.id }
expect(response).to have_http_status(200)
+ expect(body_as_json)
+ .to contain_exactly(
+ include(id: keyword.id.to_s)
+ )
end
context "when trying to access another's user filters" do
diff --git a/spec/controllers/api/v2/filters/statuses_controller_spec.rb b/spec/controllers/api/v2/filters/statuses_controller_spec.rb
index 5c2a62395..d9df80397 100644
--- a/spec/controllers/api/v2/filters/statuses_controller_spec.rb
+++ b/spec/controllers/api/v2/filters/statuses_controller_spec.rb
@@ -22,6 +22,10 @@ RSpec.describe Api::V2::Filters::StatusesController do
it 'returns http success' do
get :index, params: { filter_id: filter.id }
expect(response).to have_http_status(200)
+ expect(body_as_json)
+ .to contain_exactly(
+ include(id: status_filter.id.to_s)
+ )
end
context "when trying to access another's user filters" do
diff --git a/spec/controllers/auth/confirmations_controller_spec.rb b/spec/controllers/auth/confirmations_controller_spec.rb
index 58bc38f54..15403e8ea 100644
--- a/spec/controllers/auth/confirmations_controller_spec.rb
+++ b/spec/controllers/auth/confirmations_controller_spec.rb
@@ -41,8 +41,9 @@ describe Auth::ConfirmationsController do
get :show, params: { confirmation_token: 'foobar' }
end
- it 'redirects to login' do
+ it 'redirects to login and confirms user' do
expect(response).to redirect_to(new_user_session_path)
+ expect(user.reload.confirmed_at).to_not be_nil
end
end
@@ -87,8 +88,9 @@ describe Auth::ConfirmationsController do
get :show, params: { confirmation_token: 'foobar' }
end
- it 'redirects to login' do
+ it 'redirects to login and confirms email' do
expect(response).to redirect_to(new_user_session_path)
+ expect(user.reload.unconfirmed_email).to be_nil
end
it 'does not queue up bootstrapping of home timeline' do
diff --git a/spec/controllers/auth/passwords_controller_spec.rb b/spec/controllers/auth/passwords_controller_spec.rb
index e7f7ab467..d70490abc 100644
--- a/spec/controllers/auth/passwords_controller_spec.rb
+++ b/spec/controllers/auth/passwords_controller_spec.rb
@@ -70,6 +70,7 @@ describe Auth::PasswordsController do
it 'deactivates all sessions' do
expect(user.session_activations.count).to eq 0
+ expect { session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'revokes all access tokens' do
@@ -78,6 +79,7 @@ describe Auth::PasswordsController do
it 'removes push subscriptions' do
expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0
+ expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb
index f341d75b7..212cc4d5e 100644
--- a/spec/controllers/auth/sessions_controller_spec.rb
+++ b/spec/controllers/auth/sessions_controller_spec.rb
@@ -123,9 +123,8 @@ RSpec.describe Auth::SessionsController do
let(:previous_ip) { '1.2.3.4' }
let(:current_ip) { '4.3.2.1' }
- let!(:previous_login) { Fabricate(:login_activity, user: user, ip: previous_ip) }
-
before do
+ Fabricate(:login_activity, user: user, ip: previous_ip)
allow(controller.request).to receive(:remote_ip).and_return(current_ip)
user.update(current_sign_in_at: 1.month.ago)
post :create, params: { user: { email: user.email, password: user.password } }
@@ -328,12 +327,6 @@ RSpec.describe Auth::SessionsController do
Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
end
- let!(:recovery_codes) do
- codes = user.generate_otp_backup_codes!
- user.save
- return codes
- end
-
let!(:webauthn_credential) do
user.update(webauthn_id: WebAuthn.generate_user_id)
public_key_credential = WebAuthn::Credential.from_create(fake_client.create)
@@ -356,6 +349,11 @@ RSpec.describe Auth::SessionsController do
let(:fake_credential) { fake_client.get(challenge: challenge, sign_count: sign_count) }
+ before do
+ user.generate_otp_backup_codes!
+ user.save
+ end
+
context 'when using email and password' do
before do
post :create, params: { user: { email: user.email, password: user.password } }
diff --git a/spec/controllers/concerns/signature_verification_spec.rb b/spec/controllers/concerns/signature_verification_spec.rb
deleted file mode 100644
index 650cd21ea..000000000
--- a/spec/controllers/concerns/signature_verification_spec.rb
+++ /dev/null
@@ -1,305 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe SignatureVerification do
- let(:wrapped_actor_class) do
- Class.new do
- attr_reader :wrapped_account
-
- def initialize(wrapped_account)
- @wrapped_account = wrapped_account
- end
-
- delegate :uri, :keypair, to: :wrapped_account
- end
- end
-
- controller(ApplicationController) do
- include SignatureVerification
-
- before_action :require_actor_signature!, only: [:signature_required]
-
- def success
- head 200
- end
-
- def alternative_success
- head 200
- end
-
- def signature_required
- head 200
- end
- end
-
- before do
- routes.draw do
- match :via => [:get, :post], 'success' => 'anonymous#success'
- match :via => [:get, :post], 'signature_required' => 'anonymous#signature_required'
- end
- end
-
- context 'without signature header' do
- before do
- get :success
- end
-
- describe '#signed_request?' do
- it 'returns false' do
- expect(controller.signed_request?).to be false
- end
- end
-
- describe '#signed_request_account' do
- it 'returns nil' do
- expect(controller.signed_request_account).to be_nil
- end
- end
- end
-
- context 'with signature header' do
- let!(:author) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor') }
-
- context 'without body' do
- before do
- get :success
-
- fake_request = Request.new(:get, request.url)
- fake_request.on_behalf_of(author)
-
- request.headers.merge!(fake_request.headers)
- end
-
- describe '#signed_request?' do
- it 'returns true' do
- expect(controller.signed_request?).to be true
- end
- end
-
- describe '#signed_request_account' do
- it 'returns an account' do
- expect(controller.signed_request_account).to eq author
- end
-
- it 'returns nil when path does not match' do
- request.path = '/alternative-path'
- expect(controller.signed_request_account).to be_nil
- end
-
- it 'returns nil when method does not match' do
- post :success
- expect(controller.signed_request_account).to be_nil
- end
- end
- end
-
- context 'with a valid actor that is not an Account' do
- let(:actor) { wrapped_actor_class.new(author) }
-
- before do
- get :success
-
- fake_request = Request.new(:get, request.url)
- fake_request.on_behalf_of(author)
-
- request.headers.merge!(fake_request.headers)
-
- allow(ActivityPub::TagManager.instance).to receive(:uri_to_actor).with(anything) do
- actor
- end
- end
-
- describe '#signed_request?' do
- it 'returns true' do
- expect(controller.signed_request?).to be true
- end
- end
-
- describe '#signed_request_account' do
- it 'returns nil' do
- expect(controller.signed_request_account).to be_nil
- end
- end
-
- describe '#signed_request_actor' do
- it 'returns the expected actor' do
- expect(controller.signed_request_actor).to eq actor
- end
- end
- end
-
- context 'with request with unparsable Date header' do
- before do
- get :success
-
- fake_request = Request.new(:get, request.url)
- fake_request.add_headers({ 'Date' => 'wrong date' })
- fake_request.on_behalf_of(author)
-
- request.headers.merge!(fake_request.headers)
- end
-
- describe '#signed_request?' do
- it 'returns true' do
- expect(controller.signed_request?).to be true
- end
- end
-
- describe '#signed_request_account' do
- it 'returns nil' do
- expect(controller.signed_request_account).to be_nil
- end
- end
-
- describe '#signature_verification_failure_reason' do
- it 'contains an error description' do
- controller.signed_request_account
- expect(controller.signature_verification_failure_reason[:error]).to eq 'Invalid Date header: not RFC 2616 compliant date: "wrong date"'
- end
- end
- end
-
- context 'with request older than a day' do
- before do
- get :success
-
- fake_request = Request.new(:get, request.url)
- fake_request.add_headers({ 'Date' => 2.days.ago.utc.httpdate })
- fake_request.on_behalf_of(author)
-
- request.headers.merge!(fake_request.headers)
- end
-
- describe '#signed_request?' do
- it 'returns true' do
- expect(controller.signed_request?).to be true
- end
- end
-
- describe '#signed_request_account' do
- it 'returns nil' do
- expect(controller.signed_request_account).to be_nil
- end
- end
-
- describe '#signature_verification_failure_reason' do
- it 'contains an error description' do
- controller.signed_request_account
- expect(controller.signature_verification_failure_reason[:error]).to eq 'Signed request date outside acceptable time window'
- end
- end
- end
-
- context 'with inaccessible key' do
- before do
- get :success
-
- author = Fabricate(:account, domain: 'localhost:5000', uri: 'http://localhost:5000/actor')
- fake_request = Request.new(:get, request.url)
- fake_request.on_behalf_of(author)
- author.destroy
-
- request.headers.merge!(fake_request.headers)
-
- stub_request(:get, 'http://localhost:5000/actor#main-key').to_raise(Mastodon::HostValidationError)
- end
-
- describe '#signed_request?' do
- it 'returns true' do
- expect(controller.signed_request?).to be true
- end
- end
-
- describe '#signed_request_account' do
- it 'returns nil' do
- expect(controller.signed_request_account).to be_nil
- end
- end
- end
-
- context 'with body' do
- before do
- allow(controller).to receive(:actor_refresh_key!).and_return(author)
- post :success, body: 'Hello world'
-
- fake_request = Request.new(:post, request.url, body: 'Hello world')
- fake_request.on_behalf_of(author)
-
- request.headers.merge!(fake_request.headers)
- end
-
- describe '#signed_request?' do
- it 'returns true' do
- expect(controller.signed_request?).to be true
- end
- end
-
- describe '#signed_request_account' do
- it 'returns an account' do
- expect(controller.signed_request_account).to eq author
- end
- end
-
- context 'when path does not match' do
- before do
- request.path = '/alternative-path'
- end
-
- describe '#signed_request_account' do
- it 'returns nil' do
- expect(controller.signed_request_account).to be_nil
- end
- end
-
- describe '#signature_verification_failure_reason' do
- it 'contains an error description' do
- controller.signed_request_account
- expect(controller.signature_verification_failure_reason[:error]).to include('using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)')
- expect(controller.signature_verification_failure_reason[:signed_string]).to include("(request-target): post /alternative-path\n")
- end
- end
- end
-
- context 'when method does not match' do
- before do
- get :success
- end
-
- describe '#signed_request_account' do
- it 'returns nil' do
- expect(controller.signed_request_account).to be_nil
- end
- end
- end
-
- context 'when body has been tampered' do
- before do
- post :success, body: 'doo doo doo'
- end
-
- describe '#signed_request_account' do
- it 'returns nil when body has been tampered' do
- expect(controller.signed_request_account).to be_nil
- end
- end
- end
- end
- end
-
- context 'when a signature is required' do
- before do
- get :signature_required
- end
-
- context 'without signature header' do
- it 'returns HTTP 401' do
- expect(response).to have_http_status(401)
- end
-
- it 'returns an error' do
- expect(Oj.load(response.body)['error']).to eq 'Request not signed'
- end
- end
- end
-end
diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb
index cb8c2a0e5..dd78c96c0 100644
--- a/spec/controllers/follower_accounts_controller_spec.rb
+++ b/spec/controllers/follower_accounts_controller_spec.rb
@@ -48,6 +48,13 @@ describe FollowerAccountsController do
it 'returns followers' do
expect(response).to have_http_status(200)
+ expect(body_as_json)
+ .to include(
+ orderedItems: contain_exactly(
+ include(follow_from_bob.account.username),
+ include(follow_from_chris.account.username)
+ )
+ )
expect(body['totalItems']).to eq 2
expect(body['partOf']).to be_present
end
diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb
index 095528ed0..7bb78fb42 100644
--- a/spec/controllers/following_accounts_controller_spec.rb
+++ b/spec/controllers/following_accounts_controller_spec.rb
@@ -48,6 +48,13 @@ describe FollowingAccountsController do
it 'returns followers' do
expect(response).to have_http_status(200)
+ expect(body_as_json)
+ .to include(
+ orderedItems: contain_exactly(
+ include(follow_of_bob.target_account.username),
+ include(follow_of_chris.target_account.username)
+ )
+ )
expect(body['totalItems']).to eq 2
expect(body['partOf']).to be_present
end
diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb
index b54610604..b46b944d0 100644
--- a/spec/controllers/oauth/authorized_applications_controller_spec.rb
+++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb
@@ -63,5 +63,9 @@ describe Oauth::AuthorizedApplicationsController do
it 'removes subscriptions for the application\'s access tokens' do
expect(Web::PushSubscription.where(user: user).count).to eq 0
end
+
+ it 'removes the web_push_subscription' do
+ expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
end
end
diff --git a/spec/controllers/oauth/tokens_controller_spec.rb b/spec/controllers/oauth/tokens_controller_spec.rb
index 973393bcf..dd2d8ca70 100644
--- a/spec/controllers/oauth/tokens_controller_spec.rb
+++ b/spec/controllers/oauth/tokens_controller_spec.rb
@@ -20,5 +20,9 @@ RSpec.describe Oauth::TokensController do
it 'removes web push subscription for token' do
expect(Web::PushSubscription.where(access_token: access_token).count).to eq 0
end
+
+ it 'removes the web_push_subscription' do
+ expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
end
end
diff --git a/spec/controllers/settings/imports_controller_spec.rb b/spec/controllers/settings/imports_controller_spec.rb
index 1e7b75893..89ec39e54 100644
--- a/spec/controllers/settings/imports_controller_spec.rb
+++ b/spec/controllers/settings/imports_controller_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe Settings::ImportsController do
it 'assigns the expected imports', :aggregate_failures do
expect(response).to have_http_status(200)
expect(assigns(:recent_imports)).to eq [import]
+ expect(assigns(:recent_imports)).to_not include(other_import)
expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
@@ -138,6 +139,7 @@ RSpec.describe Settings::ImportsController do
let(:bulk_import) { Fabricate(:bulk_import, account: user.account, type: import_type, state: :finished) }
before do
+ rows.each { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
bulk_import.update(total_items: bulk_import.rows.count, processed_items: bulk_import.rows.count, imported_items: 0)
end
@@ -152,11 +154,11 @@ RSpec.describe Settings::ImportsController do
context 'with follows' do
let(:import_type) { 'following' }
- let!(:rows) do
+ let(:rows) do
[
{ 'acct' => 'foo@bar' },
{ 'acct' => 'user@bar', 'show_reblogs' => false, 'notify' => true, 'languages' => %w(fr de) },
- ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+ ]
end
include_examples 'export failed rows', "Account address,Show boosts,Notify on new posts,Languages\nfoo@bar,true,false,\nuser@bar,false,true,\"fr, de\"\n"
@@ -165,11 +167,11 @@ RSpec.describe Settings::ImportsController do
context 'with blocks' do
let(:import_type) { 'blocking' }
- let!(:rows) do
+ let(:rows) do
[
{ 'acct' => 'foo@bar' },
{ 'acct' => 'user@bar' },
- ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+ ]
end
include_examples 'export failed rows', "foo@bar\nuser@bar\n"
@@ -178,11 +180,11 @@ RSpec.describe Settings::ImportsController do
context 'with mutes' do
let(:import_type) { 'muting' }
- let!(:rows) do
+ let(:rows) do
[
{ 'acct' => 'foo@bar' },
{ 'acct' => 'user@bar', 'hide_notifications' => false },
- ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+ ]
end
include_examples 'export failed rows', "Account address,Hide notifications\nfoo@bar,true\nuser@bar,false\n"
@@ -191,11 +193,11 @@ RSpec.describe Settings::ImportsController do
context 'with domain blocks' do
let(:import_type) { 'domain_blocking' }
- let!(:rows) do
+ let(:rows) do
[
{ 'domain' => 'bad.domain' },
{ 'domain' => 'evil.domain' },
- ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+ ]
end
include_examples 'export failed rows', "bad.domain\nevil.domain\n"
@@ -204,11 +206,11 @@ RSpec.describe Settings::ImportsController do
context 'with bookmarks' do
let(:import_type) { 'bookmarks' }
- let!(:rows) do
+ let(:rows) do
[
{ 'uri' => 'https://foo.com/1' },
{ 'uri' => 'https://foo.com/2' },
- ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+ ]
end
include_examples 'export failed rows', "https://foo.com/1\nhttps://foo.com/2\n"
@@ -217,11 +219,11 @@ RSpec.describe Settings::ImportsController do
context 'with lists' do
let(:import_type) { 'lists' }
- let!(:rows) do
+ let(:rows) do
[
{ 'list_name' => 'Amigos', 'acct' => 'user@example.com' },
{ 'list_name' => 'Frenemies', 'acct' => 'user@org.org' },
- ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+ ]
end
include_examples 'export failed rows', "Amigos,user@example.com\nFrenemies,user@org.org\n"
diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb
index bdc1c45ea..8084427d0 100644
--- a/spec/lib/activitypub/activity/create_spec.rb
+++ b/spec/lib/activitypub/activity/create_spec.rb
@@ -532,15 +532,21 @@ RSpec.describe ActivityPub::Activity::Create do
mediaType: 'image/png',
url: 'http://example.com/attachment.png',
},
+ {
+ type: 'Document',
+ mediaType: 'image/png',
+ url: 'http://example.com/emoji.png',
+ },
],
}
end
- it 'creates status' do
+ it 'creates status with correctly-ordered media attachments' do
status = sender.statuses.first
expect(status).to_not be_nil
- expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
+ expect(status.ordered_media_attachments.map(&:remote_url)).to eq ['http://example.com/attachment.png', 'http://example.com/emoji.png']
+ expect(status.ordered_media_attachment_ids).to be_present
end
end
diff --git a/spec/lib/activitypub/activity/delete_spec.rb b/spec/lib/activitypub/activity/delete_spec.rb
index 3a73b3726..aec2c71e5 100644
--- a/spec/lib/activitypub/activity/delete_spec.rb
+++ b/spec/lib/activitypub/activity/delete_spec.rb
@@ -50,6 +50,10 @@ RSpec.describe ActivityPub::Activity::Delete do
it 'sends delete activity to followers of rebloggers' do
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
end
+
+ it 'deletes the reblog' do
+ expect { reblog.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
end
end
diff --git a/spec/lib/mastodon/cli/maintenance_spec.rb b/spec/lib/mastodon/cli/maintenance_spec.rb
index 353bf08b6..ca492bbf6 100644
--- a/spec/lib/mastodon/cli/maintenance_spec.rb
+++ b/spec/lib/mastodon/cli/maintenance_spec.rb
@@ -62,6 +62,7 @@ describe Mastodon::CLI::Maintenance do
context 'with duplicate accounts' do
before do
prepare_duplicate_data
+ choose_local_account_to_keep
end
let(:duplicate_account_username) { 'username' }
@@ -71,21 +72,37 @@ describe Mastodon::CLI::Maintenance do
expect { subject }
.to output_results(
'Deduplicating accounts',
+ 'Multiple local accounts were found for',
'Restoring index_accounts_on_username_and_domain_lower',
'Reindexing textual indexes on accounts…',
'Finished!'
)
- .and change(duplicate_accounts, :count).from(2).to(1)
+ .and change(duplicate_remote_accounts, :count).from(2).to(1)
+ .and change(duplicate_local_accounts, :count).from(2).to(1)
end
- def duplicate_accounts
+ def duplicate_remote_accounts
Account.where(username: duplicate_account_username, domain: duplicate_account_domain)
end
+ def duplicate_local_accounts
+ Account.where(username: duplicate_account_username, domain: nil)
+ end
+
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :accounts, name: :index_accounts_on_username_and_domain_lower
- Fabricate(:account, username: duplicate_account_username, domain: duplicate_account_domain)
- Fabricate.build(:account, username: duplicate_account_username, domain: duplicate_account_domain).save(validate: false)
+ _remote_account = Fabricate(:account, username: duplicate_account_username, domain: duplicate_account_domain)
+ _remote_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: duplicate_account_domain).save(validate: false)
+ _local_account = Fabricate(:account, username: duplicate_account_username, domain: nil)
+ _local_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: nil).save(validate: false)
+ end
+
+ def choose_local_account_to_keep
+ allow(cli.shell)
+ .to receive(:ask)
+ .with(/Account to keep unchanged/, anything)
+ .and_return('0')
+ .once
end
end
@@ -175,6 +192,407 @@ describe Mastodon::CLI::Maintenance do
end
end
+ context 'with duplicate account_domain_blocks' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:duplicate_domain) { 'example.host' }
+ let(:account) { Fabricate(:account) }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Removing duplicate account domain blocks',
+ 'Restoring account domain blocks indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_account_domain_blocks, :count).from(2).to(1)
+ end
+
+ def duplicate_account_domain_blocks
+ AccountDomainBlock.where(account: account, domain: duplicate_domain)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :account_domain_blocks, [:account_id, :domain]
+ Fabricate(:account_domain_block, account: account, domain: duplicate_domain)
+ Fabricate.build(:account_domain_block, account: account, domain: duplicate_domain).save(validate: false)
+ end
+ end
+
+ context 'with duplicate announcement_reactions' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:account) { Fabricate(:account) }
+ let(:announcement) { Fabricate(:announcement) }
+ let(:name) { Fabricate(:custom_emoji).shortcode }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Removing duplicate announcement reactions',
+ 'Restoring announcement_reactions indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_announcement_reactions, :count).from(2).to(1)
+ end
+
+ def duplicate_announcement_reactions
+ AnnouncementReaction.where(account: account, announcement: announcement, name: name)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :announcement_reactions, [:account_id, :announcement_id, :name]
+ Fabricate(:announcement_reaction, account: account, announcement: announcement, name: name)
+ Fabricate.build(:announcement_reaction, account: account, announcement: announcement, name: name).save(validate: false)
+ end
+ end
+
+ context 'with duplicate conversations' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:uri) { 'https://example.host/path' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating conversations',
+ 'Restoring conversations indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_conversations, :count).from(2).to(1)
+ end
+
+ def duplicate_conversations
+ Conversation.where(uri: uri)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :conversations, :uri
+ Fabricate(:conversation, uri: uri)
+ Fabricate.build(:conversation, uri: uri).save(validate: false)
+ end
+ end
+
+ context 'with duplicate custom_emojis' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:duplicate_shortcode) { 'wowzers' }
+ let(:duplicate_domain) { 'example.host' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating custom_emojis',
+ 'Restoring custom_emojis indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_custom_emojis, :count).from(2).to(1)
+ end
+
+ def duplicate_custom_emojis
+ CustomEmoji.where(shortcode: duplicate_shortcode, domain: duplicate_domain)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :custom_emojis, [:shortcode, :domain]
+ Fabricate(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain)
+ Fabricate.build(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain).save(validate: false)
+ end
+ end
+
+ context 'with duplicate custom_emoji_categories' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:duplicate_name) { 'name_value' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating custom_emoji_categories',
+ 'Restoring custom_emoji_categories indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_custom_emoji_categories, :count).from(2).to(1)
+ end
+
+ def duplicate_custom_emoji_categories
+ CustomEmojiCategory.where(name: duplicate_name)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :custom_emoji_categories, :name
+ Fabricate(:custom_emoji_category, name: duplicate_name)
+ Fabricate.build(:custom_emoji_category, name: duplicate_name).save(validate: false)
+ end
+ end
+
+ context 'with duplicate domain_allows' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:domain) { 'example.host' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating domain_allows',
+ 'Restoring domain_allows indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_domain_allows, :count).from(2).to(1)
+ end
+
+ def duplicate_domain_allows
+ DomainAllow.where(domain: domain)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :domain_allows, :domain
+ Fabricate(:domain_allow, domain: domain)
+ Fabricate.build(:domain_allow, domain: domain).save(validate: false)
+ end
+ end
+
+ context 'with duplicate domain_blocks' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:domain) { 'example.host' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating domain_blocks',
+ 'Restoring domain_blocks indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_domain_blocks, :count).from(2).to(1)
+ end
+
+ def duplicate_domain_blocks
+ DomainBlock.where(domain: domain)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :domain_blocks, :domain
+ Fabricate(:domain_block, domain: domain)
+ Fabricate.build(:domain_block, domain: domain).save(validate: false)
+ end
+ end
+
+ context 'with duplicate email_domain_blocks' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:domain) { 'example.host' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating email_domain_blocks',
+ 'Restoring email_domain_blocks indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_email_domain_blocks, :count).from(2).to(1)
+ end
+
+ def duplicate_email_domain_blocks
+ EmailDomainBlock.where(domain: domain)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :email_domain_blocks, :domain
+ Fabricate(:email_domain_block, domain: domain)
+ Fabricate.build(:email_domain_block, domain: domain).save(validate: false)
+ end
+ end
+
+ context 'with duplicate media_attachments' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:shortcode) { 'codenam' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating media_attachments',
+ 'Restoring media_attachments indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_media_attachments, :count).from(2).to(1)
+ end
+
+ def duplicate_media_attachments
+ MediaAttachment.where(shortcode: shortcode)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :media_attachments, :shortcode
+ Fabricate(:media_attachment, shortcode: shortcode)
+ Fabricate.build(:media_attachment, shortcode: shortcode).save(validate: false)
+ end
+ end
+
+ context 'with duplicate preview_cards' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:url) { 'https://example.host/path' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating preview_cards',
+ 'Restoring preview_cards indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_preview_cards, :count).from(2).to(1)
+ end
+
+ def duplicate_preview_cards
+ PreviewCard.where(url: url)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :preview_cards, :url
+ Fabricate(:preview_card, url: url)
+ Fabricate.build(:preview_card, url: url).save(validate: false)
+ end
+ end
+
+ context 'with duplicate statuses' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:uri) { 'https://example.host/path' }
+ let(:account) { Fabricate(:account) }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating statuses',
+ 'Restoring statuses indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_statuses, :count).from(2).to(1)
+ end
+
+ def duplicate_statuses
+ Status.where(uri: uri)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :statuses, :uri
+ Fabricate(:status, account: account, uri: uri)
+ duplicate = Fabricate.build(:status, account: account, uri: uri)
+ duplicate.save(validate: false)
+ Fabricate(:status_pin, account: account, status: duplicate)
+ Fabricate(:status, in_reply_to_id: duplicate.id)
+ Fabricate(:status, reblog_of_id: duplicate.id)
+ end
+ end
+
+ context 'with duplicate tags' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:name) { 'tagname' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating tags',
+ 'Restoring tags indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_tags, :count).from(2).to(1)
+ end
+
+ def duplicate_tags
+ Tag.where(name: name)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :tags, name: 'index_tags_on_name_lower_btree'
+ Fabricate(:tag, name: name)
+ Fabricate.build(:tag, name: name).save(validate: false)
+ end
+ end
+
+ context 'with duplicate webauthn_credentials' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:external_id) { '123_123_123' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating webauthn_credentials',
+ 'Restoring webauthn_credentials indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_webauthn_credentials, :count).from(2).to(1)
+ end
+
+ def duplicate_webauthn_credentials
+ WebauthnCredential.where(external_id: external_id)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :webauthn_credentials, :external_id
+ Fabricate(:webauthn_credential, external_id: external_id)
+ Fabricate.build(:webauthn_credential, external_id: external_id).save(validate: false)
+ end
+ end
+
+ context 'with duplicate webhooks' do
+ before do
+ prepare_duplicate_data
+ end
+
+ let(:url) { 'https://example.host/path' }
+
+ it 'runs the deduplication process' do
+ expect { subject }
+ .to output_results(
+ 'Deduplicating webhooks',
+ 'Restoring webhooks indexes',
+ 'Finished!'
+ )
+ .and change(duplicate_webhooks, :count).from(2).to(1)
+ end
+
+ def duplicate_webhooks
+ Webhook.where(url: url)
+ end
+
+ def prepare_duplicate_data
+ ActiveRecord::Base.connection.remove_index :webhooks, :url
+ Fabricate(:webhook, url: url)
+ Fabricate.build(:webhook, url: url).save(validate: false)
+ end
+ end
+
def agree_to_backup_warning
allow(cli.shell)
.to receive(:yes?)
diff --git a/spec/lib/vacuum/applications_vacuum_spec.rb b/spec/lib/vacuum/applications_vacuum_spec.rb
index 57a222aaf..df5c86060 100644
--- a/spec/lib/vacuum/applications_vacuum_spec.rb
+++ b/spec/lib/vacuum/applications_vacuum_spec.rb
@@ -13,11 +13,11 @@ RSpec.describe Vacuum::ApplicationsVacuum do
let!(:unused_app) { Fabricate(:application, created_at: 1.month.ago) }
let!(:recent_app) { Fabricate(:application, created_at: 1.hour.ago) }
- let!(:active_access_token) { Fabricate(:access_token, application: app_with_token) }
- let!(:active_access_grant) { Fabricate(:access_grant, application: app_with_grant) }
- let!(:user) { Fabricate(:user, created_by_application: app_with_signup) }
-
before do
+ Fabricate(:access_token, application: app_with_token)
+ Fabricate(:access_grant, application: app_with_grant)
+ Fabricate(:user, created_by_application: app_with_signup)
+
subject.perform
end
diff --git a/spec/lib/vacuum/preview_cards_vacuum_spec.rb b/spec/lib/vacuum/preview_cards_vacuum_spec.rb
index c1b7f7e9c..9dbdf0bc2 100644
--- a/spec/lib/vacuum/preview_cards_vacuum_spec.rb
+++ b/spec/lib/vacuum/preview_cards_vacuum_spec.rb
@@ -30,5 +30,9 @@ RSpec.describe Vacuum::PreviewCardsVacuum do
it 'does not delete attached preview cards' do
expect(new_preview_card.reload).to be_persisted
end
+
+ it 'does not delete orphaned preview cards in the retention period' do
+ expect(orphaned_preview_card.reload).to be_persisted
+ end
end
end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 522549125..87aa8bc75 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -954,6 +954,7 @@ RSpec.describe Account do
it 'returns every usable non-suspended account' do
expect(described_class.searchable).to contain_exactly(silenced_local, silenced_remote, local_account, remote_account)
+ expect(described_class.searchable).to_not include(suspended_local, suspended_remote, unconfirmed, unapproved)
end
it 'does not mess with previously-applied scopes' do
diff --git a/spec/models/account_statuses_cleanup_policy_spec.rb b/spec/models/account_statuses_cleanup_policy_spec.rb
index 74fff30c9..da2a774b2 100644
--- a/spec/models/account_statuses_cleanup_policy_spec.rb
+++ b/spec/models/account_statuses_cleanup_policy_spec.rb
@@ -235,13 +235,17 @@ RSpec.describe AccountStatusesCleanupPolicy do
describe '#compute_cutoff_id' do
subject { account_statuses_cleanup_policy.compute_cutoff_id }
- let!(:unrelated_status) { Fabricate(:status, created_at: 3.years.ago) }
let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
+ before { Fabricate(:status, created_at: 3.years.ago) }
+
context 'when the account has posted multiple toots' do
- let!(:very_old_status) { Fabricate(:status, created_at: 3.years.ago, account: account) }
- let!(:old_status) { Fabricate(:status, created_at: 3.weeks.ago, account: account) }
- let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) }
+ let!(:old_status) { Fabricate(:status, created_at: 3.weeks.ago, account: account) }
+
+ before do
+ Fabricate(:status, created_at: 3.years.ago, account: account)
+ Fabricate(:status, created_at: 2.days.ago, account: account)
+ end
it 'returns the most recent id that is still below policy age' do
expect(subject).to eq old_status.id
@@ -270,16 +274,16 @@ RSpec.describe AccountStatusesCleanupPolicy do
let!(:faved_secondary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
let!(:reblogged_primary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
let!(:reblogged_secondary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
- let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) }
-
- let!(:media_attachment) { Fabricate(:media_attachment, account: account, status: status_with_media) }
- let!(:status_pin) { Fabricate(:status_pin, account: account, status: pinned_status) }
- let!(:favourite) { Fabricate(:favourite, account: account, status: self_faved) }
- let!(:bookmark) { Fabricate(:bookmark, account: account, status: self_bookmarked) }
+ let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) }
let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
before do
+ Fabricate(:media_attachment, account: account, status: status_with_media)
+ Fabricate(:status_pin, account: account, status: pinned_status)
+ Fabricate(:favourite, account: account, status: self_faved)
+ Fabricate(:bookmark, account: account, status: self_bookmarked)
+
faved_primary.status_stat.update(favourites_count: 4)
faved_secondary.status_stat.update(favourites_count: 5)
reblogged_primary.status_stat.update(reblogs_count: 4)
diff --git a/spec/models/canonical_email_block_spec.rb b/spec/models/canonical_email_block_spec.rb
index 0acff8237..c63483f96 100644
--- a/spec/models/canonical_email_block_spec.rb
+++ b/spec/models/canonical_email_block_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe CanonicalEmailBlock do
end
describe '.block?' do
- let!(:canonical_email_block) { Fabricate(:canonical_email_block, email: 'foo@bar.com') }
+ before { Fabricate(:canonical_email_block, email: 'foo@bar.com') }
it 'returns true for the same email' do
expect(described_class.block?('foo@bar.com')).to be true
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index 938d0546d..284576ced 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -265,17 +265,29 @@ RSpec.describe Status do
context 'when given one tag' do
it 'returns the expected statuses' do
- expect(described_class.tagged_with([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_all_tags.id)
- expect(described_class.tagged_with([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_with_all_tags.id)
- expect(described_class.tagged_with([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id, status_with_all_tags.id)
+ expect(described_class.tagged_with([tag_cats.id]))
+ .to include(status_with_tag_cats, status_with_all_tags)
+ .and not_include(status_without_tags)
+ expect(described_class.tagged_with([tag_dogs.id]))
+ .to include(status_with_tag_dogs, status_with_all_tags)
+ .and not_include(status_without_tags)
+ expect(described_class.tagged_with([tag_zebras.id]))
+ .to include(status_tagged_with_zebras, status_with_all_tags)
+ .and not_include(status_without_tags)
end
end
context 'when given multiple tags' do
it 'returns the expected statuses' do
- expect(described_class.tagged_with([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_tag_dogs.id, status_with_all_tags.id)
- expect(described_class.tagged_with([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_tagged_with_zebras.id, status_with_all_tags.id)
- expect(described_class.tagged_with([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_tagged_with_zebras.id, status_with_all_tags.id)
+ expect(described_class.tagged_with([tag_cats.id, tag_dogs.id]))
+ .to include(status_with_tag_cats, status_with_tag_dogs, status_with_all_tags)
+ .and not_include(status_without_tags)
+ expect(described_class.tagged_with([tag_cats.id, tag_zebras.id]))
+ .to include(status_with_tag_cats, status_tagged_with_zebras, status_with_all_tags)
+ .and not_include(status_without_tags)
+ expect(described_class.tagged_with([tag_dogs.id, tag_zebras.id]))
+ .to include(status_with_tag_dogs, status_tagged_with_zebras, status_with_all_tags)
+ .and not_include(status_without_tags)
end
end
end
@@ -292,17 +304,26 @@ RSpec.describe Status do
context 'when given one tag' do
it 'returns the expected statuses' do
- expect(described_class.tagged_with_all([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_all_tags.id)
- expect(described_class.tagged_with_all([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_with_all_tags.id)
- expect(described_class.tagged_with_all([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id)
+ expect(described_class.tagged_with_all([tag_cats.id]))
+ .to include(status_with_tag_cats, status_with_all_tags)
+ .and not_include(status_without_tags)
+ expect(described_class.tagged_with_all([tag_dogs.id]))
+ .to include(status_with_tag_dogs, status_with_all_tags)
+ .and not_include(status_without_tags)
+ expect(described_class.tagged_with_all([tag_zebras.id]))
+ .to include(status_tagged_with_zebras)
+ .and not_include(status_without_tags)
end
end
context 'when given multiple tags' do
it 'returns the expected statuses' do
- expect(described_class.tagged_with_all([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_all_tags.id)
- expect(described_class.tagged_with_all([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to eq []
- expect(described_class.tagged_with_all([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to eq []
+ expect(described_class.tagged_with_all([tag_cats.id, tag_dogs.id]))
+ .to include(status_with_all_tags)
+ expect(described_class.tagged_with_all([tag_cats.id, tag_zebras.id]))
+ .to eq []
+ expect(described_class.tagged_with_all([tag_dogs.id, tag_zebras.id]))
+ .to eq []
end
end
end
@@ -319,17 +340,29 @@ RSpec.describe Status do
context 'when given one tag' do
it 'returns the expected statuses' do
- expect(described_class.tagged_with_none([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_tagged_with_zebras.id, status_without_tags.id)
- expect(described_class.tagged_with_none([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_tagged_with_zebras.id, status_without_tags.id)
- expect(described_class.tagged_with_none([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_tag_dogs.id, status_without_tags.id)
+ expect(described_class.tagged_with_none([tag_cats.id]))
+ .to include(status_with_tag_dogs, status_tagged_with_zebras, status_without_tags)
+ .and not_include(status_with_all_tags)
+ expect(described_class.tagged_with_none([tag_dogs.id]))
+ .to include(status_with_tag_cats, status_tagged_with_zebras, status_without_tags)
+ .and not_include(status_with_all_tags)
+ expect(described_class.tagged_with_none([tag_zebras.id]))
+ .to include(status_with_tag_cats, status_with_tag_dogs, status_without_tags)
+ .and not_include(status_with_all_tags)
end
end
context 'when given multiple tags' do
it 'returns the expected statuses' do
- expect(described_class.tagged_with_none([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id, status_without_tags.id)
- expect(described_class.tagged_with_none([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_without_tags.id)
- expect(described_class.tagged_with_none([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_without_tags.id)
+ expect(described_class.tagged_with_none([tag_cats.id, tag_dogs.id]))
+ .to include(status_tagged_with_zebras, status_without_tags)
+ .and not_include(status_with_all_tags)
+ expect(described_class.tagged_with_none([tag_cats.id, tag_zebras.id]))
+ .to include(status_with_tag_dogs, status_without_tags)
+ .and not_include(status_with_all_tags)
+ expect(described_class.tagged_with_none([tag_dogs.id, tag_zebras.id]))
+ .to include(status_with_tag_cats, status_without_tags)
+ .and not_include(status_with_all_tags)
end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 9111fd7c7..ab5bd39b7 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -422,6 +422,7 @@ RSpec.describe User do
it 'deactivates all sessions' do
expect(user.session_activations.count).to eq 0
+ expect { session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'revokes all access tokens' do
@@ -430,6 +431,7 @@ RSpec.describe User do
it 'removes push subscriptions' do
expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0
+ expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 0e68fbe12..c9352a4d5 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -153,6 +153,7 @@ RSpec::Sidekiq.configure do |config|
end
RSpec::Matchers.define_negated_matcher :not_change, :change
+RSpec::Matchers.define_negated_matcher :not_include, :include
def request_fixture(name)
Rails.root.join('spec', 'fixtures', 'requests', name).read
diff --git a/spec/requests/signature_verification_spec.rb b/spec/requests/signature_verification_spec.rb
new file mode 100644
index 000000000..b753750b8
--- /dev/null
+++ b/spec/requests/signature_verification_spec.rb
@@ -0,0 +1,332 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'signature verification concern' do
+ before do
+ stub_tests_controller
+
+ # Signature checking is time-dependent, so travel to a fixed date
+ travel_to '2023-12-20T10:00:00Z'
+ end
+
+ after { Rails.application.reload_routes! }
+
+ # Include the private key so the tests can be easily adjusted and reviewed
+ let(:actor_keypair) do
+ OpenSSL::PKey.read(<<~PEM_TEXT)
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEowIBAAKCAQEAqIAYvNFGbZ5g4iiK6feSdXD4bDStFM58A7tHycYXaYtzZQpI
+ eHXAmaXuZzXIwtrP4N0gIk8JNwZvXj2UPS+S07t0V9wNK94he01LV5EMz/GN4eNn
+ FmDL64HIEuKLvV8TvgjbUPRD6Y5X0UpKi2ZIFLSb96Q5w0Z/k7ntpVKV52y8kz5F
+ jr/O/0JuHryZe0yItzJh8kzFfeMf0EXzfSnaKvT7P9jhgC6uTre+jXyvVZjiHDrn
+ qvvucdI3I7DRfXo1OqARBrLjy+TdseUAjNYJ+OuPRI1URIWQI01DCHqcohVu9+Ar
+ +BiCjFp3ua+XMuJvrvbD61d1Fvig/9nbBRR+8QIDAQABAoIBAAgySHnFWI6gItR3
+ fkfiqIm80cHCN3Xk1C6iiVu+3oBOZbHpW9R7vl9e/WOA/9O+LPjiSsQOegtWnVvd
+ RRjrl7Hj20VDlZKv5Mssm6zOGAxksrcVbqwdj+fUJaNJCL0AyyseH0x/IE9T8rDC
+ I1GH+3tB3JkhkIN/qjipdX5ab8MswEPu8IC4ViTpdBgWYY/xBcAHPw4xuL0tcwzh
+ FBlf4DqoEVQo8GdK5GAJ2Ny0S4xbXHUURzx/R4y4CCts7niAiLGqd9jmLU1kUTMk
+ QcXfQYK6l+unLc7wDYAz7sFEHh04M48VjWwiIZJnlCqmQbLda7uhhu8zkF1DqZTu
+ ulWDGQECgYEA0TIAc8BQBVab979DHEEmMdgqBwxLY3OIAk0b+r50h7VBGWCDPRsC
+ STD73fQY3lNet/7/jgSGwwAlAJ5PpMXxXiZAE3bUwPmHzgF7pvIOOLhA8O07tHSO
+ L2mvQe6NPzjZ+6iAO2U9PkClxcvGvPx2OBvisfHqZLmxC9PIVxzruQECgYEAzjM6
+ BTUXa6T/qHvLFbN699BXsUOGmHBGaLRapFDBfVvgZrwqYQcZpBBhesLdGTGSqwE7
+ gWsITPIJ+Ldo+38oGYyVys+w/V67q6ud7hgSDTW3hSvm+GboCjk6gzxlt9hQ0t9X
+ 8vfDOYhEXvVUJNv3mYO60ENqQhILO4bQ0zi+VfECgYBb/nUccfG+pzunU0Cb6Dp3
+ qOuydcGhVmj1OhuXxLFSDG84Tazo7juvHA9mp7VX76mzmDuhpHPuxN2AzB2SBEoE
+ cSW0aYld413JRfWukLuYTc6hJHIhBTCRwRQFFnae2s1hUdQySm8INT2xIc+fxBXo
+ zrp+Ljg5Wz90SAnN5TX0AQKBgDaatDOq0o/r+tPYLHiLtfWoE4Dau+rkWJDjqdk3
+ lXWn/e3WyHY3Vh/vQpEqxzgju45TXjmwaVtPATr+/usSykCxzP0PMPR3wMT+Rm1F
+ rIoY/odij+CaB7qlWwxj0x/zRbwB7x1lZSp4HnrzBpxYL+JUUwVRxPLIKndSBTza
+ GvVRAoGBAIVBcNcRQYF4fvZjDKAb4fdBsEuHmycqtRCsnkGOz6ebbEQznSaZ0tZE
+ +JuouZaGjyp8uPjNGD5D7mIGbyoZ3KyG4mTXNxDAGBso1hrNDKGBOrGaPhZx8LgO
+ 4VXJ+ybXrATf4jr8ccZYsZdFpOphPzz+j55Mqg5vac5P1XjmsGTb
+ -----END RSA PRIVATE KEY-----
+ PEM_TEXT
+ end
+
+ context 'without a Signature header' do
+ it 'does not treat the request as signed' do
+ get '/activitypub/success'
+
+ expect(response).to have_http_status(200)
+ expect(body_as_json).to match(
+ signed_request: false,
+ signature_actor_id: nil,
+ error: 'Request not signed'
+ )
+ end
+
+ context 'when a signature is required' do
+ it 'returns http unauthorized with appropriate error' do
+ get '/activitypub/signature_required'
+
+ expect(response).to have_http_status(401)
+ expect(body_as_json).to match(
+ error: 'Request not signed'
+ )
+ end
+ end
+ end
+
+ context 'with an HTTP Signature from a known account' do
+ let!(:actor) { Fabricate(:account, domain: 'remote.domain', uri: 'https://remote.domain/users/bob', private_key: nil, public_key: actor_keypair.public_key.to_pem) }
+
+ context 'with a valid signature on a GET request' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'successfuly verifies signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
+ end
+
+ context 'with a mismatching path' do
+ it 'fails to verify signature', :aggregate_failures do
+ get '/activitypub/alternative-path', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
+ }
+
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+
+ context 'with a mismatching method' do
+ it 'fails to verify signature', :aggregate_failures do
+ post '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
+ }
+
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+
+ context 'with an unparsable date' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="d4B7nfx8RJcfdJDu1J//5WzPzK/hgtPkdzZx49lu5QhnE7qdV3lgyVimmhCFrO16bwvzIp9iRMyRLkNFxLiEeVaa1gqeKbldGSnU0B0OMjx7rFBa65vLuzWQOATDitVGiBEYqoK4v0DMuFCz2DtFaA/DIUZ3sty8bZ/Ea3U1nByLOO6MacARA3zhMSI0GNxGqsSmZmG0hPLavB3jIXoE3IDoQabMnC39jrlcO/a8h1iaxBm2WD8TejrImJullgqlJIFpKhIHI3ipQkvTGPlm9dx0y+beM06qBvWaWQcmT09eRIUefVsOAzIhUtS/7FVb/URhZvircIJDa7vtiFcmZQ=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'wrong date', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'wrong date',
+ 'Signature' => signature_header,
+ }
+
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Invalid Date header: not RFC 2616 compliant date: "wrong date"'
+ )
+ end
+ end
+
+ context 'with a request older than a day' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="G1NuJv4zgoZ3B/ZIjzDWZHK4RC+5pYee74q8/LJEMCWXhcnAomcb9YHaqk1QYfQvcBUIXw3UZ3Q9xO8F9y0i8G5mzJHfQ+OgHqCoJk8EmGwsUXJMh5s1S5YFCRt8TT12TmJZz0VMqLq85ubueSYBM7QtUE/FzFIVLvz4RysgXxaXQKzdnM6+gbUEEKdCURpXdQt2NXQhp4MAmZH3+0lQoR6VxdsK0hx0Ji2PNp1nuqFTlYqNWZazVdLBN+9rETLRmvGXknvg9jOxTTppBVWnkAIl26HtLS3wwFVvz4pJzi9OQDOvLziehVyLNbU61hky+oJ215e2HuKSe2hxHNl1MA=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT',
+ 'Signature' => signature_header,
+ }
+
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Signed request date outside acceptable time window'
+ )
+ end
+ end
+
+ context 'with a valid signature on a POST request' do
+ let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'successfuly verifies signature', :aggregate_failures do
+ expect(digest_header).to eq digest_value('Hello world')
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header })
+
+ post '/activitypub/success', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Digest' => digest_header,
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
+ end
+
+ context 'when the Digest of a POST request is not signed' do
+ let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date (request-target)",signature="CPD704CG8aCm8X8qIP8kkkiGp1qwFLk/wMVQHOGP0Txxan8c2DZtg/KK7eN8RG8tHx8br/yS2hJs51x4kXImYukGzNJd7ihE3T8lp+9RI1tCcdobTzr/VcVJHDFySdQkg266GCMijRQRZfNvqlJLiisr817PI+gNVBI5qV+vnVd1XhWCEZ+YSmMe8UqYARXAYNqMykTheojqGpTeTFGPUpTQA2Fmt2BipwIjcFDm2Hpihl2kB0MUS0x3zPmHDuadvzoBbN6m3usPDLgYrpALlh+wDs1dYMntcwdwawRKY1oE1XNtgOSum12wntDq3uYL4gya2iPdcw3c929b4koUzw=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(digest_header).to eq digest_value('Hello world')
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT' })
+
+ post '/activitypub/success', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Digest' => digest_header,
+ 'Signature' => signature_header,
+ }
+
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Mastodon requires the Digest header to be signed when doing a POST request'
+ )
+ end
+ end
+
+ context 'with a tampered body on a POST request' do
+ let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(digest_header).to_not eq digest_value('Hello world!')
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header })
+
+ post '/activitypub/success', params: 'Hello world!', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
+ 'Signature' => signature_header,
+ }
+
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Invalid Digest value. Computed SHA-256 digest: wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro=; given: ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw='
+ )
+ end
+ end
+
+ context 'with a tampered path in a POST request' do
+ it 'fails to verify signature', :aggregate_failures do
+ post '/activitypub/alternative-path', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
+ 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="', # rubocop:disable Layout/LineLength
+ }
+
+ expect(response).to have_http_status(200)
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+ end
+
+ context 'with an inaccessible key' do
+ before do
+ stub_request(:get, 'https://remote.domain/users/alice#main-key').to_return(status: 404)
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => 'keyId="https://remote.domain/users/alice#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
+ }
+
+ expect(body_as_json).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Unable to fetch key JSON at https://remote.domain/users/alice#main-key'
+ )
+ end
+ end
+
+ private
+
+ def stub_tests_controller
+ stub_const('ActivityPub::TestsController', activitypub_tests_controller)
+
+ Rails.application.routes.draw do
+ # NOTE: RouteSet#draw removes all routes, so we need to re-insert one
+ resource :instance_actor, path: 'actor', only: [:show]
+
+ match :via => [:get, :post], '/activitypub/success' => 'activitypub/tests#success'
+ match :via => [:get, :post], '/activitypub/alternative-path' => 'activitypub/tests#alternative_success'
+ match :via => [:get, :post], '/activitypub/signature_required' => 'activitypub/tests#signature_required'
+ end
+ end
+
+ def activitypub_tests_controller
+ Class.new(ApplicationController) do
+ include SignatureVerification
+
+ before_action :require_actor_signature!, only: [:signature_required]
+
+ def success
+ render json: {
+ signed_request: signed_request?,
+ signature_actor_id: signed_request_actor&.id&.to_s,
+ }.merge(signature_verification_failure_reason || {})
+ end
+
+ alias_method :alternative_success, :success
+ alias_method :signature_required, :success
+ end
+ end
+
+ def digest_value(body)
+ "SHA-256=#{Digest::SHA256.base64digest(body)}"
+ end
+
+ def build_signature_string(keypair, key_id, request_target, headers)
+ algorithm = 'rsa-sha256'
+ signed_headers = headers.merge({ '(request-target)' => request_target })
+ signed_string = signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n")
+ signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))
+
+ "keyId=\"#{key_id}\",algorithm=\"#{algorithm}\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\""
+ end
+end
diff --git a/spec/services/account_statuses_cleanup_service_spec.rb b/spec/services/account_statuses_cleanup_service_spec.rb
index f7a88a917..0ac113f10 100644
--- a/spec/services/account_statuses_cleanup_service_spec.rb
+++ b/spec/services/account_statuses_cleanup_service_spec.rb
@@ -39,6 +39,13 @@ describe AccountStatusesCleanupService, type: :service do
it 'actually deletes the statuses' do
subject.call(account_policy, 10)
expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id])).to be_nil
+ expect { recent_status.reload }.to_not raise_error
+ end
+
+ it 'preserves recent and unrelated statuses' do
+ subject.call(account_policy, 10)
+ expect { unrelated_status.reload }.to_not raise_error
+ expect { recent_status.reload }.to_not raise_error
end
end
diff --git a/spec/services/activitypub/fetch_featured_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
index 466da891a..a98108cea 100644
--- a/spec/services/activitypub/fetch_featured_collection_service_spec.rb
+++ b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
@@ -87,6 +87,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
'https://example.com/account/pinned/unknown-inlined',
'https://example.com/account/pinned/unknown-reachable'
)
+ expect(actor.pinned_statuses).to_not include(known_status)
end
end
diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb
index 826b67d88..3c64feaee 100644
--- a/spec/services/activitypub/fetch_remote_status_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb
@@ -8,7 +8,6 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
subject { described_class.new }
let!(:sender) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar') }
- let!(:recipient) { Fabricate(:account) }
let(:existing_status) { nil }
diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb
index 09eb5ddee..58dd2badb 100644
--- a/spec/services/activitypub/process_account_service_spec.rb
+++ b/spec/services/activitypub/process_account_service_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
end
context 'when account is not suspended' do
- subject { described_class.new.call('alice', 'example.com', payload) }
+ subject { described_class.new.call(account.username, account.domain, payload) }
let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com') }
diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb
index df526daf3..f4a2b8fec 100644
--- a/spec/services/activitypub/process_collection_service_spec.rb
+++ b/spec/services/activitypub/process_collection_service_spec.rb
@@ -242,7 +242,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
it 'does not process forged payload' do
allow(ActivityPub::Activity).to receive(:factory)
- subject.call(json, forwarder)
+ expect { subject.call(json, forwarder) }
+ .to_not change(actor.reload.statuses, :count)
expect(ActivityPub::Activity).to_not have_received(:factory).with(
hash_including(
diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb
index 8201c9d51..991a852f0 100644
--- a/spec/services/batched_remove_status_service_spec.rb
+++ b/spec/services/batched_remove_status_service_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe BatchedRemoveStatusService, type: :service do
let!(:jeff) { Fabricate(:account) }
let!(:hank) { Fabricate(:account, username: 'hank', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
- let(:status_alice_hello) { PostStatusService.new.call(alice, text: 'Hello @bob@example.com') }
+ let(:status_alice_hello) { PostStatusService.new.call(alice, text: "Hello @#{bob.pretty_acct}") }
let(:status_alice_other) { PostStatusService.new.call(alice, text: 'Another status') }
before do
diff --git a/spec/services/block_domain_service_spec.rb b/spec/services/block_domain_service_spec.rb
index 36dce9d19..fc3a1397d 100644
--- a/spec/services/block_domain_service_spec.rb
+++ b/spec/services/block_domain_service_spec.rb
@@ -21,19 +21,19 @@ RSpec.describe BlockDomainService, type: :service do
end
it 'removes remote accounts from that domain' do
- expect(Account.find_remote('badguy666', 'evil.org').suspended?).to be true
+ expect(bad_account.reload.suspended?).to be true
end
it 'records suspension date appropriately' do
- expect(Account.find_remote('badguy666', 'evil.org').suspended_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
+ expect(bad_account.reload.suspended_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
end
it 'keeps already-banned accounts banned' do
- expect(Account.find_remote('badguy', 'evil.org').suspended?).to be true
+ expect(already_banned_account.reload.suspended?).to be true
end
it 'does not overwrite suspension date of already-banned accounts' do
- expect(Account.find_remote('badguy', 'evil.org').suspended_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
+ expect(already_banned_account.reload.suspended_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
end
it 'removes the remote accounts\'s statuses and media attachments' do
@@ -53,19 +53,19 @@ RSpec.describe BlockDomainService, type: :service do
end
it 'silences remote accounts from that domain' do
- expect(Account.find_remote('badguy666', 'evil.org').silenced?).to be true
+ expect(bad_account.reload.silenced?).to be true
end
it 'records suspension date appropriately' do
- expect(Account.find_remote('badguy666', 'evil.org').silenced_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
+ expect(bad_account.reload.silenced_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
end
it 'keeps already-banned accounts banned' do
- expect(Account.find_remote('badguy', 'evil.org').silenced?).to be true
+ expect(already_banned_account.reload.silenced?).to be true
end
it 'does not overwrite suspension date of already-banned accounts' do
- expect(Account.find_remote('badguy', 'evil.org').silenced_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
+ expect(already_banned_account.reload.silenced_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
end
it 'leaves the domains status and attachments, but clears media' do
diff --git a/spec/services/bulk_import_service_spec.rb b/spec/services/bulk_import_service_spec.rb
index 3a3f95ccc..f703650c3 100644
--- a/spec/services/bulk_import_service_spec.rb
+++ b/spec/services/bulk_import_service_spec.rb
@@ -271,14 +271,15 @@ RSpec.describe BulkImportService do
let(:import_type) { 'domain_blocking' }
let(:overwrite) { false }
- let!(:rows) do
+ let(:rows) do
[
{ 'domain' => 'blocked.com' },
{ 'domain' => 'to_block.com' },
- ].map { |data| import.rows.create!(data: data) }
+ ]
end
before do
+ rows.each { |data| import.rows.create!(data: data) }
account.block_domain!('alreadyblocked.com')
account.block_domain!('blocked.com')
end
@@ -298,14 +299,15 @@ RSpec.describe BulkImportService do
let(:import_type) { 'domain_blocking' }
let(:overwrite) { true }
- let!(:rows) do
+ let(:rows) do
[
{ 'domain' => 'blocked.com' },
{ 'domain' => 'to_block.com' },
- ].map { |data| import.rows.create!(data: data) }
+ ]
end
before do
+ rows.each { |data| import.rows.create!(data: data) }
account.block_domain!('alreadyblocked.com')
account.block_domain!('blocked.com')
end
diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb
index a2c57f1c1..f04ba9cf1 100644
--- a/spec/services/delete_account_service_spec.rb
+++ b/spec/services/delete_account_service_spec.rb
@@ -28,74 +28,69 @@ RSpec.describe DeleteAccountService, type: :service do
let!(:account_note) { Fabricate(:account_note, account: account) }
it 'deletes associated owned and target records and target notifications' do
- expect { subject }
- .to delete_associated_owned_records
- .and delete_associated_target_records
- .and delete_associated_target_notifications
+ subject
+
+ expect_deletion_of_associated_owned_records
+ expect_deletion_of_associated_target_records
+ expect_deletion_of_associated_target_notifications
end
- def delete_associated_owned_records
- change do
- [
- account.statuses,
- account.media_attachments,
- account.notifications,
- account.favourites,
- account.active_relationships,
- account.passive_relationships,
- account.polls,
- account.account_notes,
- ].map(&:count)
- end.from([2, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0])
+ def expect_deletion_of_associated_owned_records
+ expect { status.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { status_with_mention.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { mention.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { media_attachment.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { favourite.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { active_relationship.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { passive_relationship.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { poll.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { poll_vote.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { account_note.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
- def delete_associated_target_records
- change(account_pins_for_account, :count).from(1).to(0)
+ def expect_deletion_of_associated_target_records
+ expect { endorsement.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
- def account_pins_for_account
- AccountPin.where(target_account: account)
- end
-
- def delete_associated_target_notifications
- change do
- %w(
- poll favourite status mention follow
- ).map { |type| Notification.where(type: type).count }
- end.from([1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0])
+ def expect_deletion_of_associated_target_notifications
+ expect { favourite_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { follow_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { mention_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { poll_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { status_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe '#call on local account' do
before do
- stub_request(:post, 'https://alice.com/inbox').to_return(status: 201)
- stub_request(:post, 'https://bob.com/inbox').to_return(status: 201)
+ stub_request(:post, remote_alice.inbox_url).to_return(status: 201)
+ stub_request(:post, remote_bob.inbox_url).to_return(status: 201)
end
let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', domain: 'alice.com', protocol: :activitypub) }
let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', domain: 'bob.com', protocol: :activitypub) }
include_examples 'common behavior' do
- let!(:account) { Fabricate(:account) }
- let!(:local_follower) { Fabricate(:account) }
+ let(:account) { Fabricate(:account) }
+ let(:local_follower) { Fabricate(:account) }
it 'sends a delete actor activity to all known inboxes' do
subject
- expect(a_request(:post, 'https://alice.com/inbox')).to have_been_made.once
- expect(a_request(:post, 'https://bob.com/inbox')).to have_been_made.once
+ expect(a_request(:post, remote_alice.inbox_url)).to have_been_made.once
+ expect(a_request(:post, remote_bob.inbox_url)).to have_been_made.once
end
end
end
describe '#call on remote account' do
before do
- stub_request(:post, 'https://alice.com/inbox').to_return(status: 201)
- stub_request(:post, 'https://bob.com/inbox').to_return(status: 201)
+ stub_request(:post, account.inbox_url).to_return(status: 201)
end
include_examples 'common behavior' do
- let!(:account) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') }
- let!(:local_follower) { Fabricate(:account) }
+ let(:account) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') }
+ let(:local_follower) { Fabricate(:account) }
it 'sends expected activities to followed and follower inboxes' do
subject
diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb
index 3936b2363..d67da66ca 100644
--- a/spec/services/import_service_spec.rb
+++ b/spec/services/import_service_spec.rb
@@ -190,7 +190,7 @@ RSpec.describe ImportService, type: :service do
# Make sure to not actually go to the remote server
before do
- stub_request(:post, 'https://թութ.հայ/inbox').to_return(status: 200)
+ stub_request(:post, nare.inbox_url).to_return(status: 200)
end
it 'follows the listed account' do
diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb
index 8fcb58658..50055dafc 100644
--- a/spec/services/notify_service_spec.rb
+++ b/spec/services/notify_service_spec.rb
@@ -67,9 +67,10 @@ RSpec.describe NotifyService, type: :service do
context 'when the message chain is initiated by recipient, but is not direct message' do
let(:reply_to) { Fabricate(:status, account: recipient) }
- let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) }
let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) }
+ before { Fabricate(:mention, account: sender, status: reply_to) }
+
it 'does not notify' do
expect { subject }.to_not change(Notification, :count)
end
@@ -77,10 +78,11 @@ RSpec.describe NotifyService, type: :service do
context 'when the message chain is initiated by recipient, but without a mention to the sender, even if the sender sends multiple messages in a row' do
let(:reply_to) { Fabricate(:status, account: recipient) }
- let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) }
let(:dummy_reply) { Fabricate(:status, account: sender, visibility: :direct, thread: reply_to) }
let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: dummy_reply)) }
+ before { Fabricate(:mention, account: sender, status: reply_to) }
+
it 'does not notify' do
expect { subject }.to_not change(Notification, :count)
end
@@ -88,9 +90,10 @@ RSpec.describe NotifyService, type: :service do
context 'when the message chain is initiated by the recipient with a mention to the sender' do
let(:reply_to) { Fabricate(:status, account: recipient, visibility: :direct) }
- let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) }
let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) }
+ before { Fabricate(:mention, account: sender, status: reply_to) }
+
it 'does notify' do
expect { subject }.to change(Notification, :count)
end
diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb
index 7754ae800..08ce0b045 100644
--- a/spec/services/remove_status_service_spec.rb
+++ b/spec/services/remove_status_service_spec.rb
@@ -12,15 +12,15 @@ RSpec.describe RemoveStatusService, type: :service do
let!(:bill) { Fabricate(:account, username: 'bill', protocol: :activitypub, domain: 'example2.com', inbox_url: 'http://example2.com/inbox') }
before do
- stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
- stub_request(:post, 'http://example2.com/inbox').to_return(status: 200)
+ stub_request(:post, hank.inbox_url).to_return(status: 200)
+ stub_request(:post, bill.inbox_url).to_return(status: 200)
jeff.follow!(alice)
hank.follow!(alice)
end
context 'when removed status is not a reblog' do
- let!(:status) { PostStatusService.new.call(alice, text: 'Hello @bob@example.com ThisIsASecret') }
+ let!(:status) { PostStatusService.new.call(alice, text: "Hello @#{bob.pretty_acct} ThisIsASecret") }
before do
FavouriteService.new.call(jeff, status)
@@ -39,7 +39,7 @@ RSpec.describe RemoveStatusService, type: :service do
it 'sends Delete activity to followers' do
subject.call(status)
- expect(a_request(:post, 'http://example.com/inbox').with(
+ expect(a_request(:post, hank.inbox_url).with(
body: hash_including({
'type' => 'Delete',
'object' => {
@@ -53,7 +53,7 @@ RSpec.describe RemoveStatusService, type: :service do
it 'sends Delete activity to rebloggers' do
subject.call(status)
- expect(a_request(:post, 'http://example2.com/inbox').with(
+ expect(a_request(:post, bill.inbox_url).with(
body: hash_including({
'type' => 'Delete',
'object' => {
@@ -78,7 +78,7 @@ RSpec.describe RemoveStatusService, type: :service do
it 'sends Undo activity to followers' do
subject.call(status)
- expect(a_request(:post, 'http://example.com/inbox').with(
+ expect(a_request(:post, hank.inbox_url).with(
body: hash_including({
'type' => 'Undo',
'object' => hash_including({
@@ -96,7 +96,7 @@ RSpec.describe RemoveStatusService, type: :service do
it 'sends Undo activity to followers' do
subject.call(status)
- expect(a_request(:post, 'http://example.com/inbox').with(
+ expect(a_request(:post, hank.inbox_url).with(
body: hash_including({
'type' => 'Undo',
'object' => hash_including({
diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb
index d3bcd5d31..2a919f640 100644
--- a/spec/services/report_service_spec.rb
+++ b/spec/services/report_service_spec.rb
@@ -156,9 +156,8 @@ RSpec.describe ReportService, type: :service do
-> { described_class.new.call(source_account, target_account) }
end
- let!(:other_report) { Fabricate(:report, target_account: target_account) }
-
before do
+ Fabricate(:report, target_account: target_account)
ActionMailer::Base.deliveries.clear
source_account.user.settings['notification_emails.report'] = true
source_account.user.save
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
index 9f5fdca29..3c4b182e2 100644
--- a/spec/services/resolve_account_service_spec.rb
+++ b/spec/services/resolve_account_service_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe ResolveAccountService, type: :service do
let!(:remote_account) { Fabricate(:account, username: 'foo', domain: 'ap.example.com', protocol: 'activitypub') }
context 'when domain is banned' do
- let!(:domain_block) { Fabricate(:domain_block, domain: 'ap.example.com', severity: :suspend) }
+ before { Fabricate(:domain_block, domain: 'ap.example.com', severity: :suspend) }
it 'does not return an account' do
expect(subject.call('foo@ap.example.com', skip_webfinger: true)).to be_nil
@@ -214,6 +214,7 @@ RSpec.describe ResolveAccountService, type: :service do
expect(account.domain).to eq 'ap.example.com'
expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
expect(account.uri).to eq 'https://ap.example.com/users/foo'
+ expect(status.reload.account).to eq(account)
end
end
diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb
index c258995b7..b309ce511 100644
--- a/spec/services/suspend_account_service_spec.rb
+++ b/spec/services/suspend_account_service_spec.rb
@@ -46,9 +46,9 @@ RSpec.describe SuspendAccountService, type: :service do
let!(:account) { Fabricate(:account) }
let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub, domain: 'alice.com') }
let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') }
- let!(:report) { Fabricate(:report, account: remote_reporter, target_account: account) }
before do
+ Fabricate(:report, account: remote_reporter, target_account: account)
remote_follower.follow!(account)
end
diff --git a/spec/services/unallow_domain_service_spec.rb b/spec/services/unallow_domain_service_spec.rb
index 19d40e7e8..d96e35418 100644
--- a/spec/services/unallow_domain_service_spec.rb
+++ b/spec/services/unallow_domain_service_spec.rb
@@ -27,6 +27,7 @@ RSpec.describe UnallowDomainService, type: :service do
end
it 'removes remote accounts from that domain' do
+ expect { already_banned_account.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect(Account.where(domain: 'evil.org').exists?).to be false
end
diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb
index 2f737c621..211c39e6c 100644
--- a/spec/services/unsuspend_account_service_spec.rb
+++ b/spec/services/unsuspend_account_service_spec.rb
@@ -39,9 +39,9 @@ RSpec.describe UnsuspendAccountService, type: :service do
let!(:account) { Fabricate(:account) }
let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub, domain: 'alice.com') }
let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') }
- let!(:report) { Fabricate(:report, account: remote_reporter, target_account: account) }
before do
+ Fabricate(:report, account: remote_reporter, target_account: account)
remote_follower.follow!(account)
end
diff --git a/spec/workers/scheduler/user_cleanup_scheduler_spec.rb b/spec/workers/scheduler/user_cleanup_scheduler_spec.rb
index 990979500..8fda246ba 100644
--- a/spec/workers/scheduler/user_cleanup_scheduler_spec.rb
+++ b/spec/workers/scheduler/user_cleanup_scheduler_spec.rb
@@ -18,12 +18,11 @@ describe Scheduler::UserCleanupScheduler do
confirmed_user.update!(confirmed_at: 1.day.ago)
end
- it 'deletes the old unconfirmed user' do
- expect { subject.perform }.to change { User.exists?(old_unconfirmed_user.id) }.from(true).to(false)
- end
-
- it "deletes the old unconfirmed user's account" do
- expect { subject.perform }.to change { Account.exists?(old_unconfirmed_user.account_id) }.from(true).to(false)
+ it 'deletes the old unconfirmed user, their account, and the moderation note' do
+ expect { subject.perform }
+ .to change { User.exists?(old_unconfirmed_user.id) }.from(true).to(false)
+ .and change { Account.exists?(old_unconfirmed_user.account_id) }.from(true).to(false)
+ expect { moderation_note.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'does not delete the new unconfirmed user or their account' do