From 3bdfa3eb4c8e4ec16bff03928a5a8073e4d6e494 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Sat, 4 Jan 2025 13:42:27 -0600 Subject: [PATCH 001/133] Added validator for extra profile field values with empty name (#33421) --- app/models/account.rb | 3 ++- .../empty_profile_field_names_validator.rb | 15 +++++++++++++++ config/locales/activerecord.en.yml | 2 ++ spec/models/account_spec.rb | 16 ++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 app/validators/empty_profile_field_names_validator.rb diff --git a/app/models/account.rb b/app/models/account.rb index 206529301e..d42da2e9af 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -118,6 +118,7 @@ class Account < ApplicationRecord validates :display_name, length: { maximum: DISPLAY_NAME_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_display_name? } validates :note, note_length: { maximum: NOTE_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_note? } validates :fields, length: { maximum: DEFAULT_FIELDS_SIZE }, if: -> { local? && will_save_change_to_fields? } + validates_with EmptyProfileFieldNamesValidator, if: -> { local? && will_save_change_to_fields? } with_options on: :create do validates :uri, absence: true, if: :local? validates :inbox_url, absence: true, if: :local? @@ -300,7 +301,7 @@ class Account < ApplicationRecord if attributes.is_a?(Hash) attributes.each_value do |attr| - next if attr[:name].blank? + next if attr[:name].blank? && attr[:value].blank? previous = old_fields.find { |item| item['value'] == attr[:value] } diff --git a/app/validators/empty_profile_field_names_validator.rb b/app/validators/empty_profile_field_names_validator.rb new file mode 100644 index 0000000000..c979f9f567 --- /dev/null +++ b/app/validators/empty_profile_field_names_validator.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class EmptyProfileFieldNamesValidator < ActiveModel::Validator + def validate(account) + return if account.fields.empty? + + account.errors.add(:fields, :fields_with_values_missing_labels) if fields_with_values_missing_names?(account) + end + + private + + def fields_with_values_missing_names?(account) + account.fields.any? { |field| field.name.blank? && field.value.present? } + end +end diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 569c4c4d68..57404d2648 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -24,6 +24,8 @@ en: models: account: attributes: + fields: + fields_with_values_missing_labels: contains values with missing labels username: invalid: must contain only letters, numbers and underscores reserved: is reserved diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 77341b7786..5ba39848b7 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -822,6 +822,10 @@ RSpec.describe Account do it { is_expected.to validate_length_of(:display_name).is_at_most(described_class::DISPLAY_NAME_LENGTH_LIMIT) } it { is_expected.to_not allow_values(account_note_over_limit).for(:note) } + + it { is_expected.to allow_value(fields_empty_name_value).for(:fields) } + it { is_expected.to_not allow_value(fields_over_limit).for(:fields) } + it { is_expected.to_not allow_value(fields_empty_name).for(:fields) } end context 'when account is remote' do @@ -854,6 +858,18 @@ RSpec.describe Account do def account_note_over_limit 'a' * described_class::NOTE_LENGTH_LIMIT * 2 end + + def fields_empty_name_value + Array.new(4) { { 'name' => '', 'value' => '' } } + end + + def fields_over_limit + Array.new(5) { { 'name' => 'Name', 'value' => 'Value', 'verified_at' => '01/01/1970' } } + end + + def fields_empty_name + [{ 'name' => '', 'value' => 'Value', 'verified_at' => '01/01/1970' }] + end end describe 'scopes' do From 53f8ac09f3c5177329c07266e28a3ba594a39e34 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 14:55:11 +0100 Subject: [PATCH 002/133] New Crowdin Translations (automated) (#2920) * New Crowdin translations * Update no.yml * Update simple_form.no.yml --------- Co-authored-by: GitHub Actions Co-authored-by: Claire --- app/javascript/flavours/glitch/locales/ar.json | 7 ------- app/javascript/flavours/glitch/locales/cs.json | 9 --------- app/javascript/flavours/glitch/locales/de.json | 9 --------- app/javascript/flavours/glitch/locales/eo.json | 9 --------- app/javascript/flavours/glitch/locales/es-AR.json | 9 --------- app/javascript/flavours/glitch/locales/es-MX.json | 9 --------- app/javascript/flavours/glitch/locales/es.json | 9 --------- app/javascript/flavours/glitch/locales/fa.json | 2 -- app/javascript/flavours/glitch/locales/fr-CA.json | 9 --------- app/javascript/flavours/glitch/locales/fr.json | 9 --------- app/javascript/flavours/glitch/locales/id.json | 9 --------- app/javascript/flavours/glitch/locales/ja.json | 8 -------- app/javascript/flavours/glitch/locales/ko.json | 9 --------- app/javascript/flavours/glitch/locales/nl.json | 4 ---- app/javascript/flavours/glitch/locales/pl.json | 9 --------- app/javascript/flavours/glitch/locales/pt-BR.json | 9 --------- app/javascript/flavours/glitch/locales/pt-PT.json | 6 ------ app/javascript/flavours/glitch/locales/ru.json | 9 --------- app/javascript/flavours/glitch/locales/uk.json | 9 --------- app/javascript/flavours/glitch/locales/zh-CN.json | 9 --------- app/javascript/flavours/glitch/locales/zh-TW.json | 9 --------- 21 files changed, 171 deletions(-) diff --git a/app/javascript/flavours/glitch/locales/ar.json b/app/javascript/flavours/glitch/locales/ar.json index 7b86cb93ac..588ffc4633 100644 --- a/app/javascript/flavours/glitch/locales/ar.json +++ b/app/javascript/flavours/glitch/locales/ar.json @@ -41,13 +41,6 @@ "navigation_bar.app_settings": "إعدادات التطبيق", "navigation_bar.keyboard_shortcuts": "اختصارات لوحة المفاتيح", "navigation_bar.misc": "متنوع", - "notification.markForDeletion": "وضع علامة للحذف", - "notification_purge.btn_all": "تحديد الكل", - "notification_purge.btn_apply": "مسح المحدد", - "notification_purge.btn_invert": "عكس التحديد", - "notification_purge.btn_none": "إزالة التحديد", - "notifications.marked_clear": "مسح الإشعارات المحددة", - "notifications.marked_clear_confirmation": "هل أنت متأكد من أنك تريد مسح جميع الإشعارات المحددة نهائياً؟", "settings.always_show_spoilers_field": "تمكين دائما حقل تحذير المحتوى", "settings.close": "إغلاق", "settings.content_warnings": "Content warnings", diff --git a/app/javascript/flavours/glitch/locales/cs.json b/app/javascript/flavours/glitch/locales/cs.json index c4983a3b94..9c3cb19bc8 100644 --- a/app/javascript/flavours/glitch/locales/cs.json +++ b/app/javascript/flavours/glitch/locales/cs.json @@ -13,13 +13,6 @@ "navigation_bar.app_settings": "Nastavení aplikace", "navigation_bar.keyboard_shortcuts": "Klávesové zkratky", "navigation_bar.misc": "Různé", - "notification.markForDeletion": "Označit pro smazání", - "notification_purge.btn_all": "Vybrat\nvše", - "notification_purge.btn_apply": "Smazat\nvybrané", - "notification_purge.btn_invert": "Obrátit\nvýběr", - "notification_purge.btn_none": "Nevybrat\nnic", - "notifications.marked_clear": "Smazat vybraná oznámení", - "notifications.marked_clear_confirmation": "Určitě chcete trvale smazat všechna vybraná oznámení?", "settings.always_show_spoilers_field": "Vždy zobrazit pole pro varování o obsahu", "settings.close": "Zavřít", "settings.compose_box_opts": "Editační pole", @@ -29,8 +22,6 @@ "settings.content_warnings": "Varování o obsahu", "settings.content_warnings.regexp": "Regulární výraz", "settings.content_warnings_filter": "Tato varování o obsahu automaticky nerozbalovat:", - "settings.content_warnings_media_outside": "Zobrazit obrázky a videa mimo varování o obsahu", - "settings.content_warnings_media_outside_hint": "Obrázky a videa z příspěvku s varováním o obsahu se zobrazí se separátním přepínačem zobrazení, stejně jako na běžném Mastodonu.", "settings.content_warnings_shared_state": "Zobrazit/schovat všechny kopie naráz", "settings.content_warnings_shared_state_hint": "Tlačítko varování o obsahu bude mít efekt na všechny kopie příspěvku naráz, stejně jako na běžném Mastodonu. Nebude pak možné automaticky sbalit jakoukoliv kopii příspěvku, která má rozbalené varování o obsahu", "settings.content_warnings_unfold_opts": "Možnosti automatického rozbalení", diff --git a/app/javascript/flavours/glitch/locales/de.json b/app/javascript/flavours/glitch/locales/de.json index 045a0ab786..a9f0b8deba 100644 --- a/app/javascript/flavours/glitch/locales/de.json +++ b/app/javascript/flavours/glitch/locales/de.json @@ -51,14 +51,7 @@ "navigation_bar.app_settings": "App-Einstellungen", "navigation_bar.keyboard_shortcuts": "Tastaturkürzel", "navigation_bar.misc": "Sonstiges", - "notification.markForDeletion": "Zum Entfernen auswählen", - "notification_purge.btn_all": "Alle\nauswählen", - "notification_purge.btn_apply": "Ausgewählte\nentfernen", - "notification_purge.btn_invert": "Auswahl\numkehren", - "notification_purge.btn_none": "Auswahl\naufheben", "notifications.column_settings.filter_bar.show_bar": "Filterleiste anzeigen", - "notifications.marked_clear": "Ausgewählte Benachrichtigungen entfernen", - "notifications.marked_clear_confirmation": "Möchtest du wirklich alle auswählten Benachrichtigungen für immer entfernen?", "settings.always_show_spoilers_field": "Das Inhaltswarnungs-Feld immer aktivieren", "settings.close": "Schließen", "settings.compose_box_opts": "Verfassen-Box", @@ -68,8 +61,6 @@ "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Regulärer Ausdruck", "settings.content_warnings_filter": "Inhaltswarnungen, die nicht ausgeklappt werden sollen:", - "settings.content_warnings_media_outside": "Medienanhänge außerhalb von Inhaltswarnungen anzeigen", - "settings.content_warnings_media_outside_hint": "Das ursprüngliche Verhalten von Mastodon wiederherstellen, in welchem Inhaltswarnungen keine Auswirkungen auf Anhänge haben", "settings.content_warnings_shared_state": "Inhalt aller Kopien auf einmal ein-/ausblenden", "settings.content_warnings_shared_state_hint": "Das ursprüngliche Verhalten von Mastodon wiederhertstellen, in welchem der Inhaltswarnungs-Knopf alle Kopien eines Posts auf einmal ein-/ausklappt. Das wird das automatische Einklappen jedweder Kopie eines Toots mit ausgeklappter Inhaltswarnung", "settings.content_warnings_unfold_opts": "Optionen zum automatischen Ausklappen", diff --git a/app/javascript/flavours/glitch/locales/eo.json b/app/javascript/flavours/glitch/locales/eo.json index 7cc8a47cd9..d6e21efb7e 100644 --- a/app/javascript/flavours/glitch/locales/eo.json +++ b/app/javascript/flavours/glitch/locales/eo.json @@ -51,14 +51,7 @@ "navigation_bar.app_settings": "Agordoj de aplikaĵo", "navigation_bar.keyboard_shortcuts": "Fulmoklavoj", "navigation_bar.misc": "Aliaj", - "notification.markForDeletion": "Sigeli por forigi", - "notification_purge.btn_all": "Selekti ĉiujn", - "notification_purge.btn_apply": "Forigi selektajn", - "notification_purge.btn_invert": "Inverti selekton", - "notification_purge.btn_none": "Elekti neniun", "notifications.column_settings.filter_bar.show_bar": "Montri mezuron de filtrilo", - "notifications.marked_clear": "Forigi selektajn sciigojn", - "notifications.marked_clear_confirmation": "Ĉu vi certas, ke vi volas forigi tutajn selektitajn sciigojn?", "settings.always_show_spoilers_field": "Ĉiam ŝaltiĝu la arealo de Enhava Averto", "settings.close": "Fermi", "settings.compose_box_opts": "Skatolo por verki", @@ -68,8 +61,6 @@ "settings.content_warnings": "Enhavaj avertoj", "settings.content_warnings.regexp": "Regula esprimo", "settings.content_warnings_filter": "Enhavaj avertoj, kiujn ne aŭtomate malkaŝu:", - "settings.content_warnings_media_outside": "Montri plurmediojn ekstere de enhavaj avertoj", - "settings.content_warnings_media_outside_hint": "Fari same, kiel la originala programaro Mastodon, por ke la enhavaj avertoj ne influas plurmediojn", "settings.content_warnings_shared_state": "Montri/Malmontri enhavon de kopiaĵoj tuje", "settings.content_warnings_shared_state_hint": "Redonu normalan kondukton de Mastodon per havante la butonon por la Averto por la Enhavo rezultas tutajn kopiaĵojn tuje. Tion preventos aŭtomatan kolapsadon de kopiiaĵo de afiŝo ajn, kiu havus malkolapsan EA-on", "settings.content_warnings_unfold_opts": "Aŭtomat-malkolapsintaj agordoj", diff --git a/app/javascript/flavours/glitch/locales/es-AR.json b/app/javascript/flavours/glitch/locales/es-AR.json index 532fde8633..f8c4fb84d6 100644 --- a/app/javascript/flavours/glitch/locales/es-AR.json +++ b/app/javascript/flavours/glitch/locales/es-AR.json @@ -51,14 +51,7 @@ "navigation_bar.app_settings": "Ajustes de aplicación", "navigation_bar.keyboard_shortcuts": "Atajos de teclado", "navigation_bar.misc": "Misc", - "notification.markForDeletion": "Marcar para borrar", - "notification_purge.btn_all": "Seleccionar\ntodo", - "notification_purge.btn_apply": "Borrar\nselección", - "notification_purge.btn_invert": "Invertir\nselección", - "notification_purge.btn_none": "Seleccionar\nnada", "notifications.column_settings.filter_bar.show_bar": "Mostrar barra de filtros", - "notifications.marked_clear": "Limpiar notificaciones seleccionadas", - "notifications.marked_clear_confirmation": "¿Deseas borrar permanentemente todas las notificaciones seleccionadas?", "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido", "settings.close": "Cerrar", "settings.compose_box_opts": "Cuadro de redacción", @@ -68,8 +61,6 @@ "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Regexp (expresión regular)", "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:", - "settings.content_warnings_media_outside": "Mostrar archivos adjuntos fuera de las advertencias de contenido", - "settings.content_warnings_media_outside_hint": "Reproduce el comportamiento normal de Mastodon teniendo al tener el interruptor de advertencia de contenido activado, no afectando los archivos adjuntos", "settings.content_warnings_shared_state": "Mostrar/ocultar el contenido de todas las copias a la vez", "settings.content_warnings_shared_state_hint": "Reproduce el comportamiento normal de Mastodon al hacer que el botón Advertencia de contenido afecte a todas las copias de un mensaje a la vez. Esto evitará el colapso automático de cualquier copia de un toot con CW desplegado", "settings.content_warnings_unfold_opts": "Opciones de Auto-desplegado", diff --git a/app/javascript/flavours/glitch/locales/es-MX.json b/app/javascript/flavours/glitch/locales/es-MX.json index a836ac05b3..592a443fd6 100644 --- a/app/javascript/flavours/glitch/locales/es-MX.json +++ b/app/javascript/flavours/glitch/locales/es-MX.json @@ -47,13 +47,6 @@ "navigation_bar.app_settings": "Ajustes de aplicación", "navigation_bar.keyboard_shortcuts": "Atajos de teclado", "navigation_bar.misc": "Misc", - "notification.markForDeletion": "Marcar para borrar", - "notification_purge.btn_all": "Seleccionar\ntodo", - "notification_purge.btn_apply": "Borrar\nselección", - "notification_purge.btn_invert": "Invertir\nselección", - "notification_purge.btn_none": "Seleccionar\nnada", - "notifications.marked_clear": "Limpiar notificaciones seleccionadas", - "notifications.marked_clear_confirmation": "¿Deseas borrar permanentemente todas las notificaciones seleccionadas?", "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido", "settings.close": "Cerrar", "settings.compose_box_opts": "Cuadro de redacción", @@ -63,8 +56,6 @@ "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Regexp (expresión regular)", "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:", - "settings.content_warnings_media_outside": "Mostrar archivos adjuntos fuera de las advertencias de contenido", - "settings.content_warnings_media_outside_hint": "Reproduce el comportamiento normal de Mastodon teniendo al tener el interruptor de advertencia de contenido activado, no afectando los archivos adjuntos", "settings.content_warnings_shared_state": "Mostrar/ocultar el contenido de todas las copias a la vez", "settings.content_warnings_shared_state_hint": "Reproduce el comportamiento normal de Mastodon al hacer que el botón Advertencia de contenido afecte a todas las copias de un mensaje a la vez. Esto evitará el colapso automático de cualquier copia de un toot con CW desplegado", "settings.content_warnings_unfold_opts": "Opciones de Auto-desplegado", diff --git a/app/javascript/flavours/glitch/locales/es.json b/app/javascript/flavours/glitch/locales/es.json index fce26ebdf5..48a74070de 100644 --- a/app/javascript/flavours/glitch/locales/es.json +++ b/app/javascript/flavours/glitch/locales/es.json @@ -47,13 +47,6 @@ "navigation_bar.app_settings": "Ajustes de la aplicación", "navigation_bar.keyboard_shortcuts": "Atajos de teclado", "navigation_bar.misc": "Misc", - "notification.markForDeletion": "Marcar para borrado", - "notification_purge.btn_all": "Seleccionar\ntodo", - "notification_purge.btn_apply": "Borrar\nselección", - "notification_purge.btn_invert": "Invertir\nselección", - "notification_purge.btn_none": "Seleccionar\nninguno", - "notifications.marked_clear": "Limpiar las notificaciones seleccionadas", - "notifications.marked_clear_confirmation": "¿Estás seguro de borrar permanentemente todas las notificaciones seleccionadas?", "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido", "settings.close": "Cerrar", "settings.compose_box_opts": "Cuadro de redacción", @@ -63,8 +56,6 @@ "settings.content_warnings": "Advertencias de contenido", "settings.content_warnings.regexp": "Regexp (expresión regular)", "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:", - "settings.content_warnings_media_outside": "Mostrar archivos adjuntos fuera de las advertencias de contenido", - "settings.content_warnings_media_outside_hint": "Reproduce el comportamiento normal de Mastodon teniendo al tener el interruptor de advertencia de contenido activado, no afectando los archivos adjuntos", "settings.content_warnings_shared_state": "Mostrar/ocultar el contenido de todas las copias a la vez", "settings.content_warnings_shared_state_hint": "Reproduce el comportamiento normal de Mastodon al hacer que el botón Advertencia de contenido afecte a todas las copias de un mensaje a la vez. Esto evitará el colapso automático de cualquier copia de un toot con CW desplegado", "settings.content_warnings_unfold_opts": "Opciones de Auto-desplegado", diff --git a/app/javascript/flavours/glitch/locales/fa.json b/app/javascript/flavours/glitch/locales/fa.json index abc4e18cea..1591685a7e 100644 --- a/app/javascript/flavours/glitch/locales/fa.json +++ b/app/javascript/flavours/glitch/locales/fa.json @@ -10,8 +10,6 @@ "home.column_settings.advanced": "پیشرفته", "navigation_bar.app_settings": "تنظیمات کاره", "navigation_bar.keyboard_shortcuts": "میان‌برهای صفحه‌کلید", - "notification.markForDeletion": "علامت‌گذاری برای حذف", - "notification_purge.btn_all": "انتخاب همه", "settings.close": "بستن", "settings.content_warnings": "هشدارهای محتوا", "settings.media": "رسانه", diff --git a/app/javascript/flavours/glitch/locales/fr-CA.json b/app/javascript/flavours/glitch/locales/fr-CA.json index 1ebfff4841..e860431427 100644 --- a/app/javascript/flavours/glitch/locales/fr-CA.json +++ b/app/javascript/flavours/glitch/locales/fr-CA.json @@ -47,13 +47,6 @@ "navigation_bar.app_settings": "Paramètres de l'application", "navigation_bar.keyboard_shortcuts": "Raccourcis clavier", "navigation_bar.misc": "Autres", - "notification.markForDeletion": "Ajouter aux éléments à supprimer", - "notification_purge.btn_all": "Sélectionner\ntout", - "notification_purge.btn_apply": "Effacer\nla sélection", - "notification_purge.btn_invert": "Inverser\nla sélection", - "notification_purge.btn_none": "Annuler\nla sélection", - "notifications.marked_clear": "Effacer les notifications sélectionnées", - "notifications.marked_clear_confirmation": "Voulez-vous vraiment effacer de manière permanente toutes les notifications sélectionnées ?", "settings.always_show_spoilers_field": "Toujours activer le champ de rédaction de l'avertissement de contenu", "settings.close": "Fermer", "settings.compose_box_opts": "Zone de rédaction", @@ -63,8 +56,6 @@ "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Expression rationnelle", "settings.content_warnings_filter": "Avertissement de contenu à ne pas automatiquement déplier :", - "settings.content_warnings_media_outside": "Afficher les médias en dehors des avertissements de contenu", - "settings.content_warnings_media_outside_hint": "Reproduit le comportement par défaut de Mastodon, les médias attachés ne sont plus affectés par le bouton d'affichage d'un post avec avertissement", "settings.content_warnings_shared_state": "Affiche/cache le contenu de toutes les copies à la fois", "settings.content_warnings_shared_state_hint": "Reproduit le comportement par défaut de Mastodon, le bouton d'avertissement de contenu affecte toutes les copies d'un post à la fois. Cela empêchera le repliement automatique de n'importe quelle copie d'un post avec un avertissement déplié", "settings.content_warnings_unfold_opts": "Options de dépliement automatique", diff --git a/app/javascript/flavours/glitch/locales/fr.json b/app/javascript/flavours/glitch/locales/fr.json index 4129bde5ae..40b41ecc43 100644 --- a/app/javascript/flavours/glitch/locales/fr.json +++ b/app/javascript/flavours/glitch/locales/fr.json @@ -47,13 +47,6 @@ "navigation_bar.app_settings": "Paramètres de l'application", "navigation_bar.keyboard_shortcuts": "Raccourcis clavier", "navigation_bar.misc": "Autres", - "notification.markForDeletion": "Ajouter aux éléments à supprimer", - "notification_purge.btn_all": "Sélectionner\ntout", - "notification_purge.btn_apply": "Effacer\nla sélection", - "notification_purge.btn_invert": "Inverser\nla sélection", - "notification_purge.btn_none": "Annuler\nla sélection", - "notifications.marked_clear": "Effacer les notifications sélectionnées", - "notifications.marked_clear_confirmation": "Voulez-vous vraiment effacer de manière permanente toutes les notifications sélectionnées ?", "settings.always_show_spoilers_field": "Toujours activer le champ de rédaction de l'avertissement de contenu", "settings.close": "Fermer", "settings.compose_box_opts": "Zone de rédaction", @@ -63,8 +56,6 @@ "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Expression rationnelle", "settings.content_warnings_filter": "Avertissement de contenu à ne pas automatiquement déplier :", - "settings.content_warnings_media_outside": "Afficher les médias en dehors des avertissements de contenu", - "settings.content_warnings_media_outside_hint": "Reproduit le comportement par défaut de Mastodon, les médias attachés ne sont plus affectés par le bouton d'affichage d'un post avec avertissement", "settings.content_warnings_shared_state": "Affiche/cache le contenu de toutes les copies à la fois", "settings.content_warnings_shared_state_hint": "Reproduit le comportement par défaut de Mastodon, le bouton d'avertissement de contenu affecte toutes les copies d'un post à la fois. Cela empêchera le repliement automatique de n'importe quelle copie d'un post avec un avertissement déplié", "settings.content_warnings_unfold_opts": "Options de dépliement automatique", diff --git a/app/javascript/flavours/glitch/locales/id.json b/app/javascript/flavours/glitch/locales/id.json index 1ed8ec6a9a..55c17cf38d 100644 --- a/app/javascript/flavours/glitch/locales/id.json +++ b/app/javascript/flavours/glitch/locales/id.json @@ -43,13 +43,6 @@ "navigation_bar.app_settings": "Pengaturan aplikasi", "navigation_bar.keyboard_shortcuts": "Pintasan keyboard", "navigation_bar.misc": "Lainnya", - "notification.markForDeletion": "Tandai untuk dihapus", - "notification_purge.btn_all": "Pilih semua", - "notification_purge.btn_apply": "Hapus yang\ndipilih", - "notification_purge.btn_invert": "Balikkan\npilihan", - "notification_purge.btn_none": "Pilih tidak\nsatu pun", - "notifications.marked_clear": "Hapus notifikasi yang dipilih", - "notifications.marked_clear_confirmation": "Apakah Anda yakin ingin menghapus secara permanen semua notifikasi yang dipilih?", "settings.always_show_spoilers_field": "Selalu aktifkan bidang Peringatan Konten", "settings.close": "Tutup", "settings.compose_box_opts": "Kotak tulis", @@ -59,8 +52,6 @@ "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Ekspresi reguler", "settings.content_warnings_filter": "Peringatan konten yang tidak akan dibuka secara otomatis:", - "settings.content_warnings_media_outside": "Tampilkan lampiran media di luar peringatan konten", - "settings.content_warnings_media_outside_hint": "Reproduksi perilaku Mastodon upstream dengan membuat tombol Peringatan Konten tidak memengaruhi lampiran media", "settings.content_warnings_shared_state": "Tampilkan/sembunyikan konten semua salinan sekaligus", "settings.preferences": "Preferences", "settings.status_icons_reply": "Indikator balasan", diff --git a/app/javascript/flavours/glitch/locales/ja.json b/app/javascript/flavours/glitch/locales/ja.json index 3b3c369700..84ba894467 100644 --- a/app/javascript/flavours/glitch/locales/ja.json +++ b/app/javascript/flavours/glitch/locales/ja.json @@ -39,13 +39,6 @@ "navigation_bar.app_settings": "アプリ設定", "navigation_bar.keyboard_shortcuts": "キーボードショートカット", "navigation_bar.misc": "その他", - "notification.markForDeletion": "選択", - "notification_purge.btn_all": "すべて\n選択", - "notification_purge.btn_apply": "選択したものを\n削除", - "notification_purge.btn_invert": "選択を\n反転", - "notification_purge.btn_none": "選択\n解除", - "notifications.marked_clear": "選択した通知を削除する", - "notifications.marked_clear_confirmation": "削除した全ての通知を完全に削除してもよろしいですか?", "settings.always_show_spoilers_field": "常にコンテンツワーニング設定を表示する(指定がない場合は通常投稿)", "settings.close": "閉じる", "settings.compose_box_opts": "コンポーズボックス設定", @@ -55,7 +48,6 @@ "settings.content_warnings": "コンテンツワーニング", "settings.content_warnings.regexp": "正規表現", "settings.content_warnings_filter": "説明に指定した文字が含まれているものを自動で展開しないようにする", - "settings.content_warnings_media_outside": "コンテンツワーニングの外側にメディア添付ファイルを表示する", "settings.content_warnings_shared_state": "すべてのコピーの内容を一度に表示/非表示", "settings.content_warnings_unfold_opts": "自動展開オプション", "settings.enable_content_warnings_auto_unfold": "コンテンツワーニング指定されている投稿を常に表示する", diff --git a/app/javascript/flavours/glitch/locales/ko.json b/app/javascript/flavours/glitch/locales/ko.json index 70a0f68bc7..23b5138cf6 100644 --- a/app/javascript/flavours/glitch/locales/ko.json +++ b/app/javascript/flavours/glitch/locales/ko.json @@ -51,14 +51,7 @@ "navigation_bar.app_settings": "앱 설정", "navigation_bar.keyboard_shortcuts": "키보드 단축기", "navigation_bar.misc": "다양한 옵션들", - "notification.markForDeletion": "삭제하기 위해 표시", - "notification_purge.btn_all": "전체선택", - "notification_purge.btn_apply": "선택된 알림 삭제", - "notification_purge.btn_invert": "선택반전", - "notification_purge.btn_none": "전체선택해제", "notifications.column_settings.filter_bar.show_bar": "필터 막대 표시", - "notifications.marked_clear": "선택된 알림 모두 삭제", - "notifications.marked_clear_confirmation": "정말로 선택된 알림들을 영구적으로 삭제할까요?", "settings.always_show_spoilers_field": "열람주의 항목을 언제나 활성화", "settings.close": "닫기", "settings.compose_box_opts": "작성 상자", @@ -68,8 +61,6 @@ "settings.content_warnings": "열람주의", "settings.content_warnings.regexp": "정규표현식", "settings.content_warnings_filter": "자동으로 펼치지 않을 열람주의 문구:", - "settings.content_warnings_media_outside": "미디어 첨부를 열람주의 바깥에 보이기", - "settings.content_warnings_media_outside_hint": "마스토돈 원본처럼 열람주의 토글이 미디어 첨부에는 영향을 미치지 않게 합니다", "settings.content_warnings_shared_state": "동일한 글의 열람주의를 한번에 열고 닫기", "settings.content_warnings_shared_state_hint": "마스토돈 원본처럼 열람주의 버튼이 동일한 모든 글에 대해 영향을 미치게 합니다. 펼쳐진 열람주의 글이 자동으로 다시 접히는 것을 방지합니다", "settings.content_warnings_unfold_opts": "자동 펼치기 옵션", diff --git a/app/javascript/flavours/glitch/locales/nl.json b/app/javascript/flavours/glitch/locales/nl.json index 186d64b5d1..540f315e17 100644 --- a/app/javascript/flavours/glitch/locales/nl.json +++ b/app/javascript/flavours/glitch/locales/nl.json @@ -20,10 +20,6 @@ "confirmations.missing_media_description.message": "Minstens één media-bijlage mist een beschrijving. Overweeg om alle mediabijlagen voor slechtzienden te beschrijven voordat u uw toot verstuurt.", "direct.group_by_conversations": "Groeperen op gesprek", "home.column_settings.advanced": "Geavanceerd", - "notification_purge.btn_all": "Alles selecteren", - "notification_purge.btn_invert": "Selectie omkeren", - "notifications.marked_clear": "Wis geselecteerde meldingen", - "notifications.marked_clear_confirmation": "Weet je zeker dat je alle geselecteerde meldingen permanent wilt wissen?", "settings.close": "Sluiten", "settings.content_warnings": "Content warnings", "settings.general": "Algemeen", diff --git a/app/javascript/flavours/glitch/locales/pl.json b/app/javascript/flavours/glitch/locales/pl.json index 89a0a3be3a..eef3ceedd8 100644 --- a/app/javascript/flavours/glitch/locales/pl.json +++ b/app/javascript/flavours/glitch/locales/pl.json @@ -33,13 +33,6 @@ "navigation_bar.app_settings": "Ustawienia aplikacji", "navigation_bar.keyboard_shortcuts": "Skróty klawiszowe", "navigation_bar.misc": "Różne", - "notification.markForDeletion": "Oznacz do usunięcia", - "notification_purge.btn_all": "Zaznacz\nwszystkie", - "notification_purge.btn_apply": "Usuń\nzaznaczone", - "notification_purge.btn_invert": "Odwróć\nzaznaczenie", - "notification_purge.btn_none": "Odznacz\nwszystkie", - "notifications.marked_clear": "Usuń zaznaczone powiadomienia", - "notifications.marked_clear_confirmation": "Czy na pewno chcesz bezpowrtonie usunąć wszystkie powiadomienia?", "settings.always_show_spoilers_field": "Zawsze pokazuj pole ostrzeżenia o zawartości", "settings.close": "Zamknij", "settings.compose_box_opts": "Pole edycji", @@ -49,8 +42,6 @@ "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Wyrażenie regularne", "settings.content_warnings_filter": "Ostrzeżenia o zawartości nieodkrywane automatycznie:", - "settings.content_warnings_media_outside": "Wyświetlaj załączniki multimedialne poza ostrzeżeniem o zawartości", - "settings.content_warnings_media_outside_hint": "Nie ukrywaj załączników multimedialnych, gdy wpis jest ukryty za ostrzeżeniem, tak jak robi to niezmodyfikowany Mastodon", "settings.content_warnings_shared_state": "Pokaż/ukryj zawartość wszystkich kopii jednocześnie", "settings.content_warnings_shared_state_hint": "Zachowaj się tak, jak niezmodyfikowany Mastodon, tj. wymuś działanie przycisku ostrzeżenia o zawartości na wszystkie kopie danego wpisu. Włączenie tego ustawienia spowoduje wyłączenie automatycznego zwijania kopii wpisów z odkrytym ostrzeżeniem o zawartości.", "settings.content_warnings_unfold_opts": "Opcje automatycznego odkrywania", diff --git a/app/javascript/flavours/glitch/locales/pt-BR.json b/app/javascript/flavours/glitch/locales/pt-BR.json index aeb23ef3cb..41c725c803 100644 --- a/app/javascript/flavours/glitch/locales/pt-BR.json +++ b/app/javascript/flavours/glitch/locales/pt-BR.json @@ -33,13 +33,6 @@ "navigation_bar.app_settings": "Configurações do aplicativo", "navigation_bar.keyboard_shortcuts": "Atalhos de teclado", "navigation_bar.misc": "Diversos", - "notification.markForDeletion": "Marcar para exclusão", - "notification_purge.btn_all": "Selecionar\ntudo", - "notification_purge.btn_apply": "Limpar\nselecionados", - "notification_purge.btn_invert": "Inverter\nseleção", - "notification_purge.btn_none": "Selecionar\nnenhum", - "notifications.marked_clear": "Limpar as notificações selecionadas", - "notifications.marked_clear_confirmation": "Tem certeza que deseja limpar todas as notificações selecionadas permanentemente?", "settings.always_show_spoilers_field": "Sempre ativar o campo Aviso de Conteúdo", "settings.close": "Fechar", "settings.compose_box_opts": "Caixa de composição", @@ -49,8 +42,6 @@ "settings.content_warnings": "Aviso de Conteúdo", "settings.content_warnings.regexp": "Expressão regular", "settings.content_warnings_filter": "Avisos de conteúdo para não revelar automaticamente:", - "settings.content_warnings_media_outside": "Exibir anexos de mídia fora avisos de conteúdo", - "settings.content_warnings_media_outside_hint": "Reproduzir o comportamento do Mastodonte, fazendo com que a alternância do Aviso de Conteúdo não afete os anexos de mídia", "settings.content_warnings_shared_state": "Mostrar/ocultar o conteúdo de todas as cópias de uma só vez", "settings.content_warnings_shared_state_hint": "Reproduzir o comportamento do Mastodonte fazendo com que o botão de Aviso de Conteúdo afete todas as cópias de um post de uma só vez. Isto evitará o colapso automático de qualquer cópia de um toon com Aviso de Conteúdo revelado", "settings.content_warnings_unfold_opts": "Opções de auto-revelar", diff --git a/app/javascript/flavours/glitch/locales/pt-PT.json b/app/javascript/flavours/glitch/locales/pt-PT.json index 2a0766b6d2..9748926543 100644 --- a/app/javascript/flavours/glitch/locales/pt-PT.json +++ b/app/javascript/flavours/glitch/locales/pt-PT.json @@ -8,13 +8,7 @@ "column.favourited_by": "Adicionado aos favoritos de", "column.heading": "Diversos", "moved_to_warning": "Esta conta mudou-se para {moved_to_link} e, portanto, pode não aceitar novos seguidores.", - "notification.markForDeletion": "Marcada para eliminação", - "notification_purge.btn_all": "Seleccionar tudo", - "notification_purge.btn_apply": "Limpar Selecionadas", - "notification_purge.btn_none": "Desselecionar tudo", "notifications.column_settings.filter_bar.show_bar": "Mostrar barra de filtros", - "notifications.marked_clear": "Limpar as notificações selecionadas", - "notifications.marked_clear_confirmation": "Tem a certeza que deseja limpar todas as notificações selecionadas permanentemente?", "settings.always_show_spoilers_field": "Mostrar sempre o campo Aviso de Conteúdo", "settings.content_warnings": "Content warnings", "settings.layout_opts": "Disposição do conteúdo", diff --git a/app/javascript/flavours/glitch/locales/ru.json b/app/javascript/flavours/glitch/locales/ru.json index 1ae3d9be27..a6acd5621a 100644 --- a/app/javascript/flavours/glitch/locales/ru.json +++ b/app/javascript/flavours/glitch/locales/ru.json @@ -41,22 +41,13 @@ "moved_to_warning": "Этот аккаунт переехал на {moved_to_link}, и скорее всего не принимает новых подписчиков.", "navigation_bar.app_settings": "Настройки приложения", "navigation_bar.keyboard_shortcuts": "Сочетания клавиш", - "notification.markForDeletion": "Отметить для удаления", - "notification_purge.btn_all": "Выбрать все", - "notification_purge.btn_apply": "Удалить выбранное", - "notification_purge.btn_invert": "Инвертировать выбор", - "notification_purge.btn_none": "Отменить выбор", "notifications.column_settings.filter_bar.show_bar": "Показать панель фильтров", - "notifications.marked_clear": "Удалить выбранные уведомления", - "notifications.marked_clear_confirmation": "Вы уверены, что хотите безвозвратно удалить все выбранные уведомления?", "settings.always_show_spoilers_field": "Всегда ставить предупреждение о содержании", "settings.close": "Закрыть", "settings.compose_box_opts": "Форма постинга", "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Регулярное выражение", "settings.content_warnings_filter": "Предупреждения о содержании, к которым автоматическое разворачивание не применяется:", - "settings.content_warnings_media_outside": "Показывать медиафайлы внизу под предупреждением о содержании", - "settings.content_warnings_media_outside_hint": "Воспроизводить исходное поведение Mastodon, когда предупреждение о содержании сворачивает только текст поста", "settings.content_warnings_shared_state": "Показывать/скрывать содержимое всех копий вместе", "settings.content_warnings_shared_state_hint": "Воспроизводить исходное поведение Mastodon, когда раскрывание и закрывание предупреждения о содержимом охватывает все копии поста сразу. Это предотвращает автоматическое сворачивание копий поста с уже однажды раскрытым CW", "settings.content_warnings_unfold_opts": "Автоматическое раскрытие", diff --git a/app/javascript/flavours/glitch/locales/uk.json b/app/javascript/flavours/glitch/locales/uk.json index 8c94dac050..72becb9445 100644 --- a/app/javascript/flavours/glitch/locales/uk.json +++ b/app/javascript/flavours/glitch/locales/uk.json @@ -34,13 +34,6 @@ "navigation_bar.app_settings": "Налаштування програми", "navigation_bar.keyboard_shortcuts": "Комбінації клавіш", "navigation_bar.misc": "Різне", - "notification.markForDeletion": "Позначити для видалення", - "notification_purge.btn_all": "Вибрати\nвсе", - "notification_purge.btn_apply": "Очистити\nвибір", - "notification_purge.btn_invert": "Інвертувати\nвибір", - "notification_purge.btn_none": "Вибрати\nнічого", - "notifications.marked_clear": "Очистити вибрані сповіщення", - "notifications.marked_clear_confirmation": "Ви впевнені, що хочете незворотньо очистити всі вибрані сповіщення?", "settings.always_show_spoilers_field": "Завжди вмикати попередження про вміст", "settings.close": "Закрити", "settings.compose_box_opts": "Compose box", @@ -50,8 +43,6 @@ "settings.content_warnings": "Content warnings", "settings.content_warnings.regexp": "Регулярний вираз", "settings.content_warnings_filter": "Застереження щодо автоматичного розгортання вмісту:", - "settings.content_warnings_media_outside": "Відображати вкладені медіафайли поза попередженнями про вміст", - "settings.content_warnings_media_outside_hint": "Відтворити поведінку Mastodon перед початком роботи, встановивши перемикач попередження про вміст, щоб не впливати на вкладені файли мультимедіа", "settings.content_warnings_shared_state": "Показати/приховати вміст усіх копій одночасно", "settings.content_warnings_shared_state_hint": "Відтворити поведінку Mastodon, що передує, за допомогою кнопки попередження про вміст, яка впливає на всі копії допису одразу. Це запобігатиме автоматичному згортанню будь-якої копії допису з розгорнутим CW", "settings.content_warnings_unfold_opts": "Параметри автоматичного розгортання", diff --git a/app/javascript/flavours/glitch/locales/zh-CN.json b/app/javascript/flavours/glitch/locales/zh-CN.json index 3638d1ee20..139353d1ff 100644 --- a/app/javascript/flavours/glitch/locales/zh-CN.json +++ b/app/javascript/flavours/glitch/locales/zh-CN.json @@ -51,14 +51,7 @@ "navigation_bar.app_settings": "应用设置", "navigation_bar.keyboard_shortcuts": "键盘快捷键", "navigation_bar.misc": "杂项", - "notification.markForDeletion": "标记已删除", - "notification_purge.btn_all": "全选", - "notification_purge.btn_apply": "清除已选", - "notification_purge.btn_invert": "反选", - "notification_purge.btn_none": "取消全选", "notifications.column_settings.filter_bar.show_bar": "显示筛选栏", - "notifications.marked_clear": "清除选择的通知", - "notifications.marked_clear_confirmation": "你确定要永久清除所有选择的通知吗?", "settings.always_show_spoilers_field": "始终显示内容警告框", "settings.close": "关闭", "settings.compose_box_opts": "撰写框", @@ -68,8 +61,6 @@ "settings.content_warnings": "内容警告", "settings.content_warnings.regexp": "正则表达式", "settings.content_warnings_filter": "不会自动展开的内容警告:", - "settings.content_warnings_media_outside": "在内容警告外显示媒体附件", - "settings.content_warnings_media_outside_hint": "通过让内容警告开关不影响媒体附件来复制上游Mastodon行为", "settings.content_warnings_shared_state": "一次显示/隐藏所有副本的内容", "settings.content_warnings_shared_state_hint": "通过让内容警告按钮同时影响所有帖子的副本来重现上游Mastodon行为。这将防止任何展开内容警告的嘟文自动折叠。", "settings.content_warnings_unfold_opts": "自动展开设置项", diff --git a/app/javascript/flavours/glitch/locales/zh-TW.json b/app/javascript/flavours/glitch/locales/zh-TW.json index 8afbd0ddd7..7b87883539 100644 --- a/app/javascript/flavours/glitch/locales/zh-TW.json +++ b/app/javascript/flavours/glitch/locales/zh-TW.json @@ -47,14 +47,7 @@ "navigation_bar.app_settings": "應用程式設定", "navigation_bar.keyboard_shortcuts": "鍵盤快速鍵", "navigation_bar.misc": "雜項", - "notification.markForDeletion": "標記刪除", - "notification_purge.btn_all": "選取全部", - "notification_purge.btn_apply": "清除所選項目", - "notification_purge.btn_invert": "反向選擇", - "notification_purge.btn_none": "取消選取", "notifications.column_settings.filter_bar.show_bar": "顯示過濾器", - "notifications.marked_clear": "清除被選取的通知訊息", - "notifications.marked_clear_confirmation": "您確定要永久清除所有被選取的通知訊息嗎?", "settings.always_show_spoilers_field": "永遠啟用內容警告欄位", "settings.close": "關閉", "settings.compose_box_opts": "貼文撰寫框", @@ -64,8 +57,6 @@ "settings.content_warnings": "內容警告", "settings.content_warnings.regexp": "正規表達式", "settings.content_warnings_filter": "不要自動展開內容警告:", - "settings.content_warnings_media_outside": "在內容警告外顯示媒體檔案", - "settings.content_warnings_media_outside_hint": "透過內容警告切換不影響媒體檔案來重現上游 Mastodon 行為", "settings.content_warnings_shared_state": "一次顯示/隱藏所有副本的內容", "settings.content_warnings_shared_state_hint": "透過內容警告按鈕同時影響貼文的所有副本來重現上游 Mastodon 行為。 這將防止任何帶有展開的內容警告的貼文副本自動折疊", "settings.content_warnings_unfold_opts": "自動展開選項", From f16fbc069a7d07f2b60a79ba624ee452951457e5 Mon Sep 17 00:00:00 2001 From: zunda Date: Sun, 5 Jan 2025 18:47:10 -1000 Subject: [PATCH 003/133] Use configured limit for number of profile fields (#33463) --- spec/models/account_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 5ba39848b7..967809b8e1 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -864,7 +864,7 @@ RSpec.describe Account do end def fields_over_limit - Array.new(5) { { 'name' => 'Name', 'value' => 'Value', 'verified_at' => '01/01/1970' } } + Array.new(described_class::DEFAULT_FIELDS_SIZE + 1) { { 'name' => 'Name', 'value' => 'Value', 'verified_at' => '01/01/1970' } } end def fields_empty_name From 081244f692c495722f48272279795f576d6c55b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 08:55:37 +0100 Subject: [PATCH 004/133] Update dependency sass to v1.83.1 (#33453) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8f948e7609..7014e88c49 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15628,8 +15628,8 @@ __metadata: linkType: hard "sass@npm:^1.62.1": - version: 1.83.0 - resolution: "sass@npm:1.83.0" + version: 1.83.1 + resolution: "sass@npm:1.83.1" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" @@ -15640,7 +15640,7 @@ __metadata: optional: true bin: sass: sass.js - checksum: 10c0/4415361229879a9041d77c953da85482e89032aa4321ba13250a9987d39c80fac6c88af3777f2a2d76a4e8b0c8afbd21c1970fdbe84e0b3ec25fb26741f92beb + checksum: 10c0/9772506cd8290df7b5e800055098e91a8a65100840fd9e90c660deb74b248b3ddbbd1a274b8f7f09777d472d2c873575357bd87939a40fb5a80bdf654985486f languageName: node linkType: hard From c0f5705c34fdd6bba6e9351c317ca07bad19fa71 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 08:01:22 +0000 Subject: [PATCH 005/133] Update dependency uuid to v11.0.4 (#33464) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7014e88c49..8278a45d40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17848,11 +17848,11 @@ __metadata: linkType: hard "uuid@npm:^11.0.0": - version: 11.0.3 - resolution: "uuid@npm:11.0.3" + version: 11.0.4 + resolution: "uuid@npm:11.0.4" bin: uuid: dist/esm/bin/uuid - checksum: 10c0/cee762fc76d949a2ff9205770334699e0043d52bb766472593a25f150077c9deed821230251ea3d6ab3943a5ea137d2826678797f1d5f6754c7ce5ce27e9f7a6 + checksum: 10c0/3c13591c4dedaa3741f925e284df5974e3d6e0b1cb0f6f75f98c36f9c01d2a414350364fd067613ef600a21c6973dab0506530d4f499ff878f32a06f84569ead languageName: node linkType: hard From c93c1f38b73fcfa2ed68daf5a6bba36ec93237c4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:13:39 +0100 Subject: [PATCH 006/133] New Crowdin Translations (automated) (#33451) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/az.json | 4 +- app/javascript/mastodon/locales/bg.json | 2 + app/javascript/mastodon/locales/ca.json | 2 + app/javascript/mastodon/locales/cs.json | 95 ++++++++++++++++++++++ app/javascript/mastodon/locales/da.json | 1 + app/javascript/mastodon/locales/de.json | 1 + app/javascript/mastodon/locales/el.json | 2 +- app/javascript/mastodon/locales/eo.json | 1 + app/javascript/mastodon/locales/es-AR.json | 1 + app/javascript/mastodon/locales/es-MX.json | 1 + app/javascript/mastodon/locales/es.json | 1 + app/javascript/mastodon/locales/fi.json | 2 + app/javascript/mastodon/locales/fil.json | 10 +++ app/javascript/mastodon/locales/fo.json | 1 + app/javascript/mastodon/locales/fr-CA.json | 1 + app/javascript/mastodon/locales/fr.json | 1 + app/javascript/mastodon/locales/gl.json | 2 + app/javascript/mastodon/locales/he.json | 2 + app/javascript/mastodon/locales/hu.json | 2 +- app/javascript/mastodon/locales/is.json | 2 + app/javascript/mastodon/locales/ko.json | 1 + app/javascript/mastodon/locales/la.json | 65 +++++++++------ app/javascript/mastodon/locales/lt.json | 1 + app/javascript/mastodon/locales/lv.json | 13 ++- app/javascript/mastodon/locales/nl.json | 1 + app/javascript/mastodon/locales/nn.json | 1 + app/javascript/mastodon/locales/sk.json | 3 + app/javascript/mastodon/locales/sq.json | 1 + app/javascript/mastodon/locales/th.json | 12 +++ app/javascript/mastodon/locales/tr.json | 1 + app/javascript/mastodon/locales/uk.json | 38 ++++++++- app/javascript/mastodon/locales/vi.json | 2 + app/javascript/mastodon/locales/zh-CN.json | 3 +- app/javascript/mastodon/locales/zh-TW.json | 1 + config/locales/activerecord.bg.yml | 2 + config/locales/activerecord.ca.yml | 2 + config/locales/activerecord.da.yml | 2 + config/locales/activerecord.eo.yml | 2 + config/locales/activerecord.es-AR.yml | 2 + config/locales/activerecord.es-MX.yml | 2 + config/locales/activerecord.es.yml | 2 + config/locales/activerecord.fi.yml | 2 + config/locales/activerecord.fil.yml | 8 ++ config/locales/activerecord.fo.yml | 2 + config/locales/activerecord.fr-CA.yml | 2 + config/locales/activerecord.fr.yml | 2 + config/locales/activerecord.he.yml | 2 + config/locales/activerecord.is.yml | 2 + config/locales/activerecord.lt.yml | 2 + config/locales/activerecord.nan.yml | 15 ++++ config/locales/activerecord.nl.yml | 2 + config/locales/activerecord.nn.yml | 2 + config/locales/activerecord.sq.yml | 2 + config/locales/activerecord.sv.yml | 5 ++ config/locales/activerecord.th.yml | 5 ++ config/locales/activerecord.tr.yml | 2 + config/locales/activerecord.uk.yml | 3 + config/locales/activerecord.vi.yml | 2 + config/locales/activerecord.zh-TW.yml | 2 + config/locales/cs.yml | 12 +++ config/locales/devise.nan.yml | 14 ++++ config/locales/doorkeeper.cs.yml | 1 + config/locales/doorkeeper.nan.yml | 16 ++++ config/locales/lv.yml | 6 ++ config/locales/nan.yml | 41 ++++++++++ config/locales/simple_form.cs.yml | 14 ++++ config/locales/simple_form.eo.yml | 10 +++ config/locales/simple_form.nan.yml | 10 +++ config/locales/simple_form.sv.yml | 1 + config/locales/simple_form.uk.yml | 21 ++++- config/locales/sv.yml | 40 +++++++++ config/locales/uk.yml | 66 +++++++++++++-- 72 files changed, 565 insertions(+), 40 deletions(-) diff --git a/app/javascript/mastodon/locales/az.json b/app/javascript/mastodon/locales/az.json index 39801e7b2e..5f1efe3d72 100644 --- a/app/javascript/mastodon/locales/az.json +++ b/app/javascript/mastodon/locales/az.json @@ -240,5 +240,7 @@ "dismissable_banner.community_timeline": "Bunlar, hesabları {domain} serverində yerləşən insanların ən son ictimai paylaşımlarıdır.", "dismissable_banner.dismiss": "Bağla", "dismissable_banner.explore_links": "Bu xəbərlər bu gün fediversedə ən çox paylaşılır. Daha fərqli insanlar tərəfindən dərc edilən daha yeni xəbərlər daha yuxarıda sıralanır.", - "dismissable_banner.explore_statuses": "Fediversedən olan bu paylaşımlar bu gün maraq qazanır. Daha çox gücləndirici və bəyənmə olan daha yeni paylaşımlar daha yuxarıda sıralanır." + "dismissable_banner.explore_statuses": "Fediversedən olan bu paylaşımlar bu gün maraq qazanır. Daha çox gücləndirici və bəyənmə olan daha yeni paylaşımlar daha yuxarıda sıralanır.", + "domain_block_modal.block_account_instead": "@{name} istifadəçisini blokla", + "domain_block_modal.they_can_interact_with_old_posts": "Bu serverdən olan insanlar köhnə paylaşımlarınızla əlaqə qura bilər." } diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 0951cce712..7b6d205045 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Показване/скриване на текст зад предупреждение на съдържание", "keyboard_shortcuts.toggle_sensitivity": "Показване/скриване на мултимедията", "keyboard_shortcuts.toot": "Начало на нова публикация", + "keyboard_shortcuts.translate": "за превод на публикация", "keyboard_shortcuts.unfocus": "Разфокусиране на текстовото поле за съставяне/търсене", "keyboard_shortcuts.up": "Преместване нагоре в списъка", "lightbox.close": "Затваряне", @@ -836,6 +837,7 @@ "status.reblogs.empty": "Още никого не е подсилвал публикацията. Подсилващият ще се покаже тук.", "status.redraft": "Изтриване и преработване", "status.remove_bookmark": "Премахване на отметката", + "status.remove_favourite": "Премахване от любими", "status.replied_in_thread": "Отговорено в нишката", "status.replied_to": "В отговор до {name}", "status.reply": "Отговор", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 5aba9327d6..0e9a9051a8 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostra/amaga el text marcat com a sensible", "keyboard_shortcuts.toggle_sensitivity": "Mostra/amaga contingut", "keyboard_shortcuts.toot": "Escriu un nou tut", + "keyboard_shortcuts.translate": "per a traduir una publicació", "keyboard_shortcuts.unfocus": "Descentra l'àrea de composició de text/cerca", "keyboard_shortcuts.up": "Apuja a la llista", "lightbox.close": "Tanca", @@ -836,6 +837,7 @@ "status.reblogs.empty": "Encara no ha impulsat ningú aquest tut. Quan algú ho faci, apareixerà aquí.", "status.redraft": "Esborra i reescriu", "status.remove_bookmark": "Elimina el marcador", + "status.remove_favourite": "Elimina dels preferits", "status.replied_in_thread": "Respost al fil", "status.replied_to": "En resposta a {name}", "status.reply": "Respon", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index 4b4236e3fb..ba58b998f1 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -85,7 +85,26 @@ "alert.rate_limited.title": "Spojení omezena", "alert.unexpected.message": "Objevila se neočekávaná chyba.", "alert.unexpected.title": "Jejda!", + "alt_text_badge.title": "Popisek", "announcement.announcement": "Oznámení", + "annual_report.summary.archetype.booster": "Lovec obsahu", + "annual_report.summary.archetype.lurker": "Špión", + "annual_report.summary.archetype.oracle": "Vědma", + "annual_report.summary.archetype.pollster": "Průzkumník", + "annual_report.summary.archetype.replier": "Sociální motýlek", + "annual_report.summary.followers.followers": "sledujících", + "annual_report.summary.followers.total": "{count} celkem", + "annual_report.summary.here_it_is": "Zde je tvůj {year} v přehledu:", + "annual_report.summary.highlighted_post.by_favourites": "nejvíce oblíbený příspěvek", + "annual_report.summary.highlighted_post.by_reblogs": "nejvíce boostovaný příspěvek", + "annual_report.summary.highlighted_post.by_replies": "příspěvek s nejvíce odpověďmi", + "annual_report.summary.highlighted_post.possessive": "{name}", + "annual_report.summary.most_used_app.most_used_app": "nejpoužívanější aplikace", + "annual_report.summary.most_used_hashtag.most_used_hashtag": "nejpoužívanější hashtag", + "annual_report.summary.most_used_hashtag.none": "Žádné", + "annual_report.summary.new_posts.new_posts": "nové příspěvky", + "annual_report.summary.percentile.text": "To vás umisťuje do vrcholu{domain} uživatelů.", + "annual_report.summary.thanks": "Děkujeme, že jste součástí Mastodonu!", "attachments_list.unprocessed": "(nezpracováno)", "audio.hide": "Skrýt zvuk", "block_modal.remote_users_caveat": "Požádáme server {domain}, aby respektoval vaše rozhodnutí. Úplné dodržování nastavení však není zaručeno, protože některé servery mohou řešit blokování různě. Veřejné příspěvky mohou stále být viditelné pro nepřihlášené uživatele.", @@ -109,6 +128,7 @@ "bundle_column_error.routing.body": "Požadovaná stránka nebyla nalezena. Opravdu je URL v adresním řádku správně?", "bundle_column_error.routing.title": "404", "bundle_modal_error.close": "Zavřít", + "bundle_modal_error.message": "Něco se pokazilo při načítání této obrazovky.", "bundle_modal_error.retry": "Zkusit znovu", "closed_registrations.other_server_instructions": "Protože Mastodon je decentralizovaný, můžete si vytvořit účet na jiném serveru a přesto komunikovat s tímto serverem.", "closed_registrations_modal.description": "V současné době není možné vytvořit účet na {domain}, ale mějte prosím na paměti, že k používání Mastodonu nepotřebujete účet konkrétně na {domain}.", @@ -119,13 +139,16 @@ "column.blocks": "Blokovaní uživatelé", "column.bookmarks": "Záložky", "column.community": "Místní časová osa", + "column.create_list": "Vytvořit seznam", "column.direct": "Soukromé zmínky", "column.directory": "Prozkoumat profily", "column.domain_blocks": "Blokované domény", + "column.edit_list": "Upravit seznam", "column.favourites": "Oblíbené", "column.firehose": "Živé kanály", "column.follow_requests": "Žádosti o sledování", "column.home": "Domů", + "column.list_members": "Spravovat členy seznamu", "column.lists": "Seznamy", "column.mutes": "Skrytí uživatelé", "column.notifications": "Oznámení", @@ -138,6 +161,7 @@ "column_header.pin": "Připnout", "column_header.show_settings": "Zobrazit nastavení", "column_header.unpin": "Odepnout", + "column_search.cancel": "Zrušit", "column_subheading.settings": "Nastavení", "community.column_settings.local_only": "Pouze místní", "community.column_settings.media_only": "Pouze média", @@ -179,6 +203,8 @@ "confirmations.edit.confirm": "Upravit", "confirmations.edit.message": "Editovat teď znamená přepsání zprávy, kterou právě tvoříte. Opravdu chcete pokračovat?", "confirmations.edit.title": "Přepsat příspěvek?", + "confirmations.follow_to_list.confirm": "Sledovat a přidat do seznamu", + "confirmations.follow_to_list.title": "Sledovat uživatele?", "confirmations.logout.confirm": "Odhlásit se", "confirmations.logout.message": "Opravdu se chcete odhlásit?", "confirmations.logout.title": "Odhlásit se?", @@ -193,6 +219,8 @@ "confirmations.unfollow.message": "Opravdu chcete {name} přestat sledovat?", "confirmations.unfollow.title": "Přestat sledovat uživatele?", "content_warning.hide": "Skrýt příspěvek", + "content_warning.show": "Přesto zobrazit", + "content_warning.show_more": "Zobrazit více", "conversation.delete": "Smazat konverzaci", "conversation.mark_as_read": "Označit jako přečtené", "conversation.open": "Zobrazit konverzaci", @@ -293,6 +321,7 @@ "filter_modal.select_filter.subtitle": "Použít existující kategorii nebo vytvořit novou kategorii", "filter_modal.select_filter.title": "Filtrovat tento příspěvek", "filter_modal.title.status": "Filtrovat příspěvek", + "filter_warning.matches_filter": "Odpovídá filtru “{title}”", "filtered_notifications_banner.title": "Filtrovaná oznámení", "firehose.all": "Vše", "firehose.local": "Tento server", @@ -323,6 +352,7 @@ "footer.privacy_policy": "Zásady ochrany osobních údajů", "footer.source_code": "Zobrazit zdrojový kód", "footer.status": "Stav", + "footer.terms_of_service": "Obchodní podmínky", "generic.saved": "Uloženo", "getting_started.heading": "Začínáme", "hashtag.column_header.tag_mode.all": "a {additional}", @@ -340,6 +370,10 @@ "hashtag.follow": "Sledovat hashtag", "hashtag.unfollow": "Přestat sledovat hashtag", "hashtags.and_other": "…a {count, plural, one {# další} few {# další} other {# dalších}}", + "hints.profiles.see_more_followers": "Zobrazit více sledujících na {domain}", + "hints.profiles.see_more_follows": "Zobrazit další sledování na {domain}", + "hints.profiles.see_more_posts": "Zobrazit další příspěvky na {domain}", + "hints.threads.see_more": "Zobrazit další odpovědi na {domain}", "home.column_settings.show_reblogs": "Zobrazit boosty", "home.column_settings.show_replies": "Zobrazit odpovědi", "home.hide_announcements": "Skrýt oznámení", @@ -347,12 +381,17 @@ "home.pending_critical_update.link": "Zobrazit aktualizace", "home.pending_critical_update.title": "K dispozici je kritická bezpečnostní aktualizace!", "home.show_announcements": "Zobrazit oznámení", + "ignore_notifications_modal.ignore": "Ignorovat oznámení", + "interaction_modal.go": "Přejít", + "interaction_modal.no_account_yet": "Ještě nemáte účet?", "interaction_modal.on_another_server": "Na jiném serveru", "interaction_modal.on_this_server": "Na tomto serveru", "interaction_modal.title.favourite": "Oblíbit si příspěvek od uživatele {name}", "interaction_modal.title.follow": "Sledovat {name}", "interaction_modal.title.reblog": "Boostnout příspěvek uživatele {name}", "interaction_modal.title.reply": "Odpovědět na příspěvek uživatele {name}", + "interaction_modal.title.vote": "Hlasujte v anketě {name}", + "interaction_modal.username_prompt": "např. {example}", "intervals.full.days": "{number, plural, one {# den} few {# dny} many {# dní} other {# dní}}", "intervals.full.hours": "{number, plural, one {# hodina} few {# hodiny} many {# hodin} other {# hodin}}", "intervals.full.minutes": "{number, plural, one {# minuta} few {# minuty} many {# minut} other {# minut}}", @@ -388,6 +427,7 @@ "keyboard_shortcuts.toggle_hidden": "Zobrazit/skrýt text za varováním o obsahu", "keyboard_shortcuts.toggle_sensitivity": "Zobrazit/skrýt média", "keyboard_shortcuts.toot": "Začít nový příspěvek", + "keyboard_shortcuts.translate": "k přeložení příspěvku", "keyboard_shortcuts.unfocus": "Zrušit zaměření na nový příspěvek/hledání", "keyboard_shortcuts.up": "Posunout v seznamu nahoru", "lightbox.close": "Zavřít", @@ -398,13 +438,29 @@ "link_preview.author": "Podle {name}", "link_preview.more_from_author": "Více od {name}", "link_preview.shares": "{count, plural, one {{counter} příspěvek} few {{counter} příspěvky} many {{counter} příspěvků} other {{counter} příspěvků}}", + "lists.add_member": "Přidat", + "lists.add_to_lists": "Přidat {name} do seznamů", + "lists.create": "Vytvořit", + "lists.create_list": "Vytvořit seznam", "lists.delete": "Smazat seznam", + "lists.done": "Hotovo", "lists.edit": "Upravit seznam", + "lists.find_users_to_add": "Najít uživatele, které chcete přidat", + "lists.list_members": "Členové seznamu", + "lists.list_name": "Název seznamu", + "lists.new_list_name": "Název nového seznamu", + "lists.no_lists_yet": "Zatím žádné seznamy.", + "lists.no_members_yet": "Zatím žádní členové.", + "lists.no_results_found": "Nebyly nalezeny žádné výsledky.", + "lists.remove_member": "Odstranit", "lists.replies_policy.followed": "Sledovaným uživatelům", "lists.replies_policy.list": "Členům seznamu", "lists.replies_policy.none": "Nikomu", + "lists.save": "Uložit", + "lists.search": "Hledat", "load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položek} other {# nových položek}}", "loading_indicator.label": "Načítání…", + "media_gallery.hide": "Skrýt", "moved_to_account_banner.text": "Váš účet {disabledAccount} je momentálně deaktivován, protože jste se přesunul/a na {movedToAccount}.", "mute_modal.hide_from_notifications": "Skrýt z notifikací", "mute_modal.hide_options": "Skrýt možnosti", @@ -416,6 +472,7 @@ "mute_modal.you_wont_see_mentions": "Neuvidíte příspěvky, které je zmiňují.", "mute_modal.you_wont_see_posts": "Stále budou moci vidět vaše příspěvky, ale vy jejich neuvidíte.", "navigation_bar.about": "O aplikaci", + "navigation_bar.administration": "Administrace", "navigation_bar.advanced_interface": "Otevřít pokročilé webové rozhraní", "navigation_bar.blocks": "Blokovaní uživatelé", "navigation_bar.bookmarks": "Záložky", @@ -432,6 +489,7 @@ "navigation_bar.follows_and_followers": "Sledovaní a sledující", "navigation_bar.lists": "Seznamy", "navigation_bar.logout": "Odhlásit se", + "navigation_bar.moderation": "Moderace", "navigation_bar.mutes": "Skrytí uživatelé", "navigation_bar.opened_in_classic_interface": "Příspěvky, účty a další specifické stránky jsou ve výchozím nastavení otevřeny v klasickém webovém rozhraní.", "navigation_bar.personal": "Osobní", @@ -448,6 +506,11 @@ "notification.favourite": "Uživatel {name} si oblíbil váš příspěvek", "notification.follow": "Uživatel {name} vás začal sledovat", "notification.follow_request": "Uživatel {name} požádal o povolení vás sledovat", + "notification.label.mention": "Zmínka", + "notification.label.private_mention": "Soukromá zmínka", + "notification.label.reply": "Odpověď", + "notification.mention": "Zmínka", + "notification.mentioned_you": "{name} vás zmínil", "notification.moderation-warning.learn_more": "Zjistit více", "notification.moderation_warning": "Obdrželi jste moderační varování", "notification.moderation_warning.action_delete_statuses": "Některé z vašich příspěvků byly odstraněny.", @@ -468,11 +531,15 @@ "notification.status": "Uživatel {name} právě přidal příspěvek", "notification.update": "Uživatel {name} upravil příspěvek", "notification_requests.accept": "Přijmout", + "notification_requests.confirm_accept_multiple.title": "Přijmout žádosti o oznámení?", "notification_requests.dismiss": "Zamítnout", + "notification_requests.edit_selection": "Upravit", + "notification_requests.exit_selection": "Hotovo", "notification_requests.maximize": "Maximalizovat", "notification_requests.minimize_banner": "Minimalizovat banner filtrovaných oznámení", "notification_requests.notifications_from": "Oznámení od {name}", "notification_requests.title": "Vyfiltrovaná oznámení", + "notification_requests.view": "Zobrazit oznámení", "notifications.clear": "Vyčistit oznámení", "notifications.clear_confirmation": "Opravdu chcete trvale smazat všechna vaše oznámení?", "notifications.clear_title": "Vyčistit oznámení?", @@ -484,6 +551,7 @@ "notifications.column_settings.filter_bar.category": "Panel rychlého filtrování", "notifications.column_settings.follow": "Noví sledující:", "notifications.column_settings.follow_request": "Nové žádosti o sledování:", + "notifications.column_settings.group": "Skupina", "notifications.column_settings.mention": "Zmínky:", "notifications.column_settings.poll": "Výsledky anket:", "notifications.column_settings.push": "Push oznámení", @@ -507,6 +575,13 @@ "notifications.permission_denied": "Oznámení na ploše nejsou k dispozici, protože byla zamítnuta žádost o oprávnění je zobrazovat", "notifications.permission_denied_alert": "Oznámení na ploše není možné zapnout, protože oprávnění bylo v minulosti zamítnuto", "notifications.permission_required": "Oznámení na ploše nejsou k dispozici, protože nebylo uděleno potřebné oprávnění.", + "notifications.policy.accept": "Přijmout", + "notifications.policy.accept_hint": "Zobrazit v oznámeních", + "notifications.policy.drop": "Ignorovat", + "notifications.policy.filter": "Filtrovat", + "notifications.policy.filter_hint": "Odeslat do filtrované schránky oznámení", + "notifications.policy.filter_limited_accounts_hint": "Omezeno moderátory serveru", + "notifications.policy.filter_limited_accounts_title": "Moderované účty", "notifications.policy.filter_new_accounts.hint": "Vytvořeno během {days, plural, one {včerejška} few {posledních # dnů} many {posledních # dní} other {posledních # dní}}", "notifications.policy.filter_new_accounts_title": "Nové účty", "notifications.policy.filter_not_followers_hint": "Včetně lidí, kteří vás sledovali méně než {days, plural, one {jeden den} few {# dny} many {# dní} other {# dní}}", @@ -515,10 +590,15 @@ "notifications.policy.filter_not_following_title": "Lidé, které nesledujete", "notifications.policy.filter_private_mentions_hint": "Vyfiltrováno, pokud to není odpověď na vaši zmínku nebo pokud sledujete odesílatele", "notifications.policy.filter_private_mentions_title": "Nevyžádané soukromé zmínky", + "notifications.policy.title": "Spravovat oznámení od…", "notifications_permission_banner.enable": "Povolit oznámení na ploše", "notifications_permission_banner.how_to_control": "Chcete-li dostávat oznámení, i když nemáte Mastodon otevřený, povolte oznámení na ploše. Můžete si zvolit, o kterých druzích interakcí chcete být oznámením na ploše informování pod tlačítkem {icon} výše.", "notifications_permission_banner.title": "Nenechte si nic uniknout", + "onboarding.follows.back": "Zpět", + "onboarding.follows.done": "Hotovo", "onboarding.follows.empty": "Bohužel, žádné výsledky nelze momentálně zobrazit. Můžete zkusit vyhledat nebo procházet stránku s průzkumem a najít lidi, kteří budou sledovat, nebo to zkuste znovu později.", + "onboarding.follows.search": "Hledat", + "onboarding.follows.title": "Sledujte lidi a začněte", "onboarding.profile.discoverable": "Udělat svůj profil vyhledatelným", "onboarding.profile.discoverable_hint": "Když se rozhodnete být vyhledatelný na Mastodonu, vaše příspěvky se mohou objevit ve výsledcích vyhledávání a v populárních, a váš profil může být navrhován lidem s podobnými zájmy.", "onboarding.profile.display_name": "Zobrazované jméno", @@ -556,6 +636,8 @@ "privacy_policy.title": "Zásady ochrany osobních údajů", "recommended": "Doporučeno", "refresh": "Obnovit", + "regeneration_indicator.please_stand_by": "Počkej prosím.", + "regeneration_indicator.preparing_your_home_feed": "Připravujeme vaší domovskou stránku…", "relative_time.days": "{number} d", "relative_time.full.days": "před {number, plural, one {# dnem} few {# dny} many {# dny} other {# dny}}", "relative_time.full.hours": "před {number, plural, one {# hodinou} few {# hodinami} many {# hodinami} other {# hodinami}}", @@ -639,8 +721,11 @@ "search_results.accounts": "Profily", "search_results.all": "Vše", "search_results.hashtags": "Hashtagy", + "search_results.no_results": "Nelze najít.", + "search_results.no_search_yet": "Zkuste vyhledat příspěvky, profily nebo hashtagy.", "search_results.see_all": "Zobrazit vše", "search_results.statuses": "Příspěvky", + "search_results.title": "Hledat \"{q}\"", "server_banner.about_active_users": "Lidé používající tento server během posledních 30 dní (měsíční aktivní uživatelé)", "server_banner.active_users": "aktivní uživatelé", "server_banner.administered_by": "Spravováno:", @@ -658,6 +743,7 @@ "status.bookmark": "Přidat do záložek", "status.cancel_reblog_private": "Zrušit boostnutí", "status.cannot_reblog": "Tento příspěvek nemůže být boostnutý", + "status.continued_thread": "Pokračuje ve vlákně", "status.copy": "Zkopírovat odkaz na příspěvek", "status.delete": "Smazat", "status.detailed_status": "Podrobné zobrazení konverzace", @@ -666,6 +752,7 @@ "status.edit": "Upravit", "status.edited": "Naposledy upraveno {date}", "status.edited_x_times": "Upraveno {count, plural, one {{count}krát} few {{count}krát} many {{count}krát} other {{count}krát}}", + "status.embed": "Získejte kód pro vložení", "status.favourite": "Oblíbit", "status.favourites": "{count, plural, one {oblíbený} few {oblíbené} many {oblíbených} other {oblíbených}}", "status.filter": "Filtrovat tento příspěvek", @@ -690,6 +777,8 @@ "status.reblogs.empty": "Tento příspěvek ještě nikdo neboostnul. Pokud to někdo udělá, zobrazí se zde.", "status.redraft": "Smazat a přepsat", "status.remove_bookmark": "Odstranit ze záložek", + "status.remove_favourite": "Odebrat z oblíbených", + "status.replied_in_thread": "Odpověděli ve vlákně", "status.replied_to": "Odpověděl/a uživateli {name}", "status.reply": "Odpovědět", "status.replyAll": "Odpovědět na vlákno", @@ -710,6 +799,7 @@ "subscribed_languages.target": "Změnit odebírané jazyky na {target}", "tabs_bar.home": "Domů", "tabs_bar.notifications": "Oznámení", + "terms_of_service.title": "Podmínky služby", "time_remaining.days": "{number, plural, one {Zbývá # den} few {Zbývají # dny} many {Zbývá # dní} other {Zbývá # dní}}", "time_remaining.hours": "{number, plural, one {Zbývá # hodina} few {Zbývají # hodiny} many {Zbývá # hodin} other {Zbývá # hodin}}", "time_remaining.minutes": "{number, plural, one {Zbývá # minuta} few {Zbývají # minuty} many {Zbývá # minut} other {Zbývá # minut}}", @@ -727,6 +817,11 @@ "upload_error.poll": "Nahrávání souborů není povoleno s anketami.", "upload_form.audio_description": "Popis pro sluchově postižené", "upload_form.description": "Popis pro zrakově postižené", + "upload_form.drag_and_drop.instructions": "Chcete-li zvednout přílohu, stiskněte mezerník nebo enter. Při přetažení použijte klávesnicové šipky k přesunutí mediální přílohy v libovolném směru. Stiskněte mezerník nebo enter pro vložení přílohy do nové pozice, nebo stiskněte Esc pro ukončení.", + "upload_form.drag_and_drop.on_drag_cancel": "Přetažení bylo zrušeno. Příloha {item} byla vrácena.", + "upload_form.drag_and_drop.on_drag_end": "Příloha {item} byla vrácena.", + "upload_form.drag_and_drop.on_drag_over": "Příloha {item} byla přesunuta.", + "upload_form.drag_and_drop.on_drag_start": "Zvednuta příloha {item}.", "upload_form.edit": "Upravit", "upload_form.thumbnail": "Změnit miniaturu", "upload_form.video_description": "Popis pro sluchově či zrakově postižené", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 335f805576..92cfb62f51 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Vis/skjul tekst bag CW", "keyboard_shortcuts.toggle_sensitivity": "Vis/skjul medier", "keyboard_shortcuts.toot": "Påbegynd nyt indlæg", + "keyboard_shortcuts.translate": "for at oversætte et indlæg", "keyboard_shortcuts.unfocus": "Fjern fokus fra tekstskrivningsområde/søgning", "keyboard_shortcuts.up": "Flyt opad på listen", "lightbox.close": "Luk", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 967e7a1fe8..f7e64d034d 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Beitragstext hinter der Inhaltswarnung anzeigen/ausblenden", "keyboard_shortcuts.toggle_sensitivity": "Medien anzeigen/ausblenden", "keyboard_shortcuts.toot": "Neuen Beitrag erstellen", + "keyboard_shortcuts.translate": "Beitrag übersetzen", "keyboard_shortcuts.unfocus": "Eingabefeld/Suche nicht mehr fokussieren", "keyboard_shortcuts.up": "Ansicht nach oben bewegen", "lightbox.close": "Schließen", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 6d8a2cd4e5..293dcc8205 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -863,7 +863,7 @@ "time_remaining.minutes": "απομένουν {number, plural, one {# λεπτό} other {# λεπτά}}", "time_remaining.moments": "Στιγμές που απομένουν", "time_remaining.seconds": "απομένουν {number, plural, one {# δευτερόλεπτο} other {# δευτερόλεπτα}}", - "trends.counter_by_accounts": "{count, plural, one {{counter} άτομο} other {{counter} άτομα} }{days, plural, one { την τελευταία ημέρα} other { τις τελευταίες {days} ημέρες}}", + "trends.counter_by_accounts": "{count, plural, one {{counter} άτομο} other {{counter} άτομα}} {days, plural, one {την τελευταία ημέρα} other {τις τελευταίες {days} ημέρες}}", "trends.trending_now": "Δημοφιλή τώρα", "ui.beforeunload": "Το προσχέδιό σου θα χαθεί αν φύγεις από το Mastodon.", "units.short.billion": "{count}Δις", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 6f5da03daf..6408ce4b65 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Montri/kaŝi tekston malantaŭ CW", "keyboard_shortcuts.toggle_sensitivity": "Montri/kaŝi plurmedion", "keyboard_shortcuts.toot": "Komencu novan afiŝon", + "keyboard_shortcuts.translate": "Traduki afiŝon", "keyboard_shortcuts.unfocus": "Senfokusigi verki tekstareon/serĉon", "keyboard_shortcuts.up": "Movu supren en la listo", "lightbox.close": "Fermi", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 3e2e796691..721e47c271 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostrar/ocultar texto detrás de la advertencia de contenido (\"CW\")", "keyboard_shortcuts.toggle_sensitivity": "Mostrar/ocultar medios", "keyboard_shortcuts.toot": "Comenzar un mensaje nuevo", + "keyboard_shortcuts.translate": "para traducir un mensaje", "keyboard_shortcuts.unfocus": "Quitar el foco del área de texto de redacción o de búsqueda", "keyboard_shortcuts.up": "Subir en la lista", "lightbox.close": "Cerrar", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index ab65bbb6d4..5324d3d3fc 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "mostrar/ocultar texto tras aviso de contenido (CW)", "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar medios", "keyboard_shortcuts.toot": "Comenzar una nueva publicación", + "keyboard_shortcuts.translate": "para traducir una publicación", "keyboard_shortcuts.unfocus": "para retirar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "para ir hacia arriba en la lista", "lightbox.close": "Cerrar", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 82f858f667..f580822cd7 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "mostrar/ocultar texto tras aviso de contenido (CW)", "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar medios", "keyboard_shortcuts.toot": "Comienza una nueva publicación", + "keyboard_shortcuts.translate": "para traducir una publicación", "keyboard_shortcuts.unfocus": "para retirar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "para ir hacia arriba en la lista", "lightbox.close": "Cerrar", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index e445473d05..9abeeea9bf 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -104,6 +104,7 @@ "annual_report.summary.most_used_hashtag.none": "Ei mitään", "annual_report.summary.new_posts.new_posts": "uutta julkaisua", "annual_report.summary.percentile.text": "Olet osa huippujoukkoa, johon kuuluu{domain}-käyttäjistä.", + "annual_report.summary.percentile.we_wont_tell_bernie": "Emme kerro Bernie Sandersille.", "annual_report.summary.thanks": "Kiitos, että olet osa Mastodonia!", "attachments_list.unprocessed": "(käsittelemätön)", "audio.hide": "Piilota ääni", @@ -456,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Näytä tai piilota sisältövaroituksella merkitty teksti", "keyboard_shortcuts.toggle_sensitivity": "Näytä tai piilota media", "keyboard_shortcuts.toot": "Luo uusi julkaisu", + "keyboard_shortcuts.translate": "Käännä julkaisu", "keyboard_shortcuts.unfocus": "Poistu kirjoitus- tai hakukentästä", "keyboard_shortcuts.up": "Siirry luettelossa taaksepäin", "lightbox.close": "Sulje", diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json index 827758f726..492ad38bac 100644 --- a/app/javascript/mastodon/locales/fil.json +++ b/app/javascript/mastodon/locales/fil.json @@ -34,7 +34,9 @@ "account.follow_back": "Sundan pabalik", "account.followers": "Mga tagasunod", "account.followers.empty": "Wala pang sumusunod sa tagagamit na ito.", + "account.followers_counter": "{count, plural, one {{counter} tagasunod} other {{counter} tagasunod}}", "account.following": "Sinusundan", + "account.following_counter": "{count, plural, one {{counter} sinusundan} other {{counter} sinusundan}}", "account.follows.empty": "Wala pang sinusundan ang tagagamit na ito.", "account.go_to_profile": "Pumunta sa profile", "account.hide_reblogs": "Itago ang mga pagpapalakas mula sa {name}", @@ -46,14 +48,21 @@ "account.media": "Medya", "account.mention": "Banggitin si @{name}", "account.moved_to": "Ipinahihiwatig ni {name} na ang kanilang bagong account ngayon ay:", + "account.mute": "I-mute si @{name}", + "account.mute_notifications_short": "I-mute ang mga abiso", + "account.mute_short": "I-mute", + "account.muted": "Naka-mute", + "account.mutual": "Ka-mutual", "account.no_bio": "Walang nakalaan na paglalarawan.", "account.open_original_page": "Buksan ang pinagmulang pahina", "account.posts": "Mga post", "account.report": "I-ulat si/ang @{name}", + "account.requested": "Naghihintay ng pag-apruba. I-click upang ikansela ang hiling sa pagsunod", "account.requested_follow": "Hinihiling ni {name} na sundan ka", "account.share": "Ibahagi ang profile ni @{name}", "account.show_reblogs": "Ipakita ang mga pagpapalakas mula sa/kay {name}", "account.unendorse": "Huwag itampok sa profile", + "account.unfollow": "Huwag nang sundan", "admin.dashboard.retention.cohort_size": "Mga bagong tagagamit", "alert.rate_limited.message": "Mangyaring subukan muli pagkatapos ng {retry_time, time, medium}.", "audio.hide": "Itago ang tunog", @@ -355,6 +364,7 @@ "status.more": "Higit pa", "status.read_more": "Basahin ang higit pa", "status.reblogs.empty": "Wala pang nagpalakas ng post na ito. Kung may sinumang nagpalakas, makikita sila rito.", + "status.remove_favourite": "Tanggalin sa mga paborito", "status.reply": "Tumugon", "status.report": "I-ulat si/ang @{name}", "status.sensitive_warning": "Sensitibong nilalaman", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index ff354adf7f..c4d5f7296a 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -456,6 +456,7 @@ "keyboard_shortcuts.toggle_hidden": "Vís/fjal tekst handan CW", "keyboard_shortcuts.toggle_sensitivity": "Vís ella fjal innihald", "keyboard_shortcuts.toot": "Byrja nýggjan post", + "keyboard_shortcuts.translate": "at umseta ein post", "keyboard_shortcuts.unfocus": "Tak skrivi-/leiti-økið úr miðdeplinum", "keyboard_shortcuts.up": "Flyt upp á listanum", "lightbox.close": "Lat aftur", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index 488a5f3eef..d73e9dcf5d 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Déplier/replier texte derrière avertissement", "keyboard_shortcuts.toggle_sensitivity": "Afficher/cacher médias", "keyboard_shortcuts.toot": "Commencer un nouveau message", + "keyboard_shortcuts.translate": "traduire un message", "keyboard_shortcuts.unfocus": "Ne plus se concentrer sur la zone de rédaction/barre de recherche", "keyboard_shortcuts.up": "Monter dans la liste", "lightbox.close": "Fermer", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 731c15e36c..f6c45ad706 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Déplier/replier le texte derrière un CW", "keyboard_shortcuts.toggle_sensitivity": "Afficher/cacher les médias", "keyboard_shortcuts.toot": "Commencer un nouveau message", + "keyboard_shortcuts.translate": "traduire un message", "keyboard_shortcuts.unfocus": "Quitter la zone de rédaction/barre de recherche", "keyboard_shortcuts.up": "Monter dans la liste", "lightbox.close": "Fermer", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 469bad98da..02682335ef 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Para mostrar o texto tras Aviso de Contido (CW)", "keyboard_shortcuts.toggle_sensitivity": "Para amosar/agochar contido multimedia", "keyboard_shortcuts.toot": "Para escribir unha nova publicación", + "keyboard_shortcuts.translate": "para traducir unha publicación", "keyboard_shortcuts.unfocus": "Para deixar de destacar a área de escritura/procura", "keyboard_shortcuts.up": "Para mover cara arriba na listaxe", "lightbox.close": "Fechar", @@ -836,6 +837,7 @@ "status.reblogs.empty": "Aínda ninguén promoveu esta publicación. Cando alguén o faga, amosarase aquí.", "status.redraft": "Eliminar e reescribir", "status.remove_bookmark": "Eliminar marcador", + "status.remove_favourite": "Retirar das favoritas", "status.replied_in_thread": "Respondeu nun fío", "status.replied_to": "Respondeu a {name}", "status.reply": "Responder", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index fbfde82727..71ced7e0df 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "הצגת/הסתרת טקסט מוסתר מאחורי אזהרת תוכן", "keyboard_shortcuts.toggle_sensitivity": "הצגת/הסתרת מדיה", "keyboard_shortcuts.toot": "להתחיל חיצרוץ חדש", + "keyboard_shortcuts.translate": "לתרגם הודעה", "keyboard_shortcuts.unfocus": "לצאת מתיבת חיבור/חיפוש", "keyboard_shortcuts.up": "לנוע במעלה הרשימה", "lightbox.close": "סגירה", @@ -836,6 +837,7 @@ "status.reblogs.empty": "עוד לא הידהדו את ההודעה הזו. כאשר זה יקרה, ההדהודים יופיעו כאן.", "status.redraft": "מחיקה ועריכה מחדש", "status.remove_bookmark": "הסרת סימניה", + "status.remove_favourite": "להסיר מרשימת המועדפים", "status.replied_in_thread": "תגובה לשרשור", "status.replied_to": "בתגובה לחשבון {name}", "status.reply": "תגובה", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index c07b7c48cb..d1ae388675 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -440,7 +440,7 @@ "keyboard_shortcuts.heading": "Gyorsbillentyűk", "keyboard_shortcuts.home": "Saját idővonal megnyitása", "keyboard_shortcuts.hotkey": "Gyorsbillentyű", - "keyboard_shortcuts.legend": "jelmagyarázat megjelenítése", + "keyboard_shortcuts.legend": "Jelmagyarázat megjelenítése", "keyboard_shortcuts.local": "Helyi idővonal megnyitása", "keyboard_shortcuts.mention": "Szerző megemlítése", "keyboard_shortcuts.muted": "Némított felhasználók listájának megnyitása", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 3043eb323f..9a5c287f1c 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Birta/fela texta á bak við aðvörun vegna efnis", "keyboard_shortcuts.toggle_sensitivity": "Birta/fela myndir", "keyboard_shortcuts.toot": "Byrja nýja færslu", + "keyboard_shortcuts.translate": "að þýða færslu", "keyboard_shortcuts.unfocus": "Taka virkni úr textainnsetningarreit eða leit", "keyboard_shortcuts.up": "Fara ofar í listanum", "lightbox.close": "Loka", @@ -836,6 +837,7 @@ "status.reblogs.empty": "Enginn hefur ennþá endurbirt þessa færslu. Þegar einhver gerir það, mun það birtast hér.", "status.redraft": "Eyða og endurvinna drög", "status.remove_bookmark": "Fjarlægja bókamerki", + "status.remove_favourite": "Fjarlægja úr eftirlætum", "status.replied_in_thread": "Svaraði í samtali", "status.replied_to": "Svaraði til {name}", "status.reply": "Svara", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 4d9952ccb6..9593744e4b 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -449,6 +449,7 @@ "keyboard_shortcuts.toggle_hidden": "CW로 가려진 텍스트를 표시/비표시", "keyboard_shortcuts.toggle_sensitivity": "미디어 보이기/숨기기", "keyboard_shortcuts.toot": "새 게시물 작성", + "keyboard_shortcuts.translate": "게시물 번역", "keyboard_shortcuts.unfocus": "작성창에서 포커스 해제", "keyboard_shortcuts.up": "리스트에서 위로 이동", "lightbox.close": "닫기", diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json index cdba826126..f3fba6757f 100644 --- a/app/javascript/mastodon/locales/la.json +++ b/app/javascript/mastodon/locales/la.json @@ -1,14 +1,25 @@ { + "about.blocks": "Servī moderātī", "about.contact": "Ratio:", + "about.disclaimer": "Mastodon est software līberum, apertum fontem, et nōtam commercium Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Ratio abdere est", + "about.domain_blocks.preamble": "Mastodon genērāliter sinit tē contentum ex aliīs servientibus in fedīversō vidēre et cum usoribus ab iīs interāgere. Haē sunt exceptionēs quae in hōc particulārī servientē factae sunt.", "about.domain_blocks.silenced.explanation": "Tua profilia atque tuum contentum ab hac serve praecipue non videbis, nisi explōrēs expresse aut subsequeris et optēs.", + "about.domain_blocks.silenced.title": "Limitātus", + "about.domain_blocks.suspended.explanation": "Nulla data ab hōc servientē processābuntur, servābuntur aut commūtābuntur, faciendumque omnem interactionem aut communicātiōnem cum usoribus ab hōc servientē impossibilem.", + "about.domain_blocks.suspended.title": "suspensus", + "about.not_available": "Haec informātiō in hōc servientē nōn praebita est.", + "about.powered_by": "Nuntii socīālēs decentralizātī ā {mastodon} sustentātī.", + "about.rules": "Servo praecepta", + "account.account_note_header": "Nota personalia", "account.add_or_remove_from_list": "Adde aut ēripe ex tabellīs", "account.badges.bot": "Robotum", "account.badges.group": "Congregatio", "account.block": "Impedire @{name}", "account.block_domain": "Imperire dominium {domain}", + "account.block_short": "Imperire", "account.blocked": "Impeditum est", - "account.cancel_follow_request": "Withdraw follow request", + "account.cancel_follow_request": "Petitio sequī retrāhere", "account.domain_blocked": "Dominium impeditum", "account.edit_profile": "Recolere notionem", "account.featured_tags.last_status_never": "Nulla contributa", @@ -105,30 +116,30 @@ "keyboard_shortcuts.compose": "TextArea Compositi Attendere", "keyboard_shortcuts.description": "Descriptio", "keyboard_shortcuts.direct": "to open direct messages column", - "keyboard_shortcuts.down": "to move down in the list", + "keyboard_shortcuts.down": "In īndice dēscend", "keyboard_shortcuts.enter": "Aperire contributum", - "keyboard_shortcuts.federated": "to open federated timeline", + "keyboard_shortcuts.federated": "Aperī chrōnologiam foederātam", "keyboard_shortcuts.heading": "Keyboard Shortcuts", - "keyboard_shortcuts.home": "to open home timeline", - "keyboard_shortcuts.legend": "to display this legend", - "keyboard_shortcuts.local": "to open local timeline", - "keyboard_shortcuts.mention": "to mention author", - "keyboard_shortcuts.muted": "to open muted users list", - "keyboard_shortcuts.my_profile": "to open your profile", - "keyboard_shortcuts.notifications": "to open notifications column", - "keyboard_shortcuts.open_media": "to open media", - "keyboard_shortcuts.pinned": "to open pinned posts list", - "keyboard_shortcuts.profile": "to open author's profile", + "keyboard_shortcuts.home": "Aperī chrōnologiam domesticam", + "keyboard_shortcuts.legend": "Hanc legendam ostende", + "keyboard_shortcuts.local": "Aperī chrōnologiam locālem", + "keyboard_shortcuts.mention": "Memēntō auctōris", + "keyboard_shortcuts.muted": "Aperī indicem ūtentium silentiōrum", + "keyboard_shortcuts.my_profile": "Aperī prōfilum tuum", + "keyboard_shortcuts.notifications": "Aperī columnam nūntiātiōnum", + "keyboard_shortcuts.open_media": "Aperi media", + "keyboard_shortcuts.pinned": "Aperī indicem nūntiōrum affixōrum", + "keyboard_shortcuts.profile": "Aperi auctoris profile", "keyboard_shortcuts.reply": "Respondere ad contributum", - "keyboard_shortcuts.requests": "to open follow requests list", - "keyboard_shortcuts.search": "to focus search", - "keyboard_shortcuts.spoilers": "to show/hide CW field", - "keyboard_shortcuts.start": "to open \"get started\" column", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", - "keyboard_shortcuts.toot": "to start a brand new post", - "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", - "keyboard_shortcuts.up": "to move up in the list", + "keyboard_shortcuts.requests": "Aperī indicem petītiōnum sequendī", + "keyboard_shortcuts.search": "Fōcum in tabellam quaerendī", + "keyboard_shortcuts.spoilers": "Ostende / celare CW agri", + "keyboard_shortcuts.start": "Aperī columnam 'īncipere'", + "keyboard_shortcuts.toggle_hidden": "Monstrare / celare textum post CW", + "keyboard_shortcuts.toggle_sensitivity": "Ostende / celare media", + "keyboard_shortcuts.toot": "Incipe nōvum nūntium.", + "keyboard_shortcuts.unfocus": "Desinēre fōcum in ārēā componendī/inquīrendī", + "keyboard_shortcuts.up": "Sumē sūrsum in īndice", "lightbox.close": "Claudere", "lightbox.next": "Secundum", "load_pending": "{count, plural, one {# novum item} other {# nova itema}}", @@ -142,7 +153,7 @@ "notification.favourite": "{name} nuntium tuum favit", "notification.follow": "{name} te secutus est", "notification.follow_request": "{name} postulavit ut te sequeretur", - "notification.moderation_warning": "Accepistī monitionem moderationis.", + "notification.moderation_warning": "Accepistī monitionem moderationis", "notification.moderation_warning.action_disable": "Ratio tua debilitata est.", "notification.moderation_warning.action_none": "Tua ratiō monitum moderātiōnis accēpit.", "notification.moderation_warning.action_sensitive": "Tua nuntia hinc sensibiliter notabuntur.", @@ -160,7 +171,7 @@ "notification_requests.confirm_dismiss_multiple.message": "Tu {count, plural, one {unam petitionem notificationis} other {# petitiones notificationum}} abrogāre prōximum es. {count, plural, one {Illa} other {Eae}} facile accessū nōn erit. Certus es tē procedere velle?", "notifications.filter.all": "Omnia", "notifications.filter.polls": "Eventus electionis", - "notifications.group": "Notificātiōnēs", + "notifications.group": "{count} Notificātiōnēs", "onboarding.profile.display_name_hint": "Tuum nomen completum aut tuum nomen ludens…", "onboarding.profile.note_hint": "Alios hominēs vel #hashtags @nōmināre potes…", "poll.closed": "Clausum", @@ -191,7 +202,7 @@ "report.mute_explanation": "Non videbis eōrum nuntiōs. Possunt adhuc tē sequī et tuōs nuntiōs vidēre, nec sciēbunt sē tacitōs esse.", "report.next": "Secundum", "report.placeholder": "Commentāriī adiūnctī", - "report.reasons.legal_description": "Putās id legem tuae aut servientis patriae violāre.", + "report.reasons.legal_description": "Putās id legem tuae aut servientis patriae violāre", "report.reasons.violation_description": "Scis quod certa praecepta frangit", "report.submit": "Mittere", "report.target": "Report {target}", @@ -200,7 +211,7 @@ "report_notification.categories.other": "Altera", "search.placeholder": "Quaerere", "search_results.all": "Omnis", - "server_banner.active_users": "Usūrāriī āctīvī", + "server_banner.active_users": "usūāriī āctīvī", "server_banner.administered_by": "Administratur:", "server_banner.is_one_of_many": "{domain} est unum ex multis independentibus servientibus Mastodon quos adhibere potes ut participes in fediverso.", "sign_in_banner.sign_in": "Sign in", @@ -210,7 +221,7 @@ "status.copy": "Copy link to status", "status.delete": "Oblitterare", "status.edit": "Recolere", - "status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}", + "status.edited_x_times": "Emendatum est {count, plural, one {{count} tempus} other {{count} tempora}}", "status.favourites": "{count, plural, one {favoritum} other {favorita}}", "status.history.created": "{name} creatum {date}", "status.history.edited": "{name} correxit {date}", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index d83c7fae14..3940aa630c 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -453,6 +453,7 @@ "keyboard_shortcuts.toggle_hidden": "Rodyti / slėpti tekstą po TĮ", "keyboard_shortcuts.toggle_sensitivity": "Rodyti / slėpti mediją", "keyboard_shortcuts.toot": "Pradėti naują įrašą", + "keyboard_shortcuts.translate": "išversti įrašą", "keyboard_shortcuts.unfocus": "Nebefokusuoti rengykles teksto sritį / paiešką", "keyboard_shortcuts.up": "Perkelti į viršų sąraše", "lightbox.close": "Uždaryti", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index cf6796c2f5..1bc3c68382 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -86,6 +86,7 @@ "alert.unexpected.message": "Radās negaidīta kļūda.", "alert.unexpected.title": "Ups!", "announcement.announcement": "Paziņojums", + "annual_report.summary.archetype.oracle": "Orākuls", "annual_report.summary.archetype.replier": "Sabiedriskais tauriņš", "annual_report.summary.followers.followers": "sekotāji", "annual_report.summary.followers.total": "pavisam {count}", @@ -401,13 +402,20 @@ "lightbox.previous": "Iepriekšējais", "limited_account_hint.action": "Tik un tā rādīt profilu", "limited_account_hint.title": "{domain} moderatori ir paslēpuši šo profilu.", - "link_preview.author": "Pēc {name}", + "link_preview.author": "No {name}", "link_preview.more_from_author": "Vairāk no {name}", + "lists.add_member": "Pievienot", + "lists.add_to_list": "Pievienot sarakstam", + "lists.create": "Izveidot", + "lists.create_list": "Izveidot sarakstu", "lists.delete": "Izdzēst sarakstu", + "lists.done": "Gatavs", "lists.edit": "Labot sarakstu", + "lists.remove_member": "Noņemt", "lists.replies_policy.followed": "Jebkuram sekotajam lietotājam", "lists.replies_policy.list": "Saraksta dalībniekiem", "lists.replies_policy.none": "Nevienam", + "lists.save": "Saglabāt", "load_pending": "{count, plural, zero{# jaunu vienumu} one {# jauns vienums} other {# jauni vienumi}}", "loading_indicator.label": "Ielādē…", "media_gallery.hide": "Paslēpt", @@ -466,6 +474,8 @@ "notification.update": "{name} laboja ierakstu", "notification_requests.accept": "Pieņemt", "notification_requests.dismiss": "Noraidīt", + "notification_requests.edit_selection": "Labot", + "notification_requests.exit_selection": "Gatavs", "notification_requests.notifications_from": "Paziņojumi no {name}", "notification_requests.title": "Atlasītie paziņojumi", "notifications.clear": "Notīrīt paziņojumus", @@ -501,6 +511,7 @@ "notifications.permission_denied": "Darbvirsmas paziņojumi nav pieejami, jo iepriekš tika noraidīts pārlūka atļauju pieprasījums", "notifications.permission_denied_alert": "Darbvirsmas paziņojumus nevar iespējot, jo pārlūkprogrammai atļauja tika iepriekš atteikta", "notifications.permission_required": "Darbvirsmas paziņojumi nav pieejami, jo nav piešķirta nepieciešamā atļauja.", + "notifications.policy.accept": "Pieņemt", "notifications.policy.filter_new_accounts_title": "Jauni konti", "notifications.policy.filter_not_followers_title": "Cilvēki, kuri Tev neseko", "notifications.policy.filter_not_following_hint": "Līdz tos pašrocīgi apstiprināsi", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index c99c92eceb..f9b631c0e2 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Inhoudswaarschuwing tonen/verbergen", "keyboard_shortcuts.toggle_sensitivity": "Media tonen/verbergen", "keyboard_shortcuts.toot": "Nieuw bericht schrijven", + "keyboard_shortcuts.translate": "om een bericht te vertalen", "keyboard_shortcuts.unfocus": "Tekst- en zoekveld ontfocussen", "keyboard_shortcuts.up": "Naar boven in de lijst bewegen", "lightbox.close": "Sluiten", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 0d9a660119..6091f1679f 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Vis/gøym tekst bak innhaldsvarsel", "keyboard_shortcuts.toggle_sensitivity": "Vis/gøym media", "keyboard_shortcuts.toot": "Lag nytt tut", + "keyboard_shortcuts.translate": "å omsetje eit innlegg", "keyboard_shortcuts.unfocus": "for å fokusere vekk skrive-/søkefeltet", "keyboard_shortcuts.up": "Flytt opp på lista", "lightbox.close": "Lukk", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index e1e8dd9ede..f59e7b96bc 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -236,6 +236,7 @@ "domain_block_modal.they_cant_follow": "Nikto z tohoto servera ťa nemôže nasledovať.", "domain_block_modal.they_wont_know": "Nebude vedieť, že bol/a zablokovaný/á.", "domain_block_modal.title": "Blokovať doménu?", + "domain_block_modal.you_will_lose_relationships": "Stratíš všetkých sledovateľov a ľudí, ktorých ty na tomto serveri nasleduješ.", "domain_block_modal.you_wont_see_posts": "Neuvidíš príspevky, ani oboznámenia od užívateľov na tomto serveri.", "domain_pill.activitypub_like_language": "ActivityPub je ako jazyk, ktorým Mastodon hovorí s ostatnými sociálnymi sieťami.", "domain_pill.server": "Server", @@ -379,6 +380,7 @@ "ignore_notifications_modal.private_mentions_title": "Nevšímať si oznámenia o nevyžiadaných súkromných spomínaniach?", "interaction_modal.action.reply": "Pre pokračovanie musíš odpovedať s tvojho účtu.", "interaction_modal.action.vote": "Pre pokračovanie musíš hlasovať s tvojho účtu.", + "interaction_modal.go": "Prejdi", "interaction_modal.no_account_yet": "Ešte nemáš účet?", "interaction_modal.on_another_server": "Na inom serveri", "interaction_modal.on_this_server": "Na tomto serveri", @@ -422,6 +424,7 @@ "keyboard_shortcuts.toggle_hidden": "Zobraziť/skryť text za varovaním o obsahu", "keyboard_shortcuts.toggle_sensitivity": "Zobraziť/skryť médiá", "keyboard_shortcuts.toot": "Vytvoriť nový príspevok", + "keyboard_shortcuts.translate": "preložiť príspevok", "keyboard_shortcuts.unfocus": "Odísť z textového poľa", "keyboard_shortcuts.up": "Posunúť sa vyššie v zozname", "lightbox.close": "Zatvoriť", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 8bf529e0d4..ce2ec13740 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -452,6 +452,7 @@ "keyboard_shortcuts.toggle_hidden": "Për shfaqje/fshehje teksti pas CW", "keyboard_shortcuts.toggle_sensitivity": "Për shfaqje/fshehje mediash", "keyboard_shortcuts.toot": "Për të filluar një mesazh të ri", + "keyboard_shortcuts.translate": "për të përkthyer një postim", "keyboard_shortcuts.unfocus": "Për heqjen e fokusit nga fusha e hartimit të mesazheve apo kërkimeve", "keyboard_shortcuts.up": "Për ngjitje sipër nëpër listë", "lightbox.close": "Mbylle", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 7746a731e5..d8f1b0b2f3 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -400,6 +400,13 @@ "ignore_notifications_modal.not_followers_title": "เพิกเฉยการแจ้งเตือนจากผู้คนที่ไม่ได้ติดตามคุณ?", "ignore_notifications_modal.not_following_title": "เพิกเฉยการแจ้งเตือนจากผู้คนที่คุณไม่ได้ติดตาม?", "ignore_notifications_modal.private_mentions_title": "เพิกเฉยการแจ้งเตือนจากการกล่าวถึงแบบส่วนตัวที่ไม่พึงประสงค์?", + "interaction_modal.action.favourite": "เพื่อดำเนินการต่อ คุณจำเป็นต้องชื่นชอบจากบัญชีของคุณ", + "interaction_modal.action.follow": "เพื่อดำเนินการต่อ คุณจำเป็นต้องติดตามจากบัญชีของคุณ", + "interaction_modal.action.reblog": "เพื่อดำเนินการต่อ คุณจำเป็นต้องดันจากบัญชีของคุณ", + "interaction_modal.action.reply": "เพื่อดำเนินการต่อ คุณจำเป็นต้องตอบกลับจากบัญชีของคุณ", + "interaction_modal.action.vote": "เพื่อดำเนินการต่อ คุณจำเป็นต้องลงคะแนนจากบัญชีของคุณ", + "interaction_modal.go": "ไป", + "interaction_modal.no_account_yet": "ยังไม่มีบัญชี?", "interaction_modal.on_another_server": "ในเซิร์ฟเวอร์อื่น", "interaction_modal.on_this_server": "ในเซิร์ฟเวอร์นี้", "interaction_modal.title.favourite": "ชื่นชอบโพสต์ของ {name}", @@ -407,6 +414,7 @@ "interaction_modal.title.reblog": "ดันโพสต์ของ {name}", "interaction_modal.title.reply": "ตอบกลับโพสต์ของ {name}", "interaction_modal.title.vote": "ลงคะแนนในการสำรวจความคิดเห็นของ {name}", + "interaction_modal.username_prompt": "เช่น {example}", "intervals.full.days": "{number, plural, other {# วัน}}", "intervals.full.hours": "{number, plural, other {# ชั่วโมง}}", "intervals.full.minutes": "{number, plural, other {# นาที}}", @@ -442,6 +450,7 @@ "keyboard_shortcuts.toggle_hidden": "แสดง/ซ่อนข้อความที่อยู่หลังคำเตือนเนื้อหา", "keyboard_shortcuts.toggle_sensitivity": "แสดง/ซ่อนสื่อ", "keyboard_shortcuts.toot": "เริ่มโพสต์ใหม่", + "keyboard_shortcuts.translate": "เพื่อแปลโพสต์", "keyboard_shortcuts.unfocus": "เลิกโฟกัสพื้นที่เขียนข้อความ/การค้นหา", "keyboard_shortcuts.up": "ย้ายขึ้นในรายการ", "lightbox.close": "ปิด", @@ -679,6 +688,8 @@ "privacy_policy.title": "นโยบายความเป็นส่วนตัว", "recommended": "แนะนำ", "refresh": "รีเฟรช", + "regeneration_indicator.please_stand_by": "โปรดรอสักครู่", + "regeneration_indicator.preparing_your_home_feed": "กำลังเตรียมฟีดหน้าแรกของคุณ…", "relative_time.days": "{number} วัน", "relative_time.full.days": "{number, plural, other {# วัน}}ที่แล้ว", "relative_time.full.hours": "{number, plural, other {# ชั่วโมง}}ที่แล้ว", @@ -817,6 +828,7 @@ "status.reblogs.empty": "ยังไม่มีใครดันโพสต์นี้ เมื่อใครสักคนดัน เขาจะปรากฏที่นี่", "status.redraft": "ลบแล้วร่างใหม่", "status.remove_bookmark": "เอาที่คั่นหน้าออก", + "status.remove_favourite": "เอาออกจากรายการโปรด", "status.replied_in_thread": "ตอบกลับในกระทู้", "status.replied_to": "ตอบกลับ {name}", "status.reply": "ตอบกลับ", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index d57bf5e22f..9ce76a03d5 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "CW'den önceki yazıyı göstermek/gizlemek için", "keyboard_shortcuts.toggle_sensitivity": "Medyayı göstermek/gizlemek için", "keyboard_shortcuts.toot": "Yeni bir gönderi başlat", + "keyboard_shortcuts.translate": "bir gönderiyi çevirmek için", "keyboard_shortcuts.unfocus": "Aramada bir gönderiye odaklanmamak için", "keyboard_shortcuts.up": "Listede yukarıya çıkmak için", "lightbox.close": "Kapat", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 9d20c27fd0..af72998fd9 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -103,6 +103,7 @@ "annual_report.summary.most_used_hashtag.most_used_hashtag": "найчастіший хештег", "annual_report.summary.most_used_hashtag.none": "Немає", "annual_report.summary.new_posts.new_posts": "нові дописи", + "annual_report.summary.percentile.text": "Це виводить вас у топ користувачів Mastodon.", "annual_report.summary.percentile.we_wont_tell_bernie": "Ми не скажемо Bernie.", "annual_report.summary.thanks": "Дякуємо, що ви є частиною Mastodon!", "attachments_list.unprocessed": "(не оброблено)", @@ -148,6 +149,7 @@ "column.firehose": "Стрічка новин", "column.follow_requests": "Запити на підписку", "column.home": "Головна", + "column.list_members": "Керувати учасниками списку", "column.lists": "Списки", "column.mutes": "Приховані користувачі", "column.notifications": "Сповіщення", @@ -237,6 +239,10 @@ "disabled_account_banner.text": "Ваш обліковий запис {disabledAccount} наразі вимкнений.", "dismissable_banner.community_timeline": "Це останні публічні дописи від людей, чиї облікові записи розміщені на {domain}.", "dismissable_banner.dismiss": "Відхилити", + "dismissable_banner.explore_links": "Ці новини сьогодні найбільше поширюють у fediverse. Свіжіші новини, опубліковані більшою кількістю різних людей, оцінюються вище.", + "dismissable_banner.explore_statuses": "Ці дописи з усього fediverse сьогодні набирають популярності. Новіші дописи з більшою кількістю посилень і додавань у вибрані мають вищий рейтинг.", + "dismissable_banner.explore_tags": "Ці гештеґи сьогодні набувають популярності у fediverse. Гештеґи, якими користується більше людей, займають вищі позиції.", + "dismissable_banner.public_timeline": "Це найновіші загальнодоступні дописи від людей у федіверсі, на яких підписані люди в {domain}.", "domain_block_modal.block": "Блокувати сервер", "domain_block_modal.block_account_instead": "Блокувати @{name} натомість", "domain_block_modal.they_can_interact_with_old_posts": "Люди з цього сервера можуть взаємодіяти зі своїми старими дописами.", @@ -325,7 +331,7 @@ "filter_modal.select_filter.title": "Фільтрувати цей допис", "filter_modal.title.status": "Фільтрувати допис", "filter_warning.matches_filter": "Збігається з фільтром “{title}”", - "filtered_notifications_banner.pending_requests": "Від {count, plural, =0 {жодної особи} one {однієї особи} few {# осіб} many {# осіб} other {# особи}}, котрих ви можете знати", + "filtered_notifications_banner.pending_requests": "Від {count, plural, =0 {жодної особи} one {однієї особи} few {# осіб} many {# осіб} other {# особи}}, яких ви можете знати", "filtered_notifications_banner.title": "Відфільтровані сповіщення", "firehose.all": "Всі", "firehose.local": "Цей сервер", @@ -402,6 +408,10 @@ "ignore_notifications_modal.not_following_title": "Ігнорувати сповіщення від людей, на яких ви не підписалися?", "ignore_notifications_modal.private_mentions_title": "Ігнорувати сповіщення від небажаних приватних згадок?", "interaction_modal.action.favourite": "Щоб продовжити, потрібно додати улюблене з вашого облікового запису.", + "interaction_modal.action.follow": "Щоб іти далі, потрібно підписатися з вашого облікового запису.", + "interaction_modal.action.reblog": "Щоб іти далі, потрібно зробити реблог з вашого облікового запису.", + "interaction_modal.action.reply": "Щоб іти далі, потрібно відповісти з вашого облікового запису.", + "interaction_modal.action.vote": "Щоб іти далі, потрібно проголосувати з вашим обліковим записом.", "interaction_modal.go": "Вперед", "interaction_modal.no_account_yet": "Ще не зареєстровані?", "interaction_modal.on_another_server": "На іншому сервері", @@ -411,6 +421,7 @@ "interaction_modal.title.reblog": "Поширити допис {name}", "interaction_modal.title.reply": "Відповісти на допис {name}", "interaction_modal.title.vote": "Проголосувати в опитуванні {name}", + "interaction_modal.username_prompt": "Наприклад, %{example}", "intervals.full.days": "{number, plural, one {# день} few {# дні} other {# днів}}", "intervals.full.hours": "{number, plural, one {# година} few {# години} other {# годин}}", "intervals.full.minutes": "{number, plural, one {# хвилина} few {# хвилини} other {# хвилин}}", @@ -446,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Показати/приховати текст під попередженням про вміст", "keyboard_shortcuts.toggle_sensitivity": "Показати/приховати медіа", "keyboard_shortcuts.toot": "Створити новий допис", + "keyboard_shortcuts.translate": "для перекладу повідомлення", "keyboard_shortcuts.unfocus": "Розфокусуватися з нового допису чи пошуку", "keyboard_shortcuts.up": "Рухатися вгору списком", "lightbox.close": "Закрити", @@ -458,12 +470,32 @@ "link_preview.author": "Від {name}", "link_preview.more_from_author": "Більше від {name}", "link_preview.shares": "{count, plural, one {{counter} допис} few {{counter} дописи} many {{counter} дописів} other {{counter} допис}}", + "lists.add_member": "Додати", + "lists.add_to_list": "Додати до списку", + "lists.add_to_lists": "Додати {name} до списку", + "lists.create": "Створити", + "lists.create_a_list_to_organize": "Створіть новий список, щоб упорядкувати домашню стрічку", + "lists.create_list": "Створити список", "lists.delete": "Видалити список", + "lists.done": "Готово", "lists.edit": "Редагувати список", + "lists.exclusive": "Сховати учасників на головній сторінці", + "lists.exclusive_hint": "Якщо хтось є у цьому списку, сховайте їх на своїй домашній сторінці, щоб не бачити їхні дописи двічі.", + "lists.find_users_to_add": "Знайти користувачів, щоб додати їх", + "lists.list_members": "Учасники списку", + "lists.list_members_count": "{count, plural, one {# member} other {# members}}", + "lists.list_name": "Назва списку", + "lists.new_list_name": "Нова назва списку", + "lists.no_lists_yet": "Поки що немає списків.", + "lists.no_members_yet": "Ще немає учасників.", + "lists.no_results_found": "Результатів не знайдено.", + "lists.remove_member": "Видалити", "lists.replies_policy.followed": "Будь-який відстежуваний користувач", "lists.replies_policy.list": "Учасники списку", "lists.replies_policy.none": "Ніхто", + "lists.save": "Зберегти", "lists.search": "Пошук", + "lists.show_replies_to": "Включати відповіді також зі списку учасників", "load_pending": "{count, plural, one {# новий елемент} other {# нових елементів}}", "loading_indicator.label": "Завантаження…", "media_gallery.hide": "Сховати", @@ -516,6 +548,8 @@ "notification.annual_report.view": "Переглянути #Wrapstodon", "notification.favourite": "Ваш допис сподобався {name}", "notification.favourite.name_and_others_with_link": "{name} та {count, plural, one {# інший} few {# інших} many {# інших} other {# інший}} вподобали ваш допис", + "notification.favourite_pm": "{name} додав вашу особисту згадку до вибраного", + "notification.favourite_pm.name_and_others_with_link": "{name} та {count, plural, one {# other} other {# others}} додали вашу особисту згадку до вибраного", "notification.follow": "{name} підписалися на вас", "notification.follow.name_and_others": "{name} та {count, plural, one {# інший} few {# інших} many {# інших} other {# інший}} стежать за вами", "notification.follow_request": "{name} відправили запит на підписку", @@ -663,6 +697,7 @@ "recommended": "Рекомендовано", "refresh": "Оновити", "regeneration_indicator.please_stand_by": "Будь ласка, очікуйте.", + "regeneration_indicator.preparing_your_home_feed": "Готування вашої головної стрічки новин…", "relative_time.days": "{number}д", "relative_time.full.days": "{number, plural, one {# день} few {# дні} other {# днів}} тому", "relative_time.full.hours": "{number, plural, one {# година} few {# години} other {# годин}} тому", @@ -824,6 +859,7 @@ "subscribed_languages.target": "Змінити підписані мови для {target}", "tabs_bar.home": "Головна", "tabs_bar.notifications": "Сповіщення", + "terms_of_service.title": "Умови використання", "time_remaining.days": "{number, plural, one {# день} few {# дні} other {# днів}}", "time_remaining.hours": "{number, plural, one {# година} few {# години} other {# годин}}", "time_remaining.minutes": "{number, plural, one {# хвилина} few {# хвилини} other {# хвилин}}", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index d8314de9fc..834c74eb77 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "ẩn/hiện nội dung ẩn", "keyboard_shortcuts.toggle_sensitivity": "ẩn/hiện ảnh hoặc video", "keyboard_shortcuts.toot": "soạn tút mới", + "keyboard_shortcuts.translate": "dịch tút", "keyboard_shortcuts.unfocus": "đưa con trỏ ra khỏi ô soạn thảo hoặc ô tìm kiếm", "keyboard_shortcuts.up": "di chuyển lên trên danh sách", "lightbox.close": "Đóng", @@ -836,6 +837,7 @@ "status.reblogs.empty": "Tút này chưa có ai đăng lại. Nếu có, nó sẽ hiển thị ở đây.", "status.redraft": "Xóa và viết lại", "status.remove_bookmark": "Bỏ lưu", + "status.remove_favourite": "Bỏ thích", "status.replied_in_thread": "Trả lời thảo luận", "status.replied_to": "Trả lời {name}", "status.reply": "Trả lời", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 3e4530f44b..558df23eb0 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -186,7 +186,7 @@ "compose_form.poll.switch_to_single": "将投票改为单选", "compose_form.poll.type": "类型", "compose_form.publish": "发布", - "compose_form.publish_form": "新嘟文", + "compose_form.publish_form": "新建嘟文", "compose_form.reply": "回复", "compose_form.save_changes": "更改", "compose_form.spoiler.marked": "移除内容警告", @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "显示或隐藏被折叠的正文", "keyboard_shortcuts.toggle_sensitivity": "显示/隐藏媒体", "keyboard_shortcuts.toot": "发送新嘟文", + "keyboard_shortcuts.translate": "翻译嘟文", "keyboard_shortcuts.unfocus": "取消输入/搜索", "keyboard_shortcuts.up": "在列表中让光标上移", "lightbox.close": "关闭", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 05f23a43ea..0010c687c0 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "顯示或隱藏於內容警告之後的嘟文", "keyboard_shortcuts.toggle_sensitivity": "顯示或隱藏媒體", "keyboard_shortcuts.toot": "發個新嘟文", + "keyboard_shortcuts.translate": "翻譯嘟文", "keyboard_shortcuts.unfocus": "跳離文字撰寫區塊或搜尋框", "keyboard_shortcuts.up": "向上移動", "lightbox.close": "關閉", diff --git a/config/locales/activerecord.bg.yml b/config/locales/activerecord.bg.yml index 198029ac0c..68c7369033 100644 --- a/config/locales/activerecord.bg.yml +++ b/config/locales/activerecord.bg.yml @@ -24,6 +24,8 @@ bg: models: account: attributes: + fields: + fields_with_values_missing_labels: съдържа стойности с липсващи етикети username: invalid: трябва да е само буквено-цифрено и долни черти reserved: е запазено diff --git a/config/locales/activerecord.ca.yml b/config/locales/activerecord.ca.yml index ad7c63b387..09b6366b55 100644 --- a/config/locales/activerecord.ca.yml +++ b/config/locales/activerecord.ca.yml @@ -24,6 +24,8 @@ ca: models: account: attributes: + fields: + fields_with_values_missing_labels: conté valors sense etiqueta username: invalid: només pot contenir lletres, números i guions baixos reserved: està reservat diff --git a/config/locales/activerecord.da.yml b/config/locales/activerecord.da.yml index 56dd3aa2c0..e611442a1b 100644 --- a/config/locales/activerecord.da.yml +++ b/config/locales/activerecord.da.yml @@ -24,6 +24,8 @@ da: models: account: attributes: + fields: + fields_with_values_missing_labels: indeholder værdier med manglende etiketter username: invalid: må kun indeholde cifre, bogstaver og understreger reserved: er reserveret diff --git a/config/locales/activerecord.eo.yml b/config/locales/activerecord.eo.yml index d07a717d31..f5e291be8f 100644 --- a/config/locales/activerecord.eo.yml +++ b/config/locales/activerecord.eo.yml @@ -24,6 +24,8 @@ eo: models: account: attributes: + fields: + fields_with_values_missing_labels: enhavas valorojn kun mankantaj etikedoj username: invalid: devas enhavi nur literojn, ciferojn kaj substrekojn reserved: rezervita diff --git a/config/locales/activerecord.es-AR.yml b/config/locales/activerecord.es-AR.yml index b8e2b57c86..bc88933734 100644 --- a/config/locales/activerecord.es-AR.yml +++ b/config/locales/activerecord.es-AR.yml @@ -24,6 +24,8 @@ es-AR: models: account: attributes: + fields: + fields_with_values_missing_labels: contiene valores con etiquetas faltantes username: invalid: sólo letras, números y subguiones ("_") reserved: está reservado diff --git a/config/locales/activerecord.es-MX.yml b/config/locales/activerecord.es-MX.yml index f5c8080189..c2d27a65b8 100644 --- a/config/locales/activerecord.es-MX.yml +++ b/config/locales/activerecord.es-MX.yml @@ -24,6 +24,8 @@ es-MX: models: account: attributes: + fields: + fields_with_values_missing_labels: contiene valores a los que les faltan etiquetas username: invalid: debe contener sólo letras, números y guiones bajos reserved: está reservado diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index 081f87ef15..d7b25a9042 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -24,6 +24,8 @@ es: models: account: attributes: + fields: + fields_with_values_missing_labels: contiene valores con etiquetas que faltan username: invalid: solo puede contener letras, números y guiones bajos reserved: está reservado diff --git a/config/locales/activerecord.fi.yml b/config/locales/activerecord.fi.yml index e64c2fb80e..bf98beb6a3 100644 --- a/config/locales/activerecord.fi.yml +++ b/config/locales/activerecord.fi.yml @@ -24,6 +24,8 @@ fi: models: account: attributes: + fields: + fields_with_values_missing_labels: sisältää arvoja, joista puuttuu nimike username: invalid: saa sisältää vain kirjaimia, numeroita ja alaviivoja reserved: on varattu diff --git a/config/locales/activerecord.fil.yml b/config/locales/activerecord.fil.yml index 4084bf2f90..5250a30bdb 100644 --- a/config/locales/activerecord.fil.yml +++ b/config/locales/activerecord.fil.yml @@ -1 +1,9 @@ +--- fil: + activerecord: + errors: + models: + account: + attributes: + fields: + fields_with_values_missing_labels: may mga value na walang label diff --git a/config/locales/activerecord.fo.yml b/config/locales/activerecord.fo.yml index 3491480110..c419173ef0 100644 --- a/config/locales/activerecord.fo.yml +++ b/config/locales/activerecord.fo.yml @@ -24,6 +24,8 @@ fo: models: account: attributes: + fields: + fields_with_values_missing_labels: inniheldur virði við manglandi spjøldrum username: invalid: kann bara innihalda bókstavir, tøl og botnstriku reserved: er umbiðið diff --git a/config/locales/activerecord.fr-CA.yml b/config/locales/activerecord.fr-CA.yml index 7699adc43a..41ac187b94 100644 --- a/config/locales/activerecord.fr-CA.yml +++ b/config/locales/activerecord.fr-CA.yml @@ -24,6 +24,8 @@ fr-CA: models: account: attributes: + fields: + fields_with_values_missing_labels: contient des valeurs avec des étiquettes manquantes username: invalid: doit ne contenir que des lettres, des nombres et des tirets bas reserved: est réservé diff --git a/config/locales/activerecord.fr.yml b/config/locales/activerecord.fr.yml index 4235404859..e6d5bc1add 100644 --- a/config/locales/activerecord.fr.yml +++ b/config/locales/activerecord.fr.yml @@ -24,6 +24,8 @@ fr: models: account: attributes: + fields: + fields_with_values_missing_labels: contient des valeurs avec des étiquettes manquantes username: invalid: seulement des lettres, des chiffres et des tirets bas reserved: est réservé diff --git a/config/locales/activerecord.he.yml b/config/locales/activerecord.he.yml index 3696b4ce18..b63b41c74e 100644 --- a/config/locales/activerecord.he.yml +++ b/config/locales/activerecord.he.yml @@ -24,6 +24,8 @@ he: models: account: attributes: + fields: + fields_with_values_missing_labels: שדות המכילים ערכים אך חסרים תווית username: invalid: ספרות, אותיות לטיניות וקו תחתי בלבד reserved: שמור diff --git a/config/locales/activerecord.is.yml b/config/locales/activerecord.is.yml index 4d90a7d808..966d8fa9e8 100644 --- a/config/locales/activerecord.is.yml +++ b/config/locales/activerecord.is.yml @@ -24,6 +24,8 @@ is: models: account: attributes: + fields: + fields_with_values_missing_labels: inniheldur gildi sem vantar merkingar username: invalid: má aðeins innihalda bókstafi, tölur og undirstrik reserved: er frátekið diff --git a/config/locales/activerecord.lt.yml b/config/locales/activerecord.lt.yml index 86c9be5218..5171249d57 100644 --- a/config/locales/activerecord.lt.yml +++ b/config/locales/activerecord.lt.yml @@ -24,6 +24,8 @@ lt: models: account: attributes: + fields: + fields_with_values_missing_labels: turi reikšmių su trūkstamomis etiketėmis. username: invalid: turi būti tik raidės, skaičiai ir pabraukimai. reserved: užimtas. diff --git a/config/locales/activerecord.nan.yml b/config/locales/activerecord.nan.yml index 512c65fe8b..b1e9dc67bf 100644 --- a/config/locales/activerecord.nan.yml +++ b/config/locales/activerecord.nan.yml @@ -1 +1,16 @@ +--- nan: + activerecord: + attributes: + poll: + expires_at: 期限 + options: 選項 + user: + agreement: 服務協議 + email: 電子phue地址 + locale: 在地化 + password: 密碼 + user/account: + username: 用者ê名 + user/invite_request: + text: 原因 diff --git a/config/locales/activerecord.nl.yml b/config/locales/activerecord.nl.yml index b25da381f2..530e7187d4 100644 --- a/config/locales/activerecord.nl.yml +++ b/config/locales/activerecord.nl.yml @@ -24,6 +24,8 @@ nl: models: account: attributes: + fields: + fields_with_values_missing_labels: bevat waarden met ontbrekende labels username: invalid: alleen letters, nummers en underscores reserved: gereserveerd diff --git a/config/locales/activerecord.nn.yml b/config/locales/activerecord.nn.yml index 0fe0620522..2b5b6dd581 100644 --- a/config/locales/activerecord.nn.yml +++ b/config/locales/activerecord.nn.yml @@ -24,6 +24,8 @@ nn: models: account: attributes: + fields: + fields_with_values_missing_labels: inneheld verdiar med manglande etikettar username: invalid: kan berre innehalda bokstavar, tal og understrekar reserved: er reservert diff --git a/config/locales/activerecord.sq.yml b/config/locales/activerecord.sq.yml index 9e3780668a..7e53b36b87 100644 --- a/config/locales/activerecord.sq.yml +++ b/config/locales/activerecord.sq.yml @@ -24,6 +24,8 @@ sq: models: account: attributes: + fields: + fields_with_values_missing_labels: përmban vlera me etiketa që mungojnë username: invalid: duhet të përmbajë vetëm shkronja, numra dhe nënvija reserved: është i rezervuar diff --git a/config/locales/activerecord.sv.yml b/config/locales/activerecord.sv.yml index 6ac96d9ea9..f05161992c 100644 --- a/config/locales/activerecord.sv.yml +++ b/config/locales/activerecord.sv.yml @@ -39,6 +39,11 @@ sv: attributes: data: malformed: är felformad + list_account: + attributes: + account_id: + taken: finns redan i listan + must_be_following: måste vara ett följt konto status: attributes: reblog: diff --git a/config/locales/activerecord.th.yml b/config/locales/activerecord.th.yml index e1021b8afa..6575d30a22 100644 --- a/config/locales/activerecord.th.yml +++ b/config/locales/activerecord.th.yml @@ -39,6 +39,11 @@ th: attributes: data: malformed: ผิดรูปแบบ + list_account: + attributes: + account_id: + taken: อยู่ในรายการอยู่แล้ว + must_be_following: ต้องเป็นบัญชีที่ติดตาม status: attributes: reblog: diff --git a/config/locales/activerecord.tr.yml b/config/locales/activerecord.tr.yml index e780e366b9..095aa72e67 100644 --- a/config/locales/activerecord.tr.yml +++ b/config/locales/activerecord.tr.yml @@ -24,6 +24,8 @@ tr: models: account: attributes: + fields: + fields_with_values_missing_labels: değerleri eksik etiketler içeriyor username: invalid: sadece harfler, sayılar ve alt çizgiler reserved: kullanılamaz diff --git a/config/locales/activerecord.uk.yml b/config/locales/activerecord.uk.yml index 118ca7b8fd..3e82b5ac3d 100644 --- a/config/locales/activerecord.uk.yml +++ b/config/locales/activerecord.uk.yml @@ -24,6 +24,8 @@ uk: models: account: attributes: + fields: + fields_with_values_missing_labels: містить значення з відсутніми мітками username: invalid: має містити лише літери, цифри та підкреслення reserved: зарезервовано @@ -43,6 +45,7 @@ uk: attributes: account_id: taken: вже в списку + must_be_following: має бути відстежуваним обліковим записом status: attributes: reblog: diff --git a/config/locales/activerecord.vi.yml b/config/locales/activerecord.vi.yml index 878ba2770f..16f106a8bb 100644 --- a/config/locales/activerecord.vi.yml +++ b/config/locales/activerecord.vi.yml @@ -24,6 +24,8 @@ vi: models: account: attributes: + fields: + fields_with_values_missing_labels: chứa giá trị thiếu nhãn username: invalid: chỉ chấp nhận ký tự, số và dấu gạch dưới reserved: bị cấm sử dụng diff --git a/config/locales/activerecord.zh-TW.yml b/config/locales/activerecord.zh-TW.yml index aaa7007311..29c2c86af0 100644 --- a/config/locales/activerecord.zh-TW.yml +++ b/config/locales/activerecord.zh-TW.yml @@ -24,6 +24,8 @@ zh-TW: models: account: attributes: + fields: + fields_with_values_missing_labels: 包含缺少標籤之值 username: invalid: 只能有字母、數字及底線 reserved: 是保留關鍵字 diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 5c7943bc1c..c9deb24ee1 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -25,15 +25,19 @@ cs: one: Příspěvek other: Příspěvků posts_tab_heading: Příspěvky + self_follow_error: Sledování vašeho vlastního účtu není povoleno admin: account_actions: action: Vykonat akci + already_silenced: Tento účet je již omezený. + already_suspended: Tento účet již byl pozastaven. title: Vykonat moderátorskou akci pro účet %{acct} account_moderation_notes: create: Zanechat poznámku created_msg: Moderátorská poznámka byla úspěšně vytvořena! destroyed_msg: Moderátorská poznámka byla úspěšně zničena! accounts: + add_email_domain_block: Blokovat e-mailovou doménu approve: Schválit approved_msg: Žádost o registraci uživatele %{username} úspěšně schválena are_you_sure: Opravdu? @@ -48,6 +52,7 @@ cs: title: Změnit e-mail uživateli %{username} change_role: changed_msg: Role úspěšně změněna! + edit_roles: Správa uživatelských rolí label: Změnit roli no_role: Žádná role title: Změnit roli pro %{username} @@ -60,6 +65,7 @@ cs: demote: Degradovat destroyed_msg: Data účtu %{username} jsou nyní ve frontě k okamžitému smazání disable: Zmrazit + disable_sign_in_token_auth: Zrušit ověřování e-mailovým tokenem disable_two_factor_authentication: Vypnout 2FA disabled: Zmrazen display_name: Zobrazované jméno @@ -68,6 +74,7 @@ cs: email: E-mail email_status: Stav e-mailu enable: Rozmrazit + enable_sign_in_token_auth: Povolit ověřování e-mailovým tokenem enabled: Povoleno enabled_msg: Účet %{username} byl úspěšně rozmrazen followers: Sledující @@ -134,6 +141,7 @@ cs: resubscribe: Znovu odebírat role: Role search: Hledat + search_same_email_domain: Ostatní uživatelé se stejnou e-mailovou doménou search_same_ip: Další uživatelé se stejnou IP adresou security: Zabezpečení security_measures: @@ -174,6 +182,7 @@ cs: approve_appeal: Schválit odvolání approve_user: Schválit uživatele assigned_to_self_report: Přiřadit hlášení + change_email_user: Změnit e-mail uživatele change_role_user: Změnit roli uživatele confirm_user: Potvrdit uživatele create_account_warning: Vytvořit varování @@ -435,8 +444,10 @@ cs: new: create: Přidat doménu resolve: Přeložit doménu + title: Blokovat novou e-mailovou doménu not_permitted: Nepovoleno resolved_through_html: Přeložena přes %{domain} + title: Blokované e-mailové domény export_domain_allows: new: title: Importovat povolené domény @@ -813,6 +824,7 @@ cs: batch: remove_from_report: Odebrat z hlášení report: Nahlásit + contents: Obsah deleted: Smazáno favourites: Oblíbené history: Historie verzí diff --git a/config/locales/devise.nan.yml b/config/locales/devise.nan.yml index 512c65fe8b..6f47b4f652 100644 --- a/config/locales/devise.nan.yml +++ b/config/locales/devise.nan.yml @@ -1 +1,15 @@ +--- nan: + devise: + failure: + locked: Lí ê口座hőng鎖定ah。 + not_found_in_database: Bô ha̍p規定ê %{authentication_keys} á是密碼。 + pending: Lí ê口座iáu teh審查。 + timeout: Lí ê作業階段kàu期ah。請koh登入,繼續完成。 + mailer: + two_factor_disabled: + title: 2FA關掉ah + two_factor_enabled: + title: 2FA啟用ah + two_factor_recovery_codes_changed: + title: 2FA驗證碼改ah diff --git a/config/locales/doorkeeper.cs.yml b/config/locales/doorkeeper.cs.yml index 882be66ee0..1ee73c2cb9 100644 --- a/config/locales/doorkeeper.cs.yml +++ b/config/locales/doorkeeper.cs.yml @@ -60,6 +60,7 @@ cs: error: title: Vyskytla se chyba new: + prompt_html: "%{client_name} by chtěl oprávnění k přístupu k vašemu účtu. Schvalte tuto žádost pouze pokud rozpoznáte a důvěřujete tomuto zdroji." review_permissions: Zkontrolujte oprávnění title: Je vyžadována autorizace show: diff --git a/config/locales/doorkeeper.nan.yml b/config/locales/doorkeeper.nan.yml index 512c65fe8b..554d991641 100644 --- a/config/locales/doorkeeper.nan.yml +++ b/config/locales/doorkeeper.nan.yml @@ -1 +1,17 @@ +--- nan: + activerecord: + attributes: + doorkeeper/application: + name: 應用程式ê名 + redirect_uri: 重轉ê URI + scopes: 範圍 + website: 應用程式ê網站 + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + invalid_uri: Tio̍h愛是合規定ê URI。 + relative_uri: Tio̍h愛是絕對ê URI。 + secured_uri: Tio̍h愛是HTTPS/SSL URI。 diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 2102e84c99..0f757c6370 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -912,6 +912,11 @@ lv: search: Meklēt title: Tēmturi updated_msg: Tēmtura iestatījumi ir veiksmīgi atjaunināti + terms_of_service: + changelog: Kas ir mainījies + history: Vēsture + publish: Publicēt + published_on_html: Publicēts %{date} title: Pārvaldība trends: allow: Atļaut @@ -1609,6 +1614,7 @@ lv: scheduled_statuses: over_daily_limit: Tu esi pārsniedzis šodien ieplānoto %{limit} ziņu ierobežojumu over_total_limit: Tu esi pārsniedzis ieplānoto %{limit} ziņu ierobežojumu + too_soon: datumam jābūt nākotnē self_destruct: lead_html: Diemžēl domēns %{domain} tiek neatgriezeniski slēgts. Ja tev tur bija konts, tu nevarēsi turpināt to lietot, taču joprojām vari pieprasīt savu datu kopiju. title: Šis serveris tiek slēgts diff --git a/config/locales/nan.yml b/config/locales/nan.yml index d46de7249c..9180b7b064 100644 --- a/config/locales/nan.yml +++ b/config/locales/nan.yml @@ -1,6 +1,44 @@ --- nan: + about: + about_mastodon_html: 社交網路ê未來:Bô廣告、bô企業監控、設計有道德,兼非中心化!加入Mastodon,保有lí ê資料! + contact_missing: Iáu bē設定 + contact_unavailable: 無開放 + hosted_on: 佇 %{domain} 運作 ê Mastodon站 + title: 關係本站 + accounts: + followers: + other: 跟tuè ê + following: Leh跟tuè + last_active: 頂kái活動ê時間 + link_verified_on: Tsit ê連結ê所有權佇 %{date} 受檢查 + posts: + other: PO文 + posts_tab_heading: PO文 admin: + account_moderation_notes: + create: 留記錄 + created_msg: 管理記錄成功建立! + destroyed_msg: 管理記錄成功thâi掉! + accounts: + deleted: Thâi掉ah + demote: 降級 + destroyed_msg: Teh-beh thâi掉 %{username} ê資料 + disable: 冷凍 + disable_sign_in_token_auth: 停止用電子phue ê token認證 + disable_two_factor_authentication: 停止用2FA + disabled: 冷凍起來ah + display_name: 顯示ê名 + domain: 域名 + edit: 編輯 + email: 電子phue箱 + email_status: 電子phue ê狀態 + enable: 取消冷凍 + location: + all: Kui ê + local: 本地 + remote: 別ê站 + title: 位置 instances: dashboard: instance_languages_dimension: Tsia̍p用ê語言 @@ -14,3 +52,6 @@ nan: too_soon: Tio̍h用未來ê日期。 statuses: default_language: Kap界面ê語言sio kâng + user_mailer: + welcome: + sign_in_action: 登入 diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml index ef82b3232a..0bfeacd9cd 100644 --- a/config/locales/simple_form.cs.yml +++ b/config/locales/simple_form.cs.yml @@ -127,6 +127,9 @@ cs: show_application: I tak budete vždy moci vidět, která aplikace zveřejnila váš příspěvek. tag: name: Můžete měnit pouze velikost písmen, například kvůli lepší čitelnosti + terms_of_service: + changelog: Může být strukturováno pomocí Markdown syntaxu. + text: Může být strukturováno pomocí Markdown syntaxu. user: chosen_languages: Po zaškrtnutí budou ve veřejných časových osách zobrazeny pouze příspěvky ve zvolených jazycích user_role: @@ -313,6 +316,17 @@ cs: name: Hashtag trendable: Povolit zobrazení tohoto hashtagu mezi populárními usable: Povolit příspěvkům používat tento hashtag lokálně + terms_of_service: + changelog: Co se změnilo? + text: Podmínky užití + terms_of_service_generator: + admin_email: E-mailová adresa pro právní upozornění + arbitration_address: Fyzická adresa pro upozornění na arbitrační řízení + arbitration_website: Webová stránka pro zasílání arbitračních upozornění + dmca_address: Fyzická adresa pro oznámení DMCA/porušení autorských práv + dmca_email: E-mailová adresa pro oznámení DMCA/porušení autorských práv + domain: Doména + jurisdiction: Právní příslušnost user: role: Role time_zone: Časové pásmo diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index a741040477..be2ed2135e 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -130,6 +130,16 @@ eo: show_application: Vi ĉiam povos vidi kiu aplikaĵo publikigis vian afiŝon ĉiaokaze. tag: name: Vi povas ŝanĝi nur la majuskladon de la literoj, ekzemple, por igi ĝin pli legebla + terms_of_service: + changelog: Povas esti strukturita per sintakso Markdown-a. + text: Povas esti strukturita per sintakso Markdown-a. + terms_of_service_generator: + admin_email: Legalaj sciigoj povas esti kontraŭsciigoj, postulaĵoj de tribunalo, postulaĵoj pri forigo, kaj postulaĵoj de la policaro. + arbitration_address: Povas esti la sama kiel Fizika adreso supre, aŭ "N/A" se oni uzas retpoŝton + arbitration_website: Povas esti retformo, aŭ "N/A" se oni uzas retpoŝton + dmca_address: Por tenantoj en Usono, uzu la adreson registritan en la DMCA Designated Agenŭ Directory. Registrolibro de poŝtskatoloj haveblas per direkta postulo, uzu la DMCA Designated Agent Post Office Box Waiver Request por retpoŝti la Ofico de Kopirajto kaj priskribu, ke vi estas hejm-trovigita administranto por enhavo kaj devas uzi Poŝtskatolon por forigi vian hejmadreson de publika vido. + dmca_email: Povas esti la sama retpoŝtadreso uzita por "Retpoŝtadreso por legalaj sciigoj" supre + domain: Unika identigilo de la retaj servicoj, ke vi provizas. user: chosen_languages: Kun tio markita nur mesaĝoj en elektitaj lingvoj aperos en publikaj tempolinioj role: La rolo kontrolas kiujn permesojn la uzanto havas. diff --git a/config/locales/simple_form.nan.yml b/config/locales/simple_form.nan.yml index 512c65fe8b..d9049a784b 100644 --- a/config/locales/simple_form.nan.yml +++ b/config/locales/simple_form.nan.yml @@ -1 +1,11 @@ +--- nan: + simple_form: + hints: + account: + display_name: Lí ê全名á是別號。 + fields: Lí ê頭頁、代名詞、年歲,kap其他beh分享ê。 + defaults: + password: 用 8 ê字元以上 + setting_display_media_hide_all: 一直khàm掉媒體 + setting_display_media_show_all: 一直展示媒體 diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 72c3f000f6..0815170fcc 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -60,6 +60,7 @@ sv: setting_display_media_default: Dölj media markerad som känslig setting_display_media_hide_all: Dölj alltid all media setting_display_media_show_all: Visa alltid media markerad som känslig + setting_system_scrollbars_ui: Gäller endast för webbläsare som är baserade på Safari och Chrome setting_use_blurhash: Gradienter är baserade på färgerna av de dolda objekten men fördunklar alla detaljer setting_use_pending_items: Dölj tidslinjeuppdateringar bakom ett klick istället för att automatiskt bläddra i flödet username: Du kan använda bokstäver, siffror och understreck diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml index 49f191bcdd..9ecd12517b 100644 --- a/config/locales/simple_form.uk.yml +++ b/config/locales/simple_form.uk.yml @@ -130,6 +130,17 @@ uk: show_application: Ви завжди зможете побачити, з якого застосунку опубліковано ваш допис. tag: name: Тут ви можете лише змінювати регістр літер, щоб підвищити читабельність + terms_of_service: + changelog: Можна структурувати за допомогою синтаксису Markdown. + text: Можна структурувати за допомогою синтаксису Markdown. + terms_of_service_generator: + admin_email: Юридичні повідомлення містять зустрічні повідомлення, ухвали суду, запити на видалення та запити правоохоронних органів. + arbitration_address: Може бути таким самим, як і фізична адреса вище, або "N/A", якщо використано електронну пошту + arbitration_website: Може бути вебформою або "N/A", якщо використано електронну пошту + dmca_address: Для американських операторів використовуйте адресу, зареєстровану в довіднику призначених агентів DMCA. П.О. Перелік скриньок доступний за прямим запитом. Скористайтеся запитом на відмову від поштової скриньки призначеного агента Закону про захист авторських прав у цифрову епоху, щоб надіслати електронний лист до Бюро авторських прав, і опишіть, що ви домашній модератор вмісту, який боїться помсти чи відплати за свої дії та потребує використання P.O. Box, щоб видалити вашу домашню адресу з публічного перегляду. + dmca_email: Це може бути та сама адреса електронної пошти, яку використано в розділі «Електронна адреса для юридичних повідомлень» вище + domain: Унікальна ідентифікація онлайн-сервісу, який ви надаєте. + jurisdiction: Укажіть країну, де живе той, хто платить за рахунками. Якщо це компанія чи інша організація, вкажіть країну, де вона зареєстрована, а також місто, регіон, територію чи штат відповідно. user: chosen_languages: У глобальних стрічках будуть показані дописи тільки вибраними мовами role: Роль визначає, які права має користувач. @@ -214,7 +225,7 @@ uk: setting_default_privacy: Видимість дописів setting_default_sensitive: Позначати медіа делікатними setting_delete_modal: Показувати діалог підтвердження під час видалення допису - setting_disable_hover_cards: Вимкнути попередній перегляд профілю при наведенні + setting_disable_hover_cards: Вимкнути попередній перегляд профілю під час наведення мишки setting_disable_swiping: Вимкнути рух посування setting_display_media: Показ медіа setting_display_media_default: За промовчанням @@ -318,12 +329,18 @@ uk: listable: Дозволити появу цього хештеґа у каталозі пошуку і пропозицій name: Хештеґ trendable: Дозволити появу цього хештеґа у списку популярних хештеґів - usable: Дозволити дописам використовувати цей хештег локально + usable: Дозволити дописам використовувати цей гештеґ локально terms_of_service: changelog: Що змінилося? text: Умови використання terms_of_service_generator: + admin_email: Адреса електронної пошти для юридичних повідомлень + arbitration_address: Фізична адреса для арбітражних повідомлень + arbitration_website: Сайт для надсилання арбітражних повідомлень + dmca_address: Фізична адреса для сповіщень про DMCA/авторські права + dmca_email: Фізична адреса для сповіщень про DMCA/авторські права domain: Домен + jurisdiction: Правова юрисдикція user: role: Роль time_zone: Часовий пояс diff --git a/config/locales/sv.yml b/config/locales/sv.yml index aa2de7de32..7855c911d1 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -214,6 +214,7 @@ sv: enable_user: Aktivera användare memorialize_account: Minnesmärk konto promote_user: Befordra användare + publish_terms_of_service: Publicera användarvillkor reject_appeal: Avvisa överklagande reject_user: Avvisa användare remove_avatar_user: Ta bort avatar @@ -278,6 +279,7 @@ sv: enable_user_html: "%{name} aktiverade inloggning för användaren %{target}" memorialize_account_html: "%{name} gjorde %{target}s konto till en minnessida" promote_user_html: "%{name} befordrade användaren %{target}" + publish_terms_of_service_html: "%{name} publicerade uppdateringar till användarvillkoren" reject_appeal_html: "%{name} avvisade överklagande av modereringsbeslut från %{target}" reject_user_html: "%{name} avvisade registrering från %{target}" remove_avatar_user_html: "%{name} tog bort %{target}s avatar" @@ -926,8 +928,33 @@ sv: title: Hashtaggar updated_msg: Hashtagg-inställningarna har uppdaterats terms_of_service: + back: Tillbaka till användarvillkoren + changelog: Vad har ändrats + create: Använd dina egna + current: Nuvarande draft: Utkast + generate: Använd mall + generates: + action: Generera + chance_to_review_html: "De genererade villkoren för tjänsten kommer inte att publiceras automatiskt. Du kommer att ha en chans att granska resultatet. Vänligen fyll i nödvändig information för att fortsätta." + explanation_html: Användarvillkorsmallen tillhandahålls endast i informationssyfte, och skall inte tolkas som juridisk rådgivning i något ämne. Rådgör med ditt eget juridiska ombud om din situation och specifika juridiska frågor du har. + title: Inställningar för användarvillkor + history: Historik + live: Aktuella + no_history: Det finns inga sparade ändringar i användarvillkoren ännu. + no_terms_of_service_html: Du har för närvarande inte några användarvillkor. Användarvillkoren är avsedda att ge klarhet och skydda dig i tvister med dina användare. + notified_on_html: Användare meddelade på %{date} + notify_users: Meddela användare + preview: + explanation_html: 'E-postmeddelandet kommer att skickas till %{display_count} användare som har registrerat sig före %{date}. Följande text kommer att inkluderas i meddelandet:' + send_preview: Skicka till %{email} för förhandsgranskning + send_to_all: + one: Skicka %{display_count} meddelande + other: Skicka %{display_count} meddelanden + title: Förhandsgranska användarvillkorsmeddelande publish: Publicera + published_on_html: Publicerade den %{date} + save_draft: Spara utkast title: Användarvillkor title: Administration trends: @@ -1160,6 +1187,7 @@ sv: set_new_password: Skriv in nytt lösenord setup: email_below_hint_html: Kolla din skräppost-mapp eller begär en ny. Du kan korrigera din e-postadress om den är fel. + email_settings_hint_html: Klicka på länken vi skickade till %{email} för att börja använda Mastodon. Vi väntar här. link_not_received: Fick du ingen länk? new_confirmation_instructions_sent: Du kommer att få ett nytt e-postmeddelande med bekräftelselänken om några minuter! title: Kolla din inkorg @@ -1168,6 +1196,7 @@ sv: title: Logga in på %{domain} sign_up: manual_review: Registreringar på %{domain} går igenom manuell granskning av våra moderatorer. För att hjälpa oss att hantera din registrering, skriv lite om dig själv och varför du vill ha ett konto på %{domain}. + preamble: Med ett konto på denna Mastodon-server kan du följa alla andra personer i fediversum, oavsett vilken server deras konto tillhör. title: Låt oss få igång dig på %{domain}. status: account_status: Kontostatus @@ -1179,6 +1208,7 @@ sv: view_strikes: Visa tidigare prickar på ditt konto too_fast: Formuläret har skickats för snabbt, försök igen. use_security_key: Använd säkerhetsnyckel + user_agreement_html: Jag har läst och godkänner användarvillkoren och integritetspolicy author_attribution: example_title: Exempeltext hint_html: Skriver du nyheter eller bloggartiklar utanför Mastodon? Kontrollera hur du får krediteras när de delas på Mastodon. @@ -1677,6 +1707,7 @@ sv: scheduled_statuses: over_daily_limit: Du har överskridit dygnsgränsen på %{limit} schemalagda inlägg over_total_limit: Du har överskridit gränsen på %{limit} schemalagda inlägg + too_soon: datumet måste vara i framtiden self_destruct: lead_html: Tyvärr stänger %{domain} för gott. Om du hade ett konto där kommer du inte längre kunna använda det, men du kan fortfarande begära en säkerhetskopia av din data. title: Denna server stänger ned @@ -1901,6 +1932,15 @@ sv: further_actions_html: Om detta inte var du, rekommenderar vi att du snarast %{action} och aktiverar tvåfaktorsautentisering för att hålla ditt konto säkert. subject: Ditt konto har nåtts från en ny IP-adress title: En ny inloggning + terms_of_service_changed: + agreement: Genom att fortsätta använda %{domain} godkänner du dessa villkor. Om du inte håller med om de uppdaterade villkoren kan du när som helst säga upp ditt avtal med %{domain} genom att radera ditt konto. + changelog: 'I korthet, här är vad denna uppdatering innebär för dig:' + description: 'Du får detta e-postmeddelande eftersom vi gör vissa ändringar i våra användarvillkor på %{domain}. Vi uppmanar dig att granska de uppdaterade villkoren i sin helhet här:' + description_html: Du får detta e-postmeddelande eftersom vi gör vissa ändringar i våra användarvillkor på %{domain}. Vi uppmanar dig att granska de uppdaterade villkoren i sin helhet här. + sign_off: "%{domain} teamet" + subject: Uppdateringar till våra användarvillkor + subtitle: Villkoren för tjänsten på %{domain} ändras + title: Viktig uppdatering warning: appeal: Skicka överklagan appeal_description: Om du anser detta felaktigt kan du skicka överklagan till administratörerna av %{instance}. diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 0478ce6d3b..6f0ddbd81f 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -58,7 +58,7 @@ uk: title: Змінити роль для %{username} confirm: Зберегти confirmed: Підтверджено - confirming: Зберігається + confirming: Підтверджується custom: Власне delete: Видалити дані deleted: Видалено @@ -92,8 +92,8 @@ uk: title: Розміщення login_status: Стан входу media_attachments: Мультимедійні вкладення - memorialize: Меморіалізувати - memorialized: Перетворено на пам'ятник + memorialize: Увічнити + memorialized: Увічнено memorialized_msg: "%{username} успішно перетворено на пам'ятний обліковий запис" moderation: active: Активний @@ -193,6 +193,7 @@ uk: create_domain_block: Створити блокування домену create_email_domain_block: Створити блокування домену е-пошти create_ip_block: Створити правило IP + create_relay: Створити реле create_unavailable_domain: Створити недоступний домен create_user_role: Створити роль demote_user: Понизити користувача @@ -204,18 +205,22 @@ uk: destroy_email_domain_block: Видалити блокування домену е-пошти destroy_instance: Очистити домен destroy_ip_block: Видалити правило IP + destroy_relay: Видалити реле destroy_status: Видалити допис destroy_unavailable_domain: Видалити недоступний домен destroy_user_role: Знищити роль disable_2fa_user: Вимкнути 2FA disable_custom_emoji: Вимкнути користувацькі емодзі + disable_relay: Вимкнути реле disable_sign_in_token_auth_user: Вимкнути автентифікацію за допомогою токена е-пошти для користувача disable_user: Відключити користувача enable_custom_emoji: Увімкнути користувацькі емодзі + enable_relay: Увімкнути реле enable_sign_in_token_auth_user: Увімкнути автентифікацію за допомогою токена е-пошти для користувача enable_user: Активувати користувача memorialize_account: Меморіалізувати акаунт promote_user: Підвищити користувача + publish_terms_of_service: Опублікувати Умови використання reject_appeal: Відхилити апеляцію reject_user: Відхилити користувача remove_avatar_user: Видалити аватар @@ -253,6 +258,7 @@ uk: create_domain_block_html: "%{name} блокує домен %{target}" create_email_domain_block_html: "%{name} блокує домен електронної пошти %{target}" create_ip_block_html: "%{name} створює правило для IP %{target}" + create_relay_html: "%{name} створив реле %{target}" create_unavailable_domain_html: "%{name} зупиняє доставляння на домен %{target}" create_user_role_html: "%{name} створює роль %{target}" demote_user_html: "%{name} понижує користувача %{target}" @@ -264,18 +270,22 @@ uk: destroy_email_domain_block_html: "%{name} розблоковує домен електронної пошти %{target}" destroy_instance_html: "%{name} очищує домен %{target}" destroy_ip_block_html: "%{name} видаляє правило для IP %{target}" + destroy_relay_html: "%{name} видалив реле %{target}" destroy_status_html: "%{name} вилучає допис %{target}" destroy_unavailable_domain_html: "%{name} відновлює доставляння на домен %{target}" destroy_user_role_html: "%{name} видаляє роль %{target}" disable_2fa_user_html: "%{name} вимикає двоетапну перевірку для користувача %{target}" disable_custom_emoji_html: "%{name} вимикає емодзі %{target}" + disable_relay_html: "%{name} вимкнув реле %{target}" disable_sign_in_token_auth_user_html: "%{name} вимикає автентифікацію через токен е-пошти для %{target}" disable_user_html: "%{name} вимикає вхід для користувача %{target}" enable_custom_emoji_html: "%{name} вмикає емодзі %{target}" + enable_relay_html: "%{name} увімкнув реле %{target}" enable_sign_in_token_auth_user_html: "%{name} вмикає автентифікацію через токен е-пошти для %{target}" enable_user_html: "%{name} вмикає вхід для користувача %{target}" memorialize_account_html: "%{name} перетворює обліковий запис %{target} на сторінку пам'яті" promote_user_html: "%{name} підвищує користувача %{target}" + publish_terms_of_service_html: "%{name} опублікував оновлення умов використання" reject_appeal_html: "%{name} відхилили звернення на оскарження рішення від %{target}" reject_user_html: "%{name} відхиляє реєстрацію від %{target}" remove_avatar_user_html: "%{name} прибирає аватар %{target}" @@ -846,8 +856,10 @@ uk: back_to_account: Назад до сторінки облікового запису back_to_report: Повернутися до сторінки скарги batch: + add_to_report: 'Додати до звіту #%{id}' remove_from_report: Вилучити зі скарги report: Скарга + contents: Вміст deleted: Видалено favourites: Вподобане history: Історія версій @@ -856,12 +868,17 @@ uk: media: title: Медіа metadata: Метадані + no_history: Цей допис ще не редагували no_status_selected: Жодного допису не було змінено, оскільки жодного з них не було вибрано open: Відкрити допис original_status: Оригінальний допис reblogs: Поширення + replied_to_html: Відповів %{acct_link} status_changed: Допис змінено + status_title: "@%{name} опублікував допис" + title: Дописи облікового запису - @%{name} trending: Популярне + view_publicly: Переглянути привселюдно visibility: Видимість with_media: З медіа strikes: @@ -922,7 +939,7 @@ uk: moderation: not_trendable: Не трендові not_usable: Невикористовувані - pending_review: Очікує розгляду + pending_review: Очікує на розгляд review_requested: Запит на розгляд reviewed: Розглянуто title: Статус @@ -936,8 +953,34 @@ uk: reset: Скинути review: Переглянути допис search: Пошук - title: Хештеги + title: Гештеґи updated_msg: Параметри хештеґів успішно оновлені + terms_of_service: + back: Назад до умов використання + changelog: Що змінилося + create: Використовувати власний + current: Поточний + draft: Чернетка + generate: Використовувати шаблон + generates: + action: Згенерувати + chance_to_review_html: "Створені умови використання не опублікуються автоматично. Ви матимете змогу переглянути результати. Будь ласка, заповніть потрібні дані, щоб перейти далі." + explanation_html: Наданий шаблон умов надання послуг призначений лише для інформаційних цілей і не має розглядатися як юридична консультація з будь-якого питання. Проконсультуйтеся зі своїм юридичним радником щодо вашої ситуації та конкретних юридичних питань, які у вас постали. + title: Налаштування Умов обслуговування + history: Історія + live: Наживо + no_history: Змін умов обслуговування поки що не зафіксовано. + no_terms_of_service_html: Наразі у вас не налаштовано умов використання. Умови використання призначені для забезпечення ясності та захисту від потенційних зобов’язань у суперечках із користувачами. + notified_on_html: Користувачі сповіщені %{date} + notify_users: Сповістити користувачів + preview: + explanation_html: 'Електронний лист буде надіслано %{display_count} користувачам, які зареєструвалися до %{date}. Розміщений нижче текст додасться до електронного листа:' + send_preview: Надіслати попередній перегляд на %{email} + title: Попередній перегляд сповіщення про умови використання + publish: Опублікувати + published_on_html: Опубліковано %{date} + save_draft: Зберегти чернетку + title: Умови використання title: Адміністрування trends: allow: Дозволити @@ -1186,6 +1229,7 @@ uk: title: Увійти до %{domain} sign_up: manual_review: Реєстрація на %{domain} проходить через ручний розгляд нашими модераторами. Щоб допомогти нам завершити вашу реєстрацію, напишіть трохи про себе і чому ви хочете зареєструватися на %{domain}. + preamble: За допомогою облікового запису на цьому сервері Mastodon ви зможете стежити за будь-якою іншою людиною у федіверсі незалежно від того, де розміщений обліковий запис. title: Налаштуймо вас на %{domain}. status: account_status: Стан облікового запису @@ -1197,6 +1241,7 @@ uk: view_strikes: Переглянути попередні попередження вашому обліковому запису too_fast: Форму подано занадто швидко, спробуйте ще раз. use_security_key: Використовувати ключ безпеки + user_agreement_html: Я прочитав і приймаю умови використання та політику конфіденційності< /a> author_attribution: example_title: Зразок тексту hint_html: Ви пишете новини чи статті в блозі за межами Mastodon? Контролюйте, як вони підписуються, коли ними діляться на Mastodon. @@ -1844,6 +1889,8 @@ uk: too_late: Запізно оскаржувати це попередження tags: does_not_match_previous_name: не збігається з попереднім ім'ям + terms_of_service: + title: Умови використання themes: contrast: Mastodon (Висока контрастність) default: Mastodon (Темна) @@ -1904,6 +1951,15 @@ uk: further_actions_html: Якщо це були не ви. Радимо вам негайно %{action} й увімкнути двоетапну перевірку, щоб уберегти свій обліковий запис. subject: До вашого облікового запису отримано доступ з нової IP-адреси title: Новий вхід + terms_of_service_changed: + agreement: Далі використовуючи %{domain}, ви погоджуєтеся з цими умовами. Якщо ви не згодні з оновленими умовами, ви можете припинити свою угоду з %{domain} будь-якої миті, видаливши ваш обліковий запис. + changelog: 'Коротко, ось що це оновлення означає для вас:' + description: 'Ви отримали цього електронного листа, тому що ми впроваджуємо деякі зміни в наші умови обслуговування в %{domain}. Радимо переглянути оновлені умови повністю тут:' + description_html: Ви отримали цього електронного листа, тому що ми впроваджуємо деякі зміни до наших умов обслуговування в %{domain}. Радимо переглянути повністю оновлені умови тут. + sign_off: Команда %{domain} + subject: Оновлення до наших умов обслуговування + subtitle: Умови використання %{domain} змінюються + title: Важливе оновлення warning: appeal: Подати апеляцію appeal_description: Якщо ви вважаєте, що це помилка, ви можете надіслати оскаржити дії персоналу %{instance}. From 612d6182e47ea132de21a1b769ce7d599cb4d85d Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 6 Jan 2025 09:14:11 +0100 Subject: [PATCH 007/133] Fix `/share` not using server-set characters limit (#33459) --- app/javascript/mastodon/containers/compose_container.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/mastodon/containers/compose_container.jsx b/app/javascript/mastodon/containers/compose_container.jsx index 171f14d3b2..a2513cc552 100644 --- a/app/javascript/mastodon/containers/compose_container.jsx +++ b/app/javascript/mastodon/containers/compose_container.jsx @@ -1,6 +1,7 @@ import { Provider } from 'react-redux'; import { fetchCustomEmojis } from 'mastodon/actions/custom_emojis'; +import { fetchServer } from 'mastodon/actions/server'; import { hydrateStore } from 'mastodon/actions/store'; import { Router } from 'mastodon/components/router'; import Compose from 'mastodon/features/standalone/compose'; @@ -13,6 +14,7 @@ if (initialState) { } store.dispatch(fetchCustomEmojis()); +store.dispatch(fetchServer()); const ComposeContainer = () => ( From 08dd11f8d4c281b8b564df0548d3d991d3eaee68 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 03:18:05 -0500 Subject: [PATCH 008/133] Use `in_order_of` with `filter: false` in `AccountSummary.localized` (#33446) --- app/models/account_summary.rb | 2 +- spec/models/account_summary_spec.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 spec/models/account_summary_spec.rb diff --git a/app/models/account_summary.rb b/app/models/account_summary.rb index 327c0ef305..7522a70193 100644 --- a/app/models/account_summary.rb +++ b/app/models/account_summary.rb @@ -17,6 +17,6 @@ class AccountSummary < ApplicationRecord has_many :follow_recommendation_suppressions, primary_key: :account_id, foreign_key: :account_id, inverse_of: false, dependent: nil scope :safe, -> { where(sensitive: false) } - scope :localized, ->(locale) { order(Arel::Nodes::Case.new.when(arel_table[:language].eq(locale)).then(1).else(0).desc) } + scope :localized, ->(locale) { in_order_of(:language, [locale], filter: false) } scope :filtered, -> { where.missing(:follow_recommendation_suppressions) } end diff --git a/spec/models/account_summary_spec.rb b/spec/models/account_summary_spec.rb new file mode 100644 index 0000000000..cede8cda55 --- /dev/null +++ b/spec/models/account_summary_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe AccountSummary do + describe 'Scopes' do + describe '.localized' do + let(:first) { Fabricate :account } + let(:last) { Fabricate :account } + + before do + Fabricate :status, account: first, language: 'en' + Fabricate :status, account: last, language: 'es' + described_class.refresh + end + + it 'returns records in order of language' do + expect(described_class.localized('en')) + .to contain_exactly( + have_attributes(account_id: first.id, language: 'en'), + have_attributes(account_id: last.id, language: 'es') + ) + end + end + end +end From dbbf450ef5e43431e962ea35c0f6042c094d179a Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 6 Jan 2025 11:04:25 +0100 Subject: [PATCH 009/133] Fix `fediverse:creator` metadata not showing up in REST API (#33466) --- app/models/preview_card.rb | 2 +- .../rest/preview_card_serializer_spec.rb | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index f6f37c8c82..56fe483635 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -170,7 +170,7 @@ class PreviewCard < ApplicationRecord private def serialized_authors - if author_name? || author_url? + if author_name? || author_url? || author_account_id? PreviewCard::Author .new(self) end diff --git a/spec/serializers/rest/preview_card_serializer_spec.rb b/spec/serializers/rest/preview_card_serializer_spec.rb index 6dbc337865..41ba305b7c 100644 --- a/spec/serializers/rest/preview_card_serializer_spec.rb +++ b/spec/serializers/rest/preview_card_serializer_spec.rb @@ -21,7 +21,24 @@ RSpec.describe REST::PreviewCardSerializer do end end - context 'when preview card has author data' do + context 'when preview card has fediverse author data' do + let(:preview_card) { Fabricate.build :preview_card, author_account: Fabricate(:account) } + + it 'includes populated authors array' do + expect(subject.deep_symbolize_keys) + .to include( + authors: be_an(Array).and( + contain_exactly( + include( + account: be_present + ) + ) + ) + ) + end + end + + context 'when preview card has non-fediverse author data' do let(:preview_card) { Fabricate.build :preview_card, author_name: 'Name', author_url: 'https://host.example/123' } it 'includes populated authors array' do From ea9b10d112d62bfa7a60886a00f9774fff0da3d7 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 6 Jan 2025 11:16:42 +0100 Subject: [PATCH 010/133] Fix error 500 when passing an invalid `lang` parameter (#33467) --- app/controllers/concerns/localized.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/concerns/localized.rb b/app/controllers/concerns/localized.rb index ede299d5a4..14742e3b5c 100644 --- a/app/controllers/concerns/localized.rb +++ b/app/controllers/concerns/localized.rb @@ -25,7 +25,7 @@ module Localized end def available_locale_or_nil(locale_name) - locale_name.to_sym if locale_name.present? && I18n.available_locales.include?(locale_name.to_sym) + locale_name.to_sym if locale_name.respond_to?(:to_sym) && I18n.available_locales.include?(locale_name.to_sym) end def content_locale From ae302d2f5a66390cf1a8f88738e55f66cf233d45 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 6 Jan 2025 12:50:40 +0100 Subject: [PATCH 011/133] [Glitch] Standalone share page: Dispatch fetchServer for maxChars (#2929) --- app/javascript/flavours/glitch/containers/compose_container.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/flavours/glitch/containers/compose_container.jsx b/app/javascript/flavours/glitch/containers/compose_container.jsx index 772ac5fa6b..c155180c8d 100644 --- a/app/javascript/flavours/glitch/containers/compose_container.jsx +++ b/app/javascript/flavours/glitch/containers/compose_container.jsx @@ -1,6 +1,7 @@ import { Provider } from 'react-redux'; import { fetchCustomEmojis } from 'flavours/glitch/actions/custom_emojis'; +import { fetchServer } from 'flavours/glitch/actions/server'; import { hydrateStore } from 'flavours/glitch/actions/store'; import { Router } from 'flavours/glitch/components/router'; import Compose from 'flavours/glitch/features/standalone/compose'; @@ -13,6 +14,7 @@ if (initialState) { } store.dispatch(fetchCustomEmojis()); +store.dispatch(fetchServer()); const ComposeContainer = () => ( From c692f69dbab7df442463e68ed3629896d3c6767c Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 6 Jan 2025 17:24:32 +0100 Subject: [PATCH 012/133] Fix color contrast in report modal (#33468) --- app/javascript/styles/mastodon/components.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index f070fa6fbb..d21175595d 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1590,7 +1590,7 @@ body > [data-popper-placement] { padding: 0 10px; .detailed-status__display-name { - color: lighten($inverted-text-color, 16%); + color: $dark-text-color; span { display: inline; From e8672e27e8372d4e76838771d47c40bd67c504bf Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 6 Jan 2025 19:22:07 +0100 Subject: [PATCH 013/133] Further remove old notifications code (#33465) --- .../mastodon/actions/notifications.js | 42 ++----------------- .../containers/column_settings_container.js | 6 +-- 2 files changed, 6 insertions(+), 42 deletions(-) diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index 3266df5a59..2499b8da1d 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -7,26 +7,18 @@ import { requestNotificationPermission } from '../utils/notifications'; import { fetchFollowRequests } from './accounts'; import { importFetchedAccount, - importFetchedStatus, } from './importer'; import { submitMarkers } from './markers'; import { notificationsUpdate } from "./notifications_typed"; import { register as registerPushNotifications } from './push_notifications'; -import { saveSettings } from './settings'; export * from "./notifications_typed"; -export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP'; - export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET'; export const NOTIFICATIONS_SET_BROWSER_SUPPORT = 'NOTIFICATIONS_SET_BROWSER_SUPPORT'; export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION'; -export const NOTIFICATION_REQUESTS_DISMISS_REQUEST = 'NOTIFICATION_REQUESTS_DISMISS_REQUEST'; -export const NOTIFICATION_REQUESTS_DISMISS_SUCCESS = 'NOTIFICATION_REQUESTS_DISMISS_SUCCESS'; -export const NOTIFICATION_REQUESTS_DISMISS_FAIL = 'NOTIFICATION_REQUESTS_DISMISS_FAIL'; - defineMessages({ mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, group: { id: 'notifications.group', defaultMessage: '{count} notifications' }, @@ -34,8 +26,6 @@ defineMessages({ export function updateNotifications(notification, intlMessages, intlLocale) { return (dispatch, getState) => { - const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']); - const showInColumn = activeFilter === 'all' ? getState().getIn(['settings', 'notifications', 'shows', notification.type], true) : activeFilter === notification.type; const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true); const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true); @@ -57,24 +47,9 @@ export function updateNotifications(notification, intlMessages, intlLocale) { dispatch(submitMarkers()); - if (showInColumn) { - dispatch(importFetchedAccount(notification.account)); - - if (notification.status) { - dispatch(importFetchedStatus(notification.status)); - } - - if (notification.report) { - dispatch(importFetchedAccount(notification.report.target_account)); - } - - dispatch(notificationsUpdate({ notification, playSound: playSound && !filtered})); - } else if (playSound && !filtered) { - dispatch({ - type: NOTIFICATIONS_UPDATE_NOOP, - meta: { sound: 'boop' }, - }); - } + // `notificationsUpdate` is still used in `user_lists` and `relationships` reducers + dispatch(importFetchedAccount(notification.account)); + dispatch(notificationsUpdate({ notification, playSound: playSound && !filtered})); // Desktop notifications if (typeof window.Notification !== 'undefined' && showAlert && !filtered) { @@ -93,17 +68,6 @@ export function updateNotifications(notification, intlMessages, intlLocale) { const noOp = () => {}; -export function setFilter (filterType) { - return dispatch => { - dispatch({ - type: NOTIFICATIONS_FILTER_SET, - path: ['notifications', 'quickFilter', 'active'], - value: filterType, - }); - dispatch(saveSettings()); - }; -} - // Browser support export function setupBrowserNotifications() { return dispatch => { diff --git a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js index f061060ec7..eddd35df4a 100644 --- a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js +++ b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js @@ -3,10 +3,10 @@ import { defineMessages, injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import { openModal } from 'mastodon/actions/modal'; -import { fetchNotifications } from 'mastodon/actions/notification_groups'; +import { fetchNotifications , setNotificationsFilter } from 'mastodon/actions/notification_groups'; import { showAlert } from '../../../actions/alerts'; -import { setFilter, requestBrowserPermission } from '../../../actions/notifications'; +import { requestBrowserPermission } from '../../../actions/notifications'; import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications'; import { changeSetting } from '../../../actions/settings'; import ColumnSettings from '../components/column_settings'; @@ -43,7 +43,7 @@ const mapDispatchToProps = (dispatch) => ({ } } else if (path[0] === 'quickFilter') { dispatch(changeSetting(['notifications', ...path], checked)); - dispatch(setFilter('all')); + dispatch(setNotificationsFilter('all')); } else if (path[0] === 'alerts' && checked && typeof window.Notification !== 'undefined' && Notification.permission !== 'granted') { if (checked && typeof window.Notification !== 'undefined' && Notification.permission !== 'granted') { dispatch(requestBrowserPermission((permission) => { From 1eb752fb38d48ecd42248a733d2cfd9246a77e6c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 13:22:32 -0500 Subject: [PATCH 014/133] Extract constants for `AnnualReport::*` minimum thresholds (#33469) --- app/lib/annual_report/commonly_interacted_with_accounts.rb | 7 ++++++- app/lib/annual_report/most_reblogged_accounts.rb | 7 ++++++- app/lib/annual_report/top_hashtags.rb | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/lib/annual_report/commonly_interacted_with_accounts.rb b/app/lib/annual_report/commonly_interacted_with_accounts.rb index 2316789f2a..c2aee44dea 100644 --- a/app/lib/annual_report/commonly_interacted_with_accounts.rb +++ b/app/lib/annual_report/commonly_interacted_with_accounts.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class AnnualReport::CommonlyInteractedWithAccounts < AnnualReport::Source + MINIMUM_INTERACTIONS = 1 SET_SIZE = 40 def generate @@ -17,6 +18,10 @@ class AnnualReport::CommonlyInteractedWithAccounts < AnnualReport::Source private def commonly_interacted_with_accounts - report_statuses.where.not(in_reply_to_account_id: @account.id).group(:in_reply_to_account_id).having('count(*) > 1').order(count_all: :desc).limit(SET_SIZE).count + report_statuses.where.not(in_reply_to_account_id: @account.id).group(:in_reply_to_account_id).having(minimum_interaction_count).order(count_all: :desc).limit(SET_SIZE).count + end + + def minimum_interaction_count + Arel.star.count.gt(MINIMUM_INTERACTIONS) end end diff --git a/app/lib/annual_report/most_reblogged_accounts.rb b/app/lib/annual_report/most_reblogged_accounts.rb index 69e247f2a6..a23734fce3 100644 --- a/app/lib/annual_report/most_reblogged_accounts.rb +++ b/app/lib/annual_report/most_reblogged_accounts.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class AnnualReport::MostRebloggedAccounts < AnnualReport::Source + MINIMUM_REBLOGS = 1 SET_SIZE = 10 def generate @@ -17,6 +18,10 @@ class AnnualReport::MostRebloggedAccounts < AnnualReport::Source private def most_reblogged_accounts - report_statuses.where.not(reblog_of_id: nil).joins(reblog: :account).group(accounts: [:id]).having('count(*) > 1').order(count_all: :desc).limit(SET_SIZE).count + report_statuses.where.not(reblog_of_id: nil).joins(reblog: :account).group(accounts: [:id]).having(minimum_reblog_count).order(count_all: :desc).limit(SET_SIZE).count + end + + def minimum_reblog_count + Arel.star.count.gt(MINIMUM_REBLOGS) end end diff --git a/app/lib/annual_report/top_hashtags.rb b/app/lib/annual_report/top_hashtags.rb index ae000a8beb..42420a2770 100644 --- a/app/lib/annual_report/top_hashtags.rb +++ b/app/lib/annual_report/top_hashtags.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class AnnualReport::TopHashtags < AnnualReport::Source + MINIMUM_TAGGINGS = 1 SET_SIZE = 40 def generate @@ -17,7 +18,11 @@ class AnnualReport::TopHashtags < AnnualReport::Source private def top_hashtags - Tag.joins(:statuses).where(statuses: { id: report_statuses.select(:id) }).group(coalesced_tag_names).having('count(*) > 1').order(count_all: :desc).limit(SET_SIZE).count + Tag.joins(:statuses).where(statuses: { id: report_statuses.select(:id) }).group(coalesced_tag_names).having(minimum_taggings_count).order(count_all: :desc).limit(SET_SIZE).count + end + + def minimum_taggings_count + Arel.star.count.gt(MINIMUM_TAGGINGS) end def coalesced_tag_names From 4cf031ee13b8bda88fdf2ae23a7d6632deeac90c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 13:23:05 -0500 Subject: [PATCH 015/133] Extricate `Invite` constants for code generation (#33472) --- app/models/invite.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/invite.rb b/app/models/invite.rb index ea095a3ac1..d1981f16ad 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -20,6 +20,9 @@ class Invite < ApplicationRecord include Expireable COMMENT_SIZE_LIMIT = 420 + ELIGIBLE_CODE_CHARACTERS = [*('a'..'z'), *('A'..'Z'), *('0'..'9')].freeze + HOMOGLYPHS = %w(0 1 I l O).freeze + VALID_CODE_CHARACTERS = ELIGIBLE_CODE_CHARACTERS - HOMOGLYPHS belongs_to :user, inverse_of: :invites has_many :users, inverse_of: :invite, dependent: nil @@ -38,7 +41,7 @@ class Invite < ApplicationRecord def set_code loop do - self.code = ([*('a'..'z'), *('A'..'Z'), *('0'..'9')] - %w(0 1 I l O)).sample(8).join + self.code = VALID_CODE_CHARACTERS.sample(8).join break if Invite.find_by(code: code).nil? end end From ef39398b82449440872d899cc5902b8eb7f7c26c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 13:23:57 -0500 Subject: [PATCH 016/133] Add `urgent` scope and `by_version` method to `SoftwareUpdate` (#33470) --- .../admin/software_updates_controller.rb | 2 +- app/mailers/admin_mailer.rb | 4 ++-- app/models/software_update.rb | 6 +++++ spec/models/software_update_spec.rb | 23 +++++++++++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/software_updates_controller.rb b/app/controllers/admin/software_updates_controller.rb index 52d8cb41e6..d7b114a100 100644 --- a/app/controllers/admin/software_updates_controller.rb +++ b/app/controllers/admin/software_updates_controller.rb @@ -6,7 +6,7 @@ module Admin def index authorize :software_update, :index? - @software_updates = SoftwareUpdate.all.sort_by(&:gem_version) + @software_updates = SoftwareUpdate.by_version end private diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index fffbbb3c6d..cc2a537b3c 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -48,7 +48,7 @@ class AdminMailer < ApplicationMailer end def new_software_updates - @software_updates = SoftwareUpdate.all.to_a.sort_by(&:gem_version) + @software_updates = SoftwareUpdate.by_version locale_for_account(@me) do mail subject: default_i18n_subject(instance: @instance) @@ -56,7 +56,7 @@ class AdminMailer < ApplicationMailer end def new_critical_software_updates - @software_updates = SoftwareUpdate.where(urgent: true).to_a.sort_by(&:gem_version) + @software_updates = SoftwareUpdate.urgent.by_version locale_for_account(@me) do mail subject: default_i18n_subject(instance: @instance) diff --git a/app/models/software_update.rb b/app/models/software_update.rb index 7e2b15656e..4c868f6f59 100644 --- a/app/models/software_update.rb +++ b/app/models/software_update.rb @@ -18,6 +18,8 @@ class SoftwareUpdate < ApplicationRecord enum :type, { patch: 0, minor: 1, major: 2 }, suffix: :type + scope :urgent, -> { where(urgent: true) } + def gem_version Gem::Version.new(version) end @@ -35,6 +37,10 @@ class SoftwareUpdate < ApplicationRecord Rails.configuration.x.mastodon.software_update_url.present? end + def by_version + all.sort_by(&:gem_version) + end + def pending_to_a return [] unless check_enabled? diff --git a/spec/models/software_update_spec.rb b/spec/models/software_update_spec.rb index 43e9cd058f..8c698e51b3 100644 --- a/spec/models/software_update_spec.rb +++ b/spec/models/software_update_spec.rb @@ -3,6 +3,29 @@ require 'rails_helper' RSpec.describe SoftwareUpdate do + describe 'Scopes' do + describe '.urgent' do + let!(:urgent_update) { Fabricate :software_update, urgent: true } + let!(:non_urgent_update) { Fabricate :software_update, urgent: false } + + it 'returns records that are urgent' do + expect(described_class.urgent) + .to include(urgent_update) + .and not_include(non_urgent_update) + end + end + end + + describe '.by_version' do + let!(:latest_update) { Fabricate :software_update, version: '4.0.0' } + let!(:older_update) { Fabricate :software_update, version: '3.0.0' } + + it 'returns record in gem version order' do + expect(described_class.by_version) + .to eq([older_update, latest_update]) + end + end + describe '#pending?' do subject { described_class.new(version: update_version) } From 24554bb6b967e8ed80137d72b45233c4e706db8f Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 13:24:04 -0500 Subject: [PATCH 017/133] Use `ubuntu-latest` for all GH actions runners (#33473) --- .github/workflows/check-i18n.yml | 2 +- .github/workflows/test-ruby.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-i18n.yml b/.github/workflows/check-i18n.yml index 4f87f0fe5f..c46090c1b5 100644 --- a/.github/workflows/check-i18n.yml +++ b/.github/workflows/check-i18n.yml @@ -18,7 +18,7 @@ permissions: jobs: check-i18n: - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 08b50e2680..900faad07f 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -174,7 +174,7 @@ jobs: test-libvips: name: Libvips tests - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest needs: - build From fbd283329ed8d7bae7ef1affd9be68531127549e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 13:24:39 -0500 Subject: [PATCH 018/133] Avoid "double icon" near each other in nav sidebar (#33449) --- config/navigation.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/navigation.rb b/config/navigation.rb index de9e530f58..225106592c 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -17,7 +17,7 @@ SimpleNavigation::Configuration.run do |navigation| n.item :preferences, safe_join([material_symbol('settings'), t('settings.preferences')]), settings_preferences_path, if: -> { current_user.functional? && !self_destruct } do |s| s.item :appearance, safe_join([material_symbol('computer'), t('settings.appearance')]), settings_preferences_appearance_path s.item :notifications, safe_join([material_symbol('mail'), t('settings.notifications')]), settings_preferences_notifications_path - s.item :other, safe_join([material_symbol('settings'), t('preferences.other')]), settings_preferences_other_path + s.item :other, safe_join([material_symbol('tune'), t('preferences.other')]), settings_preferences_other_path end n.item :relationships, safe_join([material_symbol('groups'), t('settings.relationships')]), relationships_path, if: -> { current_user.functional? && !self_destruct } do |s| @@ -65,7 +65,7 @@ SimpleNavigation::Configuration.run do |navigation| n.item :admin, safe_join([material_symbol('manufacturing'), t('admin.title')]), nil, if: -> { current_user.can?(:view_dashboard, :manage_settings, :manage_rules, :manage_announcements, :manage_custom_emojis, :manage_webhooks, :manage_federation) && !self_destruct } do |s| s.item :dashboard, safe_join([material_symbol('speed'), t('admin.dashboard.title')]), admin_dashboard_path, if: -> { current_user.can?(:view_dashboard) } - s.item :settings, safe_join([material_symbol('manufacturing'), t('admin.settings.title')]), admin_settings_path, if: -> { current_user.can?(:manage_settings) }, highlights_on: %r{/admin/settings} + s.item :settings, safe_join([material_symbol('tune'), t('admin.settings.title')]), admin_settings_path, if: -> { current_user.can?(:manage_settings) }, highlights_on: %r{/admin/settings} s.item :terms_of_service, safe_join([material_symbol('description'), t('admin.terms_of_service.title')]), admin_terms_of_service_index_path, highlights_on: %r{/admin/terms_of_service}, if: -> { current_user.can?(:manage_rules) } s.item :rules, safe_join([material_symbol('gavel'), t('admin.rules.title')]), admin_rules_path, highlights_on: %r{/admin/rules}, if: -> { current_user.can?(:manage_rules) } s.item :warning_presets, safe_join([material_symbol('warning'), t('admin.warning_presets.title')]), admin_warning_presets_path, highlights_on: %r{/admin/warning_presets}, if: -> { current_user.can?(:manage_settings) } From 82d2ce293d98a2b9e024bc5396c3586904d46ae5 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 13:24:54 -0500 Subject: [PATCH 019/133] Convert `admin/warning_presets` spec controller->system (#33474) --- .../admin/warning_presets_controller_spec.rb | 85 ------------------- spec/system/admin/warning_presets_spec.rb | 81 ++++++++++++++++++ 2 files changed, 81 insertions(+), 85 deletions(-) delete mode 100644 spec/controllers/admin/warning_presets_controller_spec.rb create mode 100644 spec/system/admin/warning_presets_spec.rb diff --git a/spec/controllers/admin/warning_presets_controller_spec.rb b/spec/controllers/admin/warning_presets_controller_spec.rb deleted file mode 100644 index d416b9c3cf..0000000000 --- a/spec/controllers/admin/warning_presets_controller_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Admin::WarningPresetsController do - render_views - - let(:user) { Fabricate(:admin_user) } - - before do - sign_in user, scope: :user - end - - describe 'GET #index' do - it 'returns http success' do - get :index - - expect(response).to have_http_status(:success) - end - end - - describe 'GET #edit' do - let(:account_warning_preset) { Fabricate(:account_warning_preset) } - - it 'returns http success and renders edit' do - get :edit, params: { id: account_warning_preset.id } - - expect(response).to have_http_status(:success) - expect(response).to render_template(:edit) - end - end - - describe 'POST #create' do - context 'with valid data' do - it 'creates a new account_warning_preset and redirects' do - expect do - post :create, params: { account_warning_preset: { text: 'The account_warning_preset text.' } } - end.to change(AccountWarningPreset, :count).by(1) - - expect(response).to redirect_to(admin_warning_presets_path) - end - end - - context 'with invalid data' do - it 'does creates a new account_warning_preset and renders index' do - expect do - post :create, params: { account_warning_preset: { text: '' } } - end.to_not change(AccountWarningPreset, :count) - - expect(response).to render_template(:index) - end - end - end - - describe 'PUT #update' do - let(:account_warning_preset) { Fabricate(:account_warning_preset, text: 'Original text') } - - context 'with valid data' do - it 'updates the account_warning_preset and redirects' do - put :update, params: { id: account_warning_preset.id, account_warning_preset: { text: 'Updated text.' } } - - expect(response).to redirect_to(admin_warning_presets_path) - end - end - - context 'with invalid data' do - it 'does not update the account_warning_preset and renders index' do - put :update, params: { id: account_warning_preset.id, account_warning_preset: { text: '' } } - - expect(response).to render_template(:edit) - end - end - end - - describe 'DELETE #destroy' do - let!(:account_warning_preset) { Fabricate(:account_warning_preset) } - - it 'destroys the account_warning_preset and redirects' do - delete :destroy, params: { id: account_warning_preset.id } - - expect { account_warning_preset.reload }.to raise_error(ActiveRecord::RecordNotFound) - expect(response).to redirect_to(admin_warning_presets_path) - end - end -end diff --git a/spec/system/admin/warning_presets_spec.rb b/spec/system/admin/warning_presets_spec.rb new file mode 100644 index 0000000000..f1ab690981 --- /dev/null +++ b/spec/system/admin/warning_presets_spec.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Admin Warning Presets' do + describe 'Managing warning presets' do + before { sign_in Fabricate(:admin_user) } + + describe 'Viewing warning presets' do + let!(:account_warning_preset) { Fabricate :account_warning_preset, text: 'This is a preset' } + + it 'lists existing records' do + visit admin_warning_presets_path + + expect(page) + .to have_content(I18n.t('admin.warning_presets.title')) + .and have_content(account_warning_preset.text) + end + end + + describe 'Creating a new account warning preset' do + it 'creates new record with valid attributes' do + visit admin_warning_presets_path + + # Invalid submission + fill_in 'account_warning_preset_text', with: '' + expect { submit_form } + .to_not change(AccountWarningPreset, :count) + expect(page) + .to have_content(/error below/) + + # Valid submission + fill_in 'account_warning_preset_text', with: 'You cant do that here' + expect { submit_form } + .to change(AccountWarningPreset, :count).by(1) + expect(page) + .to have_content(I18n.t('admin.warning_presets.title')) + end + + def submit_form + click_on I18n.t('admin.warning_presets.add_new') + end + end + + describe 'Editing an existing account warning preset' do + let!(:account_warning_preset) { Fabricate :account_warning_preset, text: 'Preset text' } + + it 'updates with valid attributes' do + visit admin_warning_presets_path + + # Invalid submission + click_on account_warning_preset.text + fill_in 'account_warning_preset_text', with: '' + expect { submit_form } + .to_not change(account_warning_preset.reload, :updated_at) + + # Valid update + fill_in 'account_warning_preset_text', with: 'Updated text' + expect { submit_form } + .to(change { account_warning_preset.reload.text }) + end + + def submit_form + click_on I18n.t('generic.save_changes') + end + end + + describe 'Destroy an account warning preset' do + let!(:account_warning_preset) { Fabricate :account_warning_preset } + + it 'removes the record' do + visit admin_warning_presets_path + + expect { click_on I18n.t('admin.warning_presets.delete') } + .to change(AccountWarningPreset, :count).by(-1) + expect { account_warning_preset.reload } + .to raise_error(ActiveRecord::RecordNotFound) + end + end + end +end From 32567a6d8d774d53701141f08e3e33cca94021b0 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 6 Jan 2025 17:24:32 +0100 Subject: [PATCH 020/133] [Glitch] Fix color contrast in report modal Port c692f69dbab7df442463e68ed3629896d3c6767c to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/components.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index 2d65c2f9e9..d4e0cded96 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -1641,7 +1641,7 @@ body > [data-popper-placement] { padding: 0 10px; .detailed-status__display-name { - color: lighten($inverted-text-color, 16%); + color: $dark-text-color; span { display: inline; From 412a0dd6616ba80ae4e1970b1e658f6dc7980e89 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 6 Jan 2025 19:22:07 +0100 Subject: [PATCH 021/133] [Glitch] Further remove old notifications code Port e8672e27e8372d4e76838771d47c40bd67c504bf to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/actions/notifications.js | 42 ++----------------- .../containers/column_settings_container.js | 6 +-- 2 files changed, 6 insertions(+), 42 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js index 3266df5a59..2499b8da1d 100644 --- a/app/javascript/flavours/glitch/actions/notifications.js +++ b/app/javascript/flavours/glitch/actions/notifications.js @@ -7,26 +7,18 @@ import { requestNotificationPermission } from '../utils/notifications'; import { fetchFollowRequests } from './accounts'; import { importFetchedAccount, - importFetchedStatus, } from './importer'; import { submitMarkers } from './markers'; import { notificationsUpdate } from "./notifications_typed"; import { register as registerPushNotifications } from './push_notifications'; -import { saveSettings } from './settings'; export * from "./notifications_typed"; -export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP'; - export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET'; export const NOTIFICATIONS_SET_BROWSER_SUPPORT = 'NOTIFICATIONS_SET_BROWSER_SUPPORT'; export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION'; -export const NOTIFICATION_REQUESTS_DISMISS_REQUEST = 'NOTIFICATION_REQUESTS_DISMISS_REQUEST'; -export const NOTIFICATION_REQUESTS_DISMISS_SUCCESS = 'NOTIFICATION_REQUESTS_DISMISS_SUCCESS'; -export const NOTIFICATION_REQUESTS_DISMISS_FAIL = 'NOTIFICATION_REQUESTS_DISMISS_FAIL'; - defineMessages({ mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, group: { id: 'notifications.group', defaultMessage: '{count} notifications' }, @@ -34,8 +26,6 @@ defineMessages({ export function updateNotifications(notification, intlMessages, intlLocale) { return (dispatch, getState) => { - const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']); - const showInColumn = activeFilter === 'all' ? getState().getIn(['settings', 'notifications', 'shows', notification.type], true) : activeFilter === notification.type; const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true); const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true); @@ -57,24 +47,9 @@ export function updateNotifications(notification, intlMessages, intlLocale) { dispatch(submitMarkers()); - if (showInColumn) { - dispatch(importFetchedAccount(notification.account)); - - if (notification.status) { - dispatch(importFetchedStatus(notification.status)); - } - - if (notification.report) { - dispatch(importFetchedAccount(notification.report.target_account)); - } - - dispatch(notificationsUpdate({ notification, playSound: playSound && !filtered})); - } else if (playSound && !filtered) { - dispatch({ - type: NOTIFICATIONS_UPDATE_NOOP, - meta: { sound: 'boop' }, - }); - } + // `notificationsUpdate` is still used in `user_lists` and `relationships` reducers + dispatch(importFetchedAccount(notification.account)); + dispatch(notificationsUpdate({ notification, playSound: playSound && !filtered})); // Desktop notifications if (typeof window.Notification !== 'undefined' && showAlert && !filtered) { @@ -93,17 +68,6 @@ export function updateNotifications(notification, intlMessages, intlLocale) { const noOp = () => {}; -export function setFilter (filterType) { - return dispatch => { - dispatch({ - type: NOTIFICATIONS_FILTER_SET, - path: ['notifications', 'quickFilter', 'active'], - value: filterType, - }); - dispatch(saveSettings()); - }; -} - // Browser support export function setupBrowserNotifications() { return dispatch => { diff --git a/app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js b/app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js index 6a933237f9..92ea81877c 100644 --- a/app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js +++ b/app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js @@ -3,10 +3,10 @@ import { defineMessages, injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import { openModal } from 'flavours/glitch/actions/modal'; -import { fetchNotifications } from 'flavours/glitch/actions/notification_groups'; +import { fetchNotifications , setNotificationsFilter } from 'flavours/glitch/actions/notification_groups'; import { showAlert } from '../../../actions/alerts'; -import { setFilter, requestBrowserPermission } from '../../../actions/notifications'; +import { requestBrowserPermission } from '../../../actions/notifications'; import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications'; import { changeSetting } from '../../../actions/settings'; import ColumnSettings from '../components/column_settings'; @@ -43,7 +43,7 @@ const mapDispatchToProps = (dispatch) => ({ } } else if (path[0] === 'quickFilter') { dispatch(changeSetting(['notifications', ...path], checked)); - dispatch(setFilter('all')); + dispatch(setNotificationsFilter('all')); } else if (path[0] === 'alerts' && checked && typeof window.Notification !== 'undefined' && Notification.permission !== 'granted') { if (checked && typeof window.Notification !== 'undefined' && Notification.permission !== 'granted') { dispatch(requestBrowserPermission((permission) => { From b0634b2943352a956f802dfbbd2c672a9c7c4f9d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 14:59:11 -0500 Subject: [PATCH 022/133] Add helpers for ios/android app store links in welcome mailer (#33475) --- app/helpers/application_helper.rb | 8 ++++++++ app/views/application/mailer/_checklist.html.haml | 4 ++-- app/views/user_mailer/welcome.text.erb | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e1ca536c7d..5a5ee05532 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -238,6 +238,14 @@ module ApplicationHelper I18n.t 'user_mailer.welcome.hashtags_recent_count', people: number_with_delimiter(people), count: people end + def app_store_url_ios + 'https://apps.apple.com/app/mastodon-for-iphone-and-ipad/id1571998974' + end + + def app_store_url_android + 'https://play.google.com/store/apps/details?id=org.joinmastodon.android' + end + private def storage_host_var diff --git a/app/views/application/mailer/_checklist.html.haml b/app/views/application/mailer/_checklist.html.haml index 91c7c98f26..4b460fa26d 100644 --- a/app/views/application/mailer/_checklist.html.haml +++ b/app/views/application/mailer/_checklist.html.haml @@ -29,8 +29,8 @@ %div - if defined?(show_apps_buttons) && show_apps_buttons .email-welcome-apps-btns - = link_to image_tag(frontend_asset_url('images/mailer-new/store-icons/btn-app-store.png'), alt: t('user_mailer.welcome.apps_ios_action'), width: 120, height: 40), 'https://apps.apple.com/app/mastodon-for-iphone-and-ipad/id1571998974' - = link_to image_tag(frontend_asset_url('images/mailer-new/store-icons/btn-google-play.png'), alt: t('user_mailer.welcome.apps_android_action'), width: 120, height: 40), 'https://play.google.com/store/apps/details?id=org.joinmastodon.android' + = link_to image_tag(frontend_asset_url('images/mailer-new/store-icons/btn-app-store.png'), alt: t('user_mailer.welcome.apps_ios_action'), width: 120, height: 40), app_store_url_ios + = link_to image_tag(frontend_asset_url('images/mailer-new/store-icons/btn-google-play.png'), alt: t('user_mailer.welcome.apps_android_action'), width: 120, height: 40), app_store_url_android - elsif defined?(button_text) && defined?(button_url) && defined?(checked) && !checked = render 'application/mailer/button', text: button_text, url: button_url, has_arrow: false /[if mso] diff --git a/app/views/user_mailer/welcome.text.erb b/app/views/user_mailer/welcome.text.erb index 144d44b842..383f436f8e 100644 --- a/app/views/user_mailer/welcome.text.erb +++ b/app/views/user_mailer/welcome.text.erb @@ -30,8 +30,8 @@ 5. <%= t('user_mailer.welcome.apps_title') %> <%= t('user_mailer.welcome.apps_step') %> - * iOS: https://apps.apple.com/app/mastodon-for-iphone-and-ipad/id1571998974 - * Android: https://play.google.com/store/apps/details?id=org.joinmastodon.android + * iOS: <%= app_store_url_ios %> + * Android: <%= app_store_url_android %> --- From efcd4ea5de2a617069be546852f504195de0336d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 6 Jan 2025 18:25:13 -0500 Subject: [PATCH 023/133] Reference value constants from specs (#33479) --- spec/lib/activitypub/activity/create_spec.rb | 8 ++++---- spec/models/account_spec.rb | 3 +-- spec/models/appeal_spec.rb | 2 +- spec/policies/backup_policy_spec.rb | 12 ++++++++++-- spec/requests/api/v1/accounts/credentials_spec.rb | 2 +- spec/requests/api/v1/accounts/notes_spec.rb | 2 +- spec/requests/api/v2/media_spec.rb | 2 +- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index 8be6766dad..d70456e458 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -624,7 +624,7 @@ RSpec.describe ActivityPub::Activity::Create do type: 'Document', mediaType: 'image/png', url: 'http://example.com/attachment.png', - name: '*' * 1500, + name: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH, }, ], } @@ -636,7 +636,7 @@ RSpec.describe ActivityPub::Activity::Create do status = sender.statuses.first expect(status).to_not be_nil - expect(status.media_attachments.map(&:description)).to include('*' * 1500) + expect(status.media_attachments.map(&:description)).to include('*' * MediaAttachment::MAX_DESCRIPTION_LENGTH) end end @@ -651,7 +651,7 @@ RSpec.describe ActivityPub::Activity::Create do type: 'Document', mediaType: 'image/png', url: 'http://example.com/attachment.png', - summary: '*' * 1500, + summary: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH, }, ], } @@ -663,7 +663,7 @@ RSpec.describe ActivityPub::Activity::Create do status = sender.statuses.first expect(status).to_not be_nil - expect(status.media_attachments.map(&:description)).to include('*' * 1500) + expect(status.media_attachments.map(&:description)).to include('*' * MediaAttachment::MAX_DESCRIPTION_LENGTH) end end diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 967809b8e1..7755cf9611 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -824,8 +824,7 @@ RSpec.describe Account do it { is_expected.to_not allow_values(account_note_over_limit).for(:note) } it { is_expected.to allow_value(fields_empty_name_value).for(:fields) } - it { is_expected.to_not allow_value(fields_over_limit).for(:fields) } - it { is_expected.to_not allow_value(fields_empty_name).for(:fields) } + it { is_expected.to_not allow_values(fields_over_limit, fields_empty_name).for(:fields) } end context 'when account is remote' do diff --git a/spec/models/appeal_spec.rb b/spec/models/appeal_spec.rb index e974ff9254..06775f5dd6 100644 --- a/spec/models/appeal_spec.rb +++ b/spec/models/appeal_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Appeal do it { is_expected.to validate_length_of(:text).is_at_most(described_class::TEXT_LENGTH_LIMIT) } context 'with a strike created too long ago' do - let(:strike) { Fabricate.build :account_warning, created_at: 100.days.ago } + let(:strike) { Fabricate.build :account_warning, created_at: (described_class::MAX_STRIKE_AGE * 2).ago } it { is_expected.to_not allow_values(strike).for(:strike).against(:base).on(:create) } end diff --git a/spec/policies/backup_policy_spec.rb b/spec/policies/backup_policy_spec.rb index 031021d91d..28995d195b 100644 --- a/spec/policies/backup_policy_spec.rb +++ b/spec/policies/backup_policy_spec.rb @@ -23,22 +23,30 @@ RSpec.describe BackupPolicy do context 'when backups are too old' do it 'permits' do - travel(-8.days) do + travel(-before_time) do Fabricate(:backup, user: john.user) end expect(subject).to permit(john, Backup) end + + def before_time + described_class::MIN_AGE + 2.days + end end context 'when backups are newer' do it 'denies' do - travel(-3.days) do + travel(-within_time) do Fabricate(:backup, user: john.user) end expect(subject).to_not permit(john, Backup) end + + def within_time + described_class::MIN_AGE - 2.days + end end end end diff --git a/spec/requests/api/v1/accounts/credentials_spec.rb b/spec/requests/api/v1/accounts/credentials_spec.rb index 0bd3ace132..0badc17e1b 100644 --- a/spec/requests/api/v1/accounts/credentials_spec.rb +++ b/spec/requests/api/v1/accounts/credentials_spec.rb @@ -85,7 +85,7 @@ RSpec.describe 'credentials API' do end describe 'with invalid data' do - let(:params) { { note: 'This is too long. ' * 30 } } + let(:params) { { note: 'a' * 2 * Account::NOTE_LENGTH_LIMIT } } it 'returns http unprocessable entity' do subject diff --git a/spec/requests/api/v1/accounts/notes_spec.rb b/spec/requests/api/v1/accounts/notes_spec.rb index 1677ec07e3..e616df1e6f 100644 --- a/spec/requests/api/v1/accounts/notes_spec.rb +++ b/spec/requests/api/v1/accounts/notes_spec.rb @@ -29,7 +29,7 @@ RSpec.describe 'Accounts Notes API' do end context 'when account note exceeds allowed length', :aggregate_failures do - let(:comment) { 'a' * 2_001 } + let(:comment) { 'a' * AccountNote::COMMENT_SIZE_LIMIT * 2 } it 'does not create account note' do subject diff --git a/spec/requests/api/v2/media_spec.rb b/spec/requests/api/v2/media_spec.rb index 807e427d3f..18ebb9cdda 100644 --- a/spec/requests/api/v2/media_spec.rb +++ b/spec/requests/api/v2/media_spec.rb @@ -33,7 +33,7 @@ RSpec.describe 'Media API', :attachment_processing do let(:params) do { file: fixture_file_upload('attachment-jpg.123456_abcd', 'image/jpeg'), - description: 'aa' * MediaAttachment::MAX_DESCRIPTION_LENGTH, + description: 'a' * MediaAttachment::MAX_DESCRIPTION_LENGTH * 2, } end From dd937e115a42b1029038578217bb105c56a0f4a4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 7 Jan 2025 02:47:58 -0500 Subject: [PATCH 024/133] Use `distributable?` method in admin/status_policy method (#33477) --- app/policies/admin/status_policy.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/policies/admin/status_policy.rb b/app/policies/admin/status_policy.rb index e9379c25ec..c4ba5c2606 100644 --- a/app/policies/admin/status_policy.rb +++ b/app/policies/admin/status_policy.rb @@ -12,7 +12,7 @@ class Admin::StatusPolicy < ApplicationPolicy end def show? - role.can?(:manage_reports, :manage_users) && (record.public_visibility? || record.unlisted_visibility? || record.reported? || viewable_through_normal_policy?) + role.can?(:manage_reports, :manage_users) && eligible_to_show? end def destroy? @@ -29,6 +29,10 @@ class Admin::StatusPolicy < ApplicationPolicy private + def eligible_to_show? + record.distributable? || record.reported? || viewable_through_normal_policy? + end + def viewable_through_normal_policy? StatusPolicy.new(current_account, record, @preloaded_relations).show? end From edf62d4fe376d91ce194907b13599305846d4e42 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 7 Jan 2025 02:50:19 -0500 Subject: [PATCH 025/133] Add `self_editing?` method to user role policy (#33476) --- app/policies/user_role_policy.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/policies/user_role_policy.rb b/app/policies/user_role_policy.rb index 6144a0ec4a..44b5589581 100644 --- a/app/policies/user_role_policy.rb +++ b/app/policies/user_role_policy.rb @@ -10,10 +10,16 @@ class UserRolePolicy < ApplicationPolicy end def update? - role.can?(:manage_roles) && (role.overrides?(record) || role.id == record.id) + role.can?(:manage_roles) && (role.overrides?(record) || self_editing?) end def destroy? - !record.everyone? && role.can?(:manage_roles) && role.overrides?(record) && role.id != record.id + !record.everyone? && role.can?(:manage_roles) && role.overrides?(record) && !self_editing? + end + + private + + def self_editing? + role.id == record.id end end From 9715bd796f06f933493ff9f9b3ea2f25a2a2a874 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 08:50:27 +0100 Subject: [PATCH 026/133] New Crowdin Translations (automated) (#33480) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/da.json | 1 + app/javascript/mastodon/locales/fo.json | 2 ++ app/javascript/mastodon/locales/ko.json | 1 + app/javascript/mastodon/locales/lv.json | 3 +- app/javascript/mastodon/locales/sv.json | 34 +++++++++++++++----- config/locales/fo.yml | 42 +++++++++++++++++++++++++ config/locales/simple_form.fo.yml | 22 +++++++++++++ 7 files changed, 96 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 92cfb62f51..ad81e5f35a 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -482,6 +482,7 @@ "lists.exclusive": "Skjul medlemmer i Hjem", "lists.exclusive_hint": "Er nogen er på denne liste, skjul personen i hjemme-feeds for at undgå at se vedkommendes indlæg to gange.", "lists.find_users_to_add": "Find brugere at tilføje", + "lists.list_members": "Liste over medlemmer", "lists.list_members_count": "{count, plural, one {# medlem} other {# medlemmer}}", "lists.list_name": "Listetitel", "lists.new_list_name": "Ny listetitel", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index c4d5f7296a..91acfb0e15 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -362,6 +362,7 @@ "footer.privacy_policy": "Privatlívspolitikkur", "footer.source_code": "Vís keldukotuna", "footer.status": "Støða", + "footer.terms_of_service": "Tænastutreytir", "generic.saved": "Goymt", "getting_started.heading": "At byrja", "hashtag.admin_moderation": "Lat umsjónarmarkamót upp fyri #{name}", @@ -858,6 +859,7 @@ "subscribed_languages.target": "Broyt haldaramál fyri {target}", "tabs_bar.home": "Heim", "tabs_bar.notifications": "Fráboðanir", + "terms_of_service.title": "Tænastutreytir", "time_remaining.days": "{number, plural, one {# dagur} other {# dagar}} eftir", "time_remaining.hours": "{number, plural, one {# tími} other {# tímar}} eftir", "time_remaining.minutes": "{number, plural, one {# minuttur} other {# minuttir}} eftir", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 9593744e4b..abaf40931c 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -414,6 +414,7 @@ "interaction_modal.title.reblog": "{name} 님의 게시물을 부스트", "interaction_modal.title.reply": "{name} 님의 게시물에 답글", "interaction_modal.title.vote": "{name} 님의 투표에 참여", + "interaction_modal.username_prompt": "예시: {example}", "intervals.full.days": "{number} 일", "intervals.full.hours": "{number} 시간", "intervals.full.minutes": "{number} 분", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 1bc3c68382..9244f3509c 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -52,7 +52,7 @@ "account.mute_notifications_short": "Izslēgt paziņojumu skaņu", "account.mute_short": "Apklusināt", "account.muted": "Apklusināts", - "account.mutual": "Savstarpējs", + "account.mutual": "Abpusēji", "account.no_bio": "Apraksts nav sniegts.", "account.open_original_page": "Atvērt oriģinālo lapu", "account.posts": "Ieraksti", @@ -85,6 +85,7 @@ "alert.rate_limited.title": "Biežums ierobežots", "alert.unexpected.message": "Radās negaidīta kļūda.", "alert.unexpected.title": "Ups!", + "alt_text_badge.title": "Alt teksts", "announcement.announcement": "Paziņojums", "annual_report.summary.archetype.oracle": "Orākuls", "annual_report.summary.archetype.replier": "Sabiedriskais tauriņš", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index d53825295f..a9b299c97f 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -141,7 +141,7 @@ "column.bookmarks": "Bokmärken", "column.community": "Lokal tidslinje", "column.create_list": "Skapa lista", - "column.direct": "Privata nämningar", + "column.direct": "Privata omnämnande", "column.directory": "Bläddra bland profiler", "column.domain_blocks": "Blockerade domäner", "column.edit_list": "Redigera lista", @@ -239,6 +239,10 @@ "disabled_account_banner.text": "Ditt konto {disabledAccount} är för närvarande inaktiverat.", "dismissable_banner.community_timeline": "Dessa är de senaste offentliga inläggen från personer vars konton tillhandahålls av {domain}.", "dismissable_banner.dismiss": "Avfärda", + "dismissable_banner.explore_links": "Dessa nyhetshistorier delas mest på fediversum idag. Nyare nyhetshistorier som publiceras av fler olika personer rankas högre.", + "dismissable_banner.explore_statuses": "Dessa inlägg från fediverse vinner dragkraft idag. Nyare inlägg som många boostar och favoritmarkerar rankas högre.", + "dismissable_banner.explore_tags": "De här hashtaggarna vinner dragkraft i fediversum idag. Hashtaggar som används av fler olika personer rankas högre.", + "dismissable_banner.public_timeline": "De här är de aktuella publika inlägg från personer i fediversum som personer i {domain} följer.", "domain_block_modal.block": "Blockera server", "domain_block_modal.block_account_instead": "Blockera @{name} istället", "domain_block_modal.they_can_interact_with_old_posts": "Personer från denna server kan interagera med dina gamla inlägg.", @@ -285,7 +289,7 @@ "empty_column.blocks": "Du har ännu ej blockerat några användare.", "empty_column.bookmarked_statuses": "Du har inte bokmärkt några inlägg än. När du bokmärker ett inlägg kommer det synas här.", "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att sätta bollen i rullning!", - "empty_column.direct": "Du har inga privata nämningar. När du skickar eller tar emot ett direktmeddelande kommer det att visas här.", + "empty_column.direct": "Du har inga privata omnämninande. När du skickar eller tar emot ett direktmeddelande kommer det att visas här.", "empty_column.domain_blocks": "Det finns ännu inga dolda domäner.", "empty_column.explore_statuses": "Ingenting är trendigt just nu. Kom tillbaka senare!", "empty_column.favourited_statuses": "Du har inga favoritmarkerade inlägg ännu. När du favoritmärker ett så kommer det att dyka upp här.", @@ -402,7 +406,14 @@ "ignore_notifications_modal.new_accounts_title": "Vill du ignorera aviseringar från nya konton?", "ignore_notifications_modal.not_followers_title": "Vill du ignorera aviseringar från personer som inte följer dig?", "ignore_notifications_modal.not_following_title": "Vill du blockera aviseringar från personer som du inte följer dig?", - "ignore_notifications_modal.private_mentions_title": "Vill du ignorera aviseringar från oönskade privata omnämningar?", + "ignore_notifications_modal.private_mentions_title": "Vill du ignorera aviseringar från oombedda privata omnämnanden?", + "interaction_modal.action.favourite": "För att fortsätta, måste du favoritmarkera från ditt konto.", + "interaction_modal.action.follow": "För att fortsätta, måste du följa från ditt konto.", + "interaction_modal.action.reblog": "För att fortsätta, måste du boosta från ditt konto.", + "interaction_modal.action.reply": "För att fortsätta, måste du svara från ditt konto.", + "interaction_modal.action.vote": "För att fortsätta, måste du rösta från ditt konto.", + "interaction_modal.go": "Vidare", + "interaction_modal.no_account_yet": "Har du inget konto än?", "interaction_modal.on_another_server": "På en annan server", "interaction_modal.on_this_server": "På denna server", "interaction_modal.title.favourite": "Favoritmarkera {name}s inlägg", @@ -410,6 +421,7 @@ "interaction_modal.title.reblog": "Boosta {name}s inlägg", "interaction_modal.title.reply": "Svara på {name}s inlägg", "interaction_modal.title.vote": "Rösta i {name}s enkät", + "interaction_modal.username_prompt": "T.ex. {example}", "intervals.full.days": "{number, plural, one {# dag} other {# dagar}}", "intervals.full.hours": "{number, plural, one {# timme} other {# timmar}}", "intervals.full.minutes": "{number, plural, one {# minut} other {# minuter}}", @@ -419,7 +431,7 @@ "keyboard_shortcuts.column": "Fokusera kolumn", "keyboard_shortcuts.compose": "Fokusera skrivfältet", "keyboard_shortcuts.description": "Beskrivning", - "keyboard_shortcuts.direct": "för att öppna privata nämningskolumnen", + "keyboard_shortcuts.direct": "för att öppna privata omnämnandekolumnen", "keyboard_shortcuts.down": "Flytta ner i listan", "keyboard_shortcuts.enter": "Öppna inlägg", "keyboard_shortcuts.favourite": "Favoritmarkera inlägg", @@ -445,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Visa/gömma text bakom CW", "keyboard_shortcuts.toggle_sensitivity": "Visa/gömma media", "keyboard_shortcuts.toot": "Starta nytt inlägg", + "keyboard_shortcuts.translate": "för att översätta ett inlägg", "keyboard_shortcuts.unfocus": "Avfokusera skrivfält/sökfält", "keyboard_shortcuts.up": "Flytta uppåt i listan", "lightbox.close": "Stäng", @@ -466,6 +479,7 @@ "lists.delete": "Radera lista", "lists.done": "Klar", "lists.edit": "Redigera lista", + "lists.exclusive": "Dölj medlemmar i Hem flödet", "lists.exclusive_hint": "Om någon är med på den här listan, göm dem i ditt Hemtidlinje för att undvika att se deras inlägg två gånger.", "lists.find_users_to_add": "Hitta användare att lägga till", "lists.list_members": "Lista medlemmar", @@ -474,12 +488,14 @@ "lists.new_list_name": "Nytt listnamn", "lists.no_lists_yet": "Ännu inga listor.", "lists.no_members_yet": "Inga medlemmar ännu.", + "lists.no_results_found": "Inga resultat hittades.", "lists.remove_member": "Ta bort", "lists.replies_policy.followed": "Alla användare som följs", "lists.replies_policy.list": "Medlemmar i listan", "lists.replies_policy.none": "Ingen", "lists.save": "Spara", "lists.search": "Sök", + "lists.show_replies_to": "Inkludera svar från listmedlemmar till", "load_pending": "{count, plural, one {# nytt objekt} other {# nya objekt}}", "loading_indicator.label": "Laddar…", "media_gallery.hide": "Dölj", @@ -500,7 +516,7 @@ "navigation_bar.bookmarks": "Bokmärken", "navigation_bar.community_timeline": "Lokal tidslinje", "navigation_bar.compose": "Författa nytt inlägg", - "navigation_bar.direct": "Privata nämningar", + "navigation_bar.direct": "Privata omnämnande", "navigation_bar.discover": "Upptäck", "navigation_bar.domain_blocks": "Dolda domäner", "navigation_bar.explore": "Utforska", @@ -532,12 +548,14 @@ "notification.annual_report.view": "Visa #Wrapstodon", "notification.favourite": "{name} favoritmarkerade ditt inlägg", "notification.favourite.name_and_others_with_link": "{name} och {count, plural, one {# annan} other {# andra}} har favoritmarkerat ditt inlägg", + "notification.favourite_pm": "{name} favoritmarkerade ditt privata omnämnande", + "notification.favourite_pm.name_and_others_with_link": "{name} och {count, plural, one {# annan} other {# andra}} favoritmarkerade ditt privata omnämnande", "notification.follow": "{name} följer dig", "notification.follow.name_and_others": "{name} och {count, plural, one {# annan} other {# andra}} följer dig", "notification.follow_request": "{name} har begärt att följa dig", "notification.follow_request.name_and_others": "{name} och {count, plural, one {# en annan} other {# andra}} har bett att följa dig", "notification.label.mention": "Nämn", - "notification.label.private_mention": "Privat nämning", + "notification.label.private_mention": "Privat omnämnande", "notification.label.private_reply": "Privata svar", "notification.label.reply": "Svar", "notification.mention": "Nämn", @@ -783,8 +801,8 @@ "status.copy": "Kopiera inläggslänk", "status.delete": "Radera", "status.detailed_status": "Detaljerad samtalsvy", - "status.direct": "Nämn @{name} privat", - "status.direct_indicator": "Privat nämning", + "status.direct": "Omnämn @{name} privat", + "status.direct_indicator": "Privat omnämnande", "status.edit": "Redigera", "status.edited": "Senast ändrad {date}", "status.edited_x_times": "Redigerad {count, plural, one {{count} gång} other {{count} gånger}}", diff --git a/config/locales/fo.yml b/config/locales/fo.yml index 00ffed90bb..441217ec0e 100644 --- a/config/locales/fo.yml +++ b/config/locales/fo.yml @@ -214,6 +214,7 @@ fo: enable_user: Ger brúkara virknan memorialize_account: Minnst til Konto promote_user: Vís fram Brúkara + publish_terms_of_service: Útgev tænastutreytir reject_appeal: Avvís mótmali reject_user: Avvís Brúkara remove_avatar_user: Sletta Avatar @@ -278,6 +279,7 @@ fo: enable_user_html: "%{name} gjørdi innritan virkna fyri brúkaran %{target}" memorialize_account_html: "%{name} broytti kontuna hjá %{target} til eina minnissíðu" promote_user_html: "%{name} flutti brúkaran %{target} fram" + publish_terms_of_service_html: "%{name} útgav dagføringar til tænastutreytirnar" reject_appeal_html: "%{name} avvísti umsjónaráheitan frá %{target}" reject_user_html: "%{name} avvísti skráseting hjá %{target}" remove_avatar_user_html: "%{name} strikaði eftirgjørda skapningin hjá %{target}" @@ -925,6 +927,32 @@ fo: search: Leita title: Frámerki updated_msg: Frámerkjastillingar dagførdar + terms_of_service: + back: Aftur til tænastutreytir + changelog: Hvat er broytt + create: Brúka tínar egnu + current: Núverandi + draft: Kladda + generate: Brúka leist + generates: + action: Framleið + chance_to_review_html: "Framleiddu tænastutreytirnar verða ikki útgivnar av sær sjálvum. Tú fær møguleika at eftirhyggja úrslitini. Vinarliga útfyll neyðugu smálutirnar fyri at halda fram." + explanation_html: Leisturin við tænastutreytum er einans til kunningar og skal ikki fatast sum løgfrøðislig ráðgeving yvirhøvur. Vinarliga spyr tín egna løgfrøðisliga ráðgeva um tína støðu og ítøkiligu løgfrøðisligu spurningarnar hjá tær. + title: Uppseting av tænastutreytum + history: Søga + live: Beinleiðis + no_history: Enn eru ongar skrásettar broytingar í tænastutreytunum. + no_terms_of_service_html: Í løtuni hevur tú ongar tænastutreytir uppsettar. Hugsanin við tænastutreytum er at veita greidleika og at verja teg ímóti møguligum ábyrgdum í ósemjum við tínar brúkarar. + notified_on_html: Fráboðan latin brúkarum %{date} + notify_users: Gev brúkarum fráboðan + preview: + explanation_html: 'Teldubrævið verður sent til %{display_count} brúkarar, sum hava stovna kontu áðrenn %{date}. Fylgjandi tekstur kemur við í teldubrævið:' + send_preview: Send undanvísing til %{email} + title: Undanvís fráboðan um tænastutreytir + publish: Útgev + published_on_html: Útgivið %{date} + save_draft: Goym kladdu + title: Tænastutreytir title: Umsiting trends: allow: Loyv @@ -1156,6 +1184,7 @@ fo: set_new_password: Áset nýtt loyniorð setup: email_below_hint_html: Kekka mappuna við ruskposti ella bið um ein annan. Tú kanst rætta teldupostadressuna, um hon er skeiv. + email_settings_hint_html: Trýst á leinkið, sum vit sendu til %{email} fyri at byrja at brúka Mastodon. Vit bíða beint her. link_not_received: Fekk tú einki leinki? new_confirmation_instructions_sent: Tú fer at móttaka eitt nýtt teldubræv við váttanarleinkinum um nakrar fáar minuttir! title: Kekka innbakkan hjá tær @@ -1164,6 +1193,7 @@ fo: title: Rita inn á %{domain} sign_up: manual_review: Tilmeldingar til %{domain} fara ígjøgnum eina manuella eftirkanning av okkara kjakleiðarum. Fyri at hjálpa okkum at skunda undir skrásetingina, skriva eitt sindur um teg sjálva/n og hví tú vil hava eina kontu á %{domain}. + preamble: Við eini kontu á hesum Mastodon ambætaranum ber til hjá tær at fylgja ein og hvønn annan persón á fediversinum, óansæð hvar teirra konta er hýst. title: Latum okkum fáa teg settan upp á %{domain}. status: account_status: Kontustøða @@ -1175,6 +1205,7 @@ fo: view_strikes: Vís eldri atsóknir móti tíni kontu too_fast: Oyðublaðið innsent ov skjótt, royn aftur. use_security_key: Brúka trygdarlykil + user_agreement_html: Eg havi lisið og taki undir við tænastutreytunum og privatlívspolitikkinum author_attribution: example_title: Tekstadømi hint_html: Skrivar tú tíðindi ella greinar til bloggin uttanfyri Mastodon? Her kanst tú stýra, hvussu tú verður tilsipað/ur, tá ið títt tilfar verður deilt á Mastodon. @@ -1836,6 +1867,8 @@ fo: too_late: Tað er ov seint at kæra hesa atsókn tags: does_not_match_previous_name: samsvarar ikki við undanfarna navnið + terms_of_service: + title: Tænastutreytir themes: contrast: Mastodon (høgur kontrastur) default: Mastodon (myrkt) @@ -1896,6 +1929,15 @@ fo: further_actions_html: Var hetta ikki tú, so mæla vit til, at tú %{action} beinan vegin og at tú ger váttan í tveimum stigum virkna fyri at konta tín kann vera trygg. subject: Atgongd er fingin til kontu tína frá eini nýggjari IP adressu title: Ein nýggj innritan + terms_of_service_changed: + agreement: Við framhaldandi at brúka %{domain} góðtekur tú hesar treytir. Tekur tú ikki undir við dagførdu treytunum, so kanst tú til einhvørja tíð uppsiga avtaluna við %{domain} við at strika kontu tína. + changelog: 'Í stuttum merkir henda dagføringin:' + description: 'Tú móttekur hetta teldubrævið, tí at vit gera nakrar broytingar í okkara tænastutreytum á %{domain}. Vit eggja tær til at eftirhyggja dagførdu treytirnar her:' + description_html: Tú móttekur hetta teldubrævið, tí at vit gera nakrar broytingar í okkara tænastutreytum á %{domain}. Vit eggja tær til at eftirhyggja dagførdu og samlaðu treytirnar her. + sign_off: "%{domain} toymið" + subject: Dagføringar til okkara tænastutreytir + subtitle: Tænastutreytirnar hjá %{domain} eru við at verða broyttar + title: Týdningarmikil dagføring warning: appeal: Innsend eina kæru appeal_description: Trýrt tú, at hetta er ein feilur, so kanst tú senda eina kæru til starvsfólkini á %{instance}. diff --git a/config/locales/simple_form.fo.yml b/config/locales/simple_form.fo.yml index 0c9ec51722..99bb1ccaa0 100644 --- a/config/locales/simple_form.fo.yml +++ b/config/locales/simple_form.fo.yml @@ -130,6 +130,17 @@ fo: show_application: Óansæð, so er altíð møguligt hjá tær at síggja, hvør app postaði tín post. tag: name: Tú kanst einans broyta millum stórar og smáar stavir, til dømis fyri at gera tað meira lesiligt + terms_of_service: + changelog: Kunnu vera uppbygdar við Markdown syntaksi. + text: Kunnu vera uppbygdar við Markdown syntaksi. + terms_of_service_generator: + admin_email: Løgfrøðisligar fráboðanir fata um rættarligar mótfráboðanir, úrskurðir, áheitanir um at taka niður og løgfrøðisligar áheitanir. + arbitration_address: Kann vera tann sami sum fysiski bústaðurin omanfyri ella "N/A", um teldupostur verður brúktur + arbitration_website: Kann vera ein vevformularur ella "N/A", um teldupostur verður brúktur + dmca_address: Fyristøðufólk og -feløg í USA brúka bústaðin, ið er skrásettur í DMCA Designated Agent Directory. Ein postsmoguskráseting er tøk, um biðið verður um hana beinleiðis. Brúka DMCA Designated Agent Post Office Box Waiver Request fyri at senda teldubræv til Copyright Office og greið frá, at tú er ein kjakleiðari, sum virkar heimanifrá, og at tú er bangin fyri hevnd ella afturløning fyri tí, tú ger, og at tú hevur tørv á at brúka eina postsmogu fyri at fjala heimabústaðin fyri almenninginum. + dmca_email: Kann vera sami teldupoststaður, sum er brúktur til "Teldupoststaður fyri løgfrøðisligar fráboðanir" omanfyri + domain: Makaleys eyðmerking av nettænastuni, sum tú veitir. + jurisdiction: Lista landið, har sum tann, ið rindar rokningarnar, livir. Er tað eitt felag ella ein onnur eind, lista landið, har tað er skrásett, umframt býin, økið, umveldið ella statin, alt eftir hvat er hóskandi. user: chosen_languages: Tá hetta er valt, verða einans postar í valdum málum vístir á almennum tíðarlinjum role: Leikluturin stýrir hvørji rættindi, brúkarin hevur. @@ -319,6 +330,17 @@ fo: name: Tvíkrossur trendable: Loyv hesum frámerki at síggjast undir rákum usable: Loyv postum at brúka hetta frámerki lokalt + terms_of_service: + changelog: Hvat er broytt? + text: Tænastutreytir + terms_of_service_generator: + admin_email: Teldupoststaður fyri løgfrøðisligar fráboðanir + arbitration_address: Fysisk adressa fyri gerðarrættarfráboðanir + arbitration_website: Heimasíða har gerðarrættarfráboðanir kunnu innlatast + dmca_address: Fysiskur bústaður fyri DMCA/copyright fráboðanir + dmca_email: Teldubústaður fyri DMCA/copyright fráboðanir + domain: Navnaøki + jurisdiction: Løgdømi user: role: Leiklutur time_zone: Tíðarsona From 8233293429baa8721f0193d3228df793f3c3f882 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:28:06 +0000 Subject: [PATCH 027/133] Update DefinitelyTyped types (non-major) (#33220) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Renaud Chaput --- package.json | 2 ++ yarn.lock | 44 ++++++++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 6eee16dc28..a4817816a2 100644 --- a/package.json +++ b/package.json @@ -198,6 +198,8 @@ "webpack-dev-server": "^3.11.3" }, "resolutions": { + "@types/react": "^18.2.7", + "@types/react-dom": "^18.2.4", "kind-of": "^6.0.3", "webpack/terser-webpack-plugin": "^4.2.3" }, diff --git a/yarn.lock b/yarn.lock index 8278a45d40..119c85c7af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3830,12 +3830,12 @@ __metadata: linkType: hard "@types/hoist-non-react-statics@npm:3, @types/hoist-non-react-statics@npm:^3.3.1": - version: 3.3.5 - resolution: "@types/hoist-non-react-statics@npm:3.3.5" + version: 3.3.6 + resolution: "@types/hoist-non-react-statics@npm:3.3.6" dependencies: "@types/react": "npm:*" hoist-non-react-statics: "npm:^3.3.0" - checksum: 10c0/2a3b64bf3d9817d7830afa60ee314493c475fb09570a64e7737084cd482d2177ebdddf888ce837350bac51741278b077683facc9541f052d4bbe8487b4e3e618 + checksum: 10c0/149a4c217d81f21f8a1e152160a59d5b99b6a9aa6d354385d5f5bc02760cbf1e170a8442ba92eb653befff44b0c5bc2234bb77ce33e0d11a65f779e8bab5c321 languageName: node linkType: hard @@ -4015,9 +4015,9 @@ __metadata: linkType: hard "@types/prop-types@npm:*, @types/prop-types@npm:^15.7.5": - version: 15.7.13 - resolution: "@types/prop-types@npm:15.7.13" - checksum: 10c0/1b20fc67281902c6743379960247bc161f3f0406ffc0df8e7058745a85ea1538612109db0406290512947f9632fe9e10e7337bf0ce6338a91d6c948df16a7c61 + version: 15.7.14 + resolution: "@types/prop-types@npm:15.7.14" + checksum: 10c0/1ec775160bfab90b67a782d735952158c7e702ca4502968aa82565bd8e452c2de8601c8dfe349733073c31179116cf7340710160d3836aa8a1ef76d1532893b1 languageName: node linkType: hard @@ -4057,11 +4057,11 @@ __metadata: linkType: hard "@types/react-dom@npm:^18.2.4": - version: 18.3.1 - resolution: "@types/react-dom@npm:18.3.1" - dependencies: - "@types/react": "npm:*" - checksum: 10c0/8b416551c60bb6bd8ec10e198c957910cfb271bc3922463040b0d57cf4739cdcd24b13224f8d68f10318926e1ec3cd69af0af79f0291b599a992f8c80d47f1eb + version: 18.3.5 + resolution: "@types/react-dom@npm:18.3.5" + peerDependencies: + "@types/react": ^18.0.0 + checksum: 10c0/b163d35a6b32a79f5782574a7aeb12a31a647e248792bf437e6d596e2676961c394c5e3c6e91d1ce44ae90441dbaf93158efb4f051c0d61e2612f1cb04ce4faa languageName: node linkType: hard @@ -4124,20 +4124,20 @@ __metadata: linkType: hard "@types/react-swipeable-views@npm:^0.13.1": - version: 0.13.5 - resolution: "@types/react-swipeable-views@npm:0.13.5" + version: 0.13.6 + resolution: "@types/react-swipeable-views@npm:0.13.6" dependencies: "@types/react": "npm:*" - checksum: 10c0/d1dcc78d862f37d30a43d79d915fdb388e05dce0b2ac07462ca4f1b00e0eef37cb41d75997f5685dec79bcce1ffee0dfbc744f20d5266dd3090658def5b4e193 + checksum: 10c0/a26879146748417234bb7f44c5a71e6bab2b76c0b34c72f0493c18403487a5d77021510e8665bd8bd22786904fbbd90d6db55c8dd2bf983c32421139de851c94 languageName: node linkType: hard "@types/react-test-renderer@npm:^18.0.0": - version: 18.3.0 - resolution: "@types/react-test-renderer@npm:18.3.0" + version: 18.3.1 + resolution: "@types/react-test-renderer@npm:18.3.1" dependencies: - "@types/react": "npm:*" - checksum: 10c0/3c9748be52e8e659e7adf91dea6939486463264e6f633bf21c4cb116de18af7bef0595568a1e588160420b2f65289473075dda1cb417c2875df8cf7a09f5d913 + "@types/react": "npm:^18" + checksum: 10c0/9fc8467ff1a3f14be6cc3498a75fc788d2c92c0fffa7bf21269ed5d9d82db9195bf2178ddc42ea16a0836995c1b77601c6be8abb27bd1864668c418c6d0e5a3b languageName: node linkType: hard @@ -4159,13 +4159,13 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7": - version: 18.3.12 - resolution: "@types/react@npm:18.3.12" +"@types/react@npm:^18.2.7": + version: 18.3.18 + resolution: "@types/react@npm:18.3.18" dependencies: "@types/prop-types": "npm:*" csstype: "npm:^3.0.2" - checksum: 10c0/8bae8d9a41619804561574792e29112b413044eb0d53746dde2b9720c1f9a59f71c895bbd7987cd8ce9500b00786e53bc032dced38cddf42910458e145675290 + checksum: 10c0/8fb2b00672072135d0858dc9db07873ea107cc238b6228aaa2a9afd1ef7a64a7074078250db38afbeb19064be8ea6af5eac32d404efdd5f45e093cc4829d87f8 languageName: node linkType: hard From c12b85e7a94a04cb391b13ba2f5ae6b69315cef7 Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Tue, 7 Jan 2025 06:24:52 -0500 Subject: [PATCH 028/133] Fix Style/MultipleComparison (#33313) Co-authored-by: Matt Jankowski --- .rubocop/style.yml | 3 --- app/lib/activitypub/tag_manager.rb | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.rubocop/style.yml b/.rubocop/style.yml index 7dd4299c3e..df1da2ba36 100644 --- a/.rubocop/style.yml +++ b/.rubocop/style.yml @@ -29,9 +29,6 @@ Style/IfUnlessModifier: Style/KeywordArgumentsMerging: Enabled: false -Style/MultipleComparison: - Enabled: false - Style/NumericLiterals: AllowedPatterns: - \d{4}_\d{2}_\d{2}_\d{6} diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 23b44be372..a489928407 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -13,7 +13,7 @@ class ActivityPub::TagManager }.freeze def public_collection?(uri) - uri == COLLECTIONS[:public] || uri == 'as:Public' || uri == 'Public' + uri == COLLECTIONS[:public] || %w(as:Public Public).include?(uri) end def url_for(target) From 82e046ea0661e23681304d0e3b95d59736256d21 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 7 Jan 2025 12:29:38 +0100 Subject: [PATCH 029/133] =?UTF-8?q?Fix=20preview=20card=20sizing=20in=20?= =?UTF-8?q?=E2=80=9CAuthor=20attribution=E2=80=9D=20in=20profile=20setting?= =?UTF-8?q?s=20(#33482)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/javascript/styles/mastodon/forms.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index d79ab2aa21..82c53de5f7 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -659,6 +659,10 @@ code { } } } + + .status-card { + contain: unset; + } } .block-icon { From 927c7bb6bb5ac1bd86bad776d6260e22f6aae4f5 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 7 Jan 2025 08:46:56 -0500 Subject: [PATCH 030/133] Use ruby version 3.4.1 (#33304) --- .github/ISSUE_TEMPLATE/2.server_bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/3.troubleshooting.yml | 2 +- .github/renovate.json5 | 5 ----- .github/workflows/test-ruby.yml | 4 ++++ .ruby-version | 2 +- Dockerfile | 6 +++--- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/2.server_bug_report.yml b/.github/ISSUE_TEMPLATE/2.server_bug_report.yml index a66f5c1076..b868c672f3 100644 --- a/.github/ISSUE_TEMPLATE/2.server_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/2.server_bug_report.yml @@ -60,7 +60,7 @@ body: Any additional technical details you may have, like logs or error traces value: | If this is happening on your own Mastodon server, please fill out those: - - Ruby version: (from `ruby --version`, eg. v3.3.5) + - Ruby version: (from `ruby --version`, eg. v3.4.1) - Node.js version: (from `node --version`, eg. v20.18.0) validations: required: false diff --git a/.github/ISSUE_TEMPLATE/3.troubleshooting.yml b/.github/ISSUE_TEMPLATE/3.troubleshooting.yml index eeb74b160b..fa9bfc7c80 100644 --- a/.github/ISSUE_TEMPLATE/3.troubleshooting.yml +++ b/.github/ISSUE_TEMPLATE/3.troubleshooting.yml @@ -61,7 +61,7 @@ body: value: | Please at least include those informations: - Operating system: (eg. Ubuntu 22.04) - - Ruby version: (from `ruby --version`, eg. v3.3.5) + - Ruby version: (from `ruby --version`, eg. v3.4.1) - Node.js version: (from `node --version`, eg. v20.18.0) validations: required: false diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 3d3499922e..8a10676283 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -14,11 +14,6 @@ // If we do not want a package to be grouped with others, we need to set its groupName // to `null` after any other rule set it to something. dependencyDashboardHeader: 'This issue lists Renovate updates and detected dependencies. Read the [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) docs to learn more. Before approving any upgrade: read the description and comments in the [`renovate.json5` file](https://github.com/mastodon/mastodon/blob/main/.github/renovate.json5).', - constraints: { - // Mastodon should work on Ruby 3.4, but its test dependencies are currently uninstallable on Ruby 3.4. - // TODO: remove this once https://github.com/briandunn/flatware/issues/103 is fixed - ruby: '3.3', - }, postUpdateOptions: ['yarnDedupeHighest'], packageRules: [ { diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 900faad07f..4deb08d328 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -125,6 +125,7 @@ jobs: matrix: ruby-version: - '3.2' + - '3.3' - '.ruby-version' steps: - uses: actions/checkout@v4 @@ -226,6 +227,7 @@ jobs: matrix: ruby-version: - '3.2' + - '3.3' - '.ruby-version' steps: - uses: actions/checkout@v4 @@ -304,6 +306,7 @@ jobs: matrix: ruby-version: - '3.2' + - '3.3' - '.ruby-version' steps: @@ -420,6 +423,7 @@ jobs: matrix: ruby-version: - '3.2' + - '3.3' - '.ruby-version' search-image: - docker.elastic.co/elasticsearch/elasticsearch:7.17.13 diff --git a/.ruby-version b/.ruby-version index 9c25013dbb..47b322c971 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.6 +3.4.1 diff --git a/Dockerfile b/Dockerfile index d80a4e1555..97c7a91499 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,9 @@ ARG TARGETPLATFORM=${TARGETPLATFORM} ARG BUILDPLATFORM=${BUILDPLATFORM} -# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"] +# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"] # renovate: datasource=docker depName=docker.io/ruby -ARG RUBY_VERSION="3.3.6" +ARG RUBY_VERSION="3.4.1" # # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] # renovate: datasource=node-version depName=node ARG NODE_MAJOR_VERSION="22" @@ -20,7 +20,7 @@ ARG NODE_MAJOR_VERSION="22" ARG DEBIAN_VERSION="bookworm" # Node image to use for base image based on combined variables (ex: 20-bookworm-slim) FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node -# Ruby image to use for base image based on combined variables (ex: 3.3.x-slim-bookworm) +# Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm) FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA diff --git a/Gemfile b/Gemfile index ad174cfdc4..d2e6769ace 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ # frozen_string_literal: true source 'https://rubygems.org' -ruby '>= 3.2.0', '< 3.5' +ruby '>= 3.2.0', '< 3.5.0' gem 'propshaft' gem 'puma', '~> 6.3' diff --git a/Gemfile.lock b/Gemfile.lock index f49de6bf3b..afb06744ed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -238,11 +238,11 @@ GEM ffi-compiler (1.3.2) ffi (>= 1.15.5) rake - flatware (2.3.3) + flatware (2.3.4) drb thor (< 2.0) - flatware-rspec (2.3.3) - flatware (= 2.3.3) + flatware-rspec (2.3.4) + flatware (= 2.3.4) rspec (>= 3.6) fog-core (2.5.0) builder @@ -1034,7 +1034,7 @@ DEPENDENCIES xorcist (~> 1.1) RUBY VERSION - ruby 3.3.6p108 + ruby 3.4.1p0 BUNDLED WITH 2.6.2 From fdfbf6e04ee398b3fe39bf2dbddb810c63cb395f Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 7 Jan 2025 09:11:12 -0500 Subject: [PATCH 031/133] Set statement timeout to zero in maintenance CLI (#33484) --- lib/mastodon/cli/maintenance.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb index 532fbc328a..32ee35c7c7 100644 --- a/lib/mastodon/cli/maintenance.rb +++ b/lib/mastodon/cli/maintenance.rb @@ -192,6 +192,7 @@ module Mastodon::CLI verify_schema_version! verify_sidekiq_not_active! verify_backup_warning! + disable_timeout! end def process_deduplications @@ -251,6 +252,13 @@ module Mastodon::CLI fail_with_message 'Maintenance process stopped.' unless yes?('Continue? (Yes/No)') end + def disable_timeout! + # Remove server-configured timeout if present + database_connection.execute(<<~SQL.squish) + SET statement_timeout = 0 + SQL + end + def deduplicate_accounts! remove_index_if_exists!(:accounts, 'index_accounts_on_username_and_domain_lower') From fd7bcfa7493b789cb420b9c3357d83196dd71b45 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:18:05 +0100 Subject: [PATCH 032/133] Update dependency rubocop-performance to v1.23.1 (#33486) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index afb06744ed..43959a9e77 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -723,7 +723,7 @@ GEM parser (>= 3.3.1.0) rubocop-capybara (2.21.0) rubocop (~> 1.41) - rubocop-performance (1.23.0) + rubocop-performance (1.23.1) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) rubocop-rails (2.28.0) From 7d6fd68efd92b40d3b432d80f5f59383f0a8c493 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:18:09 +0100 Subject: [PATCH 033/133] Update dependency test-prof to v1.4.4 (#33487) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 43959a9e77..4a71eccab5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -809,7 +809,7 @@ GEM unicode-display_width (>= 1.1.1, < 3) terrapin (1.0.1) climate_control - test-prof (1.4.3) + test-prof (1.4.4) thor (1.3.2) tilt (2.5.0) timeout (0.4.3) From 5c4e224b66c77d55f98be4db38758f1ba92c4171 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Tue, 7 Jan 2025 16:40:24 +0100 Subject: [PATCH 034/133] Comment sidekiq build in docker compose (#33483) --- docker-compose.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6048129318..63f17bf495 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -100,7 +100,8 @@ services: - redis sidekiq: - build: . + # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes + # build: . image: ghcr.io/mastodon/mastodon:v4.3.2 restart: always env_file: .env.production From e0863fd7660b3ac08e309d750df7d0c2cb46b5a1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:06:00 +0100 Subject: [PATCH 035/133] Update peter-evans/create-pull-request action to v7.0.6 (#33492) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/crowdin-download-stable.yml | 2 +- .github/workflows/crowdin-download.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/crowdin-download-stable.yml b/.github/workflows/crowdin-download-stable.yml index ef28258cca..6d9a058629 100644 --- a/.github/workflows/crowdin-download-stable.yml +++ b/.github/workflows/crowdin-download-stable.yml @@ -50,7 +50,7 @@ jobs: # Create or update the pull request - name: Create Pull Request - uses: peter-evans/create-pull-request@v7.0.5 + uses: peter-evans/create-pull-request@v7.0.6 with: commit-message: 'New Crowdin translations' title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)' diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml index e9b909b9e0..a7e47bb2a1 100644 --- a/.github/workflows/crowdin-download.yml +++ b/.github/workflows/crowdin-download.yml @@ -52,7 +52,7 @@ jobs: # Create or update the pull request - name: Create Pull Request - uses: peter-evans/create-pull-request@v7.0.5 + uses: peter-evans/create-pull-request@v7.0.6 with: commit-message: 'New Crowdin translations' title: 'New Crowdin Translations (automated)' From 5bbcb1bb2ee5ce57b1a6a0d9168fa938e61b408a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:06:05 +0100 Subject: [PATCH 036/133] Update dependency fastimage to v2.4.0 (#33491) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4a71eccab5..9c81c57ce4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -233,7 +233,7 @@ GEM faraday-net_http (3.4.0) net-http (>= 0.5.0) fast_blank (1.0.1) - fastimage (2.3.1) + fastimage (2.4.0) ffi (1.17.1) ffi-compiler (1.3.2) ffi (>= 1.15.5) From bbe9dcfade3b9766721604fd3fb811e0303f5836 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:06:11 +0100 Subject: [PATCH 037/133] Update dependency aws-sdk-s3 to v1.177.0 (#33490) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9c81c57ce4..e2525b13e2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -94,7 +94,7 @@ GEM ast (2.4.2) attr_required (1.0.2) aws-eventstream (1.3.0) - aws-partitions (1.1029.0) + aws-partitions (1.1032.0) aws-sdk-core (3.214.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -103,7 +103,7 @@ GEM aws-sdk-kms (1.96.0) aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.176.1) + aws-sdk-s3 (1.177.0) aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) From 7ad44e22edee9b07f5a1b2df1efb5b5c7a12afe5 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 7 Jan 2025 11:28:35 -0500 Subject: [PATCH 038/133] Remove role color highlighting from custom css (#33493) --- app/controllers/custom_css_controller.rb | 6 ----- app/models/user_role.rb | 3 --- app/views/admin/roles/_role.html.haml | 4 ++-- app/views/custom_css/show.css.erb | 6 ----- spec/requests/custom_css_spec.rb | 29 ------------------------ 5 files changed, 2 insertions(+), 46 deletions(-) diff --git a/app/controllers/custom_css_controller.rb b/app/controllers/custom_css_controller.rb index eb6417698a..9f33870bd2 100644 --- a/app/controllers/custom_css_controller.rb +++ b/app/controllers/custom_css_controller.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class CustomCssController < ActionController::Base # rubocop:disable Rails/ApplicationController - before_action :set_user_roles - def show expires_in 3.minutes, public: true render content_type: 'text/css' @@ -14,8 +12,4 @@ class CustomCssController < ActionController::Base # rubocop:disable Rails/Appli Setting.custom_css end helper_method :custom_css_styles - - def set_user_roles - @user_roles = UserRole.providing_styles - end end diff --git a/app/models/user_role.rb b/app/models/user_role.rb index f9c4c14c4b..24cd5983f3 100644 --- a/app/models/user_role.rb +++ b/app/models/user_role.rb @@ -101,9 +101,6 @@ class UserRole < ApplicationRecord before_validation :set_position scope :assignable, -> { where.not(id: EVERYONE_ROLE_ID).order(position: :asc) } - scope :highlighted, -> { where(highlighted: true) } - scope :with_color, -> { where.not(color: [nil, '']) } - scope :providing_styles, -> { highlighted.with_color } has_many :users, inverse_of: :role, foreign_key: 'role_id', dependent: :nullify diff --git a/app/views/admin/roles/_role.html.haml b/app/views/admin/roles/_role.html.haml index 636127354b..085bdbd156 100644 --- a/app/views/admin/roles/_role.html.haml +++ b/app/views/admin/roles/_role.html.haml @@ -1,7 +1,7 @@ .announcements-list__item - if can?(:update, role) = link_to edit_admin_role_path(role), class: 'announcements-list__item__title' do - %span.user-role{ class: "user-role-#{role.id}" } + %span.user-role = material_symbol 'group' - if role.everyone? @@ -10,7 +10,7 @@ = role.name - else %span.announcements-list__item__title - %span.user-role{ class: "user-role-#{role.id}" } + %span.user-role = material_symbol 'group' - if role.everyone? diff --git a/app/views/custom_css/show.css.erb b/app/views/custom_css/show.css.erb index 78da809ed6..d4b24b2106 100644 --- a/app/views/custom_css/show.css.erb +++ b/app/views/custom_css/show.css.erb @@ -2,9 +2,3 @@ <%= raw custom_css_styles %> <%- end %> -<%- @user_roles.each do |role| %> -.user-role-<%= role.id %> { - --user-role-accent: <%= role.color %>; -} - -<%- end %> diff --git a/spec/requests/custom_css_spec.rb b/spec/requests/custom_css_spec.rb index d97da00187..380c32908a 100644 --- a/spec/requests/custom_css_spec.rb +++ b/spec/requests/custom_css_spec.rb @@ -45,34 +45,5 @@ RSpec.describe 'Custom CSS' do CSS end end - - context 'with highlighted colored UserRole records' do - before do - _highlighted_colored = Fabricate :user_role, highlighted: true, color: '#336699', id: '123_123_123' - _highlighted_no_color = Fabricate :user_role, highlighted: true, color: '' - _no_highlight_with_color = Fabricate :user_role, highlighted: false, color: '' - end - - it 'returns stylesheet from settings' do - get '/custom.css' - - expect(response) - .to have_http_status(200) - .and have_cacheable_headers - .and have_attributes( - content_type: match('text/css') - ) - expect(response.body.strip) - .to eq(expected_css) - end - - def expected_css - <<~CSS.strip - .user-role-123123123 { - --user-role-accent: #336699; - } - CSS - end - end end end From b3243ef41c5d992afa87b60147b459c725931b55 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 7 Jan 2025 11:28:54 -0500 Subject: [PATCH 039/133] Refer to constant values from `api/v1/apps` request spec (#33488) --- app/lib/application_extension.rb | 10 +++++++--- spec/models/doorkeeper/application_spec.rb | 15 +++++++++++++++ spec/requests/api/v1/apps_spec.rb | 6 +++--- 3 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 spec/models/doorkeeper/application_spec.rb diff --git a/app/lib/application_extension.rb b/app/lib/application_extension.rb index d7aaeba5bd..d8090d15bc 100644 --- a/app/lib/application_extension.rb +++ b/app/lib/application_extension.rb @@ -3,14 +3,18 @@ module ApplicationExtension extend ActiveSupport::Concern + APP_NAME_LIMIT = 60 + APP_REDIRECT_URI_LIMIT = 2_000 + APP_WEBSITE_LIMIT = 2_000 + included do include Redisable has_many :created_users, class_name: 'User', foreign_key: 'created_by_application_id', inverse_of: :created_by_application - validates :name, length: { maximum: 60 } - validates :website, url: true, length: { maximum: 2_000 }, if: :website? - validates :redirect_uri, length: { maximum: 2_000 } + validates :name, length: { maximum: APP_NAME_LIMIT } + validates :redirect_uri, length: { maximum: APP_REDIRECT_URI_LIMIT } + validates :website, url: true, length: { maximum: APP_WEBSITE_LIMIT }, if: :website? # The relationship used between Applications and AccessTokens is using # dependent: delete_all, which means the ActiveRecord callback in diff --git a/spec/models/doorkeeper/application_spec.rb b/spec/models/doorkeeper/application_spec.rb new file mode 100644 index 0000000000..e026d90caa --- /dev/null +++ b/spec/models/doorkeeper/application_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Doorkeeper::Application do + describe 'Associations' do + it { is_expected.to have_many(:created_users).class_name('User').inverse_of(:created_by_application).with_foreign_key(:created_by_application_id) } + end + + describe 'Validations' do + it { is_expected.to validate_length_of(:name).is_at_most(described_class::APP_NAME_LIMIT) } + it { is_expected.to validate_length_of(:redirect_uri).is_at_most(described_class::APP_REDIRECT_URI_LIMIT) } + it { is_expected.to validate_length_of(:website).is_at_most(described_class::APP_WEBSITE_LIMIT) } + end +end diff --git a/spec/requests/api/v1/apps_spec.rb b/spec/requests/api/v1/apps_spec.rb index 4e9147ba32..3120ab9c64 100644 --- a/spec/requests/api/v1/apps_spec.rb +++ b/spec/requests/api/v1/apps_spec.rb @@ -122,7 +122,7 @@ RSpec.describe 'Apps' do end context 'with a too-long name' do - let(:client_name) { 'hoge' * 20 } + let(:client_name) { 'a' * Doorkeeper::Application::APP_NAME_LIMIT * 2 } it 'returns http unprocessable entity' do subject @@ -134,7 +134,7 @@ RSpec.describe 'Apps' do end context 'with a too-long website' do - let(:website) { "https://foo.bar/#{'hoge' * 2_000}" } + let(:website) { "https://foo.bar/#{'a' * Doorkeeper::Application::APP_WEBSITE_LIMIT * 2}" } it 'returns http unprocessable entity' do subject @@ -146,7 +146,7 @@ RSpec.describe 'Apps' do end context 'with a too-long redirect_uri' do - let(:redirect_uris) { "https://app.example/#{'hoge' * 2_000}" } + let(:redirect_uris) { "https://app.example/#{'a' * Doorkeeper::Application::APP_REDIRECT_URI_LIMIT * 2}" } it 'returns http unprocessable entity' do subject From c0264c8013615cb725f9fd2d69bea60ea4a13324 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 8 Jan 2025 03:48:45 -0500 Subject: [PATCH 040/133] Extend custom CSS cache time with digest paths (#33207) --- app/controllers/custom_css_controller.rb | 2 +- app/helpers/theme_helper.rb | 23 +++++++++++++++ app/models/form/admin_settings.rb | 18 ++++++++++++ app/views/layouts/application.html.haml | 2 +- config/initializers/settings_digests.rb | 18 ++++++++++++ config/routes.rb | 3 +- spec/helpers/theme_helper_spec.rb | 20 +++++++++++++ spec/models/form/admin_settings_spec.rb | 36 ++++++++++++++++++++++++ spec/requests/cache_spec.rb | 1 + spec/requests/custom_css_spec.rb | 6 ++-- spec/routing/custom_css_routing_spec.rb | 19 +++++++++++++ 11 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 config/initializers/settings_digests.rb create mode 100644 spec/routing/custom_css_routing_spec.rb diff --git a/app/controllers/custom_css_controller.rb b/app/controllers/custom_css_controller.rb index 9f33870bd2..5b98914114 100644 --- a/app/controllers/custom_css_controller.rb +++ b/app/controllers/custom_css_controller.rb @@ -2,7 +2,7 @@ class CustomCssController < ActionController::Base # rubocop:disable Rails/ApplicationController def show - expires_in 3.minutes, public: true + expires_in 1.month, public: true render content_type: 'text/css' end diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb index fab899a533..5dfb4a5184 100644 --- a/app/helpers/theme_helper.rb +++ b/app/helpers/theme_helper.rb @@ -23,8 +23,31 @@ module ThemeHelper end end + def custom_stylesheet + if active_custom_stylesheet.present? + stylesheet_link_tag( + custom_css_path(active_custom_stylesheet), + host: root_url, + media: :all, + skip_pipeline: true + ) + end + end + private + def active_custom_stylesheet + if cached_custom_css_digest.present? + [:custom, cached_custom_css_digest.to_s.first(8)] + .compact_blank + .join('-') + end + end + + def cached_custom_css_digest + Rails.cache.read(:setting_digest_custom_css) + end + def theme_color_for(theme) theme == 'mastodon-light' ? Themes::THEME_COLORS[:light] : Themes::THEME_COLORS[:dark] end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 515909d5cf..ffd2d4049c 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -69,6 +69,10 @@ class Form::AdminSettings favicon ).freeze + DIGEST_KEYS = %i( + custom_css + ).freeze + OVERRIDEN_SETTINGS = { authorized_fetch: :authorized_fetch_mode?, }.freeze @@ -122,6 +126,8 @@ class Form::AdminSettings KEYS.each do |key| next unless instance_variable_defined?(:"@#{key}") + cache_digest_value(key) if DIGEST_KEYS.include?(key) + if UPLOAD_KEYS.include?(key) public_send(key).save else @@ -133,6 +139,18 @@ class Form::AdminSettings private + def cache_digest_value(key) + Rails.cache.delete(:"setting_digest_#{key}") + + key_value = instance_variable_get(:"@#{key}") + if key_value.present? + Rails.cache.write( + :"setting_digest_#{key}", + Digest::SHA256.hexdigest(key_value) + ) + end + end + def typecast_value(key, value) if BOOLEAN_KEYS.include?(key) value == '1' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 99e89d45ce..6f016c6cf5 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -34,7 +34,7 @@ = csrf_meta_tags unless skip_csrf_meta_tags? %meta{ name: 'style-nonce', content: request.content_security_policy_nonce } - = stylesheet_link_tag custom_css_path, skip_pipeline: true, host: root_url, media: 'all' + = custom_stylesheet = yield :header_tags diff --git a/config/initializers/settings_digests.rb b/config/initializers/settings_digests.rb new file mode 100644 index 0000000000..2a5d925c70 --- /dev/null +++ b/config/initializers/settings_digests.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +Rails.application.config.to_prepare do + custom_css = begin + Setting.custom_css + rescue ActiveRecord::AdapterError # Running without a database, not migrated, no connection, etc + nil + end + + if custom_css.present? + Rails + .cache + .write( + :setting_digest_custom_css, + Digest::SHA256.hexdigest(custom_css) + ) + end +end diff --git a/config/routes.rb b/config/routes.rb index 3909dd1b77..5adec04c7d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -55,7 +55,8 @@ Rails.application.routes.draw do get 'manifest', to: 'manifests#show', defaults: { format: 'json' } get 'intent', to: 'intents#show' - get 'custom.css', to: 'custom_css#show', as: :custom_css + get 'custom.css', to: 'custom_css#show' + resources :custom_css, only: :show, path: :css get 'remote_interaction_helper', to: 'remote_interaction_helper#index' diff --git a/spec/helpers/theme_helper_spec.rb b/spec/helpers/theme_helper_spec.rb index 7663e59436..c811b7c981 100644 --- a/spec/helpers/theme_helper_spec.rb +++ b/spec/helpers/theme_helper_spec.rb @@ -79,6 +79,26 @@ RSpec.describe ThemeHelper do end end + describe '#custom_stylesheet' do + context 'when custom css setting value digest is present' do + before { Rails.cache.write(:setting_digest_custom_css, '1a2s3d4f1a2s3d4f') } + + it 'returns value from settings' do + expect(custom_stylesheet) + .to match('/css/custom-1a2s3d4f.css') + end + end + + context 'when custom css setting value digest is not present' do + before { Rails.cache.delete(:setting_digest_custom_css) } + + it 'returns default value' do + expect(custom_stylesheet) + .to be_blank + end + end + end + private def html_links diff --git a/spec/models/form/admin_settings_spec.rb b/spec/models/form/admin_settings_spec.rb index 73106f2b69..899d56703a 100644 --- a/spec/models/form/admin_settings_spec.rb +++ b/spec/models/form/admin_settings_spec.rb @@ -17,4 +17,40 @@ RSpec.describe Form::AdminSettings do end end end + + describe '#save' do + describe 'updating digest values' do + context 'when updating custom css to real value' do + subject { described_class.new(custom_css: css) } + + let(:css) { 'body { color: red; }' } + let(:digested) { Digest::SHA256.hexdigest(css) } + + it 'changes relevant digest value' do + expect { subject.save } + .to(change { Rails.cache.read(:setting_digest_custom_css) }.to(digested)) + end + end + + context 'when updating custom css to empty value' do + subject { described_class.new(custom_css: '') } + + before { Rails.cache.write(:setting_digest_custom_css, 'previous-value') } + + it 'changes relevant digest value' do + expect { subject.save } + .to(change { Rails.cache.read(:setting_digest_custom_css) }.to(be_blank)) + end + end + + context 'when updating other fields' do + subject { described_class.new(site_contact_email: 'test@example.host') } + + it 'does not update digests' do + expect { subject.save } + .to(not_change { Rails.cache.read(:setting_digest_custom_css) }) + end + end + end + end end diff --git a/spec/requests/cache_spec.rb b/spec/requests/cache_spec.rb index 2a52e4dea9..8ca2817263 100644 --- a/spec/requests/cache_spec.rb +++ b/spec/requests/cache_spec.rb @@ -10,6 +10,7 @@ module TestEndpoints /.well-known/nodeinfo /nodeinfo/2.0 /manifest + /css/custom-1a2s3d4f.css /custom.css /actor /api/v1/instance/extended_description diff --git a/spec/requests/custom_css_spec.rb b/spec/requests/custom_css_spec.rb index 380c32908a..66ff5c4b13 100644 --- a/spec/requests/custom_css_spec.rb +++ b/spec/requests/custom_css_spec.rb @@ -5,10 +5,10 @@ require 'rails_helper' RSpec.describe 'Custom CSS' do include RoutingHelper - describe 'GET /custom.css' do + describe 'GET /css/:id.css' do context 'without any CSS or User Roles' do it 'returns empty stylesheet' do - get '/custom.css' + get '/css/custom-123.css' expect(response) .to have_http_status(200) @@ -27,7 +27,7 @@ RSpec.describe 'Custom CSS' do end it 'returns stylesheet from settings' do - get '/custom.css' + get '/css/custom-456.css' expect(response) .to have_http_status(200) diff --git a/spec/routing/custom_css_routing_spec.rb b/spec/routing/custom_css_routing_spec.rb new file mode 100644 index 0000000000..26139b4744 --- /dev/null +++ b/spec/routing/custom_css_routing_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Custom CSS routes' do + describe 'the legacy route' do + it 'routes to correct place' do + expect(get('/custom.css')) + .to route_to('custom_css#show') + end + end + + describe 'the custom digest route' do + it 'routes to correct place' do + expect(get('/css/custom-1a2s3d4f.css')) + .to route_to('custom_css#show', id: 'custom-1a2s3d4f', format: 'css') + end + end +end From c3fc12c2b4920227b79c1cd394c6a20485f3b2e9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 09:48:51 +0100 Subject: [PATCH 041/133] Update dependency rubyzip to v2.4.1 (#33494) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e2525b13e2..dfea405a3a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -744,7 +744,7 @@ GEM ruby-vips (2.2.2) ffi (~> 1.12) logger - rubyzip (2.3.2) + rubyzip (2.4.1) rufus-scheduler (3.9.2) fugit (~> 1.1, >= 1.11.1) safety_net_attestation (0.4.0) From b6c2923cf729c579c0963f23a0db7043a17eaece Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 8 Jan 2025 03:53:26 -0500 Subject: [PATCH 042/133] Convert `settings/migration` spec controller->system (#33496) --- .../settings/migrations_controller_spec.rb | 104 ------------------ spec/requests/settings/migrations_spec.rb | 21 ++++ spec/system/settings/migrations_spec.rb | 99 +++++++++++++++++ 3 files changed, 120 insertions(+), 104 deletions(-) delete mode 100644 spec/controllers/settings/migrations_controller_spec.rb create mode 100644 spec/requests/settings/migrations_spec.rb create mode 100644 spec/system/settings/migrations_spec.rb diff --git a/spec/controllers/settings/migrations_controller_spec.rb b/spec/controllers/settings/migrations_controller_spec.rb deleted file mode 100644 index dca4c925fd..0000000000 --- a/spec/controllers/settings/migrations_controller_spec.rb +++ /dev/null @@ -1,104 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Settings::MigrationsController do - render_views - - describe 'GET #show' do - context 'when user is not sign in' do - subject { get :show } - - it { is_expected.to redirect_to new_user_session_path } - end - - context 'when user is sign in' do - subject { get :show } - - let(:user) { Fabricate(:account, moved_to_account: moved_to_account).user } - - before { sign_in user, scope: :user } - - context 'when user does not have moved to account' do - let(:moved_to_account) { nil } - - it 'renders show page' do - expect(subject).to have_http_status 200 - expect(subject).to render_template :show - end - end - - context 'when user has a moved to account' do - let(:moved_to_account) { Fabricate(:account) } - - it 'renders show page' do - expect(subject).to have_http_status 200 - expect(subject).to render_template :show - end - end - end - end - - describe 'POST #create' do - context 'when user is not sign in' do - subject { post :create } - - it { is_expected.to redirect_to new_user_session_path } - end - - context 'when user is signed in' do - subject { post :create, params: { account_migration: { acct: acct, current_password: '12345678' } } } - - let(:user) { Fabricate(:user, password: '12345678') } - - before { sign_in user, scope: :user } - - context 'when migration account is changed' do - let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) } - - it 'updates moved to account' do - expect(subject).to redirect_to settings_migration_path - expect(user.account.reload.moved_to_account_id).to eq acct.id - end - end - - context 'when acct is the current account' do - let(:acct) { user.account } - - it 'does not update the moved account', :aggregate_failures do - subject - - expect(user.account.reload.moved_to_account_id).to be_nil - expect(response).to render_template :show - end - end - - context 'when target account does not reference the account being moved from' do - let(:acct) { Fabricate(:account, also_known_as: []) } - - it 'does not update the moved account', :aggregate_failures do - subject - - expect(user.account.reload.moved_to_account_id).to be_nil - expect(response).to render_template :show - end - end - - context 'when a recent migration already exists' do - let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) } - - before do - moved_to = Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) - user.account.migrations.create!(acct: moved_to.acct) - end - - it 'does not update the moved account', :aggregate_failures do - subject - - expect(user.account.reload.moved_to_account_id).to be_nil - expect(response).to render_template :show - end - end - end - end -end diff --git a/spec/requests/settings/migrations_spec.rb b/spec/requests/settings/migrations_spec.rb new file mode 100644 index 0000000000..4103d6b320 --- /dev/null +++ b/spec/requests/settings/migrations_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Settings Migrations' do + describe 'GET #show' do + context 'when user is not signed in' do + subject { get '/settings/migration' } + + it { is_expected.to redirect_to new_user_session_path } + end + end + + describe 'POST #create' do + context 'when user is not signed in' do + subject { post '/settings/migration' } + + it { is_expected.to redirect_to new_user_session_path } + end + end +end diff --git a/spec/system/settings/migrations_spec.rb b/spec/system/settings/migrations_spec.rb new file mode 100644 index 0000000000..fecde36f35 --- /dev/null +++ b/spec/system/settings/migrations_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Settings Migrations' do + describe 'Viewing settings migrations' do + let(:user) { Fabricate(:account, moved_to_account: moved_to_account).user } + + before { sign_in(user) } + + context 'when user does not have moved to account' do + let(:moved_to_account) { nil } + + it 'renders show page' do + visit settings_migration_path + + expect(page) + .to have_content(I18n.t('settings.migrate')) + end + end + + context 'when user has a moved to account' do + let(:moved_to_account) { Fabricate(:account) } + + it 'renders show page and account details' do + visit settings_migration_path + + expect(page) + .to have_content(I18n.t('settings.migrate')) + .and have_content(moved_to_account.pretty_acct) + end + end + end + + describe 'Creating migrations' do + let(:user) { Fabricate(:user, password: '12345678') } + + before { sign_in(user) } + + context 'when migration account is changed' do + let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) } + + it 'updates moved to account' do + visit settings_migration_path + + expect { fill_in_and_submit } + .to(change { user.account.reload.moved_to_account_id }.to(acct.id)) + expect(page) + .to have_content(I18n.t('settings.migrate')) + end + end + + context 'when acct is the current account' do + let(:acct) { user.account } + + it 'does not update the moved account', :aggregate_failures do + visit settings_migration_path + + expect { fill_in_and_submit } + .to_not(change { user.account.reload.moved_to_account_id }.from(nil)) + expect(page) + .to have_content(I18n.t('settings.migrate')) + end + end + + context 'when target account does not reference the account being moved from' do + let(:acct) { Fabricate(:account, also_known_as: []) } + + it 'does not update the moved account', :aggregate_failures do + visit settings_migration_path + + expect { fill_in_and_submit } + .to_not(change { user.account.reload.moved_to_account_id }.from(nil)) + expect(page) + .to have_content(I18n.t('settings.migrate')) + end + end + + context 'when a recent migration already exists' do + let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) } + let(:moved_to) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) } + + before { user.account.migrations.create!(acct: moved_to.acct) } + + it 'can not update the moved account', :aggregate_failures do + visit settings_migration_path + + expect(find_by_id('account_migration_acct')) + .to be_disabled + end + end + + def fill_in_and_submit + fill_in 'account_migration_acct', with: acct.username + fill_in 'account_migration_current_password', with: '12345678' + click_on I18n.t('migrations.proceed_with_move') + end + end +end From 0c690511c220d2ee9de9fc41e0d1f94b3cdb0b88 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 8 Jan 2025 03:54:08 -0500 Subject: [PATCH 043/133] Convert `auth/challenges` spec controller->request (#33495) --- .../auth/challenges_spec.rb} | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) rename spec/{controllers/auth/challenges_controller_spec.rb => requests/auth/challenges_spec.rb} (64%) diff --git a/spec/controllers/auth/challenges_controller_spec.rb b/spec/requests/auth/challenges_spec.rb similarity index 64% rename from spec/controllers/auth/challenges_controller_spec.rb rename to spec/requests/auth/challenges_spec.rb index 3c9d2a5964..628bfe499b 100644 --- a/spec/controllers/auth/challenges_controller_spec.rb +++ b/spec/requests/auth/challenges_spec.rb @@ -2,9 +2,7 @@ require 'rails_helper' -RSpec.describe Auth::ChallengesController do - render_views - +RSpec.describe 'Auth Challenges' do let(:password) { 'foobar12345' } let(:user) { Fabricate(:user, password: password) } @@ -14,9 +12,9 @@ RSpec.describe Auth::ChallengesController do let(:return_to) { edit_user_registration_path } context 'with correct password' do - before { post :create, params: { form_challenge: { return_to: return_to, current_password: password } } } - it 'redirects back and sets challenge passed at in session' do + post '/auth/challenge', params: { form_challenge: { return_to: return_to, current_password: password } } + expect(response) .to redirect_to(return_to) expect(session[:challenge_passed_at]) @@ -25,13 +23,12 @@ RSpec.describe Auth::ChallengesController do end context 'with incorrect password' do - before { post :create, params: { form_challenge: { return_to: return_to, current_password: 'hhfggjjd562' } } } - it 'renders challenge, displays error, does not set session' do - expect(response) - .to render_template('auth/challenges/new') + post '/auth/challenge', params: { form_challenge: { return_to: return_to, current_password: 'hhfggjjd562' } } + expect(response.body) - .to include 'Invalid password' + .to include(I18n.t('challenge.prompt')) + .and include('Invalid password') expect(session[:challenge_passed_at]) .to be_nil end From 35e57138f1d330e5440c0f7a47f7e760e3b9367c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 09:01:28 +0000 Subject: [PATCH 044/133] New Crowdin Translations (automated) (#33500) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/cy.json | 2 ++ app/javascript/mastodon/locales/pt-PT.json | 2 ++ app/javascript/mastodon/locales/sv.json | 11 +++++++++-- config/locales/activerecord.cy.yml | 2 ++ config/locales/activerecord.gl.yml | 2 ++ config/locales/activerecord.pt-PT.yml | 2 ++ config/locales/activerecord.tr.yml | 2 +- config/locales/doorkeeper.sk.yml | 1 + config/locales/simple_form.fr.yml | 2 +- config/locales/simple_form.sv.yml | 12 ++++++++++++ 10 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index ffe15f538f..d7585b4ac2 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Dangos/cuddio testun tu ôl i CW", "keyboard_shortcuts.toggle_sensitivity": "Dangos/cuddio cyfryngau", "keyboard_shortcuts.toot": "Dechrau post newydd", + "keyboard_shortcuts.translate": "i gyfieithu postiad", "keyboard_shortcuts.unfocus": "Dad-ffocysu ardal cyfansoddi testun/chwilio", "keyboard_shortcuts.up": "Symud yn uwch yn y rhestr", "lightbox.close": "Cau", @@ -835,6 +836,7 @@ "status.reblogs.empty": "Does neb wedi hybio'r post yma eto. Pan y bydd rhywun yn gwneud, byddent yn ymddangos yma.", "status.redraft": "Dileu ac ailddrafftio", "status.remove_bookmark": "Tynnu nod tudalen", + "status.remove_favourite": "Tynnu o'r ffefrynnau", "status.replied_in_thread": "Atebodd mewn edefyn", "status.replied_to": "Wedi ateb {name}", "status.reply": "Ateb", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index ade10fc478..fa4f715b6d 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "mostrar / esconder texto atrás do aviso de conteúdo", "keyboard_shortcuts.toggle_sensitivity": "mostrar / ocultar multimédia", "keyboard_shortcuts.toot": "criar uma nova publicação", + "keyboard_shortcuts.translate": "traduzir uma publicação", "keyboard_shortcuts.unfocus": "remover o foco da área de texto / pesquisa", "keyboard_shortcuts.up": "mover para cima na lista", "lightbox.close": "Fechar", @@ -836,6 +837,7 @@ "status.reblogs.empty": "Ainda ninguém impulsionou esta publicação. Quando alguém o fizer, aparecerá aqui.", "status.redraft": "Eliminar e reescrever", "status.remove_bookmark": "Retirar dos marcadores", + "status.remove_favourite": "Remover dos favoritos", "status.replied_in_thread": "Responder na conversa", "status.replied_to": "Respondeu a {name}", "status.reply": "Responder", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index a9b299c97f..486c3ac19d 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -240,7 +240,7 @@ "dismissable_banner.community_timeline": "Dessa är de senaste offentliga inläggen från personer vars konton tillhandahålls av {domain}.", "dismissable_banner.dismiss": "Avfärda", "dismissable_banner.explore_links": "Dessa nyhetshistorier delas mest på fediversum idag. Nyare nyhetshistorier som publiceras av fler olika personer rankas högre.", - "dismissable_banner.explore_statuses": "Dessa inlägg från fediverse vinner dragkraft idag. Nyare inlägg som många boostar och favoritmarkerar rankas högre.", + "dismissable_banner.explore_statuses": "Dessa inlägg från fediversum vinner dragkraft idag. Nyare inlägg som många boostar och favoritmarkerar rankas högre.", "dismissable_banner.explore_tags": "De här hashtaggarna vinner dragkraft i fediversum idag. Hashtaggar som används av fler olika personer rankas högre.", "dismissable_banner.public_timeline": "De här är de aktuella publika inlägg från personer i fediversum som personer i {domain} följer.", "domain_block_modal.block": "Blockera server", @@ -658,6 +658,7 @@ "onboarding.follows.done": "Färdig", "onboarding.follows.empty": "Tyvärr kan inga resultat visas just nu. Du kan prova att använda sökfunktionen eller utforska sidan för att hitta personer att följa, eller försök igen senare.", "onboarding.follows.search": "Sök", + "onboarding.follows.title": "Följ människor för att komma igång", "onboarding.profile.discoverable": "Gör min profil upptäckbar", "onboarding.profile.discoverable_hint": "När du väljer att vara upptäckbar på Mastodon kan dina inlägg visas i sök- och trendresultat, och din profil kan föreslås för personer med liknande intressen som du.", "onboarding.profile.display_name": "Visningsnamn", @@ -695,6 +696,7 @@ "privacy_policy.title": "Integritetspolicy", "recommended": "Rekommenderas", "refresh": "Läs om", + "regeneration_indicator.please_stand_by": "Vänligen vänta.", "relative_time.days": "{number}d", "relative_time.full.days": "{number, plural, one {# dag} other {# dagar}} sedan", "relative_time.full.hours": "{number, plural, one {# timme} other {# timmar}} sedan", @@ -778,15 +780,18 @@ "search_results.accounts": "Profiler", "search_results.all": "Alla", "search_results.hashtags": "Hashtaggar", + "search_results.no_results": "Inga resultat.", + "search_results.no_search_yet": "Prova att söka efter inlägg, profiler eller hashtags.", "search_results.see_all": "Visa alla", "search_results.statuses": "Inlägg", + "search_results.title": "Sök efter \"{q}\"", "server_banner.about_active_users": "Personer som använt denna server de senaste 30 dagarna (månatligt aktiva användare)", "server_banner.active_users": "aktiva användare", "server_banner.administered_by": "Administrerad av:", "server_banner.is_one_of_many": "{domain} är en av de många oberoende Mastodon-servrar som du kan använda för att delta i Fediversen.", "server_banner.server_stats": "Serverstatistik:", "sign_in_banner.create_account": "Skapa konto", - "sign_in_banner.follow_anyone": "Följ vem som helst över Fediverse och se allt i kronologisk ordning. Inga algoritmer, inga annonser och inga klickbeten i sikte.", + "sign_in_banner.follow_anyone": "Följ vem som helst över Fediversum och se allt i kronologisk ordning. Inga algoritmer, annonser eller klickbeten i sikte.", "sign_in_banner.mastodon_is": "Mastodon är det bästa sättet att hänga med i vad som händer.", "sign_in_banner.sign_in": "Logga in", "sign_in_banner.sso_redirect": "Logga in eller registrera dig", @@ -831,6 +836,7 @@ "status.reblogs.empty": "Ingen har boostat detta inlägg än. När någon gör det kommer de synas här.", "status.redraft": "Radera & gör om", "status.remove_bookmark": "Ta bort bokmärke", + "status.remove_favourite": "Ta bort från Favoriter", "status.replied_in_thread": "Svarade i tråden", "status.replied_to": "Svarade på {name}", "status.reply": "Svara", @@ -852,6 +858,7 @@ "subscribed_languages.target": "Ändra språkprenumerationer för {target}", "tabs_bar.home": "Hem", "tabs_bar.notifications": "Aviseringar", + "terms_of_service.title": "Användarvillkor", "time_remaining.days": "{number, plural, one {# dag} other {# dagar}} kvar", "time_remaining.hours": "{number, plural, one {# timme} other {# timmar}} kvar", "time_remaining.minutes": "{number, plural, one {# minut} other {# minuter}} kvar", diff --git a/config/locales/activerecord.cy.yml b/config/locales/activerecord.cy.yml index 3620c1c2f6..9bc9b3b2bc 100644 --- a/config/locales/activerecord.cy.yml +++ b/config/locales/activerecord.cy.yml @@ -24,6 +24,8 @@ cy: models: account: attributes: + fields: + fields_with_values_missing_labels: yn cynnwys gwerthoedd gyda labeli coll username: invalid: rhaid iddo gynnwys dim ond llythrennau, rhifau a thanlinellau reserved: wedi ei neilltuo diff --git a/config/locales/activerecord.gl.yml b/config/locales/activerecord.gl.yml index 0c4941f048..8fdff40282 100644 --- a/config/locales/activerecord.gl.yml +++ b/config/locales/activerecord.gl.yml @@ -24,6 +24,8 @@ gl: models: account: attributes: + fields: + fields_with_values_missing_labels: contén valores aos que lle faltan etiquetas username: invalid: só letras, números e trazo baixo reserved: está reservado diff --git a/config/locales/activerecord.pt-PT.yml b/config/locales/activerecord.pt-PT.yml index 86581331db..8b17ade2eb 100644 --- a/config/locales/activerecord.pt-PT.yml +++ b/config/locales/activerecord.pt-PT.yml @@ -24,6 +24,8 @@ pt-PT: models: account: attributes: + fields: + fields_with_values_missing_labels: contém valores com etiquetas em falta username: invalid: deve conter apenas letras, números e traços inferiores reserved: está reservado diff --git a/config/locales/activerecord.tr.yml b/config/locales/activerecord.tr.yml index 095aa72e67..4795e629fb 100644 --- a/config/locales/activerecord.tr.yml +++ b/config/locales/activerecord.tr.yml @@ -25,7 +25,7 @@ tr: account: attributes: fields: - fields_with_values_missing_labels: değerleri eksik etiketler içeriyor + fields_with_values_missing_labels: etiketleri eksik değerler içeriyor username: invalid: sadece harfler, sayılar ve alt çizgiler reserved: kullanılamaz diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml index 774a2648f9..ba9a440a11 100644 --- a/config/locales/doorkeeper.sk.yml +++ b/config/locales/doorkeeper.sk.yml @@ -134,6 +134,7 @@ sk: media: Mediálne prílohy mutes: Stíšenia notifications: Upozornenia + profile: Váš Mastodon profil push: Upozornenia push reports: Hlásenia search: Vyhľadávanie diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index 1b0469f7a3..62dd23f930 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -174,7 +174,7 @@ fr: admin_account_action: include_statuses: Inclure les messages signalés dans le courriel send_email_notification: Notifier l’utilisateur par courriel - text: Attention personnalisée + text: Avertissement personnalisé type: Action types: disable: Désactiver diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 0815170fcc..5fd258d7e0 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -130,6 +130,12 @@ sv: show_application: Du kommer alltid att kunna se vilken app som publicerat ditt inlägg oavsett. tag: name: Du kan bara ändra skriftläget av bokstäverna, till exempel, för att göra det mer läsbart + terms_of_service: + changelog: Kan struktureras med Markdown syntax. + text: Kan struktureras med Markdown syntax. + terms_of_service_generator: + arbitration_website: Kan vara ett webbformulär, eller ”N/A” om du använder e-post + jurisdiction: Lista det land där vem som än betalar räkningarna bor. Om det är ett företag eller annan enhet, lista landet där det är inkorporerat, och staden, regionen, territoriet eller staten på lämpligt sätt. user: chosen_languages: Vid aktivering visas bara inlägg på dina valda språk i offentliga tidslinjer role: Rollen styr vilka behörigheter användaren har. @@ -224,6 +230,7 @@ sv: setting_hide_network: Göm ditt nätverk setting_reduce_motion: Minska rörelser i animationer setting_system_font_ui: Använd systemets standardfont + setting_system_scrollbars_ui: Använd systemets standardrullningsfält setting_theme: Sidans tema setting_trends: Visa dagens trender setting_unfollow_modal: Visa bekräftelse innan du slutar följa någon @@ -318,6 +325,11 @@ sv: name: Hashtagg trendable: Tillåt denna hashtagg att visas under trender usable: Tillåt inlägg att använda denna fyrkantstagg + terms_of_service: + changelog: Vad har ändrats? + text: Användarvillkor + terms_of_service_generator: + admin_email: E-postadress för juridiska meddelanden user: role: Roll time_zone: Tidszon From ee1cbda2262cec267c5c774c559d5ea7388cc541 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 09:06:37 +0000 Subject: [PATCH 045/133] Update opentelemetry-ruby (non-major) (#33501) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index d2e6769ace..8df00d8c0a 100644 --- a/Gemfile +++ b/Gemfile @@ -108,7 +108,7 @@ group :opentelemetry do gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.21.0', require: false gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false gem 'opentelemetry-instrumentation-excon', '~> 0.22.0', require: false - gem 'opentelemetry-instrumentation-faraday', '~> 0.24.1', require: false + gem 'opentelemetry-instrumentation-faraday', '~> 0.25.0', require: false gem 'opentelemetry-instrumentation-http', '~> 0.23.2', require: false gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false diff --git a/Gemfile.lock b/Gemfile.lock index dfea405a3a..8330d7bb7d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -510,7 +510,7 @@ GEM opentelemetry-instrumentation-excon (0.22.5) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-faraday (0.24.8) + opentelemetry-instrumentation-faraday (0.25.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-http (0.23.5) @@ -963,7 +963,7 @@ DEPENDENCIES opentelemetry-instrumentation-active_model_serializers (~> 0.21.0) opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2) opentelemetry-instrumentation-excon (~> 0.22.0) - opentelemetry-instrumentation-faraday (~> 0.24.1) + opentelemetry-instrumentation-faraday (~> 0.25.0) opentelemetry-instrumentation-http (~> 0.23.2) opentelemetry-instrumentation-http_client (~> 0.22.3) opentelemetry-instrumentation-net_http (~> 0.22.4) From 242221c11f8da661737b61c16efa7fdd01853f63 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:21:58 +0100 Subject: [PATCH 046/133] Update dependency connection_pool to v2.5.0 (#33503) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8330d7bb7d..f3a4e11f5d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -160,7 +160,7 @@ GEM cocoon (1.2.15) color_diff (0.1) concurrent-ruby (1.3.4) - connection_pool (2.4.1) + connection_pool (2.5.0) cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) From 5a142060e117a3f4124fd99a72dc3b5d69d199d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:47:39 +0100 Subject: [PATCH 047/133] Update opentelemetry-ruby (non-major) (#33506) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f3a4e11f5d..36373bd337 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -490,7 +490,7 @@ GEM opentelemetry-instrumentation-active_job (0.7.8) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-active_model_serializers (0.21.0) + opentelemetry-instrumentation-active_model_serializers (0.21.1) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-active_support (>= 0.7.0) opentelemetry-instrumentation-base (~> 0.22.1) @@ -522,7 +522,7 @@ GEM opentelemetry-instrumentation-net_http (0.22.8) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-pg (0.29.1) + opentelemetry-instrumentation-pg (0.29.2) opentelemetry-api (~> 1.0) opentelemetry-helpers-sql-obfuscation opentelemetry-instrumentation-base (~> 0.22.1) From 6a351e2247d01be1b6f04d5fb4d16272df35d260 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:57:40 +0100 Subject: [PATCH 048/133] Update dependency core-js to v3.40.0 (#33504) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 119c85c7af..7b9ecfe86f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6534,9 +6534,9 @@ __metadata: linkType: hard "core-js@npm:^3.30.2": - version: 3.39.0 - resolution: "core-js@npm:3.39.0" - checksum: 10c0/f7602069b6afb2e3298eec612a5c1e0c3e6a458930fbfc7a4c5f9ac03426507f49ce395eecdd2d9bae9024f820e44582b67ffe16f2272395af26964f174eeb6b + version: 3.40.0 + resolution: "core-js@npm:3.40.0" + checksum: 10c0/db7946ada881e845d8b157061945b1187618fa45cf162f392a151e8a497962aed2da688c982eaa1d444c864be97a70f8be4d73385294b515d224dd164d19f1d4 languageName: node linkType: hard From 67a8d4638c6e6a3ef178258504a6535746608c42 Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Wed, 8 Jan 2025 06:01:51 -0500 Subject: [PATCH 049/133] Unpin peter-evans/create-pull-request to v7 (#30817) --- .github/workflows/crowdin-download.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml index a7e47bb2a1..d247a514d9 100644 --- a/.github/workflows/crowdin-download.yml +++ b/.github/workflows/crowdin-download.yml @@ -52,7 +52,7 @@ jobs: # Create or update the pull request - name: Create Pull Request - uses: peter-evans/create-pull-request@v7.0.6 + uses: peter-evans/create-pull-request@v7 with: commit-message: 'New Crowdin translations' title: 'New Crowdin Translations (automated)' From a16d83ffef6693d750283b3af1f18ef5996222e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:22:44 +0100 Subject: [PATCH 050/133] Update Node.js to 22.13 (#33502) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 35d2d08ea1..fb0a135541 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.12 +22.13 From d34e9eaf17a56e97c9db84a4b8fc3399446cef3a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:02:07 +0100 Subject: [PATCH 051/133] Update dependency @reduxjs/toolkit to v2.5.0 (#33102) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7b9ecfe86f..0f8217566f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3291,22 +3291,22 @@ __metadata: linkType: hard "@reduxjs/toolkit@npm:^2.0.1": - version: 2.3.0 - resolution: "@reduxjs/toolkit@npm:2.3.0" + version: 2.5.0 + resolution: "@reduxjs/toolkit@npm:2.5.0" dependencies: immer: "npm:^10.0.3" redux: "npm:^5.0.1" redux-thunk: "npm:^3.1.0" reselect: "npm:^5.1.0" peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 peerDependenciesMeta: react: optional: true react-redux: optional: true - checksum: 10c0/414e90b706331385a2122fc79e33f90c59a9caf9a59419f1bfd7f5e594bc8e4987902fd1bccbc53eb96d22c65ec2981ff5581f3d2df3ecd381a630f391edfc3e + checksum: 10c0/81748a5a6d2f52a14769b6ed25aea1e77cda81b1db6599c7c3a1d1605696c65c4469f55146c2b2a7a2f8ebafa5ecd4996aa8deecb37aebb5307217ec2fe384ac languageName: node linkType: hard From 2c36283a89b8cd59f72e63aac03a674c4c96a326 Mon Sep 17 00:00:00 2001 From: Lamparter <71598437+Lamparter@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:03:21 +0000 Subject: [PATCH 052/133] Refresh `README.md` (#32143) --- README.md | 75 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 17d9eefb57..60dfa11448 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,24 @@ -

- - - Mastodon -

+> [!NOTE] +> Want to learn more about Mastodon? +> Click below to find out more in a video. -[![GitHub release](https://img.shields.io/github/release/mastodon/mastodon.svg)][releases] -[![Ruby Testing](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml/badge.svg)](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml) -[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin] +

+ + Mastodon hero image + +

-[releases]: https://github.com/mastodon/mastodon/releases -[crowdin]: https://crowdin.com/project/mastodon +

+ + Release + + Ruby Testing + + Crowdin +

Mastodon is a **free, open-source social network server** based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, and video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!) -Click below to **learn more** in a video: - -[![Screenshot](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/ezgif-2-60f1b00403.gif)][youtube_demo] - -[youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE - ## Navigation - [Project homepage 🐘](https://joinmastodon.org) @@ -37,25 +37,15 @@ Click below to **learn more** in a video: -### No vendor lock-in: Fully interoperable with any conforming platform +**No vendor lock-in: Fully interoperable with any conforming platform** - It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/) -It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/) +**Real-time, chronological timeline updates** - updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well! -### Real-time, chronological timeline updates +**Media attachments like images and short videos** - upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously! -Updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well! +**Safety and moderation tools** - Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/) -### Media attachments like images and short videos - -Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously! - -### Safety and moderation tools - -Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/) - -### OAuth2 and a straightforward REST API - -Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices! +**OAuth2 and a straightforward REST API** - Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices! ## Deployment @@ -138,17 +128,30 @@ Mastodon is **free, open-source software** licensed under **AGPLv3**. You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). -**IRC channel**: #mastodon on irc.libera.chat +**IRC channel**: #mastodon on [`irc.libera.chat`](https://libera.chat) ## License -Copyright (C) 2016-2024 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md)) +Copyright (c) 2016-2024 Eugen Rochko (+ [`mastodon authors`](AUTHORS.md)) -This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +Licensed under GNU Affero General Public License as stated in the [LICENSE](LICENSE): -This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. +``` +Copyright (c) 2016-2024 Eugen Rochko & other Mastodon contributors -You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see https://www.gnu.org/licenses/ +``` [codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json [Dev Container extension]: https://containers.dev/supporting#dev-containers From 15669fcf75e79b4f6a2fe0c09ee8ae56d1a25270 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Wed, 8 Jan 2025 15:26:08 +0100 Subject: [PATCH 053/133] Include time portion in formatted datetimes when provided (#33191) --- app/javascript/entrypoints/public.tsx | 6 +++++- app/views/admin/report_notes/_report_note.html.haml | 2 +- app/views/admin/reports/_comment.html.haml | 2 +- app/views/disputes/strikes/show.html.haml | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/javascript/entrypoints/public.tsx b/app/javascript/entrypoints/public.tsx index 9e8ff9caa1..0560e76628 100644 --- a/app/javascript/entrypoints/public.tsx +++ b/app/javascript/entrypoints/public.tsx @@ -119,7 +119,11 @@ function loaded() { formattedContent = dateFormat.format(datetime); } - content.title = formattedContent; + const timeGiven = content.dateTime.includes('T'); + content.title = timeGiven + ? dateTimeFormat.format(datetime) + : dateFormat.format(datetime); + content.textContent = formattedContent; }); diff --git a/app/views/admin/report_notes/_report_note.html.haml b/app/views/admin/report_notes/_report_note.html.haml index dd60f7eabd..9c8e267d62 100644 --- a/app/views/admin/report_notes/_report_note.html.haml +++ b/app/views/admin/report_notes/_report_note.html.haml @@ -4,7 +4,7 @@ .report-notes__item__header %span.username = link_to report_note.account.username, admin_account_path(report_note.account_id) - %time.relative-formatted{ datetime: report_note.created_at.iso8601 } + %time.relative-formatted{ datetime: report_note.created_at.iso8601, title: report_note.created_at } = l report_note.created_at.to_date .report-notes__item__content diff --git a/app/views/admin/reports/_comment.html.haml b/app/views/admin/reports/_comment.html.haml index 8c07210af9..2b3af15c49 100644 --- a/app/views/admin/reports/_comment.html.haml +++ b/app/views/admin/reports/_comment.html.haml @@ -18,7 +18,7 @@ = link_to report.account.username, admin_account_path(report.account_id) - else = link_to report.account.domain, admin_instance_path(report.account.domain) - %time.relative-formatted{ datetime: report.created_at.iso8601 } + %time.relative-formatted{ datetime: report.created_at.iso8601, title: report.created_at } = l report.created_at.to_date .report-notes__item__content = simple_format(h(report.comment)) diff --git a/app/views/disputes/strikes/show.html.haml b/app/views/disputes/strikes/show.html.haml index 150dc06759..322d820a2a 100644 --- a/app/views/disputes/strikes/show.html.haml +++ b/app/views/disputes/strikes/show.html.haml @@ -66,7 +66,7 @@ .report-notes__item__header %span.username = link_to @appeal.account.username, can?(:show, @appeal.account) ? admin_account_path(@appeal.account_id) : short_account_url(@appeal.account) - %time.relative-formatted{ datetime: @appeal.created_at.iso8601 } + %time.relative-formatted{ datetime: @appeal.created_at.iso8601, title: @appeal.created_at } = l @appeal.created_at.to_date .report-notes__item__content From f22a2aab40dab165b2c0872b016e3803c12db972 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 8 Jan 2025 09:28:08 -0500 Subject: [PATCH 054/133] Add `Account#remote?` query method (#33508) --- app/models/account.rb | 6 +++++- app/models/poll.rb | 6 +----- spec/models/account_spec.rb | 28 ++++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index d42da2e9af..fefc40869f 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -107,7 +107,7 @@ class Account < ApplicationRecord validates_with UniqueUsernameValidator, if: -> { will_save_change_to_username? } # Remote user validations, also applies to internal actors - validates :username, format: { with: USERNAME_ONLY_RE }, if: -> { (!local? || actor_type == 'Application') && will_save_change_to_username? } + validates :username, format: { with: USERNAME_ONLY_RE }, if: -> { (remote? || actor_type == 'Application') && will_save_change_to_username? } # Remote user validations validates :uri, presence: true, unless: :local?, on: :create @@ -186,6 +186,10 @@ class Account < ApplicationRecord domain.nil? end + def remote? + domain.present? + end + def moved? moved_to_account_id.present? end diff --git a/app/models/poll.rb b/app/models/poll.rb index b4e0edcd0f..93ef0cc589 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -61,11 +61,7 @@ class Poll < ApplicationRecord votes.where(account: account).pluck(:choice) end - delegate :local?, to: :account - - def remote? - !local? - end + delegate :local?, :remote?, to: :account def emojis @emojis ||= CustomEmoji.from_text(options.join(' '), account.domain) diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 7755cf9611..809ec52cde 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -48,14 +48,34 @@ RSpec.describe Account do end describe '#local?' do - it 'returns true when the account is local' do + it 'returns true when domain is null' do account = Fabricate(:account, domain: nil) - expect(account.local?).to be true + expect(account).to be_local end - it 'returns false when the account is on a different domain' do + it 'returns false when domain is present' do account = Fabricate(:account, domain: 'foreign.tld') - expect(account.local?).to be false + expect(account).to_not be_local + end + end + + describe '#remote?' do + context 'when the domain is null' do + subject { Fabricate.build :account, domain: nil } + + it { is_expected.to_not be_remote } + end + + context 'when the domain is blank' do + subject { Fabricate.build :account, domain: '' } + + it { is_expected.to_not be_remote } + end + + context 'when the domain is present' do + subject { Fabricate.build :account, domain: 'host.example' } + + it { is_expected.to be_remote } end end From 78e0d6a11a3ef73904bbfc3269dd061d564cac54 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 8 Jan 2025 09:29:32 -0500 Subject: [PATCH 055/133] Extract development section from top-level readme into standalone doc (#33328) --- CONTRIBUTING.md | 4 ++- README.md | 72 +++++-------------------------------------- docs/DEVELOPMENT.md | 75 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 66 deletions(-) create mode 100644 docs/DEVELOPMENT.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8286fdd2f7..4a3825484e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ You can contribute in the following ways: If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). Please review the org-level [contribution guidelines] for high-level acceptance -criteria guidance. +criteria guidance and the [DEVELOPMENT] guide for environment-specific details. [contribution guidelines]: https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md @@ -53,3 +53,5 @@ It is not always possible to phrase every change in such a manner, but it is des ## Documentation The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation). + +[DEVELOPMENT]: docs/DEVELOPMENT.md diff --git a/README.md b/README.md index 60dfa11448..8a34754576 100644 --- a/README.md +++ b/README.md @@ -64,69 +64,14 @@ Mastodon is a **free, open-source social network server** based on ActivityPub w The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. -## Development - -### Vagrant - -A **Vagrant** configuration is included for development purposes. To use it, complete the following steps: - -- Install Vagrant and Virtualbox -- Install the `vagrant-hostsupdater` plugin: `vagrant plugin install vagrant-hostsupdater` -- Run `vagrant up` -- Run `vagrant ssh -c "cd /vagrant && bin/dev"` -- Open `http://mastodon.local` in your browser - -### macOS - -To set up **macOS** for native development, complete the following steps: - -- Install [Homebrew] and run `brew install postgresql@14 redis imagemagick -libidn nvm` to install the required project dependencies -- Use a Ruby version manager to activate the ruby in `.ruby-version` and run - `nvm use` to activate the node version from `.nvmrc` -- Run the `bin/setup` script, which will install the required ruby gems and node - packages and prepare the database for local development -- Finally, run the `bin/dev` script which will launch services via `overmind` - (if installed) or `foreman` - -### Docker - -For production hosting and deployment with **Docker**, use the `Dockerfile` and -`docker-compose.yml` in the project root directory. - -For local development, install and launch [Docker], and run: - -```shell -docker compose -f .devcontainer/compose.yaml up -d -docker compose -f .devcontainer/compose.yaml exec app bin/setup -docker compose -f .devcontainer/compose.yaml exec app bin/dev -``` - -### Dev Containers - -Within IDEs that support the [Development Containers] specification, start the -"Mastodon on local machine" container from the editor. The necessary `docker -compose` commands to build and setup the container should run automatically. For -**Visual Studio Code** this requires installing the [Dev Container extension]. - -### GitHub Codespaces - -[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted -development environment configured with the software needed for this project. - -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace] - -- Click the button to create a new codespace, and confirm the options -- Wait for the environment to build (takes a few minutes) -- When the editor is ready, run `bin/dev` in the terminal -- Wait for an _Open in Browser_ prompt. This will open Mastodon -- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_ - ## Contributing Mastodon is **free, open-source software** licensed under **AGPLv3**. -You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). +You can open issues for bugs you've found or features you think are missing. You +can also submit pull requests to this repository or translations via Crowdin. To +get started, look at the [CONTRIBUTING] and [DEVELOPMENT] guides. For changes +accepted into Mastodon, you can request to be paid through our [OpenCollective]. **IRC channel**: #mastodon on [`irc.libera.chat`](https://libera.chat) @@ -153,9 +98,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/ ``` -[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json -[Dev Container extension]: https://containers.dev/supporting#dev-containers -[Development Containers]: https://containers.dev/supporting -[Docker]: https://docs.docker.com -[GitHub Codespaces]: https://docs.github.com/en/codespaces -[Homebrew]: https://brew.sh +[CONTRIBUTING]: CONTRIBUTING.md +[DEVELOPMENT]: docs/DEVELOPMENT.md +[OpenCollective]: https://opencollective.com/mastodon diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000000..39f57dd402 --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,75 @@ +# Development + +## Overview + +Before starting local development, read the [CONTRIBUTING] guide to understand +what changes are desirable and what general processes to use. + +## Environments + +### Vagrant + +A **Vagrant** configuration is included for development purposes. To use it, +complete the following steps: + +- Install Vagrant and Virtualbox +- Install the `vagrant-hostsupdater` plugin: + `vagrant plugin install vagrant-hostsupdater` +- Run `vagrant up` +- Run `vagrant ssh -c "cd /vagrant && bin/dev"` +- Open `http://mastodon.local` in your browser + +### macOS + +To set up **macOS** for native development, complete the following steps: + +- Install [Homebrew] and run: + `brew install postgresql@14 redis imagemagick libidn nvm` + to install the required project dependencies +- Use a Ruby version manager to activate the ruby in `.ruby-version` and run + `nvm use` to activate the node version from `.nvmrc` +- Run the `bin/setup` script, which will install the required ruby gems and node + packages and prepare the database for local development +- Finally, run the `bin/dev` script which will launch services via `overmind` + (if installed) or `foreman` + +### Docker + +For production hosting and deployment with **Docker**, use the `Dockerfile` and +`docker-compose.yml` in the project root directory. + +For local development, install and launch [Docker], and run: + +```shell +docker compose -f .devcontainer/compose.yaml up -d +docker compose -f .devcontainer/compose.yaml exec app bin/setup +docker compose -f .devcontainer/compose.yaml exec app bin/dev +``` + +### Dev Containers + +Within IDEs that support the [Development Containers] specification, start the +"Mastodon on local machine" container from the editor. The necessary `docker +compose` commands to build and setup the container should run automatically. For +**Visual Studio Code** this requires installing the [Dev Container extension]. + +### GitHub Codespaces + +[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted +development environment configured with the software needed for this project. + +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace] + +- Click the button to create a new codespace, and confirm the options +- Wait for the environment to build (takes a few minutes) +- When the editor is ready, run `bin/dev` in the terminal +- Wait for an _Open in Browser_ prompt. This will open Mastodon +- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_ + +[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json +[CONTRIBUTING]: CONTRIBUTING.md +[Dev Container extension]: https://containers.dev/supporting#dev-containers +[Development Containers]: https://containers.dev/supporting +[Docker]: https://docs.docker.com +[GitHub Codespaces]: https://docs.github.com/en/codespaces +[Homebrew]: https://brew.sh From 1bf61957632bff5c0a3e8d0af60eeefb41a4cc18 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 8 Jan 2025 17:25:41 +0100 Subject: [PATCH 056/133] Fix use of deprecated `Iterable.isIndexed` from immutable (#33510) --- app/javascript/mastodon/actions/store.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/actions/store.js b/app/javascript/mastodon/actions/store.js index 8ab75cdc44..e8fec13453 100644 --- a/app/javascript/mastodon/actions/store.js +++ b/app/javascript/mastodon/actions/store.js @@ -1,4 +1,4 @@ -import { Iterable, fromJS } from 'immutable'; +import { fromJS, isIndexed } from 'immutable'; import { hydrateCompose } from './compose'; import { importFetchedAccounts } from './importer'; @@ -9,8 +9,7 @@ export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY'; const convertState = rawState => fromJS(rawState, (k, v) => - Iterable.isIndexed(v) ? v.toList() : v.toMap()); - + isIndexed(v) ? v.toList() : v.toMap()); export function hydrateStore(rawState) { return dispatch => { From 6b1ea8dd2c998a5b6fe5f37454e4c04df4863772 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 8 Jan 2025 17:26:54 +0100 Subject: [PATCH 057/133] Require specific subtype of `formatMessage` in `timeAgoString` (#33511) --- .../mastodon/components/relative_timestamp.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/components/relative_timestamp.tsx b/app/javascript/mastodon/components/relative_timestamp.tsx index ac385e88c6..6253525091 100644 --- a/app/javascript/mastodon/components/relative_timestamp.tsx +++ b/app/javascript/mastodon/components/relative_timestamp.tsx @@ -1,6 +1,6 @@ import { Component } from 'react'; -import type { IntlShape } from 'react-intl'; +import type { MessageDescriptor, PrimitiveType, IntlShape } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl'; const messages = defineMessages({ @@ -102,7 +102,13 @@ const getUnitDelay = (units: string) => { }; export const timeAgoString = ( - intl: Pick, + intl: { + formatDate: IntlShape['formatDate']; + formatMessage: ( + { id, defaultMessage }: MessageDescriptor, + values?: Record, + ) => string; + }, date: Date, now: number, year: number, From 3c7f3b190cbaf6b6db25b15c16c8dc0bff599c00 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:53:12 +0100 Subject: [PATCH 058/133] Update formatjs monorepo (#32774) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 208 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 136 insertions(+), 72 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0f8217566f..a51d49ca56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2267,14 +2267,26 @@ __metadata: languageName: node linkType: hard -"@formatjs/ecma402-abstract@npm:2.2.3": - version: 2.2.3 - resolution: "@formatjs/ecma402-abstract@npm:2.2.3" +"@formatjs/ecma402-abstract@npm:2.2.4": + version: 2.2.4 + resolution: "@formatjs/ecma402-abstract@npm:2.2.4" dependencies: "@formatjs/fast-memoize": "npm:2.2.3" - "@formatjs/intl-localematcher": "npm:0.5.7" + "@formatjs/intl-localematcher": "npm:0.5.8" tslib: "npm:2" - checksum: 10c0/611d12bf320fc5c5b85cb2b57e3dcebe8490a51c6a0459c857c7a3560656cd2bdba5b117e9dd7cf174f5aa120c11eaad7a65a6783637b816caa59a1bc5c727f6 + checksum: 10c0/3f262533fa704ea7a1a7a8107deee2609774a242c621f8cb5dd4bf4c97abf2fc12f5aeda3f4ce85be18147c484a0ca87303dca6abef53290717e685c55eabd2d + languageName: node + linkType: hard + +"@formatjs/ecma402-abstract@npm:2.3.2": + version: 2.3.2 + resolution: "@formatjs/ecma402-abstract@npm:2.3.2" + dependencies: + "@formatjs/fast-memoize": "npm:2.2.6" + "@formatjs/intl-localematcher": "npm:0.5.10" + decimal.js: "npm:10" + tslib: "npm:2" + checksum: 10c0/364e9e7de974fed976e0e8142a0f888ee0af4a11a61899115e5761ed933e7c1f16379b7b54a01524fd3c5d58bf08b71308237ea969cd54889eaf7bb2d30ec776 languageName: node linkType: hard @@ -2296,6 +2308,15 @@ __metadata: languageName: node linkType: hard +"@formatjs/fast-memoize@npm:2.2.6": + version: 2.2.6 + resolution: "@formatjs/fast-memoize@npm:2.2.6" + dependencies: + tslib: "npm:2" + checksum: 10c0/dccdc21105af673e58ec7b04eb17cd6fde1fb1a7e7a446273ca43f7ab97c26d5c0fcc2b9e80d5b54bf9b80354f9e1e681273c0ed26633ec72f0adc2d116dfd7f + languageName: node + linkType: hard + "@formatjs/icu-messageformat-parser@npm:2.7.10": version: 2.7.10 resolution: "@formatjs/icu-messageformat-parser@npm:2.7.10" @@ -2307,14 +2328,35 @@ __metadata: languageName: node linkType: hard -"@formatjs/icu-messageformat-parser@npm:2.9.3": - version: 2.9.3 - resolution: "@formatjs/icu-messageformat-parser@npm:2.9.3" +"@formatjs/icu-messageformat-parser@npm:2.9.4": + version: 2.9.4 + resolution: "@formatjs/icu-messageformat-parser@npm:2.9.4" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.3" - "@formatjs/icu-skeleton-parser": "npm:1.8.7" + "@formatjs/ecma402-abstract": "npm:2.2.4" + "@formatjs/icu-skeleton-parser": "npm:1.8.8" tslib: "npm:2" - checksum: 10c0/519b59f7b4cf90681315c5382f7fcd105eb1974486f0d62d9227b6d0498895114ccc818792c208baae1ef79571d93b0edb9914c676e5ab76924dddb7fd6c28a0 + checksum: 10c0/f1ed14ece7ef0abc9fb62e323b78c994fc772d346801ad5aaa9555e1a7d5c0fda791345f4f2e53a3223f0b82c1a4eaf9a83544c1c20cb39349d1a39bedcf1648 + languageName: node + linkType: hard + +"@formatjs/icu-messageformat-parser@npm:2.9.8": + version: 2.9.8 + resolution: "@formatjs/icu-messageformat-parser@npm:2.9.8" + dependencies: + "@formatjs/ecma402-abstract": "npm:2.3.2" + "@formatjs/icu-skeleton-parser": "npm:1.8.12" + tslib: "npm:2" + checksum: 10c0/df97c7f24fbeb8ef49ae1371f9498ad90f231f88211bf1effb7b2e8ac3531bec67c5d9147ddcb1add0ba697e8d089729add44a9a9c5015e0e8d61e7a43f062d9 + languageName: node + linkType: hard + +"@formatjs/icu-skeleton-parser@npm:1.8.12": + version: 1.8.12 + resolution: "@formatjs/icu-skeleton-parser@npm:1.8.12" + dependencies: + "@formatjs/ecma402-abstract": "npm:2.3.2" + tslib: "npm:2" + checksum: 10c0/03e743aa09acb2137e37d03b98578fcbbc949d056b8c151763778e885d04d621e69c82f7656547f0532351d2a987bffac0a8c4c3d81186f47a28047ba64385e2 languageName: node linkType: hard @@ -2328,35 +2370,44 @@ __metadata: languageName: node linkType: hard -"@formatjs/icu-skeleton-parser@npm:1.8.7": - version: 1.8.7 - resolution: "@formatjs/icu-skeleton-parser@npm:1.8.7" +"@formatjs/icu-skeleton-parser@npm:1.8.8": + version: 1.8.8 + resolution: "@formatjs/icu-skeleton-parser@npm:1.8.8" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.3" + "@formatjs/ecma402-abstract": "npm:2.2.4" tslib: "npm:2" - checksum: 10c0/e29eb4151580f2d324e6591509dc4543e2326266fc209a08580c94d502acab14acc3560d98b3aaf9ffbd5ff8e2683601ff08c65b32886f22da015c31ca35c5d0 + checksum: 10c0/5ad78a5682e83b973e6fed4fca68660b944c41d1e941f0c84d69ff3d10ae835330062dc0a2cf0d237d2675ad3463405061a3963c14c2b9d8d1c1911f892b1a8d languageName: node linkType: hard -"@formatjs/intl-displaynames@npm:6.8.3": - version: 6.8.3 - resolution: "@formatjs/intl-displaynames@npm:6.8.3" +"@formatjs/intl-displaynames@npm:6.8.5": + version: 6.8.5 + resolution: "@formatjs/intl-displaynames@npm:6.8.5" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.3" - "@formatjs/intl-localematcher": "npm:0.5.7" + "@formatjs/ecma402-abstract": "npm:2.2.4" + "@formatjs/intl-localematcher": "npm:0.5.8" tslib: "npm:2" - checksum: 10c0/54d3ecaabc45dc8494be4cd9633bbf5eb0bc40c5f62ed5e5073aa5b214c4a9a9620d61d1c8a6f0074618474fd9c2ccc42904605078d2f6341c242bf43627bb3a + checksum: 10c0/1092d6bac9ba7ee22470b85c9af16802244aa8a54f07e6cd560d15b96e8a08fc359f20dee88a064fe4c9ca8860f439abb109cbb7977b9ccceb846e28aacdf29c languageName: node linkType: hard -"@formatjs/intl-listformat@npm:7.7.3": - version: 7.7.3 - resolution: "@formatjs/intl-listformat@npm:7.7.3" +"@formatjs/intl-listformat@npm:7.7.5": + version: 7.7.5 + resolution: "@formatjs/intl-listformat@npm:7.7.5" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.3" - "@formatjs/intl-localematcher": "npm:0.5.7" + "@formatjs/ecma402-abstract": "npm:2.2.4" + "@formatjs/intl-localematcher": "npm:0.5.8" tslib: "npm:2" - checksum: 10c0/2683513e86cc7885528f23237079e3ab9e3a8bc7111aa10d4c685dcbe368ef07039573115df240112fb20f2ec0b70c8ea189ea3b79cbfed7e3dc46024a4667ff + checksum: 10c0/f514397f6b05ac29171fffbbd15636fbec086080058c79c159f24edd2038747c22579d46ebf339cbb672f8505ea408e5d960d6751064c16e02d18445cf4e7e61 + languageName: node + linkType: hard + +"@formatjs/intl-localematcher@npm:0.5.10": + version: 0.5.10 + resolution: "@formatjs/intl-localematcher@npm:0.5.10" + dependencies: + tslib: "npm:2" + checksum: 10c0/362ec83aca9382165be575f1cefa477478339e6fead8ca8866185ce6e58427ea1487a811b12c73d1bcfa99fd4db0c24543b35c823451839f585576bfccb8c9cc languageName: node linkType: hard @@ -2369,43 +2420,44 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-localematcher@npm:0.5.7": - version: 0.5.7 - resolution: "@formatjs/intl-localematcher@npm:0.5.7" +"@formatjs/intl-localematcher@npm:0.5.8": + version: 0.5.8 + resolution: "@formatjs/intl-localematcher@npm:0.5.8" dependencies: tslib: "npm:2" - checksum: 10c0/1ae374ca146a0d7457794926eed808c99971628e594f704a42ae2540b1f38928b26cbf942a7bbcc2796cc9fe8d9d7a603ac422bd9b89b714d2f91b506da40792 + checksum: 10c0/7a660263986326b662d4cb537e8386331c34fda61fb830b105e6c62d49be58ace40728dae614883b27a41cec7b1df8b44f72f79e16e6028bfca65d398dc04f3b languageName: node linkType: hard "@formatjs/intl-pluralrules@npm:^5.2.2": - version: 5.3.3 - resolution: "@formatjs/intl-pluralrules@npm:5.3.3" + version: 5.4.2 + resolution: "@formatjs/intl-pluralrules@npm:5.4.2" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.3" - "@formatjs/intl-localematcher": "npm:0.5.7" + "@formatjs/ecma402-abstract": "npm:2.3.2" + "@formatjs/intl-localematcher": "npm:0.5.10" + decimal.js: "npm:10" tslib: "npm:2" - checksum: 10c0/003d33af6f5d902517f230b7038e39b8336da3a84500fe5f4278fa5cac0c9a1b746de484f1c2bb2315026fd771491eafdc0aa59a809bb25189b63093efb8400d + checksum: 10c0/0ecb9da19084d7a15e636362c206c7ee14297ad365e3e63ea53c82c8442d02cd4dcf8a0da65af9b0f835b32f0e8c3d43407d6ee0a0d7974c3044c92574b49686 languageName: node linkType: hard -"@formatjs/intl@npm:2.10.13": - version: 2.10.13 - resolution: "@formatjs/intl@npm:2.10.13" +"@formatjs/intl@npm:2.10.15": + version: 2.10.15 + resolution: "@formatjs/intl@npm:2.10.15" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.3" + "@formatjs/ecma402-abstract": "npm:2.2.4" "@formatjs/fast-memoize": "npm:2.2.3" - "@formatjs/icu-messageformat-parser": "npm:2.9.3" - "@formatjs/intl-displaynames": "npm:6.8.3" - "@formatjs/intl-listformat": "npm:7.7.3" - intl-messageformat: "npm:10.7.5" + "@formatjs/icu-messageformat-parser": "npm:2.9.4" + "@formatjs/intl-displaynames": "npm:6.8.5" + "@formatjs/intl-listformat": "npm:7.7.5" + intl-messageformat: "npm:10.7.7" tslib: "npm:2" peerDependencies: typescript: ^4.7 || 5 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/14edcc45addc181c7a45845bc34cec5c73bb5544dcf8ed455eca916bbd3a5023c7204a8e8e289060545b85006933c6f297c33c4f04a8a4cb3890aa7baae5bfbf + checksum: 10c0/5d51fd0785d5547f375991d7df2d6303479b0083eeb35c42c30c9633aab77101895498f1eace419fd34fdb5c84aea19037c5280c3a9d85f9c3ffe6eef76b6f39 languageName: node linkType: hard @@ -2429,11 +2481,11 @@ __metadata: languageName: node linkType: hard -"@formatjs/ts-transformer@npm:3.13.22": - version: 3.13.22 - resolution: "@formatjs/ts-transformer@npm:3.13.22" +"@formatjs/ts-transformer@npm:3.13.27": + version: 3.13.27 + resolution: "@formatjs/ts-transformer@npm:3.13.27" dependencies: - "@formatjs/icu-messageformat-parser": "npm:2.9.3" + "@formatjs/icu-messageformat-parser": "npm:2.9.8" "@types/json-stable-stringify": "npm:1" "@types/node": "npm:14 || 16 || 17 || 18 || 20 || 22" chalk: "npm:4" @@ -2445,7 +2497,7 @@ __metadata: peerDependenciesMeta: ts-jest: optional: true - checksum: 10c0/42503292248bcae728181fdf68e79eac4169c18064953beb9245097d2c58e5434ae7a7978e6ce2829dfd6eb4b4155f78fecef70ac6820626a338f231c66f60cb + checksum: 10c0/ab26ce081835ce2a1384172a6e24db42d48bc9f8b9b941c646fba2f01ce8370123480623d1fbc1e75f6ec90a232f8ff67b5243c5e9ad18311dc03d7061a4892b languageName: node linkType: hard @@ -5341,21 +5393,21 @@ __metadata: linkType: hard "babel-plugin-formatjs@npm:^10.5.1": - version: 10.5.24 - resolution: "babel-plugin-formatjs@npm:10.5.24" + version: 10.5.30 + resolution: "babel-plugin-formatjs@npm:10.5.30" dependencies: "@babel/core": "npm:^7.25.0" "@babel/helper-plugin-utils": "npm:^7.25.0" "@babel/plugin-syntax-jsx": "npm:^7.25.0" "@babel/traverse": "npm:^7.25.0" "@babel/types": "npm:^7.25.0" - "@formatjs/icu-messageformat-parser": "npm:2.9.3" - "@formatjs/ts-transformer": "npm:3.13.22" + "@formatjs/icu-messageformat-parser": "npm:2.9.8" + "@formatjs/ts-transformer": "npm:3.13.27" "@types/babel__core": "npm:^7.20.5" "@types/babel__helper-plugin-utils": "npm:^7.10.3" "@types/babel__traverse": "npm:^7.20.6" tslib: "npm:2" - checksum: 10c0/a99c92e62edb30e0b6a5bf1115811eb18336b851109f8c096b5a9aa2be3eb72299eea3a8cb706e03705eb79edfa5a62d69bb52d80a375c1d5a1c963e3d00c40e + checksum: 10c0/cf9f17b71da9e95a402e0722cfbf4a549c984a8bb536f6b46dc187fa4b69dd2e248e6a825ea95d5ddfa2a9b09b3eb399cefa2dce1dcf52fd6e661adb99a9d3fa languageName: node linkType: hard @@ -7121,7 +7173,7 @@ __metadata: languageName: node linkType: hard -"decimal.js@npm:^10.4.2, decimal.js@npm:^10.4.3": +"decimal.js@npm:10, decimal.js@npm:^10.4.2, decimal.js@npm:^10.4.3": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" checksum: 10c0/6d60206689ff0911f0ce968d40f163304a6c1bc739927758e6efc7921cfa630130388966f16bf6ef6b838cb33679fbe8e7a78a2f3c478afce841fd55ac8fb8ee @@ -9918,15 +9970,27 @@ __metadata: languageName: node linkType: hard -"intl-messageformat@npm:10.7.5, intl-messageformat@npm:^10.3.5": - version: 10.7.5 - resolution: "intl-messageformat@npm:10.7.5" +"intl-messageformat@npm:10.7.7": + version: 10.7.7 + resolution: "intl-messageformat@npm:10.7.7" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.3" + "@formatjs/ecma402-abstract": "npm:2.2.4" "@formatjs/fast-memoize": "npm:2.2.3" - "@formatjs/icu-messageformat-parser": "npm:2.9.3" + "@formatjs/icu-messageformat-parser": "npm:2.9.4" tslib: "npm:2" - checksum: 10c0/1aa173a8c16ace50520af3de7d3f0ce9bafda90d47b6d674eb88cc47c12c3460d4d53c97ca412fae1141b00abb8ff2ba313250997a040837f01a25379165c949 + checksum: 10c0/691895fb6a73a2feb2569658706e0d452861441de184dd1c9201e458a39fb80fc80080dd40d3d370400a52663f87de7a6d5a263c94245492f7265dd760441a95 + languageName: node + linkType: hard + +"intl-messageformat@npm:^10.3.5": + version: 10.7.11 + resolution: "intl-messageformat@npm:10.7.11" + dependencies: + "@formatjs/ecma402-abstract": "npm:2.3.2" + "@formatjs/fast-memoize": "npm:2.2.6" + "@formatjs/icu-messageformat-parser": "npm:2.9.8" + tslib: "npm:2" + checksum: 10c0/7ccd972277cc6798038af876c830203084db6552becfa99c3706541fd67838552013f57f8ed0ed3aed03d4fba436591a83a25f913365d66ad04ee9332eee7b73 languageName: node linkType: hard @@ -14662,18 +14726,18 @@ __metadata: linkType: hard "react-intl@npm:^6.4.2": - version: 6.8.6 - resolution: "react-intl@npm:6.8.6" + version: 6.8.9 + resolution: "react-intl@npm:6.8.9" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.3" - "@formatjs/icu-messageformat-parser": "npm:2.9.3" - "@formatjs/intl": "npm:2.10.13" - "@formatjs/intl-displaynames": "npm:6.8.3" - "@formatjs/intl-listformat": "npm:7.7.3" + "@formatjs/ecma402-abstract": "npm:2.2.4" + "@formatjs/icu-messageformat-parser": "npm:2.9.4" + "@formatjs/intl": "npm:2.10.15" + "@formatjs/intl-displaynames": "npm:6.8.5" + "@formatjs/intl-listformat": "npm:7.7.5" "@types/hoist-non-react-statics": "npm:3" "@types/react": "npm:16 || 17 || 18" hoist-non-react-statics: "npm:3" - intl-messageformat: "npm:10.7.5" + intl-messageformat: "npm:10.7.7" tslib: "npm:2" peerDependencies: react: ^16.6.0 || 17 || 18 @@ -14681,7 +14745,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/ec88cc2b1d0edf089f04c8061ffda730840fb3317177d0dc1a6df208245ec278f52b4ca8546ff9511b0b28ff2d89863c5837a274d33731d4c8f75a3b3baafbe2 + checksum: 10c0/d42a6252beac5448b4a248d84923b0f75dfbbee6208cd5c49ac2f525714ab94efe2a4933d464c64cb161ddccaa37b83dffb2dd0529428219b8a60ce548da3e57 languageName: node linkType: hard From dc14695f8a46fd60c4f39223afbfaf55e95bf077 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 8 Jan 2025 18:24:57 +0100 Subject: [PATCH 059/133] Fix extraneous margins below CW in absence of mentions (#2936) --- .../flavours/glitch/components/mentions_placeholder.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/mentions_placeholder.jsx b/app/javascript/flavours/glitch/components/mentions_placeholder.jsx index dde4d77a84..0829bcc101 100644 --- a/app/javascript/flavours/glitch/components/mentions_placeholder.jsx +++ b/app/javascript/flavours/glitch/components/mentions_placeholder.jsx @@ -3,7 +3,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { Permalink } from 'flavours/glitch/components/permalink'; export const MentionsPlaceholder = ({ status }) => { - if (status.get('spoiler_text').length === 0 || !status.get('mentions')) { + if (status.get('spoiler_text').length === 0 || !status.get('mentions') || status.get('mentions').isEmpty()) { return null; } From 10e24113a8bf3103a42512657342dd8ca5de92cd Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 8 Jan 2025 18:25:09 +0100 Subject: [PATCH 060/133] =?UTF-8?q?Fix=20issue=20with=20=E2=80=9CTranslate?= =?UTF-8?q?=E2=80=9D=20button=20being=20overlayed=20on=20text=20(#2935)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2932 --- app/javascript/flavours/glitch/styles/components.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index d4e0cded96..9418a45617 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -1344,7 +1344,7 @@ body > [data-popper-placement] { } } -.status__content.status__content--collapsed .status__content__text { +.status__content.status__content--collapsed { max-height: 20px * 15; // 15 lines is roughly above 500 characters } From 1ce2dc3d1309adaefdcdc9bd4f300d744b4361b8 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Thu, 9 Jan 2025 09:09:13 +0100 Subject: [PATCH 061/133] Target same browsers in development and production (#33513) --- .browserslistrc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.browserslistrc b/.browserslistrc index 6367e4d358..0135379d6e 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,10 +1,6 @@ -[production] defaults > 0.2% firefox >= 78 ios >= 15.6 not dead not OperaMini all - -[development] -supports es6-module From ce1501c3a72991b750f7084a20bdf2668595db5e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 9 Jan 2025 03:12:48 -0500 Subject: [PATCH 062/133] Add "Account::Search" shared example for use in `Account` spec (#33507) --- spec/models/account_spec.rb | 266 +---------------- .../models/concerns/account/search.rb | 268 ++++++++++++++++++ 2 files changed, 269 insertions(+), 265 deletions(-) create mode 100644 spec/support/examples/models/concerns/account/search.rb diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 809ec52cde..f70690ddb6 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -3,6 +3,7 @@ require 'rails_helper' RSpec.describe Account do + include_examples 'Account::Search' include_examples 'Reviewable' context 'with an account record' do @@ -344,271 +345,6 @@ RSpec.describe Account do end end - describe '.search_for' do - before do - _missing = Fabricate( - :account, - display_name: 'Missing', - username: 'missing', - domain: 'missing.com' - ) - end - - it 'does not return suspended users' do - Fabricate( - :account, - display_name: 'Display Name', - username: 'username', - domain: 'example.com', - suspended: true - ) - - results = described_class.search_for('username') - expect(results).to eq [] - end - - it 'does not return unapproved users' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username' - ) - - match.user.update(approved: false) - - results = described_class.search_for('username') - expect(results).to eq [] - end - - it 'does not return unconfirmed users' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username' - ) - - match.user.update(confirmed_at: nil) - - results = described_class.search_for('username') - expect(results).to eq [] - end - - it 'accepts ?, \, : and space as delimiter' do - match = Fabricate( - :account, - display_name: 'A & l & i & c & e', - username: 'username', - domain: 'example.com' - ) - - results = described_class.search_for('A?l\i:c e') - expect(results).to eq [match] - end - - it 'finds accounts with matching display_name' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username', - domain: 'example.com' - ) - - results = described_class.search_for('display') - expect(results).to eq [match] - end - - it 'finds accounts with matching username' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username', - domain: 'example.com' - ) - - results = described_class.search_for('username') - expect(results).to eq [match] - end - - it 'finds accounts with matching domain' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username', - domain: 'example.com' - ) - - results = described_class.search_for('example') - expect(results).to eq [match] - end - - it 'limits via constant by default' do - stub_const('Account::Search::DEFAULT_LIMIT', 1) - 2.times.each { Fabricate(:account, display_name: 'Display Name') } - results = described_class.search_for('display') - expect(results.size).to eq 1 - end - - it 'accepts arbitrary limits' do - 2.times.each { Fabricate(:account, display_name: 'Display Name') } - results = described_class.search_for('display', limit: 1) - expect(results.size).to eq 1 - end - - it 'ranks multiple matches higher' do - matches = [ - { username: 'username', display_name: 'username' }, - { display_name: 'Display Name', username: 'username', domain: 'example.com' }, - ].map(&method(:Fabricate).curry(2).call(:account)) - - results = described_class.search_for('username') - expect(results).to eq matches - end - end - - describe '.advanced_search_for' do - let(:account) { Fabricate(:account) } - - context 'when limiting search to followed accounts' do - it 'accepts ?, \, : and space as delimiter' do - match = Fabricate( - :account, - display_name: 'A & l & i & c & e', - username: 'username', - domain: 'example.com' - ) - account.follow!(match) - - results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true) - expect(results).to eq [match] - end - - it 'does not return non-followed accounts' do - Fabricate( - :account, - display_name: 'A & l & i & c & e', - username: 'username', - domain: 'example.com' - ) - - results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true) - expect(results).to eq [] - end - - it 'does not return suspended users' do - Fabricate( - :account, - display_name: 'Display Name', - username: 'username', - domain: 'example.com', - suspended: true - ) - - results = described_class.advanced_search_for('username', account, limit: 10, following: true) - expect(results).to eq [] - end - - it 'does not return unapproved users' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username' - ) - - match.user.update(approved: false) - - results = described_class.advanced_search_for('username', account, limit: 10, following: true) - expect(results).to eq [] - end - - it 'does not return unconfirmed users' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username' - ) - - match.user.update(confirmed_at: nil) - - results = described_class.advanced_search_for('username', account, limit: 10, following: true) - expect(results).to eq [] - end - end - - it 'does not return suspended users' do - Fabricate( - :account, - display_name: 'Display Name', - username: 'username', - domain: 'example.com', - suspended: true - ) - - results = described_class.advanced_search_for('username', account) - expect(results).to eq [] - end - - it 'does not return unapproved users' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username' - ) - - match.user.update(approved: false) - - results = described_class.advanced_search_for('username', account) - expect(results).to eq [] - end - - it 'does not return unconfirmed users' do - match = Fabricate( - :account, - display_name: 'Display Name', - username: 'username' - ) - - match.user.update(confirmed_at: nil) - - results = described_class.advanced_search_for('username', account) - expect(results).to eq [] - end - - it 'accepts ?, \, : and space as delimiter' do - match = Fabricate( - :account, - display_name: 'A & l & i & c & e', - username: 'username', - domain: 'example.com' - ) - - results = described_class.advanced_search_for('A?l\i:c e', account) - expect(results).to eq [match] - end - - it 'limits result count by default value' do - stub_const('Account::Search::DEFAULT_LIMIT', 1) - 2.times { Fabricate(:account, display_name: 'Display Name') } - results = described_class.advanced_search_for('display', account) - expect(results.size).to eq 1 - end - - it 'accepts arbitrary limits' do - 2.times { Fabricate(:account, display_name: 'Display Name') } - results = described_class.advanced_search_for('display', account, limit: 1) - expect(results.size).to eq 1 - end - - it 'ranks followed accounts higher' do - match = Fabricate(:account, username: 'Matching') - followed_match = Fabricate(:account, username: 'Matcher') - Fabricate(:follow, account: account, target_account: followed_match) - - results = described_class.advanced_search_for('match', account) - expect(results).to eq [followed_match, match] - expect(results.first.rank).to be > results.last.rank - end - end - describe '#statuses_count' do subject { Fabricate(:account) } diff --git a/spec/support/examples/models/concerns/account/search.rb b/spec/support/examples/models/concerns/account/search.rb new file mode 100644 index 0000000000..9d9b499732 --- /dev/null +++ b/spec/support/examples/models/concerns/account/search.rb @@ -0,0 +1,268 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'Account::Search' do + describe '.search_for' do + before do + _missing = Fabricate( + :account, + display_name: 'Missing', + username: 'missing', + domain: 'missing.com' + ) + end + + it 'does not return suspended users' do + Fabricate( + :account, + display_name: 'Display Name', + username: 'username', + domain: 'example.com', + suspended: true + ) + + results = described_class.search_for('username') + expect(results).to eq [] + end + + it 'does not return unapproved users' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username' + ) + + match.user.update(approved: false) + + results = described_class.search_for('username') + expect(results).to eq [] + end + + it 'does not return unconfirmed users' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username' + ) + + match.user.update(confirmed_at: nil) + + results = described_class.search_for('username') + expect(results).to eq [] + end + + it 'accepts ?, \, : and space as delimiter' do + match = Fabricate( + :account, + display_name: 'A & l & i & c & e', + username: 'username', + domain: 'example.com' + ) + + results = described_class.search_for('A?l\i:c e') + expect(results).to eq [match] + end + + it 'finds accounts with matching display_name' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username', + domain: 'example.com' + ) + + results = described_class.search_for('display') + expect(results).to eq [match] + end + + it 'finds accounts with matching username' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username', + domain: 'example.com' + ) + + results = described_class.search_for('username') + expect(results).to eq [match] + end + + it 'finds accounts with matching domain' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username', + domain: 'example.com' + ) + + results = described_class.search_for('example') + expect(results).to eq [match] + end + + it 'limits via constant by default' do + stub_const('Account::Search::DEFAULT_LIMIT', 1) + 2.times.each { Fabricate(:account, display_name: 'Display Name') } + results = described_class.search_for('display') + expect(results.size).to eq 1 + end + + it 'accepts arbitrary limits' do + 2.times.each { Fabricate(:account, display_name: 'Display Name') } + results = described_class.search_for('display', limit: 1) + expect(results.size).to eq 1 + end + + it 'ranks multiple matches higher' do + matches = [ + { username: 'username', display_name: 'username' }, + { display_name: 'Display Name', username: 'username', domain: 'example.com' }, + ].map(&method(:Fabricate).curry(2).call(:account)) + + results = described_class.search_for('username') + expect(results).to eq matches + end + end + + describe '.advanced_search_for' do + let(:account) { Fabricate(:account) } + + context 'when limiting search to followed accounts' do + it 'accepts ?, \, : and space as delimiter' do + match = Fabricate( + :account, + display_name: 'A & l & i & c & e', + username: 'username', + domain: 'example.com' + ) + account.follow!(match) + + results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true) + expect(results).to eq [match] + end + + it 'does not return non-followed accounts' do + Fabricate( + :account, + display_name: 'A & l & i & c & e', + username: 'username', + domain: 'example.com' + ) + + results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true) + expect(results).to eq [] + end + + it 'does not return suspended users' do + Fabricate( + :account, + display_name: 'Display Name', + username: 'username', + domain: 'example.com', + suspended: true + ) + + results = described_class.advanced_search_for('username', account, limit: 10, following: true) + expect(results).to eq [] + end + + it 'does not return unapproved users' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username' + ) + + match.user.update(approved: false) + + results = described_class.advanced_search_for('username', account, limit: 10, following: true) + expect(results).to eq [] + end + + it 'does not return unconfirmed users' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username' + ) + + match.user.update(confirmed_at: nil) + + results = described_class.advanced_search_for('username', account, limit: 10, following: true) + expect(results).to eq [] + end + end + + it 'does not return suspended users' do + Fabricate( + :account, + display_name: 'Display Name', + username: 'username', + domain: 'example.com', + suspended: true + ) + + results = described_class.advanced_search_for('username', account) + expect(results).to eq [] + end + + it 'does not return unapproved users' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username' + ) + + match.user.update(approved: false) + + results = described_class.advanced_search_for('username', account) + expect(results).to eq [] + end + + it 'does not return unconfirmed users' do + match = Fabricate( + :account, + display_name: 'Display Name', + username: 'username' + ) + + match.user.update(confirmed_at: nil) + + results = described_class.advanced_search_for('username', account) + expect(results).to eq [] + end + + it 'accepts ?, \, : and space as delimiter' do + match = Fabricate( + :account, + display_name: 'A & l & i & c & e', + username: 'username', + domain: 'example.com' + ) + + results = described_class.advanced_search_for('A?l\i:c e', account) + expect(results).to eq [match] + end + + it 'limits result count by default value' do + stub_const('Account::Search::DEFAULT_LIMIT', 1) + 2.times { Fabricate(:account, display_name: 'Display Name') } + results = described_class.advanced_search_for('display', account) + expect(results.size).to eq 1 + end + + it 'accepts arbitrary limits' do + 2.times { Fabricate(:account, display_name: 'Display Name') } + results = described_class.advanced_search_for('display', account, limit: 1) + expect(results.size).to eq 1 + end + + it 'ranks followed accounts higher' do + match = Fabricate(:account, username: 'Matching') + followed_match = Fabricate(:account, username: 'Matcher') + Fabricate(:follow, account: account, target_account: followed_match) + + results = described_class.advanced_search_for('match', account) + expect(results).to eq [followed_match, match] + expect(results.first.rank).to be > results.last.rank + end + end +end From f4b463ecb1203beaafbca11b9a760729b9a66223 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 9 Jan 2025 03:17:00 -0500 Subject: [PATCH 063/133] Use `response.parsed_body` for error view application controller spec (#33515) --- .../application_controller_spec.rb | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 4ee951628e..2e7a59db05 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -3,6 +3,8 @@ require 'rails_helper' RSpec.describe ApplicationController do + render_views + controller do def success head 200 @@ -23,9 +25,22 @@ RSpec.describe ApplicationController do shared_examples 'respond_with_error' do |code| it "returns http #{code} for http and renders template" do - expect(subject).to render_template("errors/#{code}", layout: 'error') + subject - expect(response).to have_http_status(code) + expect(response) + .to have_http_status(code) + expect(response.parsed_body) + .to have_css('body[class=error]') + expect(response.parsed_body.css('h1').to_s) + .to include(error_content(code)) + end + + def error_content(code) + if code == 422 + I18n.t('errors.422.content') + else + I18n.t("errors.#{code}") + end end end From 4148b6843097295cb1d110c072946ea8f78fc238 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 9 Jan 2025 03:17:06 -0500 Subject: [PATCH 064/133] Remove `render_template` from remote interaction helper request spec (#33518) --- spec/requests/remote_interaction_helper_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/requests/remote_interaction_helper_spec.rb b/spec/requests/remote_interaction_helper_spec.rb index 942f70b9a4..b89060b5b2 100644 --- a/spec/requests/remote_interaction_helper_spec.rb +++ b/spec/requests/remote_interaction_helper_spec.rb @@ -9,7 +9,6 @@ RSpec.describe 'Remote Interaction Helper' do expect(response) .to have_http_status(200) - .and render_template(:index, layout: 'helper_frame') .and have_attributes( headers: include( 'X-Frame-Options' => 'SAMEORIGIN', @@ -17,6 +16,8 @@ RSpec.describe 'Remote Interaction Helper' do 'Content-Security-Policy' => expected_csp_headers ) ) + expect(response.body) + .to match(/remote_interaction_helper/) end end From cbae00ad235db71dc8963749a616b6b9561afdab Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 9 Jan 2025 03:17:11 -0500 Subject: [PATCH 065/133] Remove `render_template` from accounts request spec (#33519) --- spec/requests/accounts_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/requests/accounts_spec.rb b/spec/requests/accounts_spec.rb index afd9ac80e2..72913ebf22 100644 --- a/spec/requests/accounts_spec.rb +++ b/spec/requests/accounts_spec.rb @@ -53,8 +53,9 @@ RSpec.describe 'Accounts show response' do it 'returns a standard HTML response', :aggregate_failures do expect(response) .to have_http_status(200) - .and render_template(:show) .and have_http_link_header(ActivityPub::TagManager.instance.uri_for(account)).for(rel: 'alternate') + expect(response.parsed_body.at('title').content) + .to include(account.username) end end From 4e2c15b45dbe324d1f202f0260889364fd5f1702 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 08:21:47 +0000 Subject: [PATCH 066/133] New Crowdin Translations (automated) (#33522) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/hu.json | 1 + app/javascript/mastodon/locales/ia.json | 2 ++ app/javascript/mastodon/locales/nn.json | 1 + app/javascript/mastodon/locales/pt-PT.json | 6 ++--- app/javascript/mastodon/locales/sv.json | 1 + config/locales/activerecord.hu.yml | 2 ++ config/locales/activerecord.ia.yml | 2 ++ config/locales/activerecord.lv.yml | 7 +++++ config/locales/activerecord.sv.yml | 2 ++ config/locales/gl.yml | 4 +-- config/locales/lv.yml | 30 ++++++++++++++++++---- config/locales/pt-PT.yml | 2 +- config/locales/simple_form.lv.yml | 9 +++++-- config/locales/simple_form.pt-PT.yml | 4 +-- config/locales/simple_form.sv.yml | 5 ++++ 15 files changed, 63 insertions(+), 15 deletions(-) diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index d1ae388675..703abf242b 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Tartalmi figyelmeztetéssel ellátott szöveg megjelenítése/elrejtése", "keyboard_shortcuts.toggle_sensitivity": "Média megjelenítése/elrejtése", "keyboard_shortcuts.toot": "Új bejegyzés írása", + "keyboard_shortcuts.translate": "Bejegyzés lefordítása", "keyboard_shortcuts.unfocus": "Szerkesztés/keresés fókuszból való kivétele", "keyboard_shortcuts.up": "Mozgás felfelé a listában", "lightbox.close": "Bezárás", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index 0b17e60710..e2f821022e 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Monstrar/celar texto detra advertimento de contento", "keyboard_shortcuts.toggle_sensitivity": "Monstrar/celar multimedia", "keyboard_shortcuts.toot": "Initiar un nove message", + "keyboard_shortcuts.translate": "a traducer un message", "keyboard_shortcuts.unfocus": "Disfocalisar le area de composition de texto/de recerca", "keyboard_shortcuts.up": "Displaciar in alto in le lista", "lightbox.close": "Clauder", @@ -836,6 +837,7 @@ "status.reblogs.empty": "Necuno ha ancora impulsate iste message. Quando alcuno lo face, le impulsos apparera hic.", "status.redraft": "Deler e reconciper", "status.remove_bookmark": "Remover marcapagina", + "status.remove_favourite": "Remover del favoritos", "status.replied_in_thread": "Respondite in le discussion", "status.replied_to": "Respondite a {name}", "status.reply": "Responder", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 6091f1679f..1a7f92be18 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -837,6 +837,7 @@ "status.reblogs.empty": "Ingen har framheva dette tutet enno. Om nokon gjer, så dukkar det opp her.", "status.redraft": "Slett & skriv på nytt", "status.remove_bookmark": "Fjern bokmerke", + "status.remove_favourite": "Fjern frå favorittar", "status.replied_in_thread": "Svara i tråden", "status.replied_to": "Svarte {name}", "status.reply": "Svar", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index fa4f715b6d..b4b3c40dc5 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -85,7 +85,7 @@ "alert.rate_limited.title": "Limite de tentativas", "alert.unexpected.message": "Ocorreu um erro inesperado.", "alert.unexpected.title": "Bolas!", - "alt_text_badge.title": "Texto alternativo", + "alt_text_badge.title": "Texto descritivo", "announcement.announcement": "Mensagem de manutenção", "annual_report.summary.archetype.booster": "O caçador de frescura", "annual_report.summary.archetype.lurker": "O espreitador", @@ -642,10 +642,10 @@ "notifications.policy.filter_hint": "Enviar para a caixa de notificações filtradas", "notifications.policy.filter_limited_accounts_hint": "Limitado pelos moderadores do servidor", "notifications.policy.filter_limited_accounts_title": "Contas moderadas", - "notifications.policy.filter_new_accounts.hint": "Criada {days, plural, one {no último dia} other {nos últimos # dias}}", + "notifications.policy.filter_new_accounts.hint": "Criadas {days, plural, one {no último dia} other {nos últimos # dias}}", "notifications.policy.filter_new_accounts_title": "Novas contas", "notifications.policy.filter_not_followers_hint": "Incluindo pessoas que te seguem há menos de {days, plural, one {um dia} other {# dias}}", - "notifications.policy.filter_not_followers_title": "Pessoas não te seguem", + "notifications.policy.filter_not_followers_title": "Pessoas que não te seguem", "notifications.policy.filter_not_following_hint": "Até que os aproves manualmente", "notifications.policy.filter_not_following_title": "Pessoas que não segues", "notifications.policy.filter_private_mentions_hint": "Filtrado, a não ser que seja em resposta à tua própria menção ou se seguires o remetente", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 486c3ac19d..f451caf6f9 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -697,6 +697,7 @@ "recommended": "Rekommenderas", "refresh": "Läs om", "regeneration_indicator.please_stand_by": "Vänligen vänta.", + "regeneration_indicator.preparing_your_home_feed": "Förbereder ditt hemflöde…", "relative_time.days": "{number}d", "relative_time.full.days": "{number, plural, one {# dag} other {# dagar}} sedan", "relative_time.full.hours": "{number, plural, one {# timme} other {# timmar}} sedan", diff --git a/config/locales/activerecord.hu.yml b/config/locales/activerecord.hu.yml index 27ca9018d4..b26d1afbe3 100644 --- a/config/locales/activerecord.hu.yml +++ b/config/locales/activerecord.hu.yml @@ -24,6 +24,8 @@ hu: models: account: attributes: + fields: + fields_with_values_missing_labels: hiányzó címkékkel rendelkező értékeket tartalmaz username: invalid: csak betűket, számokat vagy alávonást tartalmazhat reserved: foglalt diff --git a/config/locales/activerecord.ia.yml b/config/locales/activerecord.ia.yml index fcb3f68278..35b28a19aa 100644 --- a/config/locales/activerecord.ia.yml +++ b/config/locales/activerecord.ia.yml @@ -24,6 +24,8 @@ ia: models: account: attributes: + fields: + fields_with_values_missing_labels: contine valores con etiquettas perdite username: invalid: debe continer solmente litteras, numeros e lineettas basse reserved: es reservate diff --git a/config/locales/activerecord.lv.yml b/config/locales/activerecord.lv.yml index b7e2db65e8..3fb928c89c 100644 --- a/config/locales/activerecord.lv.yml +++ b/config/locales/activerecord.lv.yml @@ -24,6 +24,8 @@ lv: models: account: attributes: + fields: + fields_with_values_missing_labels: satur vērtības ar trūkstošām iezīmēm username: invalid: drīkst saturēt tikai burtus, ciparus un pasvītras reserved: ir rezervēts @@ -39,6 +41,11 @@ lv: attributes: data: malformed: ir nepareizi veidots + list_account: + attributes: + account_id: + taken: jau ir sarakstā + must_be_following: jābūt kontam, kuram seko status: attributes: reblog: diff --git a/config/locales/activerecord.sv.yml b/config/locales/activerecord.sv.yml index f05161992c..bf1ef22f3d 100644 --- a/config/locales/activerecord.sv.yml +++ b/config/locales/activerecord.sv.yml @@ -24,6 +24,8 @@ sv: models: account: attributes: + fields: + fields_with_values_missing_labels: innehåller värden med saknade etiketter username: invalid: endast bokstäver, siffror och understrykning reserved: är reserverat diff --git a/config/locales/gl.yml b/config/locales/gl.yml index ac6581c8c9..63a2b9b340 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1859,9 +1859,9 @@ gl: '63113904': 2 anos '7889238': 3 meses min_age_label: Límite temporal - min_favs: Manter as publicacións favorecidas polo menos + min_favs: Manter publicacións favorecidas polo menos min_favs_hint: Non elimina ningunha das túas publicacións que recibiron alomenos esta cantidade de favorecementos. Deixa en branco para eliminar publicacións independentemente do número de favorecementos - min_reblogs: Manter publicacións promovidas máis de + min_reblogs: Manter publicacións promovidas polo menos min_reblogs_hint: Non elimina ningunha das túas publicacións se foron promovidas máis deste número de veces. Deixa en branco para eliminar publicacións independentemente do seu número de promocións stream_entries: sensitive_content: Contido sensible diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 0f757c6370..09b629c4da 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -217,6 +217,7 @@ lv: enable_user: Ieslēgt Lietotāju memorialize_account: Saglabāt Kontu Piemiņai promote_user: Izceltt Lietotāju + publish_terms_of_service: Publicēt pakalpojuma izmantošanas nosacījumus reject_appeal: Noraidīt Apelāciju reject_user: Noraidīt lietotāju remove_avatar_user: Noņemt profila attēlu @@ -273,6 +274,7 @@ lv: enable_user_html: "%{name} iespējoja pieteikšanos lietotājam %{target}" memorialize_account_html: "%{name} pārvērta %{target} kontu par atmiņas lapu" promote_user_html: "%{name} paaugstināja lietotāju %{target}" + publish_terms_of_service_html: "%{name} padarīja pieejamus pakalpojuma izmantošanas nosacījumu atjauninājumus" reject_appeal_html: "%{name} noraidīja satura pārraudzības lēmuma iebildumu no %{target}" reject_user_html: "%{name} noraidīja reģistrēšanos no %{target}" remove_avatar_user_html: "%{name} noņēma %{target} profila attēlu" @@ -641,7 +643,7 @@ lv: create_and_resolve: Atrisināt ar piezīmi create_and_unresolve: Atvērt atkārtoti ar piezīmi delete: Dzēst - placeholder: Apraksti veiktās darbības vai citus saistītus atjauninājumus... + placeholder: Jāapraksta veiktās darbības vai jebkuri citi saistītie atjauninājumi... title: Piezīmes notes_description_html: Skati un atstāj piezīmes citiem moderatoriem un sev nākotnei processed_msg: 'Pārskats #%{id} veiksmīgi apstrādāts' @@ -746,13 +748,13 @@ lv: rules: add_new: Pievienot noteikumu delete: Dzēst - description_html: Lai gan lielākā daļa apgalvo, ka ir izlasījuši pakalpojumu sniegšanas noteikumus un piekrīt tiem, parasti cilvēki to izlasa tikai pēc problēmas rašanās. Padariet vienkāršāku sava servera noteikumu uztveršanu, veidojot tos vienkāršā sarakstā pa punktiem. Centieties, lai atsevišķi noteikumi būtu īsi un vienkārši, taču arī nesadaliet tos daudzos atsevišķos vienumos. + description_html: Kaut arī lielākā daļa apgalvo, ka ir lasījuši un piekrīt pakalpojuma izmantošanas nosacījumiem, parasti cilvēki tos neizlasa, līdz rodas sarežģījumi. Padari vienkāršāku sava servera noteikumu pārskatīšanu, sniedzot tos vienkāršā uzsvēruma punktu sarakstā! Jāmēģina atsevišķus noteikumus veidot īsus un vienkāršus, bet jāmēģina arī tos nesadalīt daudzos atsevišķos vienumos. edit: Labot nosacījumu empty: Servera noteikumi vēl nav definēti. title: Servera noteikumi settings: about: - manage_rules: Pārvaldīt servera nosacījumus + manage_rules: Pārvaldīt servera noteikumus preamble: Sniedz padziļinātu informāciju par to, kā serveris tiek darbināts, moderēts un finansēts. rules_hint: Noteikumiem, kas taviem lietotājiem ir jāievēro, ir īpaša sadaļa. title: Par @@ -821,6 +823,7 @@ lv: back_to_account: Atpakaļ uz konta lapu back_to_report: Atpakaļ uz paziņojumu lapu batch: + add_to_report: 'Pievienot atskaitei #%{id}' remove_from_report: Noņemt no ziņojuma report: Ziņojums contents: Saturs @@ -832,13 +835,17 @@ lv: media: title: Multivide metadata: Metadati + no_history: Šis ieraksts nav bijis labots no_status_selected: Neviena ziņa netika mainīta, jo neviena netika atlasīta open: Atvērt ziņu original_status: Oriģinālā ziņa reblogs: Reblogi + replied_to_html: Atbildēja %{acct_link} status_changed: Ziņa mainīta status_title: Publicēja @%{name} + title: Konta ieraksti - @%{name} trending: Aktuāli + view_publicly: Skatīt publiski visibility: Redzamība with_media: Ar multividi strikes: @@ -876,8 +883,8 @@ lv: message_html: 'Nesaderīga Elasticsearch versija: %{value}' version_comparison: Darbojas Elasticsearch %{running_version}, tomēr ir nepieciešama %{required_version} rules_check: - action: Pārvaldīt servera nosacījumus - message_html: Tu neesi definējis nevienu servera nosacījumu. + action: Pārvaldīt servera noteikumus + message_html: Nav pievienots neviens servera noteikums. sidekiq_process_check: message_html: Rindā(s) %{value} nedarbojas neviens Sidekiq process. Lūdzu, pārskati savu Sidekiq konfigurāciju software_version_check: @@ -907,13 +914,21 @@ lv: name: Nosaukums newest: Jaunākie oldest: Vecākie + open: Apskatīt publiski reset: Atiestatīt review: Pārskatīt stāvokli search: Meklēt title: Tēmturi updated_msg: Tēmtura iestatījumi ir veiksmīgi atjaunināti terms_of_service: + back: Atpakaļ uz pakalpojuma izmantošanas nosacījumiem changelog: Kas ir mainījies + create: Izmantot savus + current: Pašreizējie + draft: Melnraksts + generate: Izmantot sagatavi + generates: + action: Izveidot history: Vēsture publish: Publicēt published_on_html: Publicēts %{date} @@ -1826,6 +1841,11 @@ lv: further_actions_html: Ja tas nebiji tu, iesakām nekavējoties %{action} un iespējot divu faktoru autentifikāciju, lai tavs konts būtu drošībā. subject: Tavam kontam ir piekļūts no jaunas IP adreses title: Jauna pieteikšanās + terms_of_service_changed: + sign_off: "%{domain} komanda" + subject: Mūsu pakalpojuma izmantošanas nosacījumu atjauninājumi + subtitle: Mainās %{domain} pakalpojuma izmantošanas nosacījumi + title: Svarīgs atjauninājums warning: appeal: Iesniegt apelāciju appeal_description: Ja uzskatāt, ka tā ir kļūda, varat iesniegt apelāciju %{instance} darbiniekiem. diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index 41fc9fbecc..bba71f165d 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -10,7 +10,7 @@ pt-PT: followers: one: Seguidor other: Seguidores - following: A seguir + following: Seguindo instance_actor_flash: Esta conta é um ator virtual utilizado para representar o servidor em si e não um utilizador individual. É utilizada para efeitos de federação e não deve ser suspensa. last_active: última atividade link_verified_on: A posse desta hiperligação foi verificada em %{date} diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml index 2f4a05dca4..53224966f1 100644 --- a/config/locales/simple_form.lv.yml +++ b/config/locales/simple_form.lv.yml @@ -119,8 +119,8 @@ lv: sign_up_requires_approval: Jaunām reģistrācijām būs nepieciešams tavs apstiprinājums severity: Izvēlies, kas notiks ar pieprasījumiem no šīs IP adreses rule: - hint: Izvēles. Sniedz vairāk informācijas par nosacījumu - text: Apraksti nosacījumus vai prasības šī servera lietotājiem. Centies, lai tas būtu īss un vienkāršs + hint: Izvēles. Sniedz vairāk informācijas par noteikumu + text: Jāapraksta nosacījums vai prasība šī servera lietotājiem. Jāmēģina to veidot īsu un vienkāršu sessions: otp: 'Ievadi divfaktoru kodu, ko ģenerējusi tava tālruņa lietotne, vai izmanto kādu no atkopšanas kodiem:' webauthn: Ja tā ir USB atslēga, noteikti ievieto to un, ja nepieciešams, pieskaries tai. @@ -317,6 +317,11 @@ lv: name: Tēmturis trendable: Atļaut šim tēmturim parādīties zem tendencēm usable: Ļaut ierakstos vietēji izmantot šo tēmturi + terms_of_service: + changelog: Kas ir mainījies? + text: Pakalpojuma izmantošanas nosacījumi + terms_of_service_generator: + domain: Domēna vārds user: role: Loma time_zone: Laika josla diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml index efc64d6dc6..f5a7e3a27e 100644 --- a/config/locales/simple_form.pt-PT.yml +++ b/config/locales/simple_form.pt-PT.yml @@ -161,7 +161,7 @@ pt-PT: fields: name: Rótulo value: Conteúdo - indexable: Incluir mensagens públicas nos resultados da pesquisa + indexable: Incluir mensagens públicas nos resultados de pesquisas show_collections: Mostrar quem sigo e os meus seguidores no perfil unlocked: Aceitar automaticamente novos seguidores account_alias: @@ -205,7 +205,7 @@ pt-PT: email: Endereço de correio electrónico expires_in: Expira em fields: Metadados de perfil - header: Cabeçalho + header: Imagem de cabeçalho honeypot: "%{label} (não preencher)" inbox_url: URL da caixa de entrada do repetidor irreversible: Expandir em vez de esconder diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 5fd258d7e0..421d360fac 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -134,7 +134,9 @@ sv: changelog: Kan struktureras med Markdown syntax. text: Kan struktureras med Markdown syntax. terms_of_service_generator: + arbitration_address: Kan vara samma som fysisk adress ovan, eller “N/A” om du använder e-post arbitration_website: Kan vara ett webbformulär, eller ”N/A” om du använder e-post + dmca_email: Kan vara samma e-postadress som används för “E-postadress för juridiska meddelanden” ovan jurisdiction: Lista det land där vem som än betalar räkningarna bor. Om det är ett företag eller annan enhet, lista landet där det är inkorporerat, och staden, regionen, territoriet eller staten på lämpligt sätt. user: chosen_languages: Vid aktivering visas bara inlägg på dina valda språk i offentliga tidslinjer @@ -330,6 +332,9 @@ sv: text: Användarvillkor terms_of_service_generator: admin_email: E-postadress för juridiska meddelanden + dmca_address: Fysisk adress för meddelanden om DMCA/upphovsrätt + dmca_email: Fysisk adress för meddelanden om DMCA/upphovsrätt + domain: Domän user: role: Roll time_zone: Tidszon From 8e2c642d4436eb06bf3c49379efe93cce20e58ef Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Thu, 9 Jan 2025 09:35:35 +0100 Subject: [PATCH 067/133] Do now swallow response body on persistent connection (#32729) --- app/lib/request.rb | 10 ++------ spec/lib/request_spec.rb | 49 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/app/lib/request.rb b/app/lib/request.rb index 3d2a0c0e31..f984f0e63e 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -111,16 +111,10 @@ class Request end begin - # If we are using a persistent connection, we have to - # read every response to be able to move forward at all. - # However, simply calling #to_s or #flush may not be safe, - # as the response body, if malicious, could be too big - # for our memory. So we use the #body_with_limit method - response.body_with_limit if http_client.persistent? - yield response if block_given? ensure - http_client.close unless http_client.persistent? + response.truncated_body if http_client.persistent? && !response.connection.finished_request? + http_client.close unless http_client.persistent? && response.connection.finished_request? end end diff --git a/spec/lib/request_spec.rb b/spec/lib/request_spec.rb index c600a48ee2..f17cf637b9 100644 --- a/spec/lib/request_spec.rb +++ b/spec/lib/request_spec.rb @@ -4,7 +4,9 @@ require 'rails_helper' require 'securerandom' RSpec.describe Request do - subject { described_class.new(:get, 'http://example.com') } + subject { described_class.new(:get, 'http://example.com', **options) } + + let(:options) { {} } describe '#headers' do it 'returns user agent' do @@ -39,8 +41,8 @@ RSpec.describe Request do end describe '#perform' do - context 'with valid host' do - before { stub_request(:get, 'http://example.com') } + context 'with valid host and non-persistent connection' do + before { stub_request(:get, 'http://example.com').to_return(body: 'lorem ipsum') } it 'executes a HTTP request' do expect { |block| subject.perform(&block) }.to yield_control @@ -71,9 +73,9 @@ RSpec.describe Request do expect(subject.send(:http_client)).to have_received(:close) end - it 'returns response which implements body_with_limit' do + it 'yields response' do subject.perform do |response| - expect(response).to respond_to :body_with_limit + expect(response.body_with_limit).to eq 'lorem ipsum' end end end @@ -95,6 +97,43 @@ RSpec.describe Request do expect { subject.perform }.to raise_error Mastodon::ValidationError end end + + context 'with persistent connection' do + before { stub_request(:get, 'http://example.com').to_return(body: SecureRandom.random_bytes(2.megabytes)) } + + let(:http_client) { described_class.http_client.persistent('http://example.com') } + let(:options) { { http_client: http_client } } + + it 'leaves connection open after completely consumed response' do + allow(http_client).to receive(:close) + + subject.perform { |response| response.truncated_body(3.megabytes) } + + expect(http_client).to_not have_received(:close) + end + + it 'leaves connection open after nearly consumed response' do + allow(http_client).to receive(:close) + + subject.perform { |response| response.truncated_body(1.8.megabytes) } + + expect(http_client).to_not have_received(:close) + end + + it 'closes connection after unconsumed response' do + allow(http_client).to receive(:close) + + subject.perform + + expect(http_client).to have_received(:close) + end + + it 'yields response' do + subject.perform do |response| + expect(response.body_with_limit(2.megabytes).size).to eq 2.megabytes + end + end + end end describe "response's body_with_limit method" do From b0fbb7175997c81cffee815fa34022142fcc8ef0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:55:34 +0100 Subject: [PATCH 068/133] Update dependency jsdom to v26 (#33521) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- streaming/package.json | 2 +- yarn.lock | 121 ++++++++++++++++++++++++----------------- 2 files changed, 71 insertions(+), 52 deletions(-) diff --git a/streaming/package.json b/streaming/package.json index 2419ffd273..b98608edd5 100644 --- a/streaming/package.json +++ b/streaming/package.json @@ -21,7 +21,7 @@ "dotenv": "^16.0.3", "express": "^4.18.2", "ioredis": "^5.3.2", - "jsdom": "^25.0.0", + "jsdom": "^26.0.0", "pg": "^8.5.0", "pg-connection-string": "^2.6.0", "pino": "^9.0.0", diff --git a/yarn.lock b/yarn.lock index a51d49ca56..3136b05aa5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,6 +42,19 @@ __metadata: languageName: node linkType: hard +"@asamuzakjp/css-color@npm:^2.8.2": + version: 2.8.2 + resolution: "@asamuzakjp/css-color@npm:2.8.2" + dependencies: + "@csstools/css-calc": "npm:^2.1.1" + "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-parser-algorithms": "npm:^3.0.4" + "@csstools/css-tokenizer": "npm:^3.0.3" + lru-cache: "npm:^11.0.2" + checksum: 10c0/352b91ca7741876e459cd3cb350a969e842da1e532577157d38365a6da89b7d6e6944249489366ee61b8a225ede1b521e7ab305b70ad4c688b01404061eecca8 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": version: 7.26.0 resolution: "@babel/code-frame@npm:7.26.0" @@ -3061,7 +3074,7 @@ __metadata: eslint-define-config: "npm:^2.0.0" express: "npm:^4.18.2" ioredis: "npm:^5.3.2" - jsdom: "npm:^25.0.0" + jsdom: "npm:^26.0.0" pg: "npm:^8.5.0" pg-connection-string: "npm:^2.6.0" pino: "npm:^9.0.0" @@ -4792,12 +4805,10 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0": - version: 7.1.0 - resolution: "agent-base@npm:7.1.0" - dependencies: - debug: "npm:^4.3.4" - checksum: 10c0/fc974ab57ffdd8421a2bc339644d312a9cca320c20c3393c9d8b1fd91731b9bbabdb985df5fc860f5b79d81c3e350daa3fcb31c5c07c0bb385aafc817df004ce +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": + version: 7.1.3 + resolution: "agent-base@npm:7.1.3" + checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11 languageName: node linkType: hard @@ -7045,12 +7056,13 @@ __metadata: languageName: node linkType: hard -"cssstyle@npm:^4.1.0": - version: 4.1.0 - resolution: "cssstyle@npm:4.1.0" +"cssstyle@npm:^4.2.1": + version: 4.2.1 + resolution: "cssstyle@npm:4.2.1" dependencies: - rrweb-cssom: "npm:^0.7.1" - checksum: 10c0/05c6597e5d3e0ec6b15221f2c0ce9a0443a46cc50a6089a3ba9ee1ac27f83ff86a445a8f95435137dadd859f091fc61b6d342abaf396d3c910471b5b33cfcbfa + "@asamuzakjp/css-color": "npm:^2.8.2" + rrweb-cssom: "npm:^0.8.0" + checksum: 10c0/02ba8c47c0caaab57acadacb3eb6c0f5f009000f55d61f6563670e07d389b26edefeed497e6c1847fcd2e6bbe0b6974c2d4291f97fa0c6ec6add13a7fa926d84 languageName: node linkType: hard @@ -7761,7 +7773,7 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.2.0, entities@npm:^4.4.0": +"entities@npm:^4.2.0, entities@npm:^4.5.0": version: 4.5.0 resolution: "entities@npm:4.5.0" checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 @@ -8918,14 +8930,14 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0": - version: 4.0.0 - resolution: "form-data@npm:4.0.0" +"form-data@npm:^4.0.0, form-data@npm:^4.0.1": + version: 4.0.1 + resolution: "form-data@npm:4.0.1" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" mime-types: "npm:^2.1.12" - checksum: 10c0/cb6f3ac49180be03ff07ba3ff125f9eba2ff0b277fb33c7fc47569fc5e616882c5b1c69b9904c4c4187e97dd0419dd03b134174756f296dec62041e6527e2c6e + checksum: 10c0/bb102d570be8592c23f4ea72d7df9daa50c7792eb0cf1c5d7e506c1706e7426a4e4ae48a35b109e91c85f1c0ec63774a21ae252b66f4eb981cb8efef7d0463c8 languageName: node linkType: hard @@ -9705,13 +9717,13 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5": - version: 7.0.5 - resolution: "https-proxy-agent@npm:7.0.5" +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.6": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" dependencies: - agent-base: "npm:^7.0.2" + agent-base: "npm:^7.1.2" debug: "npm:4" - checksum: 10c0/2490e3acec397abeb88807db52cac59102d5ed758feee6df6112ab3ccd8325e8a1ce8bce6f4b66e5470eca102d31e425ace904242e4fa28dbe0c59c4bafa7b2c + checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac languageName: node linkType: hard @@ -11316,21 +11328,21 @@ __metadata: languageName: node linkType: hard -"jsdom@npm:^25.0.0": - version: 25.0.1 - resolution: "jsdom@npm:25.0.1" +"jsdom@npm:^26.0.0": + version: 26.0.0 + resolution: "jsdom@npm:26.0.0" dependencies: - cssstyle: "npm:^4.1.0" + cssstyle: "npm:^4.2.1" data-urls: "npm:^5.0.0" decimal.js: "npm:^10.4.3" - form-data: "npm:^4.0.0" + form-data: "npm:^4.0.1" html-encoding-sniffer: "npm:^4.0.0" http-proxy-agent: "npm:^7.0.2" - https-proxy-agent: "npm:^7.0.5" + https-proxy-agent: "npm:^7.0.6" is-potential-custom-element-name: "npm:^1.0.1" - nwsapi: "npm:^2.2.12" - parse5: "npm:^7.1.2" - rrweb-cssom: "npm:^0.7.1" + nwsapi: "npm:^2.2.16" + parse5: "npm:^7.2.1" + rrweb-cssom: "npm:^0.8.0" saxes: "npm:^6.0.0" symbol-tree: "npm:^3.2.4" tough-cookie: "npm:^5.0.0" @@ -11338,15 +11350,15 @@ __metadata: webidl-conversions: "npm:^7.0.0" whatwg-encoding: "npm:^3.1.1" whatwg-mimetype: "npm:^4.0.0" - whatwg-url: "npm:^14.0.0" + whatwg-url: "npm:^14.1.0" ws: "npm:^8.18.0" xml-name-validator: "npm:^5.0.0" peerDependencies: - canvas: ^2.11.2 + canvas: ^3.0.0 peerDependenciesMeta: canvas: optional: true - checksum: 10c0/6bda32a6dfe4e37a30568bf51136bdb3ba9c0b72aadd6356280404275a34c9e097c8c25b5eb3c742e602623741e172da977ff456684befd77c9042ed9bf8c2b4 + checksum: 10c0/e48725ba4027edcfc9bca5799eaec72c6561ecffe3675a8ff87fe9c3541ca4ff9f82b4eff5b3d9c527302da0d859b2f60e9364347a5d42b77f5c76c436c569dc languageName: node linkType: hard @@ -11813,6 +11825,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^11.0.2": + version: 11.0.2 + resolution: "lru-cache@npm:11.0.2" + checksum: 10c0/c993b8e06ead0b24b969c1dbb5b301716aed66e320e9014a80012f5febe280b438f28ff50046b2c55ff404e889351ccb332ff91f8dd175a21f5eae80e3fb155f + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -12640,10 +12659,10 @@ __metadata: languageName: node linkType: hard -"nwsapi@npm:^2.2.12, nwsapi@npm:^2.2.2": - version: 2.2.12 - resolution: "nwsapi@npm:2.2.12" - checksum: 10c0/95e9623d63df111405503df8c5d800e26f71675d319e2c9c70cddfa31e5ace1d3f8b6d98d354544fc156a1506d920ec291e303fab761e4f99296868e199a466e +"nwsapi@npm:^2.2.16, nwsapi@npm:^2.2.2": + version: 2.2.16 + resolution: "nwsapi@npm:2.2.16" + checksum: 10c0/0aa0637f4d51043d0183d994e08336bae996b03b42984381bf09ebdf3ff4909c018eda6b2a8aba0a08f3ea8303db8a0dad0608b38dc0bff15fd87017286ae21a languageName: node linkType: hard @@ -13040,12 +13059,12 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^7.0.0, parse5@npm:^7.1.1, parse5@npm:^7.1.2": - version: 7.1.2 - resolution: "parse5@npm:7.1.2" +"parse5@npm:^7.0.0, parse5@npm:^7.1.1, parse5@npm:^7.2.1": + version: 7.2.1 + resolution: "parse5@npm:7.2.1" dependencies: - entities: "npm:^4.4.0" - checksum: 10c0/297d7af8224f4b5cb7f6617ecdae98eeaed7f8cbd78956c42785e230505d5a4f07cef352af10d3006fa5c1544b76b57784d3a22d861ae071bbc460c649482bf4 + entities: "npm:^4.5.0" + checksum: 10c0/829d37a0c709215a887e410a7118d754f8e1afd7edb529db95bc7bbf8045fb0266a7b67801331d8e8d9d073ea75793624ec27ce9ff3b96862c3b9008f4d68e80 languageName: node linkType: hard @@ -15590,10 +15609,10 @@ __metadata: languageName: node linkType: hard -"rrweb-cssom@npm:^0.7.1": - version: 0.7.1 - resolution: "rrweb-cssom@npm:0.7.1" - checksum: 10c0/127b8ca6c8aac45e2755abbae6138d4a813b1bedc2caabf79466ae83ab3cfc84b5bfab513b7033f0aa4561c7753edf787d0dd01163ceacdee2e8eb1b6bf7237e +"rrweb-cssom@npm:^0.8.0": + version: 0.8.0 + resolution: "rrweb-cssom@npm:0.8.0" + checksum: 10c0/56f2bfd56733adb92c0b56e274c43f864b8dd48784d6fe946ef5ff8d438234015e59ad837fc2ad54714b6421384141c1add4eb569e72054e350d1f8a50b8ac7b languageName: node linkType: hard @@ -18334,13 +18353,13 @@ __metadata: languageName: node linkType: hard -"whatwg-url@npm:^14.0.0": - version: 14.0.0 - resolution: "whatwg-url@npm:14.0.0" +"whatwg-url@npm:^14.0.0, whatwg-url@npm:^14.1.0": + version: 14.1.0 + resolution: "whatwg-url@npm:14.1.0" dependencies: tr46: "npm:^5.0.0" webidl-conversions: "npm:^7.0.0" - checksum: 10c0/ac32e9ba9d08744605519bbe9e1371174d36229689ecc099157b6ba102d4251a95e81d81f3d80271eb8da182eccfa65653f07f0ab43ea66a6934e643fd091ba9 + checksum: 10c0/f00104f1c67ce086ba8ffedab529cbbd9aefd8c0a6555320026de7aeff31f91c38680f95818b140a7c9cc657cde3781e567835dda552ddb1e2b8faaba0ac3cb6 languageName: node linkType: hard From 51a92427cea51064debe59abe70c48a39fac41e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 11:04:14 +0100 Subject: [PATCH 069/133] Update dependency react-intl to v7 (#32954) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 128 ++++++++------------------------------------------- 2 files changed, 21 insertions(+), 109 deletions(-) diff --git a/package.json b/package.json index a4817816a2..6cb314db34 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "react-hotkeys": "^1.1.4", "react-immutable-proptypes": "^2.2.0", "react-immutable-pure-component": "^2.2.2", - "react-intl": "^6.4.2", + "react-intl": "^7.0.0", "react-motion": "^0.5.2", "react-notification": "^6.8.5", "react-overlays": "^5.2.1", diff --git a/yarn.lock b/yarn.lock index 3136b05aa5..e708282d87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2280,17 +2280,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/ecma402-abstract@npm:2.2.4": - version: 2.2.4 - resolution: "@formatjs/ecma402-abstract@npm:2.2.4" - dependencies: - "@formatjs/fast-memoize": "npm:2.2.3" - "@formatjs/intl-localematcher": "npm:0.5.8" - tslib: "npm:2" - checksum: 10c0/3f262533fa704ea7a1a7a8107deee2609774a242c621f8cb5dd4bf4c97abf2fc12f5aeda3f4ce85be18147c484a0ca87303dca6abef53290717e685c55eabd2d - languageName: node - linkType: hard - "@formatjs/ecma402-abstract@npm:2.3.2": version: 2.3.2 resolution: "@formatjs/ecma402-abstract@npm:2.3.2" @@ -2312,15 +2301,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/fast-memoize@npm:2.2.3": - version: 2.2.3 - resolution: "@formatjs/fast-memoize@npm:2.2.3" - dependencies: - tslib: "npm:2" - checksum: 10c0/f1004c3b280de7e362bd37c5f48ff34c2ba1d6271d4a7b695fed561d1201a3379397824d8bffbf15fecee344d1e70398393bbb04297f242692310a305f12e75b - languageName: node - linkType: hard - "@formatjs/fast-memoize@npm:2.2.6": version: 2.2.6 resolution: "@formatjs/fast-memoize@npm:2.2.6" @@ -2341,17 +2321,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/icu-messageformat-parser@npm:2.9.4": - version: 2.9.4 - resolution: "@formatjs/icu-messageformat-parser@npm:2.9.4" - dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.4" - "@formatjs/icu-skeleton-parser": "npm:1.8.8" - tslib: "npm:2" - checksum: 10c0/f1ed14ece7ef0abc9fb62e323b78c994fc772d346801ad5aaa9555e1a7d5c0fda791345f4f2e53a3223f0b82c1a4eaf9a83544c1c20cb39349d1a39bedcf1648 - languageName: node - linkType: hard - "@formatjs/icu-messageformat-parser@npm:2.9.8": version: 2.9.8 resolution: "@formatjs/icu-messageformat-parser@npm:2.9.8" @@ -2383,38 +2352,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/icu-skeleton-parser@npm:1.8.8": - version: 1.8.8 - resolution: "@formatjs/icu-skeleton-parser@npm:1.8.8" - dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.4" - tslib: "npm:2" - checksum: 10c0/5ad78a5682e83b973e6fed4fca68660b944c41d1e941f0c84d69ff3d10ae835330062dc0a2cf0d237d2675ad3463405061a3963c14c2b9d8d1c1911f892b1a8d - languageName: node - linkType: hard - -"@formatjs/intl-displaynames@npm:6.8.5": - version: 6.8.5 - resolution: "@formatjs/intl-displaynames@npm:6.8.5" - dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.4" - "@formatjs/intl-localematcher": "npm:0.5.8" - tslib: "npm:2" - checksum: 10c0/1092d6bac9ba7ee22470b85c9af16802244aa8a54f07e6cd560d15b96e8a08fc359f20dee88a064fe4c9ca8860f439abb109cbb7977b9ccceb846e28aacdf29c - languageName: node - linkType: hard - -"@formatjs/intl-listformat@npm:7.7.5": - version: 7.7.5 - resolution: "@formatjs/intl-listformat@npm:7.7.5" - dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.4" - "@formatjs/intl-localematcher": "npm:0.5.8" - tslib: "npm:2" - checksum: 10c0/f514397f6b05ac29171fffbbd15636fbec086080058c79c159f24edd2038747c22579d46ebf339cbb672f8505ea408e5d960d6751064c16e02d18445cf4e7e61 - languageName: node - linkType: hard - "@formatjs/intl-localematcher@npm:0.5.10": version: 0.5.10 resolution: "@formatjs/intl-localematcher@npm:0.5.10" @@ -2433,15 +2370,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-localematcher@npm:0.5.8": - version: 0.5.8 - resolution: "@formatjs/intl-localematcher@npm:0.5.8" - dependencies: - tslib: "npm:2" - checksum: 10c0/7a660263986326b662d4cb537e8386331c34fda61fb830b105e6c62d49be58ace40728dae614883b27a41cec7b1df8b44f72f79e16e6028bfca65d398dc04f3b - languageName: node - linkType: hard - "@formatjs/intl-pluralrules@npm:^5.2.2": version: 5.4.2 resolution: "@formatjs/intl-pluralrules@npm:5.4.2" @@ -2454,23 +2382,21 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl@npm:2.10.15": - version: 2.10.15 - resolution: "@formatjs/intl@npm:2.10.15" +"@formatjs/intl@npm:3.1.0": + version: 3.1.0 + resolution: "@formatjs/intl@npm:3.1.0" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.4" - "@formatjs/fast-memoize": "npm:2.2.3" - "@formatjs/icu-messageformat-parser": "npm:2.9.4" - "@formatjs/intl-displaynames": "npm:6.8.5" - "@formatjs/intl-listformat": "npm:7.7.5" - intl-messageformat: "npm:10.7.7" + "@formatjs/ecma402-abstract": "npm:2.3.2" + "@formatjs/fast-memoize": "npm:2.2.6" + "@formatjs/icu-messageformat-parser": "npm:2.9.8" + intl-messageformat: "npm:10.7.11" tslib: "npm:2" peerDependencies: - typescript: ^4.7 || 5 + typescript: 5 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/5d51fd0785d5547f375991d7df2d6303479b0083eeb35c42c30c9633aab77101895498f1eace419fd34fdb5c84aea19037c5280c3a9d85f9c3ffe6eef76b6f39 + checksum: 10c0/a073768fffc51696eb7bd25fe1f0afdda1a0e38db3e2dd9b2fc3138ea799f00ef522f3d3083626ad3acbf913593254cfd728a6c6b08ef4f167dd132626a7e9fc languageName: node linkType: hard @@ -3004,7 +2930,7 @@ __metadata: react-hotkeys: "npm:^1.1.4" react-immutable-proptypes: "npm:^2.2.0" react-immutable-pure-component: "npm:^2.2.2" - react-intl: "npm:^6.4.2" + react-intl: "npm:^7.0.0" react-motion: "npm:^0.5.2" react-notification: "npm:^6.8.5" react-overlays: "npm:^5.2.1" @@ -9982,19 +9908,7 @@ __metadata: languageName: node linkType: hard -"intl-messageformat@npm:10.7.7": - version: 10.7.7 - resolution: "intl-messageformat@npm:10.7.7" - dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.4" - "@formatjs/fast-memoize": "npm:2.2.3" - "@formatjs/icu-messageformat-parser": "npm:2.9.4" - tslib: "npm:2" - checksum: 10c0/691895fb6a73a2feb2569658706e0d452861441de184dd1c9201e458a39fb80fc80080dd40d3d370400a52663f87de7a6d5a263c94245492f7265dd760441a95 - languageName: node - linkType: hard - -"intl-messageformat@npm:^10.3.5": +"intl-messageformat@npm:10.7.11, intl-messageformat@npm:^10.3.5": version: 10.7.11 resolution: "intl-messageformat@npm:10.7.11" dependencies: @@ -14744,27 +14658,25 @@ __metadata: languageName: node linkType: hard -"react-intl@npm:^6.4.2": - version: 6.8.9 - resolution: "react-intl@npm:6.8.9" +"react-intl@npm:^7.0.0": + version: 7.1.0 + resolution: "react-intl@npm:7.1.0" dependencies: - "@formatjs/ecma402-abstract": "npm:2.2.4" - "@formatjs/icu-messageformat-parser": "npm:2.9.4" - "@formatjs/intl": "npm:2.10.15" - "@formatjs/intl-displaynames": "npm:6.8.5" - "@formatjs/intl-listformat": "npm:7.7.5" + "@formatjs/ecma402-abstract": "npm:2.3.2" + "@formatjs/icu-messageformat-parser": "npm:2.9.8" + "@formatjs/intl": "npm:3.1.0" "@types/hoist-non-react-statics": "npm:3" "@types/react": "npm:16 || 17 || 18" hoist-non-react-statics: "npm:3" - intl-messageformat: "npm:10.7.7" + intl-messageformat: "npm:10.7.11" tslib: "npm:2" peerDependencies: react: ^16.6.0 || 17 || 18 - typescript: ^4.7 || 5 + typescript: 5 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/d42a6252beac5448b4a248d84923b0f75dfbbee6208cd5c49ac2f525714ab94efe2a4933d464c64cb161ddccaa37b83dffb2dd0529428219b8a60ce548da3e57 + checksum: 10c0/9d69e316a5f5c6d31fa77f136b595079db2f75f63398cf8253d407878246dd5bcf0cc2eb4d7d4aa0646530ee58b16ce9a8c3876a5c2f0dc38fdda7e4f8c07615 languageName: node linkType: hard From 91c75a63616e8a8616cc09484e033bab2780a925 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 9 Jan 2025 14:38:23 +0100 Subject: [PATCH 070/133] Re-introduce `application_id` in `ScheduledStatusSerializer` (#33505) --- app/serializers/rest/scheduled_status_serializer.rb | 4 ---- spec/serializers/rest/scheduled_status_serializer_spec.rb | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/serializers/rest/scheduled_status_serializer.rb b/app/serializers/rest/scheduled_status_serializer.rb index 8aa0d89386..7c54f39c0d 100644 --- a/app/serializers/rest/scheduled_status_serializer.rb +++ b/app/serializers/rest/scheduled_status_serializer.rb @@ -8,8 +8,4 @@ class REST::ScheduledStatusSerializer < ActiveModel::Serializer def id object.id.to_s end - - def params - object.params.without('application_id') - end end diff --git a/spec/serializers/rest/scheduled_status_serializer_spec.rb b/spec/serializers/rest/scheduled_status_serializer_spec.rb index 08ad8a6f8a..2cf0098654 100644 --- a/spec/serializers/rest/scheduled_status_serializer_spec.rb +++ b/spec/serializers/rest/scheduled_status_serializer_spec.rb @@ -17,7 +17,7 @@ RSpec.describe REST::ScheduledStatusSerializer do expect(subject.deep_symbolize_keys) .to include( scheduled_at: be_a(String).and(match_api_datetime_format), - params: not_include(:application_id) + params: include(:application_id) ) end end From c6c8e7e6ab03754f6456eaf7bfc26a9a318140dc Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 9 Jan 2025 14:47:12 +0100 Subject: [PATCH 071/133] Fix last paginated notification group only including data on a single notification (#33271) --- .../api/v2/notifications_controller.rb | 23 ++++++++- app/models/notification_group.rb | 26 +++++++--- spec/requests/api/v2/notifications_spec.rb | 49 +++++++++++++++++++ 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/v2/notifications_controller.rb b/app/controllers/api/v2/notifications_controller.rb index c070c0e5e7..cc38b95114 100644 --- a/app/controllers/api/v2/notifications_controller.rb +++ b/app/controllers/api/v2/notifications_controller.rb @@ -80,10 +80,31 @@ class Api::V2::NotificationsController < Api::BaseController return [] if @notifications.empty? MastodonOTELTracer.in_span('Api::V2::NotificationsController#load_grouped_notifications') do - NotificationGroup.from_notifications(@notifications, pagination_range: (@notifications.last.id)..(@notifications.first.id), grouped_types: params[:grouped_types]) + pagination_range = (@notifications.last.id)..@notifications.first.id + + # If the page is incomplete, we know we are on the last page + if incomplete_page? + if paginating_up? + pagination_range = @notifications.last.id...(params[:max_id]&.to_i) + else + range_start = params[:since_id]&.to_i + range_start += 1 unless range_start.nil? + pagination_range = range_start..(@notifications.first.id) + end + end + + NotificationGroup.from_notifications(@notifications, pagination_range: pagination_range, grouped_types: params[:grouped_types]) end end + def incomplete_page? + @notifications.size < limit_param(DEFAULT_NOTIFICATIONS_LIMIT) + end + + def paginating_up? + params[:min_id].present? + end + def browserable_account_notifications current_account.notifications.without_suspended.browserable( types: Array(browserable_params[:types]), diff --git a/app/models/notification_group.rb b/app/models/notification_group.rb index 9331b9406f..bf790bf7cd 100644 --- a/app/models/notification_group.rb +++ b/app/models/notification_group.rb @@ -64,21 +64,31 @@ class NotificationGroup < ActiveModelSerializers::Model binds = [ account_id, SAMPLE_ACCOUNTS_SIZE, - pagination_range.begin, - pagination_range.end, ActiveRecord::Relation::QueryAttribute.new('group_keys', group_keys, ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(ActiveModel::Type::String.new)), + pagination_range.begin || 0, ] + binds << pagination_range.end unless pagination_range.end.nil? + + upper_bound_cond = begin + if pagination_range.end.nil? + '' + elsif pagination_range.exclude_end? + 'AND id < $5' + else + 'AND id <= $5' + end + end ActiveRecord::Base.connection.select_all(<<~SQL.squish, 'grouped_notifications', binds).cast_values.to_h { |k, *values| [k, values] } SELECT groups.group_key, - (SELECT id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id <= $4 ORDER BY id DESC LIMIT 1), - array(SELECT from_account_id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id <= $4 ORDER BY id DESC LIMIT $2), - (SELECT count(*) FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id <= $4) AS notifications_count, - (SELECT id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id >= $3 ORDER BY id ASC LIMIT 1) AS min_id, - (SELECT created_at FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id <= $4 ORDER BY id DESC LIMIT 1) + (SELECT id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key #{upper_bound_cond} ORDER BY id DESC LIMIT 1), + array(SELECT from_account_id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key #{upper_bound_cond} ORDER BY id DESC LIMIT $2), + (SELECT count(*) FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key #{upper_bound_cond}) AS notifications_count, + (SELECT id FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key AND id >= $4 ORDER BY id ASC LIMIT 1) AS min_id, + (SELECT created_at FROM notifications WHERE notifications.account_id = $1 AND notifications.group_key = groups.group_key #{upper_bound_cond} ORDER BY id DESC LIMIT 1) FROM - unnest($5::text[]) AS groups(group_key); + unnest($3::text[]) AS groups(group_key); SQL else binds = [ diff --git a/spec/requests/api/v2/notifications_spec.rb b/spec/requests/api/v2/notifications_spec.rb index ffa0a71c77..aa4a861557 100644 --- a/spec/requests/api/v2/notifications_spec.rb +++ b/spec/requests/api/v2/notifications_spec.rb @@ -143,6 +143,55 @@ RSpec.describe 'Notifications' do end end + context 'when there are numerous notifications for the same final group' do + before do + user.account.notifications.destroy_all + 5.times.each { FavouriteService.new.call(Fabricate(:account), user.account.statuses.first) } + end + + context 'with no options' do + it 'returns a notification group covering all notifications' do + subject + + notification_ids = user.account.notifications.reload.pluck(:id) + + expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body[:notification_groups]).to contain_exactly( + a_hash_including( + type: 'favourite', + sample_account_ids: have_attributes(size: 5), + page_min_id: notification_ids.first.to_s, + page_max_id: notification_ids.last.to_s + ) + ) + end + end + + context 'with min_id param' do + let(:params) { { min_id: user.account.notifications.reload.first.id - 1 } } + + it 'returns a notification group covering all notifications' do + subject + + notification_ids = user.account.notifications.reload.pluck(:id) + + expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body[:notification_groups]).to contain_exactly( + a_hash_including( + type: 'favourite', + sample_account_ids: have_attributes(size: 5), + page_min_id: notification_ids.first.to_s, + page_max_id: notification_ids.last.to_s + ) + ) + end + end + end + context 'with no options' do it 'returns expected notification types', :aggregate_failures do subject From a8b2b474d79b9621dfa6309b83a5678f25926cde Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 9 Jan 2025 15:22:33 +0100 Subject: [PATCH 072/133] Add timestamp to all announcements in Web UI (#18329) --- .../components/announcements.jsx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/getting_started/components/announcements.jsx b/app/javascript/mastodon/features/getting_started/components/announcements.jsx index 713ad9f069..ad66d2e5fa 100644 --- a/app/javascript/mastodon/features/getting_started/components/announcements.jsx +++ b/app/javascript/mastodon/features/getting_started/components/announcements.jsx @@ -335,15 +335,29 @@ class Announcement extends ImmutablePureComponent { const endsAt = announcement.get('ends_at') && new Date(announcement.get('ends_at')); const now = new Date(); const hasTimeRange = startsAt && endsAt; - const skipYear = hasTimeRange && startsAt.getFullYear() === endsAt.getFullYear() && endsAt.getFullYear() === now.getFullYear(); - const skipEndDate = hasTimeRange && startsAt.getDate() === endsAt.getDate() && startsAt.getMonth() === endsAt.getMonth() && startsAt.getFullYear() === endsAt.getFullYear(); const skipTime = announcement.get('all_day'); + let timestamp = null; + if (hasTimeRange) { + const skipYear = startsAt.getFullYear() === endsAt.getFullYear() && endsAt.getFullYear() === now.getFullYear(); + const skipEndDate = startsAt.getDate() === endsAt.getDate() && startsAt.getMonth() === endsAt.getMonth() && startsAt.getFullYear() === endsAt.getFullYear(); + timestamp = ( + <> + - + + ); + } else { + const publishedAt = new Date(announcement.get('published_at')); + timestamp = ( + + ); + } + return (
- {hasTimeRange && · - } + · {timestamp} From 9b8d1fb6d171e13f043c05e9311a5de6fb9029b6 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 9 Jan 2025 09:32:48 -0500 Subject: [PATCH 073/133] Add `Account#actor_type_application?` query method (#33525) --- app/models/account.rb | 10 +++++++--- spec/models/account_spec.rb | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index fefc40869f..7321cff3db 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -107,14 +107,14 @@ class Account < ApplicationRecord validates_with UniqueUsernameValidator, if: -> { will_save_change_to_username? } # Remote user validations, also applies to internal actors - validates :username, format: { with: USERNAME_ONLY_RE }, if: -> { (remote? || actor_type == 'Application') && will_save_change_to_username? } + validates :username, format: { with: USERNAME_ONLY_RE }, if: -> { (remote? || actor_type_application?) && will_save_change_to_username? } # Remote user validations validates :uri, presence: true, unless: :local?, on: :create # Local user validations - validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: USERNAME_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' } - validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' } + validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: USERNAME_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_username? && !actor_type_application? } + validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? && !actor_type_application? } validates :display_name, length: { maximum: DISPLAY_NAME_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_display_name? } validates :note, note_length: { maximum: NOTE_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_note? } validates :fields, length: { maximum: DEFAULT_FIELDS_SIZE }, if: -> { local? && will_save_change_to_fields? } @@ -208,6 +208,10 @@ class Account < ApplicationRecord self.actor_type = ActiveModel::Type::Boolean.new.cast(val) ? 'Service' : 'Person' end + def actor_type_application? + actor_type == 'Application' + end + def group? actor_type == 'Group' end diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index f70690ddb6..3e9b36652f 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -80,6 +80,20 @@ RSpec.describe Account do end end + describe '#actor_type_application?' do + context 'when the actor is not of type application' do + subject { Fabricate.build :account, actor_type: 'Person' } + + it { is_expected.to_not be_actor_type_application } + end + + context 'when the actor is of type application' do + subject { Fabricate.build :account, actor_type: 'Application' } + + it { is_expected.to be_actor_type_application } + end + end + describe 'Local domain user methods' do subject { Fabricate(:account, domain: nil, username: 'alice') } From 3a4aed989021f0feeda491972bb73a3263c3255a Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 9 Jan 2025 09:39:49 -0500 Subject: [PATCH 074/133] Rename `app/helpers/jsonld_helper.rb` to `app/helpers/json_ld_helper.rb` (#33489) --- .rubocop_todo.yml | 6 +++--- app/helpers/{jsonld_helper.rb => json_ld_helper.rb} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename app/helpers/{jsonld_helper.rb => json_ld_helper.rb} (100%) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 552054898e..12ef0ad620 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.69.1. +# using RuboCop version 1.69.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -8,7 +8,7 @@ Lint/NonLocalExitFromIterator: Exclude: - - 'app/helpers/jsonld_helper.rb' + - 'app/helpers/json_ld_helper.rb' # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: @@ -82,7 +82,7 @@ Style/MutableConstant: # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - - 'app/helpers/jsonld_helper.rb' + - 'app/helpers/json_ld_helper.rb' - 'app/lib/admin/system_check/message.rb' - 'app/lib/request.rb' - 'app/lib/webfinger.rb' diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/json_ld_helper.rb similarity index 100% rename from app/helpers/jsonld_helper.rb rename to app/helpers/json_ld_helper.rb From 54e20301462b381f27c50ed305abeedde1ace878 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 9 Jan 2025 10:08:39 -0500 Subject: [PATCH 075/133] Add `AccountWarning#appeal_eligible?` method (#33526) --- app/models/account_warning.rb | 5 +++++ app/models/appeal.rb | 4 +--- app/policies/account_warning_policy.rb | 2 +- spec/models/account_warning_spec.rb | 14 ++++++++++++++ spec/models/appeal_spec.rb | 2 +- spec/policies/account_warning_policy_spec.rb | 4 ++-- 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/app/models/account_warning.rb b/app/models/account_warning.rb index 7aa474887b..9058f73fb8 100644 --- a/app/models/account_warning.rb +++ b/app/models/account_warning.rb @@ -27,6 +27,7 @@ class AccountWarning < ApplicationRecord suspend: 4_000, }, suffix: :action + APPEAL_WINDOW = 20.days RECENT_PERIOD = 3.months.freeze normalizes :text, with: ->(text) { text.to_s }, apply_to_nil: true @@ -49,6 +50,10 @@ class AccountWarning < ApplicationRecord overruled_at.present? end + def appeal_eligible? + created_at >= APPEAL_WINDOW.ago + end + def to_log_human_identifier target_account.acct end diff --git a/app/models/appeal.rb b/app/models/appeal.rb index fafa75e69d..6a75fec661 100644 --- a/app/models/appeal.rb +++ b/app/models/appeal.rb @@ -16,8 +16,6 @@ # updated_at :datetime not null # class Appeal < ApplicationRecord - MAX_STRIKE_AGE = 20.days - TEXT_LENGTH_LIMIT = 2_000 belongs_to :account @@ -68,6 +66,6 @@ class Appeal < ApplicationRecord private def validate_time_frame - errors.add(:base, I18n.t('strikes.errors.too_late')) if strike.created_at < MAX_STRIKE_AGE.ago + errors.add(:base, I18n.t('strikes.errors.too_late')) unless strike.appeal_eligible? end end diff --git a/app/policies/account_warning_policy.rb b/app/policies/account_warning_policy.rb index 4f8df7420e..976cae6c9c 100644 --- a/app/policies/account_warning_policy.rb +++ b/app/policies/account_warning_policy.rb @@ -6,7 +6,7 @@ class AccountWarningPolicy < ApplicationPolicy end def appeal? - target? && record.created_at >= Appeal::MAX_STRIKE_AGE.ago + target? && record.appeal_eligible? end private diff --git a/spec/models/account_warning_spec.rb b/spec/models/account_warning_spec.rb index 37866ce3da..9fe2b331eb 100644 --- a/spec/models/account_warning_spec.rb +++ b/spec/models/account_warning_spec.rb @@ -8,4 +8,18 @@ RSpec.describe AccountWarning do it { is_expected.to normalize(:text).from(nil).to('') } end end + + describe '#appeal_eligible?' do + context 'when created too long ago' do + subject { Fabricate.build :account_warning, created_at: (described_class::APPEAL_WINDOW * 2).ago } + + it { is_expected.to_not be_appeal_eligible } + end + + context 'when created recently' do + subject { Fabricate.build :account_warning, created_at: (described_class::APPEAL_WINDOW - 2.days).ago } + + it { is_expected.to be_appeal_eligible } + end + end end diff --git a/spec/models/appeal_spec.rb b/spec/models/appeal_spec.rb index 06775f5dd6..d624e14949 100644 --- a/spec/models/appeal_spec.rb +++ b/spec/models/appeal_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Appeal do it { is_expected.to validate_length_of(:text).is_at_most(described_class::TEXT_LENGTH_LIMIT) } context 'with a strike created too long ago' do - let(:strike) { Fabricate.build :account_warning, created_at: (described_class::MAX_STRIKE_AGE * 2).ago } + let(:strike) { Fabricate.build :account_warning, created_at: (AccountWarning::APPEAL_WINDOW * 2).ago } it { is_expected.to_not allow_values(strike).for(:strike).against(:base).on(:create) } end diff --git a/spec/policies/account_warning_policy_spec.rb b/spec/policies/account_warning_policy_spec.rb index 75142e2071..2603794886 100644 --- a/spec/policies/account_warning_policy_spec.rb +++ b/spec/policies/account_warning_policy_spec.rb @@ -31,11 +31,11 @@ RSpec.describe AccountWarningPolicy do context 'when account is target' do context 'when record is appealable' do - it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago + 1.hour)) } + it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id, created_at: AccountWarning::APPEAL_WINDOW.ago + 1.hour)) } end context 'when record is not appealable' do - it { is_expected.to_not permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago - 1.hour)) } + it { is_expected.to_not permit(account, AccountWarning.new(target_account_id: account.id, created_at: AccountWarning::APPEAL_WINDOW.ago - 1.hour)) } end end end From f98972e4ebbdaf586c3e267bcae35a8922849af3 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 04:00:31 -0500 Subject: [PATCH 076/133] Use `with_options` for Account `if: :local?` validation group (#33529) --- app/models/account.rb | 10 +++++----- spec/models/account_spec.rb | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index 7321cff3db..6258857b1b 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -119,11 +119,11 @@ class Account < ApplicationRecord validates :note, note_length: { maximum: NOTE_LENGTH_LIMIT }, if: -> { local? && will_save_change_to_note? } validates :fields, length: { maximum: DEFAULT_FIELDS_SIZE }, if: -> { local? && will_save_change_to_fields? } validates_with EmptyProfileFieldNamesValidator, if: -> { local? && will_save_change_to_fields? } - with_options on: :create do - validates :uri, absence: true, if: :local? - validates :inbox_url, absence: true, if: :local? - validates :shared_inbox_url, absence: true, if: :local? - validates :followers_url, absence: true, if: :local? + with_options on: :create, if: :local? do + validates :followers_url, absence: true + validates :inbox_url, absence: true + validates :shared_inbox_url, absence: true + validates :uri, absence: true end normalizes :username, with: ->(username) { username.squish } diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 3e9b36652f..5b995b4af6 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -595,6 +595,11 @@ RSpec.describe Account do it { is_expected.to allow_value(fields_empty_name_value).for(:fields) } it { is_expected.to_not allow_values(fields_over_limit, fields_empty_name).for(:fields) } + + it { is_expected.to validate_absence_of(:followers_url).on(:create) } + it { is_expected.to validate_absence_of(:inbox_url).on(:create) } + it { is_expected.to validate_absence_of(:shared_inbox_url).on(:create) } + it { is_expected.to validate_absence_of(:uri).on(:create) } end context 'when account is remote' do From 846c89b66e6300e438df17f3a2d11645cd460851 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:00:41 +0000 Subject: [PATCH 077/133] New Crowdin Translations (automated) (#33541) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/hu.json | 2 +- app/javascript/mastodon/locales/pl.json | 3 ++ app/javascript/mastodon/locales/pt-BR.json | 6 ++-- app/javascript/mastodon/locales/pt-PT.json | 4 +-- config/locales/lv.yml | 41 +++++++++++++++++----- config/locales/simple_form.lv.yml | 2 +- config/locales/simple_form.sv.yml | 2 +- 7 files changed, 44 insertions(+), 16 deletions(-) diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 703abf242b..847f7871fa 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -453,7 +453,7 @@ "keyboard_shortcuts.requests": "Követési kérések listájának megnyitása", "keyboard_shortcuts.search": "Fókuszálás a keresősávra", "keyboard_shortcuts.spoilers": "Tartalmi figyelmeztetés mező megjelenítése/elrejtése", - "keyboard_shortcuts.start": "\"Első lépések\" oszlop megnyitása", + "keyboard_shortcuts.start": "„Első lépések” oszlop megnyitása", "keyboard_shortcuts.toggle_hidden": "Tartalmi figyelmeztetéssel ellátott szöveg megjelenítése/elrejtése", "keyboard_shortcuts.toggle_sensitivity": "Média megjelenítése/elrejtése", "keyboard_shortcuts.toot": "Új bejegyzés írása", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index fe56267ea1..f9cb9743ac 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -406,6 +406,9 @@ "ignore_notifications_modal.not_followers_title": "Ignoruj powiadomienia od użytkowników którzy cię nie obserwują?", "ignore_notifications_modal.not_following_title": "Ignoruj powiadomienia od użytkowników których nie obserwujesz?", "ignore_notifications_modal.private_mentions_title": "Ignoruj powiadomienia o nieproszonych wzmiankach prywatnych?", + "interaction_modal.action.favourite": "Aby kontynuować, musisz dodać do ulubionych na swoim koncie.", + "interaction_modal.action.follow": "Aby kontynuować, musisz obserwować ze swojego konta.", + "interaction_modal.no_account_yet": "Nie masz jeszcze konta?", "interaction_modal.on_another_server": "Na innym serwerze", "interaction_modal.on_this_server": "Na tym serwerze", "interaction_modal.title.favourite": "Polub wpis użytkownika {name}", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 1505d333ba..142ae33c58 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -243,12 +243,12 @@ "dismissable_banner.explore_statuses": "Estas publicações através do fediverse estão ganhando atenção hoje. Publicações mais recentes com mais boosts e favoritos são classificados mais altamente.", "dismissable_banner.explore_tags": "Estas hashtags estão ganhando atenção hoje no fediverse. Hashtags usadas por muitas pessoas diferentes são classificadas mais altamente.", "dismissable_banner.public_timeline": "Estas são as publicações mais recentes das pessoas no fediverse que as pessoas do {domain} seguem.", - "domain_block_modal.block": "Servidor de blocos.", - "domain_block_modal.block_account_instead": "Bloco @(nome)", + "domain_block_modal.block": "Bloquear servidor", + "domain_block_modal.block_account_instead": "Bloquear @{name}", "domain_block_modal.they_can_interact_with_old_posts": "Pessoas deste servidor podem interagir com suas publicações antigas.", "domain_block_modal.they_cant_follow": "Ninguém deste servidor pode lhe seguir.", "domain_block_modal.they_wont_know": "Eles não saberão que foram bloqueados.", - "domain_block_modal.title": "Dominio do bloco", + "domain_block_modal.title": "Bloquear domínio?", "domain_block_modal.you_will_lose_num_followers": "Você perderá {followersCount, plural, one {{followersCountDisplay} seguidor} other {{followersCountDisplay} seguidores}} e {followingCount, plural, one {{followingCountDisplay} pessoa que você segue} other {{followingCountDisplay} pessoas que você segue}}.", "domain_block_modal.you_will_lose_relationships": "Você irá perder todos os seguidores e pessoas que você segue neste servidor.", "domain_block_modal.you_wont_see_posts": "Você não verá postagens ou notificações de usuários neste servidor.", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index b4b3c40dc5..f7329ad97e 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -244,7 +244,7 @@ "dismissable_banner.explore_tags": "Estas etiquetas estão a ganhar força no fediverso atualmente. As etiquetas que são utilizadas por mais pessoas diferentes são classificadas numa posição mais elevada.", "dismissable_banner.public_timeline": "Estas são as publicações públicas mais recentes de pessoas no fediverso que as pessoas em {domain} seguem.", "domain_block_modal.block": "Bloquear servidor", - "domain_block_modal.block_account_instead": "Bloquear antes @{name}", + "domain_block_modal.block_account_instead": "Em vez disso, bloquear @{name}", "domain_block_modal.they_can_interact_with_old_posts": "As pessoas deste servidor podem interagir com as tuas publicações antigas.", "domain_block_modal.they_cant_follow": "Ninguém deste servidor pode seguir-te.", "domain_block_modal.they_wont_know": "Eles não saberão que foram bloqueados.", @@ -260,7 +260,7 @@ "domain_pill.their_username": "O identificador único dele no seu servidor. É possível encontrar utilizadores com o mesmo nome de utilizador em servidores diferentes.", "domain_pill.username": "Nome de utilizador", "domain_pill.whats_in_a_handle": "Em que consiste um identificador?", - "domain_pill.who_they_are": "Uma vez que os identificadores dizem quem é alguém e onde está, pode interagir com as pessoas através da rede social de .", + "domain_pill.who_they_are": "Uma vez que os identificadores dizem quem é alguém e onde está, podes interagir com as pessoas através da rede social de .", "domain_pill.who_you_are": "Uma vez que o teu identificador indica quem és e onde estás, as pessoas podem interagir contigo através da rede social de .", "domain_pill.your_handle": "O teu identificador:", "domain_pill.your_server": "A tua casa digital, onde se encontram todas as tuas publicações. Não gostas deste? Muda de servidor a qualquer momento e leva também os teus seguidores.", diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 09b629c4da..2d256b475e 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -217,7 +217,7 @@ lv: enable_user: Ieslēgt Lietotāju memorialize_account: Saglabāt Kontu Piemiņai promote_user: Izceltt Lietotāju - publish_terms_of_service: Publicēt pakalpojuma izmantošanas nosacījumus + publish_terms_of_service: Publicēt pakalpojuma izmantošanas noteikumus reject_appeal: Noraidīt Apelāciju reject_user: Noraidīt lietotāju remove_avatar_user: Noņemt profila attēlu @@ -274,7 +274,7 @@ lv: enable_user_html: "%{name} iespējoja pieteikšanos lietotājam %{target}" memorialize_account_html: "%{name} pārvērta %{target} kontu par atmiņas lapu" promote_user_html: "%{name} paaugstināja lietotāju %{target}" - publish_terms_of_service_html: "%{name} padarīja pieejamus pakalpojuma izmantošanas nosacījumu atjauninājumus" + publish_terms_of_service_html: "%{name} padarīja pieejamus pakalpojuma izmantošanas noteikumu atjauninājumus" reject_appeal_html: "%{name} noraidīja satura pārraudzības lēmuma iebildumu no %{target}" reject_user_html: "%{name} noraidīja reģistrēšanos no %{target}" remove_avatar_user_html: "%{name} noņēma %{target} profila attēlu" @@ -748,9 +748,9 @@ lv: rules: add_new: Pievienot noteikumu delete: Dzēst - description_html: Kaut arī lielākā daļa apgalvo, ka ir lasījuši un piekrīt pakalpojuma izmantošanas nosacījumiem, parasti cilvēki tos neizlasa, līdz rodas sarežģījumi. Padari vienkāršāku sava servera noteikumu pārskatīšanu, sniedzot tos vienkāršā uzsvēruma punktu sarakstā! Jāmēģina atsevišķus noteikumus veidot īsus un vienkāršus, bet jāmēģina arī tos nesadalīt daudzos atsevišķos vienumos. + description_html: Kaut arī lielākā daļa apgalvo, ka ir lasījuši un piekrīt pakalpojuma izmantošanas noteikumiem, parasti cilvēki tos neizlasa, līdz rodas sarežģījumi. Padari vienkāršāku sava servera noteikumu pārskatīšanu, sniedzot tos vienkāršā uzsvēruma punktu sarakstā! Jāmēģina atsevišķus noteikumus veidot īsus un vienkāršus, bet jāmēģina arī tos nesadalīt daudzos atsevišķos vienumos. edit: Labot nosacījumu - empty: Servera noteikumi vēl nav definēti. + empty: Vēl nav pievienots neviens servera noteikums. title: Servera noteikumi settings: about: @@ -921,7 +921,7 @@ lv: title: Tēmturi updated_msg: Tēmtura iestatījumi ir veiksmīgi atjaunināti terms_of_service: - back: Atpakaļ uz pakalpojuma izmantošanas nosacījumiem + back: Atpakaļ uz pakalpojuma izmantošanas noteikumiem changelog: Kas ir mainījies create: Izmantot savus current: Pašreizējie @@ -929,9 +929,27 @@ lv: generate: Izmantot sagatavi generates: action: Izveidot + chance_to_review_html: "Izveidotie pakalpojuma izmantošanas noteikumi netiks automātiski publicēti. Būs iespēja izskatīt iznākumu. Lūgums norādīt nepieciešamo informāciju, lai turpinātu." + explanation_html: Pakalpojuma izmantošanas noteikumu sagatave tiek piedāvāta tikai izzināšanas nolūkam, un to nevajadzētu izmantot kā juridisku padomu jebkurā jautājumā. Lūgums sazināties ar savu juridisko padomdevēju par saviem apstākļiem un noteiktiem juridiskiem jautājumiem. + title: Pakalpojuma izmantošānas noteikumu uzstādīšana history: Vēsture + live: Darbībā + no_history: Nav ierakstu par pakalpojuma izmantošanas noteikumu izmaiņām. + no_terms_of_service_html: Pašlaik nav uzstādīti pakalpojuma izmantošanas noteikumi. Tie ir paredzēti, lai sniegtu skaidrību un aizsargātu no iespējamas atbildības strīdos ar lietotājiem. + notified_on_html: Lietotājiem paziņots %{date} + notify_users: Paziņot lietotājiem + preview: + explanation_html: 'E-pasta ziņojums tiks nosūtīts %{display_count} lietotājiem, kuri ir reģistrējušies pirms %{date}. Šis teksts tiks iekļauts e-pasta ziņojumā:' + send_preview: Nosūtīt priekšskatījumu uz %{email} + send_to_all: + one: Nosūtīt %{display_count} e-pasta ziņojumu + other: Nosūtīt %{display_count} e-pasta ziņojumus + zero: Nosūtīt %{display_count} e-pasta ziņojumu + title: Priekškatīt pakalpojuma izmantošanas noteikumu paziņojumu publish: Publicēt - published_on_html: Publicēts %{date} + published_on_html: Publicēti %{date} + save_draft: Saglabāt melnrakstu + title: Pakalpojuma izmantošanas noteikumi title: Pārvaldība trends: allow: Atļaut @@ -1172,6 +1190,7 @@ lv: view_strikes: Skati iepriekšējos brīdinājumus par savu kontu too_fast: Veidlapa ir iesniegta pārāk ātri, mēģini vēlreiz. use_security_key: Lietot drošības atslēgu + user_agreement_html: Es esmu izlasījis un piekrītu pakalpojuma izmantošanas noteikumiem un privātuma nosacījumiem author_attribution: example_title: Parauga teksts more_from_html: Vairāk no %{name} @@ -1792,6 +1811,8 @@ lv: too_late: Brīdinājuma apstrīdēšanas laiks ir nokavēts tags: does_not_match_previous_name: nesakrīt ar iepriekšējo nosaukumu + terms_of_service: + title: Pakalpojuma izmantošanas noteikumi themes: contrast: Mastodon (Augsts kontrasts) default: Mastodon (Tumšs) @@ -1842,9 +1863,13 @@ lv: subject: Tavam kontam ir piekļūts no jaunas IP adreses title: Jauna pieteikšanās terms_of_service_changed: + agreement: Ar %{domain} izmantošanas tuprināšanu tiek piekrists šiem noteikumiem. Ja ir iebildumi pret atjauninātajiem noteikumiem, savu piekrišanu var atcelt jebkurā laikā ar sava konta izdzēšanu. + changelog: 'Šeit īsumā ir aprakstīts, ko šis atjauninājums nozīmē:' + description: 'Šis e-pasta ziņojums tika saņemts, jo mēs veicam dažas izmaiņas savos pakalpojuma izmantošanas noteikumos %{domain}. Mēs aicinām pārskatīt pilnus atjauninātos noteikumus šeit:' + description_html: Šis e-pasta ziņojums tika saņemts, jo mēs veicam dažas izmaiņas savos pakalpojuma izmantošanas noteikumos %{domain}. Mēs aicinām pārskatīt pilnus atjauninātos noteikumus šeit. sign_off: "%{domain} komanda" - subject: Mūsu pakalpojuma izmantošanas nosacījumu atjauninājumi - subtitle: Mainās %{domain} pakalpojuma izmantošanas nosacījumi + subject: Mūsu pakalpojuma izmantošanas noteikumu atjauninājumi + subtitle: Mainās %{domain} pakalpojuma izmantošanas noteikumi title: Svarīgs atjauninājums warning: appeal: Iesniegt apelāciju diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml index 53224966f1..7c7b11c1b3 100644 --- a/config/locales/simple_form.lv.yml +++ b/config/locales/simple_form.lv.yml @@ -53,7 +53,7 @@ lv: locale: Lietotāja saskarnes, e-pasta ziņojumu un push paziņojumu valoda password: Izmanto vismaz 8 rakstzīmes phrase: Tiks saskaņots neatkarīgi no ziņas teksta reģistra vai satura brīdinājuma - scopes: Kuriem API lietojumprogrammai būs atļauta piekļuve. Ja izvēlies augstākā līmeņa tvērumu, tev nav jāatlasa atsevišķi vienumi. + scopes: Kuriem API lietotnei būs ļauts piekļūt. Ja atlasa augstākā līmeņa tvērumu, nav nepieciešamas atlasīt atsevišķus. setting_aggregate_reblogs: Nerādīt jaunus izcēlumus ziņām, kas nesen tika palielinātas (ietekmē tikai nesen saņemtos palielinājumus) setting_always_send_emails: Parasti e-pasta paziņojumi netiek sūtīti, kad aktīvi izmantojat Mastodon setting_default_sensitive: Sensitīva multivide pēc noklusējuma ir paslēpti, un tos var atklāt, noklikšķinot diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 421d360fac..2d2c1c0060 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -159,7 +159,7 @@ sv: name: Etikett value: Innehåll indexable: Inkludera offentliga inlägg i sökresultaten - show_collections: Göm följare och följeslagare på profilen + show_collections: Visa följare och följeslagare på profilen unlocked: Godkänn nya följare automatiskt account_alias: acct: Namnet på det gamla kontot From 4fb3dc0363956a1a61613e90e9a4e7e945a7f714 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 04:02:48 -0500 Subject: [PATCH 078/133] Extract `CSS_COLORS` constant for `UserRole` regex validation (#33532) --- app/models/user_role.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/user_role.rb b/app/models/user_role.rb index 24cd5983f3..d567bf5eca 100644 --- a/app/models/user_role.rb +++ b/app/models/user_role.rb @@ -42,6 +42,7 @@ class UserRole < ApplicationRecord NOBODY_POSITION = -1 POSITION_LIMIT = (2**31) - 1 + CSS_COLORS = /\A#?(?:[A-F0-9]{3}){1,2}\z/i # CSS-style hex colors module Flags NONE = 0 @@ -90,7 +91,7 @@ class UserRole < ApplicationRecord attr_writer :current_account validates :name, presence: true, unless: :everyone? - validates :color, format: { with: /\A#?(?:[A-F0-9]{3}){1,2}\z/i }, unless: -> { color.blank? } + validates :color, format: { with: CSS_COLORS }, if: :color? validates :position, numericality: { in: (-POSITION_LIMIT..POSITION_LIMIT) } validate :validate_permissions_elevation From 2499cd01db57275d2adc43aba97302fca5cc754f Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 04:04:23 -0500 Subject: [PATCH 079/133] Add `duplicate_record` helper to maintenance CLI spec (#33536) --- spec/lib/mastodon/cli/maintenance_spec.rb | 59 +++++++++-------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/spec/lib/mastodon/cli/maintenance_spec.rb b/spec/lib/mastodon/cli/maintenance_spec.rb index 6a15677f43..3e8eb9c360 100644 --- a/spec/lib/mastodon/cli/maintenance_spec.rb +++ b/spec/lib/mastodon/cli/maintenance_spec.rb @@ -89,10 +89,8 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :accounts, name: :index_accounts_on_username_and_domain_lower - _remote_account = Fabricate(:account, username: duplicate_account_username, domain: duplicate_account_domain) - _remote_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: duplicate_account_domain).save(validate: false) - _local_account = Fabricate(:account, username: duplicate_account_username, domain: nil) - _local_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: nil).save(validate: false) + duplicate_record(:account, username: duplicate_account_username, domain: duplicate_account_domain) + duplicate_record(:account, username: duplicate_account_username, domain: nil) end def choose_local_account_to_keep @@ -127,8 +125,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :users, :email - Fabricate(:user, email: duplicate_email) - Fabricate.build(:user, email: duplicate_email).save(validate: false) + duplicate_record(:user, email: duplicate_email) end end @@ -156,8 +153,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :users, :confirmation_token - Fabricate(:user, confirmation_token: duplicate_confirmation_token) - Fabricate.build(:user, confirmation_token: duplicate_confirmation_token).save(validate: false) + duplicate_record(:user, confirmation_token: duplicate_confirmation_token) end end @@ -185,8 +181,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :users, :reset_password_token - Fabricate(:user, reset_password_token: duplicate_reset_password_token) - Fabricate.build(:user, reset_password_token: duplicate_reset_password_token).save(validate: false) + duplicate_record(:user, reset_password_token: duplicate_reset_password_token) end end @@ -214,8 +209,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :account_domain_blocks, [:account_id, :domain] - Fabricate(:account_domain_block, account: account, domain: duplicate_domain) - Fabricate.build(:account_domain_block, account: account, domain: duplicate_domain).save(validate: false) + duplicate_record(:account_domain_block, account: account, domain: duplicate_domain) end end @@ -244,8 +238,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :announcement_reactions, [:account_id, :announcement_id, :name] - Fabricate(:announcement_reaction, account: account, announcement: announcement, name: name) - Fabricate.build(:announcement_reaction, account: account, announcement: announcement, name: name).save(validate: false) + duplicate_record(:announcement_reaction, account: account, announcement: announcement, name: name) end end @@ -272,8 +265,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :conversations, :uri - Fabricate(:conversation, uri: uri) - Fabricate.build(:conversation, uri: uri).save(validate: false) + duplicate_record(:conversation, uri: uri) end end @@ -301,8 +293,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :custom_emojis, [:shortcode, :domain] - Fabricate(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain) - Fabricate.build(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain).save(validate: false) + duplicate_record(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain) end end @@ -329,8 +320,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :custom_emoji_categories, :name - Fabricate(:custom_emoji_category, name: duplicate_name) - Fabricate.build(:custom_emoji_category, name: duplicate_name).save(validate: false) + duplicate_record(:custom_emoji_category, name: duplicate_name) end end @@ -357,8 +347,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :domain_allows, :domain - Fabricate(:domain_allow, domain: domain) - Fabricate.build(:domain_allow, domain: domain).save(validate: false) + duplicate_record(:domain_allow, domain: domain) end end @@ -385,8 +374,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :domain_blocks, :domain - Fabricate(:domain_block, domain: domain) - Fabricate.build(:domain_block, domain: domain).save(validate: false) + duplicate_record(:domain_block, domain: domain) end end @@ -413,8 +401,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :email_domain_blocks, :domain - Fabricate(:email_domain_block, domain: domain) - Fabricate.build(:email_domain_block, domain: domain).save(validate: false) + duplicate_record(:email_domain_block, domain: domain) end end @@ -441,8 +428,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :media_attachments, :shortcode - Fabricate(:media_attachment, shortcode: shortcode) - Fabricate.build(:media_attachment, shortcode: shortcode).save(validate: false) + duplicate_record(:media_attachment, shortcode: shortcode) end end @@ -469,8 +455,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :preview_cards, :url - Fabricate(:preview_card, url: url) - Fabricate.build(:preview_card, url: url).save(validate: false) + duplicate_record(:preview_card, url: url) end end @@ -530,8 +515,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :tags, name: 'index_tags_on_name_lower_btree' - Fabricate(:tag, name: name) - Fabricate.build(:tag, name: name).save(validate: false) + duplicate_record(:tag, name: name) end end @@ -558,8 +542,7 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :webauthn_credentials, :external_id - Fabricate(:webauthn_credential, external_id: external_id) - Fabricate.build(:webauthn_credential, external_id: external_id).save(validate: false) + duplicate_record(:webauthn_credential, external_id: external_id) end end @@ -586,11 +569,15 @@ RSpec.describe Mastodon::CLI::Maintenance do def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :webhooks, :url - Fabricate(:webhook, url: url) - Fabricate.build(:webhook, url: url).save(validate: false) + duplicate_record(:webhook, url: url) end end + def duplicate_record(fabricator, options = {}) + Fabricate(fabricator, options) + Fabricate.build(fabricator, options).save(validate: false) + end + def agree_to_backup_warning allow(cli.shell) .to receive(:yes?) From 8d4ca95163588a32412507c3142db5c7f3fb934e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 04:10:21 -0500 Subject: [PATCH 080/133] Convert `admin/follow_recommendations` spec controller->system (#33533) --- .../follow_recommendations_controller_spec.rb | 21 ------------------- .../admin/follow_recommendations_spec.rb | 18 ++++++++++++++++ 2 files changed, 18 insertions(+), 21 deletions(-) delete mode 100644 spec/controllers/admin/follow_recommendations_controller_spec.rb create mode 100644 spec/system/admin/follow_recommendations_spec.rb diff --git a/spec/controllers/admin/follow_recommendations_controller_spec.rb b/spec/controllers/admin/follow_recommendations_controller_spec.rb deleted file mode 100644 index 82446cd467..0000000000 --- a/spec/controllers/admin/follow_recommendations_controller_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Admin::FollowRecommendationsController do - render_views - - let(:user) { Fabricate(:admin_user) } - - before do - sign_in user, scope: :user - end - - describe 'GET #show' do - it 'returns http success' do - get :show - - expect(response).to have_http_status(:success) - end - end -end diff --git a/spec/system/admin/follow_recommendations_spec.rb b/spec/system/admin/follow_recommendations_spec.rb new file mode 100644 index 0000000000..141a0f8152 --- /dev/null +++ b/spec/system/admin/follow_recommendations_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Admin Follow Recommendations' do + let(:user) { Fabricate(:admin_user) } + + before { sign_in(user) } + + describe 'Viewing follow recommendations details' do + it 'shows a list of accounts' do + visit admin_follow_recommendations_path + + expect(page) + .to have_content(I18n.t('admin.follow_recommendations.title')) + end + end +end From d155763014b0a78792f0b4b38b5a9783172056a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:10:58 +0000 Subject: [PATCH 081/133] Update dependency react-textarea-autosize to v8.5.7 (#33542) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e708282d87..1231647d7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14942,15 +14942,15 @@ __metadata: linkType: hard "react-textarea-autosize@npm:^8.4.1": - version: 8.5.6 - resolution: "react-textarea-autosize@npm:8.5.6" + version: 8.5.7 + resolution: "react-textarea-autosize@npm:8.5.7" dependencies: "@babel/runtime": "npm:^7.20.13" use-composed-ref: "npm:^1.3.0" use-latest: "npm:^1.2.1" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10c0/652d290d316c55a253507ecf65ca27f2162801dace10c715f2241203e81d82e9de6d282095b758b26c6bc9e1af9ca552cab5c3a361b230e5fcf25bec31e1bd25 + checksum: 10c0/ff004797ea28faca442460c42b30042d4c34a140f324eeeddee74508688dbc0f98966d21282c945630655006ad28a87edbcb59e6da7f9e762f4f3042c72f9f24 languageName: node linkType: hard From 1d680f19411120c9cfdf43f5284c5676eb621185 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:11:17 +0000 Subject: [PATCH 082/133] Update dependency rubocop to v1.70.0 (#33543) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 36373bd337..cb5e2bbbae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -709,7 +709,7 @@ GEM rspec-mocks (~> 3.0) sidekiq (>= 5, < 8) rspec-support (3.13.2) - rubocop (1.69.2) + rubocop (1.70.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) From a8b0152bc548b6ac45f55ad0f0670b774a0117a6 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 04:11:57 -0500 Subject: [PATCH 083/133] Convert `admin/terms_of_service/histories` spec controller->system (#33534) --- .../histories_controller_spec.rb | 21 ------------------- .../admin/terms_of_service/histories_spec.rb | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 21 deletions(-) delete mode 100644 spec/controllers/admin/terms_of_service/histories_controller_spec.rb create mode 100644 spec/system/admin/terms_of_service/histories_spec.rb diff --git a/spec/controllers/admin/terms_of_service/histories_controller_spec.rb b/spec/controllers/admin/terms_of_service/histories_controller_spec.rb deleted file mode 100644 index 8c2c3a3de3..0000000000 --- a/spec/controllers/admin/terms_of_service/histories_controller_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Admin::TermsOfService::HistoriesController do - render_views - - let(:user) { Fabricate(:admin_user) } - - before do - sign_in user, scope: :user - end - - describe 'GET #show' do - it 'returns http success' do - get :show - - expect(response).to have_http_status(:success) - end - end -end diff --git a/spec/system/admin/terms_of_service/histories_spec.rb b/spec/system/admin/terms_of_service/histories_spec.rb new file mode 100644 index 0000000000..aa59550d09 --- /dev/null +++ b/spec/system/admin/terms_of_service/histories_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Admin Terms of Service Histories' do + let(:current_user) { Fabricate(:admin_user) } + + before { sign_in(current_user) } + + describe 'Viewing TOS histories' do + before { Fabricate :terms_of_service, changelog: 'The changelog notes from v1 are here' } + + it 'shows previous terms versions' do + visit admin_terms_of_service_history_path + + expect(page) + .to have_content(I18n.t('admin.terms_of_service.history')) + .and have_content(/changelog notes from v1/) + end + end +end From e0f6292492c5fce0c9e48b2c003c733f3d2346d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:12:45 +0000 Subject: [PATCH 084/133] Update dependency uuid to v11.0.5 (#33538) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1231647d7c..0e40cc7221 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17843,11 +17843,11 @@ __metadata: linkType: hard "uuid@npm:^11.0.0": - version: 11.0.4 - resolution: "uuid@npm:11.0.4" + version: 11.0.5 + resolution: "uuid@npm:11.0.5" bin: uuid: dist/esm/bin/uuid - checksum: 10c0/3c13591c4dedaa3741f925e284df5974e3d6e0b1cb0f6f75f98c36f9c01d2a414350364fd067613ef600a21c6973dab0506530d4f499ff878f32a06f84569ead + checksum: 10c0/6f59f0c605e02c14515401084ca124b9cb462b4dcac866916a49862bcf831874508a308588c23a7718269226ad11a92da29b39d761ad2b86e736623e3a33b6e7 languageName: node linkType: hard From 4f6edc75963f4ea4e65a3512f0626b37e83b7dd7 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 04:33:07 -0500 Subject: [PATCH 085/133] Use `in_order_of` in `trends/*` classes (#33531) --- app/models/trends/links.rb | 4 ++-- app/models/trends/query.rb | 7 +++++++ app/models/trends/statuses.rb | 4 ++-- app/models/trends/tags.rb | 5 +++-- spec/models/trends/links_spec.rb | 30 +++++++++++++++++++++++++++++ spec/models/trends/statuses_spec.rb | 25 ++++++++++++++++++++++++ spec/models/trends/tags_spec.rb | 25 ++++++++++++++++++++++++ 7 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 spec/models/trends/links_spec.rb diff --git a/app/models/trends/links.rb b/app/models/trends/links.rb index 0f3ead43f8..57ad486631 100644 --- a/app/models/trends/links.rb +++ b/app/models/trends/links.rb @@ -16,7 +16,7 @@ class Trends::Links < Trends::Base class Query < Trends::Query def to_arel scope = PreviewCard.joins(:trend).reorder(score: :desc) - scope = scope.reorder(language_order_clause.desc, score: :desc) if preferred_languages.present? + scope = scope.merge(language_order_clause) if preferred_languages.present? scope = scope.merge(PreviewCardTrend.allowed) if @allowed scope = scope.offset(@offset) if @offset.present? scope = scope.limit(@limit) if @limit.present? @@ -26,7 +26,7 @@ class Trends::Links < Trends::Base private def language_order_clause - Arel::Nodes::Case.new.when(PreviewCardTrend.arel_table[:language].in(preferred_languages)).then(1).else(0) + language_order_for(PreviewCardTrend) end end diff --git a/app/models/trends/query.rb b/app/models/trends/query.rb index 590e81f4fd..abed64042e 100644 --- a/app/models/trends/query.rb +++ b/app/models/trends/query.rb @@ -94,6 +94,13 @@ class Trends::Query to_arel.to_a end + def language_order_for(trend_class) + trend_class + .reorder(nil) + .in_order_of(:language, [preferred_languages], filter: false) + .order(score: :desc) + end + def preferred_languages if @account&.chosen_languages.present? @account.chosen_languages diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb index 1d2f02809b..9c47dd486b 100644 --- a/app/models/trends/statuses.rb +++ b/app/models/trends/statuses.rb @@ -15,7 +15,7 @@ class Trends::Statuses < Trends::Base class Query < Trends::Query def to_arel scope = Status.joins(:trend).reorder(score: :desc) - scope = scope.reorder(language_order_clause.desc, score: :desc) if preferred_languages.present? + scope = scope.merge(language_order_clause) if preferred_languages.present? scope = scope.merge(StatusTrend.allowed) if @allowed scope = scope.not_excluded_by_account(@account).not_domain_blocked_by_account(@account) if @account.present? scope = scope.offset(@offset) if @offset.present? @@ -26,7 +26,7 @@ class Trends::Statuses < Trends::Base private def language_order_clause - Arel::Nodes::Case.new.when(StatusTrend.arel_table[:language].in(preferred_languages)).then(1).else(0) + language_order_for(StatusTrend) end end diff --git a/app/models/trends/tags.rb b/app/models/trends/tags.rb index 18f2a9a949..84e8dde11a 100644 --- a/app/models/trends/tags.rb +++ b/app/models/trends/tags.rb @@ -15,7 +15,8 @@ class Trends::Tags < Trends::Base class Query < Trends::Query def to_arel - scope = Tag.joins(:trend).reorder(language_order_clause.desc, score: :desc) + scope = Tag.joins(:trend).reorder(score: :desc) + scope = scope.merge(language_order_clause) if preferred_languages.present? scope = scope.merge(TagTrend.allowed) if @allowed scope = scope.offset(@offset) if @offset.present? scope = scope.limit(@limit) if @limit.present? @@ -25,7 +26,7 @@ class Trends::Tags < Trends::Base private def language_order_clause - Arel::Nodes::Case.new.when(TagTrend.arel_table[:language].in(preferred_languages)).then(1).else(0) + language_order_for(TagTrend) end end diff --git a/spec/models/trends/links_spec.rb b/spec/models/trends/links_spec.rb new file mode 100644 index 0000000000..b0d41d4613 --- /dev/null +++ b/spec/models/trends/links_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Trends::Links do + describe 'Trends::Links::Query' do + subject { described_class.new.query } + + describe '#records' do + context 'with scored cards' do + let!(:higher_score) { Fabricate :preview_card_trend, score: 10, language: 'en' } + let!(:lower_score) { Fabricate :preview_card_trend, score: 1, language: 'es' } + + it 'returns higher score first' do + expect(subject.records) + .to eq([higher_score.preview_card, lower_score.preview_card]) + end + + context 'with preferred locale' do + before { subject.in_locale!('es') } + + it 'returns in language order' do + expect(subject.records) + .to eq([lower_score.preview_card, higher_score.preview_card]) + end + end + end + end + end +end diff --git a/spec/models/trends/statuses_spec.rb b/spec/models/trends/statuses_spec.rb index 7c30b5b997..abb1535d04 100644 --- a/spec/models/trends/statuses_spec.rb +++ b/spec/models/trends/statuses_spec.rb @@ -45,6 +45,31 @@ RSpec.describe Trends::Statuses do end end + describe 'Trends::Statuses::Query methods' do + subject { described_class.new.query } + + describe '#records' do + context 'with scored cards' do + let!(:higher_score) { Fabricate :status_trend, score: 10, language: 'en' } + let!(:lower_score) { Fabricate :status_trend, score: 1, language: 'es' } + + it 'returns higher score first' do + expect(subject.records) + .to eq([higher_score.status, lower_score.status]) + end + + context 'with preferred locale' do + before { subject.in_locale!('es') } + + it 'returns in language order' do + expect(subject.records) + .to eq([lower_score.status, higher_score.status]) + end + end + end + end + end + describe '#add' do let(:status) { Fabricate(:status) } diff --git a/spec/models/trends/tags_spec.rb b/spec/models/trends/tags_spec.rb index 936b441d92..8f36b4a50d 100644 --- a/spec/models/trends/tags_spec.rb +++ b/spec/models/trends/tags_spec.rb @@ -29,6 +29,31 @@ RSpec.describe Trends::Tags do end end + describe 'Trends::Tags::Query' do + subject { described_class.new.query } + + describe '#records' do + context 'with scored cards' do + let!(:higher_score) { Fabricate :tag_trend, score: 10, language: 'en' } + let!(:lower_score) { Fabricate :tag_trend, score: 1, language: 'es' } + + it 'returns higher score first' do + expect(subject.records) + .to eq([higher_score.tag, lower_score.tag]) + end + + context 'with preferred locale' do + before { subject.in_locale!('es') } + + it 'returns in language order' do + expect(subject.records) + .to eq([lower_score.tag, higher_score.tag]) + end + end + end + end + end + describe '#refresh' do let!(:today) { at_time } let!(:yesterday) { today - 1.day } From 2cfc2a777a01b26a9442fbf9a912790097990a0b Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 06:59:19 -0500 Subject: [PATCH 086/133] Add `build_object` method for defaults in `AP::Activity::Create` spec (#33537) --- spec/lib/activitypub/activity/create_spec.rb | 375 +++++++------------ 1 file changed, 129 insertions(+), 246 deletions(-) diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index d70456e458..5273b9be15 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -162,12 +162,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when object publication date is below ISO8601 range' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - published: '-0977-11-03T08:31:22Z', - } + build_object( + published: '-0977-11-03T08:31:22Z' + ) end it 'creates status with a valid creation date', :aggregate_failures do @@ -184,12 +181,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when object publication date is above ISO8601 range' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - published: '10000-11-03T08:31:22Z', - } + build_object( + published: '10000-11-03T08:31:22Z' + ) end it 'creates status with a valid creation date', :aggregate_failures do @@ -206,13 +200,10 @@ RSpec.describe ActivityPub::Activity::Create do context 'when object has been edited' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( published: '2022-01-22T15:00:00Z', - updated: '2022-01-22T16:00:00Z', - } + updated: '2022-01-22T16:00:00Z' + ) end it 'creates status with appropriate creation and edition dates', :aggregate_failures do @@ -232,13 +223,10 @@ RSpec.describe ActivityPub::Activity::Create do context 'when object has update date equal to creation date' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( published: '2022-01-22T15:00:00Z', - updated: '2022-01-22T15:00:00Z', - } + updated: '2022-01-22T15:00:00Z' + ) end it 'creates status and does not mark it as edited' do @@ -254,11 +242,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'with an unknown object type' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Banana', - content: 'Lorem ipsum', - } + build_object( + type: 'Banana' + ) end it 'does not create a status' do @@ -267,13 +253,7 @@ RSpec.describe ActivityPub::Activity::Create do end context 'with a standalone' do - let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - } - end + let(:object_json) { build_object } it 'creates status' do expect { subject.perform }.to change(sender.statuses, :count).by(1) @@ -296,12 +276,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when public with explicit public address' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - to: 'https://www.w3.org/ns/activitystreams#Public', - } + build_object( + to: 'https://www.w3.org/ns/activitystreams#Public' + ) end it 'creates status' do @@ -316,12 +293,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when public with as:Public' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - to: 'as:Public', - } + build_object( + to: 'as:Public' + ) end it 'creates status' do @@ -336,12 +310,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when public with Public' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - to: 'Public', - } + build_object( + to: 'Public' + ) end it 'creates status' do @@ -356,12 +327,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when unlisted with explicit public address' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - cc: 'https://www.w3.org/ns/activitystreams#Public', - } + build_object( + cc: 'https://www.w3.org/ns/activitystreams#Public' + ) end it 'creates status' do @@ -376,12 +344,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when unlisted with as:Public' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - cc: 'as:Public', - } + build_object( + cc: 'as:Public' + ) end it 'creates status' do @@ -396,12 +361,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when unlisted with Public' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - cc: 'Public', - } + build_object( + cc: 'Public' + ) end it 'creates status' do @@ -416,12 +378,9 @@ RSpec.describe ActivityPub::Activity::Create do context 'when private' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - to: 'http://example.com/followers', - } + build_object( + to: 'http://example.com/followers' + ) end it 'creates status' do @@ -436,16 +395,13 @@ RSpec.describe ActivityPub::Activity::Create do context 'when private with inlined Collection in audience' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( to: { type: 'OrderedCollection', id: 'http://example.com/followers', first: 'http://example.com/followers?page=true', - }, - } + } + ) end it 'creates status' do @@ -462,12 +418,9 @@ RSpec.describe ActivityPub::Activity::Create do let(:recipient) { Fabricate(:account) } let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - to: ActivityPub::TagManager.instance.uri_for(recipient), - } + build_object( + to: ActivityPub::TagManager.instance.uri_for(recipient) + ) end it 'creates status with a silent mention' do @@ -485,16 +438,13 @@ RSpec.describe ActivityPub::Activity::Create do let(:recipient) { Fabricate(:account) } let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( to: ActivityPub::TagManager.instance.uri_for(recipient), tag: { type: 'Mention', href: ActivityPub::TagManager.instance.uri_for(recipient), - }, - } + } + ) end it 'creates status' do @@ -511,12 +461,9 @@ RSpec.describe ActivityPub::Activity::Create do let(:original_status) { Fabricate(:status) } let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status), - } + build_object( + inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status) + ) end it 'creates status' do @@ -536,17 +483,14 @@ RSpec.describe ActivityPub::Activity::Create do let(:recipient) { Fabricate(:account) } let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( tag: [ { type: 'Mention', href: ActivityPub::TagManager.instance.uri_for(recipient), }, - ], - } + ] + ) end it 'creates status' do @@ -561,16 +505,13 @@ RSpec.describe ActivityPub::Activity::Create do context 'with mentions missing href' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( tag: [ { type: 'Mention', }, - ], - } + ] + ) end it 'creates status' do @@ -583,10 +524,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with media attachments' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( attachment: [ { type: 'Document', @@ -598,8 +536,8 @@ RSpec.describe ActivityPub::Activity::Create do mediaType: 'image/png', url: 'http://example.com/emoji.png', }, - ], - } + ] + ) end it 'creates status with correctly-ordered media attachments' do @@ -615,10 +553,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with media attachments with long description' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( attachment: [ { type: 'Document', @@ -626,8 +561,8 @@ RSpec.describe ActivityPub::Activity::Create do url: 'http://example.com/attachment.png', name: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH, }, - ], - } + ] + ) end it 'creates status' do @@ -642,10 +577,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with media attachments with long description as summary' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( attachment: [ { type: 'Document', @@ -653,8 +585,8 @@ RSpec.describe ActivityPub::Activity::Create do url: 'http://example.com/attachment.png', summary: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH, }, - ], - } + ] + ) end it 'creates status' do @@ -669,10 +601,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with media attachments with focal points' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( attachment: [ { type: 'Document', @@ -680,8 +609,8 @@ RSpec.describe ActivityPub::Activity::Create do url: 'http://example.com/attachment.png', focalPoint: [0.5, -0.7], }, - ], - } + ] + ) end it 'creates status' do @@ -696,17 +625,14 @@ RSpec.describe ActivityPub::Activity::Create do context 'with media attachments missing url' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( attachment: [ { type: 'Document', mediaType: 'image/png', }, - ], - } + ] + ) end it 'creates status' do @@ -719,18 +645,15 @@ RSpec.describe ActivityPub::Activity::Create do context 'with hashtags' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( tag: [ { type: 'Hashtag', href: 'http://example.com/blah', name: '#test', }, - ], - } + ] + ) end it 'creates status' do @@ -745,10 +668,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with featured hashtags' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( to: 'https://www.w3.org/ns/activitystreams#Public', tag: [ { @@ -756,8 +676,8 @@ RSpec.describe ActivityPub::Activity::Create do href: 'http://example.com/blah', name: '#test', }, - ], - } + ] + ) end before do @@ -779,17 +699,14 @@ RSpec.describe ActivityPub::Activity::Create do context 'with hashtags missing name' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( tag: [ { type: 'Hashtag', href: 'http://example.com/blah', }, - ], - } + ] + ) end it 'creates status' do @@ -802,18 +719,15 @@ RSpec.describe ActivityPub::Activity::Create do context 'with hashtags invalid name' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( tag: [ { type: 'Hashtag', href: 'http://example.com/blah', name: 'foo, #eh !', }, - ], - } + ] + ) end it 'creates status' do @@ -826,9 +740,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with emojis' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', + build_object( content: 'Lorem ipsum :tinking:', tag: [ { @@ -838,8 +750,8 @@ RSpec.describe ActivityPub::Activity::Create do }, name: 'tinking', }, - ], - } + ] + ) end it 'creates status' do @@ -854,9 +766,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with emojis served with invalid content-type' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', + build_object( content: 'Lorem ipsum :tinkong:', tag: [ { @@ -866,8 +776,8 @@ RSpec.describe ActivityPub::Activity::Create do }, name: 'tinkong', }, - ], - } + ] + ) end it 'creates status' do @@ -882,9 +792,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with emojis missing name' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', + build_object( content: 'Lorem ipsum :tinking:', tag: [ { @@ -893,8 +801,8 @@ RSpec.describe ActivityPub::Activity::Create do url: 'http://example.com/emoji.png', }, }, - ], - } + ] + ) end it 'creates status' do @@ -907,17 +815,15 @@ RSpec.describe ActivityPub::Activity::Create do context 'with emojis missing icon' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', + build_object( content: 'Lorem ipsum :tinking:', tag: [ { type: 'Emoji', name: 'tinking', }, - ], - } + ] + ) end it 'creates status' do @@ -930,8 +836,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with poll' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + build_object( type: 'Question', content: 'Which color was the submarine?', oneOf: [ @@ -949,8 +854,8 @@ RSpec.describe ActivityPub::Activity::Create do totalItems: 3, }, }, - ], - } + ] + ) end it 'creates status with a poll' do @@ -973,12 +878,10 @@ RSpec.describe ActivityPub::Activity::Create do let!(:local_status) { Fabricate(:status, poll: poll) } let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', + build_object( name: 'Yellow', - inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status), - } + inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status) + ).except(:content) end it 'adds a vote to the poll with correct uri' do @@ -1000,12 +903,10 @@ RSpec.describe ActivityPub::Activity::Create do let!(:local_status) { Fabricate(:status, poll: poll) } let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', + build_object( name: 'Yellow', - inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status), - } + inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status) + ).except(:content) end it 'does not add a vote to the poll' do @@ -1017,10 +918,7 @@ RSpec.describe ActivityPub::Activity::Create do context 'with counts' do let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', + build_object( likes: { id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar', '/likes'].join, type: 'Collection', @@ -1030,8 +928,8 @@ RSpec.describe ActivityPub::Activity::Create do id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar', '/shares'].join, type: 'Collection', totalItems: 100, - }, - } + } + ) end it 'uses the counts from the created object' do @@ -1060,12 +958,9 @@ RSpec.describe ActivityPub::Activity::Create do end let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - to: 'https://www.w3.org/ns/activitystreams#Public', - } + build_object( + to: 'https://www.w3.org/ns/activitystreams#Public' + ) end before do @@ -1095,13 +990,7 @@ RSpec.describe ActivityPub::Activity::Create do subject.perform end - let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - } - end + let(:object_json) { build_object } it 'creates status' do status = sender.statuses.first @@ -1116,12 +1005,9 @@ RSpec.describe ActivityPub::Activity::Create do let!(:local_status) { Fabricate(:status) } let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status), - } + build_object( + inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status) + ) end before do @@ -1140,13 +1026,11 @@ RSpec.describe ActivityPub::Activity::Create do subject { described_class.new(json, sender, delivery: true) } let!(:local_account) { Fabricate(:account) } + let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - to: ActivityPub::TagManager.instance.uri_for(local_account), - } + build_object( + to: ActivityPub::TagManager.instance.uri_for(local_account) + ) end before do @@ -1166,12 +1050,9 @@ RSpec.describe ActivityPub::Activity::Create do let!(:local_account) { Fabricate(:account) } let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - cc: ActivityPub::TagManager.instance.uri_for(local_account), - } + build_object( + cc: ActivityPub::TagManager.instance.uri_for(local_account) + ) end before do @@ -1193,17 +1074,19 @@ RSpec.describe ActivityPub::Activity::Create do subject.perform end - let(:object_json) do - { - id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, - type: 'Note', - content: 'Lorem ipsum', - } - end + let(:object_json) { build_object } it 'does not create anything' do expect(sender.statuses.count).to eq 0 end end + + def build_object(options = {}) + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + }.merge(options) + end end end From 34cd7d6585992c03298c175ab5d22ad059b58cdb Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 10:52:43 -0500 Subject: [PATCH 087/133] Use `config_for` for `Mastodon::Version` metadata/prerelease values (#33548) --- config/mastodon.yml | 3 +++ lib/mastodon/version.rb | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/config/mastodon.yml b/config/mastodon.yml index 2c09c59e0a..e20ba0ab05 100644 --- a/config/mastodon.yml +++ b/config/mastodon.yml @@ -2,3 +2,6 @@ shared: self_destruct_value: <%= ENV.fetch('SELF_DESTRUCT', nil) %> software_update_url: <%= ENV.fetch('UPDATE_CHECK_URL', 'https://api.joinmastodon.org/update-check') %> + version: + metadata: <%= ENV.fetch('MASTODON_VERSION_METADATA', nil) %> + prerelease: <%= ENV.fetch('MASTODON_VERSION_PRERELEASE', nil) %> diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index f132c3a548..ddde4a993d 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -21,11 +21,11 @@ module Mastodon end def prerelease - ENV['MASTODON_VERSION_PRERELEASE'].presence || default_prerelease + version_configuration[:prerelease].presence || default_prerelease end def build_metadata - ENV.fetch('MASTODON_VERSION_METADATA', nil) + version_configuration[:metadata] end def to_a @@ -77,5 +77,9 @@ module Mastodon def user_agent @user_agent ||= "Mastodon/#{Version} (#{HTTP::Request::USER_AGENT}; +http#{Rails.configuration.x.use_https ? 's' : ''}://#{Rails.configuration.x.web_domain}/)" end + + def version_configuration + Rails.configuration.x.mastodon.version + end end end From 19b9884bb2db8bfa48e3b49398a7e94ca0ab6667 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 7 Jan 2025 12:29:38 +0100 Subject: [PATCH 088/133] =?UTF-8?q?[Glitch]=20Fix=20preview=20card=20sizin?= =?UTF-8?q?g=20in=20=E2=80=9CAuthor=20attribution=E2=80=9D=20in=20profile?= =?UTF-8?q?=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port 82e046ea0661e23681304d0e3b95d59736256d21 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/forms.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss index c20eb871f7..0b5734f545 100644 --- a/app/javascript/flavours/glitch/styles/forms.scss +++ b/app/javascript/flavours/glitch/styles/forms.scss @@ -659,6 +659,10 @@ code { } } } + + .status-card { + contain: unset; + } } .block-icon { From fb48fc4cceffed33bc14467ea916de984e4e70f2 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Wed, 8 Jan 2025 15:26:08 +0100 Subject: [PATCH 089/133] [Glitch] Include time portion in formatted datetimes when provided Port JS part of 15669fcf75e79b4f6a2fe0c09ee8ae56d1a25270 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/entrypoints/public.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/entrypoints/public.tsx b/app/javascript/flavours/glitch/entrypoints/public.tsx index 2454957441..e22e2cc85f 100644 --- a/app/javascript/flavours/glitch/entrypoints/public.tsx +++ b/app/javascript/flavours/glitch/entrypoints/public.tsx @@ -119,7 +119,11 @@ function loaded() { formattedContent = dateFormat.format(datetime); } - content.title = formattedContent; + const timeGiven = content.dateTime.includes('T'); + content.title = timeGiven + ? dateTimeFormat.format(datetime) + : dateFormat.format(datetime); + content.textContent = formattedContent; }); From 00f410a0890ffa8dd88ded75d49e2768982545bb Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 8 Jan 2025 17:25:41 +0100 Subject: [PATCH 090/133] [Glitch] Fix use of deprecated `Iterable.isIndexed` from immutable Port 1bf61957632bff5c0a3e8d0af60eeefb41a4cc18 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/actions/store.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/store.js b/app/javascript/flavours/glitch/actions/store.js index 4a33d7ef87..b2d19575c4 100644 --- a/app/javascript/flavours/glitch/actions/store.js +++ b/app/javascript/flavours/glitch/actions/store.js @@ -1,4 +1,4 @@ -import { Iterable, fromJS } from 'immutable'; +import { fromJS, isIndexed } from 'immutable'; import { hydrateCompose } from './compose'; import { importFetchedAccounts } from './importer'; @@ -10,7 +10,7 @@ export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY'; const convertState = rawState => fromJS(rawState, (k, v) => - Iterable.isIndexed(v) ? v.toList() : v.toMap()); + isIndexed(v) ? v.toList() : v.toMap()); const applyMigrations = (state) => { return state.withMutations(state => { From 0270bd2ee541372e261257e95bcb1f22c47f380f Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 8 Jan 2025 17:26:54 +0100 Subject: [PATCH 091/133] [Glitch] Require specific subtype of `formatMessage` in `timeAgoString` Port 6b1ea8dd2c998a5b6fe5f37454e4c04df4863772 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/components/relative_timestamp.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/components/relative_timestamp.tsx b/app/javascript/flavours/glitch/components/relative_timestamp.tsx index ac385e88c6..6253525091 100644 --- a/app/javascript/flavours/glitch/components/relative_timestamp.tsx +++ b/app/javascript/flavours/glitch/components/relative_timestamp.tsx @@ -1,6 +1,6 @@ import { Component } from 'react'; -import type { IntlShape } from 'react-intl'; +import type { MessageDescriptor, PrimitiveType, IntlShape } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl'; const messages = defineMessages({ @@ -102,7 +102,13 @@ const getUnitDelay = (units: string) => { }; export const timeAgoString = ( - intl: Pick, + intl: { + formatDate: IntlShape['formatDate']; + formatMessage: ( + { id, defaultMessage }: MessageDescriptor, + values?: Record, + ) => string; + }, date: Date, now: number, year: number, From 22c1b6f3eec14062c6e0950fdb2d436c34430543 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 10 Jan 2025 15:34:18 -0500 Subject: [PATCH 092/133] Fix `Invite#code` changing value on every save (#33550) --- app/models/invite.rb | 2 +- spec/models/invite_spec.rb | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/models/invite.rb b/app/models/invite.rb index d1981f16ad..9437ebee60 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -31,7 +31,7 @@ class Invite < ApplicationRecord validates :comment, length: { maximum: COMMENT_SIZE_LIMIT } - before_validation :set_code + before_validation :set_code, on: :create def valid_for_use? (max_uses.nil? || uses < max_uses) && !expired? && user&.functional? diff --git a/spec/models/invite_spec.rb b/spec/models/invite_spec.rb index e85885a8d8..6363f77a64 100644 --- a/spec/models/invite_spec.rb +++ b/spec/models/invite_spec.rb @@ -5,6 +5,29 @@ require 'rails_helper' RSpec.describe Invite do include_examples 'Expireable' + describe 'Associations' do + it { is_expected.to belong_to(:user).inverse_of(:invites) } + it { is_expected.to have_many(:users).inverse_of(:invite) } + end + + describe 'Validations' do + it { is_expected.to validate_length_of(:comment).is_at_most(described_class::COMMENT_SIZE_LIMIT) } + end + + describe 'Scopes' do + describe '.available' do + let!(:no_expires) { Fabricate :invite, expires_at: nil } + let!(:past_expires) { Fabricate :invite, expires_at: 2.days.ago } + let!(:future_expires) { Fabricate :invite, expires_at: 2.days.from_now } + + it 'returns future and non-epiring records' do + expect(described_class.available) + .to include(no_expires, future_expires) + .and not_include(past_expires) + end + end + end + describe '#valid_for_use?' do it 'returns true when there are no limitations' do invite = Fabricate(:invite, max_uses: nil, expires_at: nil) @@ -37,4 +60,26 @@ RSpec.describe Invite do expect(invite.valid_for_use?).to be false end end + + describe 'Callbacks' do + describe 'Setting the invite code' do + context 'when creating a new record' do + subject { Fabricate.build :invite } + + it 'sets a code value' do + expect { subject.save } + .to change(subject, :code).from(be_blank).to(be_present) + end + end + + context 'when updating a record' do + subject { Fabricate :invite } + + it 'does not change the code value' do + expect { subject.update(max_uses: 123_456) } + .to not_change(subject, :code) + end + end + end + end end From f3f6b65db4ca4d1f14b610dd7f6036f8516a6fa8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:47:43 +0100 Subject: [PATCH 093/133] Update dependency @babel/plugin-transform-nullish-coalescing-operator to v7.26.5 (#33553) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0e40cc7221..db96b75974 100644 --- a/yarn.lock +++ b/yarn.lock @@ -238,10 +238,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.25.0, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.8.0": - version: 7.25.9 - resolution: "@babel/helper-plugin-utils@npm:7.25.9" - checksum: 10c0/483066a1ba36ff16c0116cd24f93de05de746a603a777cd695ac7a1b034928a65a4ecb35f255761ca56626435d7abdb73219eba196f9aa83b6c3c3169325599d +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.25.0, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.26.5, @babel/helper-plugin-utils@npm:^7.8.0": + version: 7.26.5 + resolution: "@babel/helper-plugin-utils@npm:7.26.5" + checksum: 10c0/cdaba71d4b891aa6a8dfbe5bac2f94effb13e5fa4c2c487667fdbaa04eae059b78b28d85a885071f45f7205aeb56d16759e1bed9c118b94b16e4720ef1ab0f65 languageName: node linkType: hard @@ -935,13 +935,13 @@ __metadata: linkType: hard "@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.25.9" + version: 7.26.5 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.26.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.25.9" + "@babel/helper-plugin-utils": "npm:^7.26.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/eb623db5be078a1c974afe7c7797b0309ba2ea9e9237c0b6831ade0f56d8248bb4ab3432ab34495ff8c877ec2fe412ff779d1e9b3c2b8139da18e1753d950bc3 + checksum: 10c0/2e4b84745f9e8c40caf3e611641de4d6c7da6f96c2925b7fe568e3b031ed1864e325b9dffc9cda4e442fc40be43ffabb088782e980d411e0562bd5222df547ec languageName: node linkType: hard From 44d9dc4bb0cf64fb1561adc6c4fab60316a6a43b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:51:46 +0100 Subject: [PATCH 094/133] Update dependency pino-http to v10.4.0 (#33560) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index db96b75974..0b3b8b3d95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13306,14 +13306,14 @@ __metadata: linkType: hard "pino-http@npm:^10.0.0": - version: 10.3.0 - resolution: "pino-http@npm:10.3.0" + version: 10.4.0 + resolution: "pino-http@npm:10.4.0" dependencies: get-caller-file: "npm:^2.0.5" pino: "npm:^9.0.0" pino-std-serializers: "npm:^7.0.0" process-warning: "npm:^4.0.0" - checksum: 10c0/da95d93e1176c02201f9b9bb0af53ad737105c5772acbb077dcad0f52ebce2438e0e9fc8216cd96396d1305d0ecf1f1d23142c7a50110a701ea093b2ee999ea7 + checksum: 10c0/64144e2c94e939070f56ad82dfb012b6a98d21582e0660cf821e7cee64d4e06f7724aa40bc5bf9cd1254d58ab7cbd972dec287b7989eba647d384f6edd8d95fd languageName: node linkType: hard From 99637f2deb517bc21506af18491e49a8af5e5fcb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 08:53:56 +0000 Subject: [PATCH 095/133] Update dependency ox to v2.14.20 (#33567) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index cb5e2bbbae..4603530de0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -555,7 +555,7 @@ GEM opentelemetry-api (~> 1.0) orm_adapter (0.5.0) ostruct (0.6.1) - ox (2.14.19) + ox (2.14.20) bigdecimal (>= 3.0) parallel (1.26.3) parser (3.3.6.0) From 53885b0fdb8b25bb0d531df2cd90bdb223e21dc6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:13:47 +0100 Subject: [PATCH 096/133] New Crowdin Translations (automated) (#33559) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/cs.json | 72 +++++++- app/javascript/mastodon/locales/ga.json | 12 ++ app/javascript/mastodon/locales/ia.json | 2 +- app/javascript/mastodon/locales/it.json | 5 +- app/javascript/mastodon/locales/ja.json | 8 + app/javascript/mastodon/locales/ne.json | 65 ++++++- app/javascript/mastodon/locales/pl.json | 2 + app/javascript/mastodon/locales/pt-BR.json | 8 +- app/javascript/mastodon/locales/pt-PT.json | 16 +- app/javascript/mastodon/locales/sk.json | 2 + app/javascript/mastodon/locales/vi.json | 4 +- config/locales/activerecord.cs.yml | 13 ++ config/locales/activerecord.ga.yml | 2 + config/locales/activerecord.it.yml | 2 + config/locales/activerecord.ne.yml | 34 ++++ config/locales/activerecord.pt-PT.yml | 2 +- config/locales/cs.yml | 205 ++++++++++++++++++++- config/locales/eo.yml | 6 + config/locales/pl.yml | 3 + config/locales/pt-PT.yml | 8 +- config/locales/simple_form.cs.yml | 14 ++ config/locales/simple_form.eo.yml | 7 + config/locales/simple_form.pt-PT.yml | 2 +- 23 files changed, 459 insertions(+), 35 deletions(-) diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index ba58b998f1..81540c823c 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -104,10 +104,11 @@ "annual_report.summary.most_used_hashtag.none": "Žádné", "annual_report.summary.new_posts.new_posts": "nové příspěvky", "annual_report.summary.percentile.text": "To vás umisťuje do vrcholu{domain} uživatelů.", + "annual_report.summary.percentile.we_wont_tell_bernie": "To, že jste zdejší smetánka zůstane mezi námi ;).", "annual_report.summary.thanks": "Děkujeme, že jste součástí Mastodonu!", "attachments_list.unprocessed": "(nezpracováno)", "audio.hide": "Skrýt zvuk", - "block_modal.remote_users_caveat": "Požádáme server {domain}, aby respektoval vaše rozhodnutí. Úplné dodržování nastavení však není zaručeno, protože některé servery mohou řešit blokování různě. Veřejné příspěvky mohou stále být viditelné pro nepřihlášené uživatele.", + "block_modal.remote_users_caveat": "Požádáme server {domain}, aby respektoval vaše rozhodnutí. Úplné dodržování nastavení však není zaručeno, protože některé servery mohou řešit blokování různě. Veřejné příspěvky mohou být stále viditelné pro nepřihlášené uživatele.", "block_modal.show_less": "Zobrazit méně", "block_modal.show_more": "Zobrazit více", "block_modal.they_cant_mention": "Nemůže vás zmiňovat ani sledovat.", @@ -180,6 +181,7 @@ "compose_form.poll.duration": "Doba trvání ankety", "compose_form.poll.multiple": "Výběr z více možností", "compose_form.poll.option_placeholder": "Volba {number}", + "compose_form.poll.single": "Jediná volba", "compose_form.poll.switch_to_multiple": "Povolit u ankety výběr více voleb", "compose_form.poll.switch_to_single": "Povolit u ankety výběr pouze jedné volby", "compose_form.poll.type": "Styl", @@ -204,6 +206,7 @@ "confirmations.edit.message": "Editovat teď znamená přepsání zprávy, kterou právě tvoříte. Opravdu chcete pokračovat?", "confirmations.edit.title": "Přepsat příspěvek?", "confirmations.follow_to_list.confirm": "Sledovat a přidat do seznamu", + "confirmations.follow_to_list.message": "Musíte {name} sledovat, abyste je přidali do seznamu.", "confirmations.follow_to_list.title": "Sledovat uživatele?", "confirmations.logout.confirm": "Odhlásit se", "confirmations.logout.message": "Opravdu se chcete odhlásit?", @@ -236,19 +239,25 @@ "disabled_account_banner.text": "Váš účet {disabledAccount} je momentálně deaktivován.", "dismissable_banner.community_timeline": "Toto jsou nejnovější veřejné příspěvky od lidí, jejichž účty hostuje {domain}.", "dismissable_banner.dismiss": "Zavřít", + "dismissable_banner.explore_links": "Tyto zprávy jsou dnes nejvíce sdíleny ve fediversu. Novější novinky publikované více různými lidmi jsou v pořadí vyšší.", + "dismissable_banner.explore_statuses": "Tyto příspěvky napříč fediversem dnes získávají na popularitě. Novější příspěvky s více boosty a oblíbenými jsou výše v pořadí.", + "dismissable_banner.explore_tags": "Tyto hashtagy dnes na fediversu získávají na popularitě. Hashtagy, které používá více různých lidí, jsou řazeny výše.", + "dismissable_banner.public_timeline": "Toto jsou nejnovější veřejné příspěvky od lidí na fediversu, které lidé na {domain} sledují.", "domain_block_modal.block": "Blokovat server", "domain_block_modal.block_account_instead": "Raději blokovat @{name}", "domain_block_modal.they_can_interact_with_old_posts": "Lidé z tohoto serveru mohou interagovat s vašimi starými příspěvky.", "domain_block_modal.they_cant_follow": "Nikdo z tohoto serveru vás nemůže sledovat.", "domain_block_modal.they_wont_know": "Nebude vědět, že je zablokován*a.", "domain_block_modal.title": "Blokovat doménu?", + "domain_block_modal.you_will_lose_num_followers": "Ztratíte {followersCount, plural, one {{followersCountDisplay} sledujícího} few {{followersCountDisplay} sledující} many {{followersCountDisplay} sledujících} other {{followersCountDisplay} sledujících}} a {followingCount, plural, one {{followingCountDisplay} sledovaného} few {{followingCountDisplay} sledované} many {{followingCountDisplay} sledovaných} other {{followingCountDisplay} sledovaných}}.", + "domain_block_modal.you_will_lose_relationships": "Ztratíte všechny sledující a lidi, které sledujete z tohoto serveru.", "domain_block_modal.you_wont_see_posts": "Neuvidíte příspěvky ani upozornění od uživatelů z tohoto serveru.", "domain_pill.activitypub_lets_connect": "Umožňuje vám spojit se a komunikovat s lidmi nejen na Mastodonu, ale i s dalšími sociálními aplikacemi.", "domain_pill.activitypub_like_language": "ActivityPub je jako jazyk, kterým Mastodon mluví s jinými sociálními sítěmi.", "domain_pill.server": "Server", "domain_pill.their_handle": "Handle:", - "domain_pill.their_server": "Digitální domov, kde žijí všechny příspěvky.", - "domain_pill.their_username": "Jedinečný identikátor na serveru. Je možné najít uživatele se stejným uživatelským jménem na různých serverech.", + "domain_pill.their_server": "Jejich digitální domov, kde žijí jejich všechny příspěvky.", + "domain_pill.their_username": "Jejich jedinečný identikátor na jejich serveru. Je možné najít uživatele se stejným uživatelským jménem na jiných serverech.", "domain_pill.username": "Uživatelské jméno", "domain_pill.whats_in_a_handle": "Co obsahuje handle?", "domain_pill.who_they_are": "Protože handle říkají kdo je kdo a také kde, je možné interagovat s lidmi napříč sociálními weby .", @@ -322,6 +331,7 @@ "filter_modal.select_filter.title": "Filtrovat tento příspěvek", "filter_modal.title.status": "Filtrovat příspěvek", "filter_warning.matches_filter": "Odpovídá filtru “{title}”", + "filtered_notifications_banner.pending_requests": "Od {count, plural, =0 {nikoho, koho možná znáte} one {člověka, kterého možná znáte} few {#, které možná znáte} many {#, které možná znáte} other {#, které možná znáte}}", "filtered_notifications_banner.title": "Filtrovaná oznámení", "firehose.all": "Vše", "firehose.local": "Tento server", @@ -329,14 +339,14 @@ "follow_request.authorize": "Autorizovat", "follow_request.reject": "Zamítnout", "follow_requests.unlocked_explanation": "Přestože váš účet není uzamčen, personál {domain} usoudil, že byste mohli chtít tyto požadavky na sledování zkontrolovat ručně.", - "follow_suggestions.curated_suggestion": "Výběr personálů", + "follow_suggestions.curated_suggestion": "Výběr personálu", "follow_suggestions.dismiss": "Znovu nezobrazovat", "follow_suggestions.featured_longer": "Ručně vybráno týmem {domain}", "follow_suggestions.friends_of_friends_longer": "Populární mezi lidmi, které sledujete", "follow_suggestions.hints.featured": "Tento profil byl ručně vybrán týmem {domain}.", "follow_suggestions.hints.friends_of_friends": "Tento profil je populární mezi lidmi, které sledujete.", - "follow_suggestions.hints.most_followed": "Tento profil je jedním z nejvíce sledovaných na {domain}.", - "follow_suggestions.hints.most_interactions": "Tento profil nedávno dostalo velkou pozornost na {domain}.", + "follow_suggestions.hints.most_followed": "Tento profil je jedním z nejsledovanějších na {domain}.", + "follow_suggestions.hints.most_interactions": "Tomuto profilu se nedávno dostalo velké pozornosti na {domain}.", "follow_suggestions.hints.similar_to_recently_followed": "Tento profil je podobný profilům, které jste nedávno sledovali.", "follow_suggestions.personalized_suggestion": "Přizpůsobený návrh", "follow_suggestions.popular_suggestion": "Populární návrh", @@ -355,6 +365,7 @@ "footer.terms_of_service": "Obchodní podmínky", "generic.saved": "Uloženo", "getting_started.heading": "Začínáme", + "hashtag.admin_moderation": "Otevřít moderátorské rozhraní pro #{name}", "hashtag.column_header.tag_mode.all": "a {additional}", "hashtag.column_header.tag_mode.any": "nebo {additional}", "hashtag.column_header.tag_mode.none": "bez {additional}", @@ -370,9 +381,13 @@ "hashtag.follow": "Sledovat hashtag", "hashtag.unfollow": "Přestat sledovat hashtag", "hashtags.and_other": "…a {count, plural, one {# další} few {# další} other {# dalších}}", + "hints.profiles.followers_may_be_missing": "Sledující mohou pro tento profil chybět.", + "hints.profiles.follows_may_be_missing": "Sledování mohou pro tento profil chybět.", + "hints.profiles.posts_may_be_missing": "Některé příspěvky z tohoto profilu mohou chybět.", "hints.profiles.see_more_followers": "Zobrazit více sledujících na {domain}", "hints.profiles.see_more_follows": "Zobrazit další sledování na {domain}", "hints.profiles.see_more_posts": "Zobrazit další příspěvky na {domain}", + "hints.threads.replies_may_be_missing": "Odpovědi z jiných serverů mohou chybět.", "hints.threads.see_more": "Zobrazit další odpovědi na {domain}", "home.column_settings.show_reblogs": "Zobrazit boosty", "home.column_settings.show_replies": "Zobrazit odpovědi", @@ -381,7 +396,22 @@ "home.pending_critical_update.link": "Zobrazit aktualizace", "home.pending_critical_update.title": "K dispozici je kritická bezpečnostní aktualizace!", "home.show_announcements": "Zobrazit oznámení", + "ignore_notifications_modal.disclaimer": "Mastodon nemůže informovat uživatele, že jste ignorovali jejich oznámení. Ignorování oznámení nezabrání odesílání zpráv samotných.", + "ignore_notifications_modal.filter_instead": "Místo toho filtrovat", + "ignore_notifications_modal.filter_to_act_users": "Stále budete moci přijmout, odmítnout nebo nahlásit uživatele", + "ignore_notifications_modal.filter_to_avoid_confusion": "Filtrování pomáhá vyhnout se možným nejasnostem", + "ignore_notifications_modal.filter_to_review_separately": "Filtrovaná oznámení můžete zkontrolovat samostatně", "ignore_notifications_modal.ignore": "Ignorovat oznámení", + "ignore_notifications_modal.limited_accounts_title": "Ignorovat oznámení z moderovaných účtů?", + "ignore_notifications_modal.new_accounts_title": "Ignorovat oznámení z nových účtů?", + "ignore_notifications_modal.not_followers_title": "Ignorovat oznámení od lidí, kteří vás nesledují?", + "ignore_notifications_modal.not_following_title": "Ignorovat oznámení od lidí, které nesledujete?", + "ignore_notifications_modal.private_mentions_title": "Ignorovat oznámení z nevyžádaných soukromých zmínek?", + "interaction_modal.action.favourite": "Chcete-li pokračovat, musíte oblíbit z vašeho účtu.", + "interaction_modal.action.follow": "Chcete-li pokračovat, musíte sledovat z vašeho účtu.", + "interaction_modal.action.reblog": "Chcete-li pokračovat, musíte dát boost z vašeho účtu.", + "interaction_modal.action.reply": "Chcete-li pokračovat, musíte odpovědět z vašeho účtu.", + "interaction_modal.action.vote": "Chcete-li pokračovat, musíte hlasovat z vašeho účtu.", "interaction_modal.go": "Přejít", "interaction_modal.no_account_yet": "Ještě nemáte účet?", "interaction_modal.on_another_server": "Na jiném serveru", @@ -433,20 +463,27 @@ "lightbox.close": "Zavřít", "lightbox.next": "Další", "lightbox.previous": "Předchozí", + "lightbox.zoom_in": "Přiblížit na skutečnou velikost", + "lightbox.zoom_out": "Přizpůsobit velikost", "limited_account_hint.action": "Přesto profil zobrazit", "limited_account_hint.title": "Tento profil byl skryt moderátory {domain}.", "link_preview.author": "Podle {name}", "link_preview.more_from_author": "Více od {name}", "link_preview.shares": "{count, plural, one {{counter} příspěvek} few {{counter} příspěvky} many {{counter} příspěvků} other {{counter} příspěvků}}", "lists.add_member": "Přidat", + "lists.add_to_list": "Přidat do seznamu", "lists.add_to_lists": "Přidat {name} do seznamů", "lists.create": "Vytvořit", + "lists.create_a_list_to_organize": "Vytvořte nový seznam pro organizaci vašeho domovského kanálu", "lists.create_list": "Vytvořit seznam", "lists.delete": "Smazat seznam", "lists.done": "Hotovo", "lists.edit": "Upravit seznam", + "lists.exclusive": "Skrýt členy na domovském kanálu", + "lists.exclusive_hint": "Pokud je někdo na tomto seznamu, skryjte jej ve vašem domovském kanálu, abyste se vyhnuli dvojímu vidění jejich příspěvků.", "lists.find_users_to_add": "Najít uživatele, které chcete přidat", "lists.list_members": "Členové seznamu", + "lists.list_members_count": "{count, plural, one {# člen} few {# členové} many {# členů} other {# členů}}", "lists.list_name": "Název seznamu", "lists.new_list_name": "Název nového seznamu", "lists.no_lists_yet": "Zatím žádné seznamy.", @@ -458,6 +495,7 @@ "lists.replies_policy.none": "Nikomu", "lists.save": "Uložit", "lists.search": "Hledat", + "lists.show_replies_to": "Zahrnout odpovědi od členů seznamu pro", "load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položek} other {# nových položek}}", "loading_indicator.label": "Načítání…", "media_gallery.hide": "Skrýt", @@ -500,14 +538,25 @@ "navigation_bar.security": "Zabezpečení", "not_signed_in_indicator.not_signed_in": "Pro přístup k tomuto zdroji se musíte přihlásit.", "notification.admin.report": "Uživatel {name} nahlásil {target}", + "notification.admin.report_account": "{name} nahlásil {count, plural, one {jeden příspěvek} few {# příspěvky} many {# příspěvků} other {# příspěvků}} od {target} za {category}", + "notification.admin.report_account_other": "{name} nahlásil {count, plural, one {jeden příspěvek} few {# příspěvky} many {# příspěvků} other {# příspěvků}} od {target}", "notification.admin.report_statuses": "{name} nahlásil {target} za {category}", "notification.admin.report_statuses_other": "{name} nahlásil {target}", "notification.admin.sign_up": "Uživatel {name} se zaregistroval", + "notification.admin.sign_up.name_and_others": "{name} a {count, plural, one {# další} few {# další} many {# dalších} other {# dalších}} se zaregistrovali", + "notification.annual_report.message": "Váš #Wrapstodon {year} na Vás čeká! Podívejte se, jak vypadal tento Váš rok na Mastodonu!", + "notification.annual_report.view": "Zobrazit #Wrapstodon", "notification.favourite": "Uživatel {name} si oblíbil váš příspěvek", + "notification.favourite.name_and_others_with_link": "{name} a {count, plural, one {# další si oblíbil} few {# další si oblíbili} other {# dalších si oblíbilo}} Váš příspěvek", + "notification.favourite_pm": "{name} si oblíbil vaši soukromou zmínku", + "notification.favourite_pm.name_and_others_with_link": "{name} a {count, plural, one {# další si oblíbil} few {# další si oblíbili} other {# dalších si oblíbilo}} Vaši soukromou zmínku", "notification.follow": "Uživatel {name} vás začal sledovat", + "notification.follow.name_and_others": "{name} a {count, plural, one {# další Vás začal sledovat} few {# další Vás začali sledovat} other {# dalších Vás začalo sledovat}}", "notification.follow_request": "Uživatel {name} požádal o povolení vás sledovat", + "notification.follow_request.name_and_others": "{name} a {count, plural, one {# další Vám poslal žádost o sledování} few {# další Vám poslali žádost o sledování} other {# dalších Vám poslalo žádost o sledování}}", "notification.label.mention": "Zmínka", "notification.label.private_mention": "Soukromá zmínka", + "notification.label.private_reply": "Privátní odpověď", "notification.label.reply": "Odpověď", "notification.mention": "Zmínka", "notification.mentioned_you": "{name} vás zmínil", @@ -523,6 +572,7 @@ "notification.own_poll": "Vaše anketa skončila", "notification.poll": "Anketa, ve které jste hlasovali, skončila", "notification.reblog": "Uživatel {name} boostnul váš příspěvek", + "notification.reblog.name_and_others_with_link": "{name} a {count, plural, one {# další boostnul} few {# další boostnuli} other {# dalších boostnulo}} Váš příspěvek", "notification.relationships_severance_event": "Kontakt ztracen s {name}", "notification.relationships_severance_event.account_suspension": "Administrátor z {from} pozastavil {target}, což znamená, že již od nich nemůžete přijímat aktualizace nebo s nimi interagovat.", "notification.relationships_severance_event.domain_block": "Administrátor z {from} pozastavil {target}, včetně {followersCount} z vašich sledujících a {followingCount, plural, one {# účet, který sledujete} few {# účty, které sledujete} many {# účtů, které sledujete} other {# účtů, které sledujete}}.", @@ -531,10 +581,19 @@ "notification.status": "Uživatel {name} právě přidal příspěvek", "notification.update": "Uživatel {name} upravil příspěvek", "notification_requests.accept": "Přijmout", + "notification_requests.accept_multiple": "{count, plural, one {Schválit # požadavek…} few {Schválit # požadavky…} other {Schválit # požadavků…}}", + "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Schválit požadavek} other {Schválit požadavky}}", + "notification_requests.confirm_accept_multiple.message": "Chystáte se schválit {count, plural, one {jeden požadavek} few {# požadavky} other {# požadavků}} na oznámení. Opravdu chcete pokračovat?", "notification_requests.confirm_accept_multiple.title": "Přijmout žádosti o oznámení?", + "notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Zamítnout požadavek} other {Zamítnout požadavky}}", + "notification_requests.confirm_dismiss_multiple.message": "Chystáte se zamítnout {count, plural, one {jeden požadavek} few {# požadavky} many {# požadavků} other {# požadavků}} na oznámení. Poté k {count, plural, one {němu} other {něm}} již nebudete mít snadný přístup. Opravdu chcete pokračovat?", + "notification_requests.confirm_dismiss_multiple.title": "Zamítnout požadavky na oznámení?", "notification_requests.dismiss": "Zamítnout", + "notification_requests.dismiss_multiple": "Zamítnout {count, plural, one {# požadavek} few {# požadavky} many {# požadavků} other {# požadavků}}…", "notification_requests.edit_selection": "Upravit", "notification_requests.exit_selection": "Hotovo", + "notification_requests.explainer_for_limited_account": "Oznámení z tohoto účtu byla filtrována, protože tento účet byl omezen moderátorem.", + "notification_requests.explainer_for_limited_remote_account": "Oznámení z tohoto účtu byla filtrována, protože tento účet nebo jeho server byl omezen moderátorem.", "notification_requests.maximize": "Maximalizovat", "notification_requests.minimize_banner": "Minimalizovat banner filtrovaných oznámení", "notification_requests.notifications_from": "Oznámení od {name}", @@ -578,6 +637,7 @@ "notifications.policy.accept": "Přijmout", "notifications.policy.accept_hint": "Zobrazit v oznámeních", "notifications.policy.drop": "Ignorovat", + "notifications.policy.drop_hint": "Permanentně odstranit, aby již nikdy nespatřil světlo světa", "notifications.policy.filter": "Filtrovat", "notifications.policy.filter_hint": "Odeslat do filtrované schránky oznámení", "notifications.policy.filter_limited_accounts_hint": "Omezeno moderátory serveru", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index 1f3bccee15..d533e99906 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -407,6 +407,13 @@ "ignore_notifications_modal.not_followers_title": "An dtugann tú aird ar fhógraí ó dhaoine nach leanann tú?", "ignore_notifications_modal.not_following_title": "An ndéanann tú neamhaird de fhógraí ó dhaoine nach leanann tú?", "ignore_notifications_modal.private_mentions_title": "An dtugann tú aird ar fhógraí ó Luaintí Príobháideacha gan iarraidh?", + "interaction_modal.action.favourite": "Chun leanúint ar aghaidh, ní mór duit an ceann is fearr leat ó do chuntas.", + "interaction_modal.action.follow": "Chun leanúint ar aghaidh, ní mór duit leanúint ó do chuntas.", + "interaction_modal.action.reblog": "Chun leanúint ar aghaidh, ní mór duit athbhlagáil ó do chuntas.", + "interaction_modal.action.reply": "Chun leanúint ar aghaidh, ní mór duit freagra a thabhairt ó do chuntas.", + "interaction_modal.action.vote": "Chun leanúint ar aghaidh, ní mór duit vótáil ó do chuntas.", + "interaction_modal.go": "Téigh", + "interaction_modal.no_account_yet": "Níl cuntas agat fós?", "interaction_modal.on_another_server": "Ar freastalaí eile", "interaction_modal.on_this_server": "Ar an freastalaí seo", "interaction_modal.title.favourite": "An postáil {name} is fearr leat", @@ -414,6 +421,7 @@ "interaction_modal.title.reblog": "Mol postáil de chuid {name}", "interaction_modal.title.reply": "Freagair postáil {name}", "interaction_modal.title.vote": "Vótáil i vótaíocht {name}", + "interaction_modal.username_prompt": "M.sh. {example}", "intervals.full.days": "{number, plural, one {# lá} other {# lá}}", "intervals.full.hours": "{number, plural, one {# uair} other {# uair}}", "intervals.full.minutes": "{number, plural, one {# nóiméad} other {# nóiméad}}", @@ -449,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Taispeáin/folaigh an téacs taobh thiar de CW", "keyboard_shortcuts.toggle_sensitivity": "Taispeáin / cuir i bhfolach meáin", "keyboard_shortcuts.toot": "Cuir tús le postáil nua", + "keyboard_shortcuts.translate": "post a aistriú", "keyboard_shortcuts.unfocus": "Unfocus cum textarea/search", "keyboard_shortcuts.up": "Bog suas ar an liosta", "lightbox.close": "Dún", @@ -687,6 +696,8 @@ "privacy_policy.title": "Polasaí príobháideachais", "recommended": "Molta", "refresh": "Athnuaigh", + "regeneration_indicator.please_stand_by": "Fan i do sheasamh, le do thoil.", + "regeneration_indicator.preparing_your_home_feed": "Ag ullmhú do bheatha baile…", "relative_time.days": "{number}l", "relative_time.full.days": "{number, plural, one {# lá} other {# lá}} ó shin", "relative_time.full.hours": "{number, plural, one {# uair} other {# uair}} ó shin", @@ -826,6 +837,7 @@ "status.reblogs.empty": "Níor mhol éinne an phostáil seo fós. Nuair a mholfaidh duine éigin í, taispeánfar anseo é sin.", "status.redraft": "Scrios ⁊ athdhréachtaigh", "status.remove_bookmark": "Bain leabharmharc", + "status.remove_favourite": "Bain ó cheanáin", "status.replied_in_thread": "D'fhreagair sa snáithe", "status.replied_to": "D'fhreagair {name}", "status.reply": "Freagair", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index e2f821022e..9125ce641b 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -803,7 +803,7 @@ "status.bookmark": "Adder al marcapaginas", "status.cancel_reblog_private": "Disfacer impulso", "status.cannot_reblog": "Iste message non pote esser impulsate", - "status.continued_thread": "Discussion continuate", + "status.continued_thread": "Continuation del discussion", "status.copy": "Copiar ligamine a message", "status.delete": "Deler", "status.detailed_status": "Vista detaliate del conversation", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 09e6ea072a..aa58c34687 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -431,11 +431,11 @@ "keyboard_shortcuts.column": "Focalizza alla colonna", "keyboard_shortcuts.compose": "Focalizza l'area di composizione testuale", "keyboard_shortcuts.description": "Descrizione", - "keyboard_shortcuts.direct": "per aprire la colonna menzioni private", + "keyboard_shortcuts.direct": "Apre la colonna \"menzioni private\"", "keyboard_shortcuts.down": "Scorri in basso nell'elenco", "keyboard_shortcuts.enter": "Apre il post", "keyboard_shortcuts.favourite": "Contrassegna il post come preferito", - "keyboard_shortcuts.favourites": "Apri l'elenco dei preferiti", + "keyboard_shortcuts.favourites": "Apre l'elenco dei preferiti", "keyboard_shortcuts.federated": "Apre la cronologia federata", "keyboard_shortcuts.heading": "Scorciatoie da tastiera", "keyboard_shortcuts.home": "Apre la cronologia domestica", @@ -457,6 +457,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostra/Nasconde il testo dietro CW", "keyboard_shortcuts.toggle_sensitivity": "Mostra/Nasconde media", "keyboard_shortcuts.toot": "Crea un nuovo post", + "keyboard_shortcuts.translate": "Traduce un post", "keyboard_shortcuts.unfocus": "Rimuove il focus sull'area di composizione testuale/ricerca", "keyboard_shortcuts.up": "Scorre in su nell'elenco", "lightbox.close": "Chiudi", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 208ed91104..926781b7f6 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -407,6 +407,13 @@ "ignore_notifications_modal.not_followers_title": "本当に「フォローされていないアカウントからの通知」を無視するようにしますか?", "ignore_notifications_modal.not_following_title": "本当に「フォローしていないアカウントからの通知」を無視するようにしますか?", "ignore_notifications_modal.private_mentions_title": "本当に「外部からの非公開の返信」を無視するようにしますか?", + "interaction_modal.action.favourite": "お気に入り登録はあなたのアカウントがあるサーバーで行う必要があります。", + "interaction_modal.action.follow": "ユーザーをフォローするには、あなたのアカウントがあるサーバーからフォローする必要があります。", + "interaction_modal.action.reblog": "投稿をブーストするには、あなたのアカウントがあるサーバーでブーストする必要があります。", + "interaction_modal.action.reply": "リプライを送るには、あなたのアカウントがあるサーバーから送る必要があります。", + "interaction_modal.action.vote": "票を入れるには、あなたのアカウントがあるサーバーから投票する必要があります。", + "interaction_modal.go": "サーバーに移動", + "interaction_modal.no_account_yet": "アカウントを持っていない場合は:", "interaction_modal.on_another_server": "別のサーバー", "interaction_modal.on_this_server": "このサーバー", "interaction_modal.title.favourite": "{name}さんの投稿をお気に入り登録", @@ -414,6 +421,7 @@ "interaction_modal.title.reblog": "{name}さんの投稿をブースト", "interaction_modal.title.reply": "{name}さんの投稿にリプライ", "interaction_modal.title.vote": "{name}さんのアンケートに投票", + "interaction_modal.username_prompt": "例: {example}", "intervals.full.days": "{number}日", "intervals.full.hours": "{number}時間", "intervals.full.minutes": "{number}分", diff --git a/app/javascript/mastodon/locales/ne.json b/app/javascript/mastodon/locales/ne.json index 9e9fe90888..6fe1330d6c 100644 --- a/app/javascript/mastodon/locales/ne.json +++ b/app/javascript/mastodon/locales/ne.json @@ -47,11 +47,13 @@ "account.mutual": "आपसी", "account.no_bio": "कुनै विवरण प्रदान गरिएको छैन।", "account.posts": "पोस्टहरू", + "account.posts_with_replies": "पोस्ट र जवाफहरू", "account.report": "@{name}लाई रिपोर्ट गर्नुहोस्", "account.requested": "स्वीकृतिको पर्खाइमा। फलो अनुरोध रद्द गर्न क्लिक गर्नुहोस्", "account.requested_follow": "{name} ले तपाईंलाई फलो गर्न अनुरोध गर्नुभएको छ", "account.share": "@{name} को प्रोफाइल सेयर गर्नुहोस्", "account.show_reblogs": "@{name} को बूस्टहरू देखाउनुहोस्", + "account.statuses_counter": "{count, plural, one {{counter} पोस्ट} other {{counter} पोस्टहरू}}", "account.unblock": "@{name} लाई अनब्लक गर्नुहोस्", "account.unblock_domain": "{domain} डोमेनलाई अनब्लक गर्नुहोस्", "account.unblock_short": "अनब्लक गर्नुहोस्", @@ -67,14 +69,18 @@ "alert.unexpected.message": "एउटा अनपेक्षित त्रुटि भयो।", "announcement.announcement": "घोषणा", "annual_report.summary.followers.followers": "फलोअरहरु", + "annual_report.summary.highlighted_post.by_reblogs": "सबैभन्दा बढि बूस्ट गरिएको पोस्ट", "annual_report.summary.new_posts.new_posts": "नयाँ पोस्टहरू", "block_modal.remote_users_caveat": "हामी सर्भर {domain} लाई तपाईंको निर्णयको सम्मान गर्न सोध्नेछौं। तर, हामी अनुपालनको ग्यारेन्टी दिन सक्दैनौं किनभने केही सर्भरहरूले ब्लकहरू फरक रूपमा ह्यान्डल गर्न सक्छन्। सार्वजनिक पोस्टहरू लग इन नभएका प्रयोगकर्ताहरूले देख्न सक्छन्।", "block_modal.show_less": "कम देखाउनुहोस्", "block_modal.show_more": "थप देखाउनुहोस्", - "block_modal.title": "प्रयोगकर्तालाई ब्लक गर्ने हो?", + "block_modal.title": "प्रयोगकर्तालाई ब्लक गर्ने?", + "boost_modal.reblog": "पोस्ट बुस्ट गर्ने?", + "boost_modal.undo_reblog": "पोस्ट अनबुस्ट गर्ने?", "bundle_column_error.copy_stacktrace": "त्रुटि रिपोर्ट प्रतिलिपि गर्नुहोस्", "bundle_column_error.network.title": "नेटवर्क त्रुटि", "bundle_column_error.retry": "पुन: प्रयास गर्नुहोस्", + "bundle_column_error.routing.title": "४०४", "bundle_modal_error.close": "बन्द गर्नुहोस्", "bundle_modal_error.retry": "Try again", "closed_registrations.other_server_instructions": "Mastodon विकेन्द्रीकृत भएकोले, तपाइँ अर्को सर्भरमा खाता खोल्न सक्नुहुन्छ र पनि यो सर्भरसँग अन्तरक्रिया गर्न सक्नुहुन्छ।", @@ -82,23 +88,54 @@ "closed_registrations_modal.find_another_server": "अर्को सर्भर खोज्नुहोस्", "closed_registrations_modal.title": "Mastodon मा साइन अप गर्दै", "column.blocks": "ब्लक गरिएको प्रयोगकर्ताहरु", + "column.bookmarks": "बुकमार्कहरू", + "column.create_list": "सूची बनाउनुहोस्", + "column.direct": "निजी उल्लेखहरू", "column.directory": "प्रोफाइल ब्राउज गर्नुहोस्", "column.domain_blocks": "ब्लक गरिएको डोमेन", + "column.edit_list": "सूची सम्पादन गर्नुहोस्", "column.follow_requests": "फलो अनुरोधहरू", + "column.home": "गृहपृष्ठ", "column.lists": "सूचीहरू", + "column.mutes": "म्यूट गरिएका प्रयोगकर्ताहरू", "column.notifications": "सूचनाहरू", + "column.pins": "पिन गरिएका पोस्टहरू", "column_header.hide_settings": "सेटिङ्हरू लुकाउनुहोस्", + "column_header.pin": "पिन गर्नुहोस्", + "column_header.unpin": "अनपिन गर्नुहोस्", + "column_search.cancel": "रद्द गर्नुहोस्", "column_subheading.settings": "सेटिङहरू", + "community.column_settings.media_only": "मिडिया मात्र", "compose.language.change": "भाषा परिवर्तन गर्नुहोस्", "compose.language.search": "भाषाहरू खोज्नुहोस्...", + "compose.published.body": "पोस्ट प्रकाशित भयो।", + "compose.published.open": "खोल्नुहोस्", + "compose.saved.body": "पोस्ट सेभ गरियो।", "compose_form.direct_message_warning_learn_more": "थप जान्नुहोस्", + "compose_form.placeholder": "तपाईको मनमा के छ?", + "compose_form.publish": "पोस्ट गर्नुहोस्", "compose_form.publish_form": "नयाँ पोस्ट", + "compose_form.reply": "जवाफ दिनुहोस्", + "compose_form.save_changes": "अपडेट गर्नुहोस्", + "confirmations.delete.message": "के तपाइँ पक्का हुनुहुन्छ कि तपाईं यो पोष्ट मेटाउन चाहनुहुन्छ?", + "confirmations.delete.title": "पोस्ट मेटाउने?", + "confirmations.delete_list.message": "के तपाइँ पक्का हुनुहुन्छ कि तपाईं यो सूची स्थायी रूपमा मेटाउन चाहनुहुन्छ?", + "confirmations.delete_list.title": "सूची मेटाउने?", + "confirmations.edit.confirm": "सम्पादन गर्नुहोस्", + "confirmations.edit.message": "अहिले सम्पादन गर्नाले तपाईंले हाल लेखिरहनुभएको सन्देश अधिलेखन हुनेछ। के तपाईं अगाडि बढ्न चाहनुहुन्छ?", + "confirmations.edit.title": "पोस्ट अधिलेखन गर्ने?", "confirmations.follow_to_list.confirm": "फलो गर्नुहोस र सूचीमा थप्नुहोस्", "confirmations.follow_to_list.message": "सूचीमा {name}लाई थप्नको लागि तपाईंले तिनीहरूलाई फलो गरेको हुनुपर्छ।", - "confirmations.follow_to_list.title": "प्रयोगकर्तालाई फलो गर्ने हो?", + "confirmations.follow_to_list.title": "प्रयोगकर्तालाई फलो गर्ने?", + "confirmations.logout.message": "के तपाइँ पक्का हुनुहुन्छ कि तपाइँ लाई लग आउट गर्न चाहनुहुन्छ?", + "confirmations.logout.title": "लग आउट गर्ने?", + "confirmations.redraft.title": "पोस्ट मेटाएर पुन: ड्राफ्ट गर्ने?", + "confirmations.reply.message": "अहिले जवाफ दिनाले तपाईंले हाल लेखिरहनुभएको सन्देश अधिलेखन हुनेछ। के तपाईं अगाडि बढ्न चाहनुहुन्छ?", + "confirmations.reply.title": "पोस्ट अधिलेखन गर्ने?", "confirmations.unfollow.confirm": "अनफलो गर्नुहोस्", "confirmations.unfollow.message": "के तपाइँ पक्का हुनुहुन्छ कि तपाइँ {name}लाई अनफलो गर्न चाहनुहुन्छ?", - "confirmations.unfollow.title": "प्रयोगकर्तालाई अनफलो गर्ने हो?", + "confirmations.unfollow.title": "प्रयोगकर्तालाई अनफलो गर्ने?", + "disabled_account_banner.account_settings": "खाता सेटिङहरू", "empty_column.follow_requests": "तपाईंले अहिलेसम्म कुनै पनि फलो अनुरोधहरू प्राप्त गर्नुभएको छैन। तपाईंले कुनै प्राप्त गरेपछि त्यो यहाँ देखिनेछ।", "empty_column.followed_tags": "तपाईंले अहिलेसम्म कुनै पनि ह्यासट्यागहरू फलो गर्नुभएको छैन। तपाईंले ह्यासट्याग फलो गरेपछि तिनीहरू यहाँ देखिनेछन्।", "follow_suggestions.dismiss": "फेरि नदेखाउनुहोस्", @@ -111,15 +148,35 @@ "followed_tags": "फलो गरिएका ह्यासट्यागहरू", "hashtag.follow": "ह्यासट्याग फलो गर्नुहोस्", "hashtag.unfollow": "ह्यासट्याग अनफलो गर्नुहोस्", + "home.column_settings.show_reblogs": "बूस्टहरू देखाउनुहोस्", + "interaction_modal.no_account_yet": "अहिलेसम्म खाता छैन?", "interaction_modal.title.follow": "{name} लाई फलो गर्नुहोस्", + "interaction_modal.title.reblog": "{name} को पोस्ट बुस्ट गर्नुहोस्", + "keyboard_shortcuts.boost": "पोस्ट बुस्ट गर्नुहोस्", "mute_modal.they_wont_know": "उनीहरूलाई म्यूट गरिएको बारे थाहा हुँदैन।", - "mute_modal.title": "प्रयोगकर्तालाई म्युट गर्ने हो?", + "mute_modal.title": "प्रयोगकर्तालाई म्युट गर्ने?", "navigation_bar.blocks": "ब्लक गरिएको प्रयोगकर्ताहरु", "navigation_bar.follow_requests": "फलो अनुरोधहरू", "navigation_bar.followed_tags": "फलो गरिएका ह्यासट्यागहरू", + "notification.reblog": "{name} ले तपाईंको पोस्ट बूस्ट गर्नुभयो", + "notification_requests.confirm_accept_multiple.title": "सूचना अनुरोधहरू स्वीकार गर्ने?", + "notification_requests.confirm_dismiss_multiple.title": "सूचना अनुरोधहरू खारेज गर्ने?", + "notifications.clear_title": "सूचनाहरू खाली गर्ने?", + "notifications.column_settings.reblog": "बूस्टहरू:", + "notifications.filter.boosts": "बूस्टहरू", + "report.comment.title": "के हामीले थाहा पाउनुपर्ने अरू केही छ जस्तो लाग्छ?", + "report.forward_hint": "यो खाता अर्को सर्भरबाट हो। त्यहाँ पनि रिपोर्टको गुमनाम प्रतिलिपि पठाउने हो?", + "report.rules.title": "कुन नियमहरू उल्लङ्घन भइरहेका छन्?", + "report.statuses.title": "के यस रिपोर्टलाई समर्थन गर्ने कुनै पोस्टहरू छन्?", + "report.thanks.title": "यो हेर्न चाहनुहुन्न?", "report.unfollow": "@{name} लाई अनफलो गर्नुहोस्", "search_results.hashtags": "ह्यासट्यागहरू", + "status.cancel_reblog_private": "अनबुस्ट गर्नुहोस्", + "status.cannot_reblog": "यो पोस्टलाई बुस्ट गर्न सकिँदैन", "status.mute": "@{name}लाई म्यूट गर्नुहोस्", "status.mute_conversation": "कुराकानी म्यूट गर्नुहोस्", + "status.reblog": "बूस्ट गर्नुहोस्", + "status.reblogged_by": "{name} ले बूस्ट गर्नुभएको", + "status.reblogs": "{count, plural, one {बूस्ट} other {बूस्टहरू}}", "status.unmute_conversation": "कुराकानी अनम्यूट गर्नुहोस्" } diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index f9cb9743ac..b419870d7a 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -416,6 +416,7 @@ "interaction_modal.title.reblog": "Podbij wpis {name}", "interaction_modal.title.reply": "Odpowiedz na post {name}", "interaction_modal.title.vote": "Weź udział w głosowaniu {name}", + "interaction_modal.username_prompt": "Np. {example}", "intervals.full.days": "{number, plural, one {# dzień} few {# dni} many {# dni} other {# dni}}", "intervals.full.hours": "{number, plural, one {# godzina} few {# godziny} many {# godzin} other {# godzin}}", "intervals.full.minutes": "{number, plural, one {# minuta} few {# minuty} many {# minut} other {# minut}}", @@ -828,6 +829,7 @@ "status.reblogs.empty": "Nikt nie podbił jeszcze tego wpisu. Gdy ktoś to zrobi, pojawi się tutaj.", "status.redraft": "Usuń i przeredaguj", "status.remove_bookmark": "Usuń zakładkę", + "status.remove_favourite": "Usuń z ulubionych", "status.replied_in_thread": "Odpowiedź w wątku", "status.replied_to": "Odpowiedź do wpisu użytkownika {name}", "status.reply": "Odpowiedz", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 142ae33c58..99929e1f35 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -108,7 +108,7 @@ "annual_report.summary.thanks": "Obrigada por fazer parte do Mastodon!", "attachments_list.unprocessed": "(não processado)", "audio.hide": "Ocultar áudio", - "block_modal.remote_users_caveat": "Pediremos ao servidor {domínio} que respeite sua decisão. No entanto, a conformidade não é garantida pois alguns servidores podem lidar com os blocos de maneira diferente. As postagens públicas ainda podem estar visíveis para usuários não logados.", + "block_modal.remote_users_caveat": "Pediremos ao servidor {domain} que respeite sua decisão. No entanto, a conformidade não é garantida, já que alguns servidores podem lidar com bloqueios de maneira diferente. As postagens públicas ainda podem estar visíveis para usuários não logados.", "block_modal.show_less": "Mostrar menos", "block_modal.show_more": "Mostrar mais", "block_modal.they_cant_mention": "Eles não podem mencionar ou seguir você.", @@ -259,9 +259,9 @@ "domain_pill.their_server": "Sua casa digital, onde ficam todas as suas postagens.", "domain_pill.their_username": "Seu identificador exclusivo em seu servidor. É possível encontrar usuários com o mesmo nome de usuário em servidores diferentes.", "domain_pill.username": "Nome de usuário", - "domain_pill.whats_in_a_handle": "O que há em uma alça?", - "domain_pill.who_they_are": "Como os identificadores indicam quem alguém é e onde está, você pode interagir com pessoas na web social de .", - "domain_pill.who_you_are": "Como seu identificador indica quem você é e onde está, as pessoas podem interagir com você nas redes sociais das .", + "domain_pill.whats_in_a_handle": "O que há em um identificador?", + "domain_pill.who_they_are": "Como os identificadores indicam quem alguém é e onde está, você pode interagir com pessoas na rede de .", + "domain_pill.who_you_are": "Como seu identificador indica quem você é e onde está, as pessoas podem interagir com você na rede de .", "domain_pill.your_handle": "Seu identificador:", "domain_pill.your_server": "Sua casa digital, onde ficam todas as suas postagens. Não gosta deste? Transfira servidores a qualquer momento e traga seus seguidores também.", "domain_pill.your_username": "Seu identificador exclusivo neste servidor. É possível encontrar usuários com o mesmo nome de usuário em servidores diferentes.", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index f7329ad97e..42d06ea72b 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -1,7 +1,7 @@ { "about.blocks": "Servidores moderados", "about.contact": "Contacto:", - "about.disclaimer": "O Mastodon é um software livre, de código aberto e uma marca registada do Mastodon gGmbH.", + "about.disclaimer": "O Mastodon é um software livre, de código aberto e uma marca registada de Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Motivo não disponível", "about.domain_blocks.preamble": "O Mastodon geralmente permite ver e interagir com o conteúdo de utilizadores de qualquer outra instância no fediverso. Estas são as exceções desta instância em específico.", "about.domain_blocks.silenced.explanation": "Normalmente não verás perfis e conteúdos deste servidor, a não ser que os procures explicitamente ou optes por segui-los.", @@ -723,7 +723,7 @@ "report.category.title_account": "perfil", "report.category.title_status": "publicação", "report.close": "Concluído", - "report.comment.title": "Há algo mais que pensa que devemos saber?", + "report.comment.title": "Há mais alguma coisa que devamos saber?", "report.forward": "Reencaminhar para {target}", "report.forward_hint": "A conta pertence a outro servidor. Enviar uma cópia anónima da denúncia para esse servidor também?", "report.mute": "Ocultar", @@ -739,15 +739,15 @@ "report.reasons.spam": "É spam", "report.reasons.spam_description": "Hiperligações maliciosas, contactos falsos ou respostas repetitivas", "report.reasons.violation": "Viola as regras do servidor", - "report.reasons.violation_description": "Está ciente de que infringe regras específicas", - "report.rules.subtitle": "Selecione tudo o que se aplicar", + "report.reasons.violation_description": "Infringe regras específicas", + "report.rules.subtitle": "Seleciona tudo o que se aplicar", "report.rules.title": "Que regras estão a ser violadas?", - "report.statuses.subtitle": "Selecione tudo o que se aplicar", + "report.statuses.subtitle": "Seleciona tudo o que se aplicar", "report.statuses.title": "Existe alguma publicação que suporte esta denúncia?", "report.submit": "Enviar", "report.target": "A denunciar {target}", "report.thanks.take_action": "Aqui estão as suas opções para controlar o que vê no Mastodon:", - "report.thanks.take_action_actionable": "Enquanto revemos a sua denúncia, pode tomar medidas contra @{name}:", + "report.thanks.take_action_actionable": "Enquanto revemos a tua denúncia, podes tomar medidas contra @{name}:", "report.thanks.title": "Não quer ver isto?", "report.thanks.title_actionable": "Obrigado por nos informares, vamos analisar a situação.", "report.unfollow": "Deixar de seguir @{name}", @@ -758,7 +758,7 @@ "report_notification.categories.other": "Outro", "report_notification.categories.other_sentence": "outro", "report_notification.categories.spam": "Spam", - "report_notification.categories.spam_sentence": "spam", + "report_notification.categories.spam_sentence": "publicidade indesejada / spam", "report_notification.categories.violation": "Violação de regra", "report_notification.categories.violation_sentence": "violação de regra", "report_notification.open": "Abrir denúncia", @@ -813,7 +813,7 @@ "status.edited": "Última edição em {date}", "status.edited_x_times": "Editado {count, plural,one {{count} vez} other {{count} vezes}}", "status.embed": "Obter código de incorporação", - "status.favourite": "Assinalar como favorito", + "status.favourite": "Adicionar aos favoritos", "status.favourites": "{count, plural, one {favorito} other {favoritos}}", "status.filter": "Filtrar esta publicação", "status.history.created": "{name} criado em {date}", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index f59e7b96bc..d0617a52c8 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -530,6 +530,7 @@ "notification.status": "{name} uverejňuje niečo nové", "notification.update": "{name} upravuje príspevok", "notification_requests.accept": "Prijať", + "notification_requests.confirm_accept_multiple.title": "Priať požiadavku o oboznámenia?", "notification_requests.dismiss": "Zamietnuť", "notification_requests.edit_selection": "Uprav", "notification_requests.exit_selection": "Hotovo", @@ -624,6 +625,7 @@ "privacy_policy.title": "Pravidlá ochrany súkromia", "recommended": "Odporúčané", "refresh": "Obnoviť", + "regeneration_indicator.preparing_your_home_feed": "Pripravuje sa tvoj domáci kanál…", "relative_time.days": "{number} dní", "relative_time.full.days": "Pred {number, plural, one {# dňom} other {# dňami}}", "relative_time.full.hours": "Pred {number, plural, one {# hodinou} other {# hodinami}}", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 834c74eb77..3bad8fedb7 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -550,7 +550,7 @@ "notification.favourite.name_and_others_with_link": "{name} và {count, plural, other {# người khác}} đã thích tút của bạn", "notification.favourite_pm": "{name} đã thích lượt nhắn riêng của bạn", "notification.favourite_pm.name_and_others_with_link": "{name} và {count, plural, other {# người khác}} đã thích lượt nhắn riêng của bạn", - "notification.follow": "{name} theo dõi bạn", + "notification.follow": "{name} đã theo dõi bạn", "notification.follow.name_and_others": "{name} và {count, plural, other {# người khác}} theo dõi bạn", "notification.follow_request": "{name} yêu cầu theo dõi bạn", "notification.follow_request.name_and_others": "{name} và {count, plural, other {# người khác}} đã yêu cầu theo dõi bạn", @@ -764,7 +764,7 @@ "report_notification.open": "Mở báo cáo", "search.no_recent_searches": "Gần đây chưa tìm gì", "search.placeholder": "Tìm kiếm", - "search.quick_action.account_search": "Người có tên {x}", + "search.quick_action.account_search": "Người tên {x}", "search.quick_action.go_to_account": "Xem trang {x}", "search.quick_action.go_to_hashtag": "Xem hashtag {x}", "search.quick_action.open_url": "Mở liên kết trong Mastodon", diff --git a/config/locales/activerecord.cs.yml b/config/locales/activerecord.cs.yml index 6f4fe86e3f..e62c4264dd 100644 --- a/config/locales/activerecord.cs.yml +++ b/config/locales/activerecord.cs.yml @@ -15,9 +15,17 @@ cs: user/invite_request: text: Důvod errors: + attributes: + domain: + invalid: není platný název domény + messages: + invalid_domain_on_line: "%{value} není platný název domény" + too_many_lines: překročil limit %{limit} řádků models: account: attributes: + fields: + fields_with_values_missing_labels: obsahuje hodnoty s chybějícími popisky username: invalid: musí obsahovat pouze písmena, číslice a podtržítka reserved: je vyhrazeno @@ -33,6 +41,11 @@ cs: attributes: data: malformed: je chybný + list_account: + attributes: + account_id: + taken: již je na seznamu + must_be_following: musí být sledovaný účet status: attributes: reblog: diff --git a/config/locales/activerecord.ga.yml b/config/locales/activerecord.ga.yml index 5b36d80d79..c02c537076 100644 --- a/config/locales/activerecord.ga.yml +++ b/config/locales/activerecord.ga.yml @@ -24,6 +24,8 @@ ga: models: account: attributes: + fields: + fields_with_values_missing_labels: ina bhfuil luachanna a bhfuil lipéid in easnamh orthu username: invalid: ní mór go mbeadh litreacha, uimhreacha agus pointí béime amháin reserved: in áirithe diff --git a/config/locales/activerecord.it.yml b/config/locales/activerecord.it.yml index 3e8897577a..6db643408c 100644 --- a/config/locales/activerecord.it.yml +++ b/config/locales/activerecord.it.yml @@ -24,6 +24,8 @@ it: models: account: attributes: + fields: + fields_with_values_missing_labels: contiene valori con label mancanti username: invalid: deve contenere solo lettere, numeri e trattini bassi reserved: è riservato diff --git a/config/locales/activerecord.ne.yml b/config/locales/activerecord.ne.yml index db03c5186b..12795ea20d 100644 --- a/config/locales/activerecord.ne.yml +++ b/config/locales/activerecord.ne.yml @@ -1 +1,35 @@ +--- ne: + activerecord: + attributes: + user: + agreement: सेवा सम्झौता + email: ईमेल ठेगाना + password: पासवर्ड + user/account: + username: प्रयोगकर्ता नाम + user/invite_request: + text: कारण + errors: + attributes: + domain: + invalid: मान्य डोमेन नाम होइन + messages: + invalid_domain_on_line: "%{value} मान्य डोमेन नाम होइन" + models: + account: + attributes: + username: + invalid: अक्षर, संख्या र अन्डरस्कोर मात्र हुनु पर्छ + admin/webhook: + attributes: + url: + invalid: मान्य URL होइन + doorkeeper/application: + attributes: + website: + invalid: मान्य URL होइन + list_account: + attributes: + account_id: + taken: पहिले नै सूचीमा छ diff --git a/config/locales/activerecord.pt-PT.yml b/config/locales/activerecord.pt-PT.yml index 8b17ade2eb..ecbfab3dc4 100644 --- a/config/locales/activerecord.pt-PT.yml +++ b/config/locales/activerecord.pt-PT.yml @@ -27,7 +27,7 @@ pt-PT: fields: fields_with_values_missing_labels: contém valores com etiquetas em falta username: - invalid: deve conter apenas letras, números e traços inferiores + invalid: deve conter apenas letras, números e traços inferiores (_) reserved: está reservado admin/webhook: attributes: diff --git a/config/locales/cs.yml b/config/locales/cs.yml index c9deb24ee1..135975c3c8 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -78,10 +78,10 @@ cs: enabled: Povoleno enabled_msg: Účet %{username} byl úspěšně rozmrazen followers: Sledující - follows: Sledované + follows: Sledovaní header: Záhlaví inbox_url: URL příchozí schránky - invite_request_text: Důvody založení + invite_request_text: Důvody pro připojení invited_by: Pozván uživatelem ip: IP adresa joined: Uživatelem od @@ -187,29 +187,40 @@ cs: confirm_user: Potvrdit uživatele create_account_warning: Vytvořit varování create_announcement: Nové oznámení + create_canonical_email_block: Vytvořit blok e-mailu create_custom_emoji: Vytvořit vlastní emoji create_domain_allow: Vytvořit povolení domény create_domain_block: Vytvořit blokaci domény + create_email_domain_block: Vytvořit blok e-mailové domény create_ip_block: Vytvořit IP pravidlo + create_relay: Vytvořit relay create_unavailable_domain: Vytvořit nedostupnou doménu create_user_role: Vytvořit roli demote_user: Snížit roli uživatele destroy_announcement: Odstranit oznámení + destroy_canonical_email_block: Odblokovat email destroy_custom_emoji: Odstranit vlastní emoji destroy_domain_allow: Odstranit povolení domény destroy_domain_block: Odstranit blokaci domény + destroy_email_domain_block: Smazat blokaci e-mailové domény destroy_instance: Odmazat doménu destroy_ip_block: Smazat IP pravidlo + destroy_relay: Smazat relay destroy_status: Odstranit Příspěvek destroy_unavailable_domain: Smazat nedostupnou doménu destroy_user_role: Zničit roli disable_2fa_user: Vypnout 2FA disable_custom_emoji: Zakázat vlastní emoji + disable_relay: Deaktivovat relay + disable_sign_in_token_auth_user: Zrušit uživatelovo ověřování e-mailovým tokenem disable_user: Deaktivovat uživatele enable_custom_emoji: Povolit vlastní emoji + enable_relay: Aktivovat relay + enable_sign_in_token_auth_user: Povolit uživatelovo ověřování e-mailovým tokenem enable_user: Povolit uživatele memorialize_account: Změna na „in memoriam“ promote_user: Povýšit uživatele + publish_terms_of_service: Zveřejnit smluvní podmínky reject_appeal: Zamítnout odvolání reject_user: Odmítnout uživatele remove_avatar_user: Odstranit avatar @@ -236,36 +247,50 @@ cs: approve_appeal_html: Uživatel %{name} schválil odvolání proti rozhodnutí moderátora %{target} approve_user_html: "%{name} schválil registraci od %{target}" assigned_to_self_report_html: Uživatel %{name} si přidělil hlášení %{target} + change_email_user_html: "%{name} změnil*a e-mailovou adresu %{target}" change_role_user_html: "%{name} změnil roli %{target}" + confirm_user_html: Uživatel %{name} potvrdil e-mailovou adresu uživatele %{target} create_account_warning_html: Uživatel %{name} poslal %{target} varování create_announcement_html: Uživatel %{name} vytvořil nové oznámení %{target} + create_canonical_email_block_html: "%{name} zablokoval e-mail s hashem %{target}" create_custom_emoji_html: Uživatel %{name} nahrál nové emoji %{target} create_domain_allow_html: Uživatel %{name} povolil federaci s doménou %{target} create_domain_block_html: Uživatel %{name} zablokoval doménu %{target} + create_email_domain_block_html: Uživatel %{name} zablokoval e-mailovou doménu %{target} create_ip_block_html: Uživatel %{name} vytvořil pravidlo pro IP %{target} + create_relay_html: "%{name} vytvořil*a relay %{target}" create_unavailable_domain_html: "%{name} zastavil doručování na doménu %{target}" create_user_role_html: "%{name} vytvořil %{target} roli" demote_user_html: Uživatel %{name} degradoval uživatele %{target} destroy_announcement_html: Uživatel %{name} odstranil oznámení %{target} + destroy_canonical_email_block_html: "%{name} odblokoval*a e-mail s hashem %{target}" destroy_custom_emoji_html: "%{name} odstranil emoji %{target}" destroy_domain_allow_html: Uživatel %{name} zakázal federaci s doménou %{target} destroy_domain_block_html: Uživatel %{name} odblokoval doménu %{target} + destroy_email_domain_block_html: "%{name} odblokoval*a e-mailovou doménu %{target}" destroy_instance_html: Uživatel %{name} odmazal doménu %{target} destroy_ip_block_html: Uživatel %{name} odstranil pravidlo pro IP %{target} + destroy_relay_html: "%{name} odstranil*a relay %{target}" destroy_status_html: Uživatel %{name} odstranil příspěvek uživatele %{target} destroy_unavailable_domain_html: "%{name} obnovil doručování na doménu %{target}" destroy_user_role_html: "%{name} odstranil %{target} roli" disable_2fa_user_html: Uživatel %{name} vypnul dvoufázové ověřování pro uživatele %{target} disable_custom_emoji_html: Uživatel %{name} zakázal emoji %{target} + disable_relay_html: "%{name} deaktivoval*a relay %{target}" + disable_sign_in_token_auth_user_html: "%{name} deaktivoval*a ověřování e-mailovým tokenem pro %{target}" disable_user_html: Uživatel %{name} zakázal přihlašování pro uživatele %{target} enable_custom_emoji_html: Uživatel %{name} povolil emoji %{target} + enable_relay_html: "%{name} aktivoval*a relay %{target}" + enable_sign_in_token_auth_user_html: "%{name} aktivoval*a ověřování e-mailovým tokenem pro %{target}" enable_user_html: Uživatel %{name} povolil přihlašování pro uživatele %{target} memorialize_account_html: Uživatel %{name} změnil účet %{target} na „in memoriam“ stránku promote_user_html: Uživatel %{name} povýšil uživatele %{target} + publish_terms_of_service_html: "%{name} zveřejnil*a aktualizace podmínek služby" reject_appeal_html: Uživatel %{name} zamítl odvolání proti rozhodnutí moderátora %{target} reject_user_html: "%{name} odmítl registraci od %{target}" remove_avatar_user_html: Uživatel %{name} odstranil avatar uživatele %{target} reopen_report_html: Uživatel %{name} znovu otevřel hlášení %{target} + resend_user_html: "%{name} znovu odeslal*a potvrzovací e-mail pro %{target}" reset_password_user_html: Uživatel %{name} obnovil heslo uživatele %{target} resolve_report_html: Uživatel %{name} vyřešil hlášení %{target} sensitive_account_html: "%{name} označil média účtu %{target} jako citlivá" @@ -436,6 +461,7 @@ cs: many: "%{count} pokusů o registraci za poslední týden" one: "%{count} pokus o registraci za poslední týden" other: "%{count} pokusů o registraci za poslední týden" + created_msg: E-mailová doména úspěšně zablokována delete: Smazat dns: types: @@ -445,7 +471,9 @@ cs: create: Přidat doménu resolve: Přeložit doménu title: Blokovat novou e-mailovou doménu + no_email_domain_block_selected: Žádné blokace e-mailové domény nebyly změněny, protože žádné nebyly vybrány not_permitted: Nepovoleno + resolved_dns_records_hint_html: Doménové jméno vede na následující MX domény, které mají nakonec na starost přijímání e-mailů. Blokování MX domény zablokuje registrace z jakékoliv e-mailové adresy, která používá stejnou MX doménu, i když je viditelné doménové jméno jiné. Dejte si pozor, abyste nezablokovali velké e-mailové poskytovatele. resolved_through_html: Přeložena přes %{domain} title: Blokované e-mailové domény export_domain_allows: @@ -609,7 +637,9 @@ cs: resolve_description_html: Nebudou učiněny žádné kroky proti nahlášenému účtu, žádný prohřešek zaznamenán a hlášení bude uzavřeno. silence_description_html: Účet bude viditelný pouze těm, kdo jej již sledují nebo si jej ručně vyhledají, což výrazně omezí jeho dosah. Vždy lze vrátit zpět. Uzavře všechna hlášení proti tomuto účtu. suspend_description_html: Účet a veškerý jeho obsah se znepřístupní a bude nakonec smazán, interakce s ním nebude možná. Lze vrátit zpět do 30 dnů. Uzavře všechna hlášení proti tomuto účtu. + actions_description_html: Rozhodněte, který krok učinit pro vyřešení tohoto hlášení. Pokud podniknete kárný krok proti nahlášenému účtu, bude mu zasláno e-mailové oznámení, s výjimkou případu, kdy je zvolena kategorie Spam. actions_description_remote_html: Rozhodněte, co podniknout pro vyřešení tohoto hlášení. Toto ovlivní pouze to, jak váš server komunikuje s tímto vzdáleným účtem, a zpracuje jeho obsah. + actions_no_posts: Toto hlášení nemá žádné související příspěvky k odstranění add_to_report: Přidat do hlášení další already_suspended_badges: local: Již pozastaveno na tomto serveru @@ -673,6 +703,7 @@ cs: delete_data_html: Odstranit profil a obsah @%{acct} ode dneška po 30 dní, pokud mezitím nebude zrušeno jeho pozastavení preview_preamble_html: "@%{acct} obdrží varování s následujícím obsahem:" record_strike_html: Zaznamenat prohřešek @%{acct} pro pomoc s řešením budoucích přestupků z tohoto účtu + send_email_html: Poslat varovný e-mail pro @%{acct} warning_placeholder: Volitelné další odůvodnění moderační akce. target_origin: Původ nahlášeného účtu title: Hlášení @@ -716,6 +747,7 @@ cs: manage_appeals: Spravovat odvolání manage_appeals_description: Umožňuje uživatelům posuzovat odvolání proti moderátorským zásahům manage_blocks: Spravovat blokace + manage_blocks_description: Umožňuje uživatelům blokovat poskytovatele e-mailů a IP adresy manage_custom_emojis: Spravovat vlastní emoji manage_custom_emojis_description: Umožňuje uživatelům spravovat vlastní emoji na serveru manage_federation: Spravovat federaci @@ -733,6 +765,7 @@ cs: manage_taxonomies: Správa taxonomií manage_taxonomies_description: Umožňuje uživatelům zkontrolovat populární obsah a aktualizovat nastavení hashtag manage_user_access: Spravovat uživatelské přístupy + manage_user_access_description: Umožňuje uživatelům rušit jiným uživatelům dvoufázové ověřování, měnit jejich e-mailovou adresu a obnovovat jim hesla manage_users: Spravovat uživatele manage_users_description: Umožňuje uživatelům zobrazit podrobnosti ostatních uživatelů a provádět moderování proti nim manage_webhooks: Spravovat webhooky @@ -807,6 +840,7 @@ cs: destroyed_msg: Upload stránky byl úspěšně smazán! software_updates: critical_update: Kritické — aktualizujte, prosím, co nejdříve + description: Doporučuje se udržovat vaši instalaci Mastodonu aktuální, aby se využily nejnovější opravy a funkce. Kromě toho je někdy velmi důležité včas aktualizovat Mastodon, aby se předešlo bezpečnostním problémům. Z těchto důvodů Mastodon kontroluje aktualizace každých 30 minut a bude vás informovat podle nastavení vašeho e-mailového oznámení. documentation_link: Zjistit více release_notes: Poznámky k vydání title: Dostupné aktualizace @@ -822,6 +856,7 @@ cs: back_to_account: Zpět na stránku účtu back_to_report: Zpět na stránku hlášení batch: + add_to_report: 'Přidat do hlášení #%{id}' remove_from_report: Odebrat z hlášení report: Nahlásit contents: Obsah @@ -833,12 +868,17 @@ cs: media: title: Média metadata: Metadata + no_history: Tento příspěvek nebyl upraven no_status_selected: Nebyly změněny žádné příspěvky, neboť žádné nebyly vybrány open: Otevřít příspěvek original_status: Původní příspěvek reblogs: Boosty + replied_to_html: Odpověděl %{acct_link} status_changed: Příspěvek změněn + status_title: Příspěvek od @%{name} + title: Příspěvky na účtu - @%{name} trending: Populární + view_publicly: Zobrazit veřejně visibility: Viditelnost with_media: S médii strikes: @@ -880,6 +920,9 @@ cs: message_html: Nedefinovali jste žádná pravidla serveru. sidekiq_process_check: message_html: Pro %{value} frontu/fronty neběží žádný Sidekiq proces. Zkontrolujte prosím svou Sidekiq konfiguraci + software_version_check: + action: Zobrazit dostupné aktualizace + message_html: K dispozici je aktualizace Mastodonu. software_version_critical_check: action: Zobrazit dostupné aktualizace message_html: K dispozici je kritická aktualizace Mastodonu, prosím aktualizujte co nejrychleji. @@ -906,19 +949,57 @@ cs: name: Název newest: Nejnovější oldest: Nejstarší + open: Zobrazit veřejně reset: Resetovat review: Stav posouzení search: Hledat title: Hashtagy updated_msg: Nastavení hashtagů bylo úspěšně aktualizováno + terms_of_service: + back: Zpět na podmínky služby + changelog: Co se změnilo + create: Použít vlastní + current: Aktuální + draft: Koncept + generate: Použít šablonu + generates: + action: Vygenerovat + chance_to_review_html: "Vygenerované podmínky služby nebudou zveřejněny automaticky. Výsledky budete mít možnost zkontrolovat. Pro pokračování vyplňte potřebné podrobnosti." + explanation_html: Poskytovaná šablona služeb je určena pouze pro informační účely a neměla by být vykládána jako právní poradenství v jakékoli věci. Prosíme, konzultujte Vaši situaci a konkrétní právní otázky s Vaším pravním zástupcem. + title: Nastavení podmínek služby + history: Historie + live: Živě + no_history: Zatím nejsou zaznamenány žádné změny podmínek služby. + no_terms_of_service_html: Momentálně nemáte nakonfigurovány žádné podmínky služby. Podmínky služby jsou určeny k zajištění jasnosti a ochránit Vás před případnými právními závazky ve sporech s vašimi uživateli. + notified_on_html: Uživatelé upozorněni dne %{date} + notify_users: Upozornit uživatele + preview: + explanation_html: 'E-mail bude poslán %{display_count} uživatelům, kteří se zaregistrovali před datem %{date}. Následující text bude obsažen v onom e-mailu:' + send_preview: Poslat náhled na %{email} + send_to_all: + few: Poslat %{display_count} emaily + many: Poslat %{display_count} emailů + one: Poslat %{display_count} email + other: Poslat %{display_count} emailů + title: Náhled oznámení o podmínkách služby + publish: Zveřejnit + published_on_html: Zveřejněno %{date} + save_draft: Uložit koncept + title: Podmínky služby title: Administrace trends: allow: Povolit approved: Schválené + confirm_allow: Opravdu chcete povolit vybrané štítky? + confirm_disallow: Opravdu chcete zakázat vybrané štítky? disallow: Zakázat links: allow: Povolit odkaz allow_provider: Povolit vydavatele + confirm_allow: Jste si jist, že chcete povolit vybrané odkazy? + confirm_allow_provider: Opravdu chcete povolit vybrané poskytovatele? + confirm_disallow: Opravdu chcete zakázat vybrané odkazy? + confirm_disallow_provider: Opravdu chcete zakázat vybrané poskytovatele? description_html: Toto jsou odkazy, které jsou momentálně hojně sdíleny účty, jejichž příspěvky váš server vidí. To může pomoct vašim uživatelům zjistit, co se děje ve světě. Žádné odkazy se nezobrazují veřejně, dokud neschválíte vydavatele. Můžete také povolit nebo zamítnout jednotlivé odkazy. disallow: Zakázat odkaz disallow_provider: Zakázat vydavatele @@ -944,6 +1025,10 @@ cs: statuses: allow: Povolit příspěvek allow_account: Povolit autora + confirm_allow: Opravdu chcete povolit vybrané tooty? + confirm_allow_account: Opravdu chcete povolit vybrané účty? + confirm_disallow: Opravdu chcete zakázat vybrané tooty? + confirm_disallow_account: Opravdu chcete zakázat vybrané účty? description_html: Toto jsou příspěvky, o kterých váš server ví, že jsou momentálně hodně sdíleny a oblibovány. To může pomoci vašim novým i vracejícím se uživatelům najít další lidi ke sledování. Žádné příspěvky se nezobrazují veřejně, dokud neschválíte autora a tento autor nepovolí navrhování svého účtu ostatním. Můžete také povolit či zamítnout jednotlivé příspěvky. disallow: Zakázat příspěvek disallow_account: Zakázat autora @@ -980,6 +1065,7 @@ cs: many: Použit %{count} lidmi za poslední týden one: Použit jedním člověkem za poslední týden other: Použit %{count} lidmi za poslední týden + title: Doporučení & Trendy trending: Populární warning_presets: add_new: Přidat nové @@ -1066,7 +1152,9 @@ cs: guide_link_text: Zapojit se může každý. sensitive_content: Citlivý obsah application_mailer: + notification_preferences: Změnit předvolby e-mailu salutation: "%{name}," + settings: 'Změnit předvolby e-mailu: %{link}' unsubscribe: Přestat odebírat view: 'Zobrazit:' view_profile: Zobrazit profil @@ -1086,6 +1174,7 @@ cs: hint_html: Ještě jedna věc! Musíme potvrdit, že jste člověk (to proto, abychom drželi stranou spam!). Vyřešte CAPTCHA níže a klikněte na "Pokračovat". title: Bezpečnostní kontrola confirmations: + awaiting_review: Vaše e-mailová adresa je potvrzena! Personál %{domain} nyní kontrolují vaši registraci. Pokud váš účet schválí, obdržíte e-mail! awaiting_review_title: Vaše registrace se ověřuje clicking_this_link: kliknutím na tento odkaz login_link: přihlásit se @@ -1093,6 +1182,7 @@ cs: redirect_to_app_html: Měli byste být přesměrováni do aplikace %{app_name}. Pokud se tak nestalo, zkuste %{clicking_this_link} nebo ručně se vrátit do aplikace. registration_complete: Vaše registrace na %{domain} je hotová! welcome_title: Vítám uživatele %{name}! + wrong_email_hint: Pokud není tento e-mail správný, můžete si ho změnit v nastavení účtu. delete_account: Odstranit účet delete_account_html: Chcete-li odstranit svůj účet, pokračujte zde. Budete požádáni o potvrzení. description: @@ -1112,6 +1202,7 @@ cs: migrate_account_html: Zde můžete nastavit přesměrování tohoto účtu na jiný. or_log_in_with: Nebo se přihlaste pomocí progress: + confirm: Potvrdit e-mail details: Vaše údaje review: Naše hodnocení rules: Přijmout pravidla @@ -1133,22 +1224,37 @@ cs: security: Zabezpečení set_new_password: Nastavit nové heslo setup: + email_below_hint_html: Zkontrolujte složku se spamem, nebo požádejte o další. Svou e-mailovou adresu si můžete opravit, pokud je špatně. + email_settings_hint_html: Kliknutím na odkaz, který jsme poslali do %{email}, začnete používat Mastodon. Budeme tu čekat. link_not_received: Nedostali jste odkaz? + new_confirmation_instructions_sent: Za několik minut obdržíte nový e-mail s potvrzovacím odkazem! title: Zkontrolujte doručenou poštu sign_in: preamble_html: Přihlaste se svými %{domain} údaji. Pokud je váš účet hostován na jiném serveru, přihlásit se zde nemůžete. title: Přihlásit se k %{domain} sign_up: manual_review: Registrace na %{domain} procházejí manuálním hodnocením od našich moderátorů. Abyste nám pomohli zpracovat Vaši registraci, napište trochu o sobě a proč chcete účet na %{domain}. + preamble: S účtem na tomto Mastodon serveru budete moci sledovat jakoukoli jinou osobu na fediversu, bez ohledu na to, kde je jejich účet hostován. title: Pojďme vás nastavit na %{domain}. status: account_status: Stav účtu + confirming: Čekáme na dokončení potvrzení e-mailu. functional: Váš účet je plně funkční. + pending: Vaše žádost čeká na posouzení naším personálem. To může nějakou dobu trvat. Pokud bude váš požadavek schválen, obdržíte e-mail. redirecting_to: Váš účet je neaktivní, protože je právě přesměrován na účet %{acct}. self_destruct: Protože %{domain} končí, budete mít k účtu jen omezený přístup. view_strikes: Zobrazit minulé prohřešky vašeho účtu too_fast: Formulář byl odeslán příliš rychle, zkuste to znovu. use_security_key: Použít bezpečnostní klíč + user_agreement_html: Přečetl jsem si a souhlasím s podmínkami služby a ochranou osobních údajů + author_attribution: + example_title: Ukázkový text + hint_html: Píšete novinové články nebo blog mimo Mastodon? Kontrolujte, jak Vám bude připisováno autorství, když jsou sdíleny na Mastodonu. + instructions: 'Ujistěte se, že tento kód je v HTML vašeho článku:' + more_from_html: Více od %{name} + s_blog: Blog %{name} + then_instructions: Poté přidejte název domény publikace do níže uvedeného pole. + title: Připisování autorství challenge: confirm: Pokračovat hint_html: "Tip: Po dobu jedné hodiny vás o heslo nebudeme znovu žádat." @@ -1185,6 +1291,9 @@ cs: before: 'Před pokračováním si prosím pečlivě přečtěte tyto poznámky:' caches: Obsah, který byl uložen do cache jiných serverů, nemusí být smazán data_removal: Vaše příspěvky a další data budou trvale smazána + email_change_html: Můžete změnit svou e-mailovou adresu bez odstranění svého účtu + email_contact_html: Pokud stále nedorazí, můžete poslat e-mail %{email} pro pomoc + email_reconfirmation_html: Pokud neobdržíte potvrzovací e-mail, můžete si ho vyžádat znovu irreversible: Váš účet nebude možné obnovit ani znovu aktivovat more_details_html: Podrobnosti najdete v zásadách ochrany osobních údajů. username_available: Vaše uživatelské jméno bude opět dostupné @@ -1356,6 +1465,68 @@ cs: merge_long: Ponechat existující záznamy a přidat nové overwrite: Přepsat overwrite_long: Nahradit aktuální záznamy novými + overwrite_preambles: + blocking_html: + few: Chystáte se nahradit váš seznam bloků s %{count} účty z %{filename}. + many: Chystáte se nahradit váš seznam bloků s %{count} účty z %{filename}. + one: Chystáte se nahradit váš seznam bloků s %{count} účtem z %{filename}. + other: Chystáte se nahradit váš seznam bloků s %{count} účty z %{filename}. + bookmarks_html: + few: Chystáte se nahradit své záložky s %{count} příspěvky z %{filename}. + many: Chystáte se nahradit své záložky s %{count} příspěvky z %{filename}. + one: Chystáte se nahradit své záložky s %{count} příspěvkem z %{filename}. + other: Chystáte se nahradit své záložky s %{count} příspěvky z %{filename}. + domain_blocking_html: + few: Chystáte se nahradit Váš seznam zablokovaných domén s %{count} stránky z %{filename}. + many: Chystáte se nahradit Váš seznam zablokovaných domén s %{count} stránky z %{filename}. + one: Chystáte se nahradit Váš seznam zablokovaných domén s %{count} stránkou z %{filename}. + other: Chystáte se nahradit Váš seznam zablokovaných domén s %{count} stránky z %{filename}. + following_html: + few: Chystáte se sledovat%{count} účty z %{filename} a přestanete sledovat kohokoliv jiného. + many: Chystáte se sledovat%{count} účtů z %{filename} a přestanete sledovat kohokoliv jiného. + one: Chystáte se sledovat%{count} účet z %{filename} a přestanete sledovat kohokoliv jiného. + other: Chystáte se sledovat%{count} účtů z %{filename} a přestanete sledovat kohokoliv jiného. + lists_html: + few: Chystáte se nahradit své seznamy obsahem %{filename}. Až %{count} účty budou přidány do nových seznamů. + many: Chystáte se nahradit své seznamy obsahem %{filename}. Až %{count} účtů bude přidáno do nových seznamů. + one: Chystáte se nahradit své seznamy obsahem %{filename}. Až %{count} účet bude přidán do nových seznamů. + other: Chystáte se nahradit své seznamy obsahem %{filename}. Až %{count} účtů bude přidáno do nových seznamů. + muting_html: + few: Chystáte se nahradit svůj seznam ztišených účtů s %{count} účty z %{filename}. + many: Chystáte se nahradit svůj seznam ztišených účtů s %{count} účty z %{filename}. + one: Chystáte se nahradit svůj seznam ztišených účtů s %{count} účtem z %{filename}. + other: Chystáte se nahradit svůj seznam ztišených účtů s %{count} účty z %{filename}. + preambles: + blocking_html: + few: Chystáte se zablokovat%{count} účty z %{filename}. + many: Chystáte se zablokovat%{count} účtů z %{filename}. + one: Chystáte se zablokovat%{count} účet z %{filename}. + other: Chystáte se zablokovat%{count} účtů z %{filename}. + bookmarks_html: + few: Chystáte se přidat až %{count} příspěvky z %{filename} do vašich záložek. + many: Chystáte se přidat až %{count} příspěvků z %{filename} do vašich záložek. + one: Chystáte se přidat až %{count} příspěvek z %{filename} do vašich záložek. + other: Chystáte se přidat až %{count} příspěvků z %{filename} do vašich záložek. + domain_blocking_html: + few: Chystáte se zablokovat%{count} domény z %{filename}. + many: Chystáte se zablokovat%{count} domén z %{filename}. + one: Chystáte se zablokovat%{count} doménu z %{filename}. + other: Chystáte se zablokovat%{count} domén z %{filename}. + following_html: + few: Chystáte se sledovat%{count} účty z %{filename}. + many: Chystáte se sledovat%{count} účtů z %{filename}. + one: Chystáte se sledovat%{count} účet z %{filename}. + other: Chystáte se sledovat%{count} účtů z %{filename}. + lists_html: + few: Chystáte se přidat %{count} účty z %{filename} do vaších seznamů. Nové seznamy budou vytvořeny, pokud neexistuje žádný seznam, do kterého by je bylo možné přidat. + many: Chystáte se přidat %{count} účtů z %{filename} do vaších seznamů. Nové seznamy budou vytvořeny, pokud neexistuje žádný seznam, do kterého by je bylo možné přidat. + one: Chystáte se přidat %{count} účet z %{filename} do vaších seznamů. Nové seznamy budou vytvořeny, pokud neexistuje žádný seznam, do kterého by jej bylo možné přidat. + other: Chystáte se přidat %{count} účtů z %{filename} do vaších seznamů. Nové seznamy budou vytvořeny, pokud neexistuje žádný seznam, do kterého by je bylo možné přidat. + muting_html: + few: Chystáte se ztišit%{count} účty z %{filename}. + many: Chystáte se ztišit%{count} účtů z %{filename}. + one: Chystáte se ztišit%{count} účet z %{filename}. + other: Chystáte se ztišit%{count} účtů z %{filename}. preface: Můžete importovat data, která jste exportovali z jiného serveru, jako například seznam lidí, které sledujete či blokujete. recent_imports: Nedávné importy states: @@ -1417,6 +1588,7 @@ cs: authentication_methods: otp: aplikací pro dvoufaktorové ověření password: heslem + sign_in_token: bezpečnostní kód e-mailu webauthn: bezpečnostními klíči description_html: Pokud vidíte aktivitu, kterou nepoznáváte, zvažte změnu hesla a zapnutí dvoufaktorového ověřování. empty: Není k dispozici žádná historie přihlášení @@ -1427,10 +1599,21 @@ cs: unsubscribe: action: Ano, odeberte odběr complete: Odběr byl odhlášen + confirmation_html: Jste si jisti, že chcete odhlásit odběr %{type} pro Mastodon na %{domain} na váš e-mail %{email}? Vždy se můžete znovu přihlásit ve svém nastavení e-mailových oznámení. + emails: + notification_emails: + favourite: e-mailové oznámení při oblíbení + follow: e-mailové oznámení při sledování + follow_request: e-mail při žádost o sledování + mention: e-mailové oznámení při zmínění + reblog: e-mailové oznámení při boostu + resubscribe_html: Pokud jste se odhlásili omylem, můžete se znovu přihlásit ve svých nastavení e-mailových oznámení. + success_html: Již nebudete dostávat %{type} pro Mastodon na %{domain} na vaši e-mailovou adresu %{email}. title: Odhlásit odběr media_attachments: validations: images_and_video: K příspěvku, který již obsahuje obrázky, nelze připojit video + not_found: Média %{ids} nebyla nalezena nebo již byla připojena k jinému příspěvku not_ready: Nelze připojit soubory před jejich zpracováním. Zkuste to znovu za chvíli! too_many: Nelze připojit více než 4 soubory migrations: @@ -1507,6 +1690,8 @@ cs: update: subject: Uživatel %{name} upravil příspěvek notifications: + administration_emails: E-mailová oznámení administrátora + email_events: Události pro e-mailová oznámení email_events_hint: 'Vyberte události, pro které chcete dostávat oznámení:' number: human: @@ -1600,6 +1785,7 @@ cs: scheduled_statuses: over_daily_limit: Pro dnešek jste překročili limit %{limit} naplánovaných příspěvků over_total_limit: Překročili jste limit %{limit} naplánovaných příspěvků + too_soon: datum musí být v budoucnu self_destruct: lead_html: "%{domain} bohužel končí nadobro. Pokud jste tam měli účet, nebudete jej moci dále používat, ale stále si můžete vyžádat zálohu vašich dat." title: Tento server končí @@ -1659,10 +1845,12 @@ cs: delete: Smazání účtu development: Vývoj edit_profile: Upravit profil + export: Export featured_tags: Zvýrazněné hashtagy import: Import import_and_export: Import a export migrate: Přesun účtu + notifications: Emailové oznámení preferences: Předvolby profile: Profil relationships: Sledovaní a sledující @@ -1768,6 +1956,8 @@ cs: too_late: Na odvolání proti tomuto prohřešku už je pozdě tags: does_not_match_previous_name: se neshoduje s předchozím názvem + terms_of_service: + title: Podmínky služby themes: contrast: Mastodon (vysoký kontrast) default: Mastodon (tmavý) @@ -1828,6 +2018,15 @@ cs: further_actions_html: Pokud jste to nebyli vy, doporučujeme pro zabezpečení vašeho účtu okamžitě %{action} a zapnout dvoufázové ověřování. subject: Váš účet byl použit z nové IP adresy title: Nové přihlášení + terms_of_service_changed: + agreement: Pokračováním v používání %{domain} souhlasíte s těmito podmínkami. Pokud nesouhlasíte s aktualizovanými podmínkami, můžete svůj souhlas s %{domain} kdykoliv ukončit odstraněním vašeho účtu. + changelog: 'Ve zkratce, zde je to, co tato změna znamená pro vás:' + description: 'Tento e-mail jste obdrželi, protože na %{domain} provádíme určité změny našich smluvních podmínek. Doporučujeme vám zkontrolovat aktualizované podmínky v plném znění zde:' + description_html: Tento e-mail jste obdrželi, protože na %{domain} provádíme určité změny našich smluvních podmínek. Doporučujeme Vám zkontrolovat aktualizované termíny v plném znění zde. + sign_off: Tým %{domain} + subject: Aktualizace našich podmínek služby + subtitle: Podmínky služby %{domain} se mění + title: Důležitá aktualizace warning: appeal: Podat odvolání appeal_description: Pokud se domníváte, že se jedná o chybu, můžete podat odvolání personálu %{instance}. @@ -1908,6 +2107,7 @@ cs: invalid_otp_token: Neplatný kód pro dvoufázové ověřování otp_lost_help_html: Pokud jste ztratili přístup k oběma, spojte se s %{email} rate_limited: Příliš mnoho pokusů o ověření, zkuste to znovu později. + seamless_external_login: Jste přihlášeni přes externí službu, nastavení hesla a e-mailu proto nejsou dostupná. signed_in_as: 'Přihlášeni jako:' verification: extra_instructions_html: Tip: Odkaz na vaší webové stránce může být neviditelný. Důležitou součástí je rel="me", která brání proti napodování vás na webových stránkách s obsahem vytvořeným uživatelem. Můžete dokonce použít odkaz v záhlaví stránky místo a, ale HTML musí být přístupné bez spuštění JavaScript. @@ -1916,6 +2116,7 @@ cs: instructions_html: Zkopírujte a vložte níže uvedený kód do HTML vašeho webu. Poté přidejte adresu vašeho webu do jednoho z extra políček na vašem profilu na záložce "Upravit profil" a uložte změny. verification: Ověření verified_links: Vaše ověřené odkazy + website_verification: Ověření webové stránky webauthn_credentials: add: Přidat nový bezpečnostní klíč create: diff --git a/config/locales/eo.yml b/config/locales/eo.yml index c456a424dc..ee45921aaa 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -937,9 +937,13 @@ eo: generates: action: Generi title: Agordo de kondiĉoj de uzado + history: Historio + live: Antaŭmontro no_history: Ankoraŭ ne estas registritaj ŝanĝoj de la kondiĉoj de la servo. no_terms_of_service_html: Vi nuntempe ne havas iujn ajn kondiĉojn de la servo agordita. La kondiĉoj de la servo celas doni klarecon kaj protekti vin kontraŭ eblaj respondecoj en disputoj kun viaj uzantoj. + notify_users: Informu uzantojn preview: + send_preview: Sendu antaŭrigardon al %{email} send_to_all: one: Sendi %{display_count} retpoŝton other: Sendi %{display_count} retpoŝtojn @@ -1926,6 +1930,8 @@ eo: subject: Via konto estas alirita de nova IP-adreso title: Nova saluto terms_of_service_changed: + description: 'Vi ricevas ĉi tiun retmesaĝon ĉar ni faras iujn ŝanĝojn al niaj servokondiĉoj ĉe %{domain}. Ni instigas vin revizii la ĝisdatigitajn kondiĉojn tute ĉi tie:' + description_html: Vi ricevas ĉi tiun retmesaĝon ĉar ni faras iujn ŝanĝojn al niaj servokondiĉoj ĉe %{domain}. Ni instigas vin revizii la ĝisdatigitajn kondiĉojn plene ĉi tie. sign_off: La teamo de %{domain} subject: Ĝisdatigoj al niaj kondiĉoj de servo subtitle: La kondiĉoj de la servo de %{domain} ŝanĝiĝas diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 0110fe540d..83a9674f84 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -956,6 +956,8 @@ pl: updated_msg: Pomyślnie uaktualniono ustawienia hashtagów terms_of_service: draft: Szkic + generate: Użyj szablonu + save_draft: Zapisz wersję roboczą title: Administracja trends: allow: Zezwól @@ -1214,6 +1216,7 @@ pl: view_strikes: Zobacz dawne ostrzeżenia nałożone na twoje konto too_fast: Zbyt szybko przesłano formularz, spróbuj ponownie. use_security_key: Użyj klucza bezpieczeństwa + user_agreement_html: Przeczytałem i akceptuję warunki korzystania z usługi oraz politykę prywatności author_attribution: example_title: Przykładowy tekst hint_html: Piszesz wiadomości albo bloga poza Mastodonem? Kontroluj jak będą ci przypisywane podczas dizielenia się nimi na Mastodonie. diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index bba71f165d..7dcf0977f3 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -352,7 +352,7 @@ pt-PT: not_permitted: Não estás autorizado a executar esta ação overwrite: Substituir shortcode: Código de atalho - shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e traços inferiores + shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e traços inferiores (_) title: Emojis personalizados uncategorized: Não categorizados unlist: Não listar @@ -1826,8 +1826,8 @@ pt-PT: private_long: Mostrar só aos seguidores public: Público public_long: Todos podem ver - unlisted: Não inventariado - unlisted_long: Todos podem ver, mas não será inventariado nas cronologias públicas + unlisted: Não listado + unlisted_long: Todos podem ver, mas não aparecerá nas cronologias públicas statuses_cleanup: enabled: Eliminar publicações antigas automaticamente enabled_hint: Elimina automaticamente as tuas publicações assim que atingirem um certo limite de tempo, a não ser que correspondam a uma das seguintes exceções @@ -1945,7 +1945,7 @@ pt-PT: appeal: Submeter uma contestação appeal_description: Se achas que isto é um erro, podes submeter uma contestação para a equipa de %{instance}. categories: - spam: Spam + spam: Publicidade indesejada / spam violation: O conteúdo infringe as seguintes diretrizes da comunidade explanation: delete_statuses: Algumas das tuas mensagens foram consideradas como violando uma ou mais diretrizes da comunidade e foram subsequentemente removidas pelos moderadores do %{instance}. diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml index 0bfeacd9cd..22b92a4434 100644 --- a/config/locales/simple_form.cs.yml +++ b/config/locales/simple_form.cs.yml @@ -3,12 +3,14 @@ cs: simple_form: hints: account: + attribution_domains_as_text: Jeden na řádek. Chrání před falešným připisování autorství. discoverable: Vaše veřejné příspěvky a profil mohou být zobrazeny nebo doporučeny v různých oblastech Mastodonu a váš profil může být navrhován ostatním uživatelům. display_name: Vaše celé jméno nebo přezdívka. fields: Vaše domovská stránka, zájmena, věk, cokoliv chcete. indexable: Vaše veřejné příspěvky se mohou objevit ve výsledcích vyhledávání na Mastodonu. Lidé, kteří s vašimi příspěvky interagovaly, je mohou stále vyhledávat. note: 'Můžete @zmínit jiné osoby nebo #hashtagy.' show_collections: Lidé budou moci procházet skrz sledující. Lidé, které sledujete, uvidí, že je sledujete bezohledně. + unlocked: Lidé vás budou moci sledovat, aniž by vás žádali o schválení. Zrušte zaškrtnutí, pokud chcete kontrolovat požadavky o sledování a vybírat si, zda přijmete nebo odmítnete nové sledující. account_alias: acct: Zadejte svůj účet, ze kterého se chcete přesunout jinam, ve formátu přezdívka@doména account_migration: @@ -58,6 +60,7 @@ cs: setting_display_media_default: Skrývat média označená jako citlivá setting_display_media_hide_all: Vždy skrývat média setting_display_media_show_all: Vždy zobrazovat média + setting_system_scrollbars_ui: Platí pouze pro desktopové prohlížeče založené na Safari nebo Chrome setting_use_blurhash: Gradienty jsou vytvořeny na základě barvev skrytých médií, ale zakrývají veškeré detaily setting_use_pending_items: Aktualizovat časovou osu až po kliknutí namísto automatického rolování kanálu username: Pouze písmena, číslice a podtržítka @@ -130,8 +133,17 @@ cs: terms_of_service: changelog: Může být strukturováno pomocí Markdown syntaxu. text: Může být strukturováno pomocí Markdown syntaxu. + terms_of_service_generator: + admin_email: Právní oznámení zahrnují protioznámení, soudní příkazy, žádosti o stáhnutí příspěvků a žádosti od právních vymahačů. + arbitration_address: Může být stejné jako výše uvedená fyzická adresa, nebo „N/A“, pokud používáte e-mail + arbitration_website: Může být webový formulář nebo „N/A“, pokud používáte e-mail + dmca_address: V případě provozovatelů v USA použijte adresu zapsanou v DMCA Designated Agent Directory. Na přímou žádost je možné použít namísto domovské adresy PO box, použijte DMCA Designated Agent Post Office Box Waiver Request k e-mailovaní Copyright Office a řekněte jim, že jste malým moderátorem obsahu, který se obavá pomsty nabo odplaty za Vaši práci a potřebujete použít PO box, abyste schovali Vaši domovskou adresu před širokou veřejností. + dmca_email: Může být stejný e-mail použitý pro „E-mail adresy pro právní upozornění“ + domain: Jedinečná identifikace online služby, kterou poskytujete. + jurisdiction: Uveďte zemi, kde žije ten, kdo platí účty. Pokud je to společnost nebo jiný subjekt, uveďte zemi, v níž je zapsán do obchodního rejstříku, a město, region, území, případně stát, pokud je to povinné. user: chosen_languages: Po zaškrtnutí budou ve veřejných časových osách zobrazeny pouze příspěvky ve zvolených jazycích + role: Role určuje, která oprávnění uživatel má. user_role: color: Barva, která má být použita pro roli v celém UI, jako RGB v hex formátu highlighted: Toto roli učiní veřejně viditelnou @@ -144,6 +156,7 @@ cs: url: Kam budou události odesílány labels: account: + attribution_domains_as_text: Webové stránky s povolením Vám připsat autorství discoverable: Zobrazovat profil a příspěvky ve vyhledávacích algoritmech fields: name: Označení @@ -222,6 +235,7 @@ cs: setting_hide_network: Skrýt mou síť setting_reduce_motion: Omezit pohyb v animacích setting_system_font_ui: Použít výchozí písmo systému + setting_system_scrollbars_ui: Použít výchozí posuvník systému setting_theme: Vzhled stránky setting_trends: Zobrazit dnešní trendy setting_unfollow_modal: Před zrušením sledování zobrazovat potvrzovací okno diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index be2ed2135e..59fad146d4 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -140,6 +140,7 @@ eo: dmca_address: Por tenantoj en Usono, uzu la adreson registritan en la DMCA Designated Agenŭ Directory. Registrolibro de poŝtskatoloj haveblas per direkta postulo, uzu la DMCA Designated Agent Post Office Box Waiver Request por retpoŝti la Ofico de Kopirajto kaj priskribu, ke vi estas hejm-trovigita administranto por enhavo kaj devas uzi Poŝtskatolon por forigi vian hejmadreson de publika vido. dmca_email: Povas esti la sama retpoŝtadreso uzita por "Retpoŝtadreso por legalaj sciigoj" supre domain: Unika identigilo de la retaj servicoj, ke vi provizas. + jurisdiction: Enlistigu la landon, kie loĝas kiu pagas la fakturojn. Se ĝi estas kompanio aŭ alia ento, listigu la landon, kie ĝi estas enkorpigita, kaj la urbon, regionon, teritorion aŭ ŝtaton laŭeble. user: chosen_languages: Kun tio markita nur mesaĝoj en elektitaj lingvoj aperos en publikaj tempolinioj role: La rolo kontrolas kiujn permesojn la uzanto havas. @@ -333,7 +334,13 @@ eo: changelog: Kio ŝanĝiĝis? text: Kondiĉoj de uzado terms_of_service_generator: + admin_email: Retpoŝtadreso por laŭleĝaj avizoj + arbitration_address: Fizika adreso por arbitraciaj avizoj + arbitration_website: Retejo por sendi arbitraciajn avizojn + dmca_address: Fizika adreso por DMCA/kopirajto-avizoj + dmca_email: Retpoŝtadreso por DMCA/kopirajto-avizoj domain: Domajno + jurisdiction: Laŭleĝa jurisdikcio user: role: Rolo time_zone: Horzono diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml index f5a7e3a27e..207d77f38b 100644 --- a/config/locales/simple_form.pt-PT.yml +++ b/config/locales/simple_form.pt-PT.yml @@ -63,7 +63,7 @@ pt-PT: setting_system_scrollbars_ui: Aplica-se apenas a navegadores de desktop baseados no Safari e Chrome setting_use_blurhash: Os gradientes são baseados nas cores das imagens escondidas, mas ofuscam quaisquer pormenores setting_use_pending_items: Ocultar as atualizações da cronologia após um clique em vez de percorrer automaticamente a cronologia - username: Pode utilizar letras, números e sublinhados + username: Pode utilizar letras, números e traços inferiores (_) whole_word: Quando a palavra-chave ou expressão-chave é somente alfanumérica, ela só será aplicada se corresponder à palavra completa domain_allow: domain: Este domínio será capaz de obter dados desta instância e os dados dele recebidos serão processados e armazenados From d517fa5ab7979bda3a316b62563b244d6572fbd9 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 13 Jan 2025 10:39:05 +0100 Subject: [PATCH 097/133] Change ActivityPub path generation to all happen in `ActivityPub::TagManager` (#33527) --- .../activitypub/collections_controller.rb | 2 +- .../activitypub/outboxes_controller.rb | 8 +--- .../follower_accounts_controller.rb | 2 +- app/lib/activitypub/tag_manager.rb | 34 +++++++++++++-- .../activitypub/actor_serializer.rb | 14 +++---- app/serializers/activitypub/add_serializer.rb | 2 +- .../activitypub/remove_serializer.rb | 2 +- app/serializers/webfinger_serializer.rb | 4 +- spec/lib/activitypub/tag_manager_spec.rb | 41 +++++++++++++++++-- 9 files changed, 82 insertions(+), 27 deletions(-) diff --git a/app/controllers/activitypub/collections_controller.rb b/app/controllers/activitypub/collections_controller.rb index ab1b98e646..c80db3500d 100644 --- a/app/controllers/activitypub/collections_controller.rb +++ b/app/controllers/activitypub/collections_controller.rb @@ -49,7 +49,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController def collection_presenter ActivityPub::CollectionPresenter.new( - id: account_collection_url(@account, params[:id]), + id: ActivityPub::TagManager.instance.collection_uri_for(@account, params[:id]), type: @type, size: @size, items: @items diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb index 0c995edbf8..a9476b806f 100644 --- a/app/controllers/activitypub/outboxes_controller.rb +++ b/app/controllers/activitypub/outboxes_controller.rb @@ -41,12 +41,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController end end - def outbox_url(**) - if params[:account_username].present? - account_outbox_url(@account, **) - else - instance_actor_outbox_url(**) - end + def outbox_url(...) + ActivityPub::TagManager.instance.outbox_uri_for(@account, ...) end def next_page diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb index 5effd9495e..f4c7b37088 100644 --- a/app/controllers/follower_accounts_controller.rb +++ b/app/controllers/follower_accounts_controller.rb @@ -46,7 +46,7 @@ class FollowerAccountsController < ApplicationController end def page_url(page) - account_followers_url(@account, page: page) unless page.nil? + ActivityPub::TagManager.instance.followers_uri_for(@account, page: page) unless page.nil? end def next_page_url diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index a489928407..5cba9a8006 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -86,8 +86,34 @@ class ActivityPub::TagManager account_status_shares_url(target.account, target) end - def followers_uri_for(target) - target.local? ? account_followers_url(target) : target.followers_url.presence + def following_uri_for(target, ...) + raise ArgumentError, 'target must be a local account' unless target.local? + + account_following_index_url(target, ...) + end + + def followers_uri_for(target, ...) + return target.followers_url.presence unless target.local? + + account_followers_url(target, ...) + end + + def collection_uri_for(target, ...) + raise NotImplementedError unless target.local? + + account_collection_url(target, ...) + end + + def inbox_uri_for(target) + raise NotImplementedError unless target.local? + + target.instance_actor? ? instance_actor_inbox_url : account_inbox_url(target) + end + + def outbox_uri_for(target, ...) + raise NotImplementedError unless target.local? + + target.instance_actor? ? instance_actor_outbox_url(...) : account_outbox_url(target, ...) end # Primary audience of a status @@ -99,7 +125,7 @@ class ActivityPub::TagManager when 'public' [COLLECTIONS[:public]] when 'unlisted', 'private' - [account_followers_url(status.account)] + [followers_uri_for(status.account)] when 'direct', 'limited' if status.account.silenced? # Only notify followers if the account is locally silenced @@ -133,7 +159,7 @@ class ActivityPub::TagManager case status.visibility when 'public' - cc << account_followers_url(status.account) + cc << followers_uri_for(status.account) when 'unlisted' cc << COLLECTIONS[:public] end diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index f698e758e8..ed90fa428f 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -44,7 +44,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer delegate :suspended?, :instance_actor?, to: :object def id - object.instance_actor? ? instance_actor_url : account_url(object) + ActivityPub::TagManager.instance.uri_for(object) end def type @@ -60,27 +60,27 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer end def following - account_following_index_url(object) + ActivityPub::TagManager.instance.following_uri_for(object) end def followers - account_followers_url(object) + ActivityPub::TagManager.instance.followers_uri_for(object) end def inbox - object.instance_actor? ? instance_actor_inbox_url : account_inbox_url(object) + ActivityPub::TagManager.instance.inbox_uri_for(object) end def outbox - object.instance_actor? ? instance_actor_outbox_url : account_outbox_url(object) + ActivityPub::TagManager.instance.outbox_uri_for(object) end def featured - account_collection_url(object, :featured) + ActivityPub::TagManager.instance.collection_uri_for(object, :featured) end def featured_tags - account_collection_url(object, :tags) + ActivityPub::TagManager.instance.collection_uri_for(object, :tags) end def endpoints diff --git a/app/serializers/activitypub/add_serializer.rb b/app/serializers/activitypub/add_serializer.rb index 436b05086f..640d774272 100644 --- a/app/serializers/activitypub/add_serializer.rb +++ b/app/serializers/activitypub/add_serializer.rb @@ -38,6 +38,6 @@ class ActivityPub::AddSerializer < ActivityPub::Serializer end def target - account_collection_url(object.account, :featured) + ActivityPub::TagManager.instance.collection_uri_for(object.account, :featured) end end diff --git a/app/serializers/activitypub/remove_serializer.rb b/app/serializers/activitypub/remove_serializer.rb index fb224f8a99..4f78804031 100644 --- a/app/serializers/activitypub/remove_serializer.rb +++ b/app/serializers/activitypub/remove_serializer.rb @@ -38,6 +38,6 @@ class ActivityPub::RemoveSerializer < ActivityPub::Serializer end def target - account_collection_url(object.account, :featured) + ActivityPub::TagManager.instance.collection_uri_for(object.account, :featured) end end diff --git a/app/serializers/webfinger_serializer.rb b/app/serializers/webfinger_serializer.rb index b67cd2771a..1cb3175c07 100644 --- a/app/serializers/webfinger_serializer.rb +++ b/app/serializers/webfinger_serializer.rb @@ -13,7 +13,7 @@ class WebfingerSerializer < ActiveModel::Serializer if object.instance_actor? [instance_actor_url] else - [short_account_url(object), account_url(object)] + [short_account_url(object), ActivityPub::TagManager.instance.uri_for(object)] end end @@ -43,6 +43,6 @@ class WebfingerSerializer < ActiveModel::Serializer end def self_href - object.instance_actor? ? instance_actor_url : account_url(object) + ActivityPub::TagManager.instance.uri_for(object) end end diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb index 05d2609b0d..4dcfc69824 100644 --- a/spec/lib/activitypub/tag_manager_spec.rb +++ b/spec/lib/activitypub/tag_manager_spec.rb @@ -7,17 +7,50 @@ RSpec.describe ActivityPub::TagManager do subject { described_class.instance } + let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" } + + describe '#public_collection?' do + it 'returns true for the special public collection and common shorthands' do + expect(subject.public_collection?('https://www.w3.org/ns/activitystreams#Public')).to be true + expect(subject.public_collection?('as:Public')).to be true + expect(subject.public_collection?('Public')).to be true + end + + it 'returns false for other URIs' do + expect(subject.public_collection?('https://example.com/foo/bar')).to be false + end + end + describe '#url_for' do - it 'returns a string' do + it 'returns a string starting with web domain' do account = Fabricate(:account) - expect(subject.url_for(account)).to be_a String + expect(subject.url_for(account)).to be_a(String) + .and start_with(domain) end end describe '#uri_for' do - it 'returns a string' do + it 'returns a string starting with web domain' do account = Fabricate(:account) - expect(subject.uri_for(account)).to be_a String + expect(subject.uri_for(account)).to be_a(String) + .and start_with(domain) + end + end + + describe '#activity_uri_for' do + context 'when given an account' do + it 'raises an exception' do + account = Fabricate(:account) + expect { subject.activity_uri_for(account) }.to raise_error(ArgumentError) + end + end + + context 'when given a local activity' do + it 'returns a string starting with web domain' do + status = Fabricate(:status) + expect(subject.uri_for(status)).to be_a(String) + .and start_with(domain) + end end end From 50449ae7ac57a12d81e0a3e2a6d6197af578e675 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 13 Jan 2025 12:48:47 +0100 Subject: [PATCH 098/133] Fix media preview height in compose form when 3 or more images are attached (#33571) --- app/javascript/styles/mastodon/components.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index d21175595d..072e7cb4a2 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -6815,6 +6815,8 @@ a.status-card { } &--layout-3 { + min-height: calc(64px * 2 + 8px); + & > .media-gallery__item:nth-child(1) { border-end-end-radius: 0; border-start-end-radius: 0; @@ -6834,6 +6836,8 @@ a.status-card { } &--layout-4 { + min-height: calc(64px * 2 + 8px); + & > .media-gallery__item:nth-child(1) { border-end-end-radius: 0; border-start-end-radius: 0; From 68d818121d6b0252084e97ec4e3a7229510e620d Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 13 Jan 2025 13:46:32 +0100 Subject: [PATCH 099/133] Switch `webpush` dependency to latest version of Mastodon-maintained fork (#33572) --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- app/lib/web_push_request.rb | 2 +- app/validators/web_push_key_validator.rb | 2 +- spec/workers/web/push_notification_worker_spec.rb | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 8df00d8c0a..f112e5ea5f 100644 --- a/Gemfile +++ b/Gemfile @@ -94,7 +94,7 @@ gem 'twitter-text', '~> 3.1.0' gem 'tzinfo-data', '~> 1.2023' gem 'webauthn', '~> 3.0' gem 'webpacker', '~> 5.4' -gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9' +gem 'webpush', github: 'mastodon/webpush', ref: '52725def8baf67e0d645c9d1c6c0bdff69da0c60' gem 'json-ld' gem 'json-ld-preloaded', '~> 3.2' diff --git a/Gemfile.lock b/Gemfile.lock index 4603530de0..596ed7bf52 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GIT - remote: https://github.com/ClearlyClaire/webpush.git - revision: f14a4d52e201128b1b00245d11b6de80d6cfdcd9 - ref: f14a4d52e201128b1b00245d11b6de80d6cfdcd9 + remote: https://github.com/mastodon/webpush.git + revision: 52725def8baf67e0d645c9d1c6c0bdff69da0c60 + ref: 52725def8baf67e0d645c9d1c6c0bdff69da0c60 specs: - webpush (0.3.8) + webpush (1.1.0) hkdf (~> 0.2) jwt (~> 2.0) diff --git a/app/lib/web_push_request.rb b/app/lib/web_push_request.rb index a43e22480e..91227ed460 100644 --- a/app/lib/web_push_request.rb +++ b/app/lib/web_push_request.rb @@ -33,7 +33,7 @@ class WebPushRequest end def encrypt(payload) - Webpush::Encryption.encrypt(payload, key_p256dh, key_auth) + Webpush::Legacy::Encryption.encrypt(payload, key_p256dh, key_auth) end private diff --git a/app/validators/web_push_key_validator.rb b/app/validators/web_push_key_validator.rb index a8ad5c9c6b..25914d59eb 100644 --- a/app/validators/web_push_key_validator.rb +++ b/app/validators/web_push_key_validator.rb @@ -3,7 +3,7 @@ class WebPushKeyValidator < ActiveModel::Validator def validate(subscription) begin - Webpush::Encryption.encrypt('validation_test', subscription.key_p256dh, subscription.key_auth) + Webpush::Legacy::Encryption.encrypt('validation_test', subscription.key_p256dh, subscription.key_auth) rescue ArgumentError, OpenSSL::PKey::EC::Point::Error subscription.errors.add(:base, I18n.t('crypto.errors.invalid_key')) end diff --git a/spec/workers/web/push_notification_worker_spec.rb b/spec/workers/web/push_notification_worker_spec.rb index 4993d467b3..88d88f2f3d 100644 --- a/spec/workers/web/push_notification_worker_spec.rb +++ b/spec/workers/web/push_notification_worker_spec.rb @@ -35,7 +35,7 @@ RSpec.describe Web::PushNotificationWorker do before do Setting.site_contact_email = contact_email - allow(Webpush::Encryption).to receive(:encrypt).and_return(payload) + allow(Webpush::Legacy::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: '') From 3c4a83fc6248df47d76cba944dcaac765198e6b4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 13 Jan 2025 07:58:53 -0500 Subject: [PATCH 100/133] Remove unused `LanguagePresenter#native_name` (#33551) --- app/presenters/language_presenter.rb | 6 +----- spec/requests/api/v1/instances/languages_spec.rb | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/presenters/language_presenter.rb b/app/presenters/language_presenter.rb index 69ea991d54..717d70fdbc 100644 --- a/app/presenters/language_presenter.rb +++ b/app/presenters/language_presenter.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class LanguagePresenter < ActiveModelSerializers::Model - attributes :code, :name, :native_name + attributes :code, :name def initialize(code) super() @@ -13,8 +13,4 @@ class LanguagePresenter < ActiveModelSerializers::Model def name @item[0] end - - def native_name - @item[1] - end end diff --git a/spec/requests/api/v1/instances/languages_spec.rb b/spec/requests/api/v1/instances/languages_spec.rb index 3d188d23c0..0b325bbb6d 100644 --- a/spec/requests/api/v1/instances/languages_spec.rb +++ b/spec/requests/api/v1/instances/languages_spec.rb @@ -9,10 +9,21 @@ RSpec.describe 'Languages' do end it 'returns http success and includes supported languages' do - expect(response).to have_http_status(200) + expect(response) + .to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body.pluck(:code)).to match_array LanguagesHelper::SUPPORTED_LOCALES.keys.map(&:to_s) + expect(response.parsed_body) + .to match_array(supported_locale_expectations) + end + + def supported_locale_expectations + LanguagesHelper::SUPPORTED_LOCALES.map do |key, values| + include( + code: key.to_s, + name: values.first + ) + end end end end From 3a762cddf6311db95c4bd1b674a7bbaccb446152 Mon Sep 17 00:00:00 2001 From: Jonathan de Jong Date: Mon, 13 Jan 2025 14:35:16 +0100 Subject: [PATCH 101/133] Reject announce payload if object is nil (#33570) --- app/lib/activitypub/activity/announce.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb index 9dcafff3ab..b57dca9ad7 100644 --- a/app/lib/activitypub/activity/announce.rb +++ b/app/lib/activitypub/activity/announce.rb @@ -3,6 +3,7 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity def perform return reject_payload! if delete_arrived_first?(@json['id']) || !related_to_local_activity? + return reject_payload! if @object.nil? with_redis_lock("announce:#{value_or_id(@object)}") do original_status = status_from_object From 0db75588220719d5f812b33a3e45970118c6acb9 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 13 Jan 2025 10:50:58 -0300 Subject: [PATCH 102/133] Fix HTTP 500 on `POST /api/v1/admin/ip_blocks` (#29308) --- app/models/ip_block.rb | 2 +- spec/models/ip_block_spec.rb | 2 ++ spec/requests/api/v1/admin/ip_blocks_spec.rb | 10 ++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/models/ip_block.rb b/app/models/ip_block.rb index 416ae38382..4c95ac38de 100644 --- a/app/models/ip_block.rb +++ b/app/models/ip_block.rb @@ -24,7 +24,7 @@ class IpBlock < ApplicationRecord sign_up_requires_approval: 5000, sign_up_block: 5500, no_access: 9999, - }, prefix: true + }, prefix: true, validate: true validates :ip, :severity, presence: true validates :ip, uniqueness: true diff --git a/spec/models/ip_block_spec.rb b/spec/models/ip_block_spec.rb index 93ee72423b..856d55be9d 100644 --- a/spec/models/ip_block_spec.rb +++ b/spec/models/ip_block_spec.rb @@ -12,6 +12,8 @@ RSpec.describe IpBlock do it { is_expected.to validate_presence_of(:severity) } it { is_expected.to validate_uniqueness_of(:ip) } + + it { is_expected.to allow_values(:sign_up_requires_approval, :sign_up_block, :no_access).for(:severity) } end describe '#to_log_human_identifier' do diff --git a/spec/requests/api/v1/admin/ip_blocks_spec.rb b/spec/requests/api/v1/admin/ip_blocks_spec.rb index aa3db33915..59ef8d2966 100644 --- a/spec/requests/api/v1/admin/ip_blocks_spec.rb +++ b/spec/requests/api/v1/admin/ip_blocks_spec.rb @@ -187,6 +187,16 @@ RSpec.describe 'IP Blocks' do .to start_with('application/json') end end + + context 'when the given severity is invalid' do + let(:params) { { ip: '151.0.32.55', severity: 'invalid' } } + + it 'returns http unprocessable entity' do + subject + + expect(response).to have_http_status(422) + end + end end describe 'PUT /api/v1/admin/ip_blocks/:id' do From f9451c5614061bee2f3e08bc3e76b641f327bc70 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 13 Jan 2025 09:27:30 -0500 Subject: [PATCH 103/133] Fix issue with trending order when user has chosen languages (#33557) --- app/models/trends/links.rb | 6 +++--- app/models/trends/query.rb | 11 +++++++---- app/models/trends/statuses.rb | 6 +++--- app/models/trends/tags.rb | 6 +++--- spec/models/trends/links_spec.rb | 14 ++++++++++++++ spec/models/trends/statuses_spec.rb | 14 ++++++++++++++ spec/models/trends/tags_spec.rb | 14 ++++++++++++++ 7 files changed, 58 insertions(+), 13 deletions(-) diff --git a/app/models/trends/links.rb b/app/models/trends/links.rb index 57ad486631..35ccf7744c 100644 --- a/app/models/trends/links.rb +++ b/app/models/trends/links.rb @@ -16,7 +16,7 @@ class Trends::Links < Trends::Base class Query < Trends::Query def to_arel scope = PreviewCard.joins(:trend).reorder(score: :desc) - scope = scope.merge(language_order_clause) if preferred_languages.present? + scope = scope.reorder(language_order_clause, score: :desc) if preferred_languages.present? scope = scope.merge(PreviewCardTrend.allowed) if @allowed scope = scope.offset(@offset) if @offset.present? scope = scope.limit(@limit) if @limit.present? @@ -25,8 +25,8 @@ class Trends::Links < Trends::Base private - def language_order_clause - language_order_for(PreviewCardTrend) + def trend_class + PreviewCardTrend end end diff --git a/app/models/trends/query.rb b/app/models/trends/query.rb index abed64042e..670390ae3c 100644 --- a/app/models/trends/query.rb +++ b/app/models/trends/query.rb @@ -94,11 +94,14 @@ class Trends::Query to_arel.to_a end - def language_order_for(trend_class) + def language_order_clause + Arel::Nodes::Case.new.when(language_is_preferred).then(1).else(0).desc + end + + def language_is_preferred trend_class - .reorder(nil) - .in_order_of(:language, [preferred_languages], filter: false) - .order(score: :desc) + .arel_table[:language] + .in(preferred_languages) end def preferred_languages diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb index 9c47dd486b..1a41eb9e9a 100644 --- a/app/models/trends/statuses.rb +++ b/app/models/trends/statuses.rb @@ -15,7 +15,7 @@ class Trends::Statuses < Trends::Base class Query < Trends::Query def to_arel scope = Status.joins(:trend).reorder(score: :desc) - scope = scope.merge(language_order_clause) if preferred_languages.present? + scope = scope.reorder(language_order_clause, score: :desc) if preferred_languages.present? scope = scope.merge(StatusTrend.allowed) if @allowed scope = scope.not_excluded_by_account(@account).not_domain_blocked_by_account(@account) if @account.present? scope = scope.offset(@offset) if @offset.present? @@ -25,8 +25,8 @@ class Trends::Statuses < Trends::Base private - def language_order_clause - language_order_for(StatusTrend) + def trend_class + StatusTrend end end diff --git a/app/models/trends/tags.rb b/app/models/trends/tags.rb index 84e8dde11a..63897f4f93 100644 --- a/app/models/trends/tags.rb +++ b/app/models/trends/tags.rb @@ -16,7 +16,7 @@ class Trends::Tags < Trends::Base class Query < Trends::Query def to_arel scope = Tag.joins(:trend).reorder(score: :desc) - scope = scope.merge(language_order_clause) if preferred_languages.present? + scope = scope.reorder(language_order_clause, score: :desc) if preferred_languages.present? scope = scope.merge(TagTrend.allowed) if @allowed scope = scope.offset(@offset) if @offset.present? scope = scope.limit(@limit) if @limit.present? @@ -25,8 +25,8 @@ class Trends::Tags < Trends::Base private - def language_order_clause - language_order_for(TagTrend) + def trend_class + TagTrend end end diff --git a/spec/models/trends/links_spec.rb b/spec/models/trends/links_spec.rb index b0d41d4613..81a4270c38 100644 --- a/spec/models/trends/links_spec.rb +++ b/spec/models/trends/links_spec.rb @@ -24,6 +24,20 @@ RSpec.describe Trends::Links do .to eq([lower_score.preview_card, higher_score.preview_card]) end end + + context 'when account has chosen languages' do + let!(:lang_match_higher_score) { Fabricate :preview_card_trend, score: 10, language: 'is' } + let!(:lang_match_lower_score) { Fabricate :preview_card_trend, score: 1, language: 'da' } + let(:user) { Fabricate :user, chosen_languages: %w(da is) } + let(:account) { Fabricate :account, user: user } + + before { subject.filtered_for!(account) } + + it 'returns results' do + expect(subject.records) + .to eq([lang_match_higher_score.preview_card, lang_match_lower_score.preview_card, higher_score.preview_card, lower_score.preview_card]) + end + end end end end diff --git a/spec/models/trends/statuses_spec.rb b/spec/models/trends/statuses_spec.rb index abb1535d04..3983901042 100644 --- a/spec/models/trends/statuses_spec.rb +++ b/spec/models/trends/statuses_spec.rb @@ -66,6 +66,20 @@ RSpec.describe Trends::Statuses do .to eq([lower_score.status, higher_score.status]) end end + + context 'when account has chosen languages' do + let!(:lang_match_higher_score) { Fabricate :status_trend, score: 10, language: 'is' } + let!(:lang_match_lower_score) { Fabricate :status_trend, score: 1, language: 'da' } + let(:user) { Fabricate :user, chosen_languages: %w(da is) } + let(:account) { Fabricate :account, user: user } + + before { subject.filtered_for!(account) } + + it 'returns results' do + expect(subject.records) + .to eq([lang_match_higher_score.status, lang_match_lower_score.status, higher_score.status, lower_score.status]) + end + end end end end diff --git a/spec/models/trends/tags_spec.rb b/spec/models/trends/tags_spec.rb index 8f36b4a50d..dacae602ac 100644 --- a/spec/models/trends/tags_spec.rb +++ b/spec/models/trends/tags_spec.rb @@ -50,6 +50,20 @@ RSpec.describe Trends::Tags do .to eq([lower_score.tag, higher_score.tag]) end end + + context 'when account has chosen languages' do + let!(:lang_match_higher_score) { Fabricate :tag_trend, score: 10, language: 'is' } + let!(:lang_match_lower_score) { Fabricate :tag_trend, score: 1, language: 'da' } + let(:user) { Fabricate :user, chosen_languages: %w(da is) } + let(:account) { Fabricate :account, user: user } + + before { subject.filtered_for!(account) } + + it 'returns results' do + expect(subject.records) + .to eq([lang_match_higher_score.tag, lang_match_lower_score.tag, higher_score.tag, lower_score.tag]) + end + end end end end From 77a44e61a84dedc293af5ea702f2cd8cb32240ac Mon Sep 17 00:00:00 2001 From: Wolfgang Date: Mon, 13 Jan 2025 17:05:24 +0100 Subject: [PATCH 104/133] Add enum validation to `DomainBlock#severity` (#29158) --- app/models/domain_block.rb | 2 +- spec/requests/api/v1/admin/domain_blocks_spec.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index 2853f6457a..8e7d7b6afc 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -21,7 +21,7 @@ class DomainBlock < ApplicationRecord include DomainNormalizable include DomainMaterializable - enum :severity, { silence: 0, suspend: 1, noop: 2 } + enum :severity, { silence: 0, suspend: 1, noop: 2 }, validate: true validates :domain, presence: true, uniqueness: true, domain: true diff --git a/spec/requests/api/v1/admin/domain_blocks_spec.rb b/spec/requests/api/v1/admin/domain_blocks_spec.rb index 029de72fd7..0b01d04f9a 100644 --- a/spec/requests/api/v1/admin/domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/domain_blocks_spec.rb @@ -217,6 +217,19 @@ RSpec.describe 'Domain Blocks' do .to start_with('application/json') end end + + context 'when severity is invalid' do + let(:params) { { domain: 'bar.com', severity: :bar } } + + it 'returns http unprocessable entity' do + subject + + expect(response).to have_http_status(422) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body[:error]).to eq('Validation failed: Severity is not included in the list') + end + end end describe 'PUT /api/v1/admin/domain_blocks/:id' do From 74da9e9281d549bc59453be71bf386d74946bdb7 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Tue, 14 Jan 2025 17:24:00 +0900 Subject: [PATCH 105/133] Fix custom css cache miss (#33583) --- app/helpers/theme_helper.rb | 4 +++- spec/helpers/theme_helper_spec.rb | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb index 5dfb4a5184..cda380b3bc 100644 --- a/app/helpers/theme_helper.rb +++ b/app/helpers/theme_helper.rb @@ -45,7 +45,9 @@ module ThemeHelper end def cached_custom_css_digest - Rails.cache.read(:setting_digest_custom_css) + Rails.cache.fetch(:setting_digest_custom_css) do + Setting.custom_css&.then { |content| Digest::SHA256.hexdigest(content) } + end end def theme_color_for(theme) diff --git a/spec/helpers/theme_helper_spec.rb b/spec/helpers/theme_helper_spec.rb index c811b7c981..51611b8211 100644 --- a/spec/helpers/theme_helper_spec.rb +++ b/spec/helpers/theme_helper_spec.rb @@ -80,18 +80,37 @@ RSpec.describe ThemeHelper do end describe '#custom_stylesheet' do + let(:custom_css) { 'body {}' } + let(:custom_digest) { Digest::SHA256.hexdigest(custom_css) } + + before do + Setting.custom_css = custom_css + end + context 'when custom css setting value digest is present' do - before { Rails.cache.write(:setting_digest_custom_css, '1a2s3d4f1a2s3d4f') } + before { Rails.cache.write(:setting_digest_custom_css, custom_digest) } it 'returns value from settings' do expect(custom_stylesheet) - .to match('/css/custom-1a2s3d4f.css') + .to match("/css/custom-#{custom_digest[...8]}.css") end end - context 'when custom css setting value digest is not present' do + context 'when custom css setting value digest is expired' do before { Rails.cache.delete(:setting_digest_custom_css) } + it 'returns value from settings' do + expect(custom_stylesheet) + .to match("/css/custom-#{custom_digest[...8]}.css") + end + end + + context 'when custom css setting is not present' do + before do + Setting.custom_css = nil + Rails.cache.delete(:setting_digest_custom_css) + end + it 'returns default value' do expect(custom_stylesheet) .to be_blank From ee4edbb94f4006670c819a4db653a8dea347259e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 09:44:58 +0100 Subject: [PATCH 106/133] New Crowdin Translations (automated) (#33582) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/lv.json | 2 +- app/javascript/mastodon/locales/nan.json | 26 ++++++++++++++++++++++++ config/locales/ca.yml | 3 +++ config/locales/simple_form.ca.yml | 3 +++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 9244f3509c..94c48453b7 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -345,7 +345,7 @@ "hints.profiles.see_more_followers": "Skatīt vairāk sekotāju {domain}", "hints.profiles.see_more_follows": "Skatīt vairāk sekojumu {domain}", "hints.profiles.see_more_posts": "Skatīt vairāk ierakstu {domain}", - "hints.threads.replies_may_be_missing": "Var trūkt atbildes no citiem serveriem.", + "hints.threads.replies_may_be_missing": "Var trūkt atbilžu no citiem serveriem.", "hints.threads.see_more": "Skatīt vairāk atbilžu {domain}", "home.column_settings.show_reblogs": "Rādīt pastiprinātos ierakstus", "home.column_settings.show_replies": "Rādīt atbildes", diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json index 009f96b9a7..22a7c507fc 100644 --- a/app/javascript/mastodon/locales/nan.json +++ b/app/javascript/mastodon/locales/nan.json @@ -126,9 +126,35 @@ "bundle_column_error.network.title": "網路錯誤", "bundle_column_error.retry": "Koh試", "bundle_column_error.return": "Tńg去頭頁", + "bundle_column_error.routing.body": "Tshuē bô所要求ê頁面。Lí kám確定地址liâu-á ê URL正確?", "bundle_column_error.routing.title": "404", "bundle_modal_error.close": "關", + "bundle_modal_error.message": "Tī載入tsit ê畫面ê時起錯誤。", + "bundle_modal_error.retry": "Koh試", + "column.create_list": "建立列單", + "column.direct": "私人ê提起", + "column.directory": "瀏覽個人資料", + "column.domain_blocks": "封鎖ê域名", + "column.edit_list": "編輯列單", + "column.favourites": "Siōng kah意", + "column.firehose": "Tsit-má ê動態", + "column.follow_requests": "跟tuè請求", + "column.home": "頭頁", + "column_header.pin": "釘", + "column_header.show_settings": "顯示設定", + "column_header.unpin": "Pak掉", + "column_search.cancel": "取消", + "column_subheading.settings": "設定", + "community.column_settings.local_only": "Kan-ta展示本地ê", + "community.column_settings.media_only": "Kan-ta展示媒體", + "community.column_settings.remote_only": "Kan-ta展示遠距離ê", "compose.language.change": "換語言", + "compose.language.search": "Tshiau-tshuē語言……", + "compose.published.body": "成功PO文。", + "compose.published.open": "開", + "compose.saved.body": "PO文儲存ah。", + "compose_form.direct_message_warning_learn_more": "詳細資訊", + "compose_form.encryption_warning": "Mastodon ê PO文無點tuì點加密。M̄通用Mastodon分享任何敏感ê資訊。", "confirmations.follow_to_list.confirm": "跟tuè,加入kàu列單", "notification.favourite_pm": "{name} kah意lí ê私人提起", "notification.favourite_pm.name_and_others_with_link": "{name} kap{count, plural, other {另外 # ê lâng}}kah意lí ê私人提起", diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 66738c8689..acd103a250 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -937,6 +937,7 @@ ca: generates: action: Generar chance_to_review_html: "Les condicions de servei generades no es publicaran automàticament. Tindreu l'oportunitat de revisar-ne els resultats. Empleneu els detalls necessaris per a procedir." + explanation_html: La plantilla de condicions de servei proveïda ho és només a títol informatiu i no s'ha d'interpretar com a consell jurídic en cap cas. Consulteu el vostre propi servei legal sobre la vostra situació i les qüestions legals específiques que tingueu. title: Configuració de les condicions de servei history: Historial live: En ús @@ -1892,6 +1893,8 @@ ca: title: Un inici de sessió nou terms_of_service_changed: sign_off: L'equip de %{domain} + subject: Actualitzacions de les condicions de servei + subtitle: Les condicions de servei de %{domain} canvien title: Actualització important warning: appeal: Envia una apel·lació diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index 2fecc78c79..58cc0a034c 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -130,6 +130,9 @@ ca: show_application: Sempre podràs veure quina aplicació ha publicat els teus tuts. tag: name: Només pots canviar la caixa de les lletres, per exemple, per fer-la més llegible + terms_of_service_generator: + domain: Identificació única del servei en línia que oferiu. + jurisdiction: Indiqueu el país on resideix qui paga les factures. Si és una empresa o una altra entitat, indiqueu el país en què està registrada, així com la ciutat, regió, territori o estat, segons calqui. user: chosen_languages: Quan estigui marcat, només es mostraran els tuts de les llengües seleccionades en les línies de temps públiques role: El rol controla quins permisos té l'usuari. From 4a2813158d16914450b6bc4c00e1b3e7edfa26ba Mon Sep 17 00:00:00 2001 From: S1m <31284753+p1gp1g@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:14:00 +0100 Subject: [PATCH 107/133] Add support for standard webpush (#33528) Co-authored-by: Claire --- Gemfile | 2 +- Gemfile.lock | 4 +- .../api/v1/push/subscriptions_controller.rb | 3 +- .../api/web/push_subscriptions_controller.rb | 3 +- app/lib/web_push_request.rb | 26 +++++- app/models/web/push_subscription.rb | 7 +- .../rest/web_push_subscription_serializer.rb | 4 +- app/validators/web_push_key_validator.rb | 2 +- app/workers/web/push_notification_worker.rb | 66 +++++++++++---- ...11200_add_standard_to_push_subscription.rb | 7 ++ db/schema.rb | 5 +- .../web/push_notification_worker_spec.rb | 84 ++++++++++++++++--- 12 files changed, 167 insertions(+), 46 deletions(-) create mode 100644 db/migrate/20250108111200_add_standard_to_push_subscription.rb diff --git a/Gemfile b/Gemfile index f112e5ea5f..2abdae151f 100644 --- a/Gemfile +++ b/Gemfile @@ -94,7 +94,7 @@ gem 'twitter-text', '~> 3.1.0' gem 'tzinfo-data', '~> 1.2023' gem 'webauthn', '~> 3.0' gem 'webpacker', '~> 5.4' -gem 'webpush', github: 'mastodon/webpush', ref: '52725def8baf67e0d645c9d1c6c0bdff69da0c60' +gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913' gem 'json-ld' gem 'json-ld-preloaded', '~> 3.2' diff --git a/Gemfile.lock b/Gemfile.lock index 596ed7bf52..89739f9053 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GIT remote: https://github.com/mastodon/webpush.git - revision: 52725def8baf67e0d645c9d1c6c0bdff69da0c60 - ref: 52725def8baf67e0d645c9d1c6c0bdff69da0c60 + revision: 9631ac63045cfabddacc69fc06e919b4c13eb913 + ref: 9631ac63045cfabddacc69fc06e919b4c13eb913 specs: webpush (1.1.0) hkdf (~> 0.2) diff --git a/app/controllers/api/v1/push/subscriptions_controller.rb b/app/controllers/api/v1/push/subscriptions_controller.rb index e1ad89ee3e..d74b5d958f 100644 --- a/app/controllers/api/v1/push/subscriptions_controller.rb +++ b/app/controllers/api/v1/push/subscriptions_controller.rb @@ -21,6 +21,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController endpoint: subscription_params[:endpoint], key_p256dh: subscription_params[:keys][:p256dh], key_auth: subscription_params[:keys][:auth], + standard: subscription_params[:standard] || false, data: data_params, user_id: current_user.id, access_token_id: doorkeeper_token.id @@ -55,7 +56,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController end def subscription_params - params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) + params.require(:subscription).permit(:endpoint, :standard, keys: [:auth, :p256dh]) end def data_params diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb index f515961427..7eb51c6846 100644 --- a/app/controllers/api/web/push_subscriptions_controller.rb +++ b/app/controllers/api/web/push_subscriptions_controller.rb @@ -66,7 +66,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController end def subscription_params - @subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) + @subscription_params ||= params.require(:subscription).permit(:standard, :endpoint, keys: [:auth, :p256dh]) end def web_push_subscription_params @@ -76,6 +76,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController endpoint: subscription_params[:endpoint], key_auth: subscription_params[:keys][:auth], key_p256dh: subscription_params[:keys][:p256dh], + standard: subscription_params[:standard] || false, user_id: active_session.user_id, } end diff --git a/app/lib/web_push_request.rb b/app/lib/web_push_request.rb index 91227ed460..85e8ab6bb5 100644 --- a/app/lib/web_push_request.rb +++ b/app/lib/web_push_request.rb @@ -2,7 +2,8 @@ class WebPushRequest SIGNATURE_ALGORITHM = 'p256ecdsa' - AUTH_HEADER = 'WebPush' + LEGACY_AUTH_HEADER = 'WebPush' + STANDARD_AUTH_HEADER = 'vapid' PAYLOAD_EXPIRATION = 24.hours JWT_ALGORITHM = 'ES256' JWT_TYPE = 'JWT' @@ -10,6 +11,7 @@ class WebPushRequest attr_reader :web_push_subscription delegate( + :standard, :endpoint, :key_auth, :key_p256dh, @@ -24,20 +26,36 @@ class WebPushRequest @audience ||= Addressable::URI.parse(endpoint).normalized_site end - def authorization_header - [AUTH_HEADER, encoded_json_web_token].join(' ') + def legacy_authorization_header + [LEGACY_AUTH_HEADER, encoded_json_web_token].join(' ') end def crypto_key_header [SIGNATURE_ALGORITHM, vapid_key.public_key_for_push_header].join('=') end - def encrypt(payload) + def legacy_encrypt(payload) Webpush::Legacy::Encryption.encrypt(payload, key_p256dh, key_auth) end + def standard_authorization_header + [STANDARD_AUTH_HEADER, standard_vapid_value].join(' ') + end + + def standard_encrypt(payload) + Webpush::Encryption.encrypt(payload, key_p256dh, key_auth) + end + + def legacy + !standard + end + private + def standard_vapid_value + "t=#{encoded_json_web_token},k=#{vapid_key.public_key_for_push_header}" + end + def encoded_json_web_token JWT.encode( web_token_payload, diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb index 656040d2ce..12d843cd09 100644 --- a/app/models/web/push_subscription.rb +++ b/app/models/web/push_subscription.rb @@ -5,10 +5,11 @@ # Table name: web_push_subscriptions # # id :bigint(8) not null, primary key -# endpoint :string not null -# key_p256dh :string not null -# key_auth :string not null # data :json +# endpoint :string not null +# key_auth :string not null +# key_p256dh :string not null +# standard :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null # access_token_id :bigint(8) diff --git a/app/serializers/rest/web_push_subscription_serializer.rb b/app/serializers/rest/web_push_subscription_serializer.rb index 674a2d5a86..4cb980bb93 100644 --- a/app/serializers/rest/web_push_subscription_serializer.rb +++ b/app/serializers/rest/web_push_subscription_serializer.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true class REST::WebPushSubscriptionSerializer < ActiveModel::Serializer - attributes :id, :endpoint, :alerts, :server_key, :policy + attributes :id, :endpoint, :standard, :alerts, :server_key, :policy + + delegate :standard, to: :object def alerts (object.data&.dig('alerts') || {}).each_with_object({}) { |(k, v), h| h[k] = ActiveModel::Type::Boolean.new.cast(v) } diff --git a/app/validators/web_push_key_validator.rb b/app/validators/web_push_key_validator.rb index 25914d59eb..a8ad5c9c6b 100644 --- a/app/validators/web_push_key_validator.rb +++ b/app/validators/web_push_key_validator.rb @@ -3,7 +3,7 @@ class WebPushKeyValidator < ActiveModel::Validator def validate(subscription) begin - Webpush::Legacy::Encryption.encrypt('validation_test', subscription.key_p256dh, subscription.key_auth) + Webpush::Encryption.encrypt('validation_test', subscription.key_p256dh, subscription.key_auth) rescue ArgumentError, OpenSSL::PKey::EC::Point::Error subscription.errors.add(:base, I18n.t('crypto.errors.invalid_key')) end diff --git a/app/workers/web/push_notification_worker.rb b/app/workers/web/push_notification_worker.rb index 3629904fa7..32279a9e74 100644 --- a/app/workers/web/push_notification_worker.rb +++ b/app/workers/web/push_notification_worker.rb @@ -19,7 +19,19 @@ class Web::PushNotificationWorker # in the meantime, so we have to double-check before proceeding return unless @notification.activity.present? && @subscription.pushable?(@notification) - payload = web_push_request.encrypt(push_notification_json) + if web_push_request.legacy + perform_legacy_request + else + perform_standard_request + end + rescue ActiveRecord::RecordNotFound + true + end + + private + + def perform_legacy_request + payload = web_push_request.legacy_encrypt(push_notification_json) request_pool.with(web_push_request.audience) do |http_client| request = Request.new(:post, web_push_request.endpoint, body: payload.fetch(:ciphertext), http_client: http_client) @@ -31,28 +43,48 @@ class Web::PushNotificationWorker 'Content-Encoding' => 'aesgcm', 'Encryption' => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}", 'Crypto-Key' => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{web_push_request.crypto_key_header}", - 'Authorization' => web_push_request.authorization_header, + 'Authorization' => web_push_request.legacy_authorization_header, 'Unsubscribe-URL' => subscription_url ) - request.perform do |response| - # If the server responds with an error in the 4xx range - # that isn't about rate-limiting or timeouts, we can - # assume that the subscription is invalid or expired - # and must be removed - - if (400..499).cover?(response.code) && ![408, 429].include?(response.code) - @subscription.destroy! - elsif !(200...300).cover?(response.code) - raise Mastodon::UnexpectedResponseError, response - end - end + send(request) end - rescue ActiveRecord::RecordNotFound - true end - private + def perform_standard_request + payload = web_push_request.standard_encrypt(push_notification_json) + + request_pool.with(web_push_request.audience) do |http_client| + request = Request.new(:post, web_push_request.endpoint, body: payload, http_client: http_client) + + request.add_headers( + 'Content-Type' => 'application/octet-stream', + 'Ttl' => TTL.to_s, + 'Urgency' => URGENCY, + 'Content-Encoding' => 'aes128gcm', + 'Authorization' => web_push_request.standard_authorization_header, + 'Unsubscribe-URL' => subscription_url, + 'Content-Length' => payload.length.to_s + ) + + send(request) + end + end + + def send(request) + request.perform do |response| + # If the server responds with an error in the 4xx range + # that isn't about rate-limiting or timeouts, we can + # assume that the subscription is invalid or expired + # and must be removed + + if (400..499).cover?(response.code) && ![408, 429].include?(response.code) + @subscription.destroy! + elsif !(200...300).cover?(response.code) + raise Mastodon::UnexpectedResponseError, response + end + end + end def web_push_request @web_push_request || WebPushRequest.new(@subscription) diff --git a/db/migrate/20250108111200_add_standard_to_push_subscription.rb b/db/migrate/20250108111200_add_standard_to_push_subscription.rb new file mode 100644 index 0000000000..eb72f9c62e --- /dev/null +++ b/db/migrate/20250108111200_add_standard_to_push_subscription.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddStandardToPushSubscription < ActiveRecord::Migration[8.0] + def change + add_column :web_push_subscriptions, :standard, :boolean, null: false, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 49b10cb7bd..13abd5c0cd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,9 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_12_16_224825) do +ActiveRecord::Schema[8.0].define(version: 2025_01_08_111200) do # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" + enable_extension "pg_catalog.plpgsql" create_table "account_aliases", force: :cascade do |t| t.bigint "account_id", null: false @@ -1202,6 +1202,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_12_16_224825) do t.datetime "updated_at", precision: nil, null: false t.bigint "access_token_id" t.bigint "user_id" + t.boolean "standard", default: false, null: false t.index ["access_token_id"], name: "index_web_push_subscriptions_on_access_token_id", where: "(access_token_id IS NOT NULL)" t.index ["user_id"], name: "index_web_push_subscriptions_on_user_id" end diff --git a/spec/workers/web/push_notification_worker_spec.rb b/spec/workers/web/push_notification_worker_spec.rb index 88d88f2f3d..6ee8ae53f8 100644 --- a/spec/workers/web/push_notification_worker_spec.rb +++ b/spec/workers/web/push_notification_worker_spec.rb @@ -5,21 +5,36 @@ require 'rails_helper' RSpec.describe Web::PushNotificationWorker do subject { described_class.new } - let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' } - let(:auth) { 'Q2BoAjC09xH3ywDLNJr-dA==' } let(:endpoint) { 'https://updates.push.services.mozilla.com/push/v1/subscription-id' } let(:user) { Fabricate(:user) } let(:notification) { Fabricate(:notification) } - let(:subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: p256dh, key_auth: auth, endpoint: endpoint, data: { alerts: { notification.type => true } }) } let(:vapid_public_key) { 'BB37UCyc8LLX4PNQSe-04vSFvpUWGrENubUaslVFM_l5TxcGVMY0C3RXPeUJAQHKYlcOM2P4vTYmkoo0VZGZTM4=' } let(:vapid_private_key) { 'OPrw1Sum3gRoL4-DXfSCC266r-qfFSRZrnj8MgIhRHg=' } let(:vapid_key) { Webpush::VapidKey.from_keys(vapid_public_key, vapid_private_key) } let(:contact_email) { 'sender@example.com' } - let(:ciphertext) { "+\xB8\xDBT}\x13\xB6\xDD.\xF9\xB0\xA7\xC8\xD2\x80\xFD\x99#\xF7\xAC\x83\xA4\xDB,\x1F\xB5\xB9w\x85>\xF7\xADr" } - let(:salt) { "X\x97\x953\xE4X\xF8_w\xE7T\x95\xC51q\xFE" } - let(:server_public_key) { "\x04\b-RK9w\xDD$\x16lFz\xF9=\xB4~\xC6\x12k\xF3\xF40t\xA9\xC1\fR\xC3\x81\x80\xAC\f\x7F\xE4\xCC\x8E\xC2\x88 n\x8BB\xF1\x9C\x14\a\xFA\x8D\xC9\x80\xA1\xDDyU\\&c\x01\x88#\x118Ua" } - let(:shared_secret) { "\t\xA7&\x85\t\xC5m\b\xA8\xA7\xF8B{1\xADk\xE1y'm\xEDE\xEC\xDD\xEDj\xB3$s\xA9\xDA\xF0" } - let(:payload) { { ciphertext: ciphertext, salt: salt, server_public_key: server_public_key, shared_secret: shared_secret } } + + # Legacy values + let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' } + let(:auth) { 'Q2BoAjC09xH3ywDLNJr-dA==' } + let(:legacy_subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: p256dh, key_auth: auth, endpoint: endpoint, data: { alerts: { notification.type => true } }) } + let(:legacy_payload) do + { + ciphertext: "+\xB8\xDBT}\x13\xB6\xDD.\xF9\xB0\xA7\xC8\xD2\x80\xFD\x99#\xF7\xAC\x83\xA4\xDB,\x1F\xB5\xB9w\x85>\xF7\xADr", + salt: "X\x97\x953\xE4X\xF8_w\xE7T\x95\xC51q\xFE", + server_public_key: "\x04\b-RK9w\xDD$\x16lFz\xF9=\xB4~\xC6\x12k\xF3\xF40t\xA9\xC1\fR\xC3\x81\x80\xAC\f\x7F\xE4\xCC\x8E\xC2\x88 n\x8BB\xF1\x9C\x14\a\xFA\x8D\xC9\x80\xA1\xDDyU\\&c\x01\x88#\x118Ua", + shared_secret: "\t\xA7&\x85\t\xC5m\b\xA8\xA7\xF8B{1\xADk\xE1y'm\xEDE\xEC\xDD\xEDj\xB3$s\xA9\xDA\xF0", + } + end + + # Standard values, from RFC8291 + let(:std_p256dh) { 'BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4' } + let(:std_auth) { 'BTBZMqHH6r4Tts7J_aSIgg' } + let(:std_as_public) { 'BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8' } + let(:std_as_private) { 'yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw' } + let(:std_salt) { 'DGv6ra1nlYgDCS1FRnbzlw' } + let(:std_subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: std_p256dh, key_auth: std_auth, endpoint: endpoint, standard: true, data: { alerts: { notification.type => true } }) } + let(:std_input) { 'When I grow up, I want to be a watermelon' } + let(:std_ciphertext) { 'DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPTpK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN' } describe 'perform' do around do |example| @@ -35,20 +50,40 @@ RSpec.describe Web::PushNotificationWorker do before do Setting.site_contact_email = contact_email - allow(Webpush::Legacy::Encryption).to receive(:encrypt).and_return(payload) allow(JWT).to receive(:encode).and_return('jwt.encoded.payload') stub_request(:post, endpoint).to_return(status: 201, body: '') end - it 'calls the relevant service with the correct headers' do - subject.perform(subscription.id, notification.id) + it 'Legacy push calls the relevant service with the legacy headers' do + allow(Webpush::Legacy::Encryption).to receive(:encrypt).and_return(legacy_payload) - expect(web_push_endpoint_request) + subject.perform(legacy_subscription.id, notification.id) + + expect(legacy_web_push_endpoint_request) .to have_been_made end - def web_push_endpoint_request + # We allow subject stub to encrypt the same input than the RFC8291 example + # rubocop:disable RSpec/SubjectStub + it 'Standard push calls the relevant service with the standard headers' do + # Mock server keys to match RFC example + allow(OpenSSL::PKey::EC).to receive(:generate).and_return(std_as_keys) + # Mock the random salt to match RFC example + rand = Random.new + allow(Random).to receive(:new).and_return(rand) + allow(rand).to receive(:bytes).and_return(Webpush.decode64(std_salt)) + # Mock input to match RFC example + allow(subject).to receive(:push_notification_json).and_return(std_input) + + subject.perform(std_subscription.id, notification.id) + + expect(standard_web_push_endpoint_request) + .to have_been_made + end + # rubocop:enable RSpec/SubjectStub + + def legacy_web_push_endpoint_request a_request( :post, endpoint @@ -66,5 +101,28 @@ RSpec.describe Web::PushNotificationWorker do body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr" ) end + + def standard_web_push_endpoint_request + a_request( + :post, + endpoint + ).with( + headers: { + 'Content-Encoding' => 'aes128gcm', + 'Content-Type' => 'application/octet-stream', + 'Ttl' => '172800', + 'Urgency' => 'normal', + 'Authorization' => "vapid t=jwt.encoded.payload,k=#{vapid_public_key.delete('=')}", + 'Unsubscribe-URL' => %r{/api/web/push_subscriptions/}, + }, + body: Webpush.decode64(std_ciphertext) + ) + end + + def std_as_keys + # VapidKey contains a method to retrieve EC keypair from + # B64 raw keys, the keypair is stored in curve field + Webpush::VapidKey.from_keys(std_as_public, std_as_private).curve + end end end From a9a8b6b701026e41b2ee8edb897834b9c799cd2e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 14 Jan 2025 05:27:21 -0500 Subject: [PATCH 108/133] Expand coverage of admin/trends/* areas (#33581) --- .../links/preview_card_providers_spec.rb | 49 +++++++++-- spec/system/admin/trends/links_spec.rb | 85 +++++++++++++++++-- spec/system/admin/trends/statuses_spec.rb | 83 ++++++++++++++++-- spec/system/admin/trends/tags_spec.rb | 46 ++++++++-- 4 files changed, 232 insertions(+), 31 deletions(-) diff --git a/spec/system/admin/trends/links/preview_card_providers_spec.rb b/spec/system/admin/trends/links/preview_card_providers_spec.rb index 0a5b5a7581..159a5b720a 100644 --- a/spec/system/admin/trends/links/preview_card_providers_spec.rb +++ b/spec/system/admin/trends/links/preview_card_providers_spec.rb @@ -5,20 +5,49 @@ require 'rails_helper' RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do let(:current_user) { Fabricate(:admin_user) } - before do - sign_in current_user - end + before { sign_in current_user } describe 'Performing batch updates' do - before do - visit admin_trends_links_preview_card_providers_path - end - context 'without selecting any records' do it 'displays a notice about selection' do + visit admin_trends_links_preview_card_providers_path + click_on button_for_allow - expect(page).to have_content(selection_error_text) + expect(page) + .to have_content(selection_error_text) + end + end + + context 'with providers that are not trendable' do + let!(:provider) { Fabricate :preview_card_provider, trendable: false } + + it 'allows the providers' do + visit admin_trends_links_preview_card_providers_path + + check_item + + expect { click_on button_for_allow } + .to change { provider.reload.trendable? }.from(false).to(true) + end + end + + context 'with providers that are trendable' do + let!(:provider) { Fabricate :preview_card_provider, trendable: true } + + it 'disallows the providers' do + visit admin_trends_links_preview_card_providers_path + + check_item + + expect { click_on button_for_disallow } + .to change { provider.reload.trendable? }.from(true).to(false) + end + end + + def check_item + within '.batch-table__row' do + find('input[type=checkbox]').check end end @@ -26,6 +55,10 @@ RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do I18n.t('admin.trends.allow') end + def button_for_disallow + I18n.t('admin.trends.disallow') + end + def selection_error_text I18n.t('admin.trends.links.publishers.no_publisher_selected') end diff --git a/spec/system/admin/trends/links_spec.rb b/spec/system/admin/trends/links_spec.rb index 15138f42d1..879bbe8ad9 100644 --- a/spec/system/admin/trends/links_spec.rb +++ b/spec/system/admin/trends/links_spec.rb @@ -5,20 +5,77 @@ require 'rails_helper' RSpec.describe 'Admin::Trends::Links' do let(:current_user) { Fabricate(:admin_user) } - before do - sign_in current_user - end + before { sign_in current_user } describe 'Performing batch updates' do - before do - visit admin_trends_links_path - end - context 'without selecting any records' do it 'displays a notice about selection' do + visit admin_trends_links_path + click_on button_for_allow - expect(page).to have_content(selection_error_text) + expect(page) + .to have_content(selection_error_text) + end + end + + context 'with links that are not trendable' do + let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, trendable: false) } + + it 'allows the links' do + visit admin_trends_links_path + + check_item + + expect { click_on button_for_allow } + .to change { preview_card_trend.preview_card.reload.trendable? }.from(false).to(true) + end + end + + context 'with links whose providers are not trendable' do + let(:preview_card_provider) { Fabricate :preview_card_provider, trendable: false } + let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, url: "https://#{preview_card_provider.domain}/page") } + + it 'allows the providers of the links' do + visit admin_trends_links_path + + check_item + + expect { click_on button_for_allow_providers } + .to change { preview_card_trend.preview_card.provider.reload.trendable? }.from(false).to(true) + end + end + + context 'with links that are trendable' do + let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, trendable: true) } + + it 'disallows the links' do + visit admin_trends_links_path + + check_item + + expect { click_on button_for_disallow } + .to change { preview_card_trend.preview_card.reload.trendable? }.from(true).to(false) + end + end + + context 'with links whose providers are trendable' do + let(:preview_card_provider) { Fabricate :preview_card_provider, trendable: true } + let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, url: "https://#{preview_card_provider.domain}/page") } + + it 'disallows the links' do + visit admin_trends_links_path + + check_item + + expect { click_on button_for_disallow_providers } + .to change { preview_card_trend.preview_card.provider.reload.trendable? }.from(true).to(false) + end + end + + def check_item + within '.batch-table__row' do + find('input[type=checkbox]').check end end @@ -26,6 +83,18 @@ RSpec.describe 'Admin::Trends::Links' do I18n.t('admin.trends.links.allow') end + def button_for_allow_providers + I18n.t('admin.trends.links.allow_provider') + end + + def button_for_disallow + I18n.t('admin.trends.links.disallow') + end + + def button_for_disallow_providers + I18n.t('admin.trends.links.disallow_provider') + end + def selection_error_text I18n.t('admin.trends.links.no_link_selected') end diff --git a/spec/system/admin/trends/statuses_spec.rb b/spec/system/admin/trends/statuses_spec.rb index 45c048afb0..be081df989 100644 --- a/spec/system/admin/trends/statuses_spec.rb +++ b/spec/system/admin/trends/statuses_spec.rb @@ -5,20 +5,75 @@ require 'rails_helper' RSpec.describe 'Admin::Trends::Statuses' do let(:current_user) { Fabricate(:admin_user) } - before do - sign_in current_user - end + before { sign_in current_user } describe 'Performing batch updates' do - before do - visit admin_trends_statuses_path - end - context 'without selecting any records' do it 'displays a notice about selection' do + visit admin_trends_statuses_path + click_on button_for_allow - expect(page).to have_content(selection_error_text) + expect(page) + .to have_content(selection_error_text) + end + end + + context 'with statuses that are not trendable' do + let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, trendable: false) } + + it 'allows the statuses' do + visit admin_trends_statuses_path + + check_item + + expect { click_on button_for_allow } + .to change { status_trend.status.reload.trendable? }.from(false).to(true) + end + end + + context 'with statuses whose accounts are not trendable' do + let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, account: Fabricate(:account, trendable: false)) } + + it 'allows the accounts of the statuses' do + visit admin_trends_statuses_path + + check_item + + expect { click_on button_for_allow_accounts } + .to change { status_trend.status.account.reload.trendable? }.from(false).to(true) + end + end + + context 'with statuses that are trendable' do + let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, trendable: true) } + + it 'disallows the statuses' do + visit admin_trends_statuses_path + + check_item + + expect { click_on button_for_disallow } + .to change { status_trend.status.reload.trendable? }.from(true).to(false) + end + end + + context 'with statuses whose accounts are trendable' do + let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, account: Fabricate(:account, trendable: true)) } + + it 'disallows the statuses' do + visit admin_trends_statuses_path + + check_item + + expect { click_on button_for_disallow_accounts } + .to change { status_trend.status.reload.trendable? }.from(true).to(false) + end + end + + def check_item + within '.batch-table__row' do + find('input[type=checkbox]').check end end @@ -26,6 +81,18 @@ RSpec.describe 'Admin::Trends::Statuses' do I18n.t('admin.trends.statuses.allow') end + def button_for_allow_accounts + I18n.t('admin.trends.statuses.allow_account') + end + + def button_for_disallow + I18n.t('admin.trends.statuses.disallow') + end + + def button_for_disallow_accounts + I18n.t('admin.trends.statuses.disallow_account') + end + def selection_error_text I18n.t('admin.trends.statuses.no_status_selected') end diff --git a/spec/system/admin/trends/tags_spec.rb b/spec/system/admin/trends/tags_spec.rb index 30b0850b93..a71d9ba8ca 100644 --- a/spec/system/admin/trends/tags_spec.rb +++ b/spec/system/admin/trends/tags_spec.rb @@ -5,27 +5,59 @@ require 'rails_helper' RSpec.describe 'Admin::Trends::Tags' do let(:current_user) { Fabricate(:admin_user) } - before do - sign_in current_user - end + before { sign_in current_user } describe 'Performing batch updates' do - before do - visit admin_trends_tags_path - end - context 'without selecting any records' do it 'displays a notice about selection' do + visit admin_trends_tags_path + click_on button_for_allow expect(page).to have_content(selection_error_text) end end + context 'with tags that are not trendable' do + let!(:tag_trend) { Fabricate :tag_trend, tag: Fabricate(:tag, trendable: false) } + + it 'allows the tags' do + visit admin_trends_tags_path + + check_item + + expect { click_on button_for_allow } + .to change { tag_trend.tag.reload.trendable? }.from(false).to(true) + end + end + + context 'with tags that are trendable' do + let!(:tag_trend) { Fabricate :tag_trend, tag: Fabricate(:tag, trendable: true) } + + it 'disallows the tags' do + visit admin_trends_tags_path + + check_item + + expect { click_on button_for_disallow } + .to change { tag_trend.tag.reload.trendable? }.from(true).to(false) + end + end + + def check_item + within '.batch-table__row' do + find('input[type=checkbox]').check + end + end + def button_for_allow I18n.t('admin.trends.allow') end + def button_for_disallow + I18n.t('admin.trends.disallow') + end + def selection_error_text I18n.t('admin.trends.tags.no_tag_selected') end From e2f085e2b2cec08dc1f5ae825730c2a3bf62e054 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 14 Jan 2025 11:42:06 +0100 Subject: [PATCH 109/133] Use final specification for new WebPush subscriptions in web interface (#33587) --- .../mastodon/actions/push_notifications/registerer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js index b3d3850e31..647a6bd9fb 100644 --- a/app/javascript/mastodon/actions/push_notifications/registerer.js +++ b/app/javascript/mastodon/actions/push_notifications/registerer.js @@ -33,7 +33,7 @@ const unsubscribe = ({ registration, subscription }) => subscription ? subscription.unsubscribe().then(() => registration) : registration; const sendSubscriptionToBackend = (subscription) => { - const params = { subscription }; + const params = { subscription: { ...subscription.toJSON(), standard: true } }; if (me) { const data = pushNotificationsSetting.get(me); From e9462960a7cf416b9e459d599a0f2cc8c5b070f0 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 14 Jan 2025 14:10:48 +0100 Subject: [PATCH 110/133] Redirect new users to onboarding (#33471) --- app/javascript/mastodon/api_types/accounts.ts | 2 +- .../mastodon/features/onboarding/profile.tsx | 6 +++++- app/javascript/mastodon/features/ui/index.jsx | 13 +++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/app/javascript/mastodon/api_types/accounts.ts b/app/javascript/mastodon/api_types/accounts.ts index fdbd7523fc..3f8b27497f 100644 --- a/app/javascript/mastodon/api_types/accounts.ts +++ b/app/javascript/mastodon/api_types/accounts.ts @@ -19,7 +19,7 @@ export interface BaseApiAccountJSON { avatar_static: string; bot: boolean; created_at: string; - discoverable: boolean; + discoverable?: boolean; indexable: boolean; display_name: string; emojis: ApiCustomEmojiJSON[]; diff --git a/app/javascript/mastodon/features/onboarding/profile.tsx b/app/javascript/mastodon/features/onboarding/profile.tsx index 1e5e868f18..d9b394acfb 100644 --- a/app/javascript/mastodon/features/onboarding/profile.tsx +++ b/app/javascript/mastodon/features/onboarding/profile.tsx @@ -12,6 +12,7 @@ import AddPhotoAlternateIcon from '@/material-icons/400-24px/add_photo_alternate import EditIcon from '@/material-icons/400-24px/edit.svg?react'; import PersonIcon from '@/material-icons/400-24px/person.svg?react'; import { updateAccount } from 'mastodon/actions/accounts'; +import { closeOnboarding } from 'mastodon/actions/onboarding'; import { Button } from 'mastodon/components/button'; import { Column } from 'mastodon/components/column'; import { ColumnHeader } from 'mastodon/components/column_header'; @@ -58,7 +59,9 @@ export const Profile: React.FC<{ ); const [avatar, setAvatar] = useState(); const [header, setHeader] = useState(); - const [discoverable, setDiscoverable] = useState(true); + const [discoverable, setDiscoverable] = useState( + account?.discoverable ?? true, + ); const [isSaving, setIsSaving] = useState(false); const [errors, setErrors] = useState(); const avatarFileRef = createRef(); @@ -132,6 +135,7 @@ export const Profile: React.FC<{ ) .then(() => { history.push('/start/follows'); + dispatch(closeOnboarding()); return ''; }) // eslint-disable-next-line @typescript-eslint/use-unknown-in-catch-callback-variable diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 1ecc52b6bd..b239e63ccd 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -92,6 +92,7 @@ const mapStateToProps = state => ({ hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0, canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']), firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION, + newAccount: !state.getIn(['accounts', me, 'note']) && !state.getIn(['accounts', me, 'bot']) && state.getIn(['accounts', me, 'following_count'], 0) === 0 && state.getIn(['accounts', me, 'statuses_count'], 0) === 0, username: state.getIn(['accounts', me, 'username']), }); @@ -135,6 +136,7 @@ class SwitchingColumnsArea extends PureComponent { children: PropTypes.node, location: PropTypes.object, singleColumn: PropTypes.bool, + forceOnboarding: PropTypes.bool, }; UNSAFE_componentWillMount () { @@ -165,14 +167,16 @@ class SwitchingColumnsArea extends PureComponent { }; render () { - const { children, singleColumn } = this.props; + const { children, singleColumn, forceOnboarding } = this.props; const { signedIn } = this.props.identity; const pathName = this.props.location.pathname; let redirect; if (signedIn) { - if (singleColumn) { + if (forceOnboarding) { + redirect = ; + } else if (singleColumn) { redirect = ; } else { redirect = ; @@ -276,6 +280,7 @@ class UI extends PureComponent { intl: PropTypes.object.isRequired, layout: PropTypes.string.isRequired, firstLaunch: PropTypes.bool, + newAccount: PropTypes.bool, username: PropTypes.string, ...WithRouterPropTypes, }; @@ -568,7 +573,7 @@ class UI extends PureComponent { render () { const { draggingOver } = this.state; - const { children, isComposing, location, layout } = this.props; + const { children, isComposing, location, layout, firstLaunch, newAccount } = this.props; const handlers = { help: this.handleHotkeyToggleHelp, @@ -597,7 +602,7 @@ class UI extends PureComponent {
- + {children} From 7b608b41f29ccb05fe9521fc83e3f81769f1e8df Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:13:58 +0100 Subject: [PATCH 111/133] Update dependency @babel/plugin-transform-nullish-coalescing-operator to v7.26.6 (#33584) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0b3b8b3d95..337956163f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -935,13 +935,13 @@ __metadata: linkType: hard "@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.25.9": - version: 7.26.5 - resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.26.5" + version: 7.26.6 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.26.6" dependencies: "@babel/helper-plugin-utils": "npm:^7.26.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/2e4b84745f9e8c40caf3e611641de4d6c7da6f96c2925b7fe568e3b031ed1864e325b9dffc9cda4e442fc40be43ffabb088782e980d411e0562bd5222df547ec + checksum: 10c0/574d6db7cbc5c092db5d1dece8ce26195e642b9c40dbfeaf3082058a78ad7959c1c333471cdd45f38b784ec488850548075d527b178c5010ee9bff7aa527cc7a languageName: node linkType: hard From 6356870dae9159afceac2bc3b4a3f1bfc702d5be Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:14:03 +0100 Subject: [PATCH 112/133] Update dependency sass to v1.83.4 (#33585) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 337956163f..f112e77920 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15623,8 +15623,8 @@ __metadata: linkType: hard "sass@npm:^1.62.1": - version: 1.83.1 - resolution: "sass@npm:1.83.1" + version: 1.83.4 + resolution: "sass@npm:1.83.4" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" @@ -15635,7 +15635,7 @@ __metadata: optional: true bin: sass: sass.js - checksum: 10c0/9772506cd8290df7b5e800055098e91a8a65100840fd9e90c660deb74b248b3ddbbd1a274b8f7f09777d472d2c873575357bd87939a40fb5a80bdf654985486f + checksum: 10c0/6f27f0eebfeb50222b14baaeef548ef58a05daf8abd9797e6c499334ed7ad40541767056c8693780d06ca83d8836348ea7396a923d3be439b133507993ca78be languageName: node linkType: hard From 87849d739e08036e44649643ca05e18b2723533a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:17:11 +0100 Subject: [PATCH 113/133] Update dependency rspec-github to v3 (#33589) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 2abdae151f..0efcc6562c 100644 --- a/Gemfile +++ b/Gemfile @@ -125,7 +125,7 @@ group :test do gem 'flatware-rspec' # Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab - gem 'rspec-github', '~> 2.4', require: false + gem 'rspec-github', '~> 3.0', require: false # RSpec helpers for email specs gem 'email_spec' diff --git a/Gemfile.lock b/Gemfile.lock index 89739f9053..a625765faf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -690,7 +690,7 @@ GEM rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-github (2.4.0) + rspec-github (3.0.0) rspec-core (~> 3.0) rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) @@ -994,7 +994,7 @@ DEPENDENCIES redis (~> 4.5) redis-namespace (~> 1.10) rqrcode (~> 2.2) - rspec-github (~> 2.4) + rspec-github (~> 3.0) rspec-rails (~> 7.0) rspec-sidekiq (~> 5.0) rubocop From 7c56517c7c2f3e284b912f7519682c3612d3af81 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 14 Jan 2025 09:32:29 -0500 Subject: [PATCH 114/133] Move mastodon version config to `config_for` yml (#33577) --- config/mastodon.yml | 4 ++++ lib/mastodon/version.rb | 16 ++++++++++++---- spec/presenters/instance_presenter_spec.rb | 6 ++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/config/mastodon.yml b/config/mastodon.yml index e20ba0ab05..a4442e873c 100644 --- a/config/mastodon.yml +++ b/config/mastodon.yml @@ -2,6 +2,10 @@ shared: self_destruct_value: <%= ENV.fetch('SELF_DESTRUCT', nil) %> software_update_url: <%= ENV.fetch('UPDATE_CHECK_URL', 'https://api.joinmastodon.org/update-check') %> + source: + base_url: <%= ENV.fetch('SOURCE_BASE_URL', nil) %> + repository: <%= ENV.fetch('GITHUB_REPOSITORY', 'mastodon/mastodon') %> + tag: <%= ENV.fetch('SOURCE_TAG', nil) %> version: metadata: <%= ENV.fetch('MASTODON_VERSION_METADATA', nil) %> prerelease: <%= ENV.fetch('MASTODON_VERSION_PRERELEASE', nil) %> diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index ddde4a993d..19779eb609 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -50,16 +50,16 @@ module Mastodon end def repository - ENV.fetch('GITHUB_REPOSITORY', 'mastodon/mastodon') + source_configuration[:repository] end def source_base_url - ENV.fetch('SOURCE_BASE_URL', "https://github.com/#{repository}") + source_configuration[:base_url] || "https://github.com/#{repository}" end # specify git tag or commit hash here def source_tag - ENV.fetch('SOURCE_TAG', nil) + source_configuration[:tag] end def source_url @@ -79,7 +79,15 @@ module Mastodon end def version_configuration - Rails.configuration.x.mastodon.version + mastodon_configuration.version + end + + def source_configuration + mastodon_configuration.source + end + + def mastodon_configuration + Rails.configuration.x.mastodon end end end diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb index 42f5200f3a..cc6e0533bb 100644 --- a/spec/presenters/instance_presenter_spec.rb +++ b/spec/presenters/instance_presenter_spec.rb @@ -68,6 +68,7 @@ RSpec.describe InstancePresenter do context 'with the GITHUB_REPOSITORY env variable set' do around do |example| ClimateControl.modify GITHUB_REPOSITORY: 'other/repo' do + reload_configuration example.run end end @@ -80,6 +81,7 @@ RSpec.describe InstancePresenter do context 'without the GITHUB_REPOSITORY env variable set' do around do |example| ClimateControl.modify GITHUB_REPOSITORY: nil do + reload_configuration example.run end end @@ -88,6 +90,10 @@ RSpec.describe InstancePresenter do expect(instance_presenter.source_url).to eq('https://github.com/mastodon/mastodon') end end + + def reload_configuration + Rails.configuration.x.mastodon = Rails.application.config_for(:mastodon) + end end describe '#thumbnail' do From 50013b10a50a560ebf4432cbe1782426181dba6f Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 14 Jan 2025 09:32:57 -0500 Subject: [PATCH 115/133] Add `Status::Visibility` concern to hold visibility logic (#33578) --- app/models/concerns/status/visibility.rb | 47 +++++ app/models/status.rb | 27 +-- spec/models/status_spec.rb | 32 +-- .../models/concerns/status/visibility.rb | 184 ++++++++++++++++++ 4 files changed, 234 insertions(+), 56 deletions(-) create mode 100644 app/models/concerns/status/visibility.rb create mode 100644 spec/support/examples/models/concerns/status/visibility.rb diff --git a/app/models/concerns/status/visibility.rb b/app/models/concerns/status/visibility.rb new file mode 100644 index 0000000000..e17196eb15 --- /dev/null +++ b/app/models/concerns/status/visibility.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Status::Visibility + extend ActiveSupport::Concern + + included do + enum :visibility, + { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, + suffix: :visibility, + validate: true + + scope :distributable_visibility, -> { where(visibility: %i(public unlisted)) } + scope :list_eligible_visibility, -> { where(visibility: %i(public unlisted private)) } + scope :not_direct_visibility, -> { where.not(visibility: :direct) } + + validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog? + + before_validation :set_visibility, unless: :visibility? + end + + class_methods do + def selectable_visibilities + visibilities.keys - %w(direct limited) + end + end + + def hidden? + !distributable? + end + + def distributable? + public_visibility? || unlisted_visibility? + end + + alias sign? distributable? + + private + + def set_visibility + self.visibility ||= reblog.visibility if reblog? + self.visibility ||= visibility_from_account + end + + def visibility_from_account + account.locked? ? :private : :public + end +end diff --git a/app/models/status.rb b/app/models/status.rb index 5a81b00773..c012b1ddfa 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -38,6 +38,7 @@ class Status < ApplicationRecord include Status::SearchConcern include Status::SnapshotConcern include Status::ThreadingConcern + include Status::Visibility MEDIA_ATTACHMENTS_LIMIT = 4 @@ -52,8 +53,6 @@ class Status < ApplicationRecord update_index('statuses', :proper) update_index('public_statuses', :proper) - enum :visibility, { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, suffix: :visibility, validate: true - belongs_to :application, class_name: 'Doorkeeper::Application', optional: true belongs_to :account, inverse_of: :statuses @@ -98,7 +97,6 @@ class Status < ApplicationRecord validates_with StatusLengthValidator validates_with DisallowedHashtagsValidator validates :reblog, uniqueness: { scope: :account }, if: :reblog? - validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog? accepts_nested_attributes_for :poll @@ -125,9 +123,6 @@ class Status < ApplicationRecord scope :tagged_with_none, lambda { |tag_ids| where('NOT EXISTS (SELECT * FROM statuses_tags forbidden WHERE forbidden.status_id = statuses.id AND forbidden.tag_id IN (?))', tag_ids) } - scope :distributable_visibility, -> { where(visibility: %i(public unlisted)) } - scope :list_eligible_visibility, -> { where(visibility: %i(public unlisted private)) } - scope :not_direct_visibility, -> { where.not(visibility: :direct) } after_create_commit :trigger_create_webhooks after_update_commit :trigger_update_webhooks @@ -140,7 +135,6 @@ class Status < ApplicationRecord before_validation :prepare_contents, if: :local? before_validation :set_reblog - before_validation :set_visibility before_validation :set_conversation before_validation :set_local @@ -242,16 +236,6 @@ class Status < ApplicationRecord PreviewCardsStatus.where(status_id: id).delete_all end - def hidden? - !distributable? - end - - def distributable? - public_visibility? || unlisted_visibility? - end - - alias sign? distributable? - def with_media? ordered_media_attachments.any? end @@ -351,10 +335,6 @@ class Status < ApplicationRecord end class << self - def selectable_visibilities - visibilities.keys - %w(direct limited) - end - def favourites_map(status_ids, account_id) Favourite.select(:status_id).where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |f, h| h[f.status_id] = true } end @@ -436,11 +416,6 @@ class Status < ApplicationRecord update_column(:poll_id, poll.id) if association(:poll).loaded? && poll.present? end - def set_visibility - self.visibility = reblog.visibility if reblog? && visibility.nil? - self.visibility = (account.locked? ? :private : :public) if visibility.nil? - end - def set_conversation self.thread = thread.reblog if thread&.reblog? diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 36b13df815..a197aaf1d2 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -9,6 +9,8 @@ RSpec.describe Status do let(:bob) { Fabricate(:account, username: 'bob') } let(:other) { Fabricate(:status, account: bob, text: 'Skulls for the skull god! The enemy\'s gates are sideways!') } + include_examples 'Status::Visibility' + describe '#local?' do it 'returns true when no remote URI is set' do expect(subject.local?).to be true @@ -84,36 +86,6 @@ RSpec.describe Status do end end - describe '#hidden?' do - context 'when private_visibility?' do - it 'returns true' do - subject.visibility = :private - expect(subject.hidden?).to be true - end - end - - context 'when direct_visibility?' do - it 'returns true' do - subject.visibility = :direct - expect(subject.hidden?).to be true - end - end - - context 'when public_visibility?' do - it 'returns false' do - subject.visibility = :public - expect(subject.hidden?).to be false - end - end - - context 'when unlisted_visibility?' do - it 'returns false' do - subject.visibility = :unlisted - expect(subject.hidden?).to be false - end - end - end - describe '#content' do it 'returns the text of the status if it is not a reblog' do expect(subject.content).to eql subject.text diff --git a/spec/support/examples/models/concerns/status/visibility.rb b/spec/support/examples/models/concerns/status/visibility.rb new file mode 100644 index 0000000000..dd9e0bddf0 --- /dev/null +++ b/spec/support/examples/models/concerns/status/visibility.rb @@ -0,0 +1,184 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.shared_examples 'Status::Visibility' do + describe 'Validations' do + context 'when status is a reblog' do + subject { Fabricate.build :status, reblog: Fabricate(:status) } + + it { is_expected.to allow_values('public', 'unlisted', 'private').for(:visibility) } + it { is_expected.to_not allow_values('direct', 'limited').for(:visibility) } + end + + context 'when status is not reblog' do + subject { Fabricate.build :status, reblog_of_id: nil } + + it { is_expected.to allow_values('public', 'unlisted', 'private', 'direct', 'limited').for(:visibility) } + end + end + + describe 'Scopes' do + let!(:direct_status) { Fabricate :status, visibility: :direct } + let!(:limited_status) { Fabricate :status, visibility: :limited } + let!(:private_status) { Fabricate :status, visibility: :private } + let!(:public_status) { Fabricate :status, visibility: :public } + let!(:unlisted_status) { Fabricate :status, visibility: :unlisted } + + describe '.list_eligible_visibility' do + it 'returns appropriate records' do + expect(Status.list_eligible_visibility) + .to include( + private_status, + public_status, + unlisted_status + ) + .and not_include(direct_status) + .and not_include(limited_status) + end + end + + describe '.distributable_visibility' do + it 'returns appropriate records' do + expect(Status.distributable_visibility) + .to include( + public_status, + unlisted_status + ) + .and not_include(private_status) + .and not_include(direct_status) + .and not_include(limited_status) + end + end + + describe '.not_direct_visibility' do + it 'returns appropriate records' do + expect(Status.not_direct_visibility) + .to include( + limited_status, + private_status, + public_status, + unlisted_status + ) + .and not_include(direct_status) + end + end + end + + describe 'Callbacks' do + describe 'Setting visibility in before validation' do + subject { Fabricate.build :status, visibility: nil } + + context 'when explicit value is set' do + before { subject.visibility = :public } + + it 'does not change' do + expect { subject.valid? } + .to_not change(subject, :visibility) + end + end + + context 'when status is a reblog' do + before { subject.reblog = Fabricate(:status, visibility: :public) } + + it 'changes to match the reblog' do + expect { subject.valid? } + .to change(subject, :visibility).to('public') + end + end + + context 'when account is locked' do + before { subject.account = Fabricate.build(:account, locked: true) } + + it 'changes to private' do + expect { subject.valid? } + .to change(subject, :visibility).to('private') + end + end + + context 'when account is not locked' do + before { subject.account = Fabricate.build(:account, locked: false) } + + it 'changes to public' do + expect { subject.valid? } + .to change(subject, :visibility).to('public') + end + end + end + end + + describe '.selectable_visibilities' do + it 'returns options available for default privacy selection' do + expect(Status.selectable_visibilities) + .to match(%w(public unlisted private)) + end + end + + describe '#hidden?' do + subject { Status.new } + + context 'when visibility is private' do + before { subject.visibility = :private } + + it { is_expected.to be_hidden } + end + + context 'when visibility is direct' do + before { subject.visibility = :direct } + + it { is_expected.to be_hidden } + end + + context 'when visibility is limited' do + before { subject.visibility = :limited } + + it { is_expected.to be_hidden } + end + + context 'when visibility is public' do + before { subject.visibility = :public } + + it { is_expected.to_not be_hidden } + end + + context 'when visibility is unlisted' do + before { subject.visibility = :unlisted } + + it { is_expected.to_not be_hidden } + end + end + + describe '#distributable?' do + subject { Status.new } + + context 'when visibility is public' do + before { subject.visibility = :public } + + it { is_expected.to be_distributable } + end + + context 'when visibility is unlisted' do + before { subject.visibility = :unlisted } + + it { is_expected.to be_distributable } + end + + context 'when visibility is private' do + before { subject.visibility = :private } + + it { is_expected.to_not be_distributable } + end + + context 'when visibility is direct' do + before { subject.visibility = :direct } + + it { is_expected.to_not be_distributable } + end + + context 'when visibility is limited' do + before { subject.visibility = :limited } + + it { is_expected.to_not be_distributable } + end + end +end From bfe73e153d425312211eb1114ff20275aa369059 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 17:34:26 +0100 Subject: [PATCH 116/133] Update dependency postcss to v8.5.1 (#33586) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index f112e77920..a99c1e23c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12332,12 +12332,12 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.7": - version: 3.3.7 - resolution: "nanoid@npm:3.3.7" +"nanoid@npm:^3.3.8": + version: 3.3.8 + resolution: "nanoid@npm:3.3.8" bin: nanoid: bin/nanoid.cjs - checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 + checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120 languageName: node linkType: hard @@ -14209,13 +14209,13 @@ __metadata: linkType: hard "postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.49": - version: 8.4.49 - resolution: "postcss@npm:8.4.49" + version: 8.5.1 + resolution: "postcss@npm:8.5.1" dependencies: - nanoid: "npm:^3.3.7" + nanoid: "npm:^3.3.8" picocolors: "npm:^1.1.1" source-map-js: "npm:^1.2.1" - checksum: 10c0/f1b3f17aaf36d136f59ec373459f18129908235e65dbdc3aee5eef8eba0756106f52de5ec4682e29a2eab53eb25170e7e871b3e4b52a8f1de3d344a514306be3 + checksum: 10c0/c4d90c59c98e8a0c102b77d3f4cac190f883b42d63dc60e2f3ed840f16197c0c8e25a4327d2e9a847b45a985612317dc0534178feeebd0a1cf3eb0eecf75cae4 languageName: node linkType: hard From e1d7efadc04dd0826c6bcfe43325688566e13881 Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Tue, 14 Jan 2025 10:35:58 -0600 Subject: [PATCH 117/133] Fix libyaml missing from Dockerfile build stage (#33591) --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 97c7a91499..deeac8b466 100644 --- a/Dockerfile +++ b/Dockerfile @@ -153,6 +153,7 @@ RUN \ libpq-dev \ libssl-dev \ libtool \ + libyaml-dev \ meson \ nasm \ pkg-config \ From 68c9f91ccb46b14ce43424479b3ff8ab28800997 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 14 Jan 2025 12:40:55 -0500 Subject: [PATCH 118/133] Treat non-null but blank account domain as local (#33576) --- app/models/account.rb | 4 +++- spec/models/account_spec.rb | 22 ++++++++++------------ spec/services/import_service_spec.rb | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index 6258857b1b..05e833d575 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -126,6 +126,8 @@ class Account < ApplicationRecord validates :uri, absence: true end + validates :domain, exclusion: { in: [''] } + normalizes :username, with: ->(username) { username.squish } scope :without_internal, -> { where(id: 1...) } @@ -187,7 +189,7 @@ class Account < ApplicationRecord end def remote? - domain.present? + !domain.nil? end def moved? diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 5b995b4af6..a0187b6211 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -49,14 +49,16 @@ RSpec.describe Account do end describe '#local?' do - it 'returns true when domain is null' do - account = Fabricate(:account, domain: nil) - expect(account).to be_local + context 'when the domain is null' do + subject { Fabricate.build :account, domain: nil } + + it { is_expected.to be_local } end - it 'returns false when domain is present' do - account = Fabricate(:account, domain: 'foreign.tld') - expect(account).to_not be_local + context 'when the domain is present' do + subject { Fabricate.build :account, domain: 'host.example' } + + it { is_expected.to_not be_local } end end @@ -67,12 +69,6 @@ RSpec.describe Account do it { is_expected.to_not be_remote } end - context 'when the domain is blank' do - subject { Fabricate.build :account, domain: '' } - - it { is_expected.to_not be_remote } - end - context 'when the domain is present' do subject { Fabricate.build :account, domain: 'host.example' } @@ -557,6 +553,8 @@ RSpec.describe Account do describe 'Validations' do it { is_expected.to validate_presence_of(:username) } + it { is_expected.to_not allow_value('').for(:domain) } + context 'when account is local' do subject { Fabricate.build :account, domain: nil } diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb index 0a99c5e748..2e1358c635 100644 --- a/spec/services/import_service_spec.rb +++ b/spec/services/import_service_spec.rb @@ -204,7 +204,7 @@ RSpec.describe ImportService, :inline_jobs do subject { described_class.new } let(:csv) { attachment_fixture('bookmark-imports.txt') } - let(:local_account) { Fabricate(:account, username: 'foo', domain: '') } + let(:local_account) { Fabricate(:account, username: 'foo', domain: nil) } let!(:remote_status) { Fabricate(:status, uri: 'https://example.com/statuses/1312') } let!(:direct_status) { Fabricate(:status, uri: 'https://example.com/statuses/direct', visibility: :direct) } From 563ff91cf8ffa61a435ed756b51d557798f4feea Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 14 Jan 2025 11:42:06 +0100 Subject: [PATCH 119/133] [Glitch] Use final specification for new WebPush subscriptions in web interface Port e2f085e2b2cec08dc1f5ae825730c2a3bf62e054 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/actions/push_notifications/registerer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/actions/push_notifications/registerer.js b/app/javascript/flavours/glitch/actions/push_notifications/registerer.js index b3d3850e31..647a6bd9fb 100644 --- a/app/javascript/flavours/glitch/actions/push_notifications/registerer.js +++ b/app/javascript/flavours/glitch/actions/push_notifications/registerer.js @@ -33,7 +33,7 @@ const unsubscribe = ({ registration, subscription }) => subscription ? subscription.unsubscribe().then(() => registration) : registration; const sendSubscriptionToBackend = (subscription) => { - const params = { subscription }; + const params = { subscription: { ...subscription.toJSON(), standard: true } }; if (me) { const data = pushNotificationsSetting.get(me); From 09bd5aa15679eff965ae597a0d3b646decccc36d Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 14 Jan 2025 14:10:48 +0100 Subject: [PATCH 120/133] [Glitch] Redirect new users to onboarding Port e9462960a7cf416b9e459d599a0f2cc8c5b070f0 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/api_types/accounts.ts | 2 +- .../flavours/glitch/features/onboarding/profile.tsx | 6 +++++- .../flavours/glitch/features/ui/index.jsx | 13 +++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/app/javascript/flavours/glitch/api_types/accounts.ts b/app/javascript/flavours/glitch/api_types/accounts.ts index fdbd7523fc..3f8b27497f 100644 --- a/app/javascript/flavours/glitch/api_types/accounts.ts +++ b/app/javascript/flavours/glitch/api_types/accounts.ts @@ -19,7 +19,7 @@ export interface BaseApiAccountJSON { avatar_static: string; bot: boolean; created_at: string; - discoverable: boolean; + discoverable?: boolean; indexable: boolean; display_name: string; emojis: ApiCustomEmojiJSON[]; diff --git a/app/javascript/flavours/glitch/features/onboarding/profile.tsx b/app/javascript/flavours/glitch/features/onboarding/profile.tsx index 41fbdea1ab..d5d35368f0 100644 --- a/app/javascript/flavours/glitch/features/onboarding/profile.tsx +++ b/app/javascript/flavours/glitch/features/onboarding/profile.tsx @@ -12,6 +12,7 @@ import AddPhotoAlternateIcon from '@/material-icons/400-24px/add_photo_alternate import EditIcon from '@/material-icons/400-24px/edit.svg?react'; import PersonIcon from '@/material-icons/400-24px/person.svg?react'; import { updateAccount } from 'flavours/glitch/actions/accounts'; +import { closeOnboarding } from 'flavours/glitch/actions/onboarding'; import { Button } from 'flavours/glitch/components/button'; import { Column } from 'flavours/glitch/components/column'; import { ColumnHeader } from 'flavours/glitch/components/column_header'; @@ -58,7 +59,9 @@ export const Profile: React.FC<{ ); const [avatar, setAvatar] = useState(); const [header, setHeader] = useState(); - const [discoverable, setDiscoverable] = useState(true); + const [discoverable, setDiscoverable] = useState( + account?.discoverable ?? true, + ); const [isSaving, setIsSaving] = useState(false); const [errors, setErrors] = useState(); const avatarFileRef = createRef(); @@ -132,6 +135,7 @@ export const Profile: React.FC<{ ) .then(() => { history.push('/start/follows'); + dispatch(closeOnboarding()); return ''; }) // eslint-disable-next-line @typescript-eslint/use-unknown-in-catch-callback-variable diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index db6d6a1fca..2a67355a51 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -100,6 +100,7 @@ const mapStateToProps = state => ({ hicolorPrivacyIcons: state.getIn(['local_settings', 'hicolor_privacy_icons']), moved: state.getIn(['accounts', me, 'moved']) && state.getIn(['accounts', state.getIn(['accounts', me, 'moved'])]), firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION, + newAccount: !state.getIn(['accounts', me, 'note']) && !state.getIn(['accounts', me, 'bot']) && state.getIn(['accounts', me, 'following_count'], 0) === 0 && state.getIn(['accounts', me, 'statuses_count'], 0) === 0, username: state.getIn(['accounts', me, 'username']), }); @@ -144,6 +145,7 @@ class SwitchingColumnsArea extends PureComponent { children: PropTypes.node, location: PropTypes.object, singleColumn: PropTypes.bool, + forceOnboarding: PropTypes.bool, }; UNSAFE_componentWillMount () { @@ -174,14 +176,16 @@ class SwitchingColumnsArea extends PureComponent { }; render () { - const { children, singleColumn } = this.props; + const { children, singleColumn, forceOnboarding } = this.props; const { signedIn } = this.props.identity; const pathName = this.props.location.pathname; let redirect; if (signedIn) { - if (singleColumn) { + if (forceOnboarding) { + redirect = ; + } else if (singleColumn) { redirect = ; } else { redirect = ; @@ -292,6 +296,7 @@ class UI extends PureComponent { moved: PropTypes.map, layout: PropTypes.string.isRequired, firstLaunch: PropTypes.bool, + newAccount: PropTypes.bool, username: PropTypes.string, ...WithRouterPropTypes, }; @@ -615,7 +620,7 @@ class UI extends PureComponent { render () { const { draggingOver } = this.state; - const { children, isWide, location, layout, moved } = this.props; + const { children, isWide, location, layout, moved, firstLaunch, newAccount } = this.props; const className = classNames('ui', { 'wide': isWide, @@ -662,7 +667,7 @@ class UI extends PureComponent {
- + {children} From 539515483246f66c0aa9fd3e1102dafc2c3243b5 Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Tue, 14 Jan 2025 23:47:09 +0100 Subject: [PATCH 121/133] Fix media attachments in report modal Media attachments weren't shown in the report modal since they were passed as `media` to `StatusContent`, but `StatusContent` doesn't use the media prop anymore. Instead match the file with vanilla. Signed-off-by: Plastikmensch --- .../glitch/features/report/components/status_check_box.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/features/report/components/status_check_box.jsx b/app/javascript/flavours/glitch/features/report/components/status_check_box.jsx index 06ed6ac33c..b75d0537c7 100644 --- a/app/javascript/flavours/glitch/features/report/components/status_check_box.jsx +++ b/app/javascript/flavours/glitch/features/report/components/status_check_box.jsx @@ -46,7 +46,8 @@ class StatusCheckBox extends PureComponent {
- } /> + + ); From e489410b0f1e3f5a84e58320831d54c1b6d7b6b0 Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Wed, 15 Jan 2025 08:18:39 +0100 Subject: [PATCH 122/133] Fix attachment list in conversation component (#2941) `media` is no longer part of `StatusContent`, so the attachment list wasn't shown in the private mentions column. Signed-off-by: Plastikmensch --- .../direct_timeline/components/conversation.jsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx index 5df3af4b7a..c3e5fc72b8 100644 --- a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx +++ b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx @@ -177,11 +177,6 @@ export const Conversation = ({ conversation, scrollKey, onMoveUp, onMoveDown }) toggleHidden: handleShowMore, }; - let media = null; - if (lastStatus.get('media_attachments').size > 0) { - media = ; - } - return (
@@ -206,9 +201,15 @@ export const Conversation = ({ conversation, scrollKey, onMoveUp, onMoveDown }) expanded={sharedCWState ? lastStatus.get('hidden') : expanded} onExpandedToggle={handleShowMore} collapsible - media={media} /> + {lastStatus.get('media_attachments').size > 0 && ( + + )} +
From 9f03e5b53ae0c4088ea989b3ad4e3555e33daaa1 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 15 Jan 2025 02:47:34 -0500 Subject: [PATCH 123/133] Expand coverage of `admin/*blocks` areas (#33594) --- spec/system/admin/email_domain_blocks_spec.rb | 25 ++++++++++++++++--- spec/system/admin/ip_blocks_spec.rb | 21 ++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/spec/system/admin/email_domain_blocks_spec.rb b/spec/system/admin/email_domain_blocks_spec.rb index acf5027eda..807cfb3768 100644 --- a/spec/system/admin/email_domain_blocks_spec.rb +++ b/spec/system/admin/email_domain_blocks_spec.rb @@ -5,9 +5,7 @@ require 'rails_helper' RSpec.describe 'Admin::EmailDomainBlocks' do let(:current_user) { Fabricate(:admin_user) } - before do - sign_in current_user - end + before { sign_in current_user } describe 'Performing batch updates' do before do @@ -22,6 +20,27 @@ RSpec.describe 'Admin::EmailDomainBlocks' do end end + context 'with a selected block' do + let!(:email_domain_block) { Fabricate :email_domain_block } + + it 'deletes the block' do + visit admin_email_domain_blocks_path + + check_item + + expect { click_on button_for_delete } + .to change(EmailDomainBlock, :count).by(-1) + expect { email_domain_block.reload } + .to raise_error(ActiveRecord::RecordNotFound) + end + end + + def check_item + within '.batch-table__row' do + find('input[type=checkbox]').check + end + end + def button_for_delete I18n.t('admin.email_domain_blocks.delete') end diff --git a/spec/system/admin/ip_blocks_spec.rb b/spec/system/admin/ip_blocks_spec.rb index 8e8c8031c8..3bed506b68 100644 --- a/spec/system/admin/ip_blocks_spec.rb +++ b/spec/system/admin/ip_blocks_spec.rb @@ -48,6 +48,27 @@ RSpec.describe 'Admin::IpBlocks' do end end + context 'with a selected block' do + let!(:ip_block) { Fabricate :ip_block } + + it 'deletes the block' do + visit admin_ip_blocks_path + + check_item + + expect { click_on button_for_delete } + .to change(IpBlock, :count).by(-1) + expect { ip_block.reload } + .to raise_error(ActiveRecord::RecordNotFound) + end + end + + def check_item + within '.batch-table__row' do + find('input[type=checkbox]').check + end + end + def button_for_delete I18n.t('admin.ip_blocks.delete') end From 1ae574e32a634850141f02625958a1c69d2acefb Mon Sep 17 00:00:00 2001 From: fusagiko / takayamaki <24884114+takayamaki@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:48:26 +0900 Subject: [PATCH 124/133] Enable parallel execution for linting tasks in HAML workflows (#33593) --- .github/workflows/lint-haml.yml | 2 +- lint-staged.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml index 499be2010a..9361358078 100644 --- a/.github/workflows/lint-haml.yml +++ b/.github/workflows/lint-haml.yml @@ -43,4 +43,4 @@ jobs: - name: Run haml-lint run: | echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json" - bin/haml-lint --reporter github + bin/haml-lint --parallel --reporter github diff --git a/lint-staged.config.js b/lint-staged.config.js index 63f5258a94..baf5d0d454 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -3,7 +3,7 @@ const config = { 'Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a', '*.{js,jsx,ts,tsx}': 'eslint --fix', '*.{css,scss}': 'stylelint --fix', - '*.haml': 'bin/haml-lint -a', + '*.haml': 'bin/haml-lint -a --parallel', '**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit', }; From 2a0951e98780f71ee7e672d33e2b9f9560cc9d98 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 13:48:52 +0100 Subject: [PATCH 125/133] Update dependency stackprof to v0.2.27 (#33596) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index a625765faf..3bc530f983 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -793,7 +793,7 @@ GEM simplecov-html (0.13.1) simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) - stackprof (0.2.26) + stackprof (0.2.27) stoplight (4.1.0) redlock (~> 1.0) stringio (3.1.2) From ea01ecd44103b34a6b5a0a115aba9509ed24850e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 13:49:12 +0100 Subject: [PATCH 126/133] Update dependency opentelemetry-instrumentation-rails to v0.34.1 (#33595) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3bc530f983..a4c5f8ad98 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -529,7 +529,7 @@ GEM opentelemetry-instrumentation-rack (0.25.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-rails (0.34.0) + opentelemetry-instrumentation-rails (0.34.1) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-action_mailer (~> 0.3.0) opentelemetry-instrumentation-action_pack (~> 0.10.0) @@ -538,6 +538,7 @@ GEM opentelemetry-instrumentation-active_record (~> 0.8.0) opentelemetry-instrumentation-active_support (~> 0.7.0) opentelemetry-instrumentation-base (~> 0.22.1) + opentelemetry-instrumentation-concurrent_ruby (~> 0.21.4) opentelemetry-instrumentation-redis (0.25.7) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) From c20824fa760061cf8fba258dbbd6b0a615df4749 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 15 Jan 2025 09:29:14 -0500 Subject: [PATCH 127/133] Promote `Style/WordArray` rule out of todo into main config (#33580) --- .rubocop/style.yml | 3 +++ .rubocop_todo.yml | 9 +-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.rubocop/style.yml b/.rubocop/style.yml index df1da2ba36..f59340d452 100644 --- a/.rubocop/style.yml +++ b/.rubocop/style.yml @@ -58,3 +58,6 @@ Style/TrailingCommaInArrayLiteral: Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: comma + +Style/WordArray: + MinSize: 3 # Override default of 2 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 12ef0ad620..38aec67bef 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.69.2. +# using RuboCop version 1.70.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -103,10 +103,3 @@ Style/RedundantConstantBase: Exclude: - 'config/environments/production.rb' - 'config/initializers/sidekiq.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: WordRegex. -# SupportedStyles: percent, brackets -Style/WordArray: - EnforcedStyle: percent - MinSize: 3 From 72abf052693ed0c70c7437da24ce860b8f81fad2 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 16 Jan 2025 04:00:04 -0500 Subject: [PATCH 128/133] Add "needs refresh" scenario to `api/v1/polls` request spec (#33608) --- spec/requests/api/v1/polls_spec.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/spec/requests/api/v1/polls_spec.rb b/spec/requests/api/v1/polls_spec.rb index fd38297931..c93231e1ee 100644 --- a/spec/requests/api/v1/polls_spec.rb +++ b/spec/requests/api/v1/polls_spec.rb @@ -36,6 +36,31 @@ RSpec.describe 'Polls' do end end + context 'when poll is remote and needs refresh' do + let(:poll) { Fabricate(:poll, last_fetched_at: nil, account: remote_account, status: status) } + let(:remote_account) { Fabricate :account, domain: 'host.example' } + let(:service) { instance_double(ActivityPub::FetchRemotePollService, call: nil) } + let(:status) { Fabricate(:status, visibility: 'public', account: remote_account) } + + before { allow(ActivityPub::FetchRemotePollService).to receive(:new).and_return(service) } + + it 'returns poll data and calls fetch remote service' do + subject + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body).to match( + a_hash_including( + id: poll.id.to_s + ) + ) + expect(service) + .to have_received(:call).with(poll, user.account) + end + end + context 'when parent status is private' do let(:visibility) { 'private' } From 998cf0dd53de5d19ef47c3e8de575d07b05f42eb Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 16 Jan 2025 04:03:46 -0500 Subject: [PATCH 129/133] Convert `auth/setup` spec controller->system/request (#33604) --- app/controllers/auth/setup_controller.rb | 2 +- .../controllers/auth/setup_controller_spec.rb | 25 ---------------- spec/requests/auth/setup_spec.rb | 27 +++++++++++++++++ spec/system/auth/setup_spec.rb | 30 +++++++++++++++++++ 4 files changed, 58 insertions(+), 26 deletions(-) delete mode 100644 spec/controllers/auth/setup_controller_spec.rb create mode 100644 spec/requests/auth/setup_spec.rb create mode 100644 spec/system/auth/setup_spec.rb diff --git a/app/controllers/auth/setup_controller.rb b/app/controllers/auth/setup_controller.rb index ad872dc607..376a30c16f 100644 --- a/app/controllers/auth/setup_controller.rb +++ b/app/controllers/auth/setup_controller.rb @@ -18,7 +18,7 @@ class Auth::SetupController < ApplicationController if @user.update(user_params) @user.resend_confirmation_instructions unless @user.confirmed? - redirect_to auth_setup_path, notice: I18n.t('auth.setup.new_confirmation_instructions_sent') + redirect_to auth_setup_path, notice: t('auth.setup.new_confirmation_instructions_sent') else render :show end diff --git a/spec/controllers/auth/setup_controller_spec.rb b/spec/controllers/auth/setup_controller_spec.rb deleted file mode 100644 index 28b07cb4b2..0000000000 --- a/spec/controllers/auth/setup_controller_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Auth::SetupController do - render_views - - describe 'GET #show' do - context 'with a signed out request' do - it 'returns http redirect' do - get :show - expect(response).to be_redirect - end - end - - context 'with an unconfirmed signed in user' do - before { sign_in Fabricate(:user, confirmed_at: nil) } - - it 'returns http success' do - get :show - expect(response).to have_http_status(200) - end - end - end -end diff --git a/spec/requests/auth/setup_spec.rb b/spec/requests/auth/setup_spec.rb new file mode 100644 index 0000000000..fa3c196805 --- /dev/null +++ b/spec/requests/auth/setup_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Auth Setup' do + describe 'GET /auth/setup' do + context 'with a signed out request' do + it 'redirects to root' do + get '/auth/setup' + + expect(response) + .to redirect_to(new_user_session_url) + end + end + + context 'with a confirmed signed in user' do + before { sign_in Fabricate(:user, confirmed_at: 2.days.ago) } + + it 'redirects to root' do + get '/auth/setup' + + expect(response) + .to redirect_to(root_url) + end + end + end +end diff --git a/spec/system/auth/setup_spec.rb b/spec/system/auth/setup_spec.rb new file mode 100644 index 0000000000..154f8cd5fa --- /dev/null +++ b/spec/system/auth/setup_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Auth Setup' do + context 'with an unconfirmed signed in user' do + let(:user) { Fabricate(:user, confirmed_at: nil) } + + before { sign_in(user) } + + it 'can update email address' do + visit auth_setup_path + + expect(page) + .to have_content(I18n.t('auth.setup.title')) + + find('summary.lead').click + fill_in 'user_email', with: 'new-email@example.host' + + expect { submit_form } + .to(change { user.reload.unconfirmed_email }) + expect(page) + .to have_content(I18n.t('auth.setup.new_confirmation_instructions_sent')) + end + + def submit_form + find('[name=button]').click + end + end +end From 3db84989039c2dbba5dc6797092fa7cbbbcb7db8 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 16 Jan 2025 04:09:06 -0500 Subject: [PATCH 130/133] Fix `Style/MutableConstant` cop (#33602) --- .rubocop_todo.yml | 9 --------- app/models/tag.rb | 8 ++++---- app/services/delete_account_service.rb | 2 +- lib/mastodon/migration_warning.rb | 2 +- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 38aec67bef..f2d3418b64 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -69,15 +69,6 @@ Style/MapToHash: Exclude: - 'app/models/status.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: literals, strict -Style/MutableConstant: - Exclude: - - 'app/models/tag.rb' - - 'app/services/delete_account_service.rb' - - 'lib/mastodon/migration_warning.rb' - # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: diff --git a/app/models/tag.rb b/app/models/tag.rb index c9115b905b..d29cd220f0 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -35,11 +35,11 @@ class Tag < ApplicationRecord has_one :trend, class_name: 'TagTrend', inverse_of: :tag, dependent: :destroy HASHTAG_SEPARATORS = "_\u00B7\u30FB\u200c" - HASHTAG_FIRST_SEQUENCE_CHUNK_ONE = "[[:word:]_][[:word:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}]" - HASHTAG_FIRST_SEQUENCE_CHUNK_TWO = "[[:word:]#{HASHTAG_SEPARATORS}]*[[:word:]_]" - HASHTAG_FIRST_SEQUENCE = "(#{HASHTAG_FIRST_SEQUENCE_CHUNK_ONE}#{HASHTAG_FIRST_SEQUENCE_CHUNK_TWO})" + HASHTAG_FIRST_SEQUENCE_CHUNK_ONE = "[[:word:]_][[:word:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}]".freeze + HASHTAG_FIRST_SEQUENCE_CHUNK_TWO = "[[:word:]#{HASHTAG_SEPARATORS}]*[[:word:]_]".freeze + HASHTAG_FIRST_SEQUENCE = "(#{HASHTAG_FIRST_SEQUENCE_CHUNK_ONE}#{HASHTAG_FIRST_SEQUENCE_CHUNK_TWO})".freeze HASHTAG_LAST_SEQUENCE = '([[:word:]_]*[[:alpha:]][[:word:]_]*)' - HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}" + HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}".freeze HASHTAG_RE = %r{(? Date: Thu, 16 Jan 2025 10:13:39 +0100 Subject: [PATCH 131/133] New Crowdin Translations (automated) (#33609) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/sk.json | 9 +++++++++ config/locales/activerecord.de.yml | 2 ++ config/locales/eo.yml | 7 +++++++ config/locales/simple_form.de.yml | 1 + config/locales/sk.yml | 2 ++ 5 files changed, 21 insertions(+) diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index d0617a52c8..00eb948985 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -378,6 +378,8 @@ "ignore_notifications_modal.not_followers_title": "Nevšímať si oznámenia od ľudí, ktorí ťa nenasledujú?", "ignore_notifications_modal.not_following_title": "Nevšímať si oznámenia od ľudí, ktorých nenasleduješ?", "ignore_notifications_modal.private_mentions_title": "Nevšímať si oznámenia o nevyžiadaných súkromných spomínaniach?", + "interaction_modal.action.favourite": "Pre pokračovanie si musíš obľúbiť zo svojho účtu.", + "interaction_modal.action.follow": "Pre pokračovanie musíš nasledovať zo svojho účtu.", "interaction_modal.action.reply": "Pre pokračovanie musíš odpovedať s tvojho účtu.", "interaction_modal.action.vote": "Pre pokračovanie musíš hlasovať s tvojho účtu.", "interaction_modal.go": "Prejdi", @@ -389,6 +391,7 @@ "interaction_modal.title.reblog": "Zdieľať príspevok od {name}", "interaction_modal.title.reply": "Odpovedať na príspevok od {name}", "interaction_modal.title.vote": "Hlasuj v ankete od {name}", + "interaction_modal.username_prompt": "Napr. {example}", "intervals.full.days": "{number, plural, one {# deň} few {# dni} many {# dní} other {# dní}}", "intervals.full.hours": "{number, plural, one {# hodina} few {# hodiny} many {# hodín} other {# hodín}}", "intervals.full.minutes": "{number, plural, one {# minúta} few {# minúty} many {# minút} other {# minút}}", @@ -517,6 +520,7 @@ "notification.moderation_warning": "Dostal/a si varovanie od moderátora", "notification.moderation_warning.action_delete_statuses": "Niektoré z tvojich príspevkov boli odstránené.", "notification.moderation_warning.action_disable": "Tvoj účet bol vypnutý.", + "notification.moderation_warning.action_mark_statuses_as_sensitive": "Niektoré tvoje príspevky boli označené za chúlostivé.", "notification.moderation_warning.action_none": "Tvoj účet dostal upozornenie od moderátora.", "notification.moderation_warning.action_sensitive": "Tvoje príspevky budú odteraz označované ako chúlostivé.", "notification.moderation_warning.action_silence": "Tvoj účet bol obmedzený.", @@ -575,9 +579,11 @@ "notifications.policy.accept_hint": "Ukáž v oznámeniach", "notifications.policy.drop": "Ignoruj", "notifications.policy.filter": "Triediť", + "notifications.policy.filter_limited_accounts_hint": "Obmedzené moderátormi servera", "notifications.policy.filter_limited_accounts_title": "Moderované účty", "notifications.policy.filter_new_accounts_title": "Nové účty", "notifications.policy.filter_not_followers_title": "Ľudia, ktorí ťa nenasledujú", + "notifications.policy.filter_not_following_hint": "Pokiaľ ich ručne neschváliš", "notifications.policy.filter_not_following_title": "Ľudia, ktorých nenasleduješ", "notifications.policy.filter_private_mentions_title": "Nevyžiadané priame spomenutia", "notifications.policy.title": "Spravuj oznámenia od…", @@ -625,6 +631,7 @@ "privacy_policy.title": "Pravidlá ochrany súkromia", "recommended": "Odporúčané", "refresh": "Obnoviť", + "regeneration_indicator.please_stand_by": "Prosím, čakajte.", "regeneration_indicator.preparing_your_home_feed": "Pripravuje sa tvoj domáci kanál…", "relative_time.days": "{number} dní", "relative_time.full.days": "Pred {number, plural, one {# dňom} other {# dňami}}", @@ -716,6 +723,7 @@ "server_banner.about_active_users": "Ľudia používajúci tento server za posledných 30 dní (aktívni používatelia za mesiac)", "server_banner.active_users": "Aktívne účty", "server_banner.administered_by": "Správa servera:", + "server_banner.is_one_of_many": "{domain} je jeden z mnohých nezávislých Mastodon serverov, ktoré môžeš použiť na zúčastňovanie sa v rámci fediversa.", "server_banner.server_stats": "Štatistiky servera:", "sign_in_banner.create_account": "Vytvoriť účet", "sign_in_banner.sign_in": "Prihlásiť sa", @@ -758,6 +766,7 @@ "status.reblogs.empty": "Nikto ešte tento príspevok nezdieľal. Keď tak niekto urobí, zobrazí sa to tu.", "status.redraft": "Vymazať a prepísať", "status.remove_bookmark": "Odstrániť záložku", + "status.remove_favourite": "Odstráň z obľúbených", "status.replied_in_thread": "Odpovedal/a vo vlákne", "status.replied_to": "Odpoveď na {name}", "status.reply": "Odpovedať", diff --git a/config/locales/activerecord.de.yml b/config/locales/activerecord.de.yml index bdd916634a..99d50d49e4 100644 --- a/config/locales/activerecord.de.yml +++ b/config/locales/activerecord.de.yml @@ -24,6 +24,8 @@ de: models: account: attributes: + fields: + fields_with_values_missing_labels: enthält Werte, bei denen Beschriftungen fehlen username: invalid: nur Buchstaben, Ziffern und Unterstriche reserved: ist bereits vergeben diff --git a/config/locales/eo.yml b/config/locales/eo.yml index ee45921aaa..400b03958e 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -936,17 +936,22 @@ eo: generate: Uzi ŝablonon generates: action: Generi + chance_to_review_html: "La generataj kondiĉoj de uzado ne aŭtomate publikiĝos. Estos oportuni por vi kontroli la rezultojn. Bonvole entajpu la necesajn detalojn por daŭrigi." + explanation_html: La modelo por la kondiĉoj de la servo disponeblas sole por informi. Ĝi nepre ne estas leĝa konsilo pri iu ajn temo. Bonvole konsultu vian propran leĝan konsilanton pri via situacio kaj iuj leĝaj neklarecoj. title: Agordo de kondiĉoj de uzado history: Historio live: Antaŭmontro no_history: Ankoraŭ ne estas registritaj ŝanĝoj de la kondiĉoj de la servo. no_terms_of_service_html: Vi nuntempe ne havas iujn ajn kondiĉojn de la servo agordita. La kondiĉoj de la servo celas doni klarecon kaj protekti vin kontraŭ eblaj respondecoj en disputoj kun viaj uzantoj. + notified_on_html: Uzantojn sciigis je %{date} notify_users: Informu uzantojn preview: + explanation_html: 'La retmesaĝo estos alsendata al %{display_count} uzantoj, kiuj kreis konton antaŭ %{date}. La sekvonta teksto inkluziviĝos en la retmesaĝo:' send_preview: Sendu antaŭrigardon al %{email} send_to_all: one: Sendi %{display_count} retpoŝton other: Sendi %{display_count} retpoŝtojn + title: Antaŭmontri sciigon pri la kondiĉoj de la servo publish: Publikigi published_on_html: Publikigita je %{date} save_draft: Konservi malneton @@ -1930,6 +1935,8 @@ eo: subject: Via konto estas alirita de nova IP-adreso title: Nova saluto terms_of_service_changed: + agreement: Se vi daŭrige uzos %{domain}, vi aŭtomate interkonsentos pri ĉi tiuj kondiĉoj. Se vi malkonsentas pri la novaj kondiĉoj, vi ĉiutempe rajtas nuligi la interkonsenton kun %{domain} per forigi vian konton. + changelog: 'Facile dirite, la ŝanĝoj estas la jenaj:' description: 'Vi ricevas ĉi tiun retmesaĝon ĉar ni faras iujn ŝanĝojn al niaj servokondiĉoj ĉe %{domain}. Ni instigas vin revizii la ĝisdatigitajn kondiĉojn tute ĉi tie:' description_html: Vi ricevas ĉi tiun retmesaĝon ĉar ni faras iujn ŝanĝojn al niaj servokondiĉoj ĉe %{domain}. Ni instigas vin revizii la ĝisdatigitajn kondiĉojn plene ĉi tie. sign_off: La teamo de %{domain} diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 201af831ad..ea0681e1af 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -137,6 +137,7 @@ de: admin_email: Rechtliche Hinweise umfassen Gegendarstellungen, Gerichtsbeschlüsse, Anfragen zum Herunternehmen von Inhalten und Anfragen von Strafverfolgungsbehörden. arbitration_address: Kann wie die Anschrift hierüber sein oder „N/A“, falls eine E-Mail verwendet wird arbitration_website: Kann ein Webformular sein oder „N/A“, falls eine E-Mail verwendet wird + dmca_address: US-Betreiber sollten die im „DMCA Designated Agent Directory“ eingetragene Adresse verwenden. Eine Postfachadresse ist auf direkte Anfrage verfügbar. Verwenden Sie die „DMCA Designated Agent Post Box Waiver“-Anfrage, um per E-Mail die Urheberrechtsbehörde darüber zu unterrichten, dass Sie Inhalte per Heimarbeit moderieren, eventuelle Rache oder Vergeltung für Ihre Handlungen befürchten und deshalb eine Postfachadresse benötigen, um Ihre Privatadresse nicht preiszugeben. dmca_email: Kann dieselbe E-Mail wie bei „E-Mail-Adresse für rechtliche Hinweise“ sein domain: Einzigartige Identifizierung des angebotenen Online-Services. jurisdiction: Gib das Land an, in dem die Person lebt, die alle Rechnungen bezahlt. Falls es sich dabei um ein Unternehmen oder eine andere Einrichtung handelt, gib das Land mit dem Sitz an, sowie die Stadt oder Region. diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 72ff7af37f..e7fb218e2d 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -28,6 +28,7 @@ sk: admin: account_actions: action: Vykonaj + already_silenced: Tento účet už bol obmedzený. title: Vykonaj moderovací úkon voči %{acct} account_moderation_notes: create: Zanechaj poznámku @@ -204,6 +205,7 @@ sk: enable_user: Povoľ užívateľa memorialize_account: Zmena na „in memoriam“ promote_user: Povýš užívateľskú rolu + publish_terms_of_service: Zverejni podmienky prevozu reject_appeal: Zamietni námietku reject_user: Zamietni užívateľa remove_avatar_user: Vymaž avatar From da4e55eb17e459fbc6d1a19fac3303508324324c Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 16 Jan 2025 11:10:08 +0100 Subject: [PATCH 132/133] Merge commit from fork --- app/lib/delivery_failure_tracker.rb | 2 ++ .../activitypub/process_account_service.rb | 27 ++++++++++++++----- spec/lib/delivery_failure_tracker_spec.rb | 4 +-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/app/lib/delivery_failure_tracker.rb b/app/lib/delivery_failure_tracker.rb index e17b45d667..96292923f4 100644 --- a/app/lib/delivery_failure_tracker.rb +++ b/app/lib/delivery_failure_tracker.rb @@ -46,6 +46,8 @@ class DeliveryFailureTracker urls.reject do |url| host = Addressable::URI.parse(url).normalized_host unavailable_domains_map[host] + rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError + true end end diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index df6f23c021..e5c2319728 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -9,6 +9,8 @@ class ActivityPub::ProcessAccountService < BaseService SUBDOMAINS_RATELIMIT = 10 DISCOVERIES_PER_REQUEST = 400 + VALID_URI_SCHEMES = %w(http https).freeze + # Should be called with confirmed valid JSON # and WebFinger-resolved username and domain def call(username, domain, json, options = {}) @@ -96,16 +98,28 @@ class ActivityPub::ProcessAccountService < BaseService end def set_immediate_protocol_attributes! - @account.inbox_url = @json['inbox'] || '' - @account.outbox_url = @json['outbox'] || '' - @account.shared_inbox_url = (@json['endpoints'].is_a?(Hash) ? @json['endpoints']['sharedInbox'] : @json['sharedInbox']) || '' - @account.followers_url = @json['followers'] || '' + @account.inbox_url = valid_collection_uri(@json['inbox']) + @account.outbox_url = valid_collection_uri(@json['outbox']) + @account.shared_inbox_url = valid_collection_uri(@json['endpoints'].is_a?(Hash) ? @json['endpoints']['sharedInbox'] : @json['sharedInbox']) + @account.followers_url = valid_collection_uri(@json['followers']) @account.url = url || @uri @account.uri = @uri @account.actor_type = actor_type @account.created_at = @json['published'] if @json['published'].present? end + def valid_collection_uri(uri) + uri = uri.first if uri.is_a?(Array) + uri = uri['id'] if uri.is_a?(Hash) + return '' unless uri.is_a?(String) + + parsed_uri = Addressable::URI.parse(uri) + + VALID_URI_SCHEMES.include?(parsed_uri.scheme) && parsed_uri.host.present? ? parsed_uri : '' + rescue Addressable::URI::InvalidURIError + '' + end + def set_immediate_attributes! @account.featured_collection_url = @json['featured'] || '' @account.display_name = @json['name'] || '' @@ -268,10 +282,11 @@ class ActivityPub::ProcessAccountService < BaseService end def collection_info(type) - return [nil, nil] if @json[type].blank? + collection_uri = valid_collection_uri(@json[type]) + return [nil, nil] if collection_uri.blank? return @collections[type] if @collections.key?(type) - collection = fetch_resource_without_id_validation(@json[type]) + collection = fetch_resource_without_id_validation(collection_uri) total_items = collection.is_a?(Hash) && collection['totalItems'].present? && collection['totalItems'].is_a?(Numeric) ? collection['totalItems'] : nil has_first_page = collection.is_a?(Hash) && collection['first'].present? diff --git a/spec/lib/delivery_failure_tracker_spec.rb b/spec/lib/delivery_failure_tracker_spec.rb index 40c8adc4c8..34912c8133 100644 --- a/spec/lib/delivery_failure_tracker_spec.rb +++ b/spec/lib/delivery_failure_tracker_spec.rb @@ -42,8 +42,8 @@ RSpec.describe DeliveryFailureTracker do Fabricate(:unavailable_domain, domain: 'foo.bar') end - it 'removes URLs that are unavailable' do - results = described_class.without_unavailable(['http://example.com/good/inbox', 'http://foo.bar/unavailable/inbox']) + it 'removes URLs that are bogus or unavailable' do + results = described_class.without_unavailable(['http://example.com/good/inbox', 'http://foo.bar/unavailable/inbox', '{foo:']) expect(results).to include('http://example.com/good/inbox') expect(results).to_not include('http://foo.bar/unavailable/inbox') From 0aa9bb8130b77b01f6a08c64b7e93876d892868b Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 16 Jan 2025 12:02:54 +0100 Subject: [PATCH 133/133] Bump version to v4.4.0-alpha.2 (#33615) --- CHANGELOG.md | 18 ++++++++++++++++++ docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea18b3cb92..ef6a87ebb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file. +## [4.3.3] - 2025-01-16 + +### Security + +- Fix insufficient validation of account URIs ([GHSA-5wxh-3p65-r4g6](https://github.com/mastodon/mastodon/security/advisories/GHSA-5wxh-3p65-r4g6)) +- Update dependencies + +### Fixed + +- Fix `libyaml` missing from `Dockerfile` build stage (#33591 by @vmstan) +- Fix incorrect notification settings migration for non-followers (#33348 by @ClearlyClaire) +- Fix down clause for notification policy v2 migrations (#33340 by @jesseplusplus) +- Fix error decrementing status count when `FeaturedTags#last_status_at` is `nil` (#33320 by @ClearlyClaire) +- Fix last paginated notification group only including data on a single notification (#33271 by @ClearlyClaire) +- Fix processing of mentions for post edits with an existing corresponding silent mention (#33227 by @ClearlyClaire) +- Fix deletion of unconfirmed users with Webauthn set (#33186 by @ClearlyClaire) +- Fix empty authors preview card serialization (#33151, #33466 by @mjankowski and @ClearlyClaire) + ## [4.3.2] - 2024-12-03 ### Added diff --git a/docker-compose.yml b/docker-compose.yml index 63f17bf495..6c44977562 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,7 +59,7 @@ services: web: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # build: . - image: ghcr.io/mastodon/mastodon:v4.3.2 + image: ghcr.io/mastodon/mastodon:v4.3.3 restart: always env_file: .env.production command: bundle exec puma -C config/puma.rb @@ -83,7 +83,7 @@ services: # build: # dockerfile: ./streaming/Dockerfile # context: . - image: ghcr.io/mastodon/mastodon-streaming:v4.3.2 + image: ghcr.io/mastodon/mastodon-streaming:v4.3.3 restart: always env_file: .env.production command: node ./streaming/index.js @@ -102,7 +102,7 @@ services: sidekiq: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # build: . - image: ghcr.io/mastodon/mastodon:v4.3.2 + image: ghcr.io/mastodon/mastodon:v4.3.3 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 19779eb609..3aa93bbba6 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -17,7 +17,7 @@ module Mastodon end def default_prerelease - 'alpha.1' + 'alpha.2' end def prerelease