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 () {
\
\
\
- ';
});
@@ -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('');
+ });
+ });
+
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