diff --git a/app/assets/stylesheets/request-for-comments.css.scss b/app/assets/stylesheets/request-for-comments.css.scss index a219d21c..cbaf9d36 100644 --- a/app/assets/stylesheets/request-for-comments.css.scss +++ b/app/assets/stylesheets/request-for-comments.css.scss @@ -2,4 +2,18 @@ margin-top: 2rem; height: 600px; background-color:#f9f9f9 -} \ No newline at end of file +} + +#thank-you-container { + display: none; + margin-top: 20px; + padding: 5px; + border: solid lightgrey 1px; + background-color: rgba(20, 180, 20, 0.2); + border-radius: 4px; +} + +#thank-you-note { + width: 100%; + height: 200px; +} diff --git a/app/controllers/request_for_comments_controller.rb b/app/controllers/request_for_comments_controller.rb index bcbb1f28..eb2305cc 100644 --- a/app/controllers/request_for_comments_controller.rb +++ b/app/controllers/request_for_comments_controller.rb @@ -1,5 +1,5 @@ class RequestForCommentsController < ApplicationController - before_action :set_request_for_comment, only: [:show, :edit, :update, :destroy, :mark_as_solved] + before_action :set_request_for_comment, only: [:show, :edit, :update, :destroy, :mark_as_solved, :set_thank_you_note] skip_after_action :verify_authorized @@ -34,6 +34,25 @@ class RequestForCommentsController < ApplicationController end end + def set_thank_you_note + authorize! + @request_for_comment.thank_you_note = params[:note] + commenters = [] + @request_for_comment.comments.distinct.to_a.each {|comment| + commenters.append comment.user + } + commenters = commenters.uniq {|user| user.id} + commenters.each {|commenter| UserMailer.send_thank_you_note(@request_for_comment, commenter).deliver_now} + + respond_to do |format| + if @request_for_comment.save + format.json { render :show, status: :ok, location: @request_for_comment } + else + format.json { render json: @request_for_comment.errors, status: :unprocessable_entity } + end + end + end + def submit end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 5b1a04fe..180e9b33 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -20,4 +20,12 @@ class UserMailer < ActionMailer::Base @rfc_link = request_for_comment_url(request_for_comment) mail(subject: t('mailers.user_mailer.got_new_comment.subject', commenting_user_displayname: @commenting_user_displayname), to: request_for_comment.user.email) end + + def send_thank_you_note(request_for_comments, receiver) + @receiver_displayname = receiver.displayname + @author = request_for_comments.user.displayname + @thank_you_note = request_for_comments.thank_you_note + @rfc_link = request_for_comment_url(request_for_comments) + mail(subject: t('mailers.user_mailer.send_thank_you_note.subject', author: @author), to: receiver.email) + end end diff --git a/app/models/request_for_comment.rb b/app/models/request_for_comment.rb index 4be7d325..22b39b39 100644 --- a/app/models/request_for_comment.rb +++ b/app/models/request_for_comment.rb @@ -4,6 +4,8 @@ class RequestForComment < ActiveRecord::Base belongs_to :exercise belongs_to :file, class_name: 'CodeOcean::File' + has_many :comments, through: :submission + scope :unsolved, -> { where(solved: [false, nil]) } def self.last_per_user(n = 5) diff --git a/app/models/submission.rb b/app/models/submission.rb index 6815eb40..94ba46d6 100644 --- a/app/models/submission.rb +++ b/app/models/submission.rb @@ -8,6 +8,7 @@ class Submission < ActiveRecord::Base belongs_to :exercise has_many :testruns + has_many :comments, through: :files delegate :execution_environment, to: :exercise diff --git a/app/policies/request_for_comment_policy.rb b/app/policies/request_for_comment_policy.rb index 0c5fcb4c..d02c5568 100644 --- a/app/policies/request_for_comment_policy.rb +++ b/app/policies/request_for_comment_policy.rb @@ -24,6 +24,10 @@ class RequestForCommentPolicy < ApplicationPolicy admin? || author? end + def set_thank_you_note? + admin? || author? + end + def edit? admin? end diff --git a/app/views/request_for_comments/show.html.erb b/app/views/request_for_comments/show.html.erb index 0f68f2b0..b28c1eb9 100644 --- a/app/views/request_for_comments/show.html.erb +++ b/app/views/request_for_comments/show.html.erb @@ -1,5 +1,10 @@
-

<%= link_to(@request_for_comment.exercise.title, [:implement, @request_for_comment.exercise]) %>

+

+ <% if (@request_for_comment.solved?) %> + + <% end %> + <%= link_to(@request_for_comment.exercise.title, [:implement, @request_for_comment.exercise]) %> +

<% user = @request_for_comment.user @@ -22,11 +27,21 @@ <% if (policy(@request_for_comment).mark_as_solved? and not @request_for_comment.solved?) %> - - <% elsif (@request_for_comment.solved?) %> - - <% else %> - + +

+

+ <%= t('request_for_comments.write_a_thank_you_node') %> +

+ + + +
<% end %> @@ -66,22 +81,46 @@ also, all settings from the rails model needed for the editor configuration in t var solvedButton = $('#mark-as-solved-button'); var commentOnExerciseButton = $('#comment-exercise-button'); - var addCommentExerciseButton = $('#addCommentExerciseButton') + var addCommentExerciseButton = $('#addCommentExerciseButton'); - solvedButton.on('click', function(event){ - var jqrequest = $.ajax({ + var thankYouContainer = $('#thank-you-container'); + + solvedButton.on('click', function(){ + $.ajax({ dataType: 'json', method: 'GET', url: location + '/mark_as_solved' - }); - - jqrequest.done(function(response){ + }).done(function(response){ if(response.solved){ - solvedButton.hide(); + solvedButton.removeClass('btn-primary'); + solvedButton.addClass('btn-success'); + solvedButton.html('<%= t('request_for_comments.solved') %>'); + solvedButton.off('click'); + thankYouContainer.show(); } }); }); + $('#send-thank-you-note').on('click', function () { + var value = $('#thank-you-note').val(); + if (value) { + $.ajax({ + dataType: 'json', + method: 'POST', + url: location + '/set_thank_you_note', + data: { + note: value + } + }).done(function() { + thankYouContainer.hide(); + }); + } + }); + + $('#cancel-thank-you-note').on('click', function () { + thankYouContainer.hide(); + }); + // set file paths for ace var ACE_FILES_PATH = '/assets/ace/'; _.each(['modePath', 'themePath', 'workerPath'], function(attribute) { @@ -89,7 +128,6 @@ also, all settings from the rails model needed for the editor configuration in t }); var commentitor = $('.editor'); - var userid = commentitor.data('user-id'); commentitor.each(function (index, editor) { var currentEditor = ace.edit(editor); @@ -203,9 +241,6 @@ also, all settings from the rails model needed for the editor configuration in t var editor = e.editor; if (target.className.indexOf("ace_gutter-cell") == -1) return; - //if (!editor.isFocused()) return; - //if (e.clientX > 25 + target.getBoundingClientRect().left) return; - var row = e.getDocumentPosition().row; e.stop(); @@ -250,12 +285,12 @@ also, all settings from the rails model needed for the editor configuration in t $.flash.danger({ text: message.length > 0 ? message : $('#flash').data('message-failure') }); - }; + } function stringDivider(str, width, spaceReplacer) { if (str.length>width) { - var p=width + var p=width; for (;p>0 && str[p]!=' ';p--) { } if (p>0) { diff --git a/app/views/user_mailer/send_thank_you_note.slim b/app/views/user_mailer/send_thank_you_note.slim new file mode 100644 index 00000000..fcdcdf8d --- /dev/null +++ b/app/views/user_mailer/send_thank_you_note.slim @@ -0,0 +1 @@ +== t('mailers.user_mailer.send_thank_you_note.body', receiver_displayname: @receiver_displayname, link_to_comment: link_to(@rfc_link, @rfc_link), author: @author, thank_you_note: @thank_you_note ) diff --git a/config/locales/de.yml b/config/locales/de.yml index a5bcf381..d0aef588 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -410,6 +410,41 @@ de: reset_password: body: 'Bitte besuchen Sie %{link}, sofern Sie Ihr Passwort zurücksetzen wollen.' subject: Anweisungen zum Zurücksetzen Ihres Passworts + send_thank_you_note: + body: | + English version below
+ _________________________
+
+ Hallo %{receiver_displayname},
+
+ %{author} hat Ihnen für Ihren Kommentar auf CodeOcean gedankt.
+
+ %{author} schreibt: %{thank_you_note}
+
+ Sie finden die Kommentaranfrage hier: %{link_to_comment}
+
+ Falls Sie beim Klick auf diesen Link eine Fehlermeldung erhalten, dass Sie nicht berechtigt wären diese Aktion auszuführen, öffnen Sie bitte eine beliebige Programmieraufgabe aus einem Kurs heraus und klicken den Link danach noch einmal.
+
+ Danke, dass Sie anderen Nutzern von CodeOcean helfen! +
+ Diese Mail wurde automatisch von CodeOcean verschickt.
+
+ _________________________
+
+ Dear %{receiver_displayname},
+
+ %{author} thanks you for your comment.
+
+ %{author} wrote: %{thank_you_note}
+
+ You can find the request for comments here: %{link_to_comment}
+
+ If you receive an error that you are not authorized to perform this action when clicking the link, please log-in through any course exercise beforehand and click the link again.
+
+ Thank you for helping other users on CodeOcean! +
+ This mail was automatically sent by CodeOcean.
+ subject: "%{author} sagt danke!" request_for_comments: click_here: Zum Kommentieren auf die Seitenleiste klicken! comments: Kommentare @@ -428,6 +463,9 @@ de: show_unsolved: "Nur ungelöste Anfragen anzeigen" solved: "Diese Frage wurde erfolgreich beantwortet" comment_exercise: "Ich möchte die Aufgabenstellung kommentieren" + write_a_thank_you_node: "Wenn Sie möchten, können Sie sich bei allen Mitstudenten, die Ihnen bei der Beantwortung Ihrer Frage geholfen haben, bedanken:" + send_thank_you_note: "Senden" + cancel_thank_you_note: "Nichts senden" sessions: create: failure: Fehlerhafte E-Mail oder Passwort. diff --git a/config/locales/en.yml b/config/locales/en.yml index e5077e4e..9a334697 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -431,6 +431,41 @@ en: reset_password: body: 'Please visit %{link} if you want to reset your password.' subject: Password reset instructions + send_thank_you_note: + body: | + English version below
+ _________________________
+
+ Hallo %{receiver_displayname},
+
+ %{author} hat Ihnen für Ihren Kommentar auf CodeOcean gedankt.
+
+ %{author} schreibt: %{thank_you_note}
+
+ Sie finden die Kommentaranfrage hier: %{link_to_comment}
+
+ Falls Sie beim Klick auf diesen Link eine Fehlermeldung erhalten, dass Sie nicht berechtigt wären diese Aktion auszuführen, öffnen Sie bitte eine beliebige Programmieraufgabe aus einem Kurs heraus und klicken den Link danach noch einmal.
+
+ Danke, dass Sie anderen Nutzern von CodeOcean helfen! +
+ Diese Mail wurde automatisch von CodeOcean verschickt.
+
+ _________________________
+
+ Dear %{receiver_displayname},
+
+ %{author} thanks you for your comment.
+
+ %{author} wrote: %{thank_you_note}
+
+ You can find the request for comments here: %{link_to_comment}
+
+ If you receive an error that you are not authorized to perform this action when clicking the link, please log-in through any course exercise beforehand and click the link again.
+
+ Thank you for helping other users on CodeOcean! +
+ This mail was automatically sent by CodeOcean.
+ subject: "%{author} says thank you!" request_for_comments: click_here: Click on this sidebar to comment! comments: Comments @@ -449,6 +484,9 @@ en: show_unsolved: "Unvsolved requests" solved: "This question has been answered" comment_exercise: "I would like to give feedback for this exercise" + write_a_thank_you_node: "If you want, you can write a thank you note to all your commenters:" + send_thank_you_note: "Send" + cancel_thank_you_note: "Don't send" sessions: create: failure: Invalid email or password. diff --git a/config/routes.rb b/config/routes.rb index fc0f9406..d340a204 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,6 +11,7 @@ Rails.application.routes.draw do member do get :mark_as_solved post :create_comment_exercise + post :set_thank_you_note end end resources :comments, except: [:destroy] do diff --git a/db/migrate/20170608141612_add_thank_you_note_to_request_for_comments.rb b/db/migrate/20170608141612_add_thank_you_note_to_request_for_comments.rb new file mode 100644 index 00000000..fb767aad --- /dev/null +++ b/db/migrate/20170608141612_add_thank_you_note_to_request_for_comments.rb @@ -0,0 +1,5 @@ +class AddThankYouNoteToRequestForComments < ActiveRecord::Migration + def change + add_column :request_for_comments, :thank_you_note, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index f0ecde99..81693c1d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170403162848) do +ActiveRecord::Schema.define(version: 20170608141612) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -93,7 +93,7 @@ ActiveRecord::Schema.define(version: 20170403162848) do create_table "exercise_tags", force: :cascade do |t| t.integer "exercise_id" t.integer "tag_id" - t.integer "factor", default: 0 + t.integer "factor", default: 1 end create_table "exercises", force: :cascade do |t| @@ -110,7 +110,7 @@ ActiveRecord::Schema.define(version: 20170403162848) do t.boolean "hide_file_tree" t.boolean "allow_file_creation" t.boolean "allow_auto_completion", default: false - t.integer "expected_worktime_seconds", default: 0 + t.integer "expected_worktime_seconds", default: 60 t.integer "expected_difficulty", default: 1 end @@ -247,15 +247,16 @@ ActiveRecord::Schema.define(version: 20170403162848) do end create_table "request_for_comments", force: :cascade do |t| - t.integer "user_id", null: false - t.integer "exercise_id", null: false - t.integer "file_id", null: false + t.integer "user_id", null: false + t.integer "exercise_id", null: false + t.integer "file_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.string "user_type", limit: 255 + t.string "user_type", limit: 255 t.text "question" - t.boolean "solved", default: false + t.boolean "solved", default: false t.integer "submission_id" + t.text "thank_you_note" end create_table "searches", force: :cascade do |t| @@ -296,12 +297,13 @@ ActiveRecord::Schema.define(version: 20170403162848) do end create_table "user_exercise_feedbacks", force: :cascade do |t| - t.integer "exercise_id", null: false - t.integer "user_id", null: false - t.string "user_type", null: false + t.integer "exercise_id", null: false + t.integer "user_id", null: false + t.string "user_type", null: false t.integer "difficulty" t.integer "working_time_seconds" t.string "feedback_text" + t.integer "user_estimated_worktime" end create_table "user_exercise_interventions", force: :cascade do |t| @@ -309,6 +311,8 @@ ActiveRecord::Schema.define(version: 20170403162848) do t.string "user_type" t.integer "exercise_id" t.integer "intervention_id" + t.integer "accumulated_worktime_s" + t.text "reason" t.datetime "created_at" t.datetime "updated_at" end