From 5d131301d63b8b4ceca891c7bbf1040a55e8c5d5 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Fri, 11 Jul 2025 14:45:11 +0200 Subject: [PATCH 01/14] feat: Allow users to report comments Comments can be used to potentially harass users who request comments. A button was added to report a message as inappropriate. Resolves #2715 --- .../stylesheets/request-for-comments.css.scss | 4 ++ app/controllers/comments_controller.rb | 17 +++-- .../request_for_comments_controller.rb | 4 +- .../request-for-comments.js | 29 +++++++- app/mailers/report_mailer.rb | 18 ----- app/mailers/user_content_report_mailer.rb | 11 +++ app/models/comment.rb | 2 - app/policies/comment_policy.rb | 10 ++- app/views/comments/index.json.jbuilder | 7 +- .../report_mailer/report_content.html.slim | 6 -- .../report_mailer/report_content.text.slim | 12 ---- .../request_for_comments/_report.html.slim | 2 +- .../report_content.html.slim | 6 ++ .../report_content.text.slim | 12 ++++ config/locales/de/comment.yml | 1 + config/locales/de/meta/shared.yml | 3 +- config/locales/de/request_for_comment.yml | 3 +- .../{report.yml => user_content_report.yml} | 4 +- config/locales/en/comment.yml | 1 + config/locales/en/meta/shared.yml | 1 + config/locales/en/request_for_comment.yml | 2 +- .../{report.yml => user_content_report.yml} | 4 +- config/routes.rb | 6 +- lib/user_content_report.rb | 51 ++++++++++++++ spec/lib/user_content_report_spec.rb | 67 +++++++++++++++++++ .../mailers/previews/report_mailer_preview.rb | 4 +- spec/mailers/report_mailer_spec.rb | 36 ---------- .../user_content_report_mailer_spec.rb | 41 ++++++++++++ spec/policies/comment_policy_spec.rb | 62 ++++++++++++++++- spec/request/comments_spec.rb | 19 ++++++ spec/request/request_for_comments_spec.rb | 2 +- ...equest_for_comment_comments_system_spec.rb | 39 +++++++++++ ...report_request_for_comments_system_spec.rb | 4 +- 33 files changed, 385 insertions(+), 105 deletions(-) delete mode 100644 app/mailers/report_mailer.rb create mode 100644 app/mailers/user_content_report_mailer.rb delete mode 100644 app/views/report_mailer/report_content.html.slim delete mode 100644 app/views/report_mailer/report_content.text.slim create mode 100644 app/views/user_content_report_mailer/report_content.html.slim create mode 100644 app/views/user_content_report_mailer/report_content.text.slim rename config/locales/de/{report.yml => user_content_report.yml} (63%) rename config/locales/en/{report.yml => user_content_report.yml} (61%) create mode 100644 lib/user_content_report.rb create mode 100644 spec/lib/user_content_report_spec.rb delete mode 100644 spec/mailers/report_mailer_spec.rb create mode 100644 spec/mailers/user_content_report_mailer_spec.rb create mode 100644 spec/request/comments_spec.rb create mode 100644 spec/system/report_request_for_comment_comments_system_spec.rb 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..24570cdfb 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 ist gemeldet. save_update: Speichern diff --git a/config/locales/de/meta/shared.yml b/config/locales/de/meta/shared.yml index d27796520..5903792f4 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 @@ -49,7 +50,7 @@ de: time: before: "%{time} zuvor" tooltips: - shortcut: 'Tastaturkürzel: %{shortcut}' + shortcut: "Tastaturkürzel: %{shortcut}" update: "%{model} aktualisieren" updated_at: Letzte Änderung upload_file: Datei hochladen diff --git a/config/locales/de/request_for_comment.yml b/config/locales/de/request_for_comment.yml index 647e9ec00..96b38bcd8 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 @@ -55,4 +54,4 @@ de: solved: Diese Frage wurde erfolgreich beantwortet subscribe_to_author: Bei neuen Kommentaren des/der Autors/Autorin per E-Mail benachrichtigt werden test_results: Testergebnisse - write_a_thank_you_node: 'Wenn Sie möchten, können Sie sich bei allen Mitlernenden, die Ihnen bei der Beantwortung Ihrer Frage geholfen haben, bedanken:' + write_a_thank_you_node: "Wenn Sie möchten, können Sie sich bei allen Mitlernenden, die Ihnen bei der Beantwortung Ihrer Frage geholfen haben, bedanken:" 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..524dcd4ac 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 is 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..5a3be0458 100644 --- a/config/locales/en/request_for_comment.yml +++ b/config/locales/en/request_for_comment.yml @@ -55,4 +55,4 @@ en: solved: This question has been answered subscribe_to_author: Receive E-Mail notifications for new comments of the original author test_results: Test Results - write_a_thank_you_node: 'If you want, you can write a thank you note to all your commenters:' + write_a_thank_you_node: "If you want, you can write a thank you note to all your commenters:" 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..dbeafafa1 --- /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 + + attr_reader :reported_content + + def lti_parameters = LtiParameter.find_by(study_group:, exercise:)&.lti_parameters || {} + + 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..bd2da465e --- /dev/null +++ b/spec/lib/user_content_report_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UserContentReport do + describe '#related_request_for_comment' do + it 'returns the RfC when the RfC itself is reported' do + rfc = build_stubbed(:rfc) + + expect(described_class.new(reported_content: rfc).related_request_for_comment).to eq rfc + end + + it 'returns the associated RfC when a comment is reported' do + rfc = create(:rfc) + comment = create(:comment, file: rfc.file) + expect(described_class.new(reported_content: comment).related_request_for_comment).to eq rfc + end + end + + describe '#reported_message' do + let(:message) { 'This message is reported.' } + + it 'returns the comments text' do + comment = build_stubbed(:comment, text: message) + + expect(described_class.new(reported_content: comment).reported_message).to eq message + end + + it 'returns the RfCs question' do + rfc = build_stubbed(:rfc, question: message) + + expect(described_class.new(reported_content: rfc).reported_message).to eq message + end + end + + describe '#course_url' do + it 'has no course URL if the LTI paramters are absent' do + rfc = build_stubbed(:rfc) + + expect(described_class.new(reported_content: rfc).course_url).to be_nil + end + + it 'has no course URL if the required LTI attribute is missing' do + rfc = create(:rfc) + + create(:lti_parameter, :without_return_url, + exercise: rfc.file.request_for_comment.exercise, + study_group: rfc.submission.study_group) + + expect(described_class.new(reported_content: rfc).course_url).to be_nil + end + + it 'returns the LTI parameter course URL' do + rfc = create(:rfc) + + create(:lti_parameter, + exercise: rfc.file.request_for_comment.exercise, + study_group: rfc.submission.study_group) + + expect(described_class.new(reported_content: rfc).course_url).to match(%r{https.+/courses/}) + end + end + + it 'raise an error if an unsupported model is reported' do + expect { described_class.new(reported_content: Exercise.new) }.to raise_error('Exercise is not configured for content reports.') + 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..5d6963263 --- /dev/null +++ b/spec/mailers/user_content_report_mailer_spec.rb @@ -0,0 +1,41 @@ +# 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) { 'Repoted message' } + let(:human_model_name) { 'ReportedModle' } + let(:course_url) { 'https://example.com/course/1' } + + before do + user_content_report = instance_double(UserContentReport, + human_model_name:, + reported_message:, + related_request_for_comment: instance_double(RequestForComment), + course_url:) + 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..c8d75af85 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,25 @@ 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 + + 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 + permissions :destroy?, :update?, :edit? do let(:comment) { build_stubbed(:comment) } @@ -41,4 +60,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..e63ae99a5 --- /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 of RfCs' 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 From ae590b5fef7def4bec73c76273834997360f24e2 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Wed, 6 Aug 2025 16:31:18 +0200 Subject: [PATCH 02/14] Update config/locales/de/comment.yml Co-authored-by: Nele Sina Noack --- config/locales/de/comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/de/comment.yml b/config/locales/de/comment.yml index 24570cdfb..535339b85 100644 --- a/config/locales/de/comment.yml +++ b/config/locales/de/comment.yml @@ -7,5 +7,5 @@ de: other: Kommentare comments: deleted: Gelöscht - reported: Kommentar ist gemeldet. + reported: Kommentar wurde gemeldet. save_update: Speichern From 943436121d6e7aecf430d0b9fb7d0bf4fa65a013 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Wed, 6 Aug 2025 16:31:56 +0200 Subject: [PATCH 03/14] Update config/locales/en/comment.yml Co-authored-by: Nele Sina Noack --- config/locales/en/comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/en/comment.yml b/config/locales/en/comment.yml index 524dcd4ac..4e5c7f378 100644 --- a/config/locales/en/comment.yml +++ b/config/locales/en/comment.yml @@ -7,5 +7,5 @@ en: other: Comments comments: deleted: Deleted - reported: Comment is reported. + reported: Comment has been reported. save_update: Save From 8a4224bdb4308508124a0dfa0b8c6ca82c990c38 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Wed, 6 Aug 2025 16:33:47 +0200 Subject: [PATCH 04/14] Update spec/system/report_request_for_comment_comments_system_spec.rb Co-authored-by: Nele Sina Noack --- spec/system/report_request_for_comment_comments_system_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/system/report_request_for_comment_comments_system_spec.rb b/spec/system/report_request_for_comment_comments_system_spec.rb index e63ae99a5..83ea326e2 100644 --- a/spec/system/report_request_for_comment_comments_system_spec.rb +++ b/spec/system/report_request_for_comment_comments_system_spec.rb @@ -18,7 +18,7 @@ context 'when reporting is enabled' do let(:reports_enabled) { true } - it 'allows reporting of RfCs' do + it 'allows reporting the comment' do within('.modal-content') do click_on I18n.t('shared.report') end From 3b452e8692d6476f0974e2024fbaa89f8c3c2f14 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Wed, 6 Aug 2025 16:48:19 +0200 Subject: [PATCH 05/14] Update spec/mailers/user_content_report_mailer_spec.rb Co-authored-by: Nele Sina Noack --- spec/mailers/user_content_report_mailer_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/mailers/user_content_report_mailer_spec.rb b/spec/mailers/user_content_report_mailer_spec.rb index 5d6963263..4ad4f1c83 100644 --- a/spec/mailers/user_content_report_mailer_spec.rb +++ b/spec/mailers/user_content_report_mailer_spec.rb @@ -7,8 +7,8 @@ subject(:mail) { described_class.with(reported_content:).report_content } let(:reported_content) { instance_double(Comment) } - let(:reported_message) { 'Repoted message' } - let(:human_model_name) { 'ReportedModle' } + let(:reported_message) { 'Reported message' } + let(:human_model_name) { 'Reported model' } let(:course_url) { 'https://example.com/course/1' } before do From 5298c9d1bd25fec5d6c4475fd71ca34a4d69e433 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Thu, 7 Aug 2025 11:59:31 +0200 Subject: [PATCH 06/14] Update spec/lib/user_content_report_spec.rb Co-authored-by: Nele Sina Noack --- spec/lib/user_content_report_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/user_content_report_spec.rb b/spec/lib/user_content_report_spec.rb index bd2da465e..87c398312 100644 --- a/spec/lib/user_content_report_spec.rb +++ b/spec/lib/user_content_report_spec.rb @@ -34,7 +34,7 @@ end describe '#course_url' do - it 'has no course URL if the LTI paramters are absent' do + it 'has no course URL if the LTI parameters are absent' do rfc = build_stubbed(:rfc) expect(described_class.new(reported_content: rfc).course_url).to be_nil From f9dc9e31ec79400dcb28ea6b01fc074ed61ad322 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Wed, 6 Aug 2025 17:52:53 +0200 Subject: [PATCH 07/14] Remove single line and attr_reader --- lib/user_content_report.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/user_content_report.rb b/lib/user_content_report.rb index dbeafafa1..dd43f425e 100644 --- a/lib/user_content_report.rb +++ b/lib/user_content_report.rb @@ -15,34 +15,34 @@ def initialize(reported_content:) # longer hold true. def course_url = lti_parameters['launch_presentation_return_url'] - def human_model_name = reported_content.model_name.human + def human_model_name = @reported_content.model_name.human def reported_message - case reported_content + case @reported_content when RequestForComment - reported_content.question + @reported_content.question when Comment - reported_content.text + @reported_content.text end end def related_request_for_comment - case reported_content + case @reported_content when RequestForComment - reported_content + @reported_content when Comment - reported_content.request_for_comment + @reported_content.request_for_comment end end private - attr_reader :reported_content - - def lti_parameters = LtiParameter.find_by(study_group:, exercise:)&.lti_parameters || {} + def lti_parameters + LtiParameter.find_by(study_group:, exercise:)&.lti_parameters || {} + end def study_group - reported_content.submission.study_group + @reported_content.submission.study_group end def exercise From 38d3c0d2bbfc06267939ad92b513742ff57f2695 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Thu, 7 Aug 2025 10:36:05 +0200 Subject: [PATCH 08/14] Spec improvments --- .../mailers/user_content_report_mailer_spec.rb | 7 +++++-- spec/policies/comment_policy_spec.rb | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/spec/mailers/user_content_report_mailer_spec.rb b/spec/mailers/user_content_report_mailer_spec.rb index 4ad4f1c83..879465e3f 100644 --- a/spec/mailers/user_content_report_mailer_spec.rb +++ b/spec/mailers/user_content_report_mailer_spec.rb @@ -11,12 +11,15 @@ let(:human_model_name) { 'Reported model' } let(:course_url) { 'https://example.com/course/1' } - before do - user_content_report = instance_double(UserContentReport, + 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 diff --git a/spec/policies/comment_policy_spec.rb b/spec/policies/comment_policy_spec.rb index c8d75af85..26ef4b9f8 100644 --- a/spec/policies/comment_policy_spec.rb +++ b/spec/policies/comment_policy_spec.rb @@ -24,13 +24,19 @@ end 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) + context 'without access to the RfC' do + let(:learner) { build_stubbed(:learner) } + let(:rfc_policy) { instance_double(RequestForCommentPolicy, show?: false) } - expect(described_class).not_to permit(learner, comment) + 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 From 97fafda4db6ea78680184cc95f38201b0230c9f1 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Thu, 7 Aug 2025 12:38:10 +0200 Subject: [PATCH 09/14] Use subject in user content report spec --- spec/lib/user_content_report_spec.rb | 85 ++++++++++++++++++---------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/spec/lib/user_content_report_spec.rb b/spec/lib/user_content_report_spec.rb index 87c398312..b93f11b21 100644 --- a/spec/lib/user_content_report_spec.rb +++ b/spec/lib/user_content_report_spec.rb @@ -3,65 +3,90 @@ require 'rails_helper' RSpec.describe UserContentReport do + subject(:report) { described_class.new(reported_content:) } + describe '#related_request_for_comment' do - it 'returns the RfC when the RfC itself is reported' do - rfc = build_stubbed(:rfc) + context 'when a PfC is reported' do + let(:reported_content) { build_stubbed(:rfc) } - expect(described_class.new(reported_content: rfc).related_request_for_comment).to eq rfc + it 'returns the RfC itself' do + expect(report.related_request_for_comment).to eq reported_content + end end - it 'returns the associated RfC when a comment is reported' do - rfc = create(:rfc) - comment = create(:comment, file: rfc.file) - expect(described_class.new(reported_content: comment).related_request_for_comment).to eq rfc + 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.' } - it 'returns the comments text' do - comment = build_stubbed(:comment, text: message) + context 'when a comment is reported' do + let(:reported_content) { build_stubbed(:comment, text: message) } - expect(described_class.new(reported_content: comment).reported_message).to eq message + it 'returns the comments text' do + expect(report.reported_message).to eq message + end end - it 'returns the RfCs question' do - rfc = build_stubbed(:rfc, question: message) + context 'when a PfC is reported' do + let(:reported_content) { build_stubbed(:rfc, question: message) } - expect(described_class.new(reported_content: rfc).reported_message).to eq message + it 'returns the RfCs question' do + expect(report.reported_message).to eq message + end end end describe '#course_url' do - it 'has no course URL if the LTI parameters are absent' do - rfc = build_stubbed(:rfc) + context 'when the LTI parameter is missing' do + let(:reported_content) { build_stubbed(:rfc) } - expect(described_class.new(reported_content: rfc).course_url).to be_nil + it 'returns no course URL' do + expect(report.course_url).to be_nil + end end - it 'has no course URL if the required LTI attribute is missing' do - rfc = create(:rfc) + context 'when the LTI parameter has no retrun URL' do + let(:reported_content) { create(:rfc) } - create(:lti_parameter, :without_return_url, - exercise: rfc.file.request_for_comment.exercise, - study_group: rfc.submission.study_group) + before do + create(:lti_parameter, :without_return_url, + exercise: reported_content.file.request_for_comment.exercise, + study_group: reported_content.submission.study_group) + end - expect(described_class.new(reported_content: rfc).course_url).to be_nil + it 'returns no course URL' do + expect(report.course_url).to be_nil + end end - it 'returns the LTI parameter course URL' do - rfc = create(:rfc) + context 'when the LTI parameter has the retrun URL' do + let(:reported_content) { create(:rfc) } - create(:lti_parameter, - exercise: rfc.file.request_for_comment.exercise, - study_group: rfc.submission.study_group) + before do + create(:lti_parameter, + exercise: reported_content.file.request_for_comment.exercise, + study_group: reported_content.submission.study_group) + end - expect(described_class.new(reported_content: rfc).course_url).to match(%r{https.+/courses/}) + it 'returns the LTI parameter course URL' do + expect(report.course_url).to match(%r{https.+/courses/}) + end end end - it 'raise an error if an unsupported model is reported' do - expect { described_class.new(reported_content: Exercise.new) }.to raise_error('Exercise is not configured for content reports.') + 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 From bdd3f6b58126ed246806a4b73e7ca7f856fa2ab1 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Thu, 4 Sep 2025 10:21:41 +0200 Subject: [PATCH 10/14] Normalize locals --- config/locales/de/meta/shared.yml | 2 +- config/locales/de/request_for_comment.yml | 2 +- config/locales/en/request_for_comment.yml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/config/locales/de/meta/shared.yml b/config/locales/de/meta/shared.yml index 5903792f4..35fb159f6 100644 --- a/config/locales/de/meta/shared.yml +++ b/config/locales/de/meta/shared.yml @@ -50,7 +50,7 @@ de: time: before: "%{time} zuvor" tooltips: - shortcut: "Tastaturkürzel: %{shortcut}" + shortcut: 'Tastaturkürzel: %{shortcut}' update: "%{model} aktualisieren" updated_at: Letzte Änderung upload_file: Datei hochladen diff --git a/config/locales/de/request_for_comment.yml b/config/locales/de/request_for_comment.yml index 96b38bcd8..ac8b490bd 100644 --- a/config/locales/de/request_for_comment.yml +++ b/config/locales/de/request_for_comment.yml @@ -54,4 +54,4 @@ de: solved: Diese Frage wurde erfolgreich beantwortet subscribe_to_author: Bei neuen Kommentaren des/der Autors/Autorin per E-Mail benachrichtigt werden test_results: Testergebnisse - write_a_thank_you_node: "Wenn Sie möchten, können Sie sich bei allen Mitlernenden, die Ihnen bei der Beantwortung Ihrer Frage geholfen haben, bedanken:" + write_a_thank_you_node: 'Wenn Sie möchten, können Sie sich bei allen Mitlernenden, die Ihnen bei der Beantwortung Ihrer Frage geholfen haben, bedanken:' diff --git a/config/locales/en/request_for_comment.yml b/config/locales/en/request_for_comment.yml index 5a3be0458..41092d84a 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 From 2afceff3a7aae8d11b78df2d19dafd1e8a9baa7f Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Fri, 5 Sep 2025 10:36:11 +0200 Subject: [PATCH 11/14] Normalize locals --- config/locales/en/request_for_comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/en/request_for_comment.yml b/config/locales/en/request_for_comment.yml index 41092d84a..2e65ddc2f 100644 --- a/config/locales/en/request_for_comment.yml +++ b/config/locales/en/request_for_comment.yml @@ -54,4 +54,4 @@ en: solved: This question has been answered subscribe_to_author: Receive E-Mail notifications for new comments of the original author test_results: Test Results - write_a_thank_you_node: "If you want, you can write a thank you note to all your commenters:" + write_a_thank_you_node: 'If you want, you can write a thank you note to all your commenters:' From 74ad12dcdb034bbeb10baaeb4334afe9c701b05e Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Fri, 5 Sep 2025 14:03:49 +0200 Subject: [PATCH 12/14] Update app/views/comments/index.json.jbuilder Co-authored-by: Nele Sina Noack --- app/views/comments/index.json.jbuilder | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/comments/index.json.jbuilder b/app/views/comments/index.json.jbuilder index 2d093ed31..b46c1f282 100644 --- a/app/views/comments/index.json.jbuilder +++ b/app/views/comments/index.json.jbuilder @@ -4,7 +4,7 @@ json.array!(@comments) do |comment| 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.updated (comment.created_at != comment.updated_at) json.editable policy(comment).edit? json.reportable policy(comment).report? json.url comment_url(comment, format: :json) From 26fcacffcc522aa56b5e0d1f499237096a558ef2 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Mon, 8 Sep 2025 14:26:50 +0200 Subject: [PATCH 13/14] applied rubocop changes --- app/views/comments/index.json.jbuilder | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/comments/index.json.jbuilder b/app/views/comments/index.json.jbuilder index b46c1f282..2d093ed31 100644 --- a/app/views/comments/index.json.jbuilder +++ b/app/views/comments/index.json.jbuilder @@ -4,7 +4,7 @@ json.array!(@comments) do |comment| 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.updated(comment.created_at != comment.updated_at) json.editable policy(comment).edit? json.reportable policy(comment).report? json.url comment_url(comment, format: :json) From 992301ea26ac41ef054e2c72308cccd10c12cee9 Mon Sep 17 00:00:00 2001 From: Armin Kirchner Date: Tue, 16 Sep 2025 16:25:21 +0200 Subject: [PATCH 14/14] fix typo --- spec/lib/user_content_report_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/user_content_report_spec.rb b/spec/lib/user_content_report_spec.rb index b93f11b21..02a6b44af 100644 --- a/spec/lib/user_content_report_spec.rb +++ b/spec/lib/user_content_report_spec.rb @@ -35,7 +35,7 @@ end end - context 'when a PfC is reported' do + context 'when a RfC is reported' do let(:reported_content) { build_stubbed(:rfc, question: message) } it 'returns the RfCs question' do