From 889d449265c415dde2770593a9fda47c4e03557b Mon Sep 17 00:00:00 2001 From: Sujith Haridasan Date: Thu, 28 Jan 2021 12:40:30 +0530 Subject: [PATCH] MDL-52420 mod_assign: Save comment when save buttons are clicked Save comment in the assignment when the Save changes or Save and show next button are clicked at the bottom of the page. Signed-off-by: Sujith Haridasan --- mod/assign/amd/build/grading_panel.min.js | 2 +- mod/assign/amd/build/grading_panel.min.js.map | 2 +- mod/assign/amd/src/grading_panel.js | 10 ++++ .../tests/behat/submission_comments.feature | 53 +++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/mod/assign/amd/build/grading_panel.min.js b/mod/assign/amd/build/grading_panel.min.js index 59b397625a6..180796928be 100644 --- a/mod/assign/amd/build/grading_panel.min.js +++ b/mod/assign/amd/build/grading_panel.min.js @@ -1,2 +1,2 @@ -define ("mod_assign/grading_panel",["jquery","core/yui","core/notification","core/templates","core/fragment","core/ajax","core/str","mod_assign/grading_form_change_checker","mod_assign/grading_events","core/event","core/toast"],function(a,b,c,d,e,f,g,h,i,j,k){var l=function(b){this._regionSelector=b;this._region=a(b);this._userCache=[];this.registerEventListeners()};l.prototype._regionSelector=null;l.prototype._lastUserId=0;l.prototype._lastAttemptNumber=-1;l.prototype._region=null;l.prototype.nextUserId=null;l.prototype.nextUser=!1;l.prototype._niceReplaceNodeContents=function(b,c,e){var f=a.Deferred();b.fadeOut("fast",function(){d.replaceNodeContents(b,c,e);b.fadeIn("fast",function(){f.resolve()})});return f.promise()};l.prototype._saveFormState=function(){var b=a("[data-region=\"grading-actions-form\"] [name=\"sendstudentnotifications\"]").prop("checked");a(".gradeform [name=\"sendstudentnotifications\"]").val(b)};l.prototype._submitForm=function(b,d,e){var g=a(this._region.find("form.gradeform"));a("[data-region=\"overlay\"]").show();g.trigger("save-form-state");j.notifyFormSubmitAjax(g[0]);var h=g.serialize(),i=this._region.attr("data-assignmentid");f.call([{methodname:"mod_assign_submit_grading_form",args:{assignmentid:i,userid:this._lastUserId,jsonformdata:JSON.stringify(h)},done:this._handleFormSubmissionResponse.bind(this,h,d,e),fail:c.exception}])};l.prototype._handleFormSubmissionResponse=function(d,e,f,h){if("undefined"==typeof e){e=this._lastUserId}if(h.length){a(document).trigger("reset",[this._lastUserId,d])}else{g.get_strings([{key:"gradechangessaveddetail",component:"mod_assign"}]).done(function(a){k.add(a[0])}).fail(c.exception);b.use("moodle-core-formchangechecker",function(){M.core_formchangechecker.reset_form_dirty_state()});if(e==this._lastUserId){a(document).trigger("reset",e)}else if(f){a(document).trigger("done-saving-show-next",!0)}else{a(document).trigger("user-changed",e)}}a("[data-region=\"overlay\"]").hide()};l.prototype._resetForm=function(b,c,d){var e=a.Event("custom");if("undefined"==typeof c){c=this._lastUserId}this._lastUserId=0;this._refreshGradingPanel(e,c,d)};l.prototype._chooseAttempt=function(b){var d=a(b.target),e=d.data("submissions"),f=a(document.getElementById(e)),h=f.clone(),i=h.wrap(a("
")).html();g.get_strings([{key:"viewadifferentattempt",component:"mod_assign"},{key:"view",component:"core"},{key:"cancel",component:"core"}]).done(function(b){c.confirm(b[0],i,b[1],b[2],function(){var b=a("input:radio[name='select-attemptnumber']:checked").val();this._refreshGradingPanel(null,this._lastUserId,"",b)}.bind(this))}.bind(this)).fail(c.exception)};l.prototype._addPopoutButtons=function(b){var e=a(b);d.render("mod_assign/popout_button",{}).done(function(a){var b=e.find("[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]").closest(".fitem");b.addClass("has-popout").find("label").parent().append(a);e.on("click","[data-region=\"popout-button\"]",this._togglePopout.bind(this))}.bind(this)).fail(c.exception)};l.prototype._togglePopout=function(b){b.preventDefault();var c=a(b.target).closest(".fitem");if(c.hasClass("popout")){a(".popout").removeClass("popout")}else{a(".popout").removeClass("popout");c.addClass("popout");c.addClass("moodle-has-zindex")}};l.prototype._refreshGradingPanel=function(b,f,g,i){var j=this._region.attr("data-contextid");if("undefined"==typeof g){g=""}if("undefined"==typeof i){i=-1}if(this._lastUserId==f&&this._lastAttemptNumber==i&&""===g){return}this._lastUserId=f;this._lastAttemptNumber=i;a(document).trigger("start-loading-user");window.M.util.js_pending("mod-assign-loading-user");d.render("mod_assign/loading",{}).done(function(b,d){this._niceReplaceNodeContents(this._region,b,d).done(function(){if(0 textarea");if(""!==h.value){var i=g.querySelector(".fd a[id^=\"comment-action-post-\"]");i.click()}}var k=a(this._region.find("form.gradeform"));a("[data-region=\"overlay\"]").show();k.trigger("save-form-state");j.notifyFormSubmitAjax(k[0]);var l=k.serialize(),m=this._region.attr("data-assignmentid");f.call([{methodname:"mod_assign_submit_grading_form",args:{assignmentid:m,userid:this._lastUserId,jsonformdata:JSON.stringify(l)},done:this._handleFormSubmissionResponse.bind(this,l,d,e),fail:c.exception}])};l.prototype._handleFormSubmissionResponse=function(d,e,f,h){if("undefined"==typeof e){e=this._lastUserId}if(h.length){a(document).trigger("reset",[this._lastUserId,d])}else{g.get_strings([{key:"gradechangessaveddetail",component:"mod_assign"}]).done(function(a){k.add(a[0])}).fail(c.exception);b.use("moodle-core-formchangechecker",function(){M.core_formchangechecker.reset_form_dirty_state()});if(e==this._lastUserId){a(document).trigger("reset",e)}else if(f){a(document).trigger("done-saving-show-next",!0)}else{a(document).trigger("user-changed",e)}}a("[data-region=\"overlay\"]").hide()};l.prototype._resetForm=function(b,c,d){var e=a.Event("custom");if("undefined"==typeof c){c=this._lastUserId}this._lastUserId=0;this._refreshGradingPanel(e,c,d)};l.prototype._chooseAttempt=function(b){var d=a(b.target),e=d.data("submissions"),f=a(document.getElementById(e)),h=f.clone(),i=h.wrap(a("")).html();g.get_strings([{key:"viewadifferentattempt",component:"mod_assign"},{key:"view",component:"core"},{key:"cancel",component:"core"}]).done(function(b){c.confirm(b[0],i,b[1],b[2],function(){var b=a("input:radio[name='select-attemptnumber']:checked").val();this._refreshGradingPanel(null,this._lastUserId,"",b)}.bind(this))}.bind(this)).fail(c.exception)};l.prototype._addPopoutButtons=function(b){var e=a(b);d.render("mod_assign/popout_button",{}).done(function(a){var b=e.find("[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]").closest(".fitem");b.addClass("has-popout").find("label").parent().append(a);e.on("click","[data-region=\"popout-button\"]",this._togglePopout.bind(this))}.bind(this)).fail(c.exception)};l.prototype._togglePopout=function(b){b.preventDefault();var c=a(b.target).closest(".fitem");if(c.hasClass("popout")){a(".popout").removeClass("popout")}else{a(".popout").removeClass("popout");c.addClass("popout");c.addClass("moodle-has-zindex")}};l.prototype._refreshGradingPanel=function(b,f,g,i){var j=this._region.attr("data-contextid");if("undefined"==typeof g){g=""}if("undefined"==typeof i){i=-1}if(this._lastUserId==f&&this._lastAttemptNumber==i&&""===g){return}this._lastUserId=f;this._lastAttemptNumber=i;a(document).trigger("start-loading-user");window.M.util.js_pending("mod-assign-loading-user");d.render("mod_assign/loading",{}).done(function(b,d){this._niceReplaceNodeContents(this._region,b,d).done(function(){if(0.\n\n/**\n * Javascript controller for the \"Grading\" panel at the right of the page.\n *\n * @module mod_assign/grading_panel\n * @package mod_assign\n * @class GradingPanel\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/yui', 'core/notification', 'core/templates', 'core/fragment',\n 'core/ajax', 'core/str', 'mod_assign/grading_form_change_checker',\n 'mod_assign/grading_events', 'core/event', 'core/toast'],\n function($, Y, notification, templates, fragment, ajax, str, checker, GradingEvents, Event, Toast) {\n\n /**\n * GradingPanel class.\n *\n * @class GradingPanel\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var GradingPanel = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._userCache = [];\n\n this.registerEventListeners();\n };\n\n /** @type {String} Selector for the page region containing the user navigation. */\n GradingPanel.prototype._regionSelector = null;\n\n /** @type {Integer} Remember the last user id to prevent unnessecary reloads. */\n GradingPanel.prototype._lastUserId = 0;\n\n /** @type {Integer} Remember the last attempt number to prevent unnessecary reloads. */\n GradingPanel.prototype._lastAttemptNumber = -1;\n\n /** @type {JQuery} JQuery node for the page region containing the user navigation. */\n GradingPanel.prototype._region = null;\n\n /** @type {Integer} The id of the next user in the grading list */\n GradingPanel.prototype.nextUserId = null;\n\n /** @type {Boolean} Next user exists in the grading list */\n GradingPanel.prototype.nextUser = false;\n\n /**\n * Fade the dom node out, update it, and fade it back.\n *\n * @private\n * @method _niceReplaceNodeContents\n * @param {JQuery} node\n * @param {String} html\n * @param {String} js\n * @return {Deferred} promise resolved when the animations are complete.\n */\n GradingPanel.prototype._niceReplaceNodeContents = function(node, html, js) {\n var promise = $.Deferred();\n\n node.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(node, html, js);\n node.fadeIn(\"fast\", function() {\n promise.resolve();\n });\n });\n\n return promise.promise();\n };\n\n /**\n * Make sure all form fields have the latest saved state.\n * @private\n * @method _saveFormState\n */\n GradingPanel.prototype._saveFormState = function() {\n // Copy data from notify students checkbox which was moved out of the form.\n var checked = $('[data-region=\"grading-actions-form\"] [name=\"sendstudentnotifications\"]').prop(\"checked\");\n $('.gradeform [name=\"sendstudentnotifications\"]').val(checked);\n };\n\n /**\n * Make form submit via ajax.\n *\n * @private\n * @param {Object} event\n * @param {Integer} nextUserId\n * @param {Boolean} nextUser optional. Load next user in the grading list.\n * @method _submitForm\n */\n GradingPanel.prototype._submitForm = function(event, nextUserId, nextUser) {\n // The form was submitted - send it via ajax instead.\n var form = $(this._region.find('form.gradeform'));\n\n $('[data-region=\"overlay\"]').show();\n\n // We call this, so other modules can update the form with the latest state.\n form.trigger('save-form-state');\n\n // Tell all form fields we are about to submit the form.\n Event.notifyFormSubmitAjax(form[0]);\n\n // Now we get all the current values from the form.\n var data = form.serialize();\n var assignmentid = this._region.attr('data-assignmentid');\n\n // Now we can continue...\n ajax.call([{\n methodname: 'mod_assign_submit_grading_form',\n args: {assignmentid: assignmentid, userid: this._lastUserId, jsonformdata: JSON.stringify(data)},\n done: this._handleFormSubmissionResponse.bind(this, data, nextUserId, nextUser),\n fail: notification.exception\n }]);\n };\n\n /**\n * Handle form submission response.\n *\n * @private\n * @method _handleFormSubmissionResponse\n * @param {Array} formdata - submitted values\n * @param {Integer} nextUserId - optional. The id of the user to load after the form is saved.\n * @param {Array} response List of errors.\n * @param {Boolean} nextUser - optional. If true, switch to next user in the grading list.\n */\n GradingPanel.prototype._handleFormSubmissionResponse = function(formdata, nextUserId, nextUser, response) {\n if (typeof nextUserId === \"undefined\") {\n nextUserId = this._lastUserId;\n }\n if (response.length) {\n // There was an error saving the grade. Re-render the form using the submitted data so we can show\n // validation errors.\n $(document).trigger('reset', [this._lastUserId, formdata]);\n } else {\n str.get_strings([\n {key: 'gradechangessaveddetail', component: 'mod_assign'},\n ]).done(function(strs) {\n Toast.add(strs[0]);\n }).fail(notification.exception);\n Y.use('moodle-core-formchangechecker', function() {\n M.core_formchangechecker.reset_form_dirty_state();\n });\n if (nextUserId == this._lastUserId) {\n $(document).trigger('reset', nextUserId);\n } else if (nextUser) {\n $(document).trigger('done-saving-show-next', true);\n } else {\n $(document).trigger('user-changed', nextUserId);\n }\n }\n $('[data-region=\"overlay\"]').hide();\n };\n\n /**\n * Refresh form with default values.\n *\n * @private\n * @method _resetForm\n * @param {Event} e\n * @param {Integer} userid\n * @param {Array} formdata\n */\n GradingPanel.prototype._resetForm = function(e, userid, formdata) {\n // The form was cancelled - refresh with default values.\n var event = $.Event(\"custom\");\n if (typeof userid == \"undefined\") {\n userid = this._lastUserId;\n }\n this._lastUserId = 0;\n this._refreshGradingPanel(event, userid, formdata);\n };\n\n /**\n * Open a picker to choose an older attempt.\n *\n * @private\n * @param {Object} e\n * @method _chooseAttempt\n */\n GradingPanel.prototype._chooseAttempt = function(e) {\n // Show a dialog.\n\n // The form is in the element pointed to by data-submissions.\n var link = $(e.target);\n var submissionsId = link.data('submissions');\n var submissionsform = $(document.getElementById(submissionsId));\n var formcopy = submissionsform.clone();\n var formhtml = formcopy.wrap($('')).html();\n\n str.get_strings([\n {key: 'viewadifferentattempt', component: 'mod_assign'},\n {key: 'view', component: 'core'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], formhtml, strs[1], strs[2], function() {\n var attemptnumber = $(\"input:radio[name='select-attemptnumber']:checked\").val();\n\n this._refreshGradingPanel(null, this._lastUserId, '', attemptnumber);\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Add popout buttons\n *\n * @private\n * @method _addPopoutButtons\n * @param {JQuery} selector The region selector to add popout buttons to.\n */\n GradingPanel.prototype._addPopoutButtons = function(selector) {\n var region = $(selector);\n\n templates.render('mod_assign/popout_button', {}).done(function(html) {\n var parents = region.find('[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]')\n .closest('.fitem');\n parents.addClass('has-popout').find('label').parent().append(html);\n\n region.on('click', '[data-region=\"popout-button\"]', this._togglePopout.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Make a div \"popout\" or \"popback\".\n *\n * @private\n * @method _togglePopout\n * @param {Event} event\n */\n GradingPanel.prototype._togglePopout = function(event) {\n event.preventDefault();\n var container = $(event.target).closest('.fitem');\n if (container.hasClass('popout')) {\n $('.popout').removeClass('popout');\n } else {\n $('.popout').removeClass('popout');\n container.addClass('popout');\n container.addClass('moodle-has-zindex');\n }\n };\n\n /**\n * Get the user context - re-render the template in the page.\n *\n * @private\n * @method _refreshGradingPanel\n * @param {Event} event\n * @param {Number} userid\n * @param {String} submissiondata serialised submission data.\n * @param {Integer} attemptnumber\n */\n GradingPanel.prototype._refreshGradingPanel = function(event, userid, submissiondata, attemptnumber) {\n var contextid = this._region.attr('data-contextid');\n if (typeof submissiondata === 'undefined') {\n submissiondata = '';\n }\n if (typeof attemptnumber === 'undefined') {\n attemptnumber = -1;\n }\n // Skip reloading if it is the same user.\n if (this._lastUserId == userid && this._lastAttemptNumber == attemptnumber && submissiondata === '') {\n return;\n }\n this._lastUserId = userid;\n this._lastAttemptNumber = attemptnumber;\n $(document).trigger('start-loading-user');\n // Tell behat to back off too.\n window.M.util.js_pending('mod-assign-loading-user');\n // First insert the loading template.\n templates.render('mod_assign/loading', {}).done(function(html, js) {\n // Update the page.\n this._niceReplaceNodeContents(this._region, html, js).done(function() {\n if (userid > 0) {\n this._region.show();\n // Reload the grading form \"fragment\" for this user.\n var params = {userid: userid, attemptnumber: attemptnumber, jsonformdata: JSON.stringify(submissiondata)};\n fragment.loadFragment('mod_assign', 'gradingpanel', contextid, params).done(function(html, js) {\n this._niceReplaceNodeContents(this._region, html, js)\n .done(function() {\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n $(document).on('editor-content-restored', function() {\n // If the editor has some content that has been restored\n // then save the form state again for comparison.\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n });\n $('[data-region=\"attempt-chooser\"]').on('click', this._chooseAttempt.bind(this));\n this._addPopoutButtons('[data-region=\"grade-panel\"] .gradeform');\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }.bind(this))\n .fail(notification.exception);\n }.bind(this)).fail(notification.exception);\n $('[data-region=\"review-panel\"]').show();\n } else {\n this._region.hide();\n $('[data-region=\"review-panel\"]').hide();\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Get next user data and store it in global variables\n *\n * @private\n * @method _getNextUser\n * @param {Event} event\n * @param {Object} data Next user's data\n */\n GradingPanel.prototype._getNextUser = function(event, data) {\n this.nextUserId = data.nextUserId;\n this.nextUser = data.nextUser;\n };\n\n /**\n * Handle the save-and-show-next event\n *\n * @private\n * @method _handleSaveAndShowNext\n */\n GradingPanel.prototype._handleSaveAndShowNext = function() {\n this._submitForm(null, this.nextUserId, this.nextUser);\n };\n\n /**\n * Get the grade panel element.\n *\n * @method getPanelElement\n * @return {jQuery}\n */\n GradingPanel.prototype.getPanelElement = function() {\n return $('[data-region=\"grade-panel\"]');\n };\n\n /**\n * Hide the grade panel.\n *\n * @method collapsePanel\n */\n GradingPanel.prototype.collapsePanel = function() {\n this.getPanelElement().addClass('collapsed');\n };\n\n /**\n * Show the grade panel.\n *\n * @method expandPanel\n */\n GradingPanel.prototype.expandPanel = function() {\n this.getPanelElement().removeClass('collapsed');\n };\n\n /**\n * Register event listeners for the grade panel.\n *\n * @method registerEventListeners\n */\n GradingPanel.prototype.registerEventListeners = function() {\n var docElement = $(document);\n var region = $(this._region);\n // Add an event listener to prevent form submission when pressing enter key.\n region.on('submit', 'form', function(e) {\n e.preventDefault();\n });\n\n docElement.on('next-user', this._getNextUser.bind(this));\n docElement.on('user-changed', this._refreshGradingPanel.bind(this));\n docElement.on('save-changes', this._submitForm.bind(this));\n docElement.on('save-and-show-next', this._handleSaveAndShowNext.bind(this));\n docElement.on('reset', this._resetForm.bind(this));\n\n docElement.on('save-form-state', this._saveFormState.bind(this));\n\n docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL, function() {\n this.collapsePanel();\n }.bind(this));\n\n // We should expand if the review panel is collapsed.\n docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n\n docElement.on(GradingEvents.EXPAND_GRADE_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n };\n\n return GradingPanel;\n});\n"],"file":"grading_panel.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/grading_panel.js"],"names":["define","$","Y","notification","templates","fragment","ajax","str","checker","GradingEvents","Event","Toast","GradingPanel","selector","_regionSelector","_region","_userCache","registerEventListeners","prototype","_lastUserId","_lastAttemptNumber","nextUserId","nextUser","_niceReplaceNodeContents","node","html","js","promise","Deferred","fadeOut","replaceNodeContents","fadeIn","resolve","_saveFormState","checked","prop","val","_submitForm","event","commentAreaElement","document","querySelector","commentTextAreaElement","value","commentActionPostElement","click","form","find","show","trigger","notifyFormSubmitAjax","data","serialize","assignmentid","attr","call","methodname","args","userid","jsonformdata","JSON","stringify","done","_handleFormSubmissionResponse","bind","fail","exception","formdata","response","length","get_strings","key","component","strs","add","use","M","core_formchangechecker","reset_form_dirty_state","hide","_resetForm","e","_refreshGradingPanel","_chooseAttempt","link","target","submissionsId","submissionsform","getElementById","formcopy","clone","formhtml","wrap","confirm","attemptnumber","_addPopoutButtons","region","render","parents","closest","addClass","parent","append","on","_togglePopout","preventDefault","container","hasClass","removeClass","submissiondata","contextid","window","util","js_pending","params","loadFragment","saveFormState","js_complete","_getNextUser","_handleSaveAndShowNext","getPanelElement","collapsePanel","expandPanel","docElement","COLLAPSE_GRADE_PANEL","COLLAPSE_REVIEW_PANEL","EXPAND_GRADE_PANEL"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript controller for the \"Grading\" panel at the right of the page.\n *\n * @module mod_assign/grading_panel\n * @package mod_assign\n * @class GradingPanel\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/yui', 'core/notification', 'core/templates', 'core/fragment',\n 'core/ajax', 'core/str', 'mod_assign/grading_form_change_checker',\n 'mod_assign/grading_events', 'core/event', 'core/toast'],\n function($, Y, notification, templates, fragment, ajax, str, checker, GradingEvents, Event, Toast) {\n\n /**\n * GradingPanel class.\n *\n * @class GradingPanel\n * @param {String} selector The selector for the page region containing the user navigation.\n */\n var GradingPanel = function(selector) {\n this._regionSelector = selector;\n this._region = $(selector);\n this._userCache = [];\n\n this.registerEventListeners();\n };\n\n /** @type {String} Selector for the page region containing the user navigation. */\n GradingPanel.prototype._regionSelector = null;\n\n /** @type {Integer} Remember the last user id to prevent unnessecary reloads. */\n GradingPanel.prototype._lastUserId = 0;\n\n /** @type {Integer} Remember the last attempt number to prevent unnessecary reloads. */\n GradingPanel.prototype._lastAttemptNumber = -1;\n\n /** @type {JQuery} JQuery node for the page region containing the user navigation. */\n GradingPanel.prototype._region = null;\n\n /** @type {Integer} The id of the next user in the grading list */\n GradingPanel.prototype.nextUserId = null;\n\n /** @type {Boolean} Next user exists in the grading list */\n GradingPanel.prototype.nextUser = false;\n\n /**\n * Fade the dom node out, update it, and fade it back.\n *\n * @private\n * @method _niceReplaceNodeContents\n * @param {JQuery} node\n * @param {String} html\n * @param {String} js\n * @return {Deferred} promise resolved when the animations are complete.\n */\n GradingPanel.prototype._niceReplaceNodeContents = function(node, html, js) {\n var promise = $.Deferred();\n\n node.fadeOut(\"fast\", function() {\n templates.replaceNodeContents(node, html, js);\n node.fadeIn(\"fast\", function() {\n promise.resolve();\n });\n });\n\n return promise.promise();\n };\n\n /**\n * Make sure all form fields have the latest saved state.\n * @private\n * @method _saveFormState\n */\n GradingPanel.prototype._saveFormState = function() {\n // Copy data from notify students checkbox which was moved out of the form.\n var checked = $('[data-region=\"grading-actions-form\"] [name=\"sendstudentnotifications\"]').prop(\"checked\");\n $('.gradeform [name=\"sendstudentnotifications\"]').val(checked);\n };\n\n /**\n * Make form submit via ajax.\n *\n * @private\n * @param {Object} event\n * @param {Integer} nextUserId\n * @param {Boolean} nextUser optional. Load next user in the grading list.\n * @method _submitForm\n */\n GradingPanel.prototype._submitForm = function(event, nextUserId, nextUser) {\n // If the form has data in comment-area, then we need to save that comment\n var commentAreaElement = document.querySelector('.comment-area');\n if (commentAreaElement) {\n var commentTextAreaElement = commentAreaElement.querySelector('.db > textarea');\n if (commentTextAreaElement.value !== '') {\n var commentActionPostElement = commentAreaElement.querySelector('.fd a[id^=\"comment-action-post-\"]');\n commentActionPostElement.click();\n }\n }\n\n // The form was submitted - send it via ajax instead.\n var form = $(this._region.find('form.gradeform'));\n\n $('[data-region=\"overlay\"]').show();\n\n // We call this, so other modules can update the form with the latest state.\n form.trigger('save-form-state');\n\n // Tell all form fields we are about to submit the form.\n Event.notifyFormSubmitAjax(form[0]);\n\n // Now we get all the current values from the form.\n var data = form.serialize();\n var assignmentid = this._region.attr('data-assignmentid');\n\n // Now we can continue...\n ajax.call([{\n methodname: 'mod_assign_submit_grading_form',\n args: {assignmentid: assignmentid, userid: this._lastUserId, jsonformdata: JSON.stringify(data)},\n done: this._handleFormSubmissionResponse.bind(this, data, nextUserId, nextUser),\n fail: notification.exception\n }]);\n };\n\n /**\n * Handle form submission response.\n *\n * @private\n * @method _handleFormSubmissionResponse\n * @param {Array} formdata - submitted values\n * @param {Integer} nextUserId - optional. The id of the user to load after the form is saved.\n * @param {Array} response List of errors.\n * @param {Boolean} nextUser - optional. If true, switch to next user in the grading list.\n */\n GradingPanel.prototype._handleFormSubmissionResponse = function(formdata, nextUserId, nextUser, response) {\n if (typeof nextUserId === \"undefined\") {\n nextUserId = this._lastUserId;\n }\n if (response.length) {\n // There was an error saving the grade. Re-render the form using the submitted data so we can show\n // validation errors.\n $(document).trigger('reset', [this._lastUserId, formdata]);\n } else {\n str.get_strings([\n {key: 'gradechangessaveddetail', component: 'mod_assign'},\n ]).done(function(strs) {\n Toast.add(strs[0]);\n }).fail(notification.exception);\n Y.use('moodle-core-formchangechecker', function() {\n M.core_formchangechecker.reset_form_dirty_state();\n });\n if (nextUserId == this._lastUserId) {\n $(document).trigger('reset', nextUserId);\n } else if (nextUser) {\n $(document).trigger('done-saving-show-next', true);\n } else {\n $(document).trigger('user-changed', nextUserId);\n }\n }\n $('[data-region=\"overlay\"]').hide();\n };\n\n /**\n * Refresh form with default values.\n *\n * @private\n * @method _resetForm\n * @param {Event} e\n * @param {Integer} userid\n * @param {Array} formdata\n */\n GradingPanel.prototype._resetForm = function(e, userid, formdata) {\n // The form was cancelled - refresh with default values.\n var event = $.Event(\"custom\");\n if (typeof userid == \"undefined\") {\n userid = this._lastUserId;\n }\n this._lastUserId = 0;\n this._refreshGradingPanel(event, userid, formdata);\n };\n\n /**\n * Open a picker to choose an older attempt.\n *\n * @private\n * @param {Object} e\n * @method _chooseAttempt\n */\n GradingPanel.prototype._chooseAttempt = function(e) {\n // Show a dialog.\n\n // The form is in the element pointed to by data-submissions.\n var link = $(e.target);\n var submissionsId = link.data('submissions');\n var submissionsform = $(document.getElementById(submissionsId));\n var formcopy = submissionsform.clone();\n var formhtml = formcopy.wrap($('')).html();\n\n str.get_strings([\n {key: 'viewadifferentattempt', component: 'mod_assign'},\n {key: 'view', component: 'core'},\n {key: 'cancel', component: 'core'},\n ]).done(function(strs) {\n notification.confirm(strs[0], formhtml, strs[1], strs[2], function() {\n var attemptnumber = $(\"input:radio[name='select-attemptnumber']:checked\").val();\n\n this._refreshGradingPanel(null, this._lastUserId, '', attemptnumber);\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Add popout buttons\n *\n * @private\n * @method _addPopoutButtons\n * @param {JQuery} selector The region selector to add popout buttons to.\n */\n GradingPanel.prototype._addPopoutButtons = function(selector) {\n var region = $(selector);\n\n templates.render('mod_assign/popout_button', {}).done(function(html) {\n var parents = region.find('[data-fieldtype=\"filemanager\"],[data-fieldtype=\"editor\"],[data-fieldtype=\"grading\"]')\n .closest('.fitem');\n parents.addClass('has-popout').find('label').parent().append(html);\n\n region.on('click', '[data-region=\"popout-button\"]', this._togglePopout.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Make a div \"popout\" or \"popback\".\n *\n * @private\n * @method _togglePopout\n * @param {Event} event\n */\n GradingPanel.prototype._togglePopout = function(event) {\n event.preventDefault();\n var container = $(event.target).closest('.fitem');\n if (container.hasClass('popout')) {\n $('.popout').removeClass('popout');\n } else {\n $('.popout').removeClass('popout');\n container.addClass('popout');\n container.addClass('moodle-has-zindex');\n }\n };\n\n /**\n * Get the user context - re-render the template in the page.\n *\n * @private\n * @method _refreshGradingPanel\n * @param {Event} event\n * @param {Number} userid\n * @param {String} submissiondata serialised submission data.\n * @param {Integer} attemptnumber\n */\n GradingPanel.prototype._refreshGradingPanel = function(event, userid, submissiondata, attemptnumber) {\n var contextid = this._region.attr('data-contextid');\n if (typeof submissiondata === 'undefined') {\n submissiondata = '';\n }\n if (typeof attemptnumber === 'undefined') {\n attemptnumber = -1;\n }\n // Skip reloading if it is the same user.\n if (this._lastUserId == userid && this._lastAttemptNumber == attemptnumber && submissiondata === '') {\n return;\n }\n this._lastUserId = userid;\n this._lastAttemptNumber = attemptnumber;\n $(document).trigger('start-loading-user');\n // Tell behat to back off too.\n window.M.util.js_pending('mod-assign-loading-user');\n // First insert the loading template.\n templates.render('mod_assign/loading', {}).done(function(html, js) {\n // Update the page.\n this._niceReplaceNodeContents(this._region, html, js).done(function() {\n if (userid > 0) {\n this._region.show();\n // Reload the grading form \"fragment\" for this user.\n var params = {userid: userid, attemptnumber: attemptnumber, jsonformdata: JSON.stringify(submissiondata)};\n fragment.loadFragment('mod_assign', 'gradingpanel', contextid, params).done(function(html, js) {\n this._niceReplaceNodeContents(this._region, html, js)\n .done(function() {\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n $(document).on('editor-content-restored', function() {\n // If the editor has some content that has been restored\n // then save the form state again for comparison.\n checker.saveFormState('[data-region=\"grade-panel\"] .gradeform');\n });\n $('[data-region=\"attempt-chooser\"]').on('click', this._chooseAttempt.bind(this));\n this._addPopoutButtons('[data-region=\"grade-panel\"] .gradeform');\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }.bind(this))\n .fail(notification.exception);\n }.bind(this)).fail(notification.exception);\n $('[data-region=\"review-panel\"]').show();\n } else {\n this._region.hide();\n $('[data-region=\"review-panel\"]').hide();\n $(document).trigger('finish-loading-user');\n // Tell behat we are friends again.\n window.M.util.js_complete('mod-assign-loading-user');\n }\n }.bind(this));\n }.bind(this)).fail(notification.exception);\n };\n\n /**\n * Get next user data and store it in global variables\n *\n * @private\n * @method _getNextUser\n * @param {Event} event\n * @param {Object} data Next user's data\n */\n GradingPanel.prototype._getNextUser = function(event, data) {\n this.nextUserId = data.nextUserId;\n this.nextUser = data.nextUser;\n };\n\n /**\n * Handle the save-and-show-next event\n *\n * @private\n * @method _handleSaveAndShowNext\n */\n GradingPanel.prototype._handleSaveAndShowNext = function() {\n this._submitForm(null, this.nextUserId, this.nextUser);\n };\n\n /**\n * Get the grade panel element.\n *\n * @method getPanelElement\n * @return {jQuery}\n */\n GradingPanel.prototype.getPanelElement = function() {\n return $('[data-region=\"grade-panel\"]');\n };\n\n /**\n * Hide the grade panel.\n *\n * @method collapsePanel\n */\n GradingPanel.prototype.collapsePanel = function() {\n this.getPanelElement().addClass('collapsed');\n };\n\n /**\n * Show the grade panel.\n *\n * @method expandPanel\n */\n GradingPanel.prototype.expandPanel = function() {\n this.getPanelElement().removeClass('collapsed');\n };\n\n /**\n * Register event listeners for the grade panel.\n *\n * @method registerEventListeners\n */\n GradingPanel.prototype.registerEventListeners = function() {\n var docElement = $(document);\n var region = $(this._region);\n // Add an event listener to prevent form submission when pressing enter key.\n region.on('submit', 'form', function(e) {\n e.preventDefault();\n });\n\n docElement.on('next-user', this._getNextUser.bind(this));\n docElement.on('user-changed', this._refreshGradingPanel.bind(this));\n docElement.on('save-changes', this._submitForm.bind(this));\n docElement.on('save-and-show-next', this._handleSaveAndShowNext.bind(this));\n docElement.on('reset', this._resetForm.bind(this));\n\n docElement.on('save-form-state', this._saveFormState.bind(this));\n\n docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL, function() {\n this.collapsePanel();\n }.bind(this));\n\n // We should expand if the review panel is collapsed.\n docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n\n docElement.on(GradingEvents.EXPAND_GRADE_PANEL, function() {\n this.expandPanel();\n }.bind(this));\n };\n\n return GradingPanel;\n});\n"],"file":"grading_panel.min.js"} \ No newline at end of file diff --git a/mod/assign/amd/src/grading_panel.js b/mod/assign/amd/src/grading_panel.js index dbd75ff0087..fca1a1e018e 100644 --- a/mod/assign/amd/src/grading_panel.js +++ b/mod/assign/amd/src/grading_panel.js @@ -104,6 +104,16 @@ define(['jquery', 'core/yui', 'core/notification', 'core/templates', 'core/fragm * @method _submitForm */ GradingPanel.prototype._submitForm = function(event, nextUserId, nextUser) { + // If the form has data in comment-area, then we need to save that comment + var commentAreaElement = document.querySelector('.comment-area'); + if (commentAreaElement) { + var commentTextAreaElement = commentAreaElement.querySelector('.db > textarea'); + if (commentTextAreaElement.value !== '') { + var commentActionPostElement = commentAreaElement.querySelector('.fd a[id^="comment-action-post-"]'); + commentActionPostElement.click(); + } + } + // The form was submitted - send it via ajax instead. var form = $(this._region.find('form.gradeform')); diff --git a/mod/assign/tests/behat/submission_comments.feature b/mod/assign/tests/behat/submission_comments.feature index ec77765c5c2..7e4ddd5511a 100644 --- a/mod/assign/tests/behat/submission_comments.feature +++ b/mod/assign/tests/behat/submission_comments.feature @@ -12,10 +12,12 @@ Feature: In an assignment, students can comment in their submissions | username | firstname | lastname | email | | teacher1 | Teacher | 1 | teacher1@example.com | | student1 | Student | 1 | student1@example.com | + | student2 | Student | 1 | student2@example.com | And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | + | student2 | C1 | student | Scenario: Student comments an assignment submission Given the following "activities" exist: @@ -45,6 +47,57 @@ Feature: In an assignment, students can comment in their submissions And I should see "Second student comment" And I should not see "First student comment" + Scenario: Teacher updated the comment box and clicked the save changes to reflect the comment + Given the following "activities" exist: + | activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled | + | assign | C1 | assign1 | Test assignment name | Test assignment description | 1 | + And I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Test assignment name" + And I press "Add submission" + And I set the following fields to these values: + | Online text | I'm the student submission | + And I press "Save changes" + And I log out + And I log in as "teacher1" + And I am on "Course 1" course homepage + And I follow "Test assignment name" + And I navigate to "View all submissions" in current page administration + And I click on "Grade" "link" in the "Student 1" "table_row" + And I click on ".comment-link" "css_element" + When I set the field "content" to "Teacher feedback first comment" + And I press "Save changes" + And I should see "Comments (1)" in the ".comment-link" "css_element" + And I click on ".comment-link" "css_element" + Then I should see "Teacher feedback first comment" in the ".comment-list" "css_element" + + Scenario: Teacher updated the comment box and clicked on save and show next to reflect the comment + Given the following "activities" exist: + | activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled | + | assign | C1 | assign1 | Test assignment name | Test assignment description | 1 | + And I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Test assignment name" + And I press "Add submission" + And I set the following fields to these values: + | Online text | I'm the student submission | + And I press "Save changes" + And I log out + And I log in as "teacher1" + And I am on "Course 1" course homepage + And I follow "Test assignment name" + And I navigate to "View all submissions" in current page administration + And I click on "Grade" "link" in the "Student 1" "table_row" + And I click on ".comment-link" "css_element" + When I set the field "content" to "Teacher feedback first comment" + # click the save and show next twice as we have only 2 students + # so the second time you click we reach the same student who made + # the change + And I press "Save and show next" + And I press "Save and show next" + And I click on ".comment-link" "css_element" + Then I should see "Teacher feedback first comment" in the ".comment-list" "css_element" + Scenario: Teacher can comment on an offline assignment Given the following "activities" exist: | activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled | assignmentsubmission_file_enabled | assignfeedback_comments_enabled |