From 7b7c26063e3fcf9e75a61780d81bd60b7c398ead Mon Sep 17 00:00:00 2001 From: rhoio Date: Thu, 13 Sep 2018 01:50:41 +0200 Subject: Highlight active tab in action bar (#8673) * NavLinks with activeclass instead of Link in actionbar to highlight active tab * highlight border-bottom in action bar consistent to public view --- app/javascript/styles/mastodon/components.scss | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'app/javascript/styles') diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 24fb77d98..048563336 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1297,6 +1297,11 @@ a .account__avatar { flex: 0 1 100%; border-right: 1px solid lighten($ui-base-color, 8%); padding: 10px 0; + border-bottom: 4px solid transparent; + + &.active { + border-bottom: 4px solid $ui-highlight-color; + } & > span { display: block; -- cgit From 011437dcb5c4719ad6627f3a4dc07a5ce1bd9db8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 13 Sep 2018 03:44:08 +0200 Subject: Group reports by target account (#8674) * Group reports by target account * Improve CSS --- .../admin/account_moderation_notes_helper.rb | 4 +- app/helpers/home_helper.rb | 8 +- app/javascript/styles/mastodon/admin.scss | 99 ++++++++++++++++++++++ app/views/admin/reports/_account.html.haml | 19 ----- app/views/admin/reports/_report.html.haml | 29 ------- app/views/admin/reports/index.html.haml | 52 +++++++++--- app/views/settings/exports/show.html.haml | 2 +- config/locales/ar.yml | 4 - config/locales/ast.yml | 1 - config/locales/ca.yml | 4 - config/locales/co.yml | 4 - config/locales/cs.yml | 4 - config/locales/da.yml | 4 - config/locales/de.yml | 4 - config/locales/el.yml | 4 - config/locales/en.yml | 12 ++- config/locales/eo.yml | 4 - config/locales/es.yml | 4 - config/locales/eu.yml | 4 - config/locales/fa.yml | 4 - config/locales/fi.yml | 4 - config/locales/fr.yml | 4 - config/locales/gl.yml | 4 - config/locales/he.yml | 4 - config/locales/hu.yml | 4 - config/locales/id.yml | 4 - config/locales/io.yml | 3 - config/locales/it.yml | 4 - config/locales/ja.yml | 4 - config/locales/ka.yml | 4 - config/locales/ko.yml | 4 - config/locales/nl.yml | 4 - config/locales/no.yml | 4 - config/locales/oc.yml | 4 - config/locales/pl.yml | 4 - config/locales/pt-BR.yml | 4 - config/locales/pt.yml | 4 - config/locales/ru.yml | 4 - config/locales/sk.yml | 4 - config/locales/sr-Latn.yml | 4 - config/locales/sr.yml | 4 - config/locales/sv.yml | 4 - config/locales/th.yml | 4 - config/locales/tr.yml | 4 - config/locales/uk.yml | 4 - config/locales/zh-CN.yml | 4 - config/locales/zh-HK.yml | 4 - config/locales/zh-TW.yml | 4 - 48 files changed, 154 insertions(+), 227 deletions(-) delete mode 100644 app/views/admin/reports/_account.html.haml delete mode 100644 app/views/admin/reports/_report.html.haml (limited to 'app/javascript/styles') diff --git a/app/helpers/admin/account_moderation_notes_helper.rb b/app/helpers/admin/account_moderation_notes_helper.rb index 25586fbba..4d8f0352e 100644 --- a/app/helpers/admin/account_moderation_notes_helper.rb +++ b/app/helpers/admin/account_moderation_notes_helper.rb @@ -4,7 +4,7 @@ module Admin::AccountModerationNotesHelper def admin_account_link_to(account) return if account.nil? - link_to admin_account_path(account.id), class: name_tag_classes(account) do + link_to admin_account_path(account.id), class: name_tag_classes(account), title: account.acct do safe_join([ image_tag(account.avatar.url, width: 15, height: 15, alt: display_name(account), class: 'avatar'), content_tag(:span, account.acct, class: 'username'), @@ -15,7 +15,7 @@ module Admin::AccountModerationNotesHelper def admin_account_inline_link_to(account) return if account.nil? - link_to admin_account_path(account.id), class: name_tag_classes(account, true) do + link_to admin_account_path(account.id), class: name_tag_classes(account, true), title: account.acct do content_tag(:span, account.acct, class: 'username') end end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb index f5b501235..0157785fa 100644 --- a/app/helpers/home_helper.rb +++ b/app/helpers/home_helper.rb @@ -7,13 +7,13 @@ module HomeHelper } end - def account_link_to(account, button = '') + def account_link_to(account, button = '', size: 36, path: nil) content_tag(:div, class: 'account') do content_tag(:div, class: 'account__wrapper') do section = if account.nil? content_tag(:div, class: 'account__display-name') do content_tag(:div, class: 'account__avatar-wrapper') do - content_tag(:div, '', class: 'account__avatar', style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})") + content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})") end + content_tag(:span, class: 'display-name') do content_tag(:strong, t('about.contact_missing')) + @@ -21,9 +21,9 @@ module HomeHelper end end else - link_to(TagManager.instance.url_for(account), class: 'account__display-name') do + link_to(path || TagManager.instance.url_for(account), class: 'account__display-name') do content_tag(:div, class: 'account__avatar-wrapper') do - content_tag(:div, '', class: 'account__avatar', style: "background-image: url(#{account.avatar.url})") + content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{account.avatar.url})") end + content_tag(:span, class: 'display-name') do content_tag(:bdi) do diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 42f507296..7d359921a 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -567,3 +567,102 @@ a.name-tag, color: $dark-text-color; } } + +.report-card { + background: $ui-base-color; + border-radius: 4px; + margin-bottom: 20px; + + &__profile { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15px; + + .account { + padding: 0; + border: 0; + + &__avatar-wrapper { + margin-left: 0; + } + } + + &__stats { + flex: 0 0 auto; + font-weight: 500; + color: $darker-text-color; + text-transform: uppercase; + text-align: right; + + a { + color: inherit; + text-decoration: none; + + &:focus, + &:hover, + &:active { + color: lighten($darker-text-color, 8%); + } + } + + .red { + color: $error-value-color; + } + } + } + + &__summary { + &__item { + display: flex; + justify-content: flex-start; + border-top: 1px solid darken($ui-base-color, 4%); + + &:hover { + background: lighten($ui-base-color, 2%); + } + + &__reported-by, + &__assigned { + padding: 15px; + flex: 0 0 auto; + box-sizing: border-box; + width: 150px; + color: $darker-text-color; + + &, + .username { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + &__content { + flex: 1 1 auto; + max-width: calc(100% - 300px); + + &__icon { + color: $dark-text-color; + margin-right: 4px; + font-weight: 500; + } + } + + &__content a { + display: block; + box-sizing: border-box; + width: 100%; + padding: 15px; + text-decoration: none; + color: $darker-text-color; + } + } + } +} + +.one-line { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/app/views/admin/reports/_account.html.haml b/app/views/admin/reports/_account.html.haml deleted file mode 100644 index 9ac161c9c..000000000 --- a/app/views/admin/reports/_account.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -- size ||= 36 - -.account.compact - .account__wrapper - - if account.nil? - .account__display-name - .account__avatar-wrapper - .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" } - %span.display-name - %strong= t 'about.contact_missing' - %span.display-name__account= t 'about.contact_unavailable' - - else - = link_to TagManager.instance.url_for(account), class: 'account__display-name' do - .account__avatar-wrapper - .account__avatar{ style: "background-image: url(#{account.avatar.url}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" } - %span.display-name - %bdi - %strong.display-name__html.emojify= display_name(account, custom_emojify: true) - %span.display-name__account @#{account.acct} diff --git a/app/views/admin/reports/_report.html.haml b/app/views/admin/reports/_report.html.haml deleted file mode 100644 index 7b25c924b..000000000 --- a/app/views/admin/reports/_report.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -%tr - %td.id - = "##{report.id}" - %td.target - = admin_account_link_to report.target_account - %td.reporter - - if report.account.local? - = admin_account_link_to report.account - - else - = report.account.domain - %td - %div{ title: report.comment } - = truncate(report.comment, length: 30, separator: ' ') - %div - - unless report.statuses.empty? - %span{ title: t('admin.accounts.statuses') } - = fa_icon('comment') - = report.statuses.count - - unless report.media_attachments.empty? - %span{ title: t('admin.accounts.media_attachments') } - = fa_icon('camera') - = report.media_attachments.count - %td - - if report.assigned_account.nil? - \- - - else - = admin_account_link_to report.assigned_account - %td - = table_link_to 'circle', t('admin.reports.view'), admin_report_path(report) diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index 44a531f2c..d73faccb0 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -8,17 +8,45 @@ %li= filter_link_to t('admin.reports.unresolved'), resolved: nil %li= filter_link_to t('admin.reports.resolved'), resolved: '1' -.table-wrapper - %table.table - %thead - %tr - %th= t('admin.reports.id') - %th= t('admin.reports.target') - %th= t('admin.reports.reported_by') - %th= t('admin.reports.report_contents') - %th= t('admin.reports.assigned') - %th - %tbody - = render @reports +- @reports.group_by(&:target_account_id).each do |target_account_id, reports| + - target_account = reports.first.target_account + .report-card + .report-card__profile + = account_link_to target_account, '', size: 36, path: admin_account_path(target_account.id) + .report-card__profile__stats + = link_to pluralize(target_account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_account_path(target_account.id) + %br/ + - if target_account.suspended? + %span.red= t('admin.accounts.suspended') + - elsif target_account.silenced? + %span.red= t('admin.accounts.silenced') + - elsif target_account.user&.disabled? + %span.red= t('admin.accounts.disabled') + - else + %span.neutral= t('admin.accounts.no_limits_imposed') + .report-card__summary + - reports.each do |report| + .report-card__summary__item + .report-card__summary__item__reported-by + - if report.account.local? + = admin_account_link_to report.account + - else + = report.account.domain + .report-card__summary__item__content + = link_to admin_report_path(report) do + .one-line= report.comment.presence || t('admin.reports.comment.none') + %span.report-card__summary__item__content__icon{ title: t('admin.accounts.statuses') } + = fa_icon('comment') + = report.statuses.count + + %span.report-card__summary__item__content__icon{ title: t('admin.accounts.media_attachments') } + = fa_icon('camera') + = report.media_attachments.count + + .report-card__summary__item__assigned + - if report.assigned_account.present? + = admin_account_link_to report.assigned_account + - else + \- = paginate @reports diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml index 792dccd9e..6c030b1ab 100644 --- a/app/views/settings/exports/show.html.haml +++ b/app/views/settings/exports/show.html.haml @@ -9,7 +9,7 @@ %td= number_to_human_size @export.total_storage %td %tr - %th= t('accounts.statuses', count: @export.total_statuses) + %th= t('accounts.posts', count: @export.total_statuses) %td= number_with_delimiter @export.total_statuses %td %tr diff --git a/config/locales/ar.yml b/config/locales/ar.yml index eee2bb0b9..39995209b 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -313,7 +313,6 @@ ar: comment: none: لا شيء created_at: ذكرت - id: معرّف ID mark_as_resolved: إعتبار التقرير كمحلول mark_as_unresolved: علام كغير محلولة notes: @@ -324,7 +323,6 @@ ar: placeholder: قم بوصف الإجراءات التي تم اتخاذها أو أي تحديثات أخرى ذات علاقة … reopen: إعادة فتح التقرير report: 'التقرير #%{id}' - report_contents: المحتويات reported_account: حساب مُبلّغ عنه reported_by: أبلغ عنه من طرف resolved: معالجة @@ -332,12 +330,10 @@ ar: silence_account: كتم و إخفاء الحساب status: الحالة suspend_account: فرض تعليق على الحساب - target: الهدف title: التقارير unassign: إلغاء تعيين unresolved: غير معالجة updated_at: محدث - view: عرض settings: activity_api_enabled: desc_html: عدد المنشورات المحلية و المستخدمين النشطين و التسجيلات الأسبوعية الجديدة diff --git a/config/locales/ast.yml b/config/locales/ast.yml index 9cc80ef16..aa3d34813 100644 --- a/config/locales/ast.yml +++ b/config/locales/ast.yml @@ -88,7 +88,6 @@ ast: title: Invitaciones reports: are_you_sure: "¿De xuru?" - id: ID status: Estáu settings: registrations: diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 73cb608c6..5a855945f 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -322,7 +322,6 @@ ca: comment: none: Cap created_at: Reportat - id: ID mark_as_resolved: Marca com a resolt mark_as_unresolved: Marcar sense resoldre notes: @@ -333,7 +332,6 @@ ca: placeholder: Descriu les accions que s'han pres o qualsevol altra actualització relacionada… reopen: Reobrir informe report: 'Informe #%{id}' - report_contents: Contingut reported_account: Compte reportat reported_by: Reportat per resolved: Resolt @@ -341,12 +339,10 @@ ca: silence_account: Silencia el compte status: Estat suspend_account: Suspèn el compte - target: Objectiu title: Informes unassign: Treure assignació unresolved: No resolt updated_at: Actualitzat - view: Visualització settings: activity_api_enabled: desc_html: Compte d'estatus publicats localment, usuaris actius i registres nous en períodes setmanals diff --git a/config/locales/co.yml b/config/locales/co.yml index f21471ff7..69533e527 100644 --- a/config/locales/co.yml +++ b/config/locales/co.yml @@ -322,7 +322,6 @@ co: comment: none: Nisunu created_at: Palisatu - id: ID mark_as_resolved: Indicà cum’è chjosu mark_as_unresolved: Indicà cum’è sempre apertu notes: @@ -333,7 +332,6 @@ co: placeholder: Per parlà di l’azzione pigliate, o altre messe à ghjornu nant’à u signalamentu… reopen: Riapre u signalamentu report: 'Signalamente #%{id}' - report_contents: Cuntenuti reported_account: Contu palisatu reported_by: Palisatu da resolved: Scioltu è chjosu @@ -341,12 +339,10 @@ co: silence_account: Silenzà u contu status: Statutu suspend_account: Suspende u contu - target: Oggettu title: Signalamenti unassign: Disassignà unresolved: Micca sciolti updated_at: Messi à ghjornu - view: Vede settings: activity_api_enabled: desc_html: Numeri di statuti creati quì, utilizatori attivi, è arregistramenti novi tutte e settimane diff --git a/config/locales/cs.yml b/config/locales/cs.yml index ae2d3c04f..a55ed00b0 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -322,7 +322,6 @@ cs: comment: none: Žádné created_at: Nahlášené - id: ID mark_as_resolved: Označit jako vyřešené mark_as_unresolved: Označit jako nevyřešené notes: @@ -333,7 +332,6 @@ cs: placeholder: Popište, jaké akce byly vykonány, nebo jakékoliv jiné související aktuality... reopen: Znovu otevřít nahlášení report: 'Nahlásit #%{id}' - report_contents: Obsah reported_account: Nahlášený účet reported_by: Nahlášeno uživatelem resolved: Vyřešeno @@ -341,12 +339,10 @@ cs: silence_account: Utišit účet status: Stav suspend_account: Suspendovat účet - target: Cíl title: Nahlášení unassign: Odebrat unresolved: Nevyřešeno updated_at: Aktualizováno - view: Zobrazit settings: activity_api_enabled: desc_html: Počty lokálně publikovaných příspěvků, aktivních uživatelů a nových registrací, v týdenních intervalech diff --git a/config/locales/da.yml b/config/locales/da.yml index e469fd3c0..74ac464c7 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -322,7 +322,6 @@ da: comment: none: Ingen created_at: Anmeldt - id: ID mark_as_resolved: Marker som værende løst mark_as_unresolved: Marker som værende uløst notes: @@ -333,7 +332,6 @@ da: placeholder: Beskriv hvilke handlinger der er blevet udført, eller andre relevante opdateringer... reopen: Genåben anmeldelse report: 'Anmeldelse #%{id}' - report_contents: Indhold reported_account: Anmeldt konto reported_by: Anmeldt af resolved: Løst @@ -341,12 +339,10 @@ da: silence_account: Dæmp konto status: Status suspend_account: Udeluk konto - target: Mål title: Anmeldelser unassign: Utildel unresolved: Uløst updated_at: Opdateret - view: Se settings: activity_api_enabled: desc_html: Antal af lokalt opslåede statusser, aktive brugere, og nye registreringer i ugentlige opdelinger diff --git a/config/locales/de.yml b/config/locales/de.yml index a68c64511..847cfa0d8 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -321,7 +321,6 @@ de: comment: none: Kein created_at: Gemeldet - id: ID mark_as_resolved: Als gelöst markieren mark_as_unresolved: Als ungelöst markieren notes: @@ -332,7 +331,6 @@ de: placeholder: Beschreibe, welche Maßnahmen ergriffen wurden oder irgendwelche andere Neuigkeiten… reopen: Meldung wieder öffnen report: 'Meldung #%{id}' - report_contents: Inhalt reported_account: Gemeldetes Konto reported_by: Gemeldet von resolved: Gelöst @@ -340,12 +338,10 @@ de: silence_account: Konto stummschalten status: Status suspend_account: Konto sperren - target: Ziel title: Meldungen unassign: Zuweisung entfernen unresolved: Ungelöst updated_at: Aktualisiert - view: Ansehen settings: activity_api_enabled: desc_html: Anzahl der lokal geposteten Beiträge, aktiven Nutzern und neuen Registrierungen in wöchentlichen Zusammenfassungen diff --git a/config/locales/el.yml b/config/locales/el.yml index 84119b086..6f7840288 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -322,7 +322,6 @@ el: comment: none: Κανένα created_at: Αναφέρθηκε - id: ID mark_as_resolved: Σημειωμένο ως επιλυμένο mark_as_unresolved: Σημειωμένο ως ανεπίλυτο notes: @@ -333,7 +332,6 @@ el: placeholder: Περιέγραψε τις ενέργειες που έγιναν, ή οποιαδήποτε άλλη ενημέρωση... reopen: Ξανάνοιξε την καταγγελία report: 'Καταγγελία #%{id}' - report_contents: Περιεχόμενα reported_account: Αναφερόμενος λογαριασμός reported_by: Αναφέρθηκε από resolved: Επιλύθηκε @@ -341,12 +339,10 @@ el: silence_account: Αποσιώπηση λογαριασμού status: Κατάσταση suspend_account: Ανέστειλε λογαριασμό - target: Αποδέκτης title: Αναφορές unassign: Αποσύνδεση unresolved: Άλυτη updated_at: Ενημερωμένη - view: Εμφάνιση settings: activity_api_enabled: desc_html: Καταμέτρηση τοπικών δημοσιεύσεων, ενεργών χρηστών και νέων εγγραφών σε εβδομαδιαίες ομαδοποιήσεις diff --git a/config/locales/en.yml b/config/locales/en.yml index 501694eac..5cbb790dd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -120,6 +120,7 @@ en: moderation_notes: Moderation notes most_recent_activity: Most recent activity most_recent_ip: Most recent IP + no_limits_imposed: No limits imposed not_subscribed: Not subscribed order: alphabetic: Alphabetic @@ -155,8 +156,10 @@ en: report: report targeted_reports: Reports made about this account silence: Silence + silenced: Silenced statuses: Statuses subscribe: Subscribe + suspended: Suspended title: Accounts unconfirmed_email: Unconfirmed E-mail undo_silenced: Undo silence @@ -300,8 +303,13 @@ en: title: Invites relays: add_new: Add new relay + delete: Delete description_html: A federation relay is an intermediary server that exchanges large volumes of public toots between servers that subscribe and publish to it. It can help small and medium servers discover content from the fediverse, which would otherwise require local users manually following other people on remote servers. + disable: Disable + disabled: Disabled + enable: Enable enable_hint: Once enabled, your server will subscribe to all public toots from this relay, and will begin sending this server's public toots to it. + enabled: Enabled inbox_url: Relay URL pending: Waiting for relay's approval save_and_enable: Save and enable @@ -322,7 +330,6 @@ en: comment: none: None created_at: Reported - id: ID mark_as_resolved: Mark as resolved mark_as_unresolved: Mark as unresolved notes: @@ -333,7 +340,6 @@ en: placeholder: Describe what actions have been taken, or any other related updates... reopen: Reopen report report: 'Report #%{id}' - report_contents: Contents reported_account: Reported account reported_by: Reported by resolved: Resolved @@ -341,12 +347,10 @@ en: silence_account: Silence account status: Status suspend_account: Suspend account - target: Target title: Reports unassign: Unassign unresolved: Unresolved updated_at: Updated - view: View settings: activity_api_enabled: desc_html: Counts of locally posted statuses, active users, and new registrations in weekly buckets diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 996287335..9ab128d0c 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -311,7 +311,6 @@ eo: comment: none: Nenio created_at: Signalita - id: ID mark_as_resolved: Marki solvita mark_as_unresolved: Marki nesolvita notes: @@ -322,7 +321,6 @@ eo: placeholder: Priskribu faritajn agojn, aŭ ajnan novan informon pri tiu signalo… reopen: Remalfermi signalon report: 'Signalo #%{id}' - report_contents: Enhavo reported_account: Signalita konto reported_by: Signalita de resolved: Solvita @@ -330,12 +328,10 @@ eo: silence_account: Kaŝi konton status: Mesaĝoj suspend_account: Haltigi konton - target: Celo title: Signaloj unassign: Malasigni unresolved: Nesolvita updated_at: Ĝisdatigita - view: Vidi settings: activity_api_enabled: desc_html: Sumo de lokaj mesaĝoj, aktivaj uzantoj, kaj novaj registriĝoj laŭsemajne diff --git a/config/locales/es.yml b/config/locales/es.yml index ca04f0b1c..ce0b2bb16 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -269,7 +269,6 @@ es: comment: none: Ninguno created_at: Denunciado - id: ID mark_as_resolved: Marcar como resuelto mark_as_unresolved: Marcar como no resuelto notes: @@ -280,7 +279,6 @@ es: placeholder: Especificar qué acciones se han tomado o cualquier otra novedad respecto a esta denuncia… reopen: Reabrir denuncia report: 'Reportar #%{id}' - report_contents: Contenido reported_account: Cuenta reportada reported_by: Reportado por resolved: Resuelto @@ -288,12 +286,10 @@ es: silence_account: Silenciar cuenta status: Estado suspend_account: Suspender cuenta - target: Objetivo title: Reportes unassign: Desasignar unresolved: No resuelto updated_at: Actualizado - view: Ver settings: activity_api_enabled: desc_html: Conteo de estados publicados localmente, usuarios activos, y nuevos registros en periodos semanales diff --git a/config/locales/eu.yml b/config/locales/eu.yml index e767d9ad9..d8ea6f2e0 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -315,7 +315,6 @@ eu: comment: none: Bat ere ez created_at: Salatua - id: ID mark_as_resolved: Markatu konpondutako gisa mark_as_unresolved: Markatu konpondu gabeko gisa notes: @@ -326,7 +325,6 @@ eu: placeholder: Azaldu hartutako neurriak, edo erlazioa duten bestelako berriak... reopen: Berrireki salaketa report: 'Salaketa #%{id}' - report_contents: Edukiak reported_account: Salatutako kontua reported_by: Salatzailea resolved: Konponduta @@ -334,12 +332,10 @@ eu: silence_account: Isilarazi kontua status: Mezua suspend_account: Kanporatu kontua - target: Helburua title: Salaketak unassign: Kendu esleipena unresolved: Konpondu gabea updated_at: Eguneratua - view: Ikusi settings: activity_api_enabled: desc_html: Lokalki bidalitako mezu kopurua, erabiltzaile aktiboak, eta izen emate berriak asteko diff --git a/config/locales/fa.yml b/config/locales/fa.yml index b0d230634..e2bcf7176 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -322,7 +322,6 @@ fa: comment: none: خالی created_at: گزارش‌شده - id: شناسه mark_as_resolved: علامت‌گذاری به عنوان حل‌شده mark_as_unresolved: علامت‌گذاری به عنوان حل‌نشده notes: @@ -333,7 +332,6 @@ fa: placeholder: کارهایی را که در این باره انجام شده، یا هر به‌روزرسانی دیگری را بنویسید... reopen: دوباره به جریان بیندازید report: 'گزارش #%{id}' - report_contents: محتوا reported_account: حساب گزارش‌شده reported_by: گزارش از طرف resolved: حل‌شده @@ -341,12 +339,10 @@ fa: silence_account: بی‌صدا کردن حساب status: نوشته suspend_account: معلق‌کردن حساب - target: هدف title: گزارش‌ها unassign: پس‌گرفتن مسئولیت unresolved: حل‌نشده updated_at: به‌روز شد - view: نمایش settings: activity_api_enabled: desc_html: تعداد بوق‌های محلی، کاربران فعال، و کاربران تازه در هر هفته diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 96339e35e..e6a323da6 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -268,7 +268,6 @@ fi: comment: none: Ei mitään created_at: Raportoitu - id: Tunniste mark_as_resolved: Merkitse ratkaistuksi mark_as_unresolved: Merkitse ratkaisemattomaksi notes: @@ -279,7 +278,6 @@ fi: placeholder: Kuvaile mitä toimia on tehty tai muita päivityksiä tähän raporttiin… reopen: Avaa raportti uudestaan report: Raportti nro %{id} - report_contents: Sisältö reported_account: Raportoitu tili reported_by: Raportoija resolved: Ratkaistut @@ -287,11 +285,9 @@ fi: silence_account: Hiljennä tili status: Tila suspend_account: Siirrä tili jäähylle - target: Kohde title: Raportit unresolved: Ratkaisemattomat updated_at: Päivitetty - view: Näytä settings: activity_api_enabled: desc_html: Paikallisesti julkaistujen tilojen, aktiivisten käyttäjien ja uusien rekisteröintien määrät viikoittain diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 1e08e9bbe..a9b9e7b55 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -322,7 +322,6 @@ fr: comment: none: Aucun created_at: Signalé - id: ID mark_as_resolved: Marquer comme résolu mark_as_unresolved: Marquer comme non-résolu notes: @@ -333,7 +332,6 @@ fr: placeholder: Décrivez quelles actions ont été prises, ou toute autre mise à jour… reopen: Ré-ouvrir le signalement report: 'Signalement #%{id}' - report_contents: Contenu reported_account: Compte signalé reported_by: Signalé par resolved: Résolus @@ -341,12 +339,10 @@ fr: silence_account: Masquer le compte status: Statut suspend_account: Suspendre le compte - target: Cible title: Signalements unassign: Dés-assigner unresolved: Non résolus updated_at: Mis à jour - view: Voir settings: activity_api_enabled: desc_html: Nombre de statuts affichés localement, d’utilisateur·ice·s actif·ve·s et de nouveaux enregistrements dans les registres hebdomadaires diff --git a/config/locales/gl.yml b/config/locales/gl.yml index c196e0c46..3ec89a9a2 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -321,7 +321,6 @@ gl: comment: none: Nada created_at: Reportado - id: ID mark_as_resolved: Marcar como resolto mark_as_unresolved: Marcar como non resolto notes: @@ -332,7 +331,6 @@ gl: placeholder: Describe qué medidas foron tomadas, ou calquer outra información relacionada... reopen: Voltar a abrir o informe report: 'Informe #%{id}' - report_contents: Contidos reported_account: Conta reportada reported_by: Reportada por resolved: Resolto @@ -340,12 +338,10 @@ gl: silence_account: Acalar conta status: Estado suspend_account: Suspender conta - target: Obxetivo title: Informes unassign: Non asignar unresolved: Non resolto updated_at: Actualizado - view: Vista settings: activity_api_enabled: desc_html: Conta de estados publicados localmente, usuarias activas, e novos rexistros por semana diff --git a/config/locales/he.yml b/config/locales/he.yml index ccf53e14a..3d24f22d2 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -184,20 +184,16 @@ he: are_you_sure: 100% על בטוח? comment: none: ללא - id: ID mark_as_resolved: סימון כפתור report: 'דווח על #%{id}' - report_contents: תוכן reported_account: חשבון מדווח reported_by: דווח על ידי resolved: פתור silence_account: השתקת חשבון status: הודעה suspend_account: השעיית חשבון - target: מטרה title: דיווחים unresolved: לא פתור - view: תצוגה settings: contact_information: email: נא להקליד כתובת דוא"ל פומבית diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 824a46066..5d89b44cc 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -244,20 +244,16 @@ hu: are_you_sure: Biztos vagy benne? comment: none: Egyik sem - id: ID mark_as_resolved: Megjelölés megoldottként report: "#%{id} számú jelentés" - report_contents: Tartalom reported_account: Bejelentett fiók reported_by: 'Jelentette:' resolved: Megoldott silence_account: Felhasználó némítása status: Állapot suspend_account: Felhasználó felfüggesztése - target: Cél title: Jelentések unresolved: Megoldatlan - view: Megtekintés settings: activity_api_enabled: desc_html: Helyi tülkök, aktív felhasználók és új regisztrációk száma heti bontásban diff --git a/config/locales/id.yml b/config/locales/id.yml index 33a764a28..b186b7652 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -109,20 +109,16 @@ id: reports: comment: none: Tidak ada - id: ID mark_as_resolved: Tandai telah diseleseikan report: 'Laporkan #%{id}' - report_contents: Konten reported_account: Akun yang dilaporkan reported_by: Dilaporkan oleh resolved: Terseleseikan silence_account: Akun yang didiamkan status: Status suspend_account: Akun yang disuspen - target: Target title: Laporan unresolved: Belum Terseleseikan - view: Tampilan settings: contact_information: email: Masukkan alamat email diff --git a/config/locales/io.yml b/config/locales/io.yml index 287c1d0e6..be8a87acd 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -102,7 +102,6 @@ io: reports: comment: none: None - id: ID mark_as_resolved: Mark as resolved report: 'Report #%{id}' reported_account: Reported account @@ -111,10 +110,8 @@ io: silence_account: Silence account status: Status suspend_account: Suspend account - target: Target title: Reports unresolved: Unresolved - view: View settings: contact_information: email: Enter a public e-mail address diff --git a/config/locales/it.yml b/config/locales/it.yml index c947e3107..cef823e8f 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -296,7 +296,6 @@ it: assigned: Moderatore assegnato comment: none: Nessuno - id: ID mark_as_resolved: Segna come risolto mark_as_unresolved: Segna come non risolto notes: @@ -306,17 +305,14 @@ it: delete: Elimina reopen: Riapri rapporto report: 'Rapporto #%{id}' - report_contents: Contenuti reported_by: Inviato da resolved: Risolto silence_account: Silenzia account status: Stato suspend_account: Sospendi account - target: Obbiettivo unassign: Non assegnare unresolved: Non risolto updated_at: Aggiornato - view: Mostra settings: activity_api_enabled: desc_html: Conteggi degli status pubblicati localmente, degli utenti attivi e delle nuove registrazioni in gruppi settimanali diff --git a/config/locales/ja.yml b/config/locales/ja.yml index bd6a22a64..3f55948be 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -322,7 +322,6 @@ ja: comment: none: なし created_at: レポート日時 - id: ID mark_as_resolved: 解決済みとしてマーク mark_as_unresolved: 未解決として再び開く notes: @@ -333,7 +332,6 @@ ja: placeholder: どのような措置が取られたか、または関連する更新を記述してください… reopen: 再び開く report: レポート#%{id} - report_contents: 内容 reported_account: 報告対象アカウント reported_by: 報告者 resolved: 解決済み @@ -341,12 +339,10 @@ ja: silence_account: アカウントをサイレンス status: ステータス suspend_account: アカウントを停止 - target: ターゲット title: レポート unassign: 担当を外す unresolved: 未解決 updated_at: 更新日時 - view: 表示 settings: activity_api_enabled: desc_html: 週ごとのローカルに投稿されたトゥート数、アクティブなユーザー数、新規登録者数 diff --git a/config/locales/ka.yml b/config/locales/ka.yml index c1105b017..e22836148 100644 --- a/config/locales/ka.yml +++ b/config/locales/ka.yml @@ -311,7 +311,6 @@ ka: comment: none: არაფერი created_at: რეპორტის დრო - id: იდ mark_as_resolved: მონიშნე გადაწყვეტილად mark_as_unresolved: მონიშნე გადაუწყვეტლად notes: @@ -322,7 +321,6 @@ ka: placeholder: აღწერეთ თუ რა ნაბიჯები უნდა გადაიდგას, ან სხვა დაკავშირებული განახლებები... reopen: რეპორტის ხელახალი გახსნა report: 'რეპორტი #%{id}' - report_contents: მოცულობები reported_account: დარეპორტებული ანგარიში reported_by: დაარეპორტა resolved: გადაწყვეტილი @@ -330,12 +328,10 @@ ka: silence_account: ანგარიშის გაჩუმება status: სტატუსი suspend_account: ანგარიშის შეჩერება - target: მიზანი title: რეპორტები unassign: გადაყენება unresolved: გადაუწყვეტელი updated_at: განახების დრო - view: ჩვენება settings: activity_api_enabled: desc_html: ლოკალურად გამოქვეყნებული სტატუსების, აქტიური მომხმარებლების და ყოველკვირეული რეგისტრაციების მთვლელი diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 737ab5be1..ff7152064 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -324,7 +324,6 @@ ko: comment: none: 없음 created_at: 리포트 시각 - id: ID mark_as_resolved: 해결 완료 처리 mark_as_unresolved: 미해결로 표시 notes: @@ -335,7 +334,6 @@ ko: placeholder: 이 리포트에 대한 조치, 기타 관련 된 사항에 대해 설명합니다… reopen: 리포트 다시 열기 report: '신고 #%{id}' - report_contents: 내용 reported_account: 신고 대상 계정 reported_by: 신고자 resolved: 해결됨 @@ -343,12 +341,10 @@ ko: silence_account: 계정을 침묵 처리 status: 상태 suspend_account: 계정을 정지 - target: 대상 title: 신고 unassign: 할당 해제 unresolved: 미해결 updated_at: 업데이트 시각 - view: 표시 settings: activity_api_enabled: desc_html: 주별 로컬에 게시 된 글, 활성 사용자 및 새로운 가입자 수 diff --git a/config/locales/nl.yml b/config/locales/nl.yml index c5478d862..98107f676 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -322,7 +322,6 @@ nl: comment: none: Geen created_at: Gerapporteerd op - id: ID mark_as_resolved: Markeer als opgelost mark_as_unresolved: Markeer als onopgelost notes: @@ -333,7 +332,6 @@ nl: placeholder: Beschrijf welke acties zijn ondernomen of andere gerelateerde opmerkingen… reopen: Rapportage heropenen report: 'Rapportage #%{id}' - report_contents: Inhoud reported_account: Gerapporteerde account reported_by: Gerapporteerd door resolved: Opgelost @@ -341,12 +339,10 @@ nl: silence_account: Account negeren status: Toot suspend_account: Account opschorten - target: Gerapporteerde account title: Rapportages unassign: Niet langer toewijzen unresolved: Onopgelost updated_at: Bijgewerkt - view: Weergeven settings: activity_api_enabled: desc_html: Wekelijks overzicht van de hoeveelheid lokale toots, actieve gebruikers en nieuwe registraties diff --git a/config/locales/no.yml b/config/locales/no.yml index bcf3966d4..57182b556 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -244,20 +244,16 @@ are_you_sure: Er du sikker? comment: none: Ingen - id: ID mark_as_resolved: Merk som løst report: 'Rapportér #%{id}' - report_contents: Innhold reported_account: Rapportert konto reported_by: Rapportert av resolved: Løst silence_account: Målbind konto status: Status suspend_account: Utvis konto - target: Mål title: Rapporter unresolved: Uløst - view: Vis settings: activity_api_enabled: desc_html: Antall lokale statusposter, aktive brukere og nye registreringer i ukentlige oppdelinger diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 3fd5c87b9..592ceead4 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -323,7 +323,6 @@ oc: comment: none: Pas cap created_at: Creacion - id: ID mark_as_resolved: Marcar coma resolgut mark_as_unresolved: Marcar coma pas resolgut notes: @@ -334,7 +333,6 @@ oc: placeholder: Explicatz las accions que son estadas menadas o quicòm de ligat al senhalament… reopen: Tornar dobrir lo rapòrt report: 'Senhalament #%{id}' - report_contents: Contengut reported_account: Compte senhalat reported_by: Senhalat per resolved: Resolgut @@ -342,12 +340,10 @@ oc: silence_account: Metre lo compte en silenci status: Estatut suspend_account: Suspendre lo compte - target: Cibla title: Senhalament unassign: Levar unresolved: Pas resolgut updated_at: Actualizat - view: Veire settings: activity_api_enabled: desc_html: Nombre d’estatuts publicats, d’utilizaires actius e de novèlas inscripcions en rapòrt setmanièr diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 95221e818..65b282c68 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -328,7 +328,6 @@ pl: comment: none: Brak created_at: Zgłoszono - id: ID mark_as_resolved: Oznacz jako rozwiązane mark_as_unresolved: Oznacz jako nierozwiązane notes: @@ -339,7 +338,6 @@ pl: placeholder: Opisz wykonane akcje i inne szczegóły dotyczące tego zgłoszenia… reopen: Otwórz ponownie report: 'Zgłoszenie #%{id}' - report_contents: Zawartość reported_account: Zgłoszone konto reported_by: Zgłaszający resolved: Rozwiązane @@ -347,12 +345,10 @@ pl: silence_account: Wycisz konto status: Stan suspend_account: Zawieś konto - target: Cel title: Zgłoszenia unassign: Cofnij przypisanie unresolved: Nierozwiązane updated_at: Zaktualizowano - view: Wyświetl settings: activity_api_enabled: desc_html: Liczy publikowane lokalnie wpisy, aktywnych użytkowników i nowe rejestracje w ciągu danego tygodnia diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index cc4dddb9c..bda31bd7f 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -320,7 +320,6 @@ pt-BR: comment: none: Nenhum created_at: Denunciado - id: ID mark_as_resolved: Marcar como resolvido mark_as_unresolved: Marcar como não resolvido notes: @@ -331,7 +330,6 @@ pt-BR: placeholder: Descreva que ações foram tomadas, ou quaisquer outras atualizações relacionadas… reopen: Reabrir denúncia report: 'Denúncia #%{id}' - report_contents: Conteúdos reported_account: Conta denunciada reported_by: Denunciada por resolved: Resolvido @@ -339,12 +337,10 @@ pt-BR: silence_account: Silenciar conta status: Status suspend_account: Suspender conta - target: Alvo title: Denúncias unassign: Desatribuir unresolved: Não resolvido updated_at: Atualizado - view: Visualizar settings: activity_api_enabled: desc_html: Contagem de status postados localmente, usuários ativos e novos cadastros filtrados semanalmente diff --git a/config/locales/pt.yml b/config/locales/pt.yml index eebeb498e..a1efd6c65 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -244,20 +244,16 @@ pt: are_you_sure: Tens a certeza? comment: none: Nenhum - id: ID mark_as_resolved: Marcar como resolvido report: 'Denúncia #%{id}' - report_contents: Conteúdos reported_account: Conta denunciada reported_by: Denúnciada por resolved: Resolvido silence_account: Conta silenciada status: Estado suspend_account: Conta suspensa - target: Alvo title: Denúncias unresolved: Por resolver - view: Ver settings: activity_api_enabled: desc_html: Contagem semanais de publicações locais, utilizadores activos e novos registos diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 19771aefa..121652b73 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -331,7 +331,6 @@ ru: comment: none: Нет created_at: Создано - id: ID mark_as_resolved: Отметить как разрешенную mark_as_unresolved: Отметить как неразрешённую notes: @@ -342,7 +341,6 @@ ru: placeholder: Опишите, какие действия были приняты, или любые другие подробности… reopen: Переоткрыть жалобу report: 'Жалоба #%{id}' - report_contents: Содержимое reported_account: Аккаунт нарушителя reported_by: Отправитель жалобы resolved: Разрешено @@ -350,12 +348,10 @@ ru: silence_account: Заглушить аккаунт status: Статус suspend_account: Блокировать аккаунт - target: Цель title: Жалобы unassign: Снять назначение unresolved: Неразрешенные updated_at: Обновлена - view: Просмотреть settings: activity_api_enabled: desc_html: Подсчёт количества локальных статусов, активных пользователей и новых регистраций на еженедельной основе diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 1be11baa4..e029df1ea 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -289,7 +289,6 @@ sk: comment: none: Žiadne created_at: Nahlásené - id: Identifikácia mark_as_resolved: Označiť ako vyriešené mark_as_unresolved: Označ ako nevyriešené notes: @@ -300,7 +299,6 @@ sk: placeholder: Opíš aké opatrenia boli urobené, alebo akékoľvek iné súvisiace aktualizácie… reopen: Znovu otvor report report: Nahlásiť - report_contents: Obsah reported_account: Nahlásený účet reported_by: Nahlásené užívateľom resolved: Vyriešené @@ -308,12 +306,10 @@ sk: silence_account: Zamĺčať účet status: Stav suspend_account: Pozastaviť účet - target: Cieľ title: Reporty unassign: Odobrať unresolved: Nevyriešené updated_at: Aktualizované - view: Zobraziť settings: activity_api_enabled: desc_html: Sčítanie lokálne publikovaných príspevkov, aktívnych užívateľov, a nových registrácii, v týždenných intervaloch diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index d9813db47..800fe5414 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -246,20 +246,16 @@ sr-Latn: are_you_sure: Da li ste sigurni? comment: none: Ništa - id: ID mark_as_resolved: Označi kao rešen report: 'Prijava #%{id}' - report_contents: Sadržaj reported_account: Prijavljeni nalog reported_by: Prijavio resolved: Rešeni silence_account: Ućutkaj nalog status: Status suspend_account: Suspenduj nalog - target: Cilj title: Prijave unresolved: Nerešeni - view: Pogledaj settings: bootstrap_timeline_accounts: desc_html: Odvojite više korisničkih imena zarezom. Radi samo za lokalne i otključane naloge. Ako je prazno, onda se odnosi na sve lokalne administratore. diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 516908415..960fccdc0 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -286,20 +286,16 @@ sr: are_you_sure: Да ли сте сигурни? comment: none: Ништа - id: ID mark_as_resolved: Означи као решен report: 'Пријава #%{id}' - report_contents: Садржај reported_account: Пријављени налог reported_by: Пријавио resolved: Решени silence_account: Ућуткај налог status: Статус suspend_account: Суспендуј налог - target: Циљ title: Пријаве unresolved: Нерешени - view: Погледај settings: bootstrap_timeline_accounts: desc_html: Одвојите више корисничких имена зарезом. Ради само за локалне и откључане налоге. Ако је празно, онда се односи на све локалне администраторе. diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 605d9db12..3f157ecd1 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -271,7 +271,6 @@ sv: comment: none: Ingen created_at: Anmäld - id: ID mark_as_resolved: Markera som löst mark_as_unresolved: Markera som olöst notes: @@ -282,7 +281,6 @@ sv: placeholder: Beskriv vilka åtgärder som vidtagits eller andra uppdateringar till den här anmälan. reopen: Återuppta anmälan report: 'Anmäl #%{id}' - report_contents: Innehåll reported_account: Anmält konto reported_by: Anmäld av resolved: Löst @@ -290,12 +288,10 @@ sv: silence_account: Tysta ner konto status: Status suspend_account: Suspendera konto - target: Mål title: Anmälningar unassign: Otilldela unresolved: Olösta updated_at: Uppdaterad - view: Granska settings: activity_api_enabled: desc_html: Räkning av lokalt postade statusar, aktiva användare och nyregistreringar per vecka diff --git a/config/locales/th.yml b/config/locales/th.yml index 88e1eb44c..8c411f252 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -111,20 +111,16 @@ th: reports: comment: none: None - id: ไอดี mark_as_resolved: ทำเครื่องหมายว่าจัดการแล้ว report: 'Report #%{id}' - report_contents: เนื้อหา reported_account: รายงานแอคเคาท์ reported_by: รายงานโดย resolved: จัดการแล้ว silence_account: แอคเค๊าท์ที่ปิดเสียง status: สถานะ suspend_account: แอคเค๊าท์ที่หยุดไว้ - target: เป้าหมาย title: รายงาน unresolved: Unresolved - view: วิว settings: contact_information: email: กรอกที่อยู่อีเมล์สาธารณะ diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 26dd4c1e3..d7ecb480f 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -110,20 +110,16 @@ tr: reports: comment: none: Yok - id: ID mark_as_resolved: Giderildi olarak işaretle report: 'Şikayet #%{id}' - report_contents: İçerik reported_account: Şikayet edilen hesap reported_by: Şikayet eden resolved: Giderildi silence_account: Hesabı sustur status: Durum suspend_account: Hesabı uzaklaştır - target: Hedef title: Şikayetler unresolved: Giderilmedi - view: Görüntüle settings: contact_information: email: Herkese açık e-posta adresiniz diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 6783495bb..9da52d3f5 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -299,7 +299,6 @@ uk: comment: none: Немає created_at: Створено - id: ID mark_as_resolved: Відмітити як вирішену mark_as_unresolved: Відмітити як невирішену notes: @@ -310,7 +309,6 @@ uk: placeholder: Опишіть, які дії були виконані, або інші зміни, що стосуються справи... reopen: Перевідкрити скаргу report: 'Скарга #%{id}' - report_contents: Зміст reported_account: Акаунт порушника reported_by: Відправник скарги resolved: Вирішено @@ -318,12 +316,10 @@ uk: silence_account: Заглушити акаунт status: Статус suspend_account: Заблокувати акаунт - target: Ціль title: Скарги unassign: Зняти призначення unresolved: Невирішені updated_at: Оновлені - view: Подивитися settings: activity_api_enabled: desc_html: Кількість локальних постів, активних та нових користувачів у тижневих розрізах diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index b2f9fd01e..cace8d050 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -269,7 +269,6 @@ zh-CN: comment: none: 没有 created_at: 举报时间 - id: ID mark_as_resolved: 标记为“已处理” mark_as_unresolved: 标记为“未处理” notes: @@ -280,7 +279,6 @@ zh-CN: placeholder: 描述已经执行的操作,或其他任何相关的跟进情况 reopen: 重开举报 report: '举报 #%{id}' - report_contents: 内容 reported_account: 举报用户 reported_by: 举报人 resolved: 已处理 @@ -288,12 +286,10 @@ zh-CN: silence_account: 隐藏用户 status: 状态 suspend_account: 封禁用户 - target: 被举报人 title: 举报 unassign: 取消接管 unresolved: 未处理 updated_at: 更新时间 - view: 查看 settings: activity_api_enabled: desc_html: 本站用户发布的嘟文数,以及本站的活跃用户数和一周内新用户数 diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index ee7ca4443..b1f9922fa 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -271,7 +271,6 @@ zh-HK: comment: none: 沒有 created_at: 日期 - id: ID mark_as_resolved: 標示為「已處理」 mark_as_unresolved: 標示為「未處理」 notes: @@ -282,7 +281,6 @@ zh-HK: placeholder: 記錄已執行的動作,或其他相關的更新…… reopen: 重開舉報 report: '舉報 #%{id}' - report_contents: 舉報內容 reported_account: 舉報用戶 reported_by: 舉報者 resolved: 已處理 @@ -290,12 +288,10 @@ zh-HK: silence_account: 將用戶靜音 status: 狀態 suspend_account: 將用戶停權 - target: 對象 title: 舉報 unassign: 取消指派 unresolved: 未處理 updated_at: 更新 - view: 檢視 settings: activity_api_enabled: desc_html: 本站用戶發佈的文章,以及本站的活躍用戶和一週內新用戶數 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 0a18dc990..a38395700 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -271,7 +271,6 @@ zh-TW: comment: none: 無 created_at: 日期 - id: ID mark_as_resolved: 標記為「已解決」 mark_as_unresolved: 標記為「未解決」 notes: @@ -282,7 +281,6 @@ zh-TW: placeholder: 記錄已執行的動作,或其他相關的更新... reopen: 重開檢舉 report: '檢舉 #%{id}' - report_contents: 檢舉內容 reported_account: 被檢舉使用者 reported_by: 檢舉人 resolved: 已解決 @@ -290,12 +288,10 @@ zh-TW: silence_account: 靜音使用者 status: 狀態 suspend_account: 停權使用者 - target: 對象 title: 檢舉 unassign: 取消指派 unresolved: 未解決 updated_at: 更新 - view: 檢視 settings: activity_api_enabled: desc_html: 本站使用者發佈的嘟文數量,以及本站的活躍使用者與一週內新使用者數量 -- cgit From f4d549d30081478b1fe2bde9d340262e132bb891 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 18 Sep 2018 16:45:58 +0200 Subject: Redesign forms, verify link ownership with rel="me" (#8703) * Verify link ownership with rel="me" * Add explanation about verification to UI * Perform link verifications * Add click-to-copy widget for verification HTML * Redesign edit profile page * Redesign forms * Improve responsive design of settings pages * Restore landing page sign-up form * Fix typo * Support tags, add spec * Fix links not being verified on first discovery and passive updates --- app/helpers/home_helper.rb | 8 + .../mastodon/features/account/components/header.js | 15 +- app/javascript/packs/public.js | 27 ++ app/javascript/styles/mastodon/accounts.scss | 14 + app/javascript/styles/mastodon/admin.scss | 84 +++-- app/javascript/styles/mastodon/basics.scss | 7 + app/javascript/styles/mastodon/boost.scss | 7 - app/javascript/styles/mastodon/components.scss | 9 +- app/javascript/styles/mastodon/containers.scss | 8 + app/javascript/styles/mastodon/forms.scss | 361 ++++++++++++++------- app/lib/activitypub/activity/update.rb | 1 + app/models/account.rb | 54 ++- app/serializers/rest/account_serializer.rb | 4 + .../activitypub/process_account_service.rb | 5 + app/services/fetch_link_card_service.rb | 2 +- app/services/update_account_service.rb | 5 +- app/services/verify_link_service.rb | 32 ++ app/views/about/_registration.html.haml | 11 +- app/views/accounts/_bio.html.haml | 7 +- app/views/admin/change_emails/show.html.haml | 11 +- app/views/admin/custom_emojis/new.html.haml | 5 +- app/views/admin/domain_blocks/new.html.haml | 13 +- app/views/admin/email_domain_blocks/new.html.haml | 3 +- app/views/admin/settings/edit.html.haml | 63 ++-- .../auth/confirmations/finish_signup.html.haml | 3 +- app/views/auth/confirmations/new.html.haml | 3 +- app/views/auth/passwords/edit.html.haml | 6 +- app/views/auth/passwords/new.html.haml | 3 +- app/views/auth/registrations/edit.html.haml | 20 +- app/views/auth/registrations/new.html.haml | 22 +- app/views/auth/sessions/new.html.haml | 12 +- app/views/auth/sessions/two_factor.html.haml | 3 +- app/views/filters/_fields.html.haml | 12 +- app/views/invites/_form.html.haml | 8 +- app/views/invites/index.html.haml | 2 +- app/views/settings/applications/_fields.html.haml | 6 +- app/views/settings/imports/show.html.haml | 4 +- app/views/settings/preferences/show.html.haml | 34 +- app/views/settings/profiles/show.html.haml | 53 ++- .../confirmations/new.html.haml | 3 +- .../two_factor_authentications/show.html.haml | 2 +- app/workers/verify_account_links_worker.rb | 20 ++ config/initializers/simple_form.rb | 29 +- config/locales/en.yml | 6 +- config/locales/simple_form.en.yml | 3 + spec/services/verify_link_service_spec.rb | 51 +++ 46 files changed, 756 insertions(+), 305 deletions(-) create mode 100644 app/services/verify_link_service.rb create mode 100644 app/workers/verify_account_links_worker.rb create mode 100644 spec/services/verify_link_service_spec.rb (limited to 'app/javascript/styles') diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb index 0157785fa..ba7c443c2 100644 --- a/app/helpers/home_helper.rb +++ b/app/helpers/home_helper.rb @@ -48,4 +48,12 @@ module HomeHelper '1+' end end + + def custom_field_classes(field) + if field.verified? + 'verified' + else + 'emojify' + end + end end diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js index e49758b04..11ae58905 100644 --- a/app/javascript/mastodon/features/account/components/header.js +++ b/app/javascript/mastodon/features/account/components/header.js @@ -15,8 +15,18 @@ const messages = defineMessages({ requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' }, unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, + linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' }, }); +const dateFormatOptions = { + month: 'short', + day: 'numeric', + year: 'numeric', + hour12: false, + hour: '2-digit', + minute: '2-digit', +}; + class Avatar extends ImmutablePureComponent { static propTypes = { @@ -163,7 +173,10 @@ class Header extends ImmutablePureComponent { {fields.map((pair, i) => (
-
+ +
+ {pair.get('verified_at') && } +
))} diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index c83e2889a..22a8643d9 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -68,6 +68,7 @@ function main() { }); const reactComponents = document.querySelectorAll('[data-component]'); + if (reactComponents.length > 0) { import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container') .then(({ default: MediaContainer }) => { @@ -80,6 +81,7 @@ function main() { } const parallaxComponents = document.querySelectorAll('.parallax'); + if (parallaxComponents.length > 0 ) { new Rellax('.parallax', { speed: -1 }); } @@ -87,6 +89,7 @@ function main() { const history = createHistory(); const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status'); const location = history.location; + if (detailedStatuses.length === 1 && (!location.state || !location.state.scrolledToDetailedStatus)) { detailedStatuses[0].scrollIntoView(); history.replace(location.pathname, { ...location.state, scrolledToDetailedStatus: true }); @@ -175,6 +178,30 @@ function main() { lock.style.display = 'none'; } }); + + delegate(document, '.input-copy input', 'click', ({ target }) => { + target.select(); + }); + + delegate(document, '.input-copy button', 'click', ({ target }) => { + const input = target.parentNode.querySelector('input'); + + input.focus(); + input.select(); + + try { + if (document.execCommand('copy')) { + input.blur(); + target.parentNode.classList.add('copied'); + + setTimeout(() => { + target.parentNode.classList.remove('copied'); + }, 700); + } + } catch (err) { + console.error(err); + } + }); } loadPolyfills().then(main).catch(error => { diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index c27bc0df3..06effbdb2 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -265,6 +265,20 @@ } } + .verified { + border: 1px solid rgba($valid-value-color, 0.5); + background: rgba($valid-value-color, 0.25); + + a { + color: $valid-value-color; + font-weight: 500; + } + + &__mark { + color: $valid-value-color; + } + } + dl:last-child { border-bottom: 0; } diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 7d359921a..9dfd89dc2 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -1,3 +1,5 @@ +$no-columns-breakpoint: 600px; + .admin-wrapper { display: flex; justify-content: center; @@ -24,12 +26,22 @@ height: 100px; } + @media screen and (max-width: $no-columns-breakpoint) { + & > a:first-child { + display: none; + } + } + ul { list-style: none; border-radius: 4px 0 0 4px; overflow: hidden; margin-bottom: 20px; + @media screen and (max-width: $no-columns-breakpoint) { + margin-bottom: 0; + } + a { display: block; padding: 15px; @@ -62,20 +74,24 @@ a { border: 0; padding: 15px 35px; + } + } - &.selected { - color: $primary-text-color; - background-color: $ui-highlight-color; - border-bottom: 0; - border-radius: 0; + .simple-navigation-active-leaf a { + color: $primary-text-color; + background-color: $ui-highlight-color; + border-bottom: 0; + border-radius: 0; - &:hover { - background-color: lighten($ui-highlight-color, 5%); - } - } + &:hover { + background-color: lighten($ui-highlight-color, 5%); } } } + + & > ul > .simple-navigation-active-leaf a { + border-radius: 4px 0 0 4px; + } } .content-wrapper { @@ -89,11 +105,19 @@ padding-top: 60px; padding-left: 25px; + @media screen and (max-width: $no-columns-breakpoint) { + max-width: none; + padding: 15px; + padding-top: 30px; + } + h2 { color: $secondary-text-color; font-size: 24px; line-height: 28px; font-weight: 400; + padding-bottom: 40px; + border-bottom: 1px solid lighten($ui-base-color, 8%); margin-bottom: 40px; } @@ -108,7 +132,7 @@ h4 { text-transform: uppercase; font-size: 13px; - font-weight: 500; + font-weight: 700; color: $darker-text-color; padding-bottom: 8px; margin-bottom: 8px; @@ -122,6 +146,11 @@ font-weight: 400; } + .fields-group h6 { + color: $primary-text-color; + font-weight: 500; + } + & > p { font-size: 14px; line-height: 18px; @@ -172,30 +201,7 @@ } } - .simple_form { - max-width: 400px; - - &.edit_user, - &.new_form_admin_settings, - &.new_form_two_factor_confirmation, - &.new_form_delete_confirmation, - &.new_import, - &.new_domain_block, - &.edit_domain_block { - max-width: none; - } - - .form_two_factor_confirmation_code, - .form_delete_confirmation_password { - max-width: 400px; - } - - .actions { - max-width: 400px; - } - } - - @media screen and (max-width: 600px) { + @media screen and (max-width: $no-columns-breakpoint) { display: block; overflow-y: auto; -webkit-overflow-scrolling: touch; @@ -209,16 +215,8 @@ .sidebar { width: 100%; - padding: 10px 0; + padding: 0; height: auto; - - .logo { - margin: 20px auto; - } - } - - .content { - padding-top: 20px; } } } diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index 7a6a1c490..3bbb31e6e 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -1,3 +1,10 @@ +@function hex-color($color) { + @if type-of($color) == 'color' { + $color: str-slice(ie-hex-str($color), 4); + } + @return '%23' + unquote($color) +} + body { font-family: 'mastodon-font-sans-serif', sans-serif; background: darken($ui-base-color, 8%); diff --git a/app/javascript/styles/mastodon/boost.scss b/app/javascript/styles/mastodon/boost.scss index 8e11cb596..5a6d6ae40 100644 --- a/app/javascript/styles/mastodon/boost.scss +++ b/app/javascript/styles/mastodon/boost.scss @@ -1,10 +1,3 @@ -@function hex-color($color) { - @if type-of($color) == 'color' { - $color: str-slice(ie-hex-str($color), 4); - } - @return '%23' + unquote($color) -} - button.icon-button i.fa-retweet { background-image: url("data:image/svg+xml;utf8,"); diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 048563336..9421523bd 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -5363,9 +5363,11 @@ noscript { overflow: hidden; margin: 20px -10px -20px; border-bottom: 0; + border-top: 0; dl { - border-top: 1px solid lighten($ui-base-color, 8%); + border-top: 1px solid lighten($ui-base-color, 4%); + border-bottom: 0; display: flex; } @@ -5392,6 +5394,11 @@ noscript { flex: 1 1 auto; color: $primary-text-color; background: $ui-base-color; + + &.verified { + border: 1px solid rgba($valid-value-color, 0.5); + background: rgba($valid-value-color, 0.25); + } } } diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index 2c2ce5d3b..47582f323 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -718,6 +718,14 @@ a { color: lighten($ui-highlight-color, 8%); } + + dl:first-child .verified { + border-radius: 0 4px 0 0; + } + + .verified a { + color: $valid-value-color; + } } .account__header__content { diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 144b4a519..cbd3de94c 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -1,3 +1,5 @@ +$no-columns-breakpoint: 600px; + code { font-family: 'mastodon-font-monospace', monospace; font-weight: 400; @@ -13,6 +15,60 @@ code { .input { margin-bottom: 15px; overflow: hidden; + + &.hidden { + margin: 0; + } + + &.radio_buttons { + .radio { + margin-bottom: 15px; + + &:last-child { + margin-bottom: 0; + } + } + + .radio > label { + position: relative; + padding-left: 28px; + + input { + position: absolute; + top: -2px; + left: 0; + } + } + } + + &.boolean { + position: relative; + margin-bottom: 0; + + .label_input > label { + font-family: inherit; + font-size: 14px; + padding-top: 5px; + color: $primary-text-color; + display: block; + width: auto; + } + + .label_input, + .hint { + padding-left: 28px; + } + + .label_input__wrapper { + position: static; + } + + label.checkbox { + position: absolute; + top: 2px; + left: 0; + } + } } .row { @@ -27,9 +83,22 @@ code { } } + .hint { + color: $darker-text-color; + + a { + color: $highlight-text-color; + } + + code { + border-radius: 3px; + padding: 0.2em 0.4em; + background: darken($ui-base-color, 12%); + } + } + span.hint { display: block; - color: $darker-text-color; font-size: 12px; margin-top: 4px; } @@ -44,17 +113,6 @@ code { line-height: 18px; margin-top: 15px; margin-bottom: 0; - color: $darker-text-color; - - a { - color: $highlight-text-color; - } - } - - code { - border-radius: 3px; - padding: 0.2em 0.4em; - background: darken($ui-base-color, 12%); } } @@ -72,87 +130,60 @@ code { } } - .label_input { - display: flex; + .input.with_floating_label { + .label_input { + display: flex; - label { - flex: 0 0 auto; + & > label { + font-family: inherit; + font-size: 14px; + color: $primary-text-color; + font-weight: 500; + min-width: 150px; + flex: 0 0 auto; + } + + input, + select { + flex: 1 1 auto; + } } - input { - flex: 1 1 auto; + &.select .hint { + margin-top: 6px; + margin-left: 150px; } } .input.with_label { - padding: 15px 0; - margin-bottom: 0; - - .label_input { - flex-wrap: wrap; - align-items: flex-start; - } - - &.file .label_input { - flex-wrap: nowrap; - } - - &.select .label_input { - align-items: initial; - } - .label_input > label { font-family: inherit; - font-size: 16px; + font-size: 14px; color: $primary-text-color; display: block; - padding-top: 5px; - margin-bottom: 5px; - flex: 1; - min-width: 150px; + margin-bottom: 8px; word-wrap: break-word; + font-weight: 500; + } - &.select { - flex: 0; - } - - & ~ * { - margin-left: 10px; - } + .hint { + margin-top: 6px; } ul { flex: 390px; } - - &.boolean { - padding: initial; - margin-bottom: initial; - - .label_input > label { - font-family: inherit; - font-size: 14px; - color: $primary-text-color; - display: block; - width: auto; - } - - label.checkbox { - position: relative; - padding-left: 25px; - flex: 1 1 auto; - } - } } .input.with_block_label { - padding-top: 15px; + max-width: none; & > label { font-family: inherit; font-size: 16px; color: $primary-text-color; display: block; + font-weight: 500; padding-top: 5px; } @@ -165,49 +196,70 @@ code { } } + .required abbr { + text-decoration: none; + color: lighten($error-value-color, 12%); + } + .fields-group { margin-bottom: 25px; - } - .input.radio_buttons .radio label { - margin-bottom: 5px; - font-family: inherit; - font-size: 14px; - color: $primary-text-color; - display: block; - width: auto; + .input:last-child { + margin-bottom: 0; + } } - .input.boolean { - margin-bottom: 5px; + .fields-row { + display: flex; + margin: 0 -10px; + padding-top: 5px; + margin-bottom: 25px; - label { - font-family: inherit; - font-size: 14px; - color: $primary-text-color; - display: block; - width: auto; + .input { + max-width: none; } - label.checkbox { - position: relative; - padding-left: 25px; + &__column { + box-sizing: border-box; + padding: 0 10px; flex: 1 1 auto; + min-height: 1px; + + &-6 { + max-width: 50%; + } } - input[type=checkbox] { - position: absolute; - left: 0; - top: 5px; - margin: 0; + .fields-group:last-child, + .fields-row__column.fields-group { + margin-bottom: 0; } - .hint { - padding-left: 25px; - margin-left: 0; + @media screen and (max-width: $no-columns-breakpoint) { + display: block; + margin-bottom: 0; + + &__column { + max-width: none; + } + + .fields-group:last-child, + .fields-row__column.fields-group, + .fields-row__column { + margin-bottom: 25px; + } } } + .input.radio_buttons .radio label { + margin-bottom: 5px; + font-family: inherit; + font-size: 14px; + color: $primary-text-color; + display: block; + width: auto; + } + .check_boxes { .checkbox { label { @@ -236,12 +288,7 @@ code { input[type=email], input[type=password], textarea { - background: transparent; box-sizing: border-box; - border: 0; - border-bottom: 2px solid $ui-primary-color; - border-radius: 2px 2px 0 0; - padding: 7px 4px; font-size: 16px; color: $primary-text-color; display: block; @@ -249,23 +296,31 @@ code { outline: 0; font-family: inherit; resize: vertical; + background: darken($ui-base-color, 10%); + border: 1px solid darken($ui-base-color, 14%); + border-radius: 4px; + padding: 10px; &:invalid { box-shadow: none; } &:focus:invalid { - border-bottom-color: lighten($error-red, 12%); + border-color: lighten($error-red, 12%); } &:required:valid { - border-bottom-color: $valid-value-color; + border-color: $valid-value-color; + } + + &:hover { + border-color: darken($ui-base-color, 20%); } &:active, &:focus { - border-bottom-color: $highlight-text-color; - background: rgba($base-overlay-background, 0.1); + border-color: $highlight-text-color; + background: darken($ui-base-color, 8%); } } @@ -349,22 +404,32 @@ code { } select { + appearance: none; + box-sizing: border-box; font-size: 16px; - max-height: 29px; + color: $primary-text-color; + display: block; + width: 100%; + outline: 0; + font-family: inherit; + resize: vertical; + background: darken($ui-base-color, 10%) url("data:image/svg+xml;utf8,") no-repeat right 8px center / auto 16px; + border: 1px solid darken($ui-base-color, 14%); + border-radius: 4px; + padding: 10px; + height: 41px; } - .input-with-append { - position: relative; - - .input input { - padding-right: 142px; + .label_input { + &__wrapper { + position: relative; } - .append { + &__append { position: absolute; - right: 0; - top: 0; - padding: 7px 4px; + right: 1px; + top: 1px; + padding: 10px; padding-bottom: 9px; font-size: 16px; color: $dark-text-color; @@ -383,7 +448,7 @@ code { right: 0; bottom: 1px; width: 5px; - background-image: linear-gradient(to right, rgba($ui-base-color, 0), $ui-base-color); + background-image: linear-gradient(to right, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%)); } } } @@ -459,6 +524,30 @@ code { } } +.quick-nav { + list-style: none; + margin-bottom: 25px; + font-size: 14px; + + li { + display: inline-block; + margin-right: 10px; + } + + a { + color: $highlight-text-color; + text-transform: uppercase; + text-decoration: none; + font-weight: 700; + + &:hover, + &:focus, + &:active { + color: lighten($highlight-text-color, 8%); + } + } +} + .oauth-prompt, .follow-prompt { margin-bottom: 30px; @@ -632,3 +721,49 @@ code { font-family: 'mastodon-font-monospace', monospace; } } + +.input-copy { + background: darken($ui-base-color, 10%); + border: 1px solid darken($ui-base-color, 14%); + border-radius: 4px; + display: flex; + align-items: center; + padding-right: 4px; + position: relative; + top: 1px; + transition: border-color 300ms linear; + + &__wrapper { + flex: 1 1 auto; + } + + input[type=text] { + background: transparent; + border: 0; + padding: 10px; + font-size: 14px; + font-family: 'mastodon-font-monospace', monospace; + } + + button { + flex: 0 0 auto; + margin: 4px; + text-transform: none; + font-weight: 400; + font-size: 14px; + padding: 7px 18px; + padding-bottom: 6px; + width: auto; + transition: background 300ms linear; + } + + &.copied { + border-color: $valid-value-color; + transition: none; + + button { + background: $valid-value-color; + transition: none; + } + } +} diff --git a/app/lib/activitypub/activity/update.rb b/app/lib/activitypub/activity/update.rb index 6eebc3b5c..67dae3f81 100644 --- a/app/lib/activitypub/activity/update.rb +++ b/app/lib/activitypub/activity/update.rb @@ -11,6 +11,7 @@ class ActivityPub::Activity::Update < ActivityPub::Activity def update_account return if @account.uri != object_uri + ActivityPub::ProcessAccountService.new.call(@account.username, @account.domain, @object, signed_with_known_key: true) end end diff --git a/app/models/account.rb b/app/models/account.rb index 440a731e3..979e0b12c 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -223,11 +223,19 @@ class Account < ApplicationRecord end def fields_attributes=(attributes) - fields = [] + fields = [] + old_fields = self[:fields] || [] if attributes.is_a?(Hash) attributes.each_value do |attr| next if attr[:name].blank? + + previous = old_fields.find { |item| item['value'] == attr[:value] } + + if previous && previous['verified_at'].present? + attr[:verified_at] = previous['verified_at'] + end + fields << attr end end @@ -235,13 +243,18 @@ class Account < ApplicationRecord self[:fields] = fields end + DEFAULT_FIELDS_SIZE = 4 + def build_fields - return if fields.size >= 4 + return if fields.size >= DEFAULT_FIELDS_SIZE + + tmp = self[:fields] || [] + + (DEFAULT_FIELDS_SIZE - tmp.size).times do + tmp << { name: '', value: '' } + end - raw_fields = self[:fields] || [] - add_fields = 4 - raw_fields.size - add_fields.times { raw_fields << { name: '', value: '' } } - self.fields = raw_fields + self.fields = tmp end def magic_key @@ -294,17 +307,32 @@ class Account < ApplicationRecord end class Field < ActiveModelSerializers::Model - attributes :name, :value, :account, :errors + attributes :name, :value, :verified_at, :account, :errors + + def initialize(account, attributes) + @account = account + @attributes = attributes + @name = attributes['name'].strip[0, 255] + @value = attributes['value'].strip[0, 255] + @verified_at = attributes['verified_at']&.to_datetime + @errors = {} + end + + def verified? + verified_at.present? + end + + def verifiable? + value.present? && /\A#{FetchLinkCardService::URL_PATTERN}\z/ =~ value + end - def initialize(account, attr) - @account = account - @name = attr['name'].strip[0, 255] - @value = attr['value'].strip[0, 255] - @errors = {} + def mark_verified! + @verified_at = Time.now.utc + @attributes['verified_at'] = @verified_at end def to_h - { name: @name, value: @value } + { name: @name, value: @value, verified_at: @verified_at } end end diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index 3a724aa7c..d84b48afb 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -13,6 +13,10 @@ class REST::AccountSerializer < ActiveModel::Serializer class FieldSerializer < ActiveModel::Serializer attributes :name, :value + attribute :verified_at, if: :verifiable? + + delegate :verifiable?, to: :object + def value Formatter.instance.format_field(object.account, object.value) end diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 670a0e4d6..c77858f1d 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -34,6 +34,7 @@ class ActivityPub::ProcessAccountService < BaseService after_protocol_change! if protocol_changed? after_key_change! if key_changed? && !@options[:signed_with_known_key] check_featured_collection! if @account.featured_collection_url.present? + check_links! unless @account.fields.empty? @account rescue Oj::ParseError @@ -99,6 +100,10 @@ class ActivityPub::ProcessAccountService < BaseService ActivityPub::SynchronizeFeaturedCollectionWorker.perform_async(@account.id) end + def check_links! + VerifyAccountLinksWorker.perform_async(@account.id) + end + def actor_type if @json['type'].is_a?(Array) @json['type'].find { |type| ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(type) } diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index ea94e2491..4169c685b 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -29,7 +29,7 @@ class FetchLinkCardService < BaseService end attach_card if @card&.persisted? - rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::LengthValidationError => e + rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e Rails.logger.debug "Error fetching link #{@url}: #{e}" nil end diff --git a/app/services/update_account_service.rb b/app/services/update_account_service.rb index 09ea377e7..362d80c9e 100644 --- a/app/services/update_account_service.rb +++ b/app/services/update_account_service.rb @@ -2,11 +2,14 @@ class UpdateAccountService < BaseService def call(account, params, raise_error: false) - was_locked = account.locked + was_locked = account.locked update_method = raise_error ? :update! : :update + account.send(update_method, params).tap do |ret| next unless ret + authorize_all_follow_requests(account) if was_locked && !account.locked + VerifyAccountLinksWorker.perform_async(@account.id) if account.fields_changed? end end diff --git a/app/services/verify_link_service.rb b/app/services/verify_link_service.rb new file mode 100644 index 000000000..9da7f93c3 --- /dev/null +++ b/app/services/verify_link_service.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class VerifyLinkService < BaseService + def call(field) + @link_back = ActivityPub::TagManager.instance.url_for(field.account) + @url = field.value + + perform_request! + + return unless link_back_present? + + field.mark_verified! + field.account.save! + rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e + Rails.logger.debug "Error fetching link #{@url}: #{e}" + nil + end + + private + + def perform_request! + @body = Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res| + res.code != 200 ? nil : res.body_with_limit + end + end + + def link_back_present? + return false if @body.empty? + + Nokogiri::HTML(@body).xpath('//a[@rel="me"]|//link[@rel="me"]').any? { |link| link['href'] == @link_back } + end +end diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml index 6ca1d7129..ee4f8fe2e 100644 --- a/app/views/about/_registration.html.haml +++ b/app/views/about/_registration.html.haml @@ -1,13 +1,10 @@ = simple_form_for(new_user, url: user_registration_path) do |f| = f.simple_fields_for :account do |account_fields| - .input-with-append - = account_fields.input :username, autofocus: true, placeholder: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' } - .append - = "@#{site_hostname}" + = account_fields.input :username, wrapper: :with_label, autofocus: true, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false - = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' } - = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' } - = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' } + = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false + = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false + = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false .actions = f.button :button, t('auth.register'), type: :submit, class: 'button button-primary' diff --git a/app/views/accounts/_bio.html.haml b/app/views/accounts/_bio.html.haml index 4e674beff..2ea34a048 100644 --- a/app/views/accounts/_bio.html.haml +++ b/app/views/accounts/_bio.html.haml @@ -4,8 +4,11 @@ - account.fields.each do |field| %dl %dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true) - %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value, custom_emojify: true) - + %dd{ title: field.value, class: custom_field_classes(field) } + - if field.verified? + %span.verified__mark{ title: t('accounts.link_verified_on', date: l(field.verified_at)) } + = fa_icon 'check' + = Formatter.instance.format_field(account, field.value, custom_emojify: true) = account_badge(account) - if account.note.present? diff --git a/app/views/admin/change_emails/show.html.haml b/app/views/admin/change_emails/show.html.haml index a661b1ad6..6febef9b1 100644 --- a/app/views/admin/change_emails/show.html.haml +++ b/app/views/admin/change_emails/show.html.haml @@ -2,6 +2,11 @@ = t('admin.accounts.change_email.title', username: @account.acct) = simple_form_for @user, url: admin_account_change_email_path(@account.id) do |f| - = f.input :email, wrapper: :with_label, disabled: true, label: t('admin.accounts.change_email.current_email') - = f.input :unconfirmed_email, wrapper: :with_label, label: t('admin.accounts.change_email.new_email') - = f.button :submit, class: "button", value: t('admin.accounts.change_email.submit') + .fields-group + = f.input :email, wrapper: :with_label, disabled: true, label: t('admin.accounts.change_email.current_email') + + .fields-group + = f.input :unconfirmed_email, wrapper: :with_label, label: t('admin.accounts.change_email.new_email') + + .actions + = f.button :submit, class: "button", value: t('admin.accounts.change_email.submit') diff --git a/app/views/admin/custom_emojis/new.html.haml b/app/views/admin/custom_emojis/new.html.haml index 672afe435..e15a07cb8 100644 --- a/app/views/admin/custom_emojis/new.html.haml +++ b/app/views/admin/custom_emojis/new.html.haml @@ -5,8 +5,9 @@ = render 'shared/error_messages', object: @custom_emoji .fields-group - = f.input :shortcode, placeholder: t('admin.custom_emojis.shortcode'), hint: t('admin.custom_emojis.shortcode_hint') - = f.input :image, input_html: { accept: 'image/png' }, hint: t('admin.custom_emojis.image_hint') + = f.input :shortcode, wrapper: :with_label, label: t('admin.custom_emojis.shortcode'), hint: t('admin.custom_emojis.shortcode_hint') + .fields-group + = f.input :image, wrapper: :with_label, input_html: { accept: 'image/png' }, hint: t('admin.custom_emojis.image_hint') .actions = f.button :button, t('admin.custom_emojis.upload'), type: :submit diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index 2b8b24e23..f0eb217eb 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -7,14 +7,15 @@ = simple_form_for @domain_block, url: admin_domain_blocks_path do |f| = render 'shared/error_messages', object: @domain_block - %p.hint= t('.hint') + .fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :domain, wrapper: :with_label, label: t('admin.domain_blocks.domain'), hint: t('.hint'), required: true - = f.input :domain, placeholder: t('admin.domain_blocks.domain') - = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| t(".severity.#{type}") } + .fields-row__column.fields-row__column-6.fields-group + = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| t(".severity.#{type}") }, hint: t('.severity.desc_html') - %p.hint= t('.severity.desc_html') - - = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint') + .fields-group + = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint') .actions = f.button :button, t('.create'), type: :submit diff --git a/app/views/admin/email_domain_blocks/new.html.haml b/app/views/admin/email_domain_blocks/new.html.haml index bcae867d9..f372fa512 100644 --- a/app/views/admin/email_domain_blocks/new.html.haml +++ b/app/views/admin/email_domain_blocks/new.html.haml @@ -4,7 +4,8 @@ = simple_form_for @email_domain_block, url: admin_email_domain_blocks_path do |f| = render 'shared/error_messages', object: @email_domain_block - = f.input :domain, placeholder: t('admin.email_domain_blocks.domain') + .fields-group + = f.input :domain, wrapper: :with_label, label: t('admin.email_domain_blocks.domain') .actions = f.button :button, t('.create'), type: :submit diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index f40edc35a..c5a606693 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -2,24 +2,37 @@ = t('admin.settings.title') = simple_form_for @admin_settings, url: admin_settings_path, html: { method: :patch } do |f| - .actions.actions--top - = f.button :button, t('generic.save_changes'), type: :submit .fields-group - = f.input :site_title, placeholder: t('admin.settings.site_title') - = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 } + = f.input :site_title, wrapper: :with_label, label: t('admin.settings.site_title') + + .fields-group + = f.input :theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false + + .fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :site_contact_username, wrapper: :with_label, label: t('admin.settings.contact_information.username') + .fields-row__column.fields-row__column-6.fields-group + = f.input :site_contact_email, wrapper: :with_label, label: t('admin.settings.contact_information.email') + + .fields-group = f.input :site_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description.title'), hint: t('admin.settings.site_description.desc_html'), input_html: { rows: 4 } - = f.input :site_contact_username, placeholder: t('admin.settings.contact_information.username') - = f.input :site_contact_email, placeholder: t('admin.settings.contact_information.email') - %hr/ + .fields-group + = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 } + + .fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :thumbnail, as: :file, wrapper: :with_block_label, label: t('admin.settings.thumbnail.title'), hint: t('admin.settings.thumbnail.desc_html') + .fields-row__column.fields-row__column-6.fields-group + = f.input :hero, as: :file, wrapper: :with_block_label, label: t('admin.settings.hero.title'), hint: t('admin.settings.hero.desc_html') + + %hr.spacer/ .fields-group - = f.input :theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false - = f.input :thumbnail, as: :file, wrapper: :with_block_label, label: t('admin.settings.thumbnail.title'), hint: t('admin.settings.thumbnail.desc_html') - = f.input :hero, as: :file, wrapper: :with_block_label, label: t('admin.settings.hero.title'), hint: t('admin.settings.hero.desc_html') + = f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html') - %hr/ + %hr.spacer/ .fields-group = f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html') @@ -37,34 +50,24 @@ = f.input :open_deletion, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.deletion.title'), hint: t('admin.settings.registrations.deletion.desc_html') .fields-group - = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 } - - %hr/ - - .fields-group - = f.input :min_invite_role, wrapper: :with_label, collection: %i(disabled user moderator admin), label: t('admin.settings.registrations.min_invite_role.title'), label_method: lambda { |role| role == :disabled ? t('admin.settings.registrations.min_invite_role.disabled') : t("admin.accounts.roles.#{role}") }, as: :radio_buttons, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' - - %hr/ + = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html') .fields-group - = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } - = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 } - = f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html') - %hr/ + = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.desc_html') .fields-group - = f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html') + = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html') - %hr/ + %hr.spacer/ .fields-group - = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html') + = f.input :min_invite_role, wrapper: :with_label, collection: %i(disabled user moderator admin), label: t('admin.settings.registrations.min_invite_role.title'), label_method: lambda { |role| role == :disabled ? t('admin.settings.registrations.min_invite_role.disabled') : t("admin.accounts.roles.#{role}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' .fields-group - = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.desc_html') - - .fields-group - = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html') + = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 } + = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } + = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 } + = f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html') .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/auth/confirmations/finish_signup.html.haml b/app/views/auth/confirmations/finish_signup.html.haml index 4b5161d6b..9d09b74e1 100644 --- a/app/views/auth/confirmations/finish_signup.html.haml +++ b/app/views/auth/confirmations/finish_signup.html.haml @@ -8,7 +8,8 @@ = msg %br - = f.input :email + .fields-group + = f.input :email, wrapper: :with_label, required: true, hint: false .actions = f.submit t('auth.confirm_email'), class: 'button' diff --git a/app/views/auth/confirmations/new.html.haml b/app/views/auth/confirmations/new.html.haml index 07ca4a7da..4a1bedaa4 100644 --- a/app/views/auth/confirmations/new.html.haml +++ b/app/views/auth/confirmations/new.html.haml @@ -4,7 +4,8 @@ = simple_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| = render 'shared/error_messages', object: resource - = f.input :email, autofocus: true, required: true, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') } + .fields-group + = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false .actions = f.button :button, t('auth.resend_confirmation'), type: :submit diff --git a/app/views/auth/passwords/edit.html.haml b/app/views/auth/passwords/edit.html.haml index 53d1769d6..383d44f00 100644 --- a/app/views/auth/passwords/edit.html.haml +++ b/app/views/auth/passwords/edit.html.haml @@ -7,8 +7,10 @@ - if !use_seamless_external_login? || resource.encrypted_password.present? = f.input :reset_password_token, as: :hidden - = f.input :password, autofocus: true, placeholder: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' } - = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' } + .fields-group + = f.input :password, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, required: true + .fields-group + = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, required: true .actions = f.button :button, t('auth.set_new_password'), type: :submit diff --git a/app/views/auth/passwords/new.html.haml b/app/views/auth/passwords/new.html.haml index f4d22031a..bae5b24ba 100644 --- a/app/views/auth/passwords/new.html.haml +++ b/app/views/auth/passwords/new.html.haml @@ -4,7 +4,8 @@ = simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| = render 'shared/error_messages', object: resource - = f.input :email, autofocus: true, required: true, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') } + .fields-group + = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false .actions = f.button :button, t('auth.reset_password'), type: :submit diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml index 05fc7df31..7fc08a23b 100644 --- a/app/views/auth/registrations/edit.html.haml +++ b/app/views/auth/registrations/edit.html.haml @@ -1,24 +1,32 @@ - content_for :page_title do = t('auth.security') -%h4= t('auth.change_password') = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit' }) do |f| = render 'shared/error_messages', object: resource - if !use_seamless_external_login? || resource.encrypted_password.present? - = f.input :email, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') } - = f.input :password, placeholder: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' } - = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' } - = f.input :current_password, placeholder: t('simple_form.labels.defaults.current_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' } + .fields-group + = f.input :email, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, required: true, hint: false + + .fields-group + = f.input :password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, hint: false + + .fields-group + = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' } + + .fields-group + = f.input :current_password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }, required: true .actions = f.button :button, t('generic.save_changes'), type: :submit - else %p.hint= t('users.seamless_external_login') +%hr.spacer/ + = render 'sessions' - if open_deletion? - + %hr.spacer/ %h4= t('auth.delete_account') %p.muted-hint= t('auth.delete_account_html', path: settings_delete_path) diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index 200ed42de..72ce8e531 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -13,18 +13,22 @@ = render 'application/card', account: @invite.user.account = f.simple_fields_for :account do |ff| - .input-with-append - = ff.input :username, autofocus: true, placeholder: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' } - .append - = "@#{site_hostname}" - - = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' } - = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' } - = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' } + .fields-group + = ff.input :username, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' }, append: "@#{site_hostname}", hint: t('simple_form.hints.defaults.username', domain: site_hostname) + + .fields-group + = f.input :email, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' } + + .fields-group + = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' } + .fields-group + = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' } + = f.input :invite_code, as: :hidden + %p.hint= t('auth.agreement_html', rules_path: about_more_path, terms_path: terms_path) + .actions = f.button :button, t('auth.register'), type: :submit - %p.hint.subtle-hint=t('auth.agreement_html', rules_path: about_more_path, terms_path: terms_path) .form-footer= render 'auth/shared/links' diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml index 0c9f9d5fe..ceb169408 100644 --- a/app/views/auth/sessions/new.html.haml +++ b/app/views/auth/sessions/new.html.haml @@ -5,11 +5,13 @@ = render partial: 'shared/og' = simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| - - if use_seamless_external_login? - = f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.username_or_email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') } - - else - = f.input :email, autofocus: true, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') } - = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' } + .fields-group + - if use_seamless_external_login? + = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.username_or_email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }, hint: false + - else + = f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false + .fields-group + = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false .actions = f.button :button, t('auth.login'), type: :submit diff --git a/app/views/auth/sessions/two_factor.html.haml b/app/views/auth/sessions/two_factor.html.haml index 1af3193ae..4e6bbd7a9 100644 --- a/app/views/auth/sessions/two_factor.html.haml +++ b/app/views/auth/sessions/two_factor.html.haml @@ -4,7 +4,8 @@ = simple_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| %p.hint{ style: 'margin-bottom: 25px' }= t('simple_form.hints.sessions.otp') - = f.input :otp_attempt, type: :number, placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, required: true, autofocus: true + .fields-group + = f.input :otp_attempt, type: :number, wrapper: :with_label, label: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, autofocus: true .actions = f.button :button, t('auth.login'), type: :submit diff --git a/app/views/filters/_fields.html.haml b/app/views/filters/_fields.html.haml index a5a3f0337..fb94a07fc 100644 --- a/app/views/filters/_fields.html.haml +++ b/app/views/filters/_fields.html.haml @@ -1,14 +1,16 @@ -.fields-group - = f.input :phrase, as: :string, wrapper: :with_block_label +.fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :phrase, as: :string, wrapper: :with_label, hint: false + .fields-row__column.fields-row__column-6.fields-group + = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt') .fields-group = f.input :context, wrapper: :with_block_label, collection: CustomFilter::VALID_CONTEXTS, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', label_method: lambda { |context| I18n.t("filters.contexts.#{context}") }, include_blank: false +%hr.spacer/ + .fields-group = f.input :irreversible, wrapper: :with_label .fields-group = f.input :whole_word, wrapper: :with_label - -.fields-group - = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt') diff --git a/app/views/invites/_form.html.haml b/app/views/invites/_form.html.haml index 42a107bb2..3a2a5ef0e 100644 --- a/app/views/invites/_form.html.haml +++ b/app/views/invites/_form.html.haml @@ -1,9 +1,11 @@ = simple_form_for(@invite, url: controller.is_a?(Admin::InvitesController) ? admin_invites_path : invites_path) do |f| = render 'shared/error_messages', object: @invite - .fields-group - = f.input :max_uses, wrapper: :with_label, collection: [1, 5, 10, 25, 50, 100], label_method: lambda { |num| I18n.t('invites.max_uses', count: num) }, prompt: I18n.t('invites.max_uses_prompt') - = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt') + .fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :max_uses, wrapper: :with_label, collection: [1, 5, 10, 25, 50, 100], label_method: lambda { |num| I18n.t('invites.max_uses', count: num) }, prompt: I18n.t('invites.max_uses_prompt') + .fields-row__column.fields-row__column-6.fields-group + = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt') .fields-group = f.input :autofollow, wrapper: :with_label diff --git a/app/views/invites/index.html.haml b/app/views/invites/index.html.haml index f4c5047fa..fb827f6e6 100644 --- a/app/views/invites/index.html.haml +++ b/app/views/invites/index.html.haml @@ -6,7 +6,7 @@ = render 'form' - %hr/ + %hr.spacer/ %table.table %thead diff --git a/app/views/settings/applications/_fields.html.haml b/app/views/settings/applications/_fields.html.haml index db90df349..6a2863b20 100644 --- a/app/views/settings/applications/_fields.html.haml +++ b/app/views/settings/applications/_fields.html.haml @@ -1,6 +1,8 @@ .fields-group - = f.input :name, placeholder: t('activerecord.attributes.doorkeeper/application.name') - = f.input :website, placeholder: t('activerecord.attributes.doorkeeper/application.website') + = f.input :name, wrapper: :with_label, label: t('activerecord.attributes.doorkeeper/application.name') + +.fields-group + = f.input :website, wrapper: :with_label, label: t('activerecord.attributes.doorkeeper/application.website') .fields-group = f.input :redirect_uri, wrapper: :with_block_label, label: t('activerecord.attributes.doorkeeper/application.redirect_uri'), hint: t('doorkeeper.applications.help.redirect_uri') diff --git a/app/views/settings/imports/show.html.haml b/app/views/settings/imports/show.html.haml index 2b43cb134..4512fc714 100644 --- a/app/views/settings/imports/show.html.haml +++ b/app/views/settings/imports/show.html.haml @@ -2,10 +2,8 @@ = t('settings.import') = simple_form_for @import, url: settings_import_path do |f| - %p.hint= t('imports.preface') - .field-group - = f.input :type, collection: Import.types.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| I18n.t("imports.types.#{type}") }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' + = f.input :type, collection: Import.types.keys, wrapper: :with_block_label, include_blank: false, label_method: lambda { |type| I18n.t("imports.types.#{type}") }, hint: t('imports.preface') .field-group = f.input :data, wrapper: :with_block_label, hint: t('simple_form.hints.imports.data') diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index 43430069f..c79ecf6d5 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -1,29 +1,32 @@ - content_for :page_title do = t('settings.preferences') +%ul.quick-nav + %li= link_to t('preferences.languages'), '#settings_languages' + %li= link_to t('preferences.publishing'), '#settings_publishing' + %li= link_to t('preferences.other'), '#settings_other' + %li= link_to t('preferences.web'), '#settings_web' + = simple_form_for current_user, url: settings_preferences_path, html: { method: :put } do |f| = render 'shared/error_messages', object: current_user - .actions.actions--top - = f.button :button, t('generic.save_changes'), type: :submit - - %h4= t 'preferences.languages' + .fields-row#settings_languages + .fields-group.fields-row__column.fields-row__column-6 + = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale + .fields-group.fields-row__column.fields-row__column-6 + = f.input :setting_default_language, collection: [nil] + filterable_languages.sort, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.language_detection') : human_locale(locale) }, required: false, include_blank: false .fields-group - = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale - - = f.input :setting_default_language, collection: [nil] + filterable_languages.sort, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.language_detection') : human_locale(locale) }, required: false, include_blank: false - = f.input :chosen_languages, collection: filterable_languages.sort, wrapper: :with_block_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' - %h4= t 'preferences.publishing' + %hr#settings_publishing/ .fields-group - = f.input :setting_default_privacy, collection: Status.visibilities.keys - ['direct'], wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' + = f.input :setting_default_privacy, collection: Status.visibilities.keys - ['direct'], wrapper: :with_floating_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label - %h4= t 'preferences.other' + %hr#settings_other/ .fields-group = f.input :setting_noindex, as: :boolean, wrapper: :with_label @@ -31,12 +34,13 @@ .fields-group = f.input :setting_hide_network, as: :boolean, wrapper: :with_label - %h4= t 'preferences.web' + %hr#settings_web/ - .fields-group - - if Themes.instance.names.size > 1 - = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false + - if Themes.instance.names.size > 1 + .fields-group + = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_block_label, include_blank: false + .fields-group = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label = f.input :setting_delete_modal, as: :boolean, wrapper: :with_label diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml index 9518bcb61..42e3840a1 100644 --- a/app/views/settings/profiles/show.html.haml +++ b/app/views/settings/profiles/show.html.haml @@ -4,16 +4,21 @@ = simple_form_for @account, url: settings_profile_path, html: { method: :put } do |f| = render 'shared/error_messages', object: @account - .fields-group - = f.input :display_name, placeholder: t('simple_form.labels.defaults.display_name'), hint: t('simple_form.hints.defaults.display_name', count: 30 - @account.display_name.size).html_safe - = f.input :note, placeholder: t('simple_form.labels.defaults.note'), hint: t('simple_form.hints.defaults.note', count: 160 - @account.note.size).html_safe + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + = f.input :display_name, wrapper: :with_label, hint: t('simple_form.hints.defaults.display_name', count: 30 - @account.display_name.size).html_safe + = f.input :note, wrapper: :with_label, hint: t('simple_form.hints.defaults.note', count: 160 - @account.note.size).html_safe - = render 'application/card', account: @account + .fields-row + .fields-row__column.fields-row__column-6 + = render 'application/card', account: @account - .fields-group - = f.input :avatar, wrapper: :with_label, input_html: { accept: AccountAvatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar', dimensions: '400x400', size: number_to_human_size(AccountAvatar::LIMIT)) + .fields-row__column.fields-group.fields-row__column-6 + = f.input :avatar, wrapper: :with_label, input_html: { accept: AccountAvatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar', dimensions: '400x400', size: number_to_human_size(AccountAvatar::LIMIT)) + + = f.input :header, wrapper: :with_label, input_html: { accept: AccountHeader::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.header', dimensions: '1500x500', size: number_to_human_size(AccountHeader::LIMIT)) - = f.input :header, wrapper: :with_label, input_html: { accept: AccountHeader::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.header', dimensions: '1500x500', size: number_to_human_size(AccountHeader::LIMIT)) + %hr.spacer/ .fields-group = f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked') @@ -21,15 +26,27 @@ .fields-group = f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot') - .fields-group - .input.with_block_label - %label= t('simple_form.labels.defaults.fields') - %span.hint= t('simple_form.hints.defaults.fields') + %hr.spacer/ + + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + .input.with_block_label + %label= t('simple_form.labels.defaults.fields') + %span.hint= t('simple_form.hints.defaults.fields') - = f.simple_fields_for :fields do |fields_f| - .row - = fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name') - = fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value') + = f.simple_fields_for :fields do |fields_f| + .row + = fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name') + = fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value') + + .fields-row__column.fields-group.fields-row__column-6 + %h6= t('verification.verification') + %p.hint= t('verification.explanation_html') + + .input-copy + .input-copy__wrapper + %input{ type: :text, maxlength: '999', spellcheck: 'false', readonly: 'true', value: link_to('Mastodon', ActivityPub::TagManager.instance.url_for(@account), rel: 'me').to_str } + %button{ type: :button }= t('generic.copy') .actions = f.button :button, t('generic.save_changes'), type: :submit @@ -38,3 +55,9 @@ %h6= t('auth.migrate_account') %p.muted-hint= t('auth.migrate_account_html', path: settings_migration_path) + +- if open_deletion? + %hr.spacer/ + + %h6= t('auth.delete_account') + %p.muted-hint= t('auth.delete_account_html', path: settings_delete_path) diff --git a/app/views/settings/two_factor_authentication/confirmations/new.html.haml b/app/views/settings/two_factor_authentication/confirmations/new.html.haml index fd4a3e768..e64155299 100644 --- a/app/views/settings/two_factor_authentication/confirmations/new.html.haml +++ b/app/views/settings/two_factor_authentication/confirmations/new.html.haml @@ -11,7 +11,8 @@ %p.hint= t('two_factor_authentication.manual_instructions') %samp.qr-alternative__code= current_user.otp_secret.scan(/.{4}/).join(' ') - = f.input :code, hint: t('two_factor_authentication.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' } + .fields-group + = f.input :code, wrapper: :with_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true .actions = f.button :button, t('two_factor_authentication.enable'), type: :submit diff --git a/app/views/settings/two_factor_authentications/show.html.haml b/app/views/settings/two_factor_authentications/show.html.haml index 67a64a046..259bcd1ef 100644 --- a/app/views/settings/two_factor_authentications/show.html.haml +++ b/app/views/settings/two_factor_authentications/show.html.haml @@ -10,7 +10,7 @@ %hr/ = simple_form_for @confirmation, url: settings_two_factor_authentication_path, method: :delete do |f| - = f.input :code, hint: t('two_factor_authentication.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' } + = f.input :code, wrapper: :with_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true .actions = f.button :button, t('two_factor_authentication.disable'), type: :submit diff --git a/app/workers/verify_account_links_worker.rb b/app/workers/verify_account_links_worker.rb new file mode 100644 index 000000000..901498583 --- /dev/null +++ b/app/workers/verify_account_links_worker.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class VerifyAccountLinksWorker + include Sidekiq::Worker + + sidekiq_options queue: 'pull', retry: false, unique: :until_executed + + def perform(account_id) + account = Account.find(account_id) + + account.fields.each do |field| + next unless !field.verified? && field.verifiable? + VerifyLinkService.new.call(field) + end + + account.save! if account.changed? + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index e413cdb44..386ede654 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -1,4 +1,15 @@ # Use this setup block to configure all options available in SimpleForm. + +module AppendComponent + def append(wrapper_options = nil) + @append ||= begin + options[:append].to_s.html_safe if options[:append].present? + end + end +end + +SimpleForm.include_component(AppendComponent) + SimpleForm.setup do |config| # Wrappers are used by the form builder to generate a # complete input. You can remove any component from the @@ -52,6 +63,22 @@ SimpleForm.setup do |config| config.wrappers :with_label, class: [:input, :with_label], hint_class: :field_with_hint, error_class: :field_with_errors do |b| b.use :html5 + + b.wrapper tag: :div, class: :label_input do |ba| + ba.use :label + + ba.wrapper tag: :div, class: :label_input__wrapper do |bb| + bb.use :input + bb.optional :append, wrap_with: { tag: :div, class: 'label_input__append' } + end + end + + b.use :hint, wrap_with: { tag: :span, class: :hint } + b.use :error, wrap_with: { tag: :span, class: :error } + end + + config.wrappers :with_floating_label, class: [:input, :with_floating_label], hint_class: :field_with_hint, error_class: :field_with_errors do |b| + b.use :html5 b.use :label_input, wrap_with: { tag: :div, class: :label_input } b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :error, wrap_with: { tag: :span, class: :error } @@ -111,7 +138,7 @@ SimpleForm.setup do |config| # config.item_wrapper_class = nil # How the label text should be generated altogether with the required text. - # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" } + config.label_text = lambda { |label, required, explicit_label| "#{label} #{required}" } # You can define the class to use on all labels. Default is nil. # config.label_class = nil diff --git a/config/locales/en.yml b/config/locales/en.yml index 5cbb790dd..f883b17a1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -48,6 +48,7 @@ en: other: Followers following: Following joined: Joined %{date} + link_verified_on: Ownership of this link was checked on %{date} media: Media moved_html: "%{name} has moved to %{new_profile_link}:" network_hidden: This information is not available @@ -460,7 +461,7 @@ en: warning: Be very careful with this data. Never share it with anyone! your_token: Your access token auth: - agreement_html: By signing up you agree to follow the rules of the instance and our terms of service. + agreement_html: By clicking "Sign up" below you agree to follow the rules of the instance and our terms of service. change_password: Password confirm_email: Confirm email delete_account: Delete account @@ -921,3 +922,6 @@ en: otp_lost_help_html: If you lost access to both, you may get in touch with %{email} seamless_external_login: You are logged in via an external service, so password and e-mail settings are not available. signed_in_as: 'Signed in as:' + verification: + explanation_html: 'You can verify yourself as the owner of the links in your profile metadata. For that, the linked website must contain a link back to your Mastodon profile. The link back must have a rel="me" attribute. The text content of the link does not matter. Here is an example:' + verification: Verification diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 2e4cb1eff..c2e572997 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -11,6 +11,7 @@ en: display_name: one: 1 character left other: %{count} characters left + email: You will be sent a confirmation e-mail fields: You can have up to 4 items displayed as a table on your profile header: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px inbox_url: Copy the URL from the frontpage of the relay you want to use @@ -20,12 +21,14 @@ en: note: one: 1 character left other: %{count} characters left + password: Use at least 8 characters phrase: Will be matched regardless of casing in text or content warning of a toot scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones. setting_default_language: The language of your toots can be detected automatically, but it's not always accurate setting_hide_network: Who you follow and who follows you will not be shown on your profile setting_noindex: Affects your public profile and status pages setting_theme: Affects how Mastodon looks when you're logged in from any device. + username: Your username will be unique on %{domain} whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word imports: data: CSV file exported from another Mastodon instance diff --git a/spec/services/verify_link_service_spec.rb b/spec/services/verify_link_service_spec.rb new file mode 100644 index 000000000..8ce867e9d --- /dev/null +++ b/spec/services/verify_link_service_spec.rb @@ -0,0 +1,51 @@ +require 'rails_helper' + +RSpec.describe VerifyLinkService, type: :service do + subject { described_class.new } + + let(:account) { Fabricate(:account, username: 'alice') } + let(:field) { Account::Field.new(account, 'name' => 'Website', 'value' => 'http://example.com') } + + before do + stub_request(:get, 'http://example.com').to_return(status: 200, body: html) + subject.call(field) + end + + context 'when a link contains an back' do + let(:html) do + <<-HTML + + + Follow me on Mastodon + + HTML + end + + it 'marks the field as verified' do + expect(field.verified?).to be true + end + end + + context 'when a link contains a back' do + let(:html) do + <<-HTML + + + + + HTML + end + + it 'marks the field as verified' do + expect(field.verified?).to be true + end + end + + context 'when a link does not contain a link back' do + let(:html) { '' } + + it 'marks the field as verified' do + expect(field.verified?).to be false + end + end +end -- cgit