diff --git a/app/controllers/admin/rules_controller.rb b/app/controllers/admin/rules_controller.rb index 837eb91489..b9331e7e98 100644 --- a/app/controllers/admin/rules_controller.rb +++ b/app/controllers/admin/rules_controller.rb @@ -50,6 +50,22 @@ module Admin redirect_to admin_rules_path end + def move_up + authorize @rule, :update? + + @rule.move!(-1) + + redirect_to admin_rules_path + end + + def move_down + authorize @rule, :update? + + @rule.move!(+1) + + redirect_to admin_rules_path + end + private def set_rule diff --git a/app/javascript/material-icons/400-24px/arrow_downward-fill.svg b/app/javascript/material-icons/400-24px/arrow_downward-fill.svg new file mode 100644 index 0000000000..a9157219ca --- /dev/null +++ b/app/javascript/material-icons/400-24px/arrow_downward-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/arrow_downward.svg b/app/javascript/material-icons/400-24px/arrow_downward.svg new file mode 100644 index 0000000000..a9157219ca --- /dev/null +++ b/app/javascript/material-icons/400-24px/arrow_downward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/arrow_upward-fill.svg b/app/javascript/material-icons/400-24px/arrow_upward-fill.svg new file mode 100644 index 0000000000..1ec54b0702 --- /dev/null +++ b/app/javascript/material-icons/400-24px/arrow_upward-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/arrow_upward.svg b/app/javascript/material-icons/400-24px/arrow_upward.svg new file mode 100644 index 0000000000..1ec54b0702 --- /dev/null +++ b/app/javascript/material-icons/400-24px/arrow_upward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 4c75231b5c..962fd2d20e 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -1127,6 +1127,15 @@ a.name-tag, } } +.rule-actions { + display: flex; + flex-direction: column; + + a.table-action-link { + padding-inline-start: 0; + } +} + .dashboard__counters.admin-account-counters { margin-top: 10px; } diff --git a/app/models/rule.rb b/app/models/rule.rb index 99a36397aa..3033a2b03d 100644 --- a/app/models/rule.rb +++ b/app/models/rule.rb @@ -22,4 +22,18 @@ class Rule < ApplicationRecord validates :text, presence: true, length: { maximum: TEXT_SIZE_LIMIT } scope :ordered, -> { kept.order(priority: :asc, id: :asc) } + + def move!(offset) + rules = Rule.ordered.to_a + position = rules.index(self) + + rules.delete_at(position) + rules.insert(position + offset, self) + + transaction do + rules.each.with_index do |rule, index| + rule.update!(priority: index) + end + end + end end diff --git a/app/views/admin/rules/_rule.html.haml b/app/views/admin/rules/_rule.html.haml index eb97eefb3c..7d84534d59 100644 --- a/app/views/admin/rules/_rule.html.haml +++ b/app/views/admin/rules/_rule.html.haml @@ -7,5 +7,7 @@ .announcements-list__item__meta = rule.hint - %div + .rule-actions + = table_link_to 'arrow_upward', t('admin.rules.move_up'), move_up_admin_rule_path(rule), method: :post if can?(:update, rule) && !rule_iteration.first? = table_link_to 'delete', t('admin.rules.delete'), admin_rule_path(rule), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:destroy, rule) + = table_link_to 'arrow_downward', t('admin.rules.move_down'), move_down_admin_rule_path(rule), method: :post if can?(:update, rule) && !rule_iteration.last? diff --git a/config/locales/en.yml b/config/locales/en.yml index 29b4f642a1..dad46d9d51 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -790,6 +790,8 @@ en: description_html: While most claim to have read and agree to the terms of service, usually people do not read through until after a problem arises. Make it easier to see your server's rules at a glance by providing them in a flat bullet point list. Try to keep individual rules short and simple, but try not to split them up into many separate items either. edit: Edit rule empty: No server rules have been defined yet. + move_down: Move down + move_up: Move up title: Server rules settings: about: diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 2f7b86162b..e7439f66b7 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -93,7 +93,12 @@ namespace :admin do end end - resources :rules, only: [:index, :new, :create, :edit, :update, :destroy] + resources :rules, only: [:index, :new, :create, :edit, :update, :destroy] do + member do + post :move_up + post :move_down + end + end resources :webhooks do member do diff --git a/spec/models/rule_spec.rb b/spec/models/rule_spec.rb index 375483e0db..a7fc5ef693 100644 --- a/spec/models/rule_spec.rb +++ b/spec/models/rule_spec.rb @@ -16,4 +16,24 @@ RSpec.describe Rule do end end end + + describe '#move!' do + let!(:first_rule) { Fabricate(:rule, text: 'foo') } + let!(:second_rule) { Fabricate(:rule, text: 'bar') } + let!(:third_rule) { Fabricate(:rule, text: 'baz') } + + it 'moves the rules as expected' do + expect { first_rule.move!(+1) } + .to change { described_class.ordered.pluck(:text) }.from(%w(foo bar baz)).to(%w(bar foo baz)) + + expect { first_rule.move!(-1) } + .to change { described_class.ordered.pluck(:text) }.from(%w(bar foo baz)).to(%w(foo bar baz)) + + expect { third_rule.move!(-1) } + .to change { described_class.ordered.pluck(:text) }.from(%w(foo bar baz)).to(%w(foo baz bar)) + + expect { second_rule.move!(-1) } + .to change { described_class.ordered.pluck(:text) }.from(%w(foo baz bar)).to(%w(foo bar baz)) + end + end end diff --git a/spec/system/admin/rules_spec.rb b/spec/system/admin/rules_spec.rb index afe9e87a52..dca6323f6c 100644 --- a/spec/system/admin/rules_spec.rb +++ b/spec/system/admin/rules_spec.rb @@ -48,6 +48,29 @@ RSpec.describe 'Admin Rules' do end end + describe 'Moving down an existing rule' do + let!(:first_rule) { Fabricate(:rule, text: 'This is another rule') } + let!(:second_rule) { Fabricate(:rule, text: 'This is a rule') } + + it 'moves the rule down' do + visit admin_rules_path + + expect(page) + .to have_content(I18n.t('admin.rules.title')) + + expect(Rule.ordered.pluck(:text)).to eq ['This is another rule', 'This is a rule'] + + click_on(I18n.t('admin.rules.move_down')) + + expect(page) + .to have_content(I18n.t('admin.rules.title')) + .and have_content(first_rule.text) + .and have_content(second_rule.text) + + expect(Rule.ordered.pluck(:text)).to eq ['This is a rule', 'This is another rule'] + end + end + describe 'Editing an existing rule' do let!(:rule) { Fabricate :rule, text: 'Rule text' }