diff --git a/app/controllers/admin/tags/moderators_controller.rb b/app/controllers/admin/tags/moderators_controller.rb
new file mode 100644
index 000000000..f114405c9
--- /dev/null
+++ b/app/controllers/admin/tags/moderators_controller.rb
@@ -0,0 +1,44 @@
+module Admin
+ module Tags
+ class ModeratorsController < Admin::ApplicationController
+ def authorization_resource
+ Tag
+ end
+
+ after_action only: %i[create destroy] do
+ Audit::Logger.log(:moderator, current_user, params.dup)
+ end
+
+ def create
+ user = User.find_by(id: tag_params[:user_id])
+ if user&.update(email_tag_mod_newsletter: true)
+ AssignTagModerator.add_tag_moderators([user.id], [params[:tag_id]])
+ flash[:success] = "#{user.username} was added as a tag moderator!"
+ else
+ flash[:error] = "Error: User ID ##{tag_params[:user_id]} was not found,
+ or their account has errors: #{user&.errors_as_sentence}"
+ end
+ redirect_to admin_tag_path(params[:tag_id])
+ end
+
+ def destroy
+ user = User.find_by(id: tag_params[:user_id])
+ tag = Tag.find_by(id: params[:tag_id])
+ if user&.update(email_tag_mod_newsletter: false)
+ AssignTagModerator.remove_tag_moderator(user, tag)
+ flash[:success] = "@#{user.username} - ID ##{user.id} was removed as a tag moderator."
+ else
+ flash[:error] = "Error: User ID ##{tag_params[:user_id]} was not found,
+ or their account has errors: #{user&.errors_as_sentence}"
+ end
+ redirect_to admin_tag_path(tag.id)
+ end
+
+ private
+
+ def tag_params
+ params.require(:tag).permit(:user_id)
+ end
+ end
+ end
+end
diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb
index 2abc1b943..9745b50a1 100644
--- a/app/controllers/admin/tags_controller.rb
+++ b/app/controllers/admin/tags_controller.rb
@@ -20,37 +20,27 @@ module Admin
def show
@tag = Tag.find(params[:id])
+ @tag_moderators = User.with_role(:tag_moderator, @tag).select(:id, :username)
+ @badges_for_options = Badge.pluck(:title, :id)
end
def update
@tag = Tag.find(params[:id])
- @add_user_id = params[:tag][:tag_moderator_id]
- @remove_user_id = params[:tag][:remove_moderator_id]
- add_moderator if @add_user_id
- remove_moderator if @remove_user_id
- @tag.update!(tag_params)
-
- redirect_to "/admin/tags/#{params[:id]}"
+ if @tag.update(tag_params)
+ flash[:success] = "#{@tag.name} tag successfully updated!"
+ else
+ flash[:error] = "The tag update failed: #{@tag.errors_as_sentence}"
+ end
+ redirect_to admin_tag_path(@tag.id)
end
private
- def remove_moderator
- user = User.find(@remove_user_id)
- user.update(email_tag_mod_newsletter: false)
- AssignTagModerator.remove_tag_moderator(user, @tag)
- end
-
- def add_moderator
- User.find(@add_user_id).update(email_tag_mod_newsletter: true)
- AssignTagModerator.add_tag_moderators([@add_user_id], [@tag.id])
- end
-
def tag_params
allowed_params = %i[
- supported rules_markdown short_summary pretty_name bg_color_hex
- text_color_hex tag_moderator_id remove_moderator_id alias_for badge_id
- category social_preview_template
+ id supported rules_markdown short_summary pretty_name bg_color_hex
+ text_color_hex user_id alias_for badge_id
+ category social_preview_template wiki_body_markdown
]
params.require(:tag).permit(allowed_params)
end
diff --git a/app/labor/assign_tag_moderator.rb b/app/labor/assign_tag_moderator.rb
index 087856a3b..500534883 100644
--- a/app/labor/assign_tag_moderator.rb
+++ b/app/labor/assign_tag_moderator.rb
@@ -3,9 +3,9 @@ module AssignTagModerator
return if user.has_role?(:trusted)
return if user.has_role?(:banned)
- user.add_role :trusted
+ user.add_role(:trusted)
user.update(email_community_mod_newsletter: true)
- MailchimpBot.new(user).manage_community_moderator_list
+ MailchimpBot.new(user).manage_community_moderator_list if community_mod_newsletter_enabled?
Rails.cache.delete("user-#{user.id}/has_trusted_role")
NotifyMailer.with(user: user).trusted_role_email.deliver_now
end
@@ -47,17 +47,25 @@ module AssignTagModerator
user.update(email_tag_mod_newsletter: true) if user.email_tag_mod_newsletter == false
user.add_role(:tag_moderator, tag)
Rails.cache.delete("user-#{user.id}/tag_moderators_list")
- MailchimpBot.new(user).manage_tag_moderator_list
+ MailchimpBot.new(user).manage_tag_moderator_list if tag_mod_newsletter_enabled?
end
def self.remove_tag_moderator(user, tag)
user.remove_role(:tag_moderator, tag)
user.update(email_tag_mod_newsletter: false) if user.email_tag_mod_newsletter == true
Rails.cache.delete("user-#{user.id}/tag_moderators_list")
- MailchimpBot.new(user).manage_tag_moderator_list
+ MailchimpBot.new(user).manage_tag_moderator_list if tag_mod_newsletter_enabled?
end
def self.chat_channel_slug(tag)
tag.mod_chat_channel&.slug
end
+
+ def self.community_mod_newsletter_enabled?
+ SiteConfig.mailchimp_api_key.present? && SiteConfig.mailchimp_community_moderators_id.present?
+ end
+
+ def self.tag_mod_newsletter_enabled?
+ SiteConfig.mailchimp_api_key.present? && SiteConfig.mailchimp_tag_moderators_id.present?
+ end
end
diff --git a/app/models/tag.rb b/app/models/tag.rb
index b399202dd..57ce967b0 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -89,6 +89,10 @@ class Tag < ActsAsTaggableOn::Tag
errors.add(:name, "contains non-ASCII characters") unless name.match?(/\A[[a-z0-9]]+\z/i)
end
+ def errors_as_sentence
+ errors.full_messages.to_sentence
+ end
+
private
def evaluate_markdown
diff --git a/app/views/admin/tags/_form.html.erb b/app/views/admin/tags/_form.html.erb
new file mode 100644
index 000000000..6061fc1b1
--- /dev/null
+++ b/app/views/admin/tags/_form.html.erb
@@ -0,0 +1,57 @@
+
+
Edit details
+ <%= form_with model: [:admin, tag], data: { remote: false } do |f| %>
+
+ <%= f.check_box :supported, class: "crayons-checkbox" %>
+
+ Supported
+ Allows the tag to be a searchable result when writing a post or a listing
+
+
+
+ <%= f.label :badge_id, class: "crayons-field__label" %>
+ <%= f.select(:badge_id, options_for_select(badges_for_options, tag.badge_id), { include_blank: true }, { class: "crayons-select" }) %>
+
+
+ <%= f.label :category, class: "crayons-field__label" %>
+ <%= f.select(:category, options_for_select(Tag.valid_categories, tag.category), {}, { class: "crayons-select" }) %>
+
+
+ <%= f.label :social_preview_template, class: "crayons-field__label" %>
+ <%= f.select(:social_preview_template, Tag.social_preview_templates, {}, { class: "crayons-select" }) %>
+
+
+ <%= f.label :alias_for, class: "crayons-field__label" %>
+ <%= f.text_field :alias_for, value: tag.alias_for, class: "crayons-textfield" %>
+
+
+ <%= f.label :pretty_name, class: "crayons-field__label" %>
+ <%= f.text_field :pretty_name, value: tag.pretty_name, class: "crayons-textfield" %>
+
+
+ <%= f.label :short_summary, class: "crayons-field__label" %>
+ <%= f.text_field :short_summary, value: tag.short_summary, class: "crayons-textfield" %>
+
+
+ <%= f.label :rules_markdown, class: "crayons-field__label" %>
+
+ <%= f.text_area :rules_markdown, value: tag.rules_markdown, class: "crayons-textfield" %>
+
+
+ <%= f.label :wiki_body_markdown, class: "crayons-field__label" %>
+
+ <%= f.text_area :wiki_body_markdown, value: tag.wiki_body_markdown, class: "crayons-textfield" %>
+
+ <%= f.label :bg_color_hex, class: "crayons-field__label" %>
+
+ <%= f.text_field :bg_color_hex, class: "crayons-textfield", value: tag.bg_color_hex.presence || "#000000", placeholder: "#000000" %>
+ <%= f.color_field :bg_color_hex, class: "crayons-color-selector ml-2", required: true, value: tag.bg_color_hex.presence || "#000000", placeholder: "#000000" %>
+
+ <%= f.label :text_color_hex, class: "crayons-field__label" %>
+
+ <%= f.text_field :text_color_hex, class: "crayons-textfield", value: tag.text_color_hex.presence || "#ffffff", placeholder: "#ffffff" %>
+ <%= f.color_field :text_color_hex, class: "crayons-color-selector ml-2", required: true, value: tag.text_color_hex.presence || "#ffffff", placeholder: "#ffffff" %>
+
+ <%= f.submit class: "crayons-btn mt-2" %>
+ <% end %>
+
diff --git a/app/views/admin/tags/show.html.erb b/app/views/admin/tags/show.html.erb
index 0423973c1..018402b1a 100644
--- a/app/views/admin/tags/show.html.erb
+++ b/app/views/admin/tags/show.html.erb
@@ -11,84 +11,35 @@
-
Moderators
- <% if @tag.tag_moderator_ids.any? %>
-
- <% @tag.tag_moderator_ids.each do |id| %>
-
- @<%= User.find(id).username %>
- <%= form_for [:admin, @tag] do |f| %>
- <%= f.hidden_field :remove_moderator_id, value: id %>
- <%= f.submit "Remove", class: "btn btn-danger btn-sm" %>
- <% end %>
-
- <% end %>
-
- <% else %>
-
This tag currently has no moderators.
- <% end %>
- <%= form_for [:admin, @tag], html: { class: "form-inline justify-content-between" } do |f| %>
-
- <%= f.label :tag_moderator_id, "Add Moderator (id):", class: "mr-3" %>
- <%= f.text_field :tag_moderator_id, class: "form-control" %>
-
-
- <%= f.submit "Add Moderator", class: "btn btn-primary" %>
-
- <% end %>
-
-
-
-
Edit details
- <%= form_for [:admin, @tag] do |f| %>
-
- <%= f.label :supported %>
- <%= f.check_box :supported %>
-
-
- <%= f.label :badge_id %>
- <% badges_array = Badge.pluck(:title, :id) %>
- <%= f.select(:badge_id, options_for_select([["None", nil]] + badges_array, @tag.badge_id)) %>
-
-
- <%= f.label :category %>
- <%= f.select(:category, options_for_select(Tag.valid_categories, @tag.category)) %>
-
-
- <%= f.label :social_preview_template %>
- <%= f.select(:social_preview_template, Tag.social_preview_templates) %>
-
-
- <%= f.label :alias_for %>
- <%= f.text_field :alias_for, class: "form-control" %>
-
-
- <%= f.label :pretty_name %>
- <%= f.text_field :pretty_name, class: "form-control" %>
-
-
- <%= f.label :short_summary %>
- <%= f.text_field :short_summary, class: "form-control" %>
-
-
- <%= f.label :rules_markdown %>
-
- <%= f.text_area :rules_markdown, class: "form-control" %>
-
-
- <%= f.label :wiki_body_markdown %>
-
- <%= f.text_area :wiki_body_markdown, class: "form-control" %>
-
-
- <%= f.label :bg_color_hex %>
- <%= f.color_field :bg_color_hex, class: "form-control", required: true %>
-
-
- <%= f.label :text_color_hex %>
- <%= f.color_field :text_color_hex, class: "form-control", required: true, value: @tag.text_color_hex.presence || "#ffffff" %>
-
- <%= f.submit class: "btn btn-primary float-right" %>
- <% end %>
+
+
+ <% if @tag_moderators.exists? %>
+
+ <% @tag_moderators.each do |user| %>
+
+ @<%= user.username %>
+ <%= form_with url: admin_tag_moderator_path(@tag.id), model: [:admin, @tag], method: :delete, data: { remote: false } do |f| %>
+ <%= f.hidden_field :user_id, value: user.id %>
+ <%= f.submit "Remove", class: "crayons-btn crayons-btn--danger crayons-btn--s" %>
+ <% end %>
+
+ <% end %>
+
+ <% else %>
+
+ This tag currently has no moderators.
+
+ <% end %>
+ <%= form_with url: admin_tag_moderator_path(@tag.id), model: [:admin, @tag], method: :post, data: { remote: false } do |f| %>
+
+ <%= f.label :user_id, "Add Moderator (ID):", class: "crayons-field__label" %>
+ <%= f.text_field :user_id, class: "crayons-textfield" %>
+ <%= f.submit "Add Moderator", class: "crayons-btn" %>
+
+ <% end %>
+
+ <%= render "form", tag: @tag, badges_for_options: @badges_for_options %>
diff --git a/config/routes.rb b/config/routes.rb
index 6f9d4e5c8..66052ad39 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -103,7 +103,9 @@ Rails.application.routes.draw do
post "save_status"
end
end
- resources :tags, only: %i[index update show]
+ resources :tags, only: %i[index update show] do
+ resource :moderator, only: %i[create destroy], module: "tags"
+ end
resources :users, only: %i[index show edit update] do
member do
post "banish"
diff --git a/spec/requests/admin/tags/moderators_spec.rb b/spec/requests/admin/tags/moderators_spec.rb
new file mode 100644
index 000000000..ae754cb48
--- /dev/null
+++ b/spec/requests/admin/tags/moderators_spec.rb
@@ -0,0 +1,35 @@
+require "rails_helper"
+
+RSpec.describe "/admin/tags/:id/moderator", type: :request do
+ let(:super_admin) { create(:user, :super_admin) }
+ let(:user) { create(:user) }
+ let(:tag) { create(:tag) }
+
+ describe "POST /admin/tags/:id/moderator" do
+ before { sign_in super_admin }
+
+ it "adds the given user as trusted and as a tag moderator" do
+ post admin_tag_moderator_path(tag.id), params: { tag_id: tag.id, tag: { user_id: user.id } }
+ expect(user.tag_moderator?).to be true
+ expect(user.trusted).to be true
+ end
+ end
+
+ describe "DELETE /admin/tags/:id/moderator" do
+ before do
+ sign_in super_admin
+ user.add_role :trusted
+ user.add_role :tag_moderator, tag
+ end
+
+ it "removes the tag moderator role from the user" do
+ delete admin_tag_moderator_path(tag.id), params: { tag_id: tag.id, tag: { user_id: user.id } }
+ expect(user.tag_moderator?).to be false
+ end
+
+ it "does not remove the trusted role from the user" do
+ delete admin_tag_moderator_path(tag.id), params: { tag_id: tag.id, tag: { user_id: user.id } }
+ expect(user.trusted).to be true
+ end
+ end
+end
diff --git a/spec/requests/admin/tags_spec.rb b/spec/requests/admin/tags_spec.rb
index ee9a72f5c..7c2860b72 100644
--- a/spec/requests/admin/tags_spec.rb
+++ b/spec/requests/admin/tags_spec.rb
@@ -2,23 +2,23 @@ require "rails_helper"
RSpec.describe "/admin/tags", type: :request do
let(:super_admin) { create(:user, :super_admin) }
- let(:tag) { create(:tag) }
+ let(:user) { create(:user) }
+ let!(:tag) { create(:tag) }
before do
- tag
sign_in super_admin
end
describe "GET /admin/tags" do
it "responds with 200 OK" do
- get "/admin/tags"
+ get admin_tags_path
expect(response.status).to eq 200
end
end
describe "GET /admin/tags/:id" do
it "responds with 200 OK" do
- get "/admin/tags/#{tag.id}"
+ get admin_tag_path(tag.id)
expect(response.status).to eq 200
end
end