diff --git a/app/assets/stylesheets/request-for-comments.css.scss b/app/assets/stylesheets/request-for-comments.css.scss index 2f14fd537..a3dd74b69 100644 --- a/app/assets/stylesheets/request-for-comments.css.scss +++ b/app/assets/stylesheets/request-for-comments.css.scss @@ -212,6 +212,10 @@ html[data-bs-theme="light"] { button { margin-right: 5px; } + + .action-report { + margin-left: auto; + } } } } diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 8835a8dee..768bb2cc6 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class CommentsController < ApplicationController - before_action :set_comment, only: %i[show update destroy] + before_action :set_comment, only: %i[show update destroy report] def authorize! authorize(@comment || @comments) @@ -15,12 +15,6 @@ def index submission = Submission.find_by(id: file.context_id) if submission @comments = Comment.where(file_id: params[:file_id]) - @comments.map do |comment| - comment.username = comment.user.displayname - comment.date = comment.created_at.strftime('%d.%m.%Y %k:%M') - comment.updated = (comment.created_at != comment.updated_at) - comment.editable = policy(comment).edit? - end else @comments = [] end @@ -67,6 +61,15 @@ def destroy head :no_content end + # POST /comments/1/report.json + def report + authorize! + + UserContentReportMailer.with(reported_content: @comment).report_content.deliver_later + + head :no_content + end + private # Use callbacks to share common setup or constraints between actions. diff --git a/app/controllers/request_for_comments_controller.rb b/app/controllers/request_for_comments_controller.rb index 76d5fd294..c0c94bde4 100644 --- a/app/controllers/request_for_comments_controller.rb +++ b/app/controllers/request_for_comments_controller.rb @@ -167,9 +167,9 @@ def create def report authorize! - ReportMailer.with(reported_content: @request_for_comment).report_content.deliver_later + UserContentReportMailer.with(reported_content: @request_for_comment).report_content.deliver_later - redirect_to @request_for_comment, notice: t('.report.reported'), status: :see_other + redirect_to @request_for_comment, notice: t('.reported'), status: :see_other end private diff --git a/app/javascript/sprocket-asset-import/request-for-comments.js b/app/javascript/sprocket-asset-import/request-for-comments.js index fbad1fdc9..a6e1e424d 100644 --- a/app/javascript/sprocket-asset-import/request-for-comments.js +++ b/app/javascript/sprocket-asset-import/request-for-comments.js @@ -112,9 +112,10 @@ $(document).on('turbo-migration:load', function () { \
' + commentText + '
\ \ -
\ - \ - \ +
\ + \ + \ + \
\
'; }); @@ -166,6 +167,17 @@ $(document).on('turbo-migration:load', function () { }) } + function reportComment(commentId, callback) { + const jqxhr = $.ajax({ + type: 'POST', + url: Routes.report_comment_path(commentId) + }); + jqxhr.done(function () { + callback(); + }); + jqxhr.fail(ajaxError); + } + function deleteComment(commentId, editor, file_id, callback) { const jqxhr = $.ajax({ type: 'DELETE', @@ -313,6 +325,17 @@ $(document).on('turbo-migration:load', function () { const container = otherComments.find('.container'); container.html(htmlContent); + const reportButtons = container.find('.action-report'); + reportButtons.on('click', function (event) { + const button = $(event.target); + const parent = $(button).parent().parent(); + const commentId = parent.data('comment-id'); + + reportComment(commentId, function () { + parent.html('
' + I18n.t('comments.reported') + '
'); + }); + }); + const deleteButtons = container.find('.action-delete'); deleteButtons.on('click', function (event) { const button = $(event.target); diff --git a/app/mailers/report_mailer.rb b/app/mailers/report_mailer.rb deleted file mode 100644 index 076ba13fd..000000000 --- a/app/mailers/report_mailer.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -class ReportMailer < ApplicationMailer - default to: CodeOcean::Config.new(:code_ocean).read.dig(:content_moderation, :report_emails) - - def report_content - @reported_content = params.fetch(:reported_content) - study_group = @reported_content.submission.study_group - exercise = @reported_content.exercise - - # NOTE: This implementation assumes the course URL is static and does not vary per user. - # This is currently valid for a majority of use cases. However, in dynamic scenarios (such as - # content trees in openHPI used in conjunction with A/B/n testing) this assumption may no longer hold true. - @course_url = LtiParameter.find_by(study_group:, exercise:)&.lti_parameters&.[]('launch_presentation_return_url') - - mail(subject: I18n.t('report_mailer.report_content.subject', content_name: @reported_content.model_name.human)) - end -end diff --git a/app/mailers/user_content_report_mailer.rb b/app/mailers/user_content_report_mailer.rb new file mode 100644 index 000000000..71cba6454 --- /dev/null +++ b/app/mailers/user_content_report_mailer.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class UserContentReportMailer < ApplicationMailer + default to: CodeOcean::Config.new(:code_ocean).read.dig(:content_moderation, :report_emails) + + def report_content + @user_content_report = UserContentReport.new(reported_content: params.fetch(:reported_content)) + + mail(subject: I18n.t('user_content_report_mailer.report_content.subject', human_model_name: @user_content_report.human_model_name)) + end +end diff --git a/app/models/comment.rb b/app/models/comment.rb index 4674aec73..1abd07981 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -5,8 +5,6 @@ class Comment < ApplicationRecord include Creation include ActionCableHelper - attr_accessor :username, :date, :updated, :editable - belongs_to :file, class_name: 'CodeOcean::File' has_one :submission, through: :file, source: :context, source_type: 'Submission' has_one :request_for_comment, through: :submission diff --git a/app/policies/comment_policy.rb b/app/policies/comment_policy.rb index 6a8f84134..d7b411e3d 100644 --- a/app/policies/comment_policy.rb +++ b/app/policies/comment_policy.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true class CommentPolicy < ApplicationPolicy + REPORT_RECEIVER_CONFIGURED = CodeOcean::Config.new(:code_ocean).read.dig(:content_moderation, :report_emails).present? + def create? - everyone + show? end def show? - everyone + Pundit.policy(@user, @record.request_for_comment).show? && everyone end %i[destroy? update? edit?].each do |action| @@ -16,4 +18,8 @@ def show? def index? everyone end + + def report? + REPORT_RECEIVER_CONFIGURED && show? && !author? + end end diff --git a/app/views/comments/index.json.jbuilder b/app/views/comments/index.json.jbuilder index 318504730..2d093ed31 100644 --- a/app/views/comments/index.json.jbuilder +++ b/app/views/comments/index.json.jbuilder @@ -1,6 +1,11 @@ # frozen_string_literal: true json.array!(@comments) do |comment| - json.extract! comment, :id, :user_id, :file_id, :row, :column, :text, :username, :date, :updated, :editable + json.extract! comment, :id, :user_id, :file_id, :row, :column, :text + json.username comment.user.displayname + json.date comment.created_at.strftime('%d.%m.%Y %k:%M') + json.updated(comment.created_at != comment.updated_at) + json.editable policy(comment).edit? + json.reportable policy(comment).report? json.url comment_url(comment, format: :json) end diff --git a/app/views/report_mailer/report_content.html.slim b/app/views/report_mailer/report_content.html.slim deleted file mode 100644 index de848f652..000000000 --- a/app/views/report_mailer/report_content.html.slim +++ /dev/null @@ -1,6 +0,0 @@ -h3 = t('.prolog') -blockquote style="white-space: pre-wrap;" = @reported_content.question -p = t('.take_action') -p = link_to(request_for_comment_url(@reported_content), request_for_comment_url(@reported_content)) -- if @course_url.present? - p = link_to(t('.authentication'), @course_url) diff --git a/app/views/report_mailer/report_content.text.slim b/app/views/report_mailer/report_content.text.slim deleted file mode 100644 index dbabe03c0..000000000 --- a/app/views/report_mailer/report_content.text.slim +++ /dev/null @@ -1,12 +0,0 @@ -== t('.prolog') -== "\n\n" -== @reported_content.question.lines.map { "> #{it}" }.join -== "\n\n" -== t('.take_action') -== "\n\n" -== request_for_comment_url(@reported_content) -== "\n\n" -- if @course_url.present? - == t('.authentication') - == "\n\n" - == @course_url diff --git a/app/views/request_for_comments/_report.html.slim b/app/views/request_for_comments/_report.html.slim index 8541d3817..a59ec5ab5 100644 --- a/app/views/request_for_comments/_report.html.slim +++ b/app/views/request_for_comments/_report.html.slim @@ -1,7 +1,7 @@ /# locals: (request_for_comment:) - if policy(request_for_comment).report? - = button_to(t('.report'), report_request_for_comment_path(request_for_comment), + = button_to(t('shared.report'), report_request_for_comment_path(request_for_comment), data: {confirm: t('.confirm')}, class: 'btn btn-light btn-sm', form: {class: 'd-inline float-end'}) diff --git a/app/views/user_content_report_mailer/report_content.html.slim b/app/views/user_content_report_mailer/report_content.html.slim new file mode 100644 index 000000000..18ed9d586 --- /dev/null +++ b/app/views/user_content_report_mailer/report_content.html.slim @@ -0,0 +1,6 @@ +h3 = t('.prolog') +blockquote style="white-space: pre-wrap;" = @user_content_report.reported_message +p = t('.take_action') +p = link_to(request_for_comment_url(@user_content_report.related_request_for_comment), request_for_comment_url(@user_content_report.related_request_for_comment)) +- if @user_content_report.course_url.present? + p = link_to(t('.authentication'), @user_content_report.course_url) diff --git a/app/views/user_content_report_mailer/report_content.text.slim b/app/views/user_content_report_mailer/report_content.text.slim new file mode 100644 index 000000000..3590c3632 --- /dev/null +++ b/app/views/user_content_report_mailer/report_content.text.slim @@ -0,0 +1,12 @@ +== t('.prolog') +== "\n\n" +== @user_content_report.reported_message.lines.map { "> #{it}" }.join +== "\n\n" +== t('.take_action') +== "\n\n" +== request_for_comment_url(@user_content_report.related_request_for_comment) +== "\n\n" +- if @user_content_report.course_url.present? + == t('.authentication') + == "\n\n" + == @user_content_report.course_url diff --git a/config/locales/de/comment.yml b/config/locales/de/comment.yml index a0c401ce5..535339b85 100644 --- a/config/locales/de/comment.yml +++ b/config/locales/de/comment.yml @@ -7,4 +7,5 @@ de: other: Kommentare comments: deleted: Gelöscht + reported: Kommentar wurde gemeldet. save_update: Speichern diff --git a/config/locales/de/meta/shared.yml b/config/locales/de/meta/shared.yml index d27796520..35fb159f6 100644 --- a/config/locales/de/meta/shared.yml +++ b/config/locales/de/meta/shared.yml @@ -42,6 +42,7 @@ de: object_destroyed: "%{model} wurde erfolgreich gelöscht." object_updated: "%{model} wurde erfolgreich bearbeitet." out_of: von + report: Inhalt melden reset_filters: Filter zurücksetzen resources: Ressourcen show: Anzeigen diff --git a/config/locales/de/request_for_comment.yml b/config/locales/de/request_for_comment.yml index 647e9ec00..ac8b490bd 100644 --- a/config/locales/de/request_for_comment.yml +++ b/config/locales/de/request_for_comment.yml @@ -44,7 +44,6 @@ de: passed: Erfolgreich report: confirm: Sind Sie sicher, dass Sie diesen Inhalt als unangemessen melden möchten? - report: Inhalt melden reported: Vielen Dank, dass Sie uns auf dieses Problem aufmerksam gemacht haben. Wir werden uns in Kürze darum kümmern. runtime_output: Programmausgabe send_thank_you_note: Senden diff --git a/config/locales/de/report.yml b/config/locales/de/user_content_report.yml similarity index 63% rename from config/locales/de/report.yml rename to config/locales/de/user_content_report.yml index 9c9bca37e..82528cf0a 100644 --- a/config/locales/de/report.yml +++ b/config/locales/de/user_content_report.yml @@ -1,8 +1,8 @@ --- de: - report_mailer: + user_content_report_mailer: report_content: authentication: Kurs-URL für die LTI-Authentifizierung. prolog: 'Die folgenden Inhalte wurden als unangemessen gemeldet:' - subject: 'Spam Report: Ein %{content_name} in CodeOcean wurde als unangemessen markiert.' + subject: 'Content Report: Ein %{human_model_name} in CodeOcean wurde als unangemessen markiert.' take_action: Bitte ergreifen Sie gegebenenfalls Maßnahmen. diff --git a/config/locales/en/comment.yml b/config/locales/en/comment.yml index 2effc74dc..4e5c7f378 100644 --- a/config/locales/en/comment.yml +++ b/config/locales/en/comment.yml @@ -7,4 +7,5 @@ en: other: Comments comments: deleted: Deleted + reported: Comment has been reported. save_update: Save diff --git a/config/locales/en/meta/shared.yml b/config/locales/en/meta/shared.yml index d7bce1d70..f49d0fe2a 100644 --- a/config/locales/en/meta/shared.yml +++ b/config/locales/en/meta/shared.yml @@ -42,6 +42,7 @@ en: object_destroyed: "%{model} has successfully been deleted." object_updated: "%{model} has successfully been updated." out_of: out of + report: Report reset_filters: Reset filters resources: Resources show: Show diff --git a/config/locales/en/request_for_comment.yml b/config/locales/en/request_for_comment.yml index df8e178ae..2e65ddc2f 100644 --- a/config/locales/en/request_for_comment.yml +++ b/config/locales/en/request_for_comment.yml @@ -44,7 +44,6 @@ en: passed: Passed report: confirm: Are you sure you want to report this content as inappropriate? - report: Report reported: Thank you for letting us know about this issue. We will look into the matter shortly. runtime_output: Runtime Output send_thank_you_note: Send diff --git a/config/locales/en/report.yml b/config/locales/en/user_content_report.yml similarity index 61% rename from config/locales/en/report.yml rename to config/locales/en/user_content_report.yml index 14abaf848..4290540b6 100644 --- a/config/locales/en/report.yml +++ b/config/locales/en/user_content_report.yml @@ -1,8 +1,8 @@ --- en: - report_mailer: + user_content_report_mailer: report_content: authentication: Course URL for LTI authentication. prolog: 'The following content has been reported as inappropriate:' - subject: 'Spam Report: A %{content_name} on CodeOcean has been marked as inappropriate.' + subject: 'Content Report: A %{human_model_name} on CodeOcean has been marked as inappropriate.' take_action: Please take action if required. diff --git a/config/routes.rb b/config/routes.rb index d12d3e5c0..9a5e8b154 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,7 +24,11 @@ post :report end end - resources :comments, defaults: {format: :json} + resources :comments, defaults: {format: :json} do + member do + post :report + end + end get '/my_request_for_comments', as: 'my_request_for_comments', to: 'request_for_comments#my_comment_requests' get '/my_rfc_activity', as: 'my_rfc_activity', to: 'request_for_comments#rfcs_with_my_comments' get '/exercises/:exercise_id/request_for_comments', as: 'exercise_request_for_comments', to: 'request_for_comments#rfcs_for_exercise' diff --git a/lib/user_content_report.rb b/lib/user_content_report.rb new file mode 100644 index 000000000..dd43f425e --- /dev/null +++ b/lib/user_content_report.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +class UserContentReport + def initialize(reported_content:) + unless [Comment, RequestForComment].include?(reported_content.class) + raise("#{reported_content.model_name} is not configured for content reports.") + end + + @reported_content = reported_content + end + + # NOTE: This implementation assumes the course URL is static and does not vary per user. + # This is currently valid for a majority of use cases. However, in dynamic scenarios (such as + # content trees in openHPI used in conjunction with A/B/n testing) this assumption may no + # longer hold true. + def course_url = lti_parameters['launch_presentation_return_url'] + + def human_model_name = @reported_content.model_name.human + + def reported_message + case @reported_content + when RequestForComment + @reported_content.question + when Comment + @reported_content.text + end + end + + def related_request_for_comment + case @reported_content + when RequestForComment + @reported_content + when Comment + @reported_content.request_for_comment + end + end + + private + + def lti_parameters + LtiParameter.find_by(study_group:, exercise:)&.lti_parameters || {} + end + + def study_group + @reported_content.submission.study_group + end + + def exercise + related_request_for_comment.exercise + end +end diff --git a/spec/lib/user_content_report_spec.rb b/spec/lib/user_content_report_spec.rb new file mode 100644 index 000000000..02a6b44af --- /dev/null +++ b/spec/lib/user_content_report_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UserContentReport do + subject(:report) { described_class.new(reported_content:) } + + describe '#related_request_for_comment' do + context 'when a PfC is reported' do + let(:reported_content) { build_stubbed(:rfc) } + + it 'returns the RfC itself' do + expect(report.related_request_for_comment).to eq reported_content + end + end + + context 'when a comment is reported' do + let(:rfc) { create(:rfc) } + let(:reported_content) { create(:comment, file: rfc.file) } + + it 'returns the associated RfC' do + expect(report.related_request_for_comment).to eq rfc + end + end + end + + describe '#reported_message' do + let(:message) { 'This message is reported.' } + + context 'when a comment is reported' do + let(:reported_content) { build_stubbed(:comment, text: message) } + + it 'returns the comments text' do + expect(report.reported_message).to eq message + end + end + + context 'when a RfC is reported' do + let(:reported_content) { build_stubbed(:rfc, question: message) } + + it 'returns the RfCs question' do + expect(report.reported_message).to eq message + end + end + end + + describe '#course_url' do + context 'when the LTI parameter is missing' do + let(:reported_content) { build_stubbed(:rfc) } + + it 'returns no course URL' do + expect(report.course_url).to be_nil + end + end + + context 'when the LTI parameter has no retrun URL' do + let(:reported_content) { create(:rfc) } + + before do + create(:lti_parameter, :without_return_url, + exercise: reported_content.file.request_for_comment.exercise, + study_group: reported_content.submission.study_group) + end + + it 'returns no course URL' do + expect(report.course_url).to be_nil + end + end + + context 'when the LTI parameter has the retrun URL' do + let(:reported_content) { create(:rfc) } + + before do + create(:lti_parameter, + exercise: reported_content.file.request_for_comment.exercise, + study_group: reported_content.submission.study_group) + end + + it 'returns the LTI parameter course URL' do + expect(report.course_url).to match(%r{https.+/courses/}) + end + end + end + + context 'when an unsupported model is reported' do + let(:reported_content) { Exercise.new } + + it 'raise an error' do + expect { report }.to raise_error('Exercise is not configured for content reports.') + end + end +end diff --git a/spec/mailers/previews/report_mailer_preview.rb b/spec/mailers/previews/report_mailer_preview.rb index 331cd4de5..50b4231f7 100644 --- a/spec/mailers/previews/report_mailer_preview.rb +++ b/spec/mailers/previews/report_mailer_preview.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -class ReportMailerPreview < ActionMailer::Preview +class UserContentReportMailerPreview < ActionMailer::Preview def report rfc = FactoryBot.build_stubbed(:rfc) - ReportMailer.with(reported_content: rfc).report_content + UserContentReportMailer.with(reported_content: rfc).report_content end end diff --git a/spec/mailers/report_mailer_spec.rb b/spec/mailers/report_mailer_spec.rb deleted file mode 100644 index cfdc59509..000000000 --- a/spec/mailers/report_mailer_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe ReportMailer do - describe '#report_content' do - subject(:mail) { described_class.with(reported_content:).report_content } - - context 'when an RfC is reported' do - let(:question) { 'Inappropriate content for RfC.' } - let(:reported_content) { create(:rfc, question:) } - - it 'sets the correct sender' do - expect(mail.from).to include('codeocean@openhpi.de') - end - - it 'sets the correct subject' do - expect(mail.subject).to eq(I18n.t('report_mailer.report_content.subject', content_name: RequestForComment.model_name.human)) - end - - it 'includes the reported content' do - expect(mail.text_part.body).to include(question) - expect(mail.html_part.body).to include(question) - end - - it 'includes the LTI retrun URL for course authentication' do - create(:lti_parameter, - exercise: reported_content.exercise, - study_group: reported_content.submission.study_group) - - expect(mail.text_part.body).to match(%r{https.+/courses/}) - expect(mail.html_part.body).to match(%r{https.+/courses/}) - end - end - end -end diff --git a/spec/mailers/user_content_report_mailer_spec.rb b/spec/mailers/user_content_report_mailer_spec.rb new file mode 100644 index 000000000..879465e3f --- /dev/null +++ b/spec/mailers/user_content_report_mailer_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UserContentReportMailer do + describe '#report_content' do + subject(:mail) { described_class.with(reported_content:).report_content } + + let(:reported_content) { instance_double(Comment) } + let(:reported_message) { 'Reported message' } + let(:human_model_name) { 'Reported model' } + let(:course_url) { 'https://example.com/course/1' } + + let(:user_content_report) do + instance_double(UserContentReport, + human_model_name:, + reported_message:, + related_request_for_comment: instance_double(RequestForComment), + course_url:) + end + + before do + allow(UserContentReport).to receive(:new).with(reported_content:).and_return(user_content_report) + end + + it 'sets the correct sender' do + expect(mail.from).to include('codeocean@openhpi.de') + end + + it 'includes the reported content' do + expect(mail.text_part.body).to include(reported_message) + expect(mail.html_part.body).to include(reported_message) + end + + it 'sets the correct subject' do + expect(mail.subject).to eq(I18n.t('user_content_report_mailer.report_content.subject', human_model_name:)) + end + + it 'includes the LTI retrun URL for course authentication' do + expect(mail.text_part.body).to include(course_url) + expect(mail.html_part.body).to include(course_url) + end + end +end diff --git a/spec/policies/comment_policy_spec.rb b/spec/policies/comment_policy_spec.rb index 699c36550..26ef4b9f8 100644 --- a/spec/policies/comment_policy_spec.rb +++ b/spec/policies/comment_policy_spec.rb @@ -5,7 +5,7 @@ RSpec.describe CommentPolicy do let(:user_types) { %i[learner teacher admin] } - permissions :create?, :show?, :index? do + permissions :index? do let(:comment) { build_stubbed(:comment) } it 'grants access to all user types' do @@ -15,6 +15,31 @@ end end + permissions :create?, :show? do + let(:comment) { build_stubbed(:comment) } + + it 'grants access to all user types' do + user_types.each do |user_type| + expect(described_class).to permit(build_stubbed(user_type), comment) + end + end + + context 'without access to the RfC' do + let(:learner) { build_stubbed(:learner) } + let(:rfc_policy) { instance_double(RequestForCommentPolicy, show?: false) } + + before do + allow(RequestForCommentPolicy).to receive(:new) + .with(learner, comment.request_for_comment) + .and_return(rfc_policy) + end + + it 'does not grant access' do + expect(described_class).not_to permit(learner, comment) + end + end + end + permissions :destroy?, :update?, :edit? do let(:comment) { build_stubbed(:comment) } @@ -41,4 +66,45 @@ expect(described_class).to permit(build_stubbed(:admin), comment) end end + + permissions :report? do + let(:comment) { build_stubbed(:comment) } + + before do + stub_const('CommentPolicy::REPORT_RECEIVER_CONFIGURED', reports_enabled) + end + + context 'when content moderation is enabled' do + let(:reports_enabled) { true } + + it 'grants access to all user types' do + user_types.each do |user_type| + expect(described_class).to permit(build_stubbed(user_type), comment) + end + end + + it 'does not grants access to the author' do + expect(described_class).not_to permit(comment.user, comment) + end + + it 'does not grant access to users who have no access to the RfC' do + learner = build_stubbed(:learner) + rfc_policy = instance_double(RequestForCommentPolicy, show?: false) + allow(RequestForCommentPolicy).to receive(:new).with(learner, comment.request_for_comment) + .and_return(rfc_policy) + + expect(described_class).not_to permit(learner, comment) + end + end + + context 'when content moderation is disabled' do + let(:reports_enabled) { false } + + it 'does not grant access to all user types' do + user_types.each do |user_type| + expect(described_class).not_to permit(build_stubbed(user_type), comment) + end + end + end + end end diff --git a/spec/request/comments_spec.rb b/spec/request/comments_spec.rb new file mode 100644 index 000000000..84f605806 --- /dev/null +++ b/spec/request/comments_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'POST /comments/:comment_id/report', type: :request do + let(:user) { create(:learner) } + let(:comment) { create(:comment) } + + before do + stub_const('CommentPolicy::REPORT_RECEIVER_CONFIGURED', true) + allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user) + end + + it 'sends an email to let admins know about the report' do + expect { post(report_comment_path(comment)) } + .to have_enqueued_mail(UserContentReportMailer, :report_content) + .with(params: {reported_content: comment}, args: []) + end +end diff --git a/spec/request/request_for_comments_spec.rb b/spec/request/request_for_comments_spec.rb index b8c553aa1..4cd3b03f5 100644 --- a/spec/request/request_for_comments_spec.rb +++ b/spec/request/request_for_comments_spec.rb @@ -13,7 +13,7 @@ it 'sends an email to let admins know about the report' do expect { post(report_request_for_comment_path(rfc)) } - .to have_enqueued_mail(ReportMailer, :report_content) + .to have_enqueued_mail(UserContentReportMailer, :report_content) .with(params: {reported_content: rfc}, args: []) end end diff --git a/spec/system/report_request_for_comment_comments_system_spec.rb b/spec/system/report_request_for_comment_comments_system_spec.rb new file mode 100644 index 000000000..83ea326e2 --- /dev/null +++ b/spec/system/report_request_for_comment_comments_system_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Report RfC comments for inappropriate content', :js do + let(:user) { create(:learner) } + + before do + stub_const('CommentPolicy::REPORT_RECEIVER_CONFIGURED', reports_enabled) + visit(sign_in_path) + fill_in('email', with: user.email) + fill_in('password', with: attributes_for(:learner)[:password]) + click_button(I18n.t('sessions.new.link')) + visit(request_for_comment_path(create(:rfc_with_comment))) + find('span.ace_icon').click + end + + context 'when reporting is enabled' do + let(:reports_enabled) { true } + + it 'allows reporting the comment' do + within('.modal-content') do + click_on I18n.t('shared.report') + end + + expect(page).to have_text(I18n.t('comments.reported')) + end + end + + context 'when reporting is disabled' do + let(:reports_enabled) { false } + + it 'does not display the report button' do + within('.modal-content') do + expect(page).to have_no_button(I18n.t('shared.report')) + end + end + end +end diff --git a/spec/system/report_request_for_comments_system_spec.rb b/spec/system/report_request_for_comments_system_spec.rb index 204bb96cd..0145bf800 100644 --- a/spec/system/report_request_for_comments_system_spec.rb +++ b/spec/system/report_request_for_comments_system_spec.rb @@ -19,7 +19,7 @@ it 'allows reporting of RfCs', :js do accept_confirm do - click_on I18n.t('request_for_comments.report.report') + click_on I18n.t('shared.report') end expect(page).to have_text(I18n.t('request_for_comments.report.reported')) @@ -30,7 +30,7 @@ let(:reports_enabled) { false } it 'does not display the report button' do - expect(page).to have_no_button(I18n.t('request_for_comments.report.report')) + expect(page).to have_no_button(I18n.t('shared.report')) end end end