mirror of
https://github.com/mastodon/mastodon.git
synced 2024-11-20 03:25:17 +01:00
Merge branch 'main' into patch-1
This commit is contained in:
commit
8210279f61
@ -815,7 +815,7 @@ GEM
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov_json_formatter (~> 0.1)
|
||||
simplecov-html (0.12.3)
|
||||
simplecov-html (0.13.1)
|
||||
simplecov-lcov (0.8.0)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
stackprof (0.2.26)
|
||||
|
@ -7,7 +7,23 @@ module WellKnown
|
||||
def show
|
||||
@webfinger_template = "#{webfinger_url}?resource={uri}"
|
||||
expires_in 3.days, public: true
|
||||
|
||||
respond_to do |format|
|
||||
format.any do
|
||||
render content_type: 'application/xrd+xml', formats: [:xml]
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: {
|
||||
links: [
|
||||
{
|
||||
rel: 'lrdd',
|
||||
template: @webfinger_template,
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -37,8 +37,7 @@ export const synchronouslySubmitMarkers = createAppAsyncThunk(
|
||||
});
|
||||
|
||||
return;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} else if ('navigator' && 'sendBeacon' in navigator) {
|
||||
} else if ('sendBeacon' in navigator) {
|
||||
// Failing that, we can use sendBeacon, but we have to encode the data as
|
||||
// FormData for DoorKeeper to recognize the token.
|
||||
const formData = new FormData();
|
||||
|
@ -85,6 +85,7 @@
|
||||
"alert.rate_limited.title": "Cyfradd gyfyngedig",
|
||||
"alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
|
||||
"alert.unexpected.title": "Wps!",
|
||||
"alt_text_badge.title": "Testun Amgen",
|
||||
"announcement.announcement": "Cyhoeddiad",
|
||||
"attachments_list.unprocessed": "(heb eu prosesu)",
|
||||
"audio.hide": "Cuddio sain",
|
||||
@ -221,6 +222,7 @@
|
||||
"domain_block_modal.they_cant_follow": "Ni all neb o'r gweinydd hwn eich dilyn.",
|
||||
"domain_block_modal.they_wont_know": "Fyddan nhw ddim yn gwybod eu bod wedi cael eu blocio.",
|
||||
"domain_block_modal.title": "Blocio parth?",
|
||||
"domain_block_modal.you_will_lose_relationships": "Byddwch yn colli'r holl ddilynwyr a phobl rydych chi'n eu dilyn o'r gweinydd hwn.",
|
||||
"domain_block_modal.you_wont_see_posts": "Fyddwch chi ddim yn gweld postiadau na hysbysiadau gan ddefnyddwyr ar y gweinydd hwn.",
|
||||
"domain_pill.activitypub_lets_connect": "Mae'n caniatáu ichi gysylltu a rhyngweithio â phobl nid yn unig ar Mastodon, ond ar draws gwahanol apiau cymdeithasol hefyd.",
|
||||
"domain_pill.activitypub_like_language": "Mae ActivityPub fel yr iaith y mae Mastodon yn ei siarad â rhwydweithiau cymdeithasol eraill.",
|
||||
@ -849,6 +851,11 @@
|
||||
"upload_error.poll": "Nid oes modd llwytho ffeiliau â phleidleisiau.",
|
||||
"upload_form.audio_description": "Disgrifio ar gyfer pobl sydd â cholled clyw",
|
||||
"upload_form.description": "Disgrifio i'r rheini a nam ar ei golwg",
|
||||
"upload_form.drag_and_drop.instructions": "I godi atodiad cyfryngau, pwyswch y space neu enter. Wrth lusgo, defnyddiwch y bysellau saeth i symud yr atodiad cyfryngau i unrhyw gyfeiriad penodol. Pwyswch space neu enter eto i ollwng yr atodiad cyfryngau yn ei safle newydd, neu pwyswch escape i ddiddymu.",
|
||||
"upload_form.drag_and_drop.on_drag_cancel": "Cafodd llusgo ei ddiddymu. Cafodd atodiad cyfryngau {item} ei ollwng.",
|
||||
"upload_form.drag_and_drop.on_drag_end": "Cafodd atodiad cyfryngau {item} ei ollwng.",
|
||||
"upload_form.drag_and_drop.on_drag_over": "Symudwyd atodiad cyfryngau {item}.",
|
||||
"upload_form.drag_and_drop.on_drag_start": "Atodiad cyfryngau godwyd {item}.",
|
||||
"upload_form.edit": "Golygu",
|
||||
"upload_form.thumbnail": "Newid llun bach",
|
||||
"upload_form.video_description": "Disgrifio ar gyfer pobl sydd â cholled clyw neu amhariad golwg",
|
||||
|
@ -22,10 +22,10 @@
|
||||
"account.cancel_follow_request": "Nuligi peton por sekvado",
|
||||
"account.copy": "Kopii ligilon al profilo",
|
||||
"account.direct": "Private mencii @{name}",
|
||||
"account.disable_notifications": "Ne plu sciigi min, kiam @{name} mesaĝas",
|
||||
"account.disable_notifications": "Ĉesu sciigi min kiam @{name} afiŝas",
|
||||
"account.domain_blocked": "Domajno blokita",
|
||||
"account.edit_profile": "Redakti la profilon",
|
||||
"account.enable_notifications": "Sciigi min, kiam @{name} mesaĝas",
|
||||
"account.enable_notifications": "Sciigu min kiam @{name} afiŝos",
|
||||
"account.endorse": "Rekomendi ĉe via profilo",
|
||||
"account.featured_tags.last_status_at": "Lasta afîŝo je {date}",
|
||||
"account.featured_tags.last_status_never": "Neniu afiŝo",
|
||||
@ -49,14 +49,14 @@
|
||||
"account.mention": "Mencii @{name}",
|
||||
"account.moved_to": "{name} indikis, ke ria nova konto estas nun:",
|
||||
"account.mute": "Silentigi @{name}",
|
||||
"account.mute_notifications_short": "Silentigu Sciigojn",
|
||||
"account.mute_notifications_short": "Silentigu sciigojn",
|
||||
"account.mute_short": "Silentigu",
|
||||
"account.muted": "Silentigita",
|
||||
"account.mutual": "Reciproka",
|
||||
"account.no_bio": "Neniu priskribo estas provizita.",
|
||||
"account.open_original_page": "Malfermi la originalan paĝon",
|
||||
"account.posts": "Afiŝoj",
|
||||
"account.posts_with_replies": "Mesaĝoj kaj respondoj",
|
||||
"account.posts_with_replies": "Afiŝoj kaj respondoj",
|
||||
"account.report": "Raporti @{name}",
|
||||
"account.requested": "Atendo de aprobo. Klaku por nuligi la peton por sekvado",
|
||||
"account.requested_follow": "{name} petis sekvi vin",
|
||||
@ -69,7 +69,7 @@
|
||||
"account.unendorse": "Ne plu rekomendi ĉe la profilo",
|
||||
"account.unfollow": "Ĉesi sekvi",
|
||||
"account.unmute": "Ne plu silentigi @{name}",
|
||||
"account.unmute_notifications_short": "Malsilentigu Sciigojn",
|
||||
"account.unmute_notifications_short": "Malsilentigu sciigojn",
|
||||
"account.unmute_short": "Ne plu silentigi",
|
||||
"account_note.placeholder": "Alklaku por aldoni noton",
|
||||
"admin.dashboard.daily_retention": "Uzantoretenprocento lau tag post registro",
|
||||
@ -81,7 +81,7 @@
|
||||
"admin.impact_report.instance_followers": "Sekvantojn niaj uzantoj perdus",
|
||||
"admin.impact_report.instance_follows": "Sekvantojn ties uzantoj perdus",
|
||||
"admin.impact_report.title": "Influa reporto",
|
||||
"alert.rate_limited.message": "Bonvolu reprovi post {retry_time, time, medium}.",
|
||||
"alert.rate_limited.message": "Bonvolu reprovi poste {retry_time, time, medium}.",
|
||||
"alert.rate_limited.title": "Mesaĝkvante limigita",
|
||||
"alert.unexpected.message": "Neatendita eraro okazis.",
|
||||
"alert.unexpected.title": "Aj!",
|
||||
@ -163,7 +163,7 @@
|
||||
"compose_form.poll.switch_to_single": "Ŝanĝi la balotenketon por permesi unu solan elekton",
|
||||
"compose_form.poll.type": "Stilo",
|
||||
"compose_form.publish": "Afiŝo",
|
||||
"compose_form.publish_form": "Afiŝi",
|
||||
"compose_form.publish_form": "Nova afiŝo",
|
||||
"compose_form.reply": "Respondi",
|
||||
"compose_form.save_changes": "Ĝisdatigi",
|
||||
"compose_form.spoiler.marked": "Forigi la averton de enhavo",
|
||||
@ -173,7 +173,7 @@
|
||||
"confirmations.block.confirm": "Bloki",
|
||||
"confirmations.delete.confirm": "Forigi",
|
||||
"confirmations.delete.message": "Ĉu vi certas, ke vi volas forigi ĉi tiun afiŝon?",
|
||||
"confirmations.delete.title": "Ĉu forigi Afiŝon?",
|
||||
"confirmations.delete.title": "Ĉu forigi afiŝon?",
|
||||
"confirmations.delete_list.confirm": "Forigi",
|
||||
"confirmations.delete_list.message": "Ĉu vi certas, ke vi volas porĉiame forigi ĉi tiun liston?",
|
||||
"confirmations.delete_list.title": "Ĉu forigi liston?",
|
||||
@ -213,9 +213,9 @@
|
||||
"dismissable_banner.community_timeline": "Jen la plej novaj publikaj afiŝoj de uzantoj, kies kontojn gastigas {domain}.",
|
||||
"dismissable_banner.dismiss": "Eksigi",
|
||||
"dismissable_banner.explore_links": "Tiuj novaĵoj estas aktuale priparolataj de uzantoj en tiu ĉi kaj aliaj serviloj, sur la malcentrigita reto.",
|
||||
"dismissable_banner.explore_statuses": "Ĉi tioj estas afiŝoj de socia reto kiu populariĝas hodiau.",
|
||||
"dismissable_banner.explore_statuses": "Ĉi tiuj estas afiŝoj de la tuta socia reto, kiuj populariĝas hodiaŭ. Pli novaj afiŝoj kun pli da diskonigoj kaj plej ŝatataj estas rangigitaj pli alte.",
|
||||
"dismissable_banner.explore_tags": "Ĉi tiuj kradvostoj populariĝas en ĉi tiu kaj aliaj serviloj en la malcentraliza reto nun.",
|
||||
"dismissable_banner.public_timeline": "Ĉi tioj estas plej lastaj publikaj afiŝoj de personoj ĉe socia reto kiu personoj ĉe {domain} sekvas.",
|
||||
"dismissable_banner.public_timeline": "Ĉi tiuj estas la plej lastatempaj publikaj afiŝoj de homoj en la socia reto, kiujn homoj sur {domain} sekvas.",
|
||||
"domain_block_modal.block": "Bloki servilon",
|
||||
"domain_block_modal.block_account_instead": "Bloki @{name} anstataŭe",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Homoj de ĉi tiu servilo povas interagi kun viaj malnovaj afiŝoj.",
|
||||
@ -265,8 +265,8 @@
|
||||
"empty_column.direct": "Vi ankoraŭ ne havas privatan mencion. Kiam vi sendos aŭ ricevos iun, tiu aperos ĉi tie.",
|
||||
"empty_column.domain_blocks": "Ankoraŭ neniu domajno estas blokita.",
|
||||
"empty_column.explore_statuses": "Nenio tendencas nun. Rekontrolu poste!",
|
||||
"empty_column.favourited_statuses": "Vi ankoraŭ ne havas stelumitan afiŝon.",
|
||||
"empty_column.favourites": "Ankoraŭ neniu stelumis tiun afiŝon.",
|
||||
"empty_column.favourited_statuses": "Vi ankoraŭ ne havas plej ŝatatajn afiŝojn. Kiam vi ŝatatas unu, ĝi aperos ĉi tie.",
|
||||
"empty_column.favourites": "Neniu ankoraŭ ŝatis ĉi tiun afiŝon. Kiam iu ŝatos ĝin, ili aperos ĉi tie.",
|
||||
"empty_column.follow_requests": "Vi ne ankoraŭ havas iun peton de sekvado. Kiam vi ricevos unu, ĝi aperos ĉi tie.",
|
||||
"empty_column.followed_tags": "Vi ankoraŭ ne sekvas iujn kradvortojn. Kiam vi faras, ili aperos ĉi tie.",
|
||||
"empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.",
|
||||
@ -296,7 +296,7 @@
|
||||
"filter_modal.added.review_and_configure": "Por kontroli kaj pli modifi ĉi tiu filtrilkategorio, iru al la {settings_link}.",
|
||||
"filter_modal.added.review_and_configure_title": "Filtrilopcioj",
|
||||
"filter_modal.added.settings_link": "opciopaĝo",
|
||||
"filter_modal.added.short_explanation": "Ĉi tiu mesaĝo aldonitas al la filtrilkategorio: {title}.",
|
||||
"filter_modal.added.short_explanation": "Ĉi tiu afiŝo aldonitas al la filtrilkategorio: {title}.",
|
||||
"filter_modal.added.title": "Filtrilo aldonita!",
|
||||
"filter_modal.select_filter.context_mismatch": "ne kongruas la kuntekston",
|
||||
"filter_modal.select_filter.expired": "eksvalidiĝinta",
|
||||
@ -304,7 +304,7 @@
|
||||
"filter_modal.select_filter.search": "Serĉi aŭ krei",
|
||||
"filter_modal.select_filter.subtitle": "Uzu ekzistantan kategorion aŭ kreu novan",
|
||||
"filter_modal.select_filter.title": "Filtri ĉi tiun afiŝon",
|
||||
"filter_modal.title.status": "Filtri mesaĝon",
|
||||
"filter_modal.title.status": "Filtri afiŝon",
|
||||
"filter_warning.matches_filter": "Filtrilo de kongruoj “{title}”",
|
||||
"filtered_notifications_banner.pending_requests": "El {count, plural, =0 {neniu} one {unu persono} other {# homoj}} vi eble konas",
|
||||
"filtered_notifications_banner.title": "Filtritaj sciigoj",
|
||||
@ -351,7 +351,7 @@
|
||||
"hashtag.column_settings.tag_toggle": "Aldoni pliajn etikedojn por ĉi tiu kolumno",
|
||||
"hashtag.counter_by_accounts": "{count, plural,one {{counter} partoprenanto} other {{counter} partoprenantoj}}",
|
||||
"hashtag.counter_by_uses": "{count, plural,one {{counter} afiŝo} other {{counter} afiŝoj}}",
|
||||
"hashtag.counter_by_uses_today": "{count, plural,one {{counter} afiŝo} other {{counter} afiŝoj}} hodiau",
|
||||
"hashtag.counter_by_uses_today": "{count, plural,one {{counter} afiŝo} other {{counter} afiŝoj}} hodiaŭ",
|
||||
"hashtag.follow": "Sekvi la kradvorton",
|
||||
"hashtag.unfollow": "Ne plu sekvi la kradvorton",
|
||||
"hashtags.and_other": "…kaj {count, plural,other {# pli}}",
|
||||
@ -382,9 +382,9 @@
|
||||
"ignore_notifications_modal.not_following_title": "Ĉu ignori sciigojn de homoj, kiujn vi ne sekvas?",
|
||||
"ignore_notifications_modal.private_mentions_title": "Ĉu ignori sciigojn de nepetitaj privataj mencioj?",
|
||||
"interaction_modal.description.favourite": "Per konto ĉe Mastodon, vi povas stelumiti ĉi tiun afiŝon por sciigi la afiŝanton ke vi aprezigas ŝin kaj konservas por la estonteco.",
|
||||
"interaction_modal.description.follow": "Kun konto ĉe Mastodon, vi povos sekvi {name} por vidi ties mesaĝojn en via hejmo.",
|
||||
"interaction_modal.description.follow": "Kun konto ĉe Mastodon, vi povas sekvi {name} por ricevi iliajn afiŝojn en via hejma fluo.",
|
||||
"interaction_modal.description.reblog": "Kun konto ĉe Mastodon, vi povas diskonigi ĉi tiun afiŝon, por ke viaj propraj sekvantoj vidu ĝin.",
|
||||
"interaction_modal.description.reply": "Kun konto ĉe Mastodon, vi povos respondi al ĉi tiu mesaĝo.",
|
||||
"interaction_modal.description.reply": "Kun konto ĉe Mastodon, vi povos respondi al ĉi tiu afiŝo.",
|
||||
"interaction_modal.login.action": "Prenu min hejmen",
|
||||
"interaction_modal.login.prompt": "Domajno de via hejma servilo, ekz. mastodon.social",
|
||||
"interaction_modal.no_account_yet": "Ĉu ne estas ĉe Mastodon?",
|
||||
@ -402,12 +402,12 @@
|
||||
"keyboard_shortcuts.back": "reveni",
|
||||
"keyboard_shortcuts.blocked": "Malfermi la liston de blokitaj uzantoj",
|
||||
"keyboard_shortcuts.boost": "Diskonigi la mesaĝon",
|
||||
"keyboard_shortcuts.column": "fokusi mesaĝon en unu el la kolumnoj",
|
||||
"keyboard_shortcuts.column": "Fokusi kolumnon",
|
||||
"keyboard_shortcuts.compose": "enfokusigi la tekstujon",
|
||||
"keyboard_shortcuts.description": "Priskribo",
|
||||
"keyboard_shortcuts.direct": "por malfermi la kolumnon pri privataj mencioj",
|
||||
"keyboard_shortcuts.down": "iri suben en la listo",
|
||||
"keyboard_shortcuts.enter": "malfermi mesaĝon",
|
||||
"keyboard_shortcuts.enter": "Malfermi afiŝon",
|
||||
"keyboard_shortcuts.favourite": "Stelumi afiŝon",
|
||||
"keyboard_shortcuts.favourites": "Malfermi la liston de la stelumoj",
|
||||
"keyboard_shortcuts.federated": "Malfermi la frataran templinion",
|
||||
@ -421,16 +421,16 @@
|
||||
"keyboard_shortcuts.my_profile": "malfermi vian profilon",
|
||||
"keyboard_shortcuts.notifications": "malfermi la kolumnon de sciigoj",
|
||||
"keyboard_shortcuts.open_media": "Malfermi plurmedion",
|
||||
"keyboard_shortcuts.pinned": "malfermi la liston de alpinglitaj mesaĝoj",
|
||||
"keyboard_shortcuts.pinned": "Malfermu alpinglitajn afiŝojn-liston",
|
||||
"keyboard_shortcuts.profile": "malfermi la profilon de la aŭtoro",
|
||||
"keyboard_shortcuts.reply": "respondi",
|
||||
"keyboard_shortcuts.reply": "Respondu al afiŝo",
|
||||
"keyboard_shortcuts.requests": "Malfermi la liston de petoj por sekvado",
|
||||
"keyboard_shortcuts.search": "enfokusigi la serĉilon",
|
||||
"keyboard_shortcuts.spoilers": "Montri/kaŝi la kampon de averto de enhavo (\"CW\")",
|
||||
"keyboard_shortcuts.start": "malfermi la kolumnon «por komenci»",
|
||||
"keyboard_shortcuts.toggle_hidden": "Montri/kaŝi tekston malantaŭ la averto de enhavo (\"CW\")",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "Montri/kaŝi plurmedion",
|
||||
"keyboard_shortcuts.toot": "Krei novan mesaĝon",
|
||||
"keyboard_shortcuts.toot": "Komencu novan afiŝon",
|
||||
"keyboard_shortcuts.unfocus": "malenfokusigi la tekstujon aŭ la serĉilon",
|
||||
"keyboard_shortcuts.up": "iri supren en la listo",
|
||||
"lightbox.close": "Fermi",
|
||||
@ -476,9 +476,9 @@
|
||||
"navigation_bar.blocks": "Blokitaj uzantoj",
|
||||
"navigation_bar.bookmarks": "Legosignoj",
|
||||
"navigation_bar.community_timeline": "Loka templinio",
|
||||
"navigation_bar.compose": "Skribi novan mesaĝon",
|
||||
"navigation_bar.compose": "Redakti novan afiŝon",
|
||||
"navigation_bar.direct": "Privataj mencioj",
|
||||
"navigation_bar.discover": "Esplori",
|
||||
"navigation_bar.discover": "Malkovri",
|
||||
"navigation_bar.domain_blocks": "Blokitaj domajnoj",
|
||||
"navigation_bar.explore": "Esplori",
|
||||
"navigation_bar.favourites": "Stelumoj",
|
||||
@ -487,12 +487,12 @@
|
||||
"navigation_bar.followed_tags": "Sekvataj kradvortoj",
|
||||
"navigation_bar.follows_and_followers": "Sekvatoj kaj sekvantoj",
|
||||
"navigation_bar.lists": "Listoj",
|
||||
"navigation_bar.logout": "Adiaŭi",
|
||||
"navigation_bar.logout": "Elsaluti",
|
||||
"navigation_bar.moderation": "Modereco",
|
||||
"navigation_bar.mutes": "Silentigitaj uzantoj",
|
||||
"navigation_bar.opened_in_classic_interface": "Afiŝoj, kontoj, kaj aliaj specifaj paĝoj kiuj estas malfermititaj defaulta en la klasika reta interfaco.",
|
||||
"navigation_bar.personal": "Persone",
|
||||
"navigation_bar.pins": "Alpinglitaj mesaĝoj",
|
||||
"navigation_bar.pins": "Alpinglitaj afiŝoj",
|
||||
"navigation_bar.preferences": "Preferoj",
|
||||
"navigation_bar.public_timeline": "Fratara templinio",
|
||||
"navigation_bar.search": "Serĉi",
|
||||
@ -572,7 +572,7 @@
|
||||
"notifications.column_settings.reblog": "Diskonigoj:",
|
||||
"notifications.column_settings.show": "Montri en kolumno",
|
||||
"notifications.column_settings.sound": "Eligi sonon",
|
||||
"notifications.column_settings.status": "Novaj mesaĝoj:",
|
||||
"notifications.column_settings.status": "Novaj afiŝoj:",
|
||||
"notifications.column_settings.unread_notifications.category": "Nelegitaj sciigoj",
|
||||
"notifications.column_settings.unread_notifications.highlight": "Marki nelegitajn sciigojn",
|
||||
"notifications.column_settings.update": "Redaktoj:",
|
||||
@ -660,7 +660,7 @@
|
||||
"poll.votes": "{votes, plural, one {# voĉdono} other {# voĉdonoj}}",
|
||||
"poll_button.add_poll": "Aldoni balotenketon",
|
||||
"poll_button.remove_poll": "Forigi balotenketon",
|
||||
"privacy.change": "Agordi mesaĝan privatecon",
|
||||
"privacy.change": "Ŝanĝu afiŝan privatecon",
|
||||
"privacy.direct.long": "Ĉiuj menciitaj en la afiŝo",
|
||||
"privacy.direct.short": "Specifaj homoj",
|
||||
"privacy.private.long": "Nur viaj sekvantoj",
|
||||
@ -775,13 +775,13 @@
|
||||
"sign_in_banner.sso_redirect": "Ensalutu aŭ Registriĝi",
|
||||
"status.admin_account": "Malfermi fasadon de moderigado por @{name}",
|
||||
"status.admin_domain": "Malfermu moderigan interfacon por {domain}",
|
||||
"status.admin_status": "Malfermi ĉi tiun mesaĝon en la kontrola interfaco",
|
||||
"status.admin_status": "Malfermi ĉi tiun afiŝon en la kontrola interfaco",
|
||||
"status.block": "Bloki @{name}",
|
||||
"status.bookmark": "Aldoni al la legosignoj",
|
||||
"status.cancel_reblog_private": "Ne plu diskonigi",
|
||||
"status.cannot_reblog": "Ĉi tiun afiŝon ne eblas diskonigi",
|
||||
"status.continued_thread": "Daŭrigis fadenon",
|
||||
"status.copy": "Kopii la ligilon al la mesaĝo",
|
||||
"status.copy": "Kopii la ligilon al la afiŝo",
|
||||
"status.delete": "Forigi",
|
||||
"status.detailed_status": "Detala konversacia vido",
|
||||
"status.direct": "Private mencii @{name}",
|
||||
@ -803,9 +803,9 @@
|
||||
"status.more": "Pli",
|
||||
"status.mute": "Silentigi @{name}",
|
||||
"status.mute_conversation": "Silentigi konversacion",
|
||||
"status.open": "Disvolvi la mesaĝon",
|
||||
"status.open": "Pligrandigu ĉi tiun afiŝon",
|
||||
"status.pin": "Alpingli al la profilo",
|
||||
"status.pinned": "Alpinglita mesaĝo",
|
||||
"status.pinned": "Alpinglita afiŝo",
|
||||
"status.read_more": "Legi pli",
|
||||
"status.reblog": "Diskonigi",
|
||||
"status.reblog_private": "Diskonigi kun la sama videbleco",
|
||||
|
@ -193,7 +193,7 @@
|
||||
"confirmations.reply.message": "Ao responder sobrescribirás a mensaxe que estás a compor. Tes a certeza de que queres continuar?",
|
||||
"confirmations.reply.title": "Editar a publicación?",
|
||||
"confirmations.unfollow.confirm": "Deixar de seguir",
|
||||
"confirmations.unfollow.message": "Desexas deixar de seguir a {name}?",
|
||||
"confirmations.unfollow.message": "Tes certeza de querer deixar de seguir a {name}?",
|
||||
"confirmations.unfollow.title": "Deixar de seguir á usuaria?",
|
||||
"content_warning.hide": "Agochar publicación",
|
||||
"content_warning.show": "Mostrar igualmente",
|
||||
|
@ -85,6 +85,7 @@
|
||||
"alert.rate_limited.title": "חלה הגבלה על קצב התעבורה",
|
||||
"alert.unexpected.message": "אירעה שגיאה בלתי צפויה.",
|
||||
"alert.unexpected.title": "אופס!",
|
||||
"alt_text_badge.title": "כיתוב חלופי",
|
||||
"announcement.announcement": "הכרזה",
|
||||
"attachments_list.unprocessed": "(לא מעובד)",
|
||||
"audio.hide": "השתק",
|
||||
@ -221,6 +222,8 @@
|
||||
"domain_block_modal.they_cant_follow": "משתמש משרת זה לא יכול לעקוב אחריך.",
|
||||
"domain_block_modal.they_wont_know": "הם לא ידעו כי נחסמו.",
|
||||
"domain_block_modal.title": "לחסום שרת?",
|
||||
"domain_block_modal.you_will_lose_num_followers": "{followersCount, plural,one {יאבד לך עוקב אחד}other {יאבדו לך {followersCountDisplay} עוקבים}} {followingCount, plural,one {ונעקב אחד}other {ו־{followingCountDisplay} נעקבים}}.",
|
||||
"domain_block_modal.you_will_lose_relationships": "יאבדו לך כל העוקבים והנעקבים משרת זה.",
|
||||
"domain_block_modal.you_wont_see_posts": "לא תוכלו לראות הודעות ממשתמשים על שרת זה.",
|
||||
"domain_pill.activitypub_lets_connect": "מאפשר לך להתחבר ולהתרועע עם אחרים לא רק במסטודון, אלא גם ביישומים חברתיים שונים אחרים.",
|
||||
"domain_pill.activitypub_like_language": "אקטיביטיפאב היא למעשה השפה בה מסטודון מדבר עם רשתות חברתיות אחרות.",
|
||||
@ -849,6 +852,11 @@
|
||||
"upload_error.poll": "לא ניתן להעלות קובץ עם סקר.",
|
||||
"upload_form.audio_description": "תאר/י עבור לקויי שמיעה",
|
||||
"upload_form.description": "תיאור לכבדי ראיה",
|
||||
"upload_form.drag_and_drop.instructions": "כדי לבחור קובץ מוצמד, יש ללחוץ על מקש רווח או אנטר. בעת הגרירה, השתמשו במקשי החיצים כדי להזיז את הקובץ המוצמד בכל כיוון. לחצו רווח או אנטר בשנית כדי לעזוב את הקובץ במקומו החדש, או לחצו אסקייפ לביטול.",
|
||||
"upload_form.drag_and_drop.on_drag_cancel": "הגרירה בוטלה. קובץ המדיה {item} נעזב.",
|
||||
"upload_form.drag_and_drop.on_drag_end": "קובץ המדיה {item} נעזב.",
|
||||
"upload_form.drag_and_drop.on_drag_over": "קובץ המדיה {item} הוזז.",
|
||||
"upload_form.drag_and_drop.on_drag_start": "קובץ המדיה {item} נבחר.",
|
||||
"upload_form.edit": "עריכה",
|
||||
"upload_form.thumbnail": "שנה/י תמונה ממוזערת",
|
||||
"upload_form.video_description": "תאר/י עבור לקויי שמיעה ולקויי ראייה",
|
||||
|
@ -852,8 +852,8 @@
|
||||
"upload_error.poll": "Szavazásnál nem lehet fájlt feltölteni.",
|
||||
"upload_form.audio_description": "Leírás siket vagy hallássérült emberek számára",
|
||||
"upload_form.description": "Leírás vak vagy gyengénlátó emberek számára",
|
||||
"upload_form.drag_and_drop.instructions": "Egy médiamelléklet kiválasztásához nyomjon Szóközt vagy Entert. Húzás közben használja a nyílgombokat a médiamelléklet adott irányba történő mozgatásához. A médiamelléklet új pozícióba helyezéséhez nyomja meg a Szóközt vagy az Entert, vagy a megszakításhoz nyomja meg az Esc gombot.",
|
||||
"upload_form.drag_and_drop.on_drag_cancel": "Az áthúzást megszakította. A(z) {item} médiamelléklet el lett dobva.",
|
||||
"upload_form.drag_and_drop.instructions": "Egy médiamelléklet kiválasztásához nyomj Szóközt vagy Entert. Húzás közben használd a nyílgombokat a médiamelléklet adott irányba történő mozgatásához. A médiamelléklet új pozícióba helyezéséhez nyomd meg a Szóközt vagy az Entert, vagy a megszakításhoz nyomd meg az Esc gombot.",
|
||||
"upload_form.drag_and_drop.on_drag_cancel": "Az áthúzás megszakítva. A(z) {item} médiamelléklet el lett dobva.",
|
||||
"upload_form.drag_and_drop.on_drag_end": "A(z) {item} médiamelléklet el lett dobva.",
|
||||
"upload_form.drag_and_drop.on_drag_over": "A(z) {item} médiamelléklet át lett helyezve.",
|
||||
"upload_form.drag_and_drop.on_drag_start": "A(z) {item} médiamelléklet fel lett véve.",
|
||||
|
@ -85,6 +85,7 @@
|
||||
"alert.rate_limited.title": "制限に達しました",
|
||||
"alert.unexpected.message": "不明なエラーが発生しました。",
|
||||
"alert.unexpected.title": "エラー!",
|
||||
"alt_text_badge.title": "代替テキスト",
|
||||
"announcement.announcement": "お知らせ",
|
||||
"attachments_list.unprocessed": "(未処理)",
|
||||
"audio.hide": "音声を閉じる",
|
||||
|
@ -585,6 +585,7 @@
|
||||
"status.bookmark": "Creḍ",
|
||||
"status.cancel_reblog_private": "Sefsex beṭṭu",
|
||||
"status.cannot_reblog": "Tasuffeɣt-a ur tezmir ara ad tettwabḍu tikelt-nniḍen",
|
||||
"status.continued_thread": "Asentel yettkemmil",
|
||||
"status.copy": "Nɣel assaɣ ɣer tasuffeɣt",
|
||||
"status.delete": "Kkes",
|
||||
"status.direct": "Bder-d @{name} weḥd-s",
|
||||
@ -616,6 +617,7 @@
|
||||
"status.reblogs.empty": "Ula yiwen ur yebḍi tajewwiqt-agi ar tura. Ticki yebḍa-tt yiwen, ad d-iban da.",
|
||||
"status.redraft": "Kkes tɛiwdeḍ tira",
|
||||
"status.remove_bookmark": "Kkes tacreḍt",
|
||||
"status.replied_in_thread": "Y·t·erra-d deg usentel",
|
||||
"status.replied_to": "Y·terra-yas i {name}",
|
||||
"status.reply": "Err",
|
||||
"status.replyAll": "Err i lxiḍ",
|
||||
|
@ -502,6 +502,8 @@
|
||||
"notification.reblog": "{name} fremhevet ditt innlegg",
|
||||
"notification.status": "{name} la nettopp ut",
|
||||
"notification.update": "{name} redigerte et innlegg",
|
||||
"notification_requests.minimize_banner": "Minimer banneret for filtrerte varsler",
|
||||
"notification_requests.view": "Vis varsler",
|
||||
"notifications.clear": "Fjern varsler",
|
||||
"notifications.clear_confirmation": "Er du sikker på at du vil fjerne alle dine varsler permanent?",
|
||||
"notifications.column_settings.admin.report": "Nye rapporter:",
|
||||
|
@ -852,6 +852,11 @@
|
||||
"upload_error.poll": "Anketlerde dosya yüklemesine izin verilmez.",
|
||||
"upload_form.audio_description": "İşitme kaybı olan kişiler için yazı ekleyiniz",
|
||||
"upload_form.description": "Görme engelliler için açıklama",
|
||||
"upload_form.drag_and_drop.instructions": "Bir medya eklentisini taşımak için, boşluk veya enter tuşuna basın. Sürükleme sırasında medya eklentisini herhangi bir yöne hareket ettirmek için ok tuşlarını kullanın. Medya eklentisini yeni konumuna bırakmak için tekrar boşluk veya enter tuşuna basın veya işlemi iptal etmek için escape tuşuna basın.",
|
||||
"upload_form.drag_and_drop.on_drag_cancel": "Sürükleme iptal edildi. Medya eklentisi {item} bırakıldı.",
|
||||
"upload_form.drag_and_drop.on_drag_end": "Medya eklentisi {item} bırakıldı.",
|
||||
"upload_form.drag_and_drop.on_drag_over": "Medya eklentisi {item} hareket ettirildi.",
|
||||
"upload_form.drag_and_drop.on_drag_start": "Medya eklentisi {item} tutuldu.",
|
||||
"upload_form.edit": "Düzenle",
|
||||
"upload_form.thumbnail": "Küçük resmi değiştir",
|
||||
"upload_form.video_description": "İşitme kaybı veya görme engeli olan kişiler için açıklama ekleyiniz",
|
||||
|
@ -42,6 +42,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
def process_status
|
||||
@tags = []
|
||||
@mentions = []
|
||||
@unresolved_mentions = []
|
||||
@silenced_account_ids = []
|
||||
@params = {}
|
||||
|
||||
@ -55,6 +56,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
end
|
||||
|
||||
resolve_thread(@status)
|
||||
resolve_unresolved_mentions(@status)
|
||||
fetch_replies(@status)
|
||||
distribute
|
||||
forward_for_reply
|
||||
@ -197,6 +199,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
return if account.nil?
|
||||
|
||||
@mentions << Mention.new(account: account, silent: false)
|
||||
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError
|
||||
@unresolved_mentions << tag['href']
|
||||
end
|
||||
|
||||
def process_emoji(tag)
|
||||
@ -301,6 +305,12 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
ThreadResolveWorker.perform_async(status.id, in_reply_to_uri, { 'request_id' => @options[:request_id] })
|
||||
end
|
||||
|
||||
def resolve_unresolved_mentions(status)
|
||||
@unresolved_mentions.uniq.each do |uri|
|
||||
MentionResolveWorker.perform_in(rand(30...600).seconds, status.id, uri, { 'request_id' => @options[:request_id] })
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_replies(status)
|
||||
collection = @object['replies']
|
||||
return if collection.blank?
|
||||
|
@ -9,10 +9,10 @@ class Vacuum::ImportsVacuum
|
||||
private
|
||||
|
||||
def clean_unconfirmed_imports!
|
||||
BulkImport.state_unconfirmed.where(created_at: ..10.minutes.ago).reorder(nil).in_batches.delete_all
|
||||
BulkImport.state_unconfirmed.where(created_at: ..10.minutes.ago).in_batches.delete_all
|
||||
end
|
||||
|
||||
def clean_old_imports!
|
||||
BulkImport.where(created_at: ..1.week.ago).reorder(nil).in_batches.delete_all
|
||||
BulkImport.where(created_at: ..1.week.ago).in_batches.delete_all
|
||||
end
|
||||
end
|
||||
|
72
app/lib/web_push_request.rb
Normal file
72
app/lib/web_push_request.rb
Normal file
@ -0,0 +1,72 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WebPushRequest
|
||||
SIGNATURE_ALGORITHM = 'p256ecdsa'
|
||||
AUTH_HEADER = 'WebPush'
|
||||
PAYLOAD_EXPIRATION = 24.hours
|
||||
JWT_ALGORITHM = 'ES256'
|
||||
JWT_TYPE = 'JWT'
|
||||
|
||||
attr_reader :web_push_subscription
|
||||
|
||||
delegate(
|
||||
:endpoint,
|
||||
:key_auth,
|
||||
:key_p256dh,
|
||||
to: :web_push_subscription
|
||||
)
|
||||
|
||||
def initialize(web_push_subscription)
|
||||
@web_push_subscription = web_push_subscription
|
||||
end
|
||||
|
||||
def audience
|
||||
@audience ||= Addressable::URI.parse(endpoint).normalized_site
|
||||
end
|
||||
|
||||
def authorization_header
|
||||
[AUTH_HEADER, encoded_json_web_token].join(' ')
|
||||
end
|
||||
|
||||
def crypto_key_header
|
||||
[SIGNATURE_ALGORITHM, vapid_key.public_key_for_push_header].join('=')
|
||||
end
|
||||
|
||||
def encrypt(payload)
|
||||
Webpush::Encryption.encrypt(payload, key_p256dh, key_auth)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def encoded_json_web_token
|
||||
JWT.encode(
|
||||
web_token_payload,
|
||||
vapid_key.curve,
|
||||
JWT_ALGORITHM,
|
||||
typ: JWT_TYPE
|
||||
)
|
||||
end
|
||||
|
||||
def web_token_payload
|
||||
{
|
||||
aud: audience,
|
||||
exp: PAYLOAD_EXPIRATION.from_now.to_i,
|
||||
sub: payload_subject,
|
||||
}
|
||||
end
|
||||
|
||||
def payload_subject
|
||||
[:mailto, contact_email].join(':')
|
||||
end
|
||||
|
||||
def vapid_key
|
||||
@vapid_key ||= Webpush::VapidKey.from_keys(
|
||||
Rails.configuration.x.vapid_public_key,
|
||||
Rails.configuration.x.vapid_private_key
|
||||
)
|
||||
end
|
||||
|
||||
def contact_email
|
||||
@contact_email ||= ::Setting.site_contact_email
|
||||
end
|
||||
end
|
@ -21,7 +21,7 @@ class AccountFilter
|
||||
end
|
||||
|
||||
def results
|
||||
scope = Account.includes(:account_stat, user: [:ips, :invite_request]).without_instance_actor.reorder(nil)
|
||||
scope = Account.includes(:account_stat, user: [:ips, :invite_request]).without_instance_actor
|
||||
|
||||
relevant_params.each do |key, value|
|
||||
next if key.to_s == 'page'
|
||||
|
@ -14,7 +14,7 @@ class Admin::TagFilter
|
||||
end
|
||||
|
||||
def results
|
||||
scope = Tag.reorder(nil)
|
||||
scope = Tag.all
|
||||
|
||||
params.each do |key, value|
|
||||
next if key == :page
|
||||
|
@ -29,26 +29,6 @@ class Web::PushSubscription < ApplicationRecord
|
||||
|
||||
delegate :locale, to: :associated_user
|
||||
|
||||
def encrypt(payload)
|
||||
Webpush::Encryption.encrypt(payload, key_p256dh, key_auth)
|
||||
end
|
||||
|
||||
def audience
|
||||
@audience ||= Addressable::URI.parse(endpoint).normalized_site
|
||||
end
|
||||
|
||||
def crypto_key_header
|
||||
p256ecdsa = vapid_key.public_key_for_push_header
|
||||
|
||||
"p256ecdsa=#{p256ecdsa}"
|
||||
end
|
||||
|
||||
def authorization_header
|
||||
jwt = JWT.encode({ aud: audience, exp: 24.hours.from_now.to_i, sub: "mailto:#{contact_email}" }, vapid_key.curve, 'ES256', typ: 'JWT')
|
||||
|
||||
"WebPush #{jwt}"
|
||||
end
|
||||
|
||||
def pushable?(notification)
|
||||
policy_allows_notification?(notification) && alert_enabled_for_notification_type?(notification)
|
||||
end
|
||||
@ -92,14 +72,6 @@ class Web::PushSubscription < ApplicationRecord
|
||||
)
|
||||
end
|
||||
|
||||
def vapid_key
|
||||
@vapid_key ||= Webpush::VapidKey.from_keys(Rails.configuration.x.vapid_public_key, Rails.configuration.x.vapid_private_key)
|
||||
end
|
||||
|
||||
def contact_email
|
||||
@contact_email ||= ::Setting.site_contact_email
|
||||
end
|
||||
|
||||
def alert_enabled_for_notification_type?(notification)
|
||||
truthy?(data&.dig('alerts', notification.type.to_s))
|
||||
end
|
||||
|
@ -16,12 +16,12 @@ class PurgeDomainService < BaseService
|
||||
end
|
||||
|
||||
def purge_accounts!
|
||||
Account.remote.where(domain: @domain).reorder(nil).find_each do |account|
|
||||
Account.remote.where(domain: @domain).find_each do |account|
|
||||
DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true)
|
||||
end
|
||||
end
|
||||
|
||||
def purge_emojis!
|
||||
CustomEmoji.remote.where(domain: @domain).reorder(nil).find_each(&:destroy)
|
||||
CustomEmoji.remote.where(domain: @domain).find_each(&:destroy)
|
||||
end
|
||||
end
|
||||
|
@ -30,7 +30,7 @@
|
||||
= render 'admin/accounts/counters', account: @account
|
||||
|
||||
- if @account.local? && @account.user.nil?
|
||||
= link_to t('admin.accounts.unblock_email'), unblock_email_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unblock_email, @account) && CanonicalEmailBlock.exists?(reference_account_id: @account.id)
|
||||
= button_to t('admin.accounts.unblock_email'), unblock_email_admin_account_path(@account.id), class: :button if can?(:unblock_email, @account) && CanonicalEmailBlock.exists?(reference_account_id: @account.id)
|
||||
- else
|
||||
.table-wrapper
|
||||
%table.table.inline-table
|
||||
|
@ -21,7 +21,7 @@
|
||||
- if @instance.domain_allow
|
||||
= link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@instance.domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||
- else
|
||||
= link_to t('admin.domain_allows.add_new'), admin_domain_allows_path(domain_allow: { domain: @instance.domain }), class: 'button', method: :post
|
||||
= button_to t('admin.domain_allows.add_new'), admin_domain_allows_path(domain_allow: { domain: @instance.domain }), class: :button
|
||||
- else
|
||||
%p= t('admin.instances.content_policies.description_html')
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
%td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||
|
||||
= link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button'
|
||||
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||
= button_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: :button, data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||
- else
|
||||
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button'
|
||||
|
||||
@ -70,16 +70,16 @@
|
||||
- if @instance.unavailable?
|
||||
%span.negative-hint
|
||||
= t('admin.instances.availability.failure_threshold_reached', date: l(@instance.unavailable_domain.created_at.to_date))
|
||||
= link_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }
|
||||
= button_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure') }
|
||||
- elsif @instance.exhausted_deliveries_days.empty?
|
||||
%span.positive-hint
|
||||
= t('admin.instances.availability.no_failures_recorded')
|
||||
= link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }
|
||||
= button_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure') }
|
||||
- else
|
||||
%span.negative-hint
|
||||
= t('admin.instances.availability.failures_recorded', count: @instance.delivery_failure_tracker.days)
|
||||
%span= link_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post } unless @instance.exhausted_deliveries_days.empty?
|
||||
%span= link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }
|
||||
%span= button_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure') } unless @instance.exhausted_deliveries_days.empty?
|
||||
%span= button_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure') }
|
||||
|
||||
- if @instance.purgeable?
|
||||
%p= t('admin.instances.purge_description_html')
|
||||
|
@ -34,4 +34,4 @@
|
||||
= paginate @invites
|
||||
|
||||
- if policy(:invite).deactivate_all?
|
||||
= link_to t('admin.invites.deactivate_all'), deactivate_all_admin_invites_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button'
|
||||
= button_to t('admin.invites.deactivate_all'), deactivate_all_admin_invites_path, data: { confirm: t('admin.accounts.are_you_sure') }, class: :button
|
||||
|
@ -2,7 +2,7 @@
|
||||
.report-actions
|
||||
.report-actions__item
|
||||
.report-actions__item__button
|
||||
= link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(report), method: :post, class: 'button'
|
||||
= button_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(report), class: :button
|
||||
.report-actions__item__description
|
||||
= t('admin.reports.actions.resolve_description_html')
|
||||
- if statuses.any? { |status| (status.with_media? || status.with_preview_card?) && !status.discarded? }
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
- content_for :heading_actions do
|
||||
- if @report.unresolved?
|
||||
= link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(@report), method: :post, class: 'button'
|
||||
= button_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(@report), class: :button
|
||||
- else
|
||||
= link_to t('admin.reports.mark_as_unresolved'), reopen_admin_report_path(@report), method: :post, class: 'button'
|
||||
= button_to t('admin.reports.mark_as_unresolved'), reopen_admin_report_path(@report), class: :button
|
||||
|
||||
- unless @report.account.local? || @report.target_account.local?
|
||||
.flash-message= t('admin.reports.forwarded_replies_explanation')
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
- content_for :heading_actions do
|
||||
- if @appeal.persisted?
|
||||
= link_to t('disputes.strikes.approve_appeal'), approve_admin_disputes_appeal_path(@appeal), method: :post, class: 'button' if can?(:approve, @appeal)
|
||||
= link_to t('disputes.strikes.reject_appeal'), reject_admin_disputes_appeal_path(@appeal), method: :post, class: 'button button--destructive' if can?(:reject, @appeal)
|
||||
= button_to t('disputes.strikes.approve_appeal'), approve_admin_disputes_appeal_path(@appeal), class: :button if can?(:approve, @appeal)
|
||||
= button_to t('disputes.strikes.reject_appeal'), reject_admin_disputes_appeal_path(@appeal), class: 'button button--destructive' if can?(:reject, @appeal)
|
||||
|
||||
- if @strike.overruled?
|
||||
%p.hint
|
||||
|
@ -46,7 +46,7 @@
|
||||
%p.muted-hint= t('exports.archive_takeout.hint_html')
|
||||
|
||||
- if policy(:backup).create?
|
||||
%p= link_to t('exports.archive_takeout.request'), settings_export_path, class: 'button', method: :post
|
||||
%p= button_to t('exports.archive_takeout.request'), settings_export_path, class: :button
|
||||
|
||||
- unless @backups.empty?
|
||||
%hr.spacer/
|
||||
|
@ -6,4 +6,4 @@
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
= link_to t('otp_authentication.setup'), settings_otp_authentication_path, data: { method: :post }, class: 'block-button'
|
||||
= button_to t('otp_authentication.setup'), settings_otp_authentication_path, class: 'block-button'
|
||||
|
@ -2,7 +2,7 @@
|
||||
= t('settings.two_factor_authentication')
|
||||
|
||||
- content_for :heading_actions do
|
||||
= link_to t('two_factor_authentication.disable'), disable_settings_two_factor_authentication_methods_path, class: 'button button--destructive', method: :post
|
||||
= button_to t('two_factor_authentication.disable'), disable_settings_two_factor_authentication_methods_path, class: 'button button--destructive'
|
||||
|
||||
%p.hint
|
||||
%span.positive-hint
|
||||
@ -38,4 +38,4 @@
|
||||
%hr.spacer/
|
||||
|
||||
.simple_form
|
||||
= link_to t('two_factor_authentication.generate_recovery_codes'), settings_two_factor_authentication_recovery_codes_path, data: { method: :post }, class: 'block-button'
|
||||
= button_to t('two_factor_authentication.generate_recovery_codes'), settings_two_factor_authentication_recovery_codes_path, class: 'block-button'
|
||||
|
@ -4,6 +4,6 @@ class FilteredNotificationCleanupWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(account_id, from_account_id)
|
||||
Notification.where(account_id: account_id, from_account_id: from_account_id, filtered: true).reorder(nil).in_batches(order: :desc).delete_all
|
||||
Notification.where(account_id: account_id, from_account_id: from_account_id, filtered: true).in_batches(order: :desc).delete_all
|
||||
end
|
||||
end
|
||||
|
37
app/workers/mention_resolve_worker.rb
Normal file
37
app/workers/mention_resolve_worker.rb
Normal file
@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class MentionResolveWorker
|
||||
include Sidekiq::Worker
|
||||
include ExponentialBackoff
|
||||
include JsonLdHelper
|
||||
|
||||
sidekiq_options queue: 'pull', retry: 7
|
||||
|
||||
def perform(status_id, uri, options = {})
|
||||
status = Status.find_by(id: status_id)
|
||||
return if status.nil?
|
||||
|
||||
account = account_from_uri(uri)
|
||||
account = ActivityPub::FetchRemoteAccountService.new.call(uri, request_id: options[:request_id]) if account.nil?
|
||||
|
||||
return if account.nil?
|
||||
|
||||
status.mentions.create!(account: account, silent: false)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
# Do nothing
|
||||
rescue Mastodon::UnexpectedResponseError => e
|
||||
response = e.response
|
||||
|
||||
if response_error_unsalvageable?(response)
|
||||
# Give up
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_from_uri(uri)
|
||||
ActivityPub::TagManager.instance.uri_to_resource(uri, Account)
|
||||
end
|
||||
end
|
@ -16,7 +16,7 @@ class Scheduler::UserCleanupScheduler
|
||||
private
|
||||
|
||||
def clean_unconfirmed_accounts!
|
||||
User.unconfirmed.where(confirmation_sent_at: ..UNCONFIRMED_ACCOUNTS_MAX_AGE_DAYS.days.ago).reorder(nil).find_in_batches do |batch|
|
||||
User.unconfirmed.where(confirmation_sent_at: ..UNCONFIRMED_ACCOUNTS_MAX_AGE_DAYS.days.ago).find_in_batches do |batch|
|
||||
# We have to do it separately because of missing database constraints
|
||||
AccountModerationNote.where(target_account_id: batch.map(&:account_id)).delete_all
|
||||
Account.where(id: batch.map(&:account_id)).delete_all
|
||||
|
@ -16,10 +16,10 @@ class Web::PushNotificationWorker
|
||||
# in the meantime, so we have to double-check before proceeding
|
||||
return unless @notification.activity.present? && @subscription.pushable?(@notification)
|
||||
|
||||
payload = @subscription.encrypt(push_notification_json)
|
||||
payload = web_push_request.encrypt(push_notification_json)
|
||||
|
||||
request_pool.with(@subscription.audience) do |http_client|
|
||||
request = Request.new(:post, @subscription.endpoint, body: payload.fetch(:ciphertext), http_client: http_client)
|
||||
request_pool.with(web_push_request.audience) do |http_client|
|
||||
request = Request.new(:post, web_push_request.endpoint, body: payload.fetch(:ciphertext), http_client: http_client)
|
||||
|
||||
request.add_headers(
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
@ -27,8 +27,8 @@ class Web::PushNotificationWorker
|
||||
'Urgency' => URGENCY,
|
||||
'Content-Encoding' => 'aesgcm',
|
||||
'Encryption' => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}",
|
||||
'Crypto-Key' => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{@subscription.crypto_key_header}",
|
||||
'Authorization' => @subscription.authorization_header
|
||||
'Crypto-Key' => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{web_push_request.crypto_key_header}",
|
||||
'Authorization' => web_push_request.authorization_header
|
||||
)
|
||||
|
||||
request.perform do |response|
|
||||
@ -50,17 +50,27 @@ class Web::PushNotificationWorker
|
||||
|
||||
private
|
||||
|
||||
def web_push_request
|
||||
@web_push_request || WebPushRequest.new(@subscription)
|
||||
end
|
||||
|
||||
def push_notification_json
|
||||
json = I18n.with_locale(@subscription.locale.presence || I18n.default_locale) do
|
||||
Oj.dump(serialized_notification_in_subscription_locale.as_json)
|
||||
end
|
||||
|
||||
def serialized_notification_in_subscription_locale
|
||||
I18n.with_locale(@subscription.locale.presence || I18n.default_locale) do
|
||||
serialized_notification
|
||||
end
|
||||
end
|
||||
|
||||
def serialized_notification
|
||||
ActiveModelSerializers::SerializableResource.new(
|
||||
@notification,
|
||||
serializer: Web::NotificationSerializer,
|
||||
scope: @subscription,
|
||||
scope_name: :current_push_subscription
|
||||
).as_json
|
||||
end
|
||||
|
||||
Oj.dump(json)
|
||||
)
|
||||
end
|
||||
|
||||
def request_pool
|
||||
|
@ -931,6 +931,9 @@ cy:
|
||||
message_html: Nid ydych wedi diffinio unrhyw reolau gweinydd.
|
||||
sidekiq_process_check:
|
||||
message_html: Does dim proses Sidekiq yn rhedeg ar gyfer y ciw(iau) %{value}. Adolygwch eich ffurfweddiad Sidekiq
|
||||
software_version_check:
|
||||
action: Gweld y diweddariadau sydd ar gael
|
||||
message_html: Mae diweddariad Mastodon ar gael.
|
||||
software_version_critical_check:
|
||||
action: Gweld y diweddariadau sydd ar gael
|
||||
message_html: Mae diweddariad hanfodol Mastodon ar gael, diweddarwch cyn gynted â phosibl.
|
||||
@ -1796,6 +1799,7 @@ cy:
|
||||
delete: Dileu cyfrif
|
||||
development: Datblygu
|
||||
edit_profile: Golygu proffil
|
||||
export: Allforio
|
||||
featured_tags: Prif hashnodau
|
||||
import: Mewnforio
|
||||
import_and_export: Mewnforio ac allforio
|
||||
|
@ -69,7 +69,7 @@ gl:
|
||||
buttons:
|
||||
revoke: Retirar autorización
|
||||
confirmations:
|
||||
revoke: Estás segura?
|
||||
revoke: Tes certeza?
|
||||
index:
|
||||
authorized_at: Autorizada o %{date}
|
||||
description_html: Estas aplicacións poden acceder á túa conta usando a API. Se ves aplicacións que non recoñeces, ou hai comportamentos non consentidos dalgunha delas, podes revogar o acceso.
|
||||
|
@ -903,6 +903,9 @@ he:
|
||||
message_html: לא הוגדרו שום כללי שרת.
|
||||
sidekiq_process_check:
|
||||
message_html: שום הליכי Sidekiq לא רצים עבור %{value} תור(ות). בחנו בבקשה את הגדרות Sidekiq
|
||||
software_version_check:
|
||||
action: ראו עדכונים זמינים
|
||||
message_html: עדכון מסטודון זמין כעת.
|
||||
software_version_critical_check:
|
||||
action: ראו עדכונים זמינים
|
||||
message_html: יצא עדכון קריטי למסטודון, נא לעדכן את תוכנת מסטודון בהקדם האפשרי.
|
||||
@ -1744,6 +1747,7 @@ he:
|
||||
delete: מחיקת חשבון
|
||||
development: פיתוח
|
||||
edit_profile: עריכת פרופיל
|
||||
export: ייצוא
|
||||
featured_tags: תגיות נבחרות
|
||||
import: יבוא
|
||||
import_and_export: יבוא ויצוא
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
nn:
|
||||
about:
|
||||
about_mastodon_html: 'Framtidas sosiale nettverk: Ingen annonsar, ingen verksemder som overvaker deg, etisk design og desentralisering! Eig idéane dine med Mastodon!'
|
||||
about_mastodon_html: 'Framtidas sosiale nettverk: Ingen annonsar, ingen verksemder som overvaker deg, etisk design og desentralisering! Eig dataene dine med Mastodon!'
|
||||
contact_missing: Ikkje sett
|
||||
contact_unavailable: I/T
|
||||
hosted_on: "%{domain} er vert for Mastodon"
|
||||
@ -39,7 +39,7 @@ nn:
|
||||
avatar: Bilete
|
||||
by_domain: Domene
|
||||
change_email:
|
||||
changed_msg: Konto-e-posten er endra!
|
||||
changed_msg: E-post for konto er endra!
|
||||
current_email: Noverande e-post
|
||||
label: Byt e-post
|
||||
new_email: Ny e-post
|
||||
@ -62,7 +62,7 @@ nn:
|
||||
disable: Slå av
|
||||
disable_sign_in_token_auth: Slå av e-post-token-autentisering
|
||||
disable_two_factor_authentication: Slå av 2FA
|
||||
disabled: Slege av
|
||||
disabled: Inaktiv
|
||||
display_name: Synleg namn
|
||||
domain: Domene
|
||||
edit: Rediger
|
||||
|
@ -67,7 +67,7 @@ Rails.application.routes.draw do
|
||||
scope path: '.well-known' do
|
||||
scope module: :well_known do
|
||||
get 'oauth-authorization-server', to: 'oauth_metadata#show', as: :oauth_metadata, defaults: { format: 'json' }
|
||||
get 'host-meta', to: 'host_meta#show', as: :host_meta, defaults: { format: 'xml' }
|
||||
get 'host-meta', to: 'host_meta#show', as: :host_meta
|
||||
get 'nodeinfo', to: 'node_info#index', as: :nodeinfo, defaults: { format: 'json' }
|
||||
get 'webfinger', to: 'webfinger#show', as: :webfinger
|
||||
end
|
||||
|
@ -2,5 +2,5 @@
|
||||
|
||||
Fabricator(:account_domain_block) do
|
||||
account { Fabricate.build(:account) }
|
||||
domain 'example.com'
|
||||
domain { sequence { |n| "host-#{n}.example" } }
|
||||
end
|
||||
|
@ -63,6 +63,24 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
}
|
||||
end
|
||||
|
||||
let(:invalid_mention_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), 'post2'].join('/'),
|
||||
type: 'Note',
|
||||
to: [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
ActivityPub::TagManager.instance.uri_for(follower),
|
||||
],
|
||||
content: '@bob lorem ipsum',
|
||||
published: 1.hour.ago.utc.iso8601,
|
||||
updated: 1.hour.ago.utc.iso8601,
|
||||
tag: {
|
||||
type: 'Mention',
|
||||
href: 'http://notexisting.dontexistingtld/actor',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
def activity_for_object(json)
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
@ -117,6 +135,25 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
# Creates two notifications
|
||||
expect(Notification.count).to eq 2
|
||||
end
|
||||
|
||||
it 'ignores unprocessable mention', :aggregate_failures do
|
||||
stub_request(:get, invalid_mention_json[:tag][:href]).to_raise(HTTP::ConnectionError)
|
||||
# When receiving the post that contains an invalid mention…
|
||||
described_class.new(activity_for_object(invalid_mention_json), sender, delivery: true).perform
|
||||
|
||||
# NOTE: Refering explicitly to the workers is a bit awkward
|
||||
DistributionWorker.drain
|
||||
FeedInsertWorker.drain
|
||||
|
||||
# …it creates a status
|
||||
status = Status.find_by(uri: invalid_mention_json[:id])
|
||||
|
||||
# Check the process did not crash
|
||||
expect(status.nil?).to be false
|
||||
|
||||
# It has queued a mention resolve job
|
||||
expect(MentionResolveWorker).to have_enqueued_sidekiq_job(status.id, invalid_mention_json[:tag][:href], anything)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
|
@ -3,66 +3,175 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Export do
|
||||
subject { described_class.new(account) }
|
||||
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:target_accounts) do
|
||||
[{}, { username: 'one', domain: 'local.host' }].map(&method(:Fabricate).curry(2).call(:account))
|
||||
[
|
||||
Fabricate(:account),
|
||||
Fabricate(:account, username: 'one', domain: 'local.host'),
|
||||
]
|
||||
end
|
||||
|
||||
describe 'to_csv' do
|
||||
describe '#to_bookmarks_csv' do
|
||||
before { Fabricate.times(2, :bookmark, account: account) }
|
||||
|
||||
let(:export) { CSV.parse(subject.to_bookmarks_csv) }
|
||||
|
||||
it 'returns a csv of bookmarks' do
|
||||
expect(export)
|
||||
.to contain_exactly(
|
||||
include(/statuses/),
|
||||
include(/statuses/)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_blocked_accounts_csv' do
|
||||
before { target_accounts.each { |target_account| account.block!(target_account) } }
|
||||
|
||||
let(:export) { CSV.parse(subject.to_blocked_accounts_csv) }
|
||||
|
||||
it 'returns a csv of the blocked accounts' do
|
||||
target_accounts.each { |target_account| account.block!(target_account) }
|
||||
|
||||
export = described_class.new(account).to_blocked_accounts_csv
|
||||
results = export.strip.split
|
||||
|
||||
expect(results.size).to eq 2
|
||||
expect(results.first).to eq 'one@local.host'
|
||||
expect(export)
|
||||
.to contain_exactly(
|
||||
include('one@local.host'),
|
||||
include(be_present)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_muted_accounts_csv' do
|
||||
before { target_accounts.each { |target_account| account.mute!(target_account) } }
|
||||
|
||||
let(:export) { CSV.parse(subject.to_muted_accounts_csv) }
|
||||
|
||||
it 'returns a csv of the muted accounts' do
|
||||
target_accounts.each { |target_account| account.mute!(target_account) }
|
||||
|
||||
export = described_class.new(account).to_muted_accounts_csv
|
||||
results = export.strip.split("\n")
|
||||
|
||||
expect(results.size).to eq 3
|
||||
expect(results.first).to eq 'Account address,Hide notifications'
|
||||
expect(results.second).to eq 'one@local.host,true'
|
||||
expect(export)
|
||||
.to contain_exactly(
|
||||
contain_exactly('Account address', 'Hide notifications'),
|
||||
include('one@local.host', 'true'),
|
||||
include(be_present)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_following_accounts_csv' do
|
||||
before { target_accounts.each { |target_account| account.follow!(target_account) } }
|
||||
|
||||
let(:export) { CSV.parse(subject.to_following_accounts_csv) }
|
||||
|
||||
it 'returns a csv of the following accounts' do
|
||||
target_accounts.each { |target_account| account.follow!(target_account) }
|
||||
|
||||
export = described_class.new(account).to_following_accounts_csv
|
||||
results = export.strip.split("\n")
|
||||
|
||||
expect(results.size).to eq 3
|
||||
expect(results.first).to eq 'Account address,Show boosts,Notify on new posts,Languages'
|
||||
expect(results.second).to eq 'one@local.host,true,false,'
|
||||
expect(export)
|
||||
.to contain_exactly(
|
||||
contain_exactly('Account address', 'Show boosts', 'Notify on new posts', 'Languages'),
|
||||
include('one@local.host', 'true', 'false', be_blank),
|
||||
include(be_present)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'total_storage' do
|
||||
describe '#to_lists_csv' do
|
||||
before do
|
||||
target_accounts.each do |target_account|
|
||||
account.follow!(target_account)
|
||||
Fabricate(:list, account: account).accounts << target_account
|
||||
end
|
||||
end
|
||||
|
||||
let(:export) { CSV.parse(subject.to_lists_csv) }
|
||||
|
||||
it 'returns a csv of the lists' do
|
||||
expect(export)
|
||||
.to contain_exactly(
|
||||
include('one@local.host'),
|
||||
include(be_present)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_blocked_domains_csv' do
|
||||
before { Fabricate.times(2, :account_domain_block, account: account) }
|
||||
|
||||
let(:export) { CSV.parse(subject.to_blocked_domains_csv) }
|
||||
|
||||
it 'returns a csv of the blocked domains' do
|
||||
expect(export)
|
||||
.to contain_exactly(
|
||||
include(/example/),
|
||||
include(/example/)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_storage' do
|
||||
it 'returns the total size of the media attachments' do
|
||||
media_attachment = Fabricate(:media_attachment, account: account)
|
||||
expect(described_class.new(account).total_storage).to eq media_attachment.file_file_size || 0
|
||||
expect(subject.total_storage).to eq media_attachment.file_file_size || 0
|
||||
end
|
||||
end
|
||||
|
||||
describe 'total_follows' do
|
||||
it 'returns the total number of the followed accounts' do
|
||||
target_accounts.each { |target_account| account.follow!(target_account) }
|
||||
expect(described_class.new(account.reload).total_follows).to eq 2
|
||||
describe '#total_statuses' do
|
||||
before { Fabricate.times(2, :status, account: account) }
|
||||
|
||||
it 'returns the total number of statuses' do
|
||||
expect(subject.total_statuses).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_bookmarks' do
|
||||
before { Fabricate.times(2, :bookmark, account: account) }
|
||||
|
||||
it 'returns the total number of bookmarks' do
|
||||
expect(subject.total_bookmarks).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_follows' do
|
||||
before { target_accounts.each { |target_account| account.follow!(target_account) } }
|
||||
|
||||
it 'returns the total number of the followed accounts' do
|
||||
expect(subject.total_follows).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_lists' do
|
||||
before { Fabricate.times(2, :list, account: account) }
|
||||
|
||||
it 'returns the total number of lists' do
|
||||
expect(subject.total_lists).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_followers' do
|
||||
before { target_accounts.each { |target_account| target_account.follow!(account) } }
|
||||
|
||||
it 'returns the total number of the follower accounts' do
|
||||
expect(subject.total_followers).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_blocks' do
|
||||
before { target_accounts.each { |target_account| account.block!(target_account) } }
|
||||
|
||||
it 'returns the total number of the blocked accounts' do
|
||||
target_accounts.each { |target_account| account.block!(target_account) }
|
||||
expect(described_class.new(account.reload).total_blocks).to eq 2
|
||||
expect(subject.total_blocks).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_mutes' do
|
||||
before { target_accounts.each { |target_account| account.mute!(target_account) } }
|
||||
|
||||
it 'returns the total number of the muted accounts' do
|
||||
target_accounts.each { |target_account| account.mute!(target_account) }
|
||||
expect(described_class.new(account.reload).total_mutes).to eq 2
|
||||
expect(subject.total_mutes).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_domain_blocks' do
|
||||
before { Fabricate.times(2, :account_domain_block, account: account) }
|
||||
|
||||
it 'returns the total number of account domain blocks' do
|
||||
expect(subject.total_domain_blocks).to eq(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -9,19 +9,39 @@ RSpec.describe 'The /.well-known/host-meta request' do
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_attributes(
|
||||
media_type: 'application/xrd+xml',
|
||||
body: host_meta_xml_template
|
||||
media_type: 'application/xrd+xml'
|
||||
)
|
||||
|
||||
doc = Nokogiri::XML(response.parsed_body)
|
||||
expect(doc.at_xpath('/xrd:XRD/xrd:Link[@rel="lrdd"]/@template', 'xrd' => 'http://docs.oasis-open.org/ns/xri/xrd-1.0').value)
|
||||
.to eq 'https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}'
|
||||
end
|
||||
|
||||
it 'returns http success with valid JSON response with .json extension' do
|
||||
get '/.well-known/host-meta.json'
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_attributes(
|
||||
media_type: 'application/json'
|
||||
)
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
links: [
|
||||
'rel' => 'lrdd',
|
||||
'template' => 'https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}',
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
it 'returns http success with valid JSON response with Accept header' do
|
||||
get '/.well-known/host-meta', headers: { 'Accept' => 'application/json' }
|
||||
|
||||
def host_meta_xml_template
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Link rel="lrdd" template="https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}"/>
|
||||
</XRD>
|
||||
XML
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_attributes(
|
||||
media_type: 'application/json'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -4,9 +4,14 @@ require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Well Known routes' do
|
||||
describe 'the host-meta route' do
|
||||
it 'routes to correct place with xml format' do
|
||||
it 'routes to correct place' do
|
||||
expect(get('/.well-known/host-meta'))
|
||||
.to route_to('well_known/host_meta#show', format: 'xml')
|
||||
.to route_to('well_known/host_meta#show')
|
||||
end
|
||||
|
||||
it 'routes to correct place with json format' do
|
||||
expect(get('/.well-known/host-meta.json'))
|
||||
.to route_to('well_known/host_meta#show', format: 'json')
|
||||
end
|
||||
end
|
||||
|
||||
|
38
spec/workers/mention_resolve_worker_spec.rb
Normal file
38
spec/workers/mention_resolve_worker_spec.rb
Normal file
@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe MentionResolveWorker do
|
||||
let(:status_id) { -42 }
|
||||
let(:uri) { 'https://example.com/users/unknown' }
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new.perform(status_id, uri, {}) }
|
||||
|
||||
context 'with a non-existent status' do
|
||||
it 'returns nil' do
|
||||
expect(subject).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a valid user' do
|
||||
let(:status) { Fabricate(:status) }
|
||||
let(:status_id) { status.id }
|
||||
|
||||
let(:service_double) { instance_double(ActivityPub::FetchRemoteAccountService) }
|
||||
|
||||
before do
|
||||
allow(ActivityPub::FetchRemoteAccountService).to receive(:new).and_return(service_double)
|
||||
|
||||
allow(service_double).to receive(:call).with(uri, anything) { Fabricate(:account, domain: 'example.com', uri: uri) }
|
||||
end
|
||||
|
||||
it 'resolves the account and adds a new mention', :aggregate_failures do
|
||||
expect { subject }
|
||||
.to change { status.reload.mentions }.from([]).to(a_collection_including(having_attributes(account: having_attributes(uri: uri), silent: false)))
|
||||
|
||||
expect(service_double).to have_received(:call).once
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -22,19 +22,38 @@ RSpec.describe Web::PushNotificationWorker do
|
||||
let(:payload) { { ciphertext: ciphertext, salt: salt, server_public_key: server_public_key, shared_secret: shared_secret } }
|
||||
|
||||
describe 'perform' do
|
||||
around do |example|
|
||||
original_private = Rails.configuration.x.vapid_private_key
|
||||
original_public = Rails.configuration.x.vapid_public_key
|
||||
Rails.configuration.x.vapid_private_key = vapid_private_key
|
||||
Rails.configuration.x.vapid_public_key = vapid_public_key
|
||||
example.run
|
||||
Rails.configuration.x.vapid_private_key = original_private
|
||||
Rails.configuration.x.vapid_public_key = original_public
|
||||
end
|
||||
|
||||
before do
|
||||
allow(subscription).to receive_messages(contact_email: contact_email, vapid_key: vapid_key)
|
||||
allow(Web::PushSubscription).to receive(:find).with(subscription.id).and_return(subscription)
|
||||
Setting.site_contact_email = contact_email
|
||||
|
||||
allow(Webpush::Encryption).to receive(:encrypt).and_return(payload)
|
||||
allow(JWT).to receive(:encode).and_return('jwt.encoded.payload')
|
||||
|
||||
stub_request(:post, endpoint).to_return(status: 201, body: '')
|
||||
|
||||
subject.perform(subscription.id, notification.id)
|
||||
end
|
||||
|
||||
it 'calls the relevant service with the correct headers' do
|
||||
expect(a_request(:post, endpoint).with(headers: {
|
||||
subject.perform(subscription.id, notification.id)
|
||||
|
||||
expect(web_push_endpoint_request)
|
||||
.to have_been_made
|
||||
end
|
||||
|
||||
def web_push_endpoint_request
|
||||
a_request(
|
||||
:post,
|
||||
endpoint
|
||||
).with(
|
||||
headers: {
|
||||
'Content-Encoding' => 'aesgcm',
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Crypto-Key' => "dh=BAgtUks5d90kFmxGevk9tH7GEmvz9DB0qcEMUsOBgKwMf-TMjsKIIG6LQvGcFAf6jcmAod15VVwmYwGIIxE4VWE;p256ecdsa=#{vapid_public_key.delete('=')}",
|
||||
@ -42,7 +61,9 @@ RSpec.describe Web::PushNotificationWorker do
|
||||
'Ttl' => '172800',
|
||||
'Urgency' => 'normal',
|
||||
'Authorization' => 'WebPush jwt.encoded.payload',
|
||||
}, body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr")).to have_been_made
|
||||
},
|
||||
body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user