Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/assets/stylesheets/request-for-comments.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ html[data-bs-theme="light"] {
button {
margin-right: 5px;
}

.action-report {
margin-left: auto;
}
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions app/controllers/comments_controller.rb
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Comment thread
arkirchner marked this conversation as resolved.

private

# Use callbacks to share common setup or constraints between actions.
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/request_for_comments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
arkirchner marked this conversation as resolved.
end

private
Expand Down
29 changes: 26 additions & 3 deletions app/javascript/sprocket-asset-import/request-for-comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ $(document).on('turbo-migration:load', function () {
</div> \
<div class="comment-content">' + commentText + '</div> \
<textarea class="comment-editor">' + commentText + '</textarea> \
<div class="comment-actions' + (comment.editable ? '' : ' d-none') + '"> \
<button class="action-edit btn btn-sm btn-warning">' + I18n.t('shared.edit') + '</button> \
<button class="action-delete btn btn-sm btn-danger">' + I18n.t('shared.destroy') + '</button> \
<div class="comment-actions' + (comment.editable || comment.reportable ? '' : ' d-none') + '"> \
<button class="action-edit btn btn-sm btn-warning' + (comment.editable ? '' : ' d-none') + '">' + I18n.t('shared.edit') + '</button> \
<button class="action-delete btn btn-sm btn-danger' + (comment.editable ? '' : ' d-none') + '">' + I18n.t('shared.destroy') + '</button> \
<button class="action-report btn btn-light btn-sm' + (comment.reportable ? '' : ' d-none') + '">' + I18n.t('shared.report') + '</button> \
</div> \
</div>';
});
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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('<div class="comment-reported">' + I18n.t('comments.reported') + '</div>');
});
});

const deleteButtons = container.find('.action-delete');
deleteButtons.on('click', function (event) {
const button = $(event.target);
Expand Down
18 changes: 0 additions & 18 deletions app/mailers/report_mailer.rb

This file was deleted.

11 changes: 11 additions & 0 deletions app/mailers/user_content_report_mailer.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 0 additions & 2 deletions app/models/comment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions app/policies/comment_policy.rb
Original file line number Diff line number Diff line change
@@ -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|
Expand All @@ -16,4 +18,8 @@ def show?
def index?
everyone
end

def report?
REPORT_RECEIVER_CONFIGURED && show? && !author?
end
end
7 changes: 6 additions & 1 deletion app/views/comments/index.json.jbuilder
Original file line number Diff line number Diff line change
@@ -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
Comment thread
arkirchner marked this conversation as resolved.
json.username comment.user.displayname
json.date comment.created_at.strftime('%d.%m.%Y %k:%M')
json.updated(comment.created_at != comment.updated_at)
Comment thread
arkirchner marked this conversation as resolved.
json.editable policy(comment).edit?
json.reportable policy(comment).report?
json.url comment_url(comment, format: :json)
end
6 changes: 0 additions & 6 deletions app/views/report_mailer/report_content.html.slim

This file was deleted.

12 changes: 0 additions & 12 deletions app/views/report_mailer/report_content.text.slim

This file was deleted.

2 changes: 1 addition & 1 deletion app/views/request_for_comments/_report.html.slim
Original file line number Diff line number Diff line change
@@ -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'})
6 changes: 6 additions & 0 deletions app/views/user_content_report_mailer/report_content.html.slim
Original file line number Diff line number Diff line change
@@ -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)
12 changes: 12 additions & 0 deletions app/views/user_content_report_mailer/report_content.text.slim
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions config/locales/de/comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ de:
other: Kommentare
comments:
deleted: Gelöscht
reported: Kommentar wurde gemeldet.
save_update: Speichern
1 change: 1 addition & 0 deletions config/locales/de/meta/shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion config/locales/de/request_for_comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions config/locales/en/comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ en:
other: Comments
comments:
deleted: Deleted
reported: Comment has been reported.
save_update: Save
1 change: 1 addition & 0 deletions config/locales/en/meta/shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion config/locales/en/request_for_comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
6 changes: 5 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
51 changes: 51 additions & 0 deletions lib/user_content_report.rb
Original file line number Diff line number Diff line change
@@ -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
Loading