From b3ceb3dcc4df62803aa967d7aecee686973a8996 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 17 Apr 2021 03:14:25 +0200 Subject: Add canonical e-mail blocks for suspended accounts (#16049) Prevent new accounts from being created using the same underlying e-mail as a suspended account using extensions and period permutations. Stores e-mails as a SHA256 hash --- app/helpers/email_helper.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 app/helpers/email_helper.rb (limited to 'app/helpers') diff --git a/app/helpers/email_helper.rb b/app/helpers/email_helper.rb new file mode 100644 index 000000000..360783c62 --- /dev/null +++ b/app/helpers/email_helper.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module EmailHelper + def self.included(base) + base.extend(self) + end + + def email_to_canonical_email(str) + username, domain = str.downcase.split('@', 2) + username, = username.gsub('.', '').split('+', 2) + + "#{username}@#{domain}" + end + + def email_to_canonical_email_hash(str) + Digest::SHA2.new(256).hexdigest(email_to_canonical_email(str)) + end +end -- cgit From 480d7c9478a9be3ac668b21402a269db41b10b18 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 17 Apr 2021 11:12:49 +0200 Subject: Fix missing source strings and inconsistent lead text style in admin UI (#16052) --- app/helpers/application_helper.rb | 2 -- app/views/admin/follow_recommendations/show.html.haml | 3 +-- app/views/admin/rules/index.html.haml | 5 +++-- app/views/user_mailer/webauthn_enabled.text.erb | 4 ++-- config/locales/en.yml | 7 ++++++- 5 files changed, 12 insertions(+), 9 deletions(-) (limited to 'app/helpers') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bf5742d34..fc2d2fea9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -91,8 +91,6 @@ module ApplicationHelper fa_icon('unlock', title: I18n.t('statuses.visibilities.unlisted')) elsif status.private_visibility? || status.limited_visibility? fa_icon('lock', title: I18n.t('statuses.visibilities.private')) - elsif status.direct_visibility? - fa_icon('envelope', title: I18n.t('statuses.visibilities.direct')) end end diff --git a/app/views/admin/follow_recommendations/show.html.haml b/app/views/admin/follow_recommendations/show.html.haml index 1f050329a..5b949a165 100644 --- a/app/views/admin/follow_recommendations/show.html.haml +++ b/app/views/admin/follow_recommendations/show.html.haml @@ -4,8 +4,7 @@ - content_for :header_tags do = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' -.simple_form - %p.hint= t('admin.follow_recommendations.description_html') +%p= t('admin.follow_recommendations.description_html') %hr.spacer/ diff --git a/app/views/admin/rules/index.html.haml b/app/views/admin/rules/index.html.haml index 3b069d083..4fb993ad0 100644 --- a/app/views/admin/rules/index.html.haml +++ b/app/views/admin/rules/index.html.haml @@ -1,8 +1,9 @@ - content_for :page_title do = t('admin.rules.title') -.simple_form - %p.hint= t('admin.rules.description') +%p= t('admin.rules.description_html') + +%hr.spacer/ - if can? :create, :rule = simple_form_for @rule, url: admin_rules_path do |f| diff --git a/app/views/user_mailer/webauthn_enabled.text.erb b/app/views/user_mailer/webauthn_enabled.text.erb index 4c233fefb..d4482a69b 100644 --- a/app/views/user_mailer/webauthn_enabled.text.erb +++ b/app/views/user_mailer/webauthn_enabled.text.erb @@ -1,7 +1,7 @@ -<%= t 'devise.mailer.webauthn_credentia.added.title' %> +<%= t 'devise.mailer.webauthn_credential.added.title' %> === -<%= t 'devise.mailer.webauthn_credentia.added.explanation' %> +<%= t 'devise.mailer.webauthn_credential.added.explanation' %> => <%= edit_user_registration_url %> diff --git a/config/locales/en.yml b/config/locales/en.yml index afab6d9b5..88acf3164 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -315,10 +315,12 @@ en: new: create: Create announcement title: New announcement + publish: Publish published_msg: Announcement successfully published! scheduled_for: Scheduled for %{time} scheduled_msg: Announcement scheduled for publication! title: Announcements + unpublish: Unpublish unpublished_msg: Announcement successfully unpublished! updated_msg: Announcement successfully updated! custom_emojis: @@ -552,8 +554,10 @@ en: updated_at: Updated rules: add_new: Add rule - description: While most claim to have read and agree to the terms of service, usually people do not read through until after a problem arises. Make it easier to see your server's rules at a glance by providing them in a flat bullet point list. Try to keep individual rules short and simple, but try not to split them up into many separate items either. + delete: Delete + description_html: While most claim to have read and agree to the terms of service, usually people do not read through until after a problem arises. Make it easier to see your server's rules at a glance by providing them in a flat bullet point list. Try to keep individual rules short and simple, but try not to split them up into many separate items either. edit: Edit rule + empty: No server rules have been defined yet. title: Server rules settings: activity_api_enabled: @@ -695,6 +699,7 @@ en: add_new: Add new delete: Delete edit_preset: Edit warning preset + empty: You haven't defined any warning presets yet. title: Manage warning presets admin_mailer: new_pending_account: -- cgit From e243092a5ae44dbf9a1c0ea2791214f6c9d69025 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 21 Apr 2021 15:40:00 +0200 Subject: Add DM icon back on HTML view of DMs (#16086) Fix regression from #16052 --- app/helpers/application_helper.rb | 2 ++ config/locales/en.yml | 1 + 2 files changed, 3 insertions(+) (limited to 'app/helpers') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index fc2d2fea9..bf5742d34 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -91,6 +91,8 @@ module ApplicationHelper fa_icon('unlock', title: I18n.t('statuses.visibilities.unlisted')) elsif status.private_visibility? || status.limited_visibility? fa_icon('lock', title: I18n.t('statuses.visibilities.private')) + elsif status.direct_visibility? + fa_icon('envelope', title: I18n.t('statuses.visibilities.direct')) end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 3f3adc158..7aebd0743 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1260,6 +1260,7 @@ en: sign_in_to_participate: Sign in to participate in the conversation title: '%{name}: "%{quote}"' visibilities: + direct: Direct private: Followers-only private_long: Only show to followers public: Public -- cgit From cafc7ad064b9d99a056a249b9ff03a1988e992e1 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 22 Apr 2021 05:12:27 +0200 Subject: Add af, gd and si locales (#16090) * Add af, gd and si locales * i18n-tasks normalize * Fix inconsistent interpolations Co-authored-by: GunChleoc Co-authored-by: Yamagishi Kazutoshi --- app/helpers/settings_helper.rb | 3 +++ config/application.rb | 3 +++ config/locales/gd.yml | 4 ++-- config/locales/gl.yml | 2 +- config/locales/is.yml | 8 ++++---- config/locales/sc.yml | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) (limited to 'app/helpers') diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 5b39497b6..b60901040 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -2,6 +2,7 @@ module SettingsHelper HUMAN_LOCALES = { + af: 'Afrikaans', ar: 'العربية', ast: 'Asturianu', bg: 'Български', @@ -24,6 +25,7 @@ module SettingsHelper fi: 'Suomi', fr: 'Français', ga: 'Gaeilge', + gd: 'Gàidhlig', gl: 'Galego', he: 'עברית', hi: 'हिन्दी', @@ -59,6 +61,7 @@ module SettingsHelper ru: 'Русский', sa: 'संस्कृतम्', sc: 'Sardu', + si: 'සිංහල', sk: 'Slovenčina', sl: 'Slovenščina', sq: 'Shqip', diff --git a/config/application.rb b/config/application.rb index eb2c91677..9aa1594ce 100644 --- a/config/application.rb +++ b/config/application.rb @@ -54,6 +54,7 @@ module Mastodon # All translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] config.i18n.available_locales = [ + :af, :ar, :ast, :bg, @@ -76,6 +77,7 @@ module Mastodon :fi, :fr, :ga, + :gd, :gl, :he, :hi, @@ -110,6 +112,7 @@ module Mastodon :ru, :sa, :sc, + :si, :sk, :sl, :sq, diff --git a/config/locales/gd.yml b/config/locales/gd.yml index a8e68bcb0..84a647557 100644 --- a/config/locales/gd.yml +++ b/config/locales/gd.yml @@ -73,7 +73,7 @@ gd: last_active: gnìomhach an turas mu dheireadh link_verified_on: Chaidh dearbhadh cò leis a tha an ceangal seo %{date} media: Meadhanan - moved_html: 'Chaidh {name} imrich gu %{new_profile_link}:' + moved_html: 'Chaidh %{name} imrich gu %{new_profile_link}:' network_hidden: Chan eil am fiosrachadh seo ri fhaighinn never_active: Chan ann idir nothing_here: Chan eil dad an-seo! @@ -1311,7 +1311,7 @@ gd: terms: body_html: '

Poileasaidh prìobhaideachd

Dè am fiosrachadh a chruinnicheas sinn?

  • Fiosrachadh bunasach a’ cunntais: Ma chlàraicheas tu leis an fhrithealaiche seo, dh’fhaoidte gun dèid iarraidh ort gun cuir thu a-steach ainm-cleachdaiche, seòladh puist-d agus facal-faire. Faodaidh tu barrachd fiosrachaidh a chur ris a’ phròifil agad ma thogras tu, can ainm-taisbeanaidh agus teacsa mu do dhèidhinn agus dealbhan pròifile ’s banna-chinn a luchdadh suas. Thèid an t-ainm-cleachdaiche, an t-ainm-taisbeanaidh, an teacsa mu do dhèidhinn agus dealbhan na pròifile ’s a bhanna-chinn a shealltainn gu poblach an-còmhnaidh.
  • Postaichean, luchd-leantainn agus fiosrachadh poblach eile: Tha liosta nan daoine air a leanas tu poblach mar a tha i dhan luchd-leantainn agad. Nuair a chuireas tu a-null teachdaireachd, thèid an t-àm ’s an ceann-latha a stòradh cho math ris an aplacaid leis an do chuir thu am foirm a-null. Faodaidh ceanglachain meadhain a bhith am broinn teachdaireachdan, can dealbhan no videothan. Tha postaichean poblach agus postaichean falaichte o liostaichean ri ’m faighinn gu poblach. Nuair a bhrosnaicheas tu post air a’ phròifil agad, ’s e fiosrachadh poblach a tha sin cuideachd. Thèid na postaichean agad a lìbhrigeadh dhan luchd-leantainn agad agus is ciall dha seo gun dèid an lìbhrigeadh gu frithealaichean eile aig amannan is gun dèid lethbhreacan dhiubh a stòradh thall. Nuair a sguabas tu às post, thèid sin a lìbhrigeadh dhan luchd-leantainn agad cuideachd. Tha ath-bhlogachadh no dèanamh annsachd de phost eile poblach an-còmhnaidh.
  • Postaichean dìreach is dhan luchd-leantainn a-mhàin: Thèid a h-uile post a stòradh ’s a phròiseasadh air an fhrithealaiche. Thèid na postaichean dhan luchd-leantainn a-mhàin a lìbhrigeadh dhan luchd-leantainn agad agus dhan luchd-chleachdaidh a chaidh iomradh a dhèanamh orra sa phost. Thèid postaichean dìreach a lìbhrigeadh dhan luchd-chleachdaidh a chaidh iomradh a dhèanamh orra sa phost a-mhàin. Is ciall dha seo gun dèid an lìbhrigeadh gu frithealaichean eile aig amannan is gun dèid lethbhreacan dhiubh a stòradh thall. Nì sinn ar dìcheall gun cuingich sinn an t-inntrigeadh dha na postaichean air na daoine a fhuair ùghdarrachadh dhaibh ach dh’fhaoidte nach dèan frithealaichean eile seo. Mar sin dheth, tha e cudromach gun doir thu sùil air na frithealaichean dhan a bhuineas an luchd-leantainn agad. Faodaidh tu roghainn a chur air no dheth a leigeas leat aontachadh ri luchd-leantainn ùra no an diùltadh a làimh. Thoir an aire gum faic rianairean an fhrithealaiche agus frithealaiche sam bith a gheibh am fiosrachadh na teachdaireachdan dhen leithid agus gur urrainn dha na faightearan glacaidhean-sgrìn no lethbhreacan dhiubh a dhèanamh no an cho-roinneadh air dòighean eile. Na co-roinn fiosrachadh cunnartach air Mastodon idir.
  • IPan is meata-dàta eile: Nuair a nì thu clàradh a-steach, clàraidh sinn an seòladh IP on a rinn thu clàradh a-steach cuide ri ainm aplacaid a’ bhrabhsair agad. Bidh a h-uile seisean clàraidh a-steach ri làimh dhut airson an lèirmheas agus an cùl-ghairm sna roghainnean. Thèid an seòladh IP as ùire a chleachd thu a stòradh suas ri 12 mhìos. Faodaidh sinn cuideachd logaichean an fhrithealaiche a chumail a ghabhas a-steach seòladh IP aig a h-uile iarrtas dhan fhrithealaiche againn.

Dè na h-adhbharan air an cleachd sinn am fiosrachadh agad?

Seo na dòighean air an cleachd sinn fiosrachadh sam bith a chruinnich sinn uat ma dh’fhaoidte:

  • Airson bun-ghleusan Mhastodon a lìbhrigeadh. Chan urrainn dhut eadar-ghnìomh a ghabhail le susbaint càich no an t-susbaint agad fhèin a phostadh ach nuair a bhios tu air do chlàradh a-steach. Mar eisimpleir, faodaidh tu leantainn air càch ach am faic thu na postaichean aca còmhla air loidhne-ama pearsanaichte na dachaigh agad.
  • Airson cuideachadh le maorsainneachd na coimhearsnachd, can airson coimeas a dhèanamh eadar an seòladh IP agad ri feadhainn eile feuch am mothaich sinn do sheachnadh toirmisg no briseadh eile nan riaghailtean.
  • Faodaidh sinn an seòladh puist-d agad a chleachdadh airson fiosrachadh no brathan mu eadar-ghnìomhan a ghabh càch leis an t-susbaint agad no teachdaireachdan a chur thugad, airson freagairt ri ceasnachaidhean agus/no iarrtasan no ceistean eile.

Ciamar a dhìonas sinn am fiosrachadh agad?

Cuiridh sinn iomadh gleus tèarainteachd an sàs ach an glèidheadh sinn sàbhailteachd an fhiosrachaidh phearsanta agad nuair a chuireas tu gin a-steach, nuair a chuireas tu a-null e no nuair a nì thu inntrigeadh air. Am measg gleusan eile, thèid seisean a’ bhrabhsair agad cuide ris an trafaig eadar na h-aplacaidean agad ’s an API a dhìon le SSL agus thèid hais a dhèanamh dhen fhacal-fhaire agad le algairim aon-shligheach làidir. Faodaidh tu dearbhadh dà-cheumnach a chur an comas airson barrachd tèarainteachd a chur ris an inntrigeadh dhan chunntas agad.


Dè am poileasaidh cumail dàta againn?

Nì sinn ar dìcheall:

  • Nach cùm sinn logaidhean an fhrithealaiche sa bheil seòlaidhean IP nan iarrtasan uile dhan fhrithealaiche seo nas fhaide na 90 latha ma chumas sinn logaichean dhen leithid idir.
  • Nach cùm sinn na seòlaidhean IP a tha co-cheangailte ri cleachdaichean clàraichte nas fhaide na 12 mhìos.

’S urrainn dhut tasg-lann iarraidh dhen t-susbaint agad ’s a luchdadh a-nuas is gabhaidh seo a-staigh na postaichean, na ceanglachain meadhain, dealbh na pròifil agus dealbh a’ bhanna-chinn agad.

’S urrainn dhut an cunntas agad a sguabadh às gu buan uair sam bith.


An cleachd sinn briosgaidhean?

Cleachdaidh. ’S e faidhlichean beaga a tha sna briosgaidean a thar-chuireas làrach no solaraiche seirbheise gu clàr-cruaidh a’ choimpiutair agad leis a’ bhrabhsair-lìn agad (ma cheadaicheas tu sin). Bheir na briosgaidean sin comas dhan làrach gun aithnich i am brabhsair agad agus ma tha cunntas clàraichte agad, gun co-cheangail i ris a’ chunntas chlàraichte agad e.

Cleachdaidh sinn briosgaidean airson na roghainnean agad a thuigsinn ’s a ghlèidheadh gus an tadhail thu oirnn san àm ri teachd.


Am foillsich sinn fiosrachadh sam bith gu pàrtaidhean air an taobh a-muigh?

Cha reic, malairt no tar-chuir sinn fiosrachadh air a dh’aithnichear thu fhèin gu pàrtaidh sam bith air an taobh a-muigh. Cha ghabh seo a-staigh treas-phàrtaidhean earbsach a chuidicheas leinn le ruith na làraich againn, le obrachadh a’ ghnìomhachais againn no gus an t-seirbheis a thoirt leat cho fada ’s a dh’aontaicheas na treas-phàrtaidhean sin gun cùm iad am fiosrachadh dìomhair. Faodaidh sinn am fiosrachadh agad fhoillseachadh cuideachd nuair a bhios sinn dhen bheachd gu bheil am foillseachadh sin iomchaidh airson gèilleadh dhan lagh, poileasaidhean na làraich againn èigneachadh no na còraichean, an sealbh no an t-sàbhailteachd againn fhèin no aig càch a dhìon.

Dh’fhaoidte gun dèid an t-susbaint phoblach agad a luchdadh a-nuas le frithealaichean eile san lìonra. Thèid na postaichean poblach agad ’s an fheadhainn dhan luchd-leantainn a-mhàin a lìbhrigeadh dha na frithealaichean far a bheil an luchd-leantainn agad a’ còmhnaidh agus thèid na teachdaireachdan dìreach a lìbhrigeadh gu frithealaichean nam faightearan nuair a bhios iad a’ còmhnaidh air frithealaiche eile.

Nuair a dh’ùghdarraicheas tu aplacaid gun cleachd i an cunntas agad, a-rèir sgòp nan ceadan a dh’aontaicheas tu riutha, faodaidh i fiosrachadh poblach na pròifil agad, liosta na feadhna air a bhios tu a’ leantainn, an luchd-leantainn agad, na liostaichean agad, na postaichean agad uile ’s na h-annsachdan agad inntrigeadh. Chan urrainn do dh’aplacaidean an seòladh puist-d no am facal-faire agad inntrigeadh idir.


Cleachdadh na làraich leis a’ chloinn

Ma tha am frithealaiche seo san Aonadh Eòrpach (AE) no san Roinn Eaconomach na h-Eòrpa (EEA): Tha an làrach, na batharan agus na seirbheisean againn uile ag amas air an fheadhainn a tha co-dhiù 16 bliadhnaichean a dh’aois. Ma tha thu nas òige na 16 bliadhnaichean a dh’aois, tha e riatanach fon GDPR (General Data Protection Regulation) nach cleachd thu an làrach seo.

Ma tha am frithealaiche seo sna Stàitean Aonaichte (SAA): Tha an làrach, na batharan agus na seirbheisean againn uile ag amas air an fheadhainn a tha co-dhiù 13 bliadhnaichean a dh’aois. Ma tha thu nas òige na 16 bliadhnaichean a dh’aois, tha e riatanach fon COPPA (Children''s Online Privacy Protection Act) nach cleachd thu an làrach seo.

Dh’fhaoidte gu bheil am frithealaiche seo fo riatanasan lagha eile ma tha e ann an uachdranas laghail eile.


Atharraichean air a’ phoileasaidh phrìobhaideachd againn

Ma chuireas sinn romhainn am poileasaidh prìobhaideachd againn atharrachadh, postaichidh sinn na h-atharraichean dhan duilleag seo.

Tha an sgrìobhainn seo fo cheadachas CC-BY-SA. Chaidh ùrachadh an turas mu dheireadh an t-7mh dhen Mhart 2018.

Chaidh a fhreagarrachadh o thùs o phoileasaidh prìobhaideachd Discourse.

- ' +' title: Teirmichean na seirbheise ⁊ poileasaidh prìobhaideachd %{instance} themes: contrast: Mastodon (iomsgaradh àrd) diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 8c408c8a4..02d8b4704 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -23,7 +23,7 @@ gl: hosted_on: Mastodon aloxado en %{domain} instance_actor_flash: 'Esta conta é un actor virtual utilizado para representar ao servidor e non a unha usuaria individual. Utilízase para propósitos de federación e non debería estar bloqueada a menos que queiras bloquear a toda a instancia, en tal caso deberías utilizar o bloqueo do dominio. - ' +' learn_more: Saber máis privacy_policy: Política de privacidade rules: Regras do servidor diff --git a/config/locales/is.yml b/config/locales/is.yml index cf2fcc417..a380521f5 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -270,14 +270,14 @@ is: create_domain_allow_html: "%{name} leyfði skýjasamband með léninu %{target}" create_domain_block_html: "%{name} útilokaði lénið %{target}" create_email_domain_block_html: "%{name} útilokaði póstlénið %{target}" - create_ip_block_html: "{name} útbjó reglu fyrir IP-vistfangið %{target}" + create_ip_block_html: "%{name} útbjó reglu fyrir IP-vistfangið %{target}" demote_user_html: "%{name} lækkaði notandann %{target} í tign" destroy_announcement_html: "%{name} eyddi tilkynninguni %{target}" destroy_custom_emoji_html: "%{name} henti út tjáningartákninu %{target}" destroy_domain_allow_html: "%{name} bannaði skýjasamband með léninu %{target}" destroy_domain_block_html: "%{name} aflétti útilokun af léninu %{target}" destroy_email_domain_block_html: "%{name} aflétti útilokun af póstléninu %{target}" - destroy_ip_block_html: "{name} eyddi reglu fyrir IP-vistfangið %{target}" + destroy_ip_block_html: "%{name} eyddi reglu fyrir IP-vistfangið %{target}" destroy_status_html: "%{name} fjarlægði stöðufærslu frá %{target}" disable_2fa_user_html: "%{name} gerði kröfu um tveggja-þátta innskráningu óvirka fyrir notandann %{target}" disable_custom_emoji_html: "%{name} gerði tjáningartáknið %{target} óvirkt" @@ -286,7 +286,7 @@ is: enable_user_html: "%{name} gerði innskráningu virka fyrir notandann %{target}" memorialize_account_html: "%{name} breytti notandaaðgangnum %{target} í minningargreinarsíðu" promote_user_html: "%{name} hækkaði notandann %{target} í tign" - remove_avatar_user_html: "{name} fjarlægði auðkennismynd af %{target}" + remove_avatar_user_html: "%{name} fjarlægði auðkennismynd af %{target}" reopen_report_html: "%{name} enduropnaði kæru %{target}" reset_password_user_html: "%{name} endurstillti lykilorð fyrir notandann %{target}" resolve_report_html: "%{name} leysti kæru %{target}" @@ -296,7 +296,7 @@ is: unassigned_report_html: "%{name} fjarlægði úthlutun af kæru %{target}" unsensitive_account_html: "%{name} tók merkinguna viðkvæmt af myndefni frá %{target}" unsilence_account_html: "%{name} hætti að hylja notandaaðganginn %{target}" - unsuspend_account_html: "%{name} tók notandaaðganginn {target} úr bið" + unsuspend_account_html: "%{name} tók notandaaðganginn %{target} úr bið" update_announcement_html: "%{name} uppfærði tilkynningu %{target}" update_custom_emoji_html: "%{name} uppfærði tjáningartáknið %{target}" update_domain_block_html: "%{name} uppfærði lénalás fyrir %{target}" diff --git a/config/locales/sc.yml b/config/locales/sc.yml index 2c3e0ab2a..9ded82f3c 100644 --- a/config/locales/sc.yml +++ b/config/locales/sc.yml @@ -23,7 +23,7 @@ sc: hosted_on: Mastodon allogiadu in %{domain} instance_actor_flash: 'Custu contu est un''atore virtuale impreadu pro rapresentare su pròpiu serbidore, no est un''utente individuale. Benit impreadu pro punnas de federatzione e no ddu dias dèpere blocare si non boles blocare su domìniu intreu, e in cussu casu dias dèpere impreare unu blocu de domìniu. - ' +' learn_more: Àteras informatziones privacy_policy: Polìtica de riservadesa rules: Règulas de su serbidore -- cgit From f627d2eb938d220eb767b0211b66b4281c921f75 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 1 May 2021 23:18:59 +0200 Subject: Fix trying to fetch key from empty URI when verifying HTTP signature (#16100) --- app/helpers/jsonld_helper.rb | 2 +- app/services/activitypub/fetch_remote_key_service.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'app/helpers') diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index 1c473efa3..62eb50f78 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -67,7 +67,7 @@ module JsonLdHelper unless id json = fetch_resource_without_id_validation(uri, on_behalf_of) - return unless json + return if !json.is_a?(Hash) || unsupported_uri_scheme?(json['id']) uri = json['id'] end diff --git a/app/services/activitypub/fetch_remote_key_service.rb b/app/services/activitypub/fetch_remote_key_service.rb index df17d9079..c48288b3b 100644 --- a/app/services/activitypub/fetch_remote_key_service.rb +++ b/app/services/activitypub/fetch_remote_key_service.rb @@ -5,6 +5,8 @@ class ActivityPub::FetchRemoteKeyService < BaseService # Returns account that owns the key def call(uri, id: true, prefetched_body: nil) + return if uri.blank? + if prefetched_body.nil? if id @json = fetch_resource_without_id_validation(uri) -- cgit From 351c74459084ccffce1333b57c2af9a6b55cac8d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 5 May 2021 21:16:55 +0200 Subject: Fix error when trying to render component for media without meta (#16112) --- app/controllers/statuses_controller.rb | 5 -- app/helpers/statuses_helper.rb | 80 +++++++++++++++++++++++++++ app/views/accounts/_header.html.haml | 4 +- app/views/media/player.html.haml | 7 ++- app/views/statuses/_detailed_status.html.haml | 24 +++----- app/views/statuses/_poll.html.haml | 4 +- app/views/statuses/_simple_status.html.haml | 24 +++----- app/views/statuses/_status.html.haml | 7 +-- app/views/statuses/embed.html.haml | 2 +- 9 files changed, 111 insertions(+), 46 deletions(-) (limited to 'app/helpers') diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index 87612a296..c52170d08 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -16,7 +16,6 @@ class StatusesController < ApplicationController before_action :set_referrer_policy_header, only: :show before_action :set_cache_headers before_action :set_body_classes - before_action :set_autoplay, only: :embed skip_around_action :set_locale, if: -> { request.format == :json } skip_before_action :require_functional!, only: [:show, :embed], unless: :whitelist_mode? @@ -82,8 +81,4 @@ class StatusesController < ApplicationController def set_referrer_policy_header response.headers['Referrer-Policy'] = 'origin' unless @status.distributable? end - - def set_autoplay - @autoplay = truthy_param?(:autoplay) - end end diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index 1f654f34f..25f079e9d 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -130,4 +130,84 @@ module StatusesHelper def embedded_view? params[:controller] == EMBEDDED_CONTROLLER && params[:action] == EMBEDDED_ACTION end + + def render_video_component(status, **options) + video = status.media_attachments.first + + meta = video.file.meta || {} + + component_params = { + sensitive: sensitized?(status, current_account), + src: full_asset_url(video.file.url(:original)), + preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), + alt: video.description, + blurhash: video.blurhash, + frameRate: meta.dig('original', 'frame_rate'), + inline: true, + media: [ + ActiveModelSerializers::SerializableResource.new(video, serializer: REST::MediaAttachmentSerializer), + ].as_json, + }.merge(**options) + + react_component :video, component_params do + render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + end + end + + def render_audio_component(status, **options) + audio = status.media_attachments.first + + meta = audio.file.meta || {} + + component_params = { + src: full_asset_url(audio.file.url(:original)), + poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), + alt: audio.description, + backgroundColor: meta.dig('colors', 'background'), + foregroundColor: meta.dig('colors', 'foreground'), + accentColor: meta.dig('colors', 'accent'), + duration: meta.dig('original', 'duration'), + }.merge(**options) + + react_component :audio, component_params do + render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + end + end + + def render_media_gallery_component(status, **options) + component_params = { + sensitive: sensitized?(status, current_account), + autoplay: prefers_autoplay?, + media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }, + }.merge(**options) + + react_component :media_gallery, component_params do + render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + end + end + + def render_card_component(status, **options) + component_params = { + sensitive: sensitized?(status, current_account), + maxDescription: 160, + card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json, + }.merge(**options) + + react_component :card, component_params + end + + def render_poll_component(status, **options) + component_params = { + disabled: true, + poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json, + }.merge(**options) + + react_component :poll, component_params do + render partial: 'statuses/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: prefers_autoplay? } + end + end + + def prefers_autoplay? + ActiveModel::Type::Boolean.new.cast(params[:autoplay]) || current_user&.setting_auto_play_gif + end end diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index 4ef9f9478..cae5a5ac9 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -1,9 +1,9 @@ .public-account-header{:class => ("inactive" if account.moved?)} .public-account-header__image - = image_tag (current_account&.user&.setting_auto_play_gif ? account.header_original_url : account.header_static_url), class: 'parallax' + = image_tag (prefers_autoplay? ? account.header_original_url : account.header_static_url), class: 'parallax' .public-account-header__bar = link_to short_account_url(account), class: 'avatar' do - = image_tag (current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url), id: 'profile_page_avatar', data: {original: full_asset_url(account.avatar_original_url), static: full_asset_url(account.avatar_static_url), autoplay: current_account&.user&.setting_auto_play_gif} + = image_tag (prefers_autoplay? ? account.avatar_original_url : account.avatar_static_url), id: 'profile_page_avatar', data: { original: full_asset_url(account.avatar_original_url), static: full_asset_url(account.avatar_static_url), autoplay: prefers_autoplay? } .public-account-header__tabs .public-account-header__tabs__name %h1 diff --git a/app/views/media/player.html.haml b/app/views/media/player.html.haml index 95e37bb22..f00c8f040 100644 --- a/app/views/media/player.html.haml +++ b/app/views/media/player.html.haml @@ -2,8 +2,11 @@ = render_initial_state = javascript_pack_tag 'public', crossorigin: 'anonymous' +:ruby + meta = @media_attachment.file.meta || {} + - if @media_attachment.video? - = react_component :video, src: @media_attachment.file.url(:original), preview: @media_attachment.thumbnail.present? ? @media_attachment.thumbnail.url : @media_attachment.file.url(:small), frameRate: @media_attachment.file.meta.dig('original', 'frame_rate'), blurhash: @media_attachment.blurhash, width: 670, height: 380, editable: true, detailed: true, inline: true, alt: @media_attachment.description, media: [ActiveModelSerializers::SerializableResource.new(@media_attachment, serializer: REST::MediaAttachmentSerializer)].as_json do + = react_component :video, src: @media_attachment.file.url(:original), preview: @media_attachment.thumbnail.present? ? @media_attachment.thumbnail.url : @media_attachment.file.url(:small), frameRate: meta.dig('original', 'frame_rate'), blurhash: @media_attachment.blurhash, width: 670, height: 380, editable: true, detailed: true, inline: true, alt: @media_attachment.description, media: [ActiveModelSerializers::SerializableResource.new(@media_attachment, serializer: REST::MediaAttachmentSerializer)].as_json do %video{ controls: 'controls' } %source{ src: @media_attachment.file.url(:original) } - elsif @media_attachment.gifv? @@ -11,6 +14,6 @@ %video{ autoplay: 'autoplay', muted: 'muted', loop: 'loop' } %source{ src: @media_attachment.file.url(:original) } - elsif @media_attachment.audio? - = react_component :audio, src: @media_attachment.file.url(:original), poster: @media_attachment.thumbnail.present? ? @media_attachment.thumbnail.url : @media_attachment.account.avatar_static_url, backgroundColor: @media_attachment.file.meta.dig('colors', 'background'), foregroundColor: @media_attachment.file.meta.dig('colors', 'foreground'), accentColor: @media_attachment.file.meta.dig('colors', 'accent'), width: 670, height: 380, fullscreen: true, alt: @media_attachment.description, duration: @media_attachment.file.meta.dig(:original, :duration) do + = react_component :audio, src: @media_attachment.file.url(:original), poster: @media_attachment.thumbnail.present? ? @media_attachment.thumbnail.url : @media_attachment.account.avatar_static_url, backgroundColor: meta.dig('colors', 'background'), foregroundColor: meta.dig('colors', 'foreground'), accentColor: meta.dig('colors', 'accent'), width: 670, height: 380, fullscreen: true, alt: @media_attachment.description, duration: meta.dig(:original, :duration) do %audio{ controls: 'controls' } %source{ src: @media_attachment.file.url(:original) } diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index 93af131e5..daf164949 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -2,13 +2,13 @@ .p-author.h-card = link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'detailed-status__display-name u-url', target: stream_link_target, rel: 'noopener' do .detailed-status__display-avatar - - if current_account&.user&.setting_auto_play_gif || autoplay + - if prefers_autoplay? = image_tag status.account.avatar_original_url, alt: '', class: 'account__avatar u-photo' - else = image_tag status.account.avatar_static_url, alt: '', class: 'account__avatar u-photo' %span.display-name %bdi - %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: autoplay) + %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: prefers_autoplay?) %span.display-name__account = acct(status.account) = fa_icon('lock') if status.account.locked? @@ -18,28 +18,22 @@ .status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }< - if status.spoiler_text? %p< - %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}  + %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: prefers_autoplay?)}  %button.status__content__spoiler-link= t('statuses.show_more') .e-content - = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) + = Formatter.instance.format(status, custom_emojify: true, autoplay: prefers_autoplay?) - if status.preloadable_poll - = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do - = render partial: 'statuses/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay } + = render_poll_component(status) - if !status.media_attachments.empty? - if status.media_attachments.first.video? - - video = status.media_attachments.first - = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), frameRate: video.file.meta.dig('original', 'frame_rate'), blurhash: video.blurhash, sensitive: sensitized?(status, current_account), width: 670, height: 380, detailed: true, inline: true, alt: video.description, media: [ActiveModelSerializers::SerializableResource.new(video, serializer: REST::MediaAttachmentSerializer)].as_json do - = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + = render_video_component(status, width: 670, height: 380, detailed: true) - elsif status.media_attachments.first.audio? - - audio = status.media_attachments.first - = react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 670, height: 380, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do - = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + = render_audio_component(status, width: 670, height: 380) - else - = react_component :media_gallery, height: 380, sensitive: sensitized?(status, current_account), standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do - = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + = render_media_gallery_component(status, height: 380, standalone: true) - elsif status.preview_card - = react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json + = render_card_component(status) .detailed-status__meta %data.dt-published{ value: status.created_at.to_time.iso8601 } diff --git a/app/views/statuses/_poll.html.haml b/app/views/statuses/_poll.html.haml index 64e62e97c..3546a923e 100644 --- a/app/views/statuses/_poll.html.haml +++ b/app/views/statuses/_poll.html.haml @@ -12,7 +12,7 @@ %span.poll__number>< = "#{percent.round}%" %span.poll__option__text - = Formatter.instance.format_poll_option(status, option, autoplay: autoplay) + = Formatter.instance.format_poll_option(status, option, autoplay: prefers_autoplay?) - if own_votes.include?(index) %span.poll__voted %i.poll__voted__mark.fa.fa-check @@ -23,7 +23,7 @@ %label.poll__option>< %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}>< %span.poll__option__text - = Formatter.instance.format_poll_option(status, option, autoplay: autoplay) + = Formatter.instance.format_poll_option(status, option, autoplay: prefers_autoplay?) .poll__footer - unless show_results %button.button.button-secondary{ disabled: true } diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index 7e5f11259..728e6b9b0 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -13,13 +13,13 @@ = link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'status__display-name u-url', target: stream_link_target, rel: 'noopener noreferrer' do .status__avatar %div - - if current_account&.user&.setting_auto_play_gif || autoplay + - if prefers_autoplay? = image_tag status.account.avatar_original_url, alt: '', class: 'u-photo account__avatar' - else = image_tag status.account.avatar_static_url, alt: '', class: 'u-photo account__avatar' %span.display-name %bdi - %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: autoplay) + %strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: prefers_autoplay?) = ' ' %span.display-name__account = acct(status.account) @@ -27,28 +27,22 @@ .status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }< - if status.spoiler_text? %p< - %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}  + %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: prefers_autoplay?)}  %button.status__content__spoiler-link= t('statuses.show_more') .e-content - = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) + = Formatter.instance.format(status, custom_emojify: true, autoplay: prefers_autoplay?) - if status.preloadable_poll - = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do - = render partial: 'statuses/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay } + = render_poll_component(status) - if !status.media_attachments.empty? - if status.media_attachments.first.video? - - video = status.media_attachments.first - = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), frameRate: video.file.meta.dig('original', 'frame_rate'), blurhash: video.blurhash, sensitive: sensitized?(status, current_account), width: 610, height: 343, inline: true, alt: video.description, media: [ActiveModelSerializers::SerializableResource.new(video, serializer: REST::MediaAttachmentSerializer)].as_json do - = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + = render_video_component(status, width: 610, height: 343) - elsif status.media_attachments.first.audio? - - audio = status.media_attachments.first - = react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 610, height: 343, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do - = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + = render_audio_component(status, width: 610, height: 343) - else - = react_component :media_gallery, height: 343, sensitive: sensitized?(status, current_account), autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do - = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + = render_media_gallery_component(status, height: 343) - elsif status.preview_card - = react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json + = render_card_component(status) - if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id && !hide_show_thread = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__content__read-more-button', target: stream_link_target, rel: 'noopener noreferrer' do diff --git a/app/views/statuses/_status.html.haml b/app/views/statuses/_status.html.haml index 13a06519c..9f3197d0d 100644 --- a/app/views/statuses/_status.html.haml +++ b/app/views/statuses/_status.html.haml @@ -5,7 +5,6 @@ is_successor ||= false direct_reply_id ||= false parent_id ||= false - autoplay ||= current_account&.user&.setting_auto_play_gif is_direct_parent = direct_reply_id == status.id is_direct_child = parent_id == status.in_reply_to_id centered ||= include_threads && !is_predecessor && !is_successor @@ -19,7 +18,7 @@ .entry{ class: entry_classes } = link_to_older ActivityPub::TagManager.instance.url_for(@next_ancestor) - = render partial: 'statuses/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id }, autoplay: autoplay + = render partial: 'statuses/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id } .entry{ class: entry_classes } @@ -39,14 +38,14 @@ %span = t('stream_entries.pinned') - = render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, autoplay: autoplay, hide_show_thread: is_predecessor || is_successor + = render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, hide_show_thread: is_predecessor || is_successor - if include_threads - if @since_descendant_thread_id .entry{ class: entry_classes } = link_to_newer short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1) - @descendant_threads.each do |thread| - = render partial: 'statuses/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id }, autoplay: autoplay + = render partial: 'statuses/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id } - if thread[:next_status] .entry{ class: entry_classes } diff --git a/app/views/statuses/embed.html.haml b/app/views/statuses/embed.html.haml index 2f111f53f..18d62fd8e 100644 --- a/app/views/statuses/embed.html.haml +++ b/app/views/statuses/embed.html.haml @@ -1,2 +1,2 @@ .activity-stream.activity-stream--headless - = render 'status', status: @status, centered: true, autoplay: @autoplay + = render 'status', status: @status, centered: true -- cgit From 7cb34b32f8bc925b56c79dbcf053671f93f2eb42 Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Thu, 6 May 2021 06:39:02 +0900 Subject: Add management of delivery availability in Federation settings (#15771) * Add management of delivery availavility in Federation settings * fix translate * Remove useless object creation * Fix DeepSource issue * Add shortcut for all * Fix DeepSource(skipcq) * Change 'remove' to 'clear' * Fix style * Change class method name (exhausted_deliveries_key_by) --- app/controllers/admin/instances_controller.rb | 44 +++++++++++++++++++++- app/helpers/admin/action_logs_helper.rb | 4 +- app/lib/delivery_failure_tracker.rb | 26 +++++++++++++ app/models/admin/action_log_filter.rb | 2 + app/models/instance.rb | 3 ++ app/models/instance_filter.rb | 8 +++- app/policies/delivery_policy.rb | 15 ++++++++ .../instances/_exhausted_deliveries_days.haml | 2 + app/views/admin/instances/_instance.html.haml | 8 ++++ app/views/admin/instances/index.html.haml | 18 +++++++++ app/views/admin/instances/show.html.haml | 25 ++++++++++++ config/locales/en.yml | 21 +++++++++++ config/routes.rb | 9 ++++- 13 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 app/policies/delivery_policy.rb create mode 100644 app/views/admin/instances/_exhausted_deliveries_days.haml (limited to 'app/helpers') diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb index b5918d231..748c5de5a 100644 --- a/app/controllers/admin/instances_controller.rb +++ b/app/controllers/admin/instances_controller.rb @@ -3,7 +3,8 @@ module Admin class InstancesController < BaseController before_action :set_instances, only: :index - before_action :set_instance, only: :show + before_action :set_instance, except: :index + before_action :set_exhausted_deliveries_days, only: :show def index authorize :instance, :index? @@ -13,14 +14,55 @@ module Admin authorize :instance, :show? end + def clear_delivery_errors + authorize :delivery, :clear_delivery_errors? + + @instance.delivery_failure_tracker.clear_failures! + redirect_to admin_instance_path(@instance.domain) + end + + def restart_delivery + authorize :delivery, :restart_delivery? + + last_unavailable_domain = unavailable_domain + + if last_unavailable_domain.present? + @instance.delivery_failure_tracker.track_success! + log_action :destroy, last_unavailable_domain + end + + redirect_to admin_instance_path(@instance.domain) + end + + def stop_delivery + authorize :delivery, :stop_delivery? + + UnavailableDomain.create(domain: @instance.domain) + log_action :create, unavailable_domain + redirect_to admin_instance_path(@instance.domain) + end + private def set_instance @instance = Instance.find(params[:id]) end + def set_exhausted_deliveries_days + @exhausted_deliveries_days = @instance.delivery_failure_tracker.exhausted_deliveries_days + end + def set_instances @instances = filtered_instances.page(params[:page]) + warning_domains_map = DeliveryFailureTracker.warning_domains_map + + @instances.each do |instance| + instance.failure_days = warning_domains_map[instance.domain] + end + end + + def unavailable_domain + UnavailableDomain.find_by(domain: @instance.domain) end def filtered_instances diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb index 0f3ca36e2..e9a298a24 100644 --- a/app/helpers/admin/action_logs_helper.rb +++ b/app/helpers/admin/action_logs_helper.rb @@ -21,7 +21,7 @@ module Admin::ActionLogsHelper record.shortcode when 'Report' link_to "##{record.id}", admin_report_path(record) - when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock' + when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock', 'UnavailableDomain' link_to record.domain, "https://#{record.domain}" when 'Status' link_to record.account.acct, ActivityPub::TagManager.instance.url_for(record) @@ -38,7 +38,7 @@ module Admin::ActionLogsHelper case type when 'CustomEmoji' attributes['shortcode'] - when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock' + when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock', 'UnavailableDomain' link_to attributes['domain'], "https://#{attributes['domain']}" when 'Status' tmp_status = Status.new(attributes.except('reblogs_count', 'favourites_count')) diff --git a/app/lib/delivery_failure_tracker.rb b/app/lib/delivery_failure_tracker.rb index 2cd6ef7ad..8907ade4c 100644 --- a/app/lib/delivery_failure_tracker.rb +++ b/app/lib/delivery_failure_tracker.rb @@ -17,6 +17,10 @@ class DeliveryFailureTracker UnavailableDomain.find_by(domain: @host)&.destroy end + def clear_failures! + Redis.current.del(exhausted_deliveries_key) + end + def days Redis.current.scard(exhausted_deliveries_key) || 0 end @@ -25,6 +29,10 @@ class DeliveryFailureTracker !UnavailableDomain.where(domain: @host).exists? end + def exhausted_deliveries_days + Redis.current.smembers(exhausted_deliveries_key).sort.map { |date| Date.new(date.slice(0, 4).to_i, date.slice(4, 2).to_i, date.slice(6, 2).to_i) } + end + alias reset! track_success! class << self @@ -44,6 +52,24 @@ class DeliveryFailureTracker def reset!(url) new(url).reset! end + + def warning_domains + domains = Redis.current.keys(exhausted_deliveries_key_by('*')).map do |key| + key.delete_prefix(exhausted_deliveries_key_by('')) + end + + domains - UnavailableDomain.all.pluck(:domain) + end + + def warning_domains_map + warning_domains.index_with { |domain| Redis.current.scard(exhausted_deliveries_key_by(domain)) } + end + + private + + def exhausted_deliveries_key_by(host) + "exhausted_deliveries:#{host}" + end end private diff --git a/app/models/admin/action_log_filter.rb b/app/models/admin/action_log_filter.rb index 3a1b67e06..a1c156a8b 100644 --- a/app/models/admin/action_log_filter.rb +++ b/app/models/admin/action_log_filter.rb @@ -17,12 +17,14 @@ class Admin::ActionLogFilter create_domain_allow: { target_type: 'DomainAllow', action: 'create' }.freeze, create_domain_block: { target_type: 'DomainBlock', action: 'create' }.freeze, create_email_domain_block: { target_type: 'EmailDomainBlock', action: 'create' }.freeze, + create_unavailable_domain: { target_type: 'UnavailableDomain', action: 'create' }.freeze, demote_user: { target_type: 'User', action: 'demote' }.freeze, destroy_announcement: { target_type: 'Announcement', action: 'destroy' }.freeze, destroy_custom_emoji: { target_type: 'CustomEmoji', action: 'destroy' }.freeze, destroy_domain_allow: { target_type: 'DomainAllow', action: 'destroy' }.freeze, destroy_domain_block: { target_type: 'DomainBlock', action: 'destroy' }.freeze, destroy_email_domain_block: { target_type: 'EmailDomainBlock', action: 'destroy' }.freeze, + destroy_unavailable_domain: { target_type: 'UnavailableDomain', action: 'destroy' }.freeze, destroy_status: { target_type: 'Status', action: 'destroy' }.freeze, disable_2fa_user: { target_type: 'User', action: 'disable' }.freeze, disable_custom_emoji: { target_type: 'CustomEmoji', action: 'disable' }.freeze, diff --git a/app/models/instance.rb b/app/models/instance.rb index 29be03662..8949be054 100644 --- a/app/models/instance.rb +++ b/app/models/instance.rb @@ -10,10 +10,13 @@ class Instance < ApplicationRecord self.primary_key = :domain + attr_accessor :failure_days + has_many :accounts, foreign_key: :domain, primary_key: :domain belongs_to :domain_block, foreign_key: :domain, primary_key: :domain belongs_to :domain_allow, foreign_key: :domain, primary_key: :domain + belongs_to :unavailable_domain, foreign_key: :domain, primary_key: :domain # skipcq: RB-RL1031 scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } diff --git a/app/models/instance_filter.rb b/app/models/instance_filter.rb index 0598d8fea..9e533c4aa 100644 --- a/app/models/instance_filter.rb +++ b/app/models/instance_filter.rb @@ -4,6 +4,8 @@ class InstanceFilter KEYS = %i( limited by_domain + warning + unavailable ).freeze attr_reader :params @@ -13,7 +15,7 @@ class InstanceFilter end def results - scope = Instance.includes(:domain_block, :domain_allow).order(accounts_count: :desc) + scope = Instance.includes(:domain_block, :domain_allow, :unavailable_domain).order(accounts_count: :desc) params.each do |key, value| scope.merge!(scope_for(key, value.to_s.strip)) if value.present? @@ -32,6 +34,10 @@ class InstanceFilter Instance.joins(:domain_allow).reorder(Arel.sql('domain_allows.id desc')) when 'by_domain' Instance.matches_domain(value) + when 'warning' + Instance.where(domain: DeliveryFailureTracker.warning_domains) + when 'unavailable' + Instance.joins(:unavailable_domain) else raise "Unknown filter: #{key}" end diff --git a/app/policies/delivery_policy.rb b/app/policies/delivery_policy.rb new file mode 100644 index 000000000..24d06c168 --- /dev/null +++ b/app/policies/delivery_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class DeliveryPolicy < ApplicationPolicy + def clear_delivery_errors? + admin? + end + + def restart_delivery? + admin? + end + + def stop_delivery? + admin? + end +end diff --git a/app/views/admin/instances/_exhausted_deliveries_days.haml b/app/views/admin/instances/_exhausted_deliveries_days.haml new file mode 100644 index 000000000..e581f542d --- /dev/null +++ b/app/views/admin/instances/_exhausted_deliveries_days.haml @@ -0,0 +1,2 @@ +%li.negative-hint + = l(exhausted_deliveries_days) diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml index 188d0d984..990cf9ec8 100644 --- a/app/views/admin/instances/_instance.html.haml +++ b/app/views/admin/instances/_instance.html.haml @@ -22,4 +22,12 @@ = t('admin.accounts.whitelisted') - else = t('admin.accounts.no_limits_imposed') + - if instance.failure_days + = ' / ' + %span.negative-hint + = t('admin.instances.delivery.warning_message', count: instance.failure_days) + - if instance.unavailable_domain + = ' / ' + %span.negative-hint + = t('admin.instances.delivery.unavailable_message') .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml index 7c7958786..797948d94 100644 --- a/app/views/admin/instances/index.html.haml +++ b/app/views/admin/instances/index.html.haml @@ -16,6 +16,24 @@ - unless whitelist_mode? %li= filter_link_to t('admin.instances.moderation.limited'), limited: '1' + .filter-subset + %strong= t('admin.instances.delivery.title') + %ul + %li= filter_link_to t('admin.instances.delivery.all'), warning: nil, unavailable: nil + %li= filter_link_to t('admin.instances.delivery.warning'), warning: '1', unavailable: nil + %li= filter_link_to t('admin.instances.delivery.unavailable'), warning: nil, unavailable: '1' + + .back-link + = link_to admin_instances_path() do + %i.fa.fa-chevron-left.fa-fw + = t('admin.instances.back_to_all') + = link_to admin_instances_path(limited: 1) do + %i.fa.fa-chevron-left.fa-fw + = t('admin.instances.back_to_limited') + = link_to admin_instances_path(warning: 1) do + %i.fa.fa-chevron-left.fa-fw + = t('admin.instances.back_to_warning') + - unless whitelist_mode? = form_tag admin_instances_url, method: 'GET', class: 'simple_form' do .fields-group diff --git a/app/views/admin/instances/show.html.haml b/app/views/admin/instances/show.html.haml index 0b9382771..462529338 100644 --- a/app/views/admin/instances/show.html.haml +++ b/app/views/admin/instances/show.html.haml @@ -1,6 +1,18 @@ - content_for :page_title do = @instance.domain +.filters + .back-link + = link_to admin_instances_path() do + %i.fa.fa-chevron-left.fa-fw + = t('admin.instances.back_to_all') + = link_to admin_instances_path(limited: 1) do + %i.fa.fa-chevron-left.fa-fw + = t('admin.instances.back_to_limited') + = link_to admin_instances_path(warning: 1) do + %i.fa.fa-chevron-left.fa-fw + = t('admin.instances.back_to_warning') + .dashboard__counters %div = link_to admin_accounts_path(remote: '1', by_domain: @instance.domain) do @@ -48,6 +60,13 @@ = simple_format(h(@instance.public_comment)) .speech-bubble__owner= t 'admin.instances.public_comment' +- unless @exhausted_deliveries_days.empty? + %h4= t 'admin.instances.delivery_error_days' + %ul + = render partial: 'exhausted_deliveries_days', collection: @exhausted_deliveries_days + %p.hint + = t 'admin.instances.delivery_error_hint', count: DeliveryFailureTracker::FAILURE_DAYS_THRESHOLD + %hr.spacer/ %div.action-buttons @@ -59,3 +78,9 @@ = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button' - else = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button' + - if @instance.delivery_failure_tracker.available? + - unless @exhausted_deliveries_days.empty? + = link_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' + = link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' + - else + = link_to t('admin.instances.delivery.restart'), restart_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }, class: 'button' diff --git a/config/locales/en.yml b/config/locales/en.yml index 1b41ee063..bfa489817 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -230,6 +230,7 @@ en: create_domain_block: Create Domain Block create_email_domain_block: Create E-mail Domain Block create_ip_block: Create IP rule + create_unavailable_domain: Create Unavailable Domain demote_user: Demote User destroy_announcement: Delete Announcement destroy_custom_emoji: Delete Custom Emoji @@ -238,6 +239,7 @@ en: destroy_email_domain_block: Delete e-mail domain block destroy_ip_block: Delete IP rule destroy_status: Delete Post + destroy_unavailable_domain: Delete Unavailable Domain disable_2fa_user: Disable 2FA disable_custom_emoji: Disable Custom Emoji disable_user: Disable User @@ -271,6 +273,7 @@ en: create_domain_block_html: "%{name} blocked domain %{target}" create_email_domain_block_html: "%{name} blocked e-mail domain %{target}" create_ip_block_html: "%{name} created rule for IP %{target}" + create_unavailable_domain_html: "%{name} stopped delivery to domain %{target}" demote_user_html: "%{name} demoted user %{target}" destroy_announcement_html: "%{name} deleted announcement %{target}" destroy_custom_emoji_html: "%{name} destroyed emoji %{target}" @@ -279,6 +282,7 @@ en: destroy_email_domain_block_html: "%{name} unblocked e-mail domain %{target}" destroy_ip_block_html: "%{name} deleted rule for IP %{target}" destroy_status_html: "%{name} removed post by %{target}" + destroy_unavailable_domain_html: "%{name} resumed delivery to domain %{target}" disable_2fa_user_html: "%{name} disabled two factor requirement for user %{target}" disable_custom_emoji_html: "%{name} disabled emoji %{target}" disable_user_html: "%{name} disabled login for user %{target}" @@ -451,8 +455,25 @@ en: title: Follow recommendations unsuppress: Restore follow recommendation instances: + back_to_all: All + back_to_limited: Limited + back_to_warning: Warning by_domain: Domain + delivery: + all: All + clear: Clear delivery errors + restart: Restart delivery + stop: Stop delivery + title: Delivery + unavailable: Unavailable + unavailable_message: Delivery unavailable + warning: Warning + warning_message: + one: Delivery failure %{count} day + other: Delivery failure %{count} days delivery_available: Delivery is available + delivery_error_days: Delivery error days + delivery_error_hint: If delivery is not possible for %{count} days, it will be automatically marked as undeliverable. empty: No domains found. known_accounts: one: "%{count} known account" diff --git a/config/routes.rb b/config/routes.rb index 4661a7c11..8ca7fccdd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -217,7 +217,14 @@ Rails.application.routes.draw do end end - resources :instances, only: [:index, :show], constraints: { id: /[^\/]+/ } + resources :instances, only: [:index, :show], constraints: { id: /[^\/]+/ } do + member do + post :clear_delivery_errors + post :restart_delivery + post :stop_delivery + end + end + resources :rules resources :reports, only: [:index, :show] do -- cgit