diff --git a/app/controllers/response_templates_controller.rb b/app/controllers/response_templates_controller.rb index 826474afa..60f6ffc15 100644 --- a/app/controllers/response_templates_controller.rb +++ b/app/controllers/response_templates_controller.rb @@ -31,9 +31,12 @@ class ResponseTemplatesController < ApplicationController def create authorize ResponseTemplate - response_template.user_id = current_user.id + + unless tries_to_create_a_mod_response_template? && can_create_mod_response_templates? + response_template.user_id = current_user.id + response_template.type_of = "personal_comment" + end response_template.content_type = "body_markdown" - response_template.type_of = "personal_comment" if response_template.save flash[:settings_notice] = @@ -87,6 +90,18 @@ class ResponseTemplatesController < ApplicationController private + def authorized_user + @authorized_user ||= Authorizer.for(user: current_user) + end + + def can_create_mod_response_templates? + authorized_user.accesses_mod_response_templates? + end + + def tries_to_create_a_mod_response_template? + params[:response_template][:type_of] == "mod_comment" + end + def response_template @response_template ||= if params[:id].present? ResponseTemplate.find(params[:id]) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 4b35593bc..d4495ea8e 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -218,8 +218,6 @@ class UsersController < ApplicationController def signout_confirm; end def handle_settings_tab - return @tab = "profile" if @tab.blank? - case @tab when "profile" handle_integrations_tab @@ -302,8 +300,10 @@ class UsersController < ApplicationController end def handle_response_templates_tab - @response_templates = current_user.response_templates - @response_template = ResponseTemplate.find_or_initialize_by(id: params[:id], user: current_user) + @personal_response_templates = current_user.response_templates + @trusted_response_templates = policy_scope(ResponseTemplate).where(type_of: "mod_comment") + @response_template = policy_scope(ResponseTemplate).find_by(id: params[:id]) || + ResponseTemplate.new end def set_user diff --git a/app/policies/authorizer.rb b/app/policies/authorizer.rb index 7ad3ee3ef..9d987974c 100644 --- a/app/policies/authorizer.rb +++ b/app/policies/authorizer.rb @@ -75,6 +75,10 @@ module Authorizer has_role?(:creator) end + def accesses_mod_response_templates? + has_trusted_role? || any_admin? || moderator? || tag_moderator? + end + # When you need to know if we trust the user, but don't want to # have stale information that the `trusted?` method might give # you. diff --git a/app/policies/response_template_policy.rb b/app/policies/response_template_policy.rb index 65e3222f1..d10711ed7 100644 --- a/app/policies/response_template_policy.rb +++ b/app/policies/response_template_policy.rb @@ -3,8 +3,8 @@ class ResponseTemplatePolicy < ApplicationPolicy class Scope < Scope def resolve - if user.has_trusted_role? || user.any_admin? || user.moderator? || user.tag_moderator? - scope.where(user: user, type_of: "personal_comment") + scope.where.not(type_of: "personal_comment") + if Authorizer.for(user: user).accesses_mod_response_templates? + scope.where(user: user, type_of: "personal_comment").or(scope.where.not(type_of: "personal_comment")) else scope.where(user: user, type_of: "personal_comment") end @@ -30,14 +30,21 @@ class ResponseTemplatePolicy < ApplicationPolicy user_moderator? && mod_comment? end - def destroy? + def modify? + return true if mod_comment? && user_trusted? + user_owner? end - alias update? destroy? + alias update? modify? + alias destroy? modify? def permitted_attributes_for_create - PERMITTED_ATTRIBUTES + if user_trusted? + PERMITTED_ATTRIBUTES + [:type_of] + else + PERMITTED_ATTRIBUTES + end end def permitted_attributes_for_update @@ -50,6 +57,10 @@ class ResponseTemplatePolicy < ApplicationPolicy user.id == record.user_id end + def user_trusted? + Authorizer.for(user: user).accesses_mod_response_templates? + end + def user_moderator? user_any_admin? || user.moderator_for_tags&.present? end diff --git a/app/views/users/_response_template.html.erb b/app/views/users/_response_template.html.erb new file mode 100644 index 000000000..61e7e52c2 --- /dev/null +++ b/app/views/users/_response_template.html.erb @@ -0,0 +1,13 @@ +
mb-4"> +
+
+ <%= response_template.title %> +
+
+ <%= form_with url: response_template_path(response_template.id), method: :delete, local: true, html: { onsubmit: "return confirm('#{t('views.settings.extensions.comment.confirm', template: response_template.title)}');" } do %> + + <% end %> + <%= t("views.settings.extensions.comment.edit") %> +
+
+
diff --git a/app/views/users/_response_templates.html.erb b/app/views/users/_response_templates.html.erb index 7e9da0fc5..8a4362b52 100644 --- a/app/views/users/_response_templates.html.erb +++ b/app/views/users/_response_templates.html.erb @@ -7,28 +7,23 @@

- <% if @response_templates.present? %> + <% if @personal_response_templates.present? %>

<%= t("views.settings.extensions.comment.saved") %>

- <% @response_templates.each do |response_template| %> -
mb-4"> -
-
- <%= response_template.title %> -
-
- <%= form_with url: response_template_path(response_template.id), method: :delete, local: true, html: { onsubmit: "return confirm('#{t('views.settings.extensions.comment.confirm', template: response_template.title)}');" } do %> - - <% end %> - <%= t("views.settings.extensions.comment.edit") %> -
-
-
- <% end %> -
- <% end %> + <%= render partial: "response_template", collection: @personal_response_templates %> + + <% end %> + + <% if @trusted_response_templates.present? %> +
+

+ <%= t("views.settings.extensions.comment.trusted") %> +

+ <%= render partial: "response_template", collection: @trusted_response_templates %> +
+ <% end %> <%= form_with model: @response_template do |f| %>
@@ -60,6 +55,22 @@ <%= f.text_area :content, value: content, class: "crayons-textfield" %> + <% if current_user.has_trusted_role? && @response_template.new_record? %> +
+
+ Template type + <%= f.radio_button :type_of, "personal_comment", + checked: @response_template.type_of != "mod_comment", + class: "crayons-radio", role: "radio" %> + <%= label_tag "response_template_type_of_personal_comment", "Personal" %> + + <%= f.radio_button :type_of, "mod_comment", + class: "crayons-radio", role: "radio" %> + <%= label_tag "response_template_type_of_mod_comment", "Trusted User" %> +
+
+ <% end %> +
diff --git a/config/locales/views/settings/en.yml b/config/locales/views/settings/en.yml index 9fe54d522..599857073 100644 --- a/config/locales/views/settings/en.yml +++ b/config/locales/views/settings/en.yml @@ -130,6 +130,7 @@ en: remove: Remove save: Save template saved: Saved templates + trusted: Trusted user templates github: heading: GitHub desc1: Pin your GitHub repositories to your profile. diff --git a/spec/policies/response_template_policy_spec.rb b/spec/policies/response_template_policy_spec.rb index 7c2c0c230..9711d889b 100644 --- a/spec/policies/response_template_policy_spec.rb +++ b/spec/policies/response_template_policy_spec.rb @@ -39,15 +39,14 @@ RSpec.describe ResponseTemplatePolicy, type: :policy do let(:user) { create(:user, :tag_moderator) } let(:response_template) { create(:response_template, type_of: "mod_comment", user: nil) } - it { is_expected.to permit_actions(%i[moderator_index create moderator_create]) } - it { is_expected.to forbid_actions(%i[update destroy admin_index]) } + it { is_expected.to permit_actions(%i[moderator_index create moderator_create update destroy]) } + it { is_expected.to forbid_actions(%i[admin_index]) } end context "when user is an admin" do let(:user) { create(:user, :admin) } let(:response_template) { create(:response_template, type_of: "mod_comment", user: nil) } - it { is_expected.to permit_actions(%i[moderator_index create moderator_create admin_index]) } - it { is_expected.to forbid_actions(%i[update destroy]) } + it { is_expected.to permit_actions(%i[moderator_index create moderator_create admin_index update destroy]) } end end diff --git a/spec/requests/response_templates_spec.rb b/spec/requests/response_templates_spec.rb index 1e710d753..e4842cd20 100644 --- a/spec/requests/response_templates_spec.rb +++ b/spec/requests/response_templates_spec.rb @@ -2,6 +2,7 @@ require "rails_helper" RSpec.describe "ResponseTemplate", type: :request do let(:user) { create(:user) } + let(:trusted_user) { create(:user, :trusted) } let(:moderator) { create(:user, :tag_moderator) } let(:admin) { create(:user, :admin) } @@ -30,7 +31,6 @@ RSpec.describe "ResponseTemplate", type: :request do headers = { HTTP_ACCEPT: "application/json" } get response_templates_path, params: { type_of: "personal_comment" }, headers: headers - expect(response.parsed_body.class).to eq Array expect(response.parsed_body.length).to eq total_response_templates end @@ -120,65 +120,155 @@ RSpec.describe "ResponseTemplate", type: :request do end describe "POST /response_templates #create" do - before { sign_in user } + context "when signed in as normal user" do + before { sign_in user } - let(:attributes) do - { - title: "some_title", - content: "some content", - type_of: "personal_comment" - } + let(:attributes) do + { + title: "some_title", + content: "some content", + type_of: "personal_comment" + } + end + + it "successfully creates the proper response template" do + post response_templates_path, params: { + response_template: { + title: attributes[:title], + content: attributes[:content] + } + } + + response_template = ResponseTemplate.last + expect(response_template.user_id).to eq user.id + expect(response_template.title).to eq attributes[:title] + expect(response_template.content).to eq attributes[:content] + expect(response_template.type_of).to eq attributes[:type_of] + end + + it "can only create personal response templates" do + post response_templates_path, params: { + response_template: { + title: attributes[:title], + content: attributes[:content], + type_of: "mod_comment" + } + } + + response_template = ResponseTemplate.last + expect(response_template.type_of).to eq "personal_comment" + end + + it "redirects to the edit page upon success" do + post response_templates_path, params: { + response_template: { + title: attributes[:title], + content: attributes[:content] + } + } + expect(response.redirect_url).to include user_settings_path(tab: "response-templates", + id: ResponseTemplate.last.id) + end end - it "successfully creates the proper response template" do - post response_templates_path, params: { - response_template: { - title: attributes[:title], - content: attributes[:content] - } - } + context "when signed in as trusted user" do + before { sign_in trusted_user } - response_template = ResponseTemplate.last - expect(response_template.user_id).to eq user.id - expect(response_template.title).to eq attributes[:title] - expect(response_template.content).to eq attributes[:content] - expect(response_template.type_of).to eq attributes[:type_of] - end - - it "redirects to the edit page upon success" do - post response_templates_path, params: { - response_template: { - title: attributes[:title], - content: attributes[:content] + let(:attributes) do + { + title: "some_title", + content: "some content", + type_of: "mod_comment" } - } - expect(response.redirect_url).to include user_settings_path(tab: "response-templates", - id: ResponseTemplate.last.id) + end + + it "successfully creates a personal response template" do + post response_templates_path, params: { + response_template: { + title: attributes[:title], + content: attributes[:content] + } + } + + response_template = ResponseTemplate.last + expect(response_template.user_id).to eq trusted_user.id + expect(response_template.title).to eq attributes[:title] + expect(response_template.content).to eq attributes[:content] + expect(response_template.type_of).to eq "personal_comment" + end + + it "successfully creates a mod_comment response template" do + post response_templates_path, params: { + response_template: { + title: attributes[:title], + content: attributes[:content], + type_of: attributes[:type_of] + } + } + + response_template = ResponseTemplate.last + expect(response_template.user_id).to be_nil + expect(response_template.title).to eq attributes[:title] + expect(response_template.content).to eq attributes[:content] + expect(response_template.type_of).to eq "mod_comment" + end + + it "redirects to the edit page upon success" do + post response_templates_path, params: { + response_template: { + title: attributes[:title], + content: attributes[:content] + } + } + expect(response.redirect_url).to include user_settings_path(tab: "response-templates", + id: ResponseTemplate.last.id) + end end end describe "PATCH /response_templates/:id #update" do - before { sign_in user } + context "when signed-in as normal user updating a personal template" do + before { sign_in user } - let(:response_template) { create(:response_template, user: user) } + let(:response_template) { create(:response_template, user: user) } - it "successfully updates the response template" do - title = "something else" - patch response_template_path(response_template.id), params: { response_template: { title: title } } - expect(ResponseTemplate.first.title).to eq title + it "successfully updates the response template" do + title = "something else" + patch response_template_path(response_template.id), params: { response_template: { title: title } } + expect(ResponseTemplate.first.title).to eq title + end + + it "redirects back to the response template" do + patch response_template_path(response_template.id), params: { response_template: { title: "something else" } } + expect(response.redirect_url).to include user_settings_path(tab: "response-templates", + id: ResponseTemplate.first.id) + end + + it "shows the previously written content on a failed submission" do + content = "something something something" + patch response_template_path(response_template.id), + params: { response_template: { title: "", content: content } } + follow_redirect! + expect(response.body).to include content + end end - it "redirects back to the response template" do - patch response_template_path(response_template.id), params: { response_template: { title: "something else" } } - expect(response.redirect_url).to include user_settings_path(tab: "response-templates", - id: ResponseTemplate.first.id) - end + context "when signed-in as trusted user updating a mod_comment template" do + before { sign_in trusted_user } - it "shows the previously written content on a failed submission" do - content = "something something something" - patch response_template_path(response_template.id), params: { response_template: { title: "", content: content } } - follow_redirect! - expect(response.body).to include content + let(:response_template) { create :response_template, user: nil, type_of: "mod_comment" } + + it "successfully updates the response template" do + title = "something else" + patch response_template_path(response_template.id), params: { response_template: { title: title } } + expect(ResponseTemplate.first.title).to eq title + end + + it "does not permit changing template type_of" do + patch response_template_path(response_template.id), + params: { response_template: { type_of: "personal_comment" } } + expect(ResponseTemplate.first.type_of).to eq("mod_comment") + end end end