From e5ae75bf6a88ff70570df43635e75db904e40a50 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 25 Jan 2023 16:28:29 +0100 Subject: Fix styling of featured tags in light theme (#23252) * Fix styling of featured tags in light theme Fixes #23251 * Remove broken highlighting on /settings/featured_tags --- app/views/settings/featured_tags/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views/settings') diff --git a/app/views/settings/featured_tags/index.html.haml b/app/views/settings/featured_tags/index.html.haml index 078abd788..3ead9d2e2 100644 --- a/app/views/settings/featured_tags/index.html.haml +++ b/app/views/settings/featured_tags/index.html.haml @@ -17,7 +17,7 @@ %hr.spacer/ - @featured_tags.each do |featured_tag| - .directory__tag{ class: params[:tag] == featured_tag.name ? 'active' : nil } + .directory__tag %div %h4 = fa_icon 'hashtag' -- cgit From 75131e7bf7f3d96cf325e674e6b76b0096382e99 Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Tue, 14 Mar 2023 23:15:36 -0400 Subject: Setup haml-lint CI with todo config (#23524) --- .github/workflows/haml-lint-problem-matcher.json | 17 +++ .github/workflows/lint-haml.yml | 44 +++++++ .haml-lint.yml | 103 +---------------- .haml-lint_todo.yml | 128 +++++++++++++++++++++ Gemfile | 1 + Gemfile.lock | 8 ++ app/views/admin/accounts/show.html.haml | 8 +- app/views/admin/action_logs/index.html.haml | 2 +- app/views/admin/announcements/index.html.haml | 2 +- app/views/admin/disputes/appeals/index.html.haml | 2 +- app/views/admin/instances/index.html.haml | 2 +- app/views/admin/rules/index.html.haml | 2 +- app/views/admin/warning_presets/index.html.haml | 2 +- app/views/admin/webhooks/index.html.haml | 2 +- app/views/auth/sessions/two_factor.html.haml | 2 +- app/views/filters/_filter_fields.html.haml | 2 +- app/views/filters/index.html.haml | 2 +- app/views/kaminari/_next_page.html.haml | 16 +-- app/views/kaminari/_paginator.html.haml | 15 +-- app/views/kaminari/_prev_page.html.haml | 15 +-- app/views/layouts/modal.html.haml | 2 +- app/views/notification_mailer/_status.html.haml | 4 +- app/views/oauth/authorizations/show.html.haml | 2 +- app/views/settings/applications/index.html.haml | 2 +- app/views/settings/applications/show.html.haml | 4 +- .../settings/login_activities/index.html.haml | 2 +- app/views/statuses/_poll.html.haml | 2 +- 27 files changed, 248 insertions(+), 145 deletions(-) create mode 100644 .github/workflows/haml-lint-problem-matcher.json create mode 100644 .github/workflows/lint-haml.yml create mode 100644 .haml-lint_todo.yml (limited to 'app/views/settings') diff --git a/.github/workflows/haml-lint-problem-matcher.json b/.github/workflows/haml-lint-problem-matcher.json new file mode 100644 index 000000000..3523ea295 --- /dev/null +++ b/.github/workflows/haml-lint-problem-matcher.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "haml-lint", + "severity": "warning", + "pattern": [ + { + "regexp": "^(.*):(\\d+)\\s\\[W]\\s(.*):\\s(.*)$", + "file": 1, + "line": 2, + "code": 3, + "message": 4 + } + ] + } + ] +} diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml new file mode 100644 index 000000000..a2b22a659 --- /dev/null +++ b/.github/workflows/lint-haml.yml @@ -0,0 +1,44 @@ +name: Haml Linting +on: + push: + branches-ignore: + - 'dependabot/**' + paths: + - '.github/workflows/haml-lint-problem-matcher.json' + - '.github/workflows/lint-haml.yml' + - '.haml-lint*.yml' + - '.rubocop*.yml' + - '.ruby-version' + - '**/*.haml' + - 'Gemfile*' + + pull_request: + paths: + - '.github/workflows/haml-lint-problem-matcher.json' + - '.github/workflows/lint-haml.yml' + - '.haml-lint*.yml' + - '.rubocop*.yml' + - '.ruby-version' + - '**/*.haml' + - 'Gemfile*' + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v3 + + - name: Install native Ruby dependencies + run: sudo apt-get install -y libicu-dev libidn11-dev + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Run haml-lint + run: | + echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json" + bundle exec haml-lint diff --git a/.haml-lint.yml b/.haml-lint.yml index 7853d81d7..12ca46342 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -1,108 +1,9 @@ -# Whether to ignore frontmatter at the beginning of HAML documents for -# frameworks such as Jekyll/Middleman -skip_frontmatter: false +inherits_from: .haml-lint_todo.yml exclude: - 'vendor/**/*' - - 'spec/**/*' - - 'lib/templates/**/*' - - 'app/views/kaminari/**/*' + - lib/templates/haml/scaffold/_form.html.haml linters: AltText: - enabled: false - - ClassAttributeWithStaticValue: - enabled: true - - ClassesBeforeIds: - enabled: true - - ConsecutiveComments: - enabled: true - - ConsecutiveSilentScripts: - enabled: true - max_consecutive: 2 - - EmptyObjectReference: - enabled: true - - EmptyScript: - enabled: true - - FinalNewline: - enabled: true - present: true - - HtmlAttributes: - enabled: true - - ImplicitDiv: - enabled: true - - LeadingCommentSpace: - enabled: true - - LineLength: - enabled: false - max: 80 - - MultilinePipe: - enabled: true - - MultilineScript: - enabled: true - - ObjectReferenceAttributes: - enabled: true - - RuboCop: - enabled: true - # These cops are incredibly noisy when it comes to HAML templates, so we - # ignore them. - ignored_cops: - - Lint/BlockAlignment - - Lint/EndAlignment - - Lint/Void - - Metrics/BlockLength - - Metrics/LineLength - - Style/AlignParameters - - Style/BlockNesting - - Style/ElseAlignment - - Style/EndOfLine - - Style/FileName - - Style/FinalNewline - - Style/FrozenStringLiteralComment - - Style/IfUnlessModifier - - Style/IndentationWidth - - Style/Next - - Style/TrailingBlankLines - - Style/TrailingWhitespace - - Style/WhileUntilModifier - - RubyComments: - enabled: true - - SpaceBeforeScript: - enabled: true - - SpaceInsideHashAttributes: - enabled: true - style: space - - Indentation: - enabled: true - character: space # or tab - - TagName: - enabled: true - - TrailingWhitespace: - enabled: true - - UnnecessaryInterpolation: - enabled: true - - UnnecessaryStringOutput: enabled: true diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml new file mode 100644 index 000000000..203010b04 --- /dev/null +++ b/.haml-lint_todo.yml @@ -0,0 +1,128 @@ +# This configuration was generated by +# `haml-lint --auto-gen-config` +# on 2023-02-11 02:16:03 -0500 using Haml-Lint version 0.45.0. +# The point is for the user to remove these configuration records +# one by one as the lints are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of Haml-Lint, may require this file to be generated again. + +linters: + # Offense count: 76 + RuboCop: + exclude: + - 'app/views/accounts/_og.html.haml' + - 'app/views/admin/account_actions/new.html.haml' + - 'app/views/admin/account_warnings/_account_warning.html.haml' + - 'app/views/admin/accounts/index.html.haml' + - 'app/views/admin/accounts/show.html.haml' + - 'app/views/admin/action_logs/index.html.haml' + - 'app/views/admin/announcements/edit.html.haml' + - 'app/views/admin/announcements/new.html.haml' + - 'app/views/admin/change_emails/show.html.haml' + - 'app/views/admin/custom_emojis/index.html.haml' + - 'app/views/admin/dashboard/index.html.haml' + - 'app/views/admin/disputes/appeals/_appeal.html.haml' + - 'app/views/admin/domain_blocks/edit.html.haml' + - 'app/views/admin/domain_blocks/new.html.haml' + - 'app/views/admin/ip_blocks/new.html.haml' + - 'app/views/admin/reports/actions/preview.html.haml' + - 'app/views/admin/reports/index.html.haml' + - 'app/views/admin/reports/show.html.haml' + - 'app/views/admin/roles/_form.html.haml' + - 'app/views/admin/settings/about/show.html.haml' + - 'app/views/admin/settings/appearance/show.html.haml' + - 'app/views/admin/settings/registrations/show.html.haml' + - 'app/views/admin/statuses/show.html.haml' + - 'app/views/application/_card.html.haml' + - 'app/views/application/_sidebar.html.haml' + - 'app/views/auth/registrations/_sessions.html.haml' + - 'app/views/auth/registrations/new.html.haml' + - 'app/views/auth/sessions/new.html.haml' + - 'app/views/disputes/strikes/show.html.haml' + - 'app/views/filters/_filter_fields.html.haml' + - 'app/views/invites/_form.html.haml' + - 'app/views/layouts/application.html.haml' + - 'app/views/layouts/error.html.haml' + - 'app/views/layouts/mailer.html.haml' + - 'app/views/notification_mailer/_status.html.haml' + - 'app/views/notification_mailer/favourite.html.haml' + - 'app/views/notification_mailer/follow_request.html.haml' + - 'app/views/oauth/authorizations/new.html.haml' + - 'app/views/oauth/authorized_applications/index.html.haml' + - 'app/views/settings/applications/_fields.html.haml' + - 'app/views/settings/imports/show.html.haml' + - 'app/views/settings/login_activities/_login_activity.html.haml' + - 'app/views/settings/preferences/appearance/show.html.haml' + - 'app/views/settings/preferences/other/show.html.haml' + - 'app/views/settings/preferences/other/show.html.haml' + - 'app/views/statuses_cleanup/show.html.haml' + - 'app/views/statuses/_detailed_status.html.haml' + - 'app/views/statuses/_og_image.html.haml' + - 'app/views/statuses/_poll.html.haml' + - 'app/views/statuses/_status.html.haml' + - 'app/views/statuses/show.html.haml' + - 'app/views/user_mailer/suspicious_sign_in.html.haml' + - 'app/views/user_mailer/warning.html.haml' + - 'app/views/user_mailer/webauthn_credential_added.html.haml' + - 'app/views/user_mailer/webauthn_credential_deleted.html.haml' + + # Offense count: 918 + LineLength: + enabled: false + + # Offense count: 22 + UnnecessaryStringOutput: + exclude: + - 'app/views/accounts/show.html.haml' + - 'app/views/admin/custom_emojis/_custom_emoji.html.haml' + - 'app/views/admin/relays/_relay.html.haml' + - 'app/views/admin/rules/_rule.html.haml' + - 'app/views/admin/statuses/index.html.haml' + - 'app/views/auth/registrations/_sessions.html.haml' + - 'app/views/disputes/strikes/show.html.haml' + - 'app/views/notification_mailer/_status.html.haml' + - 'app/views/settings/two_factor_authentication_methods/index.html.haml' + - 'app/views/statuses/_detailed_status.html.haml' + - 'app/views/statuses/_poll.html.haml' + - 'app/views/statuses/_simple_status.html.haml' + - 'app/views/user_mailer/suspicious_sign_in.html.haml' + - 'app/views/user_mailer/webauthn_credential_added.html.haml' + - 'app/views/user_mailer/webauthn_credential_deleted.html.haml' + - 'app/views/user_mailer/welcome.html.haml' + + # Offense count: 3 + ViewLength: + exclude: + - 'app/views/admin/accounts/show.html.haml' + - 'app/views/admin/reports/show.html.haml' + - 'app/views/disputes/strikes/show.html.haml' + + # Offense count: 43 + InstanceVariables: + exclude: + - 'app/views/admin/reports/_actions.html.haml' + - 'app/views/admin/roles/_form.html.haml' + - 'app/views/admin/webhooks/_form.html.haml' + - 'app/views/application/_sidebar.html.haml' + - 'app/views/auth/registrations/_sessions.html.haml' + - 'app/views/auth/registrations/_status.html.haml' + - 'app/views/auth/sessions/two_factor/_otp_authentication_form.html.haml' + - 'app/views/authorize_interactions/_post_follow_actions.html.haml' + - 'app/views/invites/_form.html.haml' + - 'app/views/relationships/_account.html.haml' + - 'app/views/shared/_og.html.haml' + - 'app/views/statuses/_status.html.haml' + + # Offense count: 6 + ConsecutiveSilentScripts: + exclude: + - 'app/views/admin/settings/shared/_links.html.haml' + - 'app/views/settings/login_activities/_login_activity.html.haml' + - 'app/views/statuses/_poll.html.haml' + + # Offense count: 3 + IdNames: + exclude: + - 'app/views/authorize_interactions/error.html.haml' + - 'app/views/oauth/authorizations/error.html.haml' + - 'app/views/shared/_error_messages.html.haml' diff --git a/Gemfile b/Gemfile index 7a836b6ba..c7889118d 100644 --- a/Gemfile +++ b/Gemfile @@ -139,6 +139,7 @@ group :development do gem 'memory_profiler' gem 'brakeman', '~> 5.4', require: false gem 'bundler-audit', '~> 0.9', require: false + gem 'haml_lint', require: false gem 'capistrano', '~> 3.17' gem 'capistrano-rails', '~> 1.6' diff --git a/Gemfile.lock b/Gemfile.lock index 5904ed804..0041c24c7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -308,6 +308,12 @@ GEM activesupport (>= 5.1) haml (>= 4.0.6) railties (>= 5.1) + haml_lint (0.45.0) + haml (>= 4.0, < 6.2) + parallel (~> 1.10) + rainbow + rubocop (>= 0.50.0) + sysexits (~> 1.1) hashdiff (1.0.1) hashie (5.0.0) highline (2.0.3) @@ -687,6 +693,7 @@ GEM activesupport (>= 3) attr_required (>= 0.0.5) httpclient (>= 2.4) + sysexits (1.2.0) temple (0.10.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -804,6 +811,7 @@ DEPENDENCIES fog-openstack (~> 0.3) fuubar (~> 2.5) haml-rails (~> 2.0) + haml_lint hiredis (~> 0.6) htmlentities (~> 4.3) http (~> 5.1) diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index c8a9d33a7..f5ae88379 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -206,7 +206,7 @@ - if @deletion_request.present? = link_to t('admin.accounts.delete'), admin_account_path(@account.id), method: :delete, class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure') } if can?(:destroy, @account) - else - %div.action-buttons + .action-buttons %div - if @account.local? && @account.user_approved? = link_to t('admin.accounts.warn'), new_admin_account_action_path(@account.id, type: 'none'), class: 'button' if can?(:warn, @account) @@ -276,9 +276,9 @@ %hr.spacer/ - if @account.user&.invite_request&.text&.present? - %div.speech-bubble - %div.speech-bubble__bubble + .speech-bubble + .speech-bubble__bubble = @account.user&.invite_request&.text - %div.speech-bubble__owner + .speech-bubble__owner = admin_account_link_to @account = t('admin.accounts.invite_request_text') diff --git a/app/views/admin/action_logs/index.html.haml b/app/views/admin/action_logs/index.html.haml index d8b7132f5..e2992fb80 100644 --- a/app/views/admin/action_logs/index.html.haml +++ b/app/views/admin/action_logs/index.html.haml @@ -19,7 +19,7 @@ = select_tag :action_type, options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key]}, params[:action_type]), prompt: I18n.t('admin.accounts.moderation.all') - if @action_logs.empty? - %div.muted-hint.center-text + .muted-hint.center-text = t 'admin.action_logs.empty' - else .report-notes diff --git a/app/views/admin/announcements/index.html.haml b/app/views/admin/announcements/index.html.haml index 40f02b914..ce520f59d 100644 --- a/app/views/admin/announcements/index.html.haml +++ b/app/views/admin/announcements/index.html.haml @@ -12,7 +12,7 @@ %li= filter_link_to safe_join([t('admin.announcements.live'), "(#{number_with_delimiter(Announcement.published.count)})"], ' '), published: '1', unpublished: nil - if @announcements.empty? - %div.muted-hint.center-text + .muted-hint.center-text = t 'admin.announcements.empty' - else .announcements-list diff --git a/app/views/admin/disputes/appeals/index.html.haml b/app/views/admin/disputes/appeals/index.html.haml index dd6a6f403..602414550 100644 --- a/app/views/admin/disputes/appeals/index.html.haml +++ b/app/views/admin/disputes/appeals/index.html.haml @@ -13,7 +13,7 @@ %li= filter_link_to t('admin.trends.rejected'), status: 'rejected' - if @appeals.empty? - %div.muted-hint.center-text + .muted-hint.center-text = t 'admin.disputes.appeals.empty' - else .announcements-list diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml index 8f7e3e67d..0bae70e31 100644 --- a/app/views/admin/instances/index.html.haml +++ b/app/views/admin/instances/index.html.haml @@ -47,7 +47,7 @@ %hr.spacer/ - if @instances.empty? - %div.muted-hint.center-text + .muted-hint.center-text = t 'admin.instances.empty' - else = render partial: 'instance', collection: @instances diff --git a/app/views/admin/rules/index.html.haml b/app/views/admin/rules/index.html.haml index 4fb993ad0..aa6a4c1b6 100644 --- a/app/views/admin/rules/index.html.haml +++ b/app/views/admin/rules/index.html.haml @@ -18,7 +18,7 @@ %hr.spacer/ - if @rules.empty? - %div.muted-hint.center-text + .muted-hint.center-text = t 'admin.rules.empty' - else .announcements-list diff --git a/app/views/admin/warning_presets/index.html.haml b/app/views/admin/warning_presets/index.html.haml index dbc23fa30..b26a13d96 100644 --- a/app/views/admin/warning_presets/index.html.haml +++ b/app/views/admin/warning_presets/index.html.haml @@ -17,7 +17,7 @@ %hr.spacer/ - if @warning_presets.empty? - %div.muted-hint.center-text + .muted-hint.center-text = t 'admin.warning_presets.empty' - else .announcements-list diff --git a/app/views/admin/webhooks/index.html.haml b/app/views/admin/webhooks/index.html.haml index e4499e078..603d0edd2 100644 --- a/app/views/admin/webhooks/index.html.haml +++ b/app/views/admin/webhooks/index.html.haml @@ -9,7 +9,7 @@ %hr.spacer/ - if @webhooks.empty? - %div.muted-hint.center-text + .muted-hint.center-text = t 'admin.webhooks.empty' - else .applications-list diff --git a/app/views/auth/sessions/two_factor.html.haml b/app/views/auth/sessions/two_factor.html.haml index b897a0422..20232d8dc 100644 --- a/app/views/auth/sessions/two_factor.html.haml +++ b/app/views/auth/sessions/two_factor.html.haml @@ -1,7 +1,7 @@ - content_for :page_title do = t('auth.login') -=javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous' += javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous' - if @webauthn_enabled = render partial: 'auth/sessions/two_factor/webauthn_form', locals: { hidden: @scheme_type != 'webauthn' } diff --git a/app/views/filters/_filter_fields.html.haml b/app/views/filters/_filter_fields.html.haml index c58978f5a..a554b55ff 100644 --- a/app/views/filters/_filter_fields.html.haml +++ b/app/views/filters/_filter_fields.html.haml @@ -35,6 +35,6 @@ = render 'keyword_fields', f: keyword %tfoot %tr - %td{ colspan: 3} + %td{ colspan: 3 } = link_to_add_association f, :keywords, class: 'table-action-link', partial: 'keyword_fields', 'data-association-insertion-node': '.keywords-table tbody', 'data-association-insertion-method': 'append' do = safe_join([fa_icon('plus'), t('filters.edit.add_keyword')]) diff --git a/app/views/filters/index.html.haml b/app/views/filters/index.html.haml index 0227526a4..9c84f796f 100644 --- a/app/views/filters/index.html.haml +++ b/app/views/filters/index.html.haml @@ -5,7 +5,7 @@ = link_to t('filters.new.title'), new_filter_path, class: 'button' - if @filters.empty? - %div.muted-hint.center-text= t 'filters.index.empty' + .muted-hint.center-text= t 'filters.index.empty' - else .applications-list = render partial: 'filter', collection: @filters diff --git a/app/views/kaminari/_next_page.html.haml b/app/views/kaminari/_next_page.html.haml index 30a3643d6..c44aea1f1 100644 --- a/app/views/kaminari/_next_page.html.haml +++ b/app/views/kaminari/_next_page.html.haml @@ -1,9 +1,11 @@ --# Link to the "Next" page --# available local variables --# url: url to the next page --# current_page: a page object for the currently displayed page --# total_pages: total number of pages --# per_page: number of items to fetch per page --# remote: data-remote +-# + Link to the "Next" page + available local variables + url: url to the next page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote + %span.next = link_to_unless current_page.last?, safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), url, rel: 'next', remote: remote diff --git a/app/views/kaminari/_paginator.html.haml b/app/views/kaminari/_paginator.html.haml index b1da236d5..4778f6279 100644 --- a/app/views/kaminari/_paginator.html.haml +++ b/app/views/kaminari/_paginator.html.haml @@ -1,10 +1,11 @@ --# The container tag --# available local variables --# current_page: a page object for the currently displayed page --# total_pages: total number of pages --# per_page: number of items to fetch per page --# remote: data-remote --# paginator: the paginator that renders the pagination tags inside +-# + The container tag + available local variables + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote + paginator: the paginator that renders the pagination tags inside = paginator.render do %nav.pagination = prev_page_tag unless current_page.first? diff --git a/app/views/kaminari/_prev_page.html.haml b/app/views/kaminari/_prev_page.html.haml index 1089e3566..284d6223b 100644 --- a/app/views/kaminari/_prev_page.html.haml +++ b/app/views/kaminari/_prev_page.html.haml @@ -1,9 +1,10 @@ --# Link to the "Previous" page --# available local variables --# url: url to the previous page --# current_page: a page object for the currently displayed page --# total_pages: total number of pages --# per_page: number of items to fetch per page --# remote: data-remote +-# + Link to the "Previous" page + available local variables + url: url to the previous page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote %span.prev = link_to_unless current_page.first?, safe_join([fa_icon('chevron-left'), t('pagination.prev')], ' '), url, rel: 'prev', remote: remote diff --git a/app/views/layouts/modal.html.haml b/app/views/layouts/modal.html.haml index bbdae602f..bd2dcc132 100644 --- a/app/views/layouts/modal.html.haml +++ b/app/views/layouts/modal.html.haml @@ -15,6 +15,6 @@ .modal-layout__mastodon %div - %img{alt: '', draggable: 'false', src: mascot_url } + %img{ alt: '', draggable: 'false', src: mascot_url } = render template: 'layouts/application' diff --git a/app/views/notification_mailer/_status.html.haml b/app/views/notification_mailer/_status.html.haml index e7cd5ba3e..03f49c73d 100644 --- a/app/views/notification_mailer/_status.html.haml +++ b/app/views/notification_mailer/_status.html.haml @@ -26,11 +26,11 @@ = "@#{status.account.pretty_acct}" - if status.spoiler_text? - %div.auto-dir + .auto-dir %p = status.spoiler_text - %div.auto-dir + .auto-dir = status_content_format(status) - if status.ordered_media_attachments.size > 0 diff --git a/app/views/oauth/authorizations/show.html.haml b/app/views/oauth/authorizations/show.html.haml index c3c9960d8..a5122a87f 100644 --- a/app/views/oauth/authorizations/show.html.haml +++ b/app/views/oauth/authorizations/show.html.haml @@ -3,5 +3,5 @@ %p= t('doorkeeper.authorizations.show.title') .input-copy .input-copy__wrapper - %input{ type: 'text', class: 'oauth-code', spellcheck: 'false', readonly: true, value: params[:code] } + %input.oauth-code{ type: 'text', spellcheck: 'false', readonly: true, value: params[:code] } %button{ type: :button }= t('generic.copy') diff --git a/app/views/settings/applications/index.html.haml b/app/views/settings/applications/index.html.haml index a1f904a3a..5c31d56bc 100644 --- a/app/views/settings/applications/index.html.haml +++ b/app/views/settings/applications/index.html.haml @@ -5,7 +5,7 @@ = link_to t('doorkeeper.applications.index.new'), new_settings_application_path, class: 'button' - if @applications.empty? - %div.muted-hint.center-text=t 'doorkeeper.applications.index.empty' + .muted-hint.center-text= t 'doorkeeper.applications.index.empty' - else .table-wrapper %table.table diff --git a/app/views/settings/applications/show.html.haml b/app/views/settings/applications/show.html.haml index 390682d6f..466a8ba34 100644 --- a/app/views/settings/applications/show.html.haml +++ b/app/views/settings/applications/show.html.haml @@ -6,7 +6,7 @@ .table-wrapper %table.table %tbody - %tr + %tr %th= t('doorkeeper.applications.show.application_id') %td %code= @application.uid @@ -15,7 +15,7 @@ %td %code= @application.secret %tr - %th{ rowspan: 2}= t('applications.your_token') + %th{ rowspan: 2 }= t('applications.your_token') %td %code= current_user.token_for_app(@application).token %tr diff --git a/app/views/settings/login_activities/index.html.haml b/app/views/settings/login_activities/index.html.haml index ce524fbef..6fb1bc34c 100644 --- a/app/views/settings/login_activities/index.html.haml +++ b/app/views/settings/login_activities/index.html.haml @@ -6,7 +6,7 @@ %hr.spacer/ - if @login_activities.empty? - %div.muted-hint.center-text + .muted-hint.center-text = t 'login_activities.empty' - else .announcements-list diff --git a/app/views/statuses/_poll.html.haml b/app/views/statuses/_poll.html.haml index d0f264095..248c6058c 100644 --- a/app/views/statuses/_poll.html.haml +++ b/app/views/statuses/_poll.html.haml @@ -21,7 +21,7 @@ %span.poll__chart - else %label.poll__option>< - %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}>< + %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil }>< %span.poll__option__text = prerender_custom_emojis(h(option.title), status.emojis) .poll__footer -- cgit From 65669d3c5719e72000bf26849d0ee1eaec1f9d1b Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Wed, 15 Mar 2023 15:07:43 -0400 Subject: Fix more haml-lint Rubocop (#24107) --- .haml-lint_todo.yml | 32 ++++------------------ app/views/admin/account_actions/new.html.haml | 2 +- app/views/admin/accounts/show.html.haml | 2 +- app/views/admin/action_logs/index.html.haml | 2 +- app/views/admin/change_emails/show.html.haml | 2 +- app/views/admin/custom_emojis/index.html.haml | 4 +-- app/views/admin/dashboard/index.html.haml | 2 +- app/views/application/_card.html.haml | 2 +- app/views/auth/registrations/_sessions.html.haml | 2 +- app/views/auth/sessions/new.html.haml | 2 +- app/views/layouts/mailer.html.haml | 2 +- app/views/notification_mailer/_status.html.haml | 2 +- app/views/notification_mailer/favourite.html.haml | 2 +- .../notification_mailer/follow_request.html.haml | 2 +- .../login_activities/_login_activity.html.haml | 2 +- .../settings/preferences/appearance/show.html.haml | 4 +-- app/views/statuses/_detailed_status.html.haml | 6 ++-- app/views/statuses/_og_image.html.haml | 2 +- app/views/statuses/_status.html.haml | 2 +- app/views/user_mailer/suspicious_sign_in.html.haml | 2 +- .../webauthn_credential_added.html.haml | 2 +- .../webauthn_credential_deleted.html.haml | 2 +- 22 files changed, 30 insertions(+), 52 deletions(-) (limited to 'app/views/settings') diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index 203010b04..c60168390 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -1,26 +1,21 @@ # This configuration was generated by # `haml-lint --auto-gen-config` -# on 2023-02-11 02:16:03 -0500 using Haml-Lint version 0.45.0. +# on 2023-03-15 00:55:01 -0400 using Haml-Lint version 0.45.0. # The point is for the user to remove these configuration records # one by one as the lints are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of Haml-Lint, may require this file to be generated again. linters: - # Offense count: 76 + # Offense count: 63 RuboCop: exclude: - 'app/views/accounts/_og.html.haml' - - 'app/views/admin/account_actions/new.html.haml' - 'app/views/admin/account_warnings/_account_warning.html.haml' - 'app/views/admin/accounts/index.html.haml' - 'app/views/admin/accounts/show.html.haml' - - 'app/views/admin/action_logs/index.html.haml' - 'app/views/admin/announcements/edit.html.haml' - 'app/views/admin/announcements/new.html.haml' - - 'app/views/admin/change_emails/show.html.haml' - - 'app/views/admin/custom_emojis/index.html.haml' - - 'app/views/admin/dashboard/index.html.haml' - 'app/views/admin/disputes/appeals/_appeal.html.haml' - 'app/views/admin/domain_blocks/edit.html.haml' - 'app/views/admin/domain_blocks/new.html.haml' @@ -33,40 +28,24 @@ linters: - 'app/views/admin/settings/appearance/show.html.haml' - 'app/views/admin/settings/registrations/show.html.haml' - 'app/views/admin/statuses/show.html.haml' - - 'app/views/application/_card.html.haml' - - 'app/views/application/_sidebar.html.haml' - - 'app/views/auth/registrations/_sessions.html.haml' - 'app/views/auth/registrations/new.html.haml' - - 'app/views/auth/sessions/new.html.haml' - 'app/views/disputes/strikes/show.html.haml' - 'app/views/filters/_filter_fields.html.haml' - 'app/views/invites/_form.html.haml' - 'app/views/layouts/application.html.haml' - 'app/views/layouts/error.html.haml' - - 'app/views/layouts/mailer.html.haml' - 'app/views/notification_mailer/_status.html.haml' - - 'app/views/notification_mailer/favourite.html.haml' - - 'app/views/notification_mailer/follow_request.html.haml' - - 'app/views/oauth/authorizations/new.html.haml' - - 'app/views/oauth/authorized_applications/index.html.haml' - 'app/views/settings/applications/_fields.html.haml' - 'app/views/settings/imports/show.html.haml' - - 'app/views/settings/login_activities/_login_activity.html.haml' - 'app/views/settings/preferences/appearance/show.html.haml' - 'app/views/settings/preferences/other/show.html.haml' - - 'app/views/settings/preferences/other/show.html.haml' - - 'app/views/statuses_cleanup/show.html.haml' - 'app/views/statuses/_detailed_status.html.haml' - - 'app/views/statuses/_og_image.html.haml' - 'app/views/statuses/_poll.html.haml' - - 'app/views/statuses/_status.html.haml' - 'app/views/statuses/show.html.haml' - - 'app/views/user_mailer/suspicious_sign_in.html.haml' + - 'app/views/statuses_cleanup/show.html.haml' - 'app/views/user_mailer/warning.html.haml' - - 'app/views/user_mailer/webauthn_credential_added.html.haml' - - 'app/views/user_mailer/webauthn_credential_deleted.html.haml' - # Offense count: 918 + # Offense count: 913 LineLength: enabled: false @@ -97,13 +76,12 @@ linters: - 'app/views/admin/reports/show.html.haml' - 'app/views/disputes/strikes/show.html.haml' - # Offense count: 43 + # Offense count: 41 InstanceVariables: exclude: - 'app/views/admin/reports/_actions.html.haml' - 'app/views/admin/roles/_form.html.haml' - 'app/views/admin/webhooks/_form.html.haml' - - 'app/views/application/_sidebar.html.haml' - 'app/views/auth/registrations/_sessions.html.haml' - 'app/views/auth/registrations/_status.html.haml' - 'app/views/auth/sessions/two_factor/_otp_authentication_form.html.haml' diff --git a/app/views/admin/account_actions/new.html.haml b/app/views/admin/account_actions/new.html.haml index c7bb618df..2a0cae15a 100644 --- a/app/views/admin/account_actions/new.html.haml +++ b/app/views/admin/account_actions/new.html.haml @@ -5,7 +5,7 @@ = f.input :report_id, as: :hidden .fields-group - = f.input :type, as: :radio_buttons, collection: Admin::AccountAction.types_for_account(@account), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { safe_join([I18n.t("simple_form.labels.admin_account_action.types.#{type}"), content_tag(:span, I18n.t("simple_form.hints.admin_account_action.types.#{type}"), class: 'hint')])}, hint: t('simple_form.hints.admin_account_action.type_html', acct: @account.pretty_acct) + = f.input :type, as: :radio_buttons, collection: Admin::AccountAction.types_for_account(@account), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { safe_join([I18n.t("simple_form.labels.admin_account_action.types.#{type}"), content_tag(:span, I18n.t("simple_form.hints.admin_account_action.types.#{type}"), class: 'hint')]) }, hint: t('simple_form.hints.admin_account_action.type_html', acct: @account.pretty_acct) - if @account.local? %hr.spacer/ diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index f5ae88379..44867d0a2 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -187,7 +187,7 @@ %th= t('admin.accounts.shared_inbox_url') %td = @account.shared_inbox_url - = fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check': 'times' + = fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check' : 'times' %td - if @domain_block.nil? = table_link_to 'ban', t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain) diff --git a/app/views/admin/action_logs/index.html.haml b/app/views/admin/action_logs/index.html.haml index e2992fb80..4c78797c1 100644 --- a/app/views/admin/action_logs/index.html.haml +++ b/app/views/admin/action_logs/index.html.haml @@ -16,7 +16,7 @@ .filter-subset.filter-subset--with-select %strong= t('admin.action_logs.filter_by_action') .input.select.optional - = select_tag :action_type, options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key]}, params[:action_type]), prompt: I18n.t('admin.accounts.moderation.all') + = select_tag :action_type, options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key] }, params[:action_type]), prompt: I18n.t('admin.accounts.moderation.all') - if @action_logs.empty? .muted-hint.center-text diff --git a/app/views/admin/change_emails/show.html.haml b/app/views/admin/change_emails/show.html.haml index bc00d6114..8536a18d2 100644 --- a/app/views/admin/change_emails/show.html.haml +++ b/app/views/admin/change_emails/show.html.haml @@ -9,4 +9,4 @@ = 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') + = f.button :submit, class: 'button', value: t('admin.accounts.change_email.submit') diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index bfec0407e..6ded4b433 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -15,12 +15,12 @@ %li= filter_link_to t('admin.accounts.location.all'), local: nil, remote: nil %li - if selected? local: '1', remote: nil - = filter_link_to t('admin.accounts.location.local'), {local: nil, remote: nil}, {local: '1', remote: nil} + = filter_link_to t('admin.accounts.location.local'), { local: nil, remote: nil }, { local: '1', remote: nil } - else = filter_link_to t('admin.accounts.location.local'), local: '1', remote: nil %li - if selected? remote: '1', local: nil - = filter_link_to t('admin.accounts.location.remote'), {remote: nil, local: nil}, {remote: '1', local: nil} + = filter_link_to t('admin.accounts.location.remote'), { remote: nil, local: nil }, { remote: '1', local: nil } - else = filter_link_to t('admin.accounts.location.remote'), remote: '1', local: nil diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 8354f0b9f..e05215327 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -59,7 +59,7 @@ = react_admin_component :dimension, dimension: 'servers', start_at: @time_period.first, end_at: @time_period.last, limit: 8, label: t('admin.dashboard.top_servers') .dashboard__item.dashboard__item--span-double-column - = react_admin_component :retention, start_at: @time_period.last - 6.months, end_at: @time_period.last, frequency: 'month' + = react_admin_component :retention, start_at: @time_period.last - 6.months, end_at: @time_period.last, frequency: 'month' .dashboard__item.dashboard__item--span-double-row = react_admin_component :trends, limit: 7 diff --git a/app/views/application/_card.html.haml b/app/views/application/_card.html.haml index 3d0e6b1da..719856d49 100644 --- a/app/views/application/_card.html.haml +++ b/app/views/application/_card.html.haml @@ -13,4 +13,4 @@ %strong.emojify.p-name= display_name(account, custom_emojify: true) %span = acct(account) - = fa_icon('lock', { data: ({hidden: true} unless account.locked?)}) + = fa_icon('lock', { data: ({ hidden: true } unless account.locked?) }) diff --git a/app/views/auth/registrations/_sessions.html.haml b/app/views/auth/registrations/_sessions.html.haml index c094dfd25..55d753c18 100644 --- a/app/views/auth/registrations/_sessions.html.haml +++ b/app/views/auth/registrations/_sessions.html.haml @@ -20,7 +20,7 @@ %span{ title: session.user_agent }< = fa_icon "#{session_device_icon(session)} fw", 'aria-label': session_device_icon(session) = ' ' - = t 'sessions.description', browser: t("sessions.browsers.#{session.browser}", default: "#{session.browser}"), platform: t("sessions.platforms.#{session.platform}", default: "#{session.platform}") + = t 'sessions.description', browser: t("sessions.browsers.#{session.browser}", default: session.browser.to_s), platform: t("sessions.platforms.#{session.platform}", default: session.platform.to_s) %td %samp= session.ip %td diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml index e98c1ff3d..7914e0157 100644 --- a/app/views/auth/sessions/new.html.haml +++ b/app/views/auth/sessions/new.html.haml @@ -19,7 +19,7 @@ .actions = f.button :button, t('auth.login'), type: :submit -- if devise_mapping.omniauthable? and resource_class.omniauth_providers.any? +- if devise_mapping.omniauthable? && resource_class.omniauth_providers.any? .simple_form.alternative-login %h4= omniauth_only? ? t('auth.log_in_with') : t('auth.or_log_in_with') diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml index d816f1b8c..43c855927 100644 --- a/app/views/layouts/mailer.html.haml +++ b/app/views/layouts/mailer.html.haml @@ -35,7 +35,7 @@ %tbody %tr %td.content-cell.content-end - != "  " + != '  ' %tr %td.blank-cell.footer .email-row diff --git a/app/views/notification_mailer/_status.html.haml b/app/views/notification_mailer/_status.html.haml index 03f49c73d..fd65039ae 100644 --- a/app/views/notification_mailer/_status.html.haml +++ b/app/views/notification_mailer/_status.html.haml @@ -20,7 +20,7 @@ %tbody %tr %td{ align: 'left', width: 48 } - = image_tag full_asset_url(status.account.avatar.url), alt:'' + = image_tag full_asset_url(status.account.avatar.url), alt: '' %td{ align: 'left' } %bdi= display_name(status.account) = "@#{status.account.pretty_acct}" diff --git a/app/views/notification_mailer/favourite.html.haml b/app/views/notification_mailer/favourite.html.haml index 5d9be3f57..4ec89172d 100644 --- a/app/views/notification_mailer/favourite.html.haml +++ b/app/views/notification_mailer/favourite.html.haml @@ -17,7 +17,7 @@ %tbody %tr %td - = image_tag full_pack_url('media/images/mailer/icon_grade.png'), alt:'' + = image_tag full_pack_url('media/images/mailer/icon_grade.png'), alt: '' %h1= t 'notification_mailer.favourite.title' %p.lead= t('notification_mailer.favourite.body', name: @account.pretty_acct) diff --git a/app/views/notification_mailer/follow_request.html.haml b/app/views/notification_mailer/follow_request.html.haml index 4c32c831e..3885a411d 100644 --- a/app/views/notification_mailer/follow_request.html.haml +++ b/app/views/notification_mailer/follow_request.html.haml @@ -39,5 +39,5 @@ %tbody %tr %td.button-primary - = link_to web_url("follow_requests") do + = link_to web_url('follow_requests') do %span= t 'notification_mailer.follow_request.action' diff --git a/app/views/settings/login_activities/_login_activity.html.haml b/app/views/settings/login_activities/_login_activity.html.haml index 0c2c7087d..9f4c24d87 100644 --- a/app/views/settings/login_activities/_login_activity.html.haml +++ b/app/views/settings/login_activities/_login_activity.html.haml @@ -1,6 +1,6 @@ - method_str = content_tag(:span, login_activity.omniauth? ? t(login_activity.provider, scope: 'auth.providers') : t(login_activity.authentication_method, scope: 'login_activities.authentication_methods'), class: 'target') - ip_str = content_tag(:span, login_activity.ip, class: 'target') -- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: "#{login_activity.browser}"), platform: t("sessions.platforms.#{login_activity.platform}", default: "#{login_activity.platform}")), class: 'target', title: login_activity.user_agent) +- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: login_activity.browser.to_s), platform: t("sessions.platforms.#{login_activity.platform}", default: login_activity.platform.to_)), class: 'target', title: login_activity.user_agent) .log-entry .log-entry__header diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml index 9e3964f21..bc23df647 100644 --- a/app/views/settings/preferences/appearance/show.html.haml +++ b/app/views/settings/preferences/appearance/show.html.haml @@ -13,7 +13,7 @@ - unless I18n.locale == :en .flash-message.translation-prompt - #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: "_blank", rel: "noopener")} + #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: '_blank', rel: 'noopener')} %h4= t 'appearance.advanced_web_interface' @@ -53,7 +53,7 @@ %h4= t 'appearance.sensitive_content' .fields-group - = f.input :setting_display_media, collection: ['default', 'show_all', 'hide_all'],label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label + = f.input :setting_display_media, collection: ['default', 'show_all', 'hide_all'], label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label .fields-group = f.input :setting_use_blurhash, as: :boolean, wrapper: :with_label diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index bf498e33d..70cfbd6b8 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -62,18 +62,18 @@ - else = fa_icon('reply-all') %span.detailed-status__reblogs>= friendly_number_to_human status.replies_count - = " " + = ' ' · - if status.public_visibility? || status.unlisted_visibility? %span.detailed-status__link = fa_icon('retweet') %span.detailed-status__reblogs>= friendly_number_to_human status.reblogs_count - = " " + = ' ' · %span.detailed-status__link = fa_icon('star') %span.detailed-status__favorites>= friendly_number_to_human status.favourites_count - = " " + = ' ' - if user_signed_in? · diff --git a/app/views/statuses/_og_image.html.haml b/app/views/statuses/_og_image.html.haml index 39f390fdf..1ae97adff 100644 --- a/app/views/statuses/_og_image.html.haml +++ b/app/views/statuses/_og_image.html.haml @@ -31,7 +31,7 @@ - player_card = true = opengraph 'og:image', full_asset_url(account.avatar.url(:original)) = opengraph 'og:image:width', '400' - = opengraph 'og:image:height','400' + = opengraph 'og:image:height', '400' = opengraph 'og:audio', full_asset_url(media.file.url(:original)) = opengraph 'og:audio:secure_url', full_asset_url(media.file.url(:original)) = opengraph 'og:audio:type', media.file_content_type diff --git a/app/views/statuses/_status.html.haml b/app/views/statuses/_status.html.haml index 3b7152753..afeb55faf 100644 --- a/app/views/statuses/_status.html.haml +++ b/app/views/statuses/_status.html.haml @@ -11,7 +11,7 @@ h_class = microformats_h_class(status, is_predecessor, is_successor, include_threads) style_classes = style_classes(status, is_predecessor, is_successor, include_threads) mf_classes = microformats_classes(status, is_direct_parent, is_direct_child) - entry_classes = h_class + ' ' + mf_classes + ' ' + style_classes + entry_classes = "#{h_class} #{mf_classes} #{style_classes}" - if status.reply? && include_threads - if @next_ancestor diff --git a/app/views/user_mailer/suspicious_sign_in.html.haml b/app/views/user_mailer/suspicious_sign_in.html.haml index 856f9fb7c..e4ad500c3 100644 --- a/app/views/user_mailer/suspicious_sign_in.html.haml +++ b/app/views/user_mailer/suspicious_sign_in.html.haml @@ -45,7 +45,7 @@ = @remote_ip %br/ %strong= "#{t('sessions.browser')}:" - %span{ title: @user_agent }= t 'sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: "#{@detection.id}"), platform: t("sessions.platforms.#{@detection.platform.id}", default: "#{@detection.platform.id}") + %span{ title: @user_agent }= t 'sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: @detection.id.to_s), platform: t("sessions.platforms.#{@detection.platform.id}", default: @detection.platform.id.to_s) %br/ = l(@timestamp) diff --git a/app/views/user_mailer/webauthn_credential_added.html.haml b/app/views/user_mailer/webauthn_credential_added.html.haml index 81de84b56..c91c96d6f 100644 --- a/app/views/user_mailer/webauthn_credential_added.html.haml +++ b/app/views/user_mailer/webauthn_credential_added.html.haml @@ -20,7 +20,7 @@ = image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: '' %h1= t 'devise.mailer.webauthn_credential.added.title' - %p.lead= "#{t 'devise.mailer.webauthn_credential.added.explanation' }:" + %p.lead= "#{t('devise.mailer.webauthn_credential.added.explanation')}:" %p.lead= @webauthn_credential.nickname %table.email-table{ cellspacing: 0, cellpadding: 0 } diff --git a/app/views/user_mailer/webauthn_credential_deleted.html.haml b/app/views/user_mailer/webauthn_credential_deleted.html.haml index 7b47f0c88..578a08022 100644 --- a/app/views/user_mailer/webauthn_credential_deleted.html.haml +++ b/app/views/user_mailer/webauthn_credential_deleted.html.haml @@ -20,7 +20,7 @@ = image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: '' %h1= t 'devise.mailer.webauthn_credential.deleted.title' - %p.lead= "#{t 'devise.mailer.webauthn_credential.deleted.explanation' }:" + %p.lead= "#{t('devise.mailer.webauthn_credential.deleted.explanation')}:" %p.lead= @webauthn_credential.nickname %table.email-table{ cellspacing: 0, cellpadding: 0 } -- cgit From 75e5a6e43738c278390c03c96d5d3e8575a2783c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 16 Mar 2023 22:46:52 +0100 Subject: Change user backups to use expiring URLs for download when possible (#24136) --- app/controllers/backups_controller.rb | 27 +++++++++++++++++++++++++++ app/models/backup.rb | 2 +- app/views/settings/exports/show.html.haml | 2 +- app/views/user_mailer/backup_ready.html.haml | 2 +- app/views/user_mailer/backup_ready.text.erb | 2 +- config/routes.rb | 1 + 6 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 app/controllers/backups_controller.rb (limited to 'app/views/settings') diff --git a/app/controllers/backups_controller.rb b/app/controllers/backups_controller.rb new file mode 100644 index 000000000..2f4b400b8 --- /dev/null +++ b/app/controllers/backups_controller.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class BackupsController < ApplicationController + include RoutingHelper + + skip_before_action :require_functional! + + before_action :authenticate_user! + before_action :set_backup + + def download + case Paperclip::Attachment.default_options[:storage] + when :s3 + redirect_to @backup.dump.expiring_url(10) + when :fog + redirect_to @backup.dump.expiring_url(Time.now.utc + 10) + when :filesystem + redirect_to full_asset_url(@backup.dump.url) + end + end + + private + + def set_backup + @backup = current_user.backups.find(params[:id]) + end +end diff --git a/app/models/backup.rb b/app/models/backup.rb index bec3cbfe5..dca06eb58 100644 --- a/app/models/backup.rb +++ b/app/models/backup.rb @@ -18,6 +18,6 @@ class Backup < ApplicationRecord belongs_to :user, inverse_of: :backups - has_attached_file :dump + has_attached_file :dump, s3_permissions: 'private' validates_attachment_content_type :dump, content_type: /\Aapplication/ end diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml index c49613fdc..d7b59af27 100644 --- a/app/views/settings/exports/show.html.haml +++ b/app/views/settings/exports/show.html.haml @@ -64,6 +64,6 @@ %td= l backup.created_at - if backup.processed? %td= number_to_human_size backup.dump_file_size - %td= table_link_to 'download', t('exports.archive_takeout.download'), backup.dump.url + %td= table_link_to 'download', t('exports.archive_takeout.download'), download_backup_url(backup) - else %td{ colspan: 2 }= t('exports.archive_takeout.in_progress') diff --git a/app/views/user_mailer/backup_ready.html.haml b/app/views/user_mailer/backup_ready.html.haml index 85140b08b..465ead2c8 100644 --- a/app/views/user_mailer/backup_ready.html.haml +++ b/app/views/user_mailer/backup_ready.html.haml @@ -55,5 +55,5 @@ %tbody %tr %td.button-primary - = link_to full_asset_url(@backup.dump.url) do + = link_to download_backup_url(@backup) do %span= t 'exports.archive_takeout.download' diff --git a/app/views/user_mailer/backup_ready.text.erb b/app/views/user_mailer/backup_ready.text.erb index eb89e7d74..8ebbaae85 100644 --- a/app/views/user_mailer/backup_ready.text.erb +++ b/app/views/user_mailer/backup_ready.text.erb @@ -4,4 +4,4 @@ <%= t 'user_mailer.backup_ready.explanation' %> -=> <%= full_asset_url(@backup.dump.url) %> +=> <%= download_backup_url(@backup) %> diff --git a/config/routes.rb b/config/routes.rb index ea595e1e1..8850545ca 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -220,6 +220,7 @@ Rails.application.routes.draw do resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update] get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy, format: false + get '/backups/:id/download', to: 'backups#download', as: :download_backup, format: false resource :authorize_interaction, only: [:show, :create] resource :share, only: [:show, :create] -- cgit From 63a6808ee12e2b23353a62e2a37b20fd48e4e429 Mon Sep 17 00:00:00 2001 From: mogamin <5103195+mgmn@users.noreply.github.com> Date: Sun, 26 Mar 2023 17:59:14 +0900 Subject: Fix typo in _login_activity.html.haml (#24263) --- app/views/settings/login_activities/_login_activity.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views/settings') diff --git a/app/views/settings/login_activities/_login_activity.html.haml b/app/views/settings/login_activities/_login_activity.html.haml index 9f4c24d87..94ed60312 100644 --- a/app/views/settings/login_activities/_login_activity.html.haml +++ b/app/views/settings/login_activities/_login_activity.html.haml @@ -1,6 +1,6 @@ - method_str = content_tag(:span, login_activity.omniauth? ? t(login_activity.provider, scope: 'auth.providers') : t(login_activity.authentication_method, scope: 'login_activities.authentication_methods'), class: 'target') - ip_str = content_tag(:span, login_activity.ip, class: 'target') -- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: login_activity.browser.to_s), platform: t("sessions.platforms.#{login_activity.platform}", default: login_activity.platform.to_)), class: 'target', title: login_activity.user_agent) +- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: login_activity.browser.to_s), platform: t("sessions.platforms.#{login_activity.platform}", default: login_activity.platform.to_s)), class: 'target', title: login_activity.user_agent) .log-entry .log-entry__header -- cgit From a9b5598c97fc4d3302b61b260097ef41c2ebe377 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 30 Mar 2023 14:44:00 +0200 Subject: Change user settings to be stored in a more optimal way (#23630) Co-authored-by: Claire --- .rubocop.yml | 8 ++ .rubocop_todo.yml | 1 + .../api/v1/accounts/credentials_controller.rb | 12 +- app/controllers/settings/preferences_controller.rb | 40 +----- app/lib/user_settings_decorator.rb | 155 --------------------- app/lib/user_settings_serializer.rb | 19 +++ app/models/concerns/has_user_settings.rb | 141 +++++++++++++++++++ app/models/user.rb | 59 +------- app/models/user_settings.rb | 99 +++++++++++++ app/models/user_settings/dsl.rb | 37 +++++ app/models/user_settings/glue.rb | 23 +++ app/models/user_settings/namespace.rb | 21 +++ app/models/user_settings/setting.rb | 48 +++++++ app/services/notify_service.rb | 13 +- .../settings/preferences/appearance/show.html.haml | 65 ++++----- .../preferences/notifications/show.html.haml | 36 ++--- .../settings/preferences/other/show.html.haml | 29 ++-- config/initializers/inflections.rb | 1 + config/settings.yml | 34 ----- db/migrate/20230215074327_add_settings_to_users.rb | 7 + db/migrate/20230215074423_move_user_settings.rb | 84 +++++++++++ db/schema.rb | 3 +- lib/tasks/tests.rake | 2 +- .../api/v1/accounts/credentials_controller_spec.rb | 1 + spec/controllers/application_controller_spec.rb | 10 +- .../preferences/notifications_controller_spec.rb | 14 +- .../settings/preferences/other_controller_spec.rb | 14 +- spec/lib/settings/extend_spec.rb | 16 --- spec/lib/settings/scoped_settings_spec.rb | 35 ----- spec/lib/user_settings_decorator_spec.rb | 84 ----------- spec/models/user_settings/namespace_spec.rb | 25 ++++ spec/models/user_settings/setting_spec.rb | 74 ++++++++++ spec/models/user_settings_spec.rb | 110 +++++++++++++++ spec/models/user_spec.rb | 14 +- spec/services/notify_service_spec.rb | 7 +- spec/services/report_service_spec.rb | 3 +- 36 files changed, 818 insertions(+), 526 deletions(-) delete mode 100644 app/lib/user_settings_decorator.rb create mode 100644 app/lib/user_settings_serializer.rb create mode 100644 app/models/concerns/has_user_settings.rb create mode 100644 app/models/user_settings.rb create mode 100644 app/models/user_settings/dsl.rb create mode 100644 app/models/user_settings/glue.rb create mode 100644 app/models/user_settings/namespace.rb create mode 100644 app/models/user_settings/setting.rb create mode 100644 db/migrate/20230215074327_add_settings_to_users.rb create mode 100644 db/migrate/20230215074423_move_user_settings.rb delete mode 100644 spec/lib/settings/extend_spec.rb delete mode 100644 spec/lib/settings/scoped_settings_spec.rb delete mode 100644 spec/lib/user_settings_decorator_spec.rb create mode 100644 spec/models/user_settings/namespace_spec.rb create mode 100644 spec/models/user_settings/setting_spec.rb create mode 100644 spec/models/user_settings_spec.rb (limited to 'app/views/settings') diff --git a/.rubocop.yml b/.rubocop.yml index 1033db92d..b5598b65a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -185,3 +185,11 @@ Style/TrailingCommaInHashLiteral: Style/SymbolArray: Enabled: false + +# Reason: Prefer less intendation in conditional assignments +# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantbegin +Style/RedundantBegin: + Enabled: false + +RSpec/NamedSubject: + EnforcedStyle: named_only diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 64a6b6b33..4ad266c8c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -259,6 +259,7 @@ Metrics/ModuleLength: - 'app/helpers/jsonld_helper.rb' - 'app/helpers/statuses_helper.rb' - 'app/models/concerns/account_interactions.rb' + - 'app/models/concerns/has_user_settings.rb' # Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters. Metrics/ParameterLists: diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index 94b707771..7c7d70fd3 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -13,7 +13,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController def update @account = current_account UpdateAccountService.new.call(@account, account_params, raise_error: true) - UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params + current_user.update(user_params) if user_params ActivityPub::UpdateDistributionWorker.perform_async(@account.id) render json: @account, serializer: REST::CredentialAccountSerializer end @@ -34,15 +34,17 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController ) end - def user_settings_params + def user_params return nil if params[:source].blank? source_params = params.require(:source) { - 'setting_default_privacy' => source_params.fetch(:privacy, @account.user.setting_default_privacy), - 'setting_default_sensitive' => source_params.fetch(:sensitive, @account.user.setting_default_sensitive), - 'setting_default_language' => source_params.fetch(:language, @account.user.setting_default_language), + settings_attributes: { + default_privacy: source_params.fetch(:privacy, @account.user.setting_default_privacy), + default_sensitive: source_params.fetch(:sensitive, @account.user.setting_default_sensitive), + default_language: source_params.fetch(:language, @account.user.setting_default_language), + }, } end end diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index f5d5c1244..281deb64d 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -4,8 +4,6 @@ class Settings::PreferencesController < Settings::BaseController def show; end def update - user_settings.update(user_settings_params.to_h) - if current_user.update(user_params) I18n.locale = current_user.locale redirect_to after_update_redirect_path, notice: I18n.t('generic.changes_saved_msg') @@ -20,43 +18,7 @@ class Settings::PreferencesController < Settings::BaseController settings_preferences_path end - def user_settings - UserSettingsDecorator.new(current_user) - end - def user_params - params.require(:user).permit( - :locale, - chosen_languages: [] - ) - end - - def user_settings_params - params.require(:user).permit( - :setting_default_privacy, - :setting_default_sensitive, - :setting_default_language, - :setting_unfollow_modal, - :setting_boost_modal, - :setting_delete_modal, - :setting_auto_play_gif, - :setting_display_media, - :setting_expand_spoilers, - :setting_reduce_motion, - :setting_disable_swiping, - :setting_system_font_ui, - :setting_noindex, - :setting_theme, - :setting_aggregate_reblogs, - :setting_show_application, - :setting_advanced_layout, - :setting_use_blurhash, - :setting_use_pending_items, - :setting_trends, - :setting_crop_images, - :setting_always_send_emails, - notification_emails: %i(follow follow_request reblog favourite mention report pending_account trending_tag appeal), - interactions: %i(must_be_follower must_be_following must_be_following_dm) - ) + params.require(:user).permit(:locale, chosen_languages: [], settings_attributes: UserSettings.keys) end end diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb deleted file mode 100644 index 5fb7655a9..000000000 --- a/app/lib/user_settings_decorator.rb +++ /dev/null @@ -1,155 +0,0 @@ -# frozen_string_literal: true - -class UserSettingsDecorator - attr_reader :user, :settings - - def initialize(user) - @user = user - end - - def update(settings) - @settings = settings - process_update - end - - private - - def process_update - user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails') - user.settings['interactions'] = merged_interactions if change?('interactions') - user.settings['default_privacy'] = default_privacy_preference if change?('setting_default_privacy') - user.settings['default_sensitive'] = default_sensitive_preference if change?('setting_default_sensitive') - user.settings['default_language'] = default_language_preference if change?('setting_default_language') - user.settings['unfollow_modal'] = unfollow_modal_preference if change?('setting_unfollow_modal') - user.settings['boost_modal'] = boost_modal_preference if change?('setting_boost_modal') - user.settings['delete_modal'] = delete_modal_preference if change?('setting_delete_modal') - user.settings['auto_play_gif'] = auto_play_gif_preference if change?('setting_auto_play_gif') - user.settings['display_media'] = display_media_preference if change?('setting_display_media') - user.settings['expand_spoilers'] = expand_spoilers_preference if change?('setting_expand_spoilers') - user.settings['reduce_motion'] = reduce_motion_preference if change?('setting_reduce_motion') - user.settings['disable_swiping'] = disable_swiping_preference if change?('setting_disable_swiping') - user.settings['system_font_ui'] = system_font_ui_preference if change?('setting_system_font_ui') - user.settings['noindex'] = noindex_preference if change?('setting_noindex') - user.settings['theme'] = theme_preference if change?('setting_theme') - user.settings['aggregate_reblogs'] = aggregate_reblogs_preference if change?('setting_aggregate_reblogs') - user.settings['show_application'] = show_application_preference if change?('setting_show_application') - user.settings['advanced_layout'] = advanced_layout_preference if change?('setting_advanced_layout') - user.settings['use_blurhash'] = use_blurhash_preference if change?('setting_use_blurhash') - user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items') - user.settings['trends'] = trends_preference if change?('setting_trends') - user.settings['crop_images'] = crop_images_preference if change?('setting_crop_images') - user.settings['always_send_emails'] = always_send_emails_preference if change?('setting_always_send_emails') - end - - def merged_notification_emails - user.settings['notification_emails'].merge coerced_settings('notification_emails').to_h - end - - def merged_interactions - user.settings['interactions'].merge coerced_settings('interactions').to_h - end - - def default_privacy_preference - settings['setting_default_privacy'] - end - - def default_sensitive_preference - boolean_cast_setting 'setting_default_sensitive' - end - - def unfollow_modal_preference - boolean_cast_setting 'setting_unfollow_modal' - end - - def boost_modal_preference - boolean_cast_setting 'setting_boost_modal' - end - - def delete_modal_preference - boolean_cast_setting 'setting_delete_modal' - end - - def system_font_ui_preference - boolean_cast_setting 'setting_system_font_ui' - end - - def auto_play_gif_preference - boolean_cast_setting 'setting_auto_play_gif' - end - - def display_media_preference - settings['setting_display_media'] - end - - def expand_spoilers_preference - boolean_cast_setting 'setting_expand_spoilers' - end - - def reduce_motion_preference - boolean_cast_setting 'setting_reduce_motion' - end - - def disable_swiping_preference - boolean_cast_setting 'setting_disable_swiping' - end - - def noindex_preference - boolean_cast_setting 'setting_noindex' - end - - def show_application_preference - boolean_cast_setting 'setting_show_application' - end - - def theme_preference - settings['setting_theme'] - end - - def default_language_preference - settings['setting_default_language'] - end - - def aggregate_reblogs_preference - boolean_cast_setting 'setting_aggregate_reblogs' - end - - def advanced_layout_preference - boolean_cast_setting 'setting_advanced_layout' - end - - def use_blurhash_preference - boolean_cast_setting 'setting_use_blurhash' - end - - def use_pending_items_preference - boolean_cast_setting 'setting_use_pending_items' - end - - def trends_preference - boolean_cast_setting 'setting_trends' - end - - def crop_images_preference - boolean_cast_setting 'setting_crop_images' - end - - def always_send_emails_preference - boolean_cast_setting 'setting_always_send_emails' - end - - def boolean_cast_setting(key) - ActiveModel::Type::Boolean.new.cast(settings[key]) - end - - def coerced_settings(key) - coerce_values settings.fetch(key, {}) - end - - def coerce_values(params_hash) - params_hash.transform_values { |x| ActiveModel::Type::Boolean.new.cast(x) } - end - - def change?(key) - !settings[key].nil? - end -end diff --git a/app/lib/user_settings_serializer.rb b/app/lib/user_settings_serializer.rb new file mode 100644 index 000000000..10d1be04d --- /dev/null +++ b/app/lib/user_settings_serializer.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class UserSettingsSerializer + def self.load(value) + json = begin + if value.blank? + {} + else + Oj.load(value, symbol_keys: true) + end + end + + UserSettings.new(json) + end + + def self.dump(value) + Oj.dump(value.as_json) + end +end diff --git a/app/models/concerns/has_user_settings.rb b/app/models/concerns/has_user_settings.rb new file mode 100644 index 000000000..b3fa1f683 --- /dev/null +++ b/app/models/concerns/has_user_settings.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +module HasUserSettings + extend ActiveSupport::Concern + + included do + serialize :settings, UserSettingsSerializer + end + + def settings_attributes=(attributes) + settings.update(attributes) + end + + def prefers_noindex? + settings['noindex'] + end + + def preferred_posting_language + valid_locale_cascade(settings['default_language'], locale, I18n.locale) + end + + def setting_auto_play_gif + settings['web.auto_play'] + end + + def setting_default_sensitive + settings['default_sensitive'] + end + + def setting_unfollow_modal + settings['web.unfollow_modal'] + end + + def setting_boost_modal + settings['web.reblog_modal'] + end + + def setting_delete_modal + settings['web.delete_modal'] + end + + def setting_reduce_motion + settings['web.reduce_motion'] + end + + def setting_system_font_ui + settings['web.use_system_font'] + end + + def setting_noindex + settings['noindex'] + end + + def setting_theme + settings['theme'] + end + + def setting_display_media + settings['web.display_media'] + end + + def setting_expand_spoilers + settings['web.expand_content_warnings'] + end + + def setting_default_language + settings['default_language'] + end + + def setting_aggregate_reblogs + settings['aggregate_reblogs'] + end + + def setting_show_application + settings['show_application'] + end + + def setting_advanced_layout + settings['web.advanced_layout'] + end + + def setting_use_blurhash + settings['web.use_blurhash'] + end + + def setting_use_pending_items + settings['web.use_pending_items'] + end + + def setting_trends + settings['web.trends'] + end + + def setting_crop_images + settings['web.crop_images'] + end + + def setting_disable_swiping + settings['web.disable_swiping'] + end + + def setting_always_send_emails + settings['always_send_emails'] + end + + def setting_default_privacy + settings['default_privacy'] || (account.locked? ? 'private' : 'public') + end + + def allows_report_emails? + settings['notification_emails.report'] + end + + def allows_pending_account_emails? + settings['notification_emails.pending_account'] + end + + def allows_appeal_emails? + settings['notification_emails.appeal'] + end + + def allows_trends_review_emails? + settings['notification_emails.trends'] + end + + def aggregates_reblogs? + settings['aggregate_reblogs'] + end + + def shows_application? + settings['show_application'] + end + + def show_all_media? + settings['web.display_media'] == 'show_all' + end + + def hide_all_media? + settings['web.display_media'] == 'hide_all' + end +end diff --git a/app/models/user.rb b/app/models/user.rb index d56a9b9ca..9b225d75f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -39,10 +39,11 @@ # webauthn_id :string # sign_up_ip :inet # role_id :bigint(8) +# settings :text # class User < ApplicationRecord - self.ignored_columns = %w( + self.ignored_columns += %w( remember_created_at remember_token current_sign_in_ip @@ -51,9 +52,9 @@ class User < ApplicationRecord filtered_languages ) - include Settings::Extend include Redisable include LanguagesHelper + include HasUserSettings # The home and list feeds will be stored in Redis for this amount # of time, and status fan-out to followers will include only people @@ -132,13 +133,6 @@ class User < ApplicationRecord has_many :session_activations, dependent: :destroy - delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal, - :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, - :expand_spoilers, :default_language, :aggregate_reblogs, :show_application, - :advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images, - :disable_swiping, :always_send_emails, - to: :settings, prefix: :setting, allow_nil: false - delegate :can?, to: :role attr_reader :invite_code @@ -302,42 +296,6 @@ class User < ApplicationRecord save! end - def prefers_noindex? - setting_noindex - end - - def preferred_posting_language - valid_locale_cascade(settings.default_language, locale, I18n.locale) - end - - def setting_default_privacy - settings.default_privacy || (account.locked? ? 'private' : 'public') - end - - def allows_report_emails? - settings.notification_emails['report'] - end - - def allows_pending_account_emails? - settings.notification_emails['pending_account'] - end - - def allows_appeal_emails? - settings.notification_emails['appeal'] - end - - def allows_trends_review_emails? - settings.notification_emails['trending_tag'] - end - - def aggregates_reblogs? - @aggregates_reblogs ||= settings.aggregate_reblogs - end - - def shows_application? - @shows_application ||= settings.show_application - end - def token_for_app(app) return nil if app.nil? || app.owner != self @@ -417,14 +375,6 @@ class User < ApplicationRecord send_reset_password_instructions end - def show_all_media? - setting_display_media == 'show_all' - end - - def hide_all_media? - setting_display_media == 'hide_all' - end - protected def send_devise_notification(notification, *args, **kwargs) @@ -494,7 +444,8 @@ class User < ApplicationRecord def sanitize_languages return if chosen_languages.nil? - chosen_languages.reject!(&:blank?) + chosen_languages.compact_blank! + self.chosen_languages = nil if chosen_languages.empty? end diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb new file mode 100644 index 000000000..2c025d6c5 --- /dev/null +++ b/app/models/user_settings.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +class UserSettings + class Error < StandardError; end + class KeyError < Error; end + + include UserSettings::DSL + include UserSettings::Glue + + setting :always_send_emails, default: false + setting :aggregate_reblogs, default: true + setting :theme, default: -> { ::Setting.theme } + setting :noindex, default: -> { ::Setting.noindex } + setting :show_application, default: true + setting :default_language, default: nil + setting :default_sensitive, default: false + setting :default_privacy, default: nil + + namespace :web do + setting :crop_images, default: true + setting :advanced_layout, default: false + setting :trends, default: true + setting :use_blurhash, default: true + setting :use_pending_items, default: false + setting :use_system_font, default: false + setting :disable_swiping, default: false + setting :delete_modal, default: true + setting :reblog_modal, default: false + setting :unfollow_modal, default: true + setting :reduce_motion, default: false + setting :expand_content_warnings, default: false + setting :display_media, default: 'default', in: %w(default show_all hide_all) + setting :auto_play, default: false + end + + namespace :notification_emails do + setting :follow, default: true + setting :reblog, default: false + setting :favourite, default: false + setting :mention, default: true + setting :follow_request, default: true + setting :report, default: true + setting :pending_account, default: true + setting :trends, default: true + setting :appeal, default: true + end + + namespace :interactions do + setting :must_be_follower, default: false + setting :must_be_following, default: false + setting :must_be_following_dm, default: false + end + + def initialize(original_hash) + @original_hash = original_hash || {} + end + + def [](key) + key = key.to_sym + + raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key) + + if @original_hash.key?(key) + @original_hash[key] + else + self.class.definition_for(key).default_value + end + end + + def []=(key, value) + key = key.to_sym + + raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key) + + typecast_value = self.class.definition_for(key).type_cast(value) + + if typecast_value.nil? + @original_hash.delete(key) + else + @original_hash[key] = typecast_value + end + end + + def update(params) + params.each do |k, v| + self[k] = v unless v.nil? + end + end + + keys.each do |key| + define_method(key) do + self[key] + end + end + + def as_json + @original_hash + end +end diff --git a/app/models/user_settings/dsl.rb b/app/models/user_settings/dsl.rb new file mode 100644 index 000000000..26238bbbe --- /dev/null +++ b/app/models/user_settings/dsl.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module UserSettings::DSL + module ClassMethods + def setting(key, options = {}) + @definitions ||= {} + + UserSettings::Setting.new(key, options).tap do |s| + @definitions[s.key] = s + end + end + + def namespace(key, &block) + @definitions ||= {} + + UserSettings::Namespace.new(key).configure(&block).tap do |n| + @definitions.merge!(n.definitions) + end + end + + def keys + @definitions.keys + end + + def definition_for(key) + @definitions[key.to_sym] + end + + def definition_for?(key) + @definitions.key?(key.to_sym) + end + end + + def self.included(base) + base.extend ClassMethods + end +end diff --git a/app/models/user_settings/glue.rb b/app/models/user_settings/glue.rb new file mode 100644 index 000000000..02066a411 --- /dev/null +++ b/app/models/user_settings/glue.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module UserSettings::Glue + def to_model + self + end + + def to_key + '' + end + + def persisted? + false + end + + def type_for_attribute(key) + self.class.definition_for(key)&.type + end + + def has_attribute?(key) # rubocop:disable Naming/PredicateName + self.class.definition_for?(key) + end +end diff --git a/app/models/user_settings/namespace.rb b/app/models/user_settings/namespace.rb new file mode 100644 index 000000000..b8f7e092e --- /dev/null +++ b/app/models/user_settings/namespace.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class UserSettings::Namespace + attr_reader :name, :definitions + + def initialize(name) + @name = name.to_sym + @definitions = {} + end + + def configure(&block) + instance_eval(&block) + self + end + + def setting(key, options = {}) + UserSettings::Setting.new(key, options.merge(namespace: name)).tap do |s| + @definitions[s.key] = s + end + end +end diff --git a/app/models/user_settings/setting.rb b/app/models/user_settings/setting.rb new file mode 100644 index 000000000..c359c593b --- /dev/null +++ b/app/models/user_settings/setting.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +class UserSettings::Setting + attr_reader :name, :namespace, :in + + def initialize(name, options = {}) + @name = name.to_sym + @default_value = options[:default] + @namespace = options[:namespace] + @in = options[:in] + end + + def default_value + if @default_value.respond_to?(:call) + @default_value.call + else + @default_value + end + end + + def type + if @default_value.is_a?(TrueClass) || @default_value.is_a?(FalseClass) + ActiveModel::Type::Boolean.new + else + ActiveModel::Type::String.new + end + end + + def type_cast(value) + if type.respond_to?(:cast) + type.cast(value) + else + value + end + end + + def to_a + [key, default_value] + end + + def key + if namespace + "#{namespace}.#{name}".to_sym + else + name + end + end +end diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index 4c7acbcac..994ca588a 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -3,6 +3,11 @@ class NotifyService < BaseService include Redisable + NON_EMAIL_TYPES = %i( + admin.report + admin.sign_up + ).freeze + def call(recipient, type, activity) @recipient = recipient @activity = activity @@ -36,11 +41,11 @@ class NotifyService < BaseService end def optional_non_follower? - @recipient.user.settings.interactions['must_be_follower'] && !@notification.from_account.following?(@recipient) + @recipient.user.settings['interactions.must_be_follower'] && !@notification.from_account.following?(@recipient) end def optional_non_following? - @recipient.user.settings.interactions['must_be_following'] && !following_sender? + @recipient.user.settings['interactions.must_be_following'] && !following_sender? end def message? @@ -82,7 +87,7 @@ class NotifyService < BaseService def optional_non_following_and_direct? direct_message? && - @recipient.user.settings.interactions['must_be_following_dm'] && + @recipient.user.settings['interactions.must_be_following_dm'] && !following_sender? && !response_to_recipient? end @@ -171,6 +176,6 @@ class NotifyService < BaseService end def send_email_for_notification_type? - @recipient.user.settings.notification_emails[@notification.type.to_s] + NON_EMAIL_TYPES.exclude?(@notification.type) && @recipient.user.settings["notification_emails.#{@notification.type}"] end end diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml index bc23df647..5358310e5 100644 --- a/app/views/settings/preferences/appearance/show.html.haml +++ b/app/views/settings/preferences/appearance/show.html.haml @@ -9,57 +9,58 @@ .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| native_locale_name(locale) }, selected: I18n.locale, hint: false .fields-group.fields-row__column.fields-row__column-6 - = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false, hint: false + = f.simple_fields_for :settings, current_user.settings do |ff| + = ff.input :theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false, hint: false - unless I18n.locale == :en .flash-message.translation-prompt #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: '_blank', rel: 'noopener')} - %h4= t 'appearance.advanced_web_interface' + = f.simple_fields_for :settings, current_user.settings do |ff| + %h4= t 'appearance.advanced_web_interface' - %p.hint= t 'appearance.advanced_web_interface_hint' + %p.hint= t 'appearance.advanced_web_interface_hint' - .fields-group - = f.input :setting_advanced_layout, as: :boolean, wrapper: :with_label, hint: false + .fields-group + = ff.input :'web.advanced_layout', wrapper: :with_label, hint: false, label: I18n.t('simple_form.labels.defaults.setting_advanced_layout') + %h4= t 'appearance.animations_and_accessibility' - %h4= t 'appearance.animations_and_accessibility' + .fields-group + = ff.input :'web.use_pending_items', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_use_pending_items'), hint: I18n.t('simple_form.hints.defaults.setting_use_pending_items') - .fields-group - = f.input :setting_use_pending_items, as: :boolean, wrapper: :with_label + .fields-group + = ff.input :'web.auto_play', wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_auto_play_gif') + = ff.input :'web.reduce_motion', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_reduce_motion') + = ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping') + = ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui') - .fields-group - = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label, recommended: true - = f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label - = f.input :setting_disable_swiping, as: :boolean, wrapper: :with_label - = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label + %h4= t 'appearance.toot_layout' - %h4= t 'appearance.toot_layout' + .fields-group + = ff.input :'web.crop_images', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_crop_images') - .fields-group - = f.input :setting_crop_images, as: :boolean, wrapper: :with_label + %h4= t 'appearance.discovery' - %h4= t 'appearance.discovery' + .fields-group + = ff.input :'web.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_trends') - .fields-group - = f.input :setting_trends, as: :boolean, wrapper: :with_label + %h4= t 'appearance.confirmation_dialogs' - %h4= t 'appearance.confirmation_dialogs' + .fields-group + = ff.input :'web.unfollow_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_unfollow_modal') + = ff.input :'web.reblog_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_boost_modal') + = ff.input :'web.delete_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_delete_modal') - .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 + %h4= t 'appearance.sensitive_content' - %h4= t 'appearance.sensitive_content' + .fields-group + = ff.input :'web.display_media', collection: ['default', 'show_all', 'hide_all'],label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label, label: I18n.t('simple_form.labels.defaults.setting_display_media') - .fields-group - = f.input :setting_display_media, collection: ['default', 'show_all', 'hide_all'], label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label + .fields-group + = ff.input :'web.use_blurhash', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_use_blurhash'), hint: I18n.t('simple_form.hints.defaults.setting_use_blurhash') - .fields-group - = f.input :setting_use_blurhash, as: :boolean, wrapper: :with_label - - .fields-group - = f.input :setting_expand_spoilers, as: :boolean, wrapper: :with_label + .fields-group + = ff.input :'web.expand_content_warnings', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_expand_spoilers') .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/settings/preferences/notifications/show.html.haml b/app/views/settings/preferences/notifications/show.html.haml index f00dbadd4..cb1ad0886 100644 --- a/app/views/settings/preferences/notifications/show.html.haml +++ b/app/views/settings/preferences/notifications/show.html.haml @@ -11,25 +11,25 @@ %p.hint= t 'notifications.email_events_hint' - .fields-group - = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| - = ff.input :follow, as: :boolean, wrapper: :with_label - = ff.input :follow_request, as: :boolean, wrapper: :with_label - = ff.input :reblog, as: :boolean, wrapper: :with_label - = ff.input :favourite, as: :boolean, wrapper: :with_label - = ff.input :mention, as: :boolean, wrapper: :with_label - = ff.input :report, as: :boolean, wrapper: :with_label if current_user.can?(:manage_reports) - = ff.input :appeal, as: :boolean, wrapper: :with_label if current_user.can?(:manage_appeals) - = ff.input :pending_account, as: :boolean, wrapper: :with_label if current_user.can?(:manage_users) - = ff.input :trending_tag, as: :boolean, wrapper: :with_label if current_user.can?(:manage_taxonomies) - - .fields-group - = f.input :setting_always_send_emails, as: :boolean, wrapper: :with_label + = f.simple_fields_for :settings, current_user.settings do |ff| + .fields-group + = ff.input :'notification_emails.follow', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow') + = ff.input :'notification_emails.follow_request', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow_request') + = ff.input :'notification_emails.reblog', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.reblog') + = ff.input :'notification_emails.favourite', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.favourite') + = ff.input :'notification_emails.mention', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.mention') + = ff.input :'notification_emails.report', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.report') if current_user.can?(:manage_reports) + = ff.input :'notification_emails.appeal', as: :boolean, wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.appeal') if current_user.can?(:manage_appeals) + = ff.input :'notification_emails.pending_account', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.pending_account') if current_user.can?(:manage_users) + = ff.input :'notification_emails.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_tag') if current_user.can?(:manage_taxonomies) + + .fields-group + = ff.input :always_send_emails, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_always_send_emails'), hint: I18n.t('simple_form.hints.defaults.setting_always_send_emails') %h4= t 'notifications.other_settings' .fields-group - = f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff| - = ff.input :must_be_follower, as: :boolean, wrapper: :with_label - = ff.input :must_be_following, as: :boolean, wrapper: :with_label - = ff.input :must_be_following_dm, as: :boolean, wrapper: :with_label + = f.simple_fields_for :settings, current_user.settings do |ff| + = ff.input :'interactions.must_be_follower', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_follower') + = ff.input :'interactions.must_be_following', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following') + = ff.input :'interactions.must_be_following_dm', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following_dm') diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml index 44f4af2eb..6590ec7c2 100644 --- a/app/views/settings/preferences/other/show.html.haml +++ b/app/views/settings/preferences/other/show.html.haml @@ -7,26 +7,27 @@ = simple_form_for current_user, url: settings_preferences_other_path, html: { method: :put, id: 'edit_preferences' } do |f| = render 'shared/error_messages', object: current_user - .fields-group - = f.input :setting_noindex, as: :boolean, wrapper: :with_label + = f.simple_fields_for :settings, current_user.settings do |ff| + .fields-group + = ff.input :noindex, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_noindex'), hint: I18n.t('simple_form.hints.defaults.setting_noindex') - .fields-group - = f.input :setting_aggregate_reblogs, as: :boolean, wrapper: :with_label, recommended: true + .fields-group + = ff.input :aggregate_reblogs, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_aggregate_reblogs'), hint: I18n.t('simple_form.hints.defaults.setting_aggregate_reblogs') - %h4= t 'preferences.posting_defaults' + %h4= t 'preferences.posting_defaults' - .fields-row - .fields-group.fields-row__column.fields-row__column-6 - = f.input :setting_default_privacy, collection: Status.selectable_visibilities, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), I18n.t("statuses.visibilities.#{visibility}_long")], ' - ') }, required: false, hint: false + .fields-row + .fields-group.fields-row__column.fields-row__column-6 + = ff.input :default_privacy, collection: Status.selectable_visibilities, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), I18n.t("statuses.visibilities.#{visibility}_long")], ' - ') }, required: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_privacy') - .fields-group.fields-row__column.fields-row__column-6 - = f.input :setting_default_language, collection: [nil] + filterable_languages, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.default_language') : native_locale_name(locale) }, required: false, include_blank: false, hint: false + .fields-group.fields-row__column.fields-row__column-6 + = ff.input :default_language, collection: [nil] + filterable_languages, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.default_language') : native_locale_name(locale) }, required: false, include_blank: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_language') - .fields-group - = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label + .fields-group + = ff.input :default_sensitive, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_sensitive'), hint: I18n.t('simple_form.hints.defaults.setting_default_sensitive') - .fields-group - = f.input :setting_show_application, as: :boolean, wrapper: :with_label, recommended: true + .fields-group + = ff.input :show_application, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_show_application'), hint: I18n.t('simple_form.hints.defaults.setting_show_application') %h4= t 'preferences.public_timelines' diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index a361cb0ec..95f0b5788 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -26,6 +26,7 @@ ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'URL' inflect.acronym 'ASCII' inflect.acronym 'DeepL' + inflect.acronym 'DSL' inflect.singular 'data', 'data' end diff --git a/config/settings.yml b/config/settings.yml index f0b09dd5c..4ac521a4b 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -12,48 +12,14 @@ defaults: &defaults registrations_mode: 'open' profile_directory: true closed_registrations_message: '' - open_deletion: true - min_invite_role: 'admin' timeline_preview: true show_staff_badge: true - default_sensitive: false - unfollow_modal: false - boost_modal: false - delete_modal: true - auto_play_gif: false - display_media: 'default' - expand_spoilers: false preview_sensitive_media: false - reduce_motion: false - disable_swiping: false - show_application: true - system_font_ui: false noindex: false theme: 'default' - aggregate_reblogs: true - advanced_layout: false - use_blurhash: true - use_pending_items: false trends: true trends_as_landing_page: true trendable_by_default: false - crop_images: true - notification_emails: - follow: true - reblog: false - favourite: false - mention: true - follow_request: true - digest: true - report: true - pending_account: true - trending_tag: true - appeal: true - always_send_emails: false - interactions: - must_be_follower: false - must_be_following: false - must_be_following_dm: false reserved_usernames: - admin - support diff --git a/db/migrate/20230215074327_add_settings_to_users.rb b/db/migrate/20230215074327_add_settings_to_users.rb new file mode 100644 index 000000000..ff5308f42 --- /dev/null +++ b/db/migrate/20230215074327_add_settings_to_users.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddSettingsToUsers < ActiveRecord::Migration[6.1] + def change + add_column :users, :settings, :text + end +end diff --git a/db/migrate/20230215074423_move_user_settings.rb b/db/migrate/20230215074423_move_user_settings.rb new file mode 100644 index 000000000..351a8b61d --- /dev/null +++ b/db/migrate/20230215074423_move_user_settings.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +class MoveUserSettings < ActiveRecord::Migration[6.1] + class User < ApplicationRecord; end + + MAPPING = { + default_privacy: 'default_privacy', + default_sensitive: 'web.default_sensitive', + default_language: 'default_language', + noindex: 'noindex', + theme: 'theme', + trends: 'web.trends', + unfollow_modal: 'web.unfollow_modal', + boost_modal: 'web.reblog_modal', + delete_modal: 'web.delete_modal', + auto_play_gif: 'web.auto_play', + display_media: 'web.display_media', + expand_spoilers: 'web.expand_content_warnings', + reduce_motion: 'web.reduce_motion', + disable_swiping: 'web.disable_swiping', + show_application: 'show_application', + system_font_ui: 'web.use_system_font', + aggregate_reblogs: 'aggregate_reblogs', + advanced_layout: 'web.advanced_layout', + use_blurhash: 'web.use_blurhash', + use_pending_items: 'web.use_pending_items', + crop_images: 'web.crop_images', + notification_emails: { + follow: 'notification_emails.follow', + reblog: 'notification_emails.reblog', + favourite: 'notification_emails.favourite', + mention: 'notification_emails.mention', + follow_request: 'notification_emails.follow_request', + report: 'notification_emails.report', + pending_account: 'notification_emails.pending_account', + trending_tag: 'notification_emails.trends', + appeal: 'notification_emails.appeal', + }.freeze, + always_send_emails: 'always_send_emails', + interactions: { + must_be_follower: 'interactions.must_be_follower', + must_be_following: 'interactions.must_be_following', + must_be_following_dm: 'interactions.must_be_following_dm', + }.freeze, + }.freeze + + class LegacySetting < ApplicationRecord + self.table_name = 'settings' + + def var + self[:var]&.to_sym + end + + def value + YAML.safe_load(self[:value], permitted_classes: [ActiveSupport::HashWithIndifferentAccess]) if self[:value].present? + end + end + + def up + User.find_each do |user| + previous_settings = LegacySetting.where(thing_type: 'User', thing_id: user.id).index_by(&:var) + + user_settings = {} + + MAPPING.each do |legacy_key, new_key| + value = previous_settings[legacy_key]&.value + + next if value.blank? + + if value.is_a?(Hash) + value.each do |nested_key, nested_value| + user_settings[MAPPING[legacy_key][nested_key.to_sym]] = nested_value + end + else + user_settings[new_key] = value + end + end + + user.update_column('settings', Oj.dump(user_settings)) # rubocop:disable Rails/SkipsModelValidations + end + end + + def down; end +end diff --git a/db/schema.rb b/db/schema.rb index 704cef122..620bed2bc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_12_06_114142) do +ActiveRecord::Schema.define(version: 2023_02_15_074423) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1060,6 +1060,7 @@ ActiveRecord::Schema.define(version: 2022_12_06_114142) do t.inet "sign_up_ip" t.boolean "skip_sign_in_token" t.bigint "role_id" + t.text "settings" t.index ["account_id"], name: "index_users_on_account_id" t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["created_by_application_id"], name: "index_users_on_created_by_application_id", where: "(created_by_application_id IS NOT NULL)" diff --git a/lib/tasks/tests.rake b/lib/tasks/tests.rake index 51a6ee0d7..35073b78b 100644 --- a/lib/tasks/tests.rake +++ b/lib/tasks/tests.rake @@ -54,7 +54,7 @@ namespace :tests do exit(1) end - unless User.find(1).settings.notification_emails['favourite'] == true && User.find(1).settings.notification_emails['mention'] == false + unless User.find(1).settings['notification_emails.favourite'] == true && User.find(1).settings['notification_emails.mention'] == false puts 'User settings not kept as expected' exit(1) end diff --git a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb index 57fe0aee6..b5d5c37a9 100644 --- a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb @@ -46,6 +46,7 @@ describe Api::V1::Accounts::CredentialsController do end it 'updates account info' do + user.reload user.account.reload expect(user.account.display_name).to eq("Alice Isn't Dead") diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 35c7326cb..bc6c6c0c5 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -88,21 +88,19 @@ describe ApplicationController, type: :controller do it 'returns instances\'s default theme when user didn\'t set theme' do current_user = Fabricate(:user) + current_user.settings.update(theme: 'contrast', noindex: false) + current_user.save sign_in current_user - allow(Setting).to receive(:[]).with('theme').and_return 'contrast' - allow(Setting).to receive(:[]).with('noindex').and_return false - expect(controller.view_context.current_theme).to eq 'contrast' end it 'returns user\'s theme when it is set' do current_user = Fabricate(:user) - current_user.settings['theme'] = 'mastodon-light' + current_user.settings.update(theme: 'mastodon-light') + current_user.save sign_in current_user - allow(Setting).to receive(:[]).with('theme').and_return 'contrast' - expect(controller.view_context.current_theme).to eq 'mastodon-light' end end diff --git a/spec/controllers/settings/preferences/notifications_controller_spec.rb b/spec/controllers/settings/preferences/notifications_controller_spec.rb index 66fb8c5eb..29b7b6aec 100644 --- a/spec/controllers/settings/preferences/notifications_controller_spec.rb +++ b/spec/controllers/settings/preferences/notifications_controller_spec.rb @@ -20,20 +20,22 @@ describe Settings::Preferences::NotificationsController do describe 'PUT #update' do it 'updates notifications settings' do - user.settings['notification_emails'] = user.settings['notification_emails'].merge('follow' => false) - user.settings['interactions'] = user.settings['interactions'].merge('must_be_follower' => true) + user.settings.update('notification_emails.follow': false, 'interactions.must_be_follower': true) + user.save put :update, params: { user: { - notification_emails: { follow: '1' }, - interactions: { must_be_follower: '0' }, + settings_attributes: { + 'notification_emails.follow': '1', + 'interactions.must_be_follower': '0', + }, }, } expect(response).to redirect_to(settings_preferences_notifications_path) user.reload - expect(user.settings['notification_emails']['follow']).to be true - expect(user.settings['interactions']['must_be_follower']).to be false + expect(user.settings['notification_emails.follow']).to be true + expect(user.settings['interactions.must_be_follower']).to be false end end end diff --git a/spec/controllers/settings/preferences/other_controller_spec.rb b/spec/controllers/settings/preferences/other_controller_spec.rb index 63eeefaf0..249d1b5b5 100644 --- a/spec/controllers/settings/preferences/other_controller_spec.rb +++ b/spec/controllers/settings/preferences/other_controller_spec.rb @@ -29,20 +29,22 @@ describe Settings::Preferences::OtherController do end it 'updates user settings' do - user.settings['boost_modal'] = false - user.settings['delete_modal'] = true + user.settings.update('web.reblog_modal': false, 'web.delete_modal': true) + user.save put :update, params: { user: { - setting_boost_modal: '1', - setting_delete_modal: '0', + settings_attributes: { + 'web.reblog_modal': '1', + 'web.delete_modal': '0', + }, }, } expect(response).to redirect_to(settings_preferences_other_path) user.reload - expect(user.settings['boost_modal']).to be true - expect(user.settings['delete_modal']).to be false + expect(user.settings['web.reblog_modal']).to be true + expect(user.settings['web.delete_modal']).to be false end end end diff --git a/spec/lib/settings/extend_spec.rb b/spec/lib/settings/extend_spec.rb deleted file mode 100644 index ea623137b..000000000 --- a/spec/lib/settings/extend_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Settings::Extend do - class User - include Settings::Extend - end - - describe '#settings' do - it 'sets @settings as an instance of Settings::ScopedSettings' do - user = Fabricate(:user) - expect(user.settings).to be_a Settings::ScopedSettings - end - end -end diff --git a/spec/lib/settings/scoped_settings_spec.rb b/spec/lib/settings/scoped_settings_spec.rb deleted file mode 100644 index 7566685b4..000000000 --- a/spec/lib/settings/scoped_settings_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Settings::ScopedSettings do - let(:object) { Fabricate(:user) } - let(:scoped_setting) { described_class.new(object) } - let(:val) { 'whatever' } - let(:methods) { %i(auto_play_gif default_sensitive unfollow_modal boost_modal delete_modal reduce_motion system_font_ui noindex theme) } - - describe '.initialize' do - it 'sets @object' do - scoped_setting = described_class.new(object) - expect(scoped_setting.instance_variable_get(:@object)).to be object - end - end - - describe '#method_missing' do - it 'sets scoped_setting.method_name = val' do - methods.each do |key| - scoped_setting.send("#{key}=", val) - expect(scoped_setting.send(key)).to eq val - end - end - end - - describe '#[]= and #[]' do - it 'sets [key] = val' do - methods.each do |key| - scoped_setting[key] = val - expect(scoped_setting[key]).to eq val - end - end - end -end diff --git a/spec/lib/user_settings_decorator_spec.rb b/spec/lib/user_settings_decorator_spec.rb deleted file mode 100644 index 3b9b7ee2b..000000000 --- a/spec/lib/user_settings_decorator_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe UserSettingsDecorator do - describe 'update' do - let(:user) { Fabricate(:user) } - let(:settings) { described_class.new(user) } - - it 'updates the user settings value for email notifications' do - values = { 'notification_emails' => { 'follow' => '1' } } - - settings.update(values) - expect(user.settings['notification_emails']['follow']).to be true - end - - it 'updates the user settings value for interactions' do - values = { 'interactions' => { 'must_be_follower' => '0' } } - - settings.update(values) - expect(user.settings['interactions']['must_be_follower']).to be false - end - - it 'updates the user settings value for privacy' do - values = { 'setting_default_privacy' => 'public' } - - settings.update(values) - expect(user.settings['default_privacy']).to eq 'public' - end - - it 'updates the user settings value for sensitive' do - values = { 'setting_default_sensitive' => '1' } - - settings.update(values) - expect(user.settings['default_sensitive']).to be true - end - - it 'updates the user settings value for unfollow modal' do - values = { 'setting_unfollow_modal' => '0' } - - settings.update(values) - expect(user.settings['unfollow_modal']).to be false - end - - it 'updates the user settings value for boost modal' do - values = { 'setting_boost_modal' => '1' } - - settings.update(values) - expect(user.settings['boost_modal']).to be true - end - - it 'updates the user settings value for delete toot modal' do - values = { 'setting_delete_modal' => '0' } - - settings.update(values) - expect(user.settings['delete_modal']).to be false - end - - it 'updates the user settings value for gif auto play' do - values = { 'setting_auto_play_gif' => '0' } - - settings.update(values) - expect(user.settings['auto_play_gif']).to be false - end - - it 'updates the user settings value for system font in UI' do - values = { 'setting_system_font_ui' => '0' } - - settings.update(values) - expect(user.settings['system_font_ui']).to be false - end - - it 'decoerces setting values before applying' do - values = { - 'setting_delete_modal' => 'false', - 'setting_boost_modal' => 'true', - } - - settings.update(values) - expect(user.settings['delete_modal']).to be false - expect(user.settings['boost_modal']).to be true - end - end -end diff --git a/spec/models/user_settings/namespace_spec.rb b/spec/models/user_settings/namespace_spec.rb new file mode 100644 index 000000000..ae2fa7b48 --- /dev/null +++ b/spec/models/user_settings/namespace_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UserSettings::Namespace do + subject { described_class.new(name) } + + let(:name) { :foo } + + describe '#setting' do + before do + subject.setting :bar, default: 'baz' + end + + it 'adds setting to definitions' do + expect(subject.definitions[:'foo.bar']).to have_attributes(name: :bar, namespace: :foo, default_value: 'baz') + end + end + + describe '#definitions' do + it 'returns a hash' do + expect(subject.definitions).to be_a Hash + end + end +end diff --git a/spec/models/user_settings/setting_spec.rb b/spec/models/user_settings/setting_spec.rb new file mode 100644 index 000000000..6e4ec6789 --- /dev/null +++ b/spec/models/user_settings/setting_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UserSettings::Setting do + subject { described_class.new(name, options) } + + let(:name) { :foo } + let(:options) { { default: default, namespace: namespace } } + let(:default) { false } + let(:namespace) { nil } + + describe '#default_value' do + context 'when default value is a primitive value' do + it 'returns default value' do + expect(subject.default_value).to eq default + end + end + + context 'when default value is a proc' do + let(:default) { -> { 'bar' } } + + it 'returns value from proc' do + expect(subject.default_value).to eq 'bar' + end + end + end + + describe '#type' do + it 'returns a type' do + expect(subject.type).to be_a ActiveModel::Type::Value + end + end + + describe '#type_cast' do + context 'when default value is a boolean' do + let(:default) { false } + + it 'returns boolean' do + expect(subject.type_cast('1')).to be true + end + end + + context 'when default value is a string' do + let(:default) { '' } + + it 'returns string' do + expect(subject.type_cast(1)).to eq '1' + end + end + end + + describe '#to_a' do + it 'returns an array' do + expect(subject.to_a).to eq [name, default] + end + end + + describe '#key' do + context 'when there is no namespace' do + it 'returnsn a symbol' do + expect(subject.key).to eq :foo + end + end + + context 'when there is a namespace' do + let(:namespace) { :bar } + + it 'returns a symbol' do + expect(subject.key).to eq :'bar.foo' + end + end + end +end diff --git a/spec/models/user_settings_spec.rb b/spec/models/user_settings_spec.rb new file mode 100644 index 000000000..f0e4272fd --- /dev/null +++ b/spec/models/user_settings_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UserSettings do + subject { described_class.new(json) } + + let(:json) { {} } + + describe '#[]' do + context 'when setting is not set' do + it 'returns default value' do + expect(subject[:always_send_emails]).to be false + end + end + + context 'when setting is set' do + let(:json) { { default_language: 'fr' } } + + it 'returns value' do + expect(subject[:default_language]).to eq 'fr' + end + end + + context 'when setting was not defined' do + it 'raises error' do + expect { subject[:foo] }.to raise_error UserSettings::KeyError + end + end + end + + describe '#[]=' do + context 'when value matches type' do + before do + subject[:always_send_emails] = true + end + + it 'updates value' do + expect(subject[:always_send_emails]).to be true + end + end + + context 'when value needs to be type-cast' do + before do + subject[:always_send_emails] = '1' + end + + it 'updates value with a type-cast' do + expect(subject[:always_send_emails]).to be true + end + end + end + + describe '#update' do + before do + subject.update(always_send_emails: true, default_language: 'fr', default_privacy: nil) + end + + it 'updates values' do + expect(subject[:always_send_emails]).to be true + expect(subject[:default_language]).to eq 'fr' + end + + it 'does not set values that are nil' do + expect(subject.as_json).to_not include(default_privacy: nil) + end + end + + describe '#as_json' do + let(:json) { { default_language: 'fr' } } + + it 'returns hash' do + expect(subject.as_json).to eq json + end + end + + describe '.keys' do + it 'returns an array' do + expect(described_class.keys).to be_a Array + end + end + + describe '.definition_for' do + context 'when key is defined' do + it 'returns a setting' do + expect(described_class.definition_for(:always_send_emails)).to be_a UserSettings::Setting + end + end + + context 'when key is not defined' do + it 'returns nil' do + expect(described_class.definition_for(:foo)).to be_nil + end + end + end + + describe '.definition_for?' do + context 'when key is defined' do + it 'returns true' do + expect(described_class.definition_for?(:always_send_emails)).to be true + end + end + + context 'when key is not defined' do + it 'returns false' do + expect(described_class.definition_for?(:foo)).to be false + end + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3e7b59f17..ab883927a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -313,9 +313,9 @@ RSpec.describe User, type: :model do end describe 'settings' do - it 'is instance of Settings::ScopedSettings' do + it 'is instance of UserSettings' do user = Fabricate(:user) - expect(user.settings).to be_a Settings::ScopedSettings + expect(user.settings).to be_a UserSettings end end @@ -379,16 +379,6 @@ RSpec.describe User, type: :model do end end - it_behaves_like 'Settings-extended' do - def create! - User.create!(account: Fabricate(:account, user: nil), email: 'foo@mastodon.space', password: 'abcd1234', agreement: true) - end - - def fabricate - Fabricate(:user) - end - end - describe 'token_for_app' do let(:user) { Fabricate(:user) } let(:app) { Fabricate(:application, owner: user) } diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index c58cebbfb..616a7aa20 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -54,7 +54,8 @@ RSpec.describe NotifyService, type: :service do let(:type) { :mention } before do - user.settings.interactions = user.settings.interactions.merge('must_be_following_dm' => enabled) + user.settings.update('interactions.must_be_following_dm': enabled) + user.save end context 'if recipient is supposed to be following sender' do @@ -155,8 +156,8 @@ RSpec.describe NotifyService, type: :service do before do ActionMailer::Base.deliveries.clear - notification_emails = user.settings.notification_emails - user.settings.notification_emails = notification_emails.merge('follow' => enabled) + user.settings.update('notification_emails.follow': enabled) + user.save end context 'when email notification is enabled' do diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb index 9d81bd971..452400f72 100644 --- a/spec/services/report_service_spec.rb +++ b/spec/services/report_service_spec.rb @@ -96,7 +96,8 @@ RSpec.describe ReportService, type: :service do before do ActionMailer::Base.deliveries.clear - source_account.user.settings.notification_emails['report'] = true + source_account.user.settings['notification_emails.report'] = true + source_account.user.save end it 'does not send an e-mail' do -- cgit From c3a8f6a0a33c264d0c461232ab35034a1210a733 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 7 Apr 2023 20:55:28 +0200 Subject: Fix glitch-soc HAML linting issues (#2166) --- app/views/layouts/_theme.html.haml | 5 ++--- app/views/layouts/application.html.haml | 2 +- app/views/layouts/embedded.html.haml | 2 +- app/views/media/player.html.haml | 2 +- app/views/settings/flavours/show.html.haml | 4 ++-- 5 files changed, 7 insertions(+), 8 deletions(-) (limited to 'app/views/settings') diff --git a/app/views/layouts/_theme.html.haml b/app/views/layouts/_theme.html.haml index 5dba77621..71e661de6 100644 --- a/app/views/layouts/_theme.html.haml +++ b/app/views/layouts/_theme.html.haml @@ -9,6 +9,5 @@ = stylesheet_pack_tag pack_path, media: 'all', crossorigin: 'anonymous' - else = stylesheet_pack_tag "skins/#{theme[:flavour]}/#{theme[:skin]}/#{theme[:pack]}", media: 'all', crossorigin: 'anonymous' - - if theme[:preload] - - theme[:preload].each do |link| - %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ + - theme[:preload]&.each do |link| + %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index d19ea1390..f4f8744e9 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -39,7 +39,7 @@ = yield :header_tags - -# These must come after :header_tags to ensure our initial state has been defined. + -# These must come after :header_tags to ensure our initial state has been defined. = render partial: 'layouts/theme', object: @core = render partial: 'layouts/theme', object: @theme diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml index b8817d566..210ac101d 100644 --- a/app/views/layouts/embedded.html.haml +++ b/app/views/layouts/embedded.html.haml @@ -12,7 +12,7 @@ %link{ rel: 'dns-prefetch', href: storage_host }/ = render_initial_state - = javascript_pack_tag "locales", crossorigin: 'anonymous' + = javascript_pack_tag 'locales', crossorigin: 'anonymous' - if @theme - if @theme[:supported_locales].include? I18n.locale.to_s = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", crossorigin: 'anonymous' diff --git a/app/views/media/player.html.haml b/app/views/media/player.html.haml index c1d630a63..c907d5c60 100644 --- a/app/views/media/player.html.haml +++ b/app/views/media/player.html.haml @@ -1,6 +1,6 @@ - content_for :header_tags do = render_initial_state - = javascript_pack_tag "locales", crossorigin: 'anonymous' + = javascript_pack_tag 'locales', crossorigin: 'anonymous' - if @theme - if @theme[:supported_locales].include? I18n.locale.to_s = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", crossorigin: 'anonymous' diff --git a/app/views/settings/flavours/show.html.haml b/app/views/settings/flavours/show.html.haml index c3f785aa0..ea2540858 100644 --- a/app/views/settings/flavours/show.html.haml +++ b/app/views/settings/flavours/show.html.haml @@ -5,7 +5,7 @@ = render 'shared/error_messages', object: current_user - Themes.instance.flavour(@selected)['screenshot'].each do |screen| - %img.flavour-screen{ src: full_pack_url("media/#{screen}") } + %img.flavour-screen{ src: full_pack_url("media/#{screen}"), alt: '' } .flavour-description = t "flavours.#{@selected}.description", default: '' @@ -14,7 +14,7 @@ - if Themes.instance.skins_for(@selected).length > 1 .fields-group - = f.input :setting_skin, collection: Themes.instance.skins_for(@selected), label_method: lambda { |skin| I18n.t("skins.#{@selected}.#{skin}", default: skin) }, wrapper: :with_label, include_blank: false + = f.input :setting_skin, collection: Themes.instance.skins_for(@selected), label_method: ->(skin) { I18n.t("skins.#{@selected}.#{skin}", default: skin) }, wrapper: :with_label, include_blank: false .actions = f.button :button, t('generic.use_this'), type: :submit -- cgit From bed63f6dae0879ac840066b031229e0d139089cd Mon Sep 17 00:00:00 2001 From: Plastikmensch Date: Wed, 12 Apr 2023 20:12:25 +0200 Subject: Fix default content type setting not applying (#2176) Signed-off-by: Plastikmensch --- app/views/settings/preferences/other/show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/views/settings') diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml index ebb89f44b..6420b6634 100644 --- a/app/views/settings/preferences/other/show.html.haml +++ b/app/views/settings/preferences/other/show.html.haml @@ -33,8 +33,8 @@ .fields-group = ff.input :show_application, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_show_application'), hint: I18n.t('simple_form.hints.defaults.setting_show_application') - .fields-group - = f.input :setting_default_content_type, collection: ['text/plain', 'text/markdown', 'text/html'], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.setting_default_content_type_#{item.split('/')[1]}"), content_tag(:span, t("simple_form.hints.defaults.setting_default_content_type_#{item.split('/')[1]}"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', glitch_only: true + .fields-group + = ff.input :default_content_type, collection: ['text/plain', 'text/markdown', 'text/html'], wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_content_type'), include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.setting_default_content_type_#{item.split('/')[1]}"), content_tag(:span, t("simple_form.hints.defaults.setting_default_content_type_#{item.split('/')[1]}"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', glitch_only: true %h4= t 'preferences.public_timelines' -- cgit