diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index dcff5409..68651f63 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -16,14 +16,14 @@ jobs:
fail-fast: false
matrix:
include:
- - php: '7.4'
- moodle-branch: 'MOODLE_401_STABLE'
+ - php: '8.1'
+ moodle-branch: 'MOODLE_403_STABLE'
database: 'pgsql'
- db-version: '12'
- - php: '7.4'
- moodle-branch: 'MOODLE_401_STABLE'
+ db-version: '13'
+ - php: '8.1'
+ moodle-branch: 'MOODLE_403_STABLE'
database: 'mysqli'
- db-version: '5.7'
+ db-version: '8.0'
- php: '8.1'
moodle-branch: 'MOODLE_404_STABLE'
database: 'pgsql'
@@ -49,9 +49,21 @@ jobs:
database: 'mysqli'
db-version: '8.4'
- php: '8.4'
- moodle-branch: 'v5.0.1'
+ moodle-branch: 'MOODLE_501_STABLE'
database: 'pgsql'
db-version: '15'
+ - php: '8.4'
+ moodle-branch: 'MOODLE_501_STABLE'
+ database: 'mysqli'
+ db-version: '8.4'
+ - php: '8.4'
+ moodle-branch: 'MOODLE_502_STABLE'
+ database: 'pgsql'
+ db-version: '16'
+ - php: '8.4'
+ moodle-branch: 'MOODLE_502_STABLE'
+ database: 'mysqli'
+ db-version: '8.4'
services:
postgres:
diff --git a/amd/build/duedate_form.min.js b/amd/build/duedate_form.min.js
index f4825738..366ade3a 100644
--- a/amd/build/duedate_form.min.js
+++ b/amd/build/duedate_form.min.js
@@ -1,3 +1,3 @@
-define("mod_mumie/duedate_form",["jquery","core/str","core/modal_factory","core/modal_events","core/fragment","core/ajax","core/yui"],(function($,Str,ModalFactory,ModalEvents,Fragment,Ajax,Y){var MumieDueDate=function(selector,contextid,formdata){this.contextid=contextid,this.init(selector,formdata)};return MumieDueDate.prototype.modal=null,MumieDueDate.prototype.contextid=-1,MumieDueDate.prototype.init=function(selector,formdata){const triggers=$(selector);return Str.get_string("mumie_duedate_form","mod_mumie").then(function(title){return ModalFactory.create({type:ModalFactory.types.SAVE_CANCEL,title:title,body:this.getBody(formdata)},triggers)}.bind(this)).then(function(modal){return this.modal=modal,this.modal.setLarge(),this.modal.getRoot().on(ModalEvents.hidden,function(){this.modal.setBody(this.getBody(formdata))}.bind(this)),this.modal.getRoot().on(ModalEvents.save,this.submitForm.bind(this)),this.modal.getRoot().on("submit","form",this.submitFormAjax.bind(this)),this.modal}.bind(this))},MumieDueDate.prototype.getBody=function(formdata){void 0===formdata&&(formdata={});var params={jsonformdata:JSON.stringify(formdata)};return Fragment.loadFragment("mod_mumie","new_duedate_form",this.contextid,params)},MumieDueDate.prototype.handleFormSubmissionResponse=function(){this.modal.hide(),Y.use("moodle-core-formchangechecker",(function(){M.core_formchangechecker.reset_form_dirty_state()})),document.location.reload()},MumieDueDate.prototype.handleFormSubmissionFailure=function(data){this.modal.setBody(this.getBody(data))},MumieDueDate.prototype.submitFormAjax=function(e){e.preventDefault();var formData=this.modal.getRoot().find("form").serialize();Ajax.call([{methodname:"mod_mumie_submit_mumieduedate_form",args:{contextid:this.contextid,jsonformdata:JSON.stringify(formData)},done:this.handleFormSubmissionResponse.bind(this,formData),fail:this.handleFormSubmissionFailure.bind(this,formData)}])},MumieDueDate.prototype.submitForm=function(e){e.preventDefault(),this.modal.getRoot().find("form").submit()},{init:function(selector,contextid,formdata){return new MumieDueDate(selector,contextid,formdata)}}}));
+define("mod_mumie/duedate_form",["jquery","core/str","core/modal_save_cancel","core/modal_events","core/fragment","core/ajax","core_form/changechecker"],(function($,Str,ModalSaveCancel,ModalEvents,Fragment,Ajax,FormChangeChecker){var MumieDueDate=function(selector,contextid,formdata){this.contextid=contextid,this.init(selector,formdata)};return MumieDueDate.prototype.modal=null,MumieDueDate.prototype.contextid=-1,MumieDueDate.prototype.init=function(selector,formdata){$(selector).on("click",function(e){return e.preventDefault(),Str.get_string("mumie_duedate_form","mod_mumie").then(function(title){return ModalSaveCancel.create({title:title,body:this.getBody(formdata),large:!0,removeOnClose:!0})}.bind(this)).then(function(modal){return this.modal=modal,modal.getRoot().on(ModalEvents.save,this.submitForm.bind(this)),modal.getRoot().on("submit","form",this.submitFormAjax.bind(this)),modal.show(),modal}.bind(this))}.bind(this))},MumieDueDate.prototype.getBody=function(formdata){void 0===formdata&&(formdata={});var params={jsonformdata:JSON.stringify(formdata)};return Fragment.loadFragment("mod_mumie","new_duedate_form",this.contextid,params)},MumieDueDate.prototype.handleFormSubmissionResponse=function(){this.modal.hide(),FormChangeChecker.resetAllFormDirtyStates(),document.location.reload()},MumieDueDate.prototype.handleFormSubmissionFailure=function(data){this.modal.setBody(this.getBody(data))},MumieDueDate.prototype.submitFormAjax=function(e){e.preventDefault();var formData=this.modal.getRoot().find("form").serialize();Ajax.call([{methodname:"mod_mumie_submit_mumieduedate_form",args:{contextid:this.contextid,jsonformdata:JSON.stringify(formData)},done:this.handleFormSubmissionResponse.bind(this,formData),fail:this.handleFormSubmissionFailure.bind(this,formData)}])},MumieDueDate.prototype.submitForm=function(e){e.preventDefault(),this.modal.getRoot().find("form").submit()},{init:function(selector,contextid,formdata){return new MumieDueDate(selector,contextid,formdata)}}}));
//# sourceMappingURL=duedate_form.min.js.map
\ No newline at end of file
diff --git a/amd/build/duedate_form.min.js.map b/amd/build/duedate_form.min.js.map
index c1f541a2..39a38f30 100644
--- a/amd/build/duedate_form.min.js.map
+++ b/amd/build/duedate_form.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"duedate_form.min.js","sources":["../src/duedate_form.js"],"sourcesContent":["define(['jquery', 'core/str', 'core/modal_factory', 'core/modal_events', 'core/fragment', 'core/ajax', 'core/yui'],\n function($, Str, ModalFactory, ModalEvents, Fragment, Ajax, Y) {\n\n var MumieDueDate = function(selector, contextid, formdata) {\n this.contextid = contextid;\n this.init(selector, formdata);\n };\n\n MumieDueDate.prototype.modal = null;\n\n MumieDueDate.prototype.contextid = -1;\n\n MumieDueDate.prototype.init = function(selector, formdata) {\n const triggers = $(selector);\n return Str.get_string('mumie_duedate_form', 'mod_mumie').then(function(title) {\n // Create the modal.\n return ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: title,\n body: this.getBody(formdata)\n }, triggers);\n }.bind(this)).then(function(modal) {\n // Keep a reference to the modal.\n this.modal = modal;\n\n // Forms are big, we want a big modal.\n this.modal.setLarge();\n\n // We want to reset the form every time it is opened.\n this.modal.getRoot().on(ModalEvents.hidden, function() {\n this.modal.setBody(this.getBody(formdata));\n }.bind(this));\n\n // We catch the modal save event, and use it to submit the form inside the modal.\n // Triggering a form submission will give JS validation scripts a chance to check for errors.\n this.modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));\n // We also catch the form submit event and use it to submit the form with ajax.\n this.modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));\n\n return this.modal;\n\n }.bind(this));\n };\n\n /**\n * @method getBody\n * @private\n * @param {Object} formdata\n * @return {Promise}\n */\n MumieDueDate.prototype.getBody = function(formdata) {\n if (typeof formdata === \"undefined\") {\n formdata = {};\n }\n // Get the content of the modal.\n var params = {\n jsonformdata: JSON.stringify(formdata)\n };\n return Fragment.loadFragment('mod_mumie', 'new_duedate_form', this.contextid, params);\n };\n\n /**\n * @method handleFormSubmissionResponse\n * @private\n */\n MumieDueDate.prototype.handleFormSubmissionResponse = function() {\n this.modal.hide();\n // We could trigger an event instead.\n // Yuk.\n Y.use('moodle-core-formchangechecker', function() {\n M.core_formchangechecker.reset_form_dirty_state();\n });\n\n document.location.reload();\n };\n\n /**\n * @method handleFormSubmissionFailure\n * @private\n * @param {Object} data\n */\n MumieDueDate.prototype.handleFormSubmissionFailure = function(data) {\n // Oh noes! Epic fail :(\n // Ah wait - this is normal. We need to re-display the form with errors!\n this.modal.setBody(this.getBody(data));\n };\n\n /**\n * Private method\n *\n * @method submitFormAjax\n * @private\n * @param {Event} e Form submission event.\n */\n MumieDueDate.prototype.submitFormAjax = function(e) {\n // We don't want to do a real form submission.\n e.preventDefault();\n\n // Convert all the form elements values to a serialized string.\n var formData = this.modal.getRoot().find('form').serialize();\n // Now we can continue...\n Ajax.call([{\n methodname: 'mod_mumie_submit_mumieduedate_form',\n args: {\n contextid: this.contextid,\n jsonformdata: JSON.stringify(formData)\n },\n done: this.handleFormSubmissionResponse.bind(this, formData),\n fail: this.handleFormSubmissionFailure.bind(this, formData)\n }]);\n };\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n MumieDueDate.prototype.submitForm = function(e) {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n };\n\n return /** @alias module:mod_mumie/newduedate */ {\n // Public variables and functions.\n /**\n * Attach event listeners to initialize this module.\n *\n * @method init\n * @param {string} selector The CSS selector used to find nodes that will trigger this module.\n * @param {int} contextid The contextid for the course.\n * @param {Object} formdata\n * @return {Promise}\n */\n init: function(selector, contextid, formdata) {\n return new MumieDueDate(selector, contextid, formdata);\n }\n };\n\n });"],"names":["define","$","Str","ModalFactory","ModalEvents","Fragment","Ajax","Y","MumieDueDate","selector","contextid","formdata","init","prototype","modal","triggers","get_string","then","title","create","type","types","SAVE_CANCEL","body","this","getBody","bind","setLarge","getRoot","on","hidden","setBody","save","submitForm","submitFormAjax","params","jsonformdata","JSON","stringify","loadFragment","handleFormSubmissionResponse","hide","use","M","core_formchangechecker","reset_form_dirty_state","document","location","reload","handleFormSubmissionFailure","data","e","preventDefault","formData","find","serialize","call","methodname","args","done","fail","submit"],"mappings":"AAAAA,gCAAO,CAAC,SAAU,WAAY,qBAAsB,oBAAqB,gBAAiB,YAAa,aACnG,SAASC,EAAGC,IAAKC,aAAcC,YAAaC,SAAUC,KAAMC,OAEpDC,aAAe,SAASC,SAAUC,UAAWC,eACxCD,UAAYA,eACZE,KAAKH,SAAUE,kBAGxBH,aAAaK,UAAUC,MAAQ,KAE/BN,aAAaK,UAAUH,WAAa,EAEpCF,aAAaK,UAAUD,KAAO,SAASH,SAAUE,gBACvCI,SAAWd,EAAEQ,iBACZP,IAAIc,WAAW,qBAAsB,aAAaC,KAAK,SAASC,cAE5Df,aAAagB,OAAO,CACvBC,KAAMjB,aAAakB,MAAMC,YACzBJ,MAAOA,MACPK,KAAMC,KAAKC,QAAQd,WACpBI,WACLW,KAAKF,OAAOP,KAAK,SAASH,mBAEnBA,MAAQA,WAGRA,MAAMa,gBAGNb,MAAMc,UAAUC,GAAGzB,YAAY0B,OAAQ,gBACnChB,MAAMiB,QAAQP,KAAKC,QAAQd,YAClCe,KAAKF,YAIFV,MAAMc,UAAUC,GAAGzB,YAAY4B,KAAMR,KAAKS,WAAWP,KAAKF,YAE1DV,MAAMc,UAAUC,GAAG,SAAU,OAAQL,KAAKU,eAAeR,KAAKF,OAE5DA,KAAKV,OAEdY,KAAKF,QASXhB,aAAaK,UAAUY,QAAU,SAASd,eACd,IAAbA,WACPA,SAAW,QAGXwB,OAAS,CACTC,aAAcC,KAAKC,UAAU3B,kBAE1BN,SAASkC,aAAa,YAAa,mBAAoBf,KAAKd,UAAWyB,SAOlF3B,aAAaK,UAAU2B,6BAA+B,gBAC7C1B,MAAM2B,OAGXlC,EAAEmC,IAAI,iCAAiC,WACnCC,EAAEC,uBAAuBC,4BAG7BC,SAASC,SAASC,UAQtBxC,aAAaK,UAAUoC,4BAA8B,SAASC,WAGrDpC,MAAMiB,QAAQP,KAAKC,QAAQyB,QAUpC1C,aAAaK,UAAUqB,eAAiB,SAASiB,GAE7CA,EAAEC,qBAGEC,SAAW7B,KAAKV,MAAMc,UAAU0B,KAAK,QAAQC,YAEjDjD,KAAKkD,KAAK,CAAC,CACPC,WAAY,qCACZC,KAAM,CACFhD,UAAWc,KAAKd,UAChB0B,aAAcC,KAAKC,UAAUe,WAEjCM,KAAMnC,KAAKgB,6BAA6Bd,KAAKF,KAAM6B,UACnDO,KAAMpC,KAAKyB,4BAA4BvB,KAAKF,KAAM6B,cAW1D7C,aAAaK,UAAUoB,WAAa,SAASkB,GACzCA,EAAEC,sBACGtC,MAAMc,UAAU0B,KAAK,QAAQO,UAGW,CAW7CjD,KAAM,SAASH,SAAUC,UAAWC,iBACzB,IAAIH,aAAaC,SAAUC,UAAWC"}
\ No newline at end of file
+{"version":3,"file":"duedate_form.min.js","sources":["../src/duedate_form.js"],"sourcesContent":["define([\n 'jquery',\n 'core/str',\n 'core/modal_save_cancel',\n 'core/modal_events',\n 'core/fragment',\n 'core/ajax',\n 'core_form/changechecker',\n], function($, Str, ModalSaveCancel, ModalEvents, Fragment, Ajax, FormChangeChecker) {\n\n var MumieDueDate = function(selector, contextid, formdata) {\n this.contextid = contextid;\n this.init(selector, formdata);\n };\n\n MumieDueDate.prototype.modal = null;\n\n MumieDueDate.prototype.contextid = -1;\n\n MumieDueDate.prototype.init = function(selector, formdata) {\n $(selector).on('click', function(e) {\n e.preventDefault();\n return Str.get_string('mumie_duedate_form', 'mod_mumie').then(function(title) {\n return ModalSaveCancel.create({\n title: title,\n body: this.getBody(formdata),\n large: true,\n removeOnClose: true,\n });\n }.bind(this)).then(function(modal) {\n this.modal = modal;\n\n // We catch the modal save event, and use it to submit the form inside the modal.\n // Triggering a form submission will give JS validation scripts a chance to check for errors.\n modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));\n // We also catch the form submit event and use it to submit the form with ajax.\n modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));\n\n modal.show();\n return modal;\n }.bind(this));\n }.bind(this));\n };\n\n /**\n * @method getBody\n * @private\n * @param {Object} formdata\n * @return {Promise}\n */\n MumieDueDate.prototype.getBody = function(formdata) {\n if (typeof formdata === \"undefined\") {\n formdata = {};\n }\n // Get the content of the modal.\n var params = {\n jsonformdata: JSON.stringify(formdata)\n };\n return Fragment.loadFragment('mod_mumie', 'new_duedate_form', this.contextid, params);\n };\n\n /**\n * @method handleFormSubmissionResponse\n * @private\n */\n MumieDueDate.prototype.handleFormSubmissionResponse = function() {\n this.modal.hide();\n FormChangeChecker.resetAllFormDirtyStates();\n document.location.reload();\n };\n\n /**\n * @method handleFormSubmissionFailure\n * @private\n * @param {Object} data\n */\n MumieDueDate.prototype.handleFormSubmissionFailure = function(data) {\n // Oh noes! Epic fail :(\n // Ah wait - this is normal. We need to re-display the form with errors!\n this.modal.setBody(this.getBody(data));\n };\n\n /**\n * Private method\n *\n * @method submitFormAjax\n * @private\n * @param {Event} e Form submission event.\n */\n MumieDueDate.prototype.submitFormAjax = function(e) {\n // We don't want to do a real form submission.\n e.preventDefault();\n\n // Convert all the form elements values to a serialized string.\n var formData = this.modal.getRoot().find('form').serialize();\n // Now we can continue...\n Ajax.call([{\n methodname: 'mod_mumie_submit_mumieduedate_form',\n args: {\n contextid: this.contextid,\n jsonformdata: JSON.stringify(formData)\n },\n done: this.handleFormSubmissionResponse.bind(this, formData),\n fail: this.handleFormSubmissionFailure.bind(this, formData)\n }]);\n };\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n MumieDueDate.prototype.submitForm = function(e) {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n };\n\n return /** @alias module:mod_mumie/newduedate */ {\n // Public variables and functions.\n /**\n * Attach event listeners to initialize this module.\n *\n * @method init\n * @param {string} selector The CSS selector used to find nodes that will trigger this module.\n * @param {int} contextid The contextid for the course.\n * @param {Object} formdata\n * @return {Promise}\n */\n init: function(selector, contextid, formdata) {\n return new MumieDueDate(selector, contextid, formdata);\n }\n };\n\n });"],"names":["define","$","Str","ModalSaveCancel","ModalEvents","Fragment","Ajax","FormChangeChecker","MumieDueDate","selector","contextid","formdata","init","prototype","modal","on","e","preventDefault","get_string","then","title","create","body","this","getBody","large","removeOnClose","bind","getRoot","save","submitForm","submitFormAjax","show","params","jsonformdata","JSON","stringify","loadFragment","handleFormSubmissionResponse","hide","resetAllFormDirtyStates","document","location","reload","handleFormSubmissionFailure","data","setBody","formData","find","serialize","call","methodname","args","done","fail","submit"],"mappings":"AAAAA,gCAAO,CACH,SACA,WACA,yBACA,oBACA,gBACA,YACA,4BACD,SAASC,EAAGC,IAAKC,gBAAiBC,YAAaC,SAAUC,KAAMC,uBAEtDC,aAAe,SAASC,SAAUC,UAAWC,eACxCD,UAAYA,eACZE,KAAKH,SAAUE,kBAGxBH,aAAaK,UAAUC,MAAQ,KAE/BN,aAAaK,UAAUH,WAAa,EAEpCF,aAAaK,UAAUD,KAAO,SAASH,SAAUE,UAC7CV,EAAEQ,UAAUM,GAAG,QAAS,SAASC,UAC7BA,EAAEC,iBACKf,IAAIgB,WAAW,qBAAsB,aAAaC,KAAK,SAASC,cAC5DjB,gBAAgBkB,OAAO,CAC1BD,MAAOA,MACPE,KAAMC,KAAKC,QAAQb,UACnBc,OAAO,EACPC,eAAe,KAErBC,KAAKJ,OAAOJ,KAAK,SAASL,mBACnBA,MAAQA,MAIbA,MAAMc,UAAUb,GAAGX,YAAYyB,KAAMN,KAAKO,WAAWH,KAAKJ,OAE1DT,MAAMc,UAAUb,GAAG,SAAU,OAAQQ,KAAKQ,eAAeJ,KAAKJ,OAE9DT,MAAMkB,OACClB,OACTa,KAAKJ,QACTI,KAAKJ,QASXf,aAAaK,UAAUW,QAAU,SAASb,eACd,IAAbA,WACPA,SAAW,QAGXsB,OAAS,CACTC,aAAcC,KAAKC,UAAUzB,kBAE1BN,SAASgC,aAAa,YAAa,mBAAoBd,KAAKb,UAAWuB,SAOlFzB,aAAaK,UAAUyB,6BAA+B,gBAC7CxB,MAAMyB,OACXhC,kBAAkBiC,0BAClBC,SAASC,SAASC,UAQtBnC,aAAaK,UAAU+B,4BAA8B,SAASC,WAGrD/B,MAAMgC,QAAQvB,KAAKC,QAAQqB,QAUpCrC,aAAaK,UAAUkB,eAAiB,SAASf,GAE7CA,EAAEC,qBAGE8B,SAAWxB,KAAKT,MAAMc,UAAUoB,KAAK,QAAQC,YAEjD3C,KAAK4C,KAAK,CAAC,CACPC,WAAY,qCACZC,KAAM,CACF1C,UAAWa,KAAKb,UAChBwB,aAAcC,KAAKC,UAAUW,WAEjCM,KAAM9B,KAAKe,6BAA6BX,KAAKJ,KAAMwB,UACnDO,KAAM/B,KAAKqB,4BAA4BjB,KAAKJ,KAAMwB,cAW1DvC,aAAaK,UAAUiB,WAAa,SAASd,GACzCA,EAAEC,sBACGH,MAAMc,UAAUoB,KAAK,QAAQO,UAGW,CAW7C3C,KAAM,SAASH,SAAUC,UAAWC,iBACzB,IAAIH,aAAaC,SAAUC,UAAWC"}
\ No newline at end of file
diff --git a/amd/build/mod_form.min.js b/amd/build/mod_form.min.js
index cf6efb47..1884b2b6 100644
--- a/amd/build/mod_form.min.js
+++ b/amd/build/mod_form.min.js
@@ -1,3 +1,3 @@
-define("mod_mumie/mod_form",["jquery","core/templates","core/modal_factory","auth_mumie/mumie_server_config","core/ajax"],(function(){const addServerButton=document.getElementById("id_add_server_button"),missingConfig=document.getElementsByName("mumie_missing_config")[0];let lmsSelectorUrl,systemLanguage,contextId;const durationController=function(){const durationSelector=document.getElementById("id_duration_selector"),gradedElem=document.getElementById("id_mumie_isgraded");function isUngraded(){return"0"===gradedElem.value}function updateDurationElements(){isUngraded()?(durationSelector.setAttribute("disabled","disabled"),durationSelector.value="unlimited"):durationSelector.removeAttribute("disabled");"unlimited"===durationSelector.value?(document.getElementById("fitem_id_unlimited_info").style.display="",document.getElementById("fitem_id_timelimit").style.display="none",document.getElementById("fitem_id_timelimit_info").style.display="none",document.getElementById("fitem_id_duedate").style.display="none",document.getElementById("fitem_id_duedate_info").style.display="none"):"duedate"===durationSelector.value?(document.getElementById("fitem_id_duedate").style.display="",document.getElementById("fitem_id_duedate_info").style.display="",document.getElementById("fitem_id_unlimited_info").style.display="none",document.getElementById("fitem_id_timelimit").style.display="none",document.getElementById("fitem_id_timelimit_info").style.display="none"):"timelimit"===durationSelector.value&&(document.getElementById("fitem_id_timelimit").style.display="",document.getElementById("fitem_id_timelimit_info").style.display="",document.getElementById("fitem_id_duedate").style.display="none",document.getElementById("fitem_id_duedate_info").style.display="none",document.getElementById("fitem_id_unlimited_info").style.display="none")}return{init:function(){durationSelector.onchange=function(){updateDurationElements()},window.addEventListener("load",(()=>{updateDurationElements()}))},setDurationElements:updateDurationElements,isUngraded:isUngraded,setGradedElemValue:function(isGraded){gradedElem.value=isGraded?"1":"0"},getGradedElemValue:function(){return gradedElem.value}}},serverController=function(){let serverStructure;const serverDropDown=document.getElementById("id_server");return{init:function(structure){serverStructure=structure},getSelectedServer:function(){const selectedServerName=serverDropDown.options[serverDropDown.selectedIndex].text;return serverStructure.find((server=>server.name===selectedServerName))},disable:function(){serverDropDown.disabled=!0,function(elem){for(;elem.firstChild;)elem.removeChild(elem.firstChild)}(serverDropDown)}}}(),problemSelectorController=function(){const problemSelectorButton=document.getElementById("id_prb_selector_btn"),multiProblemSelectorButton=document.getElementById("id_multi_problem_selector_btn");let problemSelectorWindow;const mumieOrg=document.getElementsByName("mumie_org")[0].value;function sendResponse(response){problemSelectorWindow&&problemSelectorWindow.postMessage(JSON.stringify(response),lmsSelectorUrl)}function sendSuccess(){let message=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";sendResponse({success:!0,message:message})}function addMessageListener(){window.addEventListener("message",(event=>{var _importObj$worksheet;if(event.origin!==lmsSelectorUrl)return;const importObj=JSON.parse(event.data),isGraded=!1!==importObj.isGraded,worksheet=null!==(_importObj$worksheet=importObj.worksheet)&&void 0!==_importObj$worksheet?_importObj$worksheet:null;try{courseController.setCourse(importObj.path_to_coursefile),langController.setLanguage(importObj.language),taskController.setSelection(importObj.link,importObj.language,importObj.name),taskController.setIsGraded(isGraded),worksheetController.setWorksheet(worksheet),sendSuccess(),window.focus(),require(["core/str","core/notification"],(function(str,notification){str.get_strings([{key:"mumie_form_updated_selection",component:"mod_mumie"}]).done((function(s){notification.addNotification({message:s[0],type:"info"})})).fail(notification.exception)}))}catch(error){!function(){sendResponse({success:!1,message:arguments.length>0&&void 0!==arguments[0]?arguments[0]:""})}(error.message)}}),!1)}function buildURL(){const gradingType=taskController.getGradingType(),selection=taskController.getDelocalizedTaskLink(),selectedServer=serverController.getSelectedServer().urlprefix;var selectedServerUrl;return(selectedServerUrl=selectedServer,new URL(lmsSelectorUrl).origin===new URL(selectedServerUrl).origin)?"/auth/mumie/problem_selector.php?org="+mumieOrg+"&serverurl="+encodeURIComponent(selectedServer)+"&problemlang="+langController.getSelectedLanguage()+"&origin="+encodeURIComponent(window.location.origin)+"&gradingtype="+gradingType+"&contextid="+contextId+(selection?"&selection="+selection:""):lmsSelectorUrl+"/lms-problem-selector?org="+mumieOrg+"&serverUrl="+encodeURIComponent(selectedServer)+"&problemLang="+langController.getSelectedLanguage()+"&origin="+encodeURIComponent(window.location.origin)+"&uiLang="+systemLanguage+"&gradingType="+gradingType+"&multiCourse=true&worksheet=true"+(selection?"&selection="+selection:"")}return{init:function(){problemSelectorButton.onclick=function(){problemSelectorWindow=window.open(buildURL(),"_blank")},window.onclose=function(){sendSuccess()},window.addEventListener("beforeunload",(function(){sendSuccess()}),!1),addMessageListener(),multiProblemSelectorButton.onclick=function(e){e.preventDefault(),problemSelectorWindow=window.open(lmsSelectorUrl+"/lms-problem-selector?serverUrl="+encodeURIComponent(serverController.getSelectedServer().urlprefix)+"&gradingType=all","_blank","toolbar=0,location=0,menubar=0")}},disable:function(){problemSelectorButton.disabled=!0}}}(),courseController=function(){const courseNameElem=document.getElementById("id_mumie_course"),courseFileElem=document.getElementsByName("mumie_coursefile")[0];function updateCourseName(){var _selectedCourse$name$;const selectedCourse=courseController.getSelectedCourse(),selectedLanguage=langController.getSelectedLanguage();selectedCourse&&selectedLanguage&&(courseNameElem.value=null===(_selectedCourse$name$=selectedCourse.name.find((translation=>translation.language===selectedLanguage)))||void 0===_selectedCourse$name$?void 0:_selectedCourse$name$.value)}return{init:function(){updateCourseName()},getSelectedCourse:function(){return serverController.getSelectedServer().courses.find((course=>course.coursefile===courseFileElem.value))},setCourse:function(courseFile){var coursefile;coursefile=courseFile,courseFileElem.value=coursefile,updateCourseName()}}}(),langController=function(){const languageElem=document.getElementById("id_language");return{getSelectedLanguage:function(){return languageElem.value},setLanguage:function(lang){languageElem.value=lang}}}(),taskController=function(){const taskSelectionInput=document.getElementsByName("taskurl")[0],nameElem=document.getElementById("id_name"),taskDisplayElement=document.getElementById("id_task_display_element");function updateTaskDisplayElement(localizedLink){taskDisplayElement.value=localizedLink}function updateTaskUri(link,language){const localizedLink=function(link,language){return link+"?lang="+language}(link,language);taskSelectionInput.value=localizedLink,updateTaskDisplayElement(localizedLink)}return{init:function(){updateTaskDisplayElement(taskSelectionInput.value)},setSelection:function(link,language,name){updateTaskUri(link,language),function(name){nameElem.value=name}(name)},setIsGraded:function(isGraded){durationController().setGradedElemValue(isGraded),function(){const disabled=durationController().isUngraded();document.getElementById("id_points").disabled=disabled,document.getElementById("id_gradepass").disabled=disabled,document.getElementById("id_gradecat").disabled=disabled,durationController().setDurationElements()}()},getGradingType:function(){const isGraded=durationController().getGradedElemValue();return"1"===isGraded?"graded":"0"===isGraded?"ungraded":"all"},getDelocalizedTaskLink:function(){return(link=taskSelectionInput.value).includes("?lang=")?link.split("?lang=")[0]:link;var link}}}(),multiTaskEditController=function(){const propertySelectionInputs=document.getElementsByName("mumie_multi_edit_property"),selectedTaskProperties=document.getElementsByName("mumie_selected_task_properties")[0];let selectedTaskProp=[];const taskSelectionInputs=document.getElementsByName("mumie_multi_edit_task"),selectedTasks=document.getElementsByName("mumie_selected_tasks")[0];let selectedTaskIds=[];const sectionInputs=document.getElementsByName("mumie_multi_edit_section");return{init:function(){taskSelectionInputs.forEach((function(checkbox){checkbox.onchange=function(){checkbox.checked?selectedTaskIds.push(checkbox.value):selectedTaskIds=selectedTaskIds.filter((elem=>elem!==checkbox.value)),selectedTasks.value=JSON.stringify(selectedTaskIds)}})),propertySelectionInputs.forEach((function(checkbox){checkbox.onchange=function(){checkbox.checked?selectedTaskProp.push(checkbox.value):selectedTaskProp=selectedTaskProp.filter((elem=>elem!==checkbox.value)),selectedTaskProperties.value=JSON.stringify(selectedTaskProp)}})),sectionInputs.forEach((function(sectionCheckbox){sectionCheckbox.onchange=function(){sectionCheckbox.checked?taskSelectionInputs.forEach((function(taskCheckbox){var array,element;taskCheckbox.getAttribute("section")===sectionCheckbox.value&&(taskCheckbox.checked=!0,array=selectedTaskIds,element=taskCheckbox.value,array.includes(element)||array.push(element))})):taskSelectionInputs.forEach((function(taskCheckbox){taskCheckbox.getAttribute("section")===sectionCheckbox.value&&(taskCheckbox.checked=!1,selectedTaskIds=selectedTaskIds.filter((elem=>taskCheckbox.value!==elem)))})),selectedTasks.value=JSON.stringify(selectedTaskIds)}}))}}}(),worksheetController=function(){const worksheetElement=document.getElementById("id_mumie_worksheet");return{setWorksheet:function(worksheet){worksheet?worksheetElement.setAttribute("value",JSON.stringify(worksheet)):worksheetElement.removeAttribute("value")}}}();function disableDropDownMenus(errorKey){require(["core/str","core/notification"],(function(str,notification){str.get_strings([{key:errorKey,component:"mod_mumie"}]).done((function(s){notification.addNotification({message:s[0]+""+missingConfig.getAttribute("value")+"",type:"problem"})})).fail(notification.exception)})),serverController.disable(),problemSelectorController.disable()}return{init:function(contextIdParam,prbSelectorUrl,lang){lmsSelectorUrl=prbSelectorUrl,systemLanguage=lang,contextId=contextIdParam;const isEdit=document.getElementById("id_name").getAttribute("value"),serverStructure=JSON.parse(document.getElementsByName("mumie_server_structure")[0].value);isEdit&&""!==document.getElementsByName("mumie_missing_config")[0].getAttribute("value")?disableDropDownMenus("mumie_form_missing_server"):serverStructure.length?(serverController.init(serverStructure),courseController.init(),taskController.init(),multiTaskEditController.init(),problemSelectorController.init(),durationController().init()):disableDropDownMenus("mumie_form_no_server_conf"),multiTaskEditController.init(),addServerButton&&require(["auth_mumie/mumie_server_config"],(function(MumieServer){MumieServer.init(addServerButton,contextId)}))}}}));
+define("mod_mumie/mod_form",["jquery","core/templates","auth_mumie/mumie_server_config","core/ajax"],(function(){const addServerButton=document.getElementById("id_add_server_button"),missingConfig=document.getElementsByName("mumie_missing_config")[0];let lmsSelectorUrl,systemLanguage,contextId,section;const durationController=function(){const durationSelector=document.getElementById("id_duration_selector"),gradedElem=document.getElementById("id_mumie_isgraded");function isUngraded(){return"0"===gradedElem.value}function updateDurationElements(){var _document$querySelect;isUngraded()?(durationSelector.setAttribute("disabled","disabled"),durationSelector.value="unlimited"):durationSelector.removeAttribute("disabled");"unlimited"===durationSelector.value?(document.getElementById("fitem_id_unlimited_info").style.display="",document.getElementById("fitem_id_timelimit").style.display="none",document.getElementById("fitem_id_timelimit_info").style.display="none",document.getElementById("fitem_id_duedate").style.display="none",document.getElementById("fitem_id_duedate_info").style.display="none"):"duedate"===durationSelector.value?(document.getElementById("fitem_id_duedate").style.display="",document.getElementById("fitem_id_duedate_info").style.display="",document.getElementById("fitem_id_unlimited_info").style.display="none",document.getElementById("fitem_id_timelimit").style.display="none",document.getElementById("fitem_id_timelimit_info").style.display="none"):"timelimit"===durationSelector.value&&(document.getElementById("fitem_id_timelimit").style.display="",document.getElementById("fitem_id_timelimit_info").style.display="",document.getElementById("fitem_id_duedate").style.display="none",document.getElementById("fitem_id_duedate_info").style.display="none",document.getElementById("fitem_id_unlimited_info").style.display="none");const duedatePropRow=null===(_document$querySelect=document.querySelector('[name="mumie_multi_edit_property"][value="duedate"]'))||void 0===_document$querySelect?void 0:_document$querySelect.closest("tr");if(duedatePropRow){const show="duedate"===durationSelector.value;if(duedatePropRow.style.display=show?"":"none",!show){const duedatePropertyCheckbox=duedatePropRow.querySelector('[name="mumie_multi_edit_property"]');duedatePropertyCheckbox&&duedatePropertyCheckbox.checked&&(duedatePropertyCheckbox.checked=!1,duedatePropertyCheckbox.dispatchEvent(new Event("change")))}}}return{init:function(){durationSelector.onchange=function(){updateDurationElements()},window.addEventListener("load",(()=>{updateDurationElements()})),updateDurationElements()},setDurationElements:updateDurationElements,isUngraded:isUngraded,setGradedElemValue:function(isGraded){gradedElem.value=isGraded?"1":"0"},getGradedElemValue:function(){return gradedElem.value}}}(),serverController=function(){let serverStructure;const serverDropDown=document.getElementById("id_server");return{init:function(structure){serverStructure=structure},getSelectedServer:function(){const selectedServerName=serverDropDown.options[serverDropDown.selectedIndex].text;return serverStructure.find((server=>server.name===selectedServerName))},disable:function(){serverDropDown.disabled=!0,function(elem){for(;elem.firstChild;)elem.removeChild(elem.firstChild)}(serverDropDown)}}}(),problemSelectorController=function(){const problemSelectorButton=document.getElementById("id_prb_selector_btn"),multiProblemSelectorButton=document.getElementById("id_multi_problem_selector_btn");let problemSelectorWindow;const mumieOrg=document.getElementsByName("mumie_org")[0].value;function sendResponse(response){problemSelectorWindow&&problemSelectorWindow.postMessage(JSON.stringify(response),lmsSelectorUrl)}function sendSuccess(){let message=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";sendResponse({success:!0,message:message})}function addMessageListener(){window.addEventListener("message",(event=>{if(event.origin===lmsSelectorUrl)try{const importObj=JSON.parse(event.data);Array.isArray(importObj)?taskController.setMultiSelection(importObj):(applyPickerPayloadToForm(importObj),require(["core/str","core/notification"],(function(str,notification){str.get_strings([{key:"mumie_form_updated_selection",component:"mod_mumie"}]).done((function(s){notification.addNotification({message:s[0],type:"info"})})).fail(notification.exception)}))),sendSuccess(),window.focus()}catch(error){!function(){sendResponse({success:!1,message:arguments.length>0&&void 0!==arguments[0]?arguments[0]:""})}(error.message)}}),!1)}function buildURL(){let multiSelect=arguments.length>0&&void 0!==arguments[0]&&arguments[0];const gradingType=multiSelect?"all":taskController.getGradingType(),selectedServer=serverController.getSelectedServer().urlprefix,useSSO=shouldUseSSO(lmsSelectorUrl,selectedServer),base=useSSO?"/auth/mumie/problem_selector.php?org="+mumieOrg+"&serverurl="+encodeURIComponent(selectedServer)+"&problemlang="+langController.getSelectedLanguage()+"&origin="+encodeURIComponent(window.location.origin)+"&gradingtype="+gradingType+"&contextid="+contextId:lmsSelectorUrl+"/lms-problem-selector?org="+mumieOrg+"&serverUrl="+encodeURIComponent(selectedServer)+"&problemLang="+langController.getSelectedLanguage()+"&origin="+encodeURIComponent(window.location.origin)+"&uiLang="+systemLanguage+"&gradingType="+gradingType+"&multiCourse=true&worksheet=true";if(multiSelect)return base+(useSSO?"&multiselect=true":"&multiSelect=true");const selection=taskController.getDelocalizedTaskLink();return selection?base+"&selection="+selection:base}function shouldUseSSO(problemSelectorUrl,selectedServerUrl){return new URL(problemSelectorUrl).origin===new URL(selectedServerUrl).origin}return{init:function(){problemSelectorButton.onclick=function(){problemSelectorWindow=window.open(buildURL(),"_blank")},window.onclose=function(){sendSuccess()},window.addEventListener("beforeunload",(function(){sendSuccess()}),!1),addMessageListener(),multiProblemSelectorButton.onclick=function(e){e.preventDefault(),problemSelectorWindow=window.open(buildURL(!0),"_blank","toolbar=0,location=0,menubar=0")}},disable:function(){problemSelectorButton.disabled=!0}}}();function applyPickerPayloadToForm(payload){var _payload$worksheet;const isGraded=!1!==payload.isGraded,worksheet=null!==(_payload$worksheet=payload.worksheet)&&void 0!==_payload$worksheet?_payload$worksheet:null;courseController.setCourse(payload.path_to_coursefile),langController.setLanguage(payload.language),taskController.setSelection(payload.link,payload.language,payload.name),taskController.setIsGraded(isGraded),worksheetController.setWorksheet(worksheet)}const courseController=function(){const courseNameElem=document.getElementById("id_mumie_course"),courseFileElem=document.getElementsByName("mumie_coursefile")[0];function updateCourseName(){var _selectedCourse$name$;const selectedCourse=courseController.getSelectedCourse(),selectedLanguage=langController.getSelectedLanguage();selectedCourse&&selectedLanguage&&(courseNameElem.value=null===(_selectedCourse$name$=selectedCourse.name.find((translation=>translation.language===selectedLanguage)))||void 0===_selectedCourse$name$?void 0:_selectedCourse$name$.value)}return{init:function(){updateCourseName()},getSelectedCourse:function(){return serverController.getSelectedServer().courses.find((course=>course.coursefile===courseFileElem.value))},setCourse:function(courseFile){var coursefile;coursefile=courseFile,courseFileElem.value=coursefile,updateCourseName()}}}(),langController=function(){const languageElem=document.getElementById("id_language");return{getSelectedLanguage:function(){return languageElem.value},setLanguage:function(lang){languageElem.value=lang}}}(),taskController=function(){const taskSelectionInput=document.getElementsByName("taskurl")[0],nameElem=document.getElementById("id_name"),taskDisplayElement=document.getElementById("id_task_display_element");function updateTaskDisplayElement(localizedLink){taskDisplayElement.value=localizedLink}function updateTaskUri(link,language){const localizedLink=function(link,language){return link+"?lang="+language}(link,language);taskSelectionInput.value=localizedLink,updateTaskDisplayElement(localizedLink)}function setMultiSelection(tasks){const tasksField=document.getElementsByName("mumie_multi_tasks")[0],summary=document.getElementById("mumie_multi_tasks_summary");if(tasksField&&(tasksField.value=JSON.stringify(tasks)),summary){const taskListItems=tasks.map((task=>{const taskListItem=document.createElement("li");return taskListItem.textContent=task.name,taskListItem}));summary.innerHTML="";const summaryHeading=document.createElement("div"),taskList=document.createElement("ul");require(["core/str"],(function(Str){Str.get_string("mumie_multi_tasks_selected","mod_mumie",tasks.length).then((function(headingText){summaryHeading.textContent=headingText}))})),taskList.classList.add("mumie_multi_tasks_list"),taskListItems.forEach((taskListItem=>taskList.appendChild(taskListItem))),summary.appendChild(summaryHeading),summary.appendChild(taskList),summary.style.display="block"}const nameField=document.getElementById("id_name");nameField.disabled=!0,nameField.value="",nameField.removeAttribute("required");const taskDisplayElem=document.getElementById("id_task_display_element");taskDisplayElem&&(taskDisplayElem.value="");const courseNameElem=document.getElementById("id_mumie_course");courseNameElem&&(courseNameElem.value="");const nameFieldContainer=document.getElementById("fitem_id_name");nameFieldContainer&&nameFieldContainer.querySelectorAll('.req, .text-danger, [title="Required field"]').forEach((requiredIndicator=>{requiredIndicator.style.display="none"}));const requiredLegend=document.querySelector(".fdescription.required");requiredLegend&&(requiredLegend.style.display="none")}return{init:function(){updateTaskDisplayElement(taskSelectionInput.value)},setSelection:function(link,language,name){const tasksField=document.getElementsByName("mumie_multi_tasks")[0];tasksField&&(tasksField.value="");const summary=document.getElementById("mumie_multi_tasks_summary");summary&&(summary.style.display="none",summary.innerHTML="");const nameField=document.getElementById("id_name");nameField&&(nameField.disabled=!1,nameField.setAttribute("required","required"));const nameFieldContainer=document.getElementById("fitem_id_name");nameFieldContainer&&nameFieldContainer.querySelectorAll('.req, .text-danger, [title="Required field"]').forEach((requiredIndicator=>{requiredIndicator.style.display=""}));const requiredLegend=document.querySelector(".fdescription.required");requiredLegend&&(requiredLegend.style.display=""),updateTaskUri(link,language),function(name){nameElem.value=name}(name)},setIsGraded:function(isGraded){durationController.setGradedElemValue(isGraded),function(){const disabled=durationController.isUngraded();document.getElementById("id_points").disabled=disabled,document.getElementById("id_gradepass").disabled=disabled,document.getElementById("id_gradecat").disabled=disabled,durationController.setDurationElements()}()},getGradingType:function(){const isGraded=durationController.getGradedElemValue();return"1"===isGraded?"graded":"0"===isGraded?"ungraded":"all"},getDelocalizedTaskLink:function(){return(link=taskSelectionInput.value).includes("?lang=")?link.split("?lang=")[0]:link;var link},setMultiSelection:setMultiSelection,hasMultiTasks:function(){const tasksField=document.getElementsByName("mumie_multi_tasks")[0];return tasksField&&!!tasksField.value},submitMultiTasks:function(){var _document$getElements;const tasksField=document.getElementsByName("mumie_multi_tasks")[0],courseId=null===(_document$getElements=document.getElementsByName("course")[0])||void 0===_document$getElements?void 0:_document$getElements.value,submitButton=document.getElementById("id_submitbutton"),form=submitButton&&submitButton.closest("form"),tasks=JSON.parse(tasksField.value),tasksFormData=tasks.map((task=>(applyPickerPayloadToForm(task),function(form){const disabledFields=Array.from(form.querySelectorAll(":disabled"));disabledFields.forEach((field=>{field.disabled=!1}));try{const omit=["sesskey","_qf__mod_mumie_mod_form"];return Array.from(new FormData(form)).filter((_ref=>{let[key]=_ref;return!omit.includes(key)})).map((_ref2=>{let[key,value]=_ref2;return encodeURIComponent(key)+"="+encodeURIComponent(value)})).join("&")}finally{disabledFields.forEach((field=>{field.disabled=!0}))}}(form))));setMultiSelection(tasks),require(["core/ajax","core/notification"],(function(Ajax,notification){Ajax.call([{methodname:"mod_mumie_create_multiple_mumie_tasks",args:{contextid:parseInt(contextId),section:section,tasksformdata:tasksFormData}}])[0].done((function(){window.location.href=M.cfg.wwwroot+"/course/view.php?id="+courseId})).fail((function(error){notification.alert("",error.message)}))}))}}}(),multiTaskEditController=function(){const propertySelectionInputs=document.getElementsByName("mumie_multi_edit_property"),selectedTaskProperties=document.getElementsByName("mumie_selected_task_properties")[0];let selectedTaskProp=[];const taskSelectionInputs=document.getElementsByName("mumie_multi_edit_task"),selectedTasks=document.getElementsByName("mumie_selected_tasks")[0];let selectedTaskIds=[];const sectionInputs=document.getElementsByName("mumie_multi_edit_section");function setPropertySelectionListeners(){propertySelectionInputs.forEach((function(checkbox){checkbox.onchange=function(){checkbox.checked?selectedTaskProp.push(checkbox.value):selectedTaskProp=selectedTaskProp.filter((elem=>elem!==checkbox.value)),selectedTaskProperties.value=JSON.stringify(selectedTaskProp),function(){const duedateSelected=Array.from(propertySelectionInputs).some((checkbox=>"duedate"===checkbox.value&&checkbox.checked));document.querySelectorAll(".mumie-form-working-period-not-duedate-warning").forEach((function(elem){elem.style.display=duedateSelected?"":"none"}))}()}}))}return{init:function(){taskSelectionInputs.forEach((function(checkbox){checkbox.onchange=function(){checkbox.checked?selectedTaskIds.push(checkbox.value):selectedTaskIds=selectedTaskIds.filter((elem=>elem!==checkbox.value)),selectedTasks.value=JSON.stringify(selectedTaskIds)}})),setPropertySelectionListeners(),sectionInputs.forEach((function(sectionCheckbox){sectionCheckbox.onchange=function(){sectionCheckbox.checked?taskSelectionInputs.forEach((function(taskCheckbox){var array,element;taskCheckbox.getAttribute("section")===sectionCheckbox.value&&(taskCheckbox.checked=!0,array=selectedTaskIds,element=taskCheckbox.value,array.includes(element)||array.push(element))})):taskSelectionInputs.forEach((function(taskCheckbox){taskCheckbox.getAttribute("section")===sectionCheckbox.value&&(taskCheckbox.checked=!1,selectedTaskIds=selectedTaskIds.filter((elem=>taskCheckbox.value!==elem)))})),selectedTasks.value=JSON.stringify(selectedTaskIds)}}))}}}(),worksheetController=function(){const worksheetElement=document.getElementById("id_mumie_worksheet");return{setWorksheet:function(worksheet){worksheet?worksheetElement.setAttribute("value",JSON.stringify(worksheet)):worksheetElement.removeAttribute("value")}}}();function disableDropDownMenus(errorKey){require(["core/str","core/notification"],(function(str,notification){str.get_strings([{key:errorKey,component:"mod_mumie"}]).done((function(s){notification.addNotification({message:s[0]+""+missingConfig.getAttribute("value")+"",type:"problem"})})).fail(notification.exception)})),serverController.disable(),problemSelectorController.disable()}return{init:function(contextIdParam,prbSelectorUrl,lang,sectionParam){lmsSelectorUrl=prbSelectorUrl,systemLanguage=lang,contextId=contextIdParam,section=sectionParam;const isEdit=document.getElementById("id_name").getAttribute("value"),serverStructure=JSON.parse(document.getElementsByName("mumie_server_structure")[0].value);isEdit&&""!==document.getElementsByName("mumie_missing_config")[0].getAttribute("value")?disableDropDownMenus("mumie_form_missing_server"):serverStructure.length?(serverController.init(serverStructure),courseController.init(),taskController.init(),multiTaskEditController.init(),problemSelectorController.init(),durationController.init()):disableDropDownMenus("mumie_form_no_server_conf"),addServerButton&&require(["auth_mumie/mumie_server_config"],(function(MumieServer){MumieServer.init(addServerButton,contextId)}));const submitButton=document.getElementById("id_submitbutton"),form=submitButton&&submitButton.closest("form");if(form){let cancelClicked=!1;const cancelButton=document.getElementById("id_cancel");cancelButton&&cancelButton.addEventListener("click",(function(){cancelClicked=!0})),form.addEventListener("submit",(function(e){cancelClicked?cancelClicked=!1:taskController.hasMultiTasks()&&(e.preventDefault(),taskController.submitMultiTasks())}))}}}}));
//# sourceMappingURL=mod_form.min.js.map
\ No newline at end of file
diff --git a/amd/build/mod_form.min.js.map b/amd/build/mod_form.min.js.map
index 5c5d82b9..8f24a322 100644
--- a/amd/build/mod_form.min.js.map
+++ b/amd/build/mod_form.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"mod_form.min.js","sources":["../src/mod_form.js"],"sourcesContent":["define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_server_config', 'core/ajax'],\n function() {\n const addServerButton = document.getElementById(\"id_add_server_button\");\n const missingConfig = document.getElementsByName(\"mumie_missing_config\")[0];\n let lmsSelectorUrl;\n let systemLanguage;\n let contextId;\n\n\n const durationController = (function() {\n\n const durationSelector = document.getElementById('id_duration_selector');\n const gradedElem = document.getElementById('id_mumie_isgraded');\n\n /**\n * Returns if task is ungraded like courses and articles.\n */\n function isUngraded() {\n return gradedElem.value === '0';\n }\n\n /**\n * Updates the visibility of elements around duration selector.\n */\n function updateDurationElements() {\n const disabled = isUngraded();\n if (disabled) {\n durationSelector.setAttribute('disabled', 'disabled');\n durationSelector.value = 'unlimited';\n } else {\n durationSelector.removeAttribute('disabled');\n }\n\n const displayNone = 'none';\n\n if (durationSelector.value === 'unlimited') {\n document.getElementById('fitem_id_unlimited_info').style.display = '';\n\n document.getElementById('fitem_id_timelimit').style.display = displayNone;\n document.getElementById('fitem_id_timelimit_info').style.display = displayNone;\n document.getElementById('fitem_id_duedate').style.display = displayNone;\n document.getElementById('fitem_id_duedate_info').style.display = displayNone;\n } else if (durationSelector.value === 'duedate') {\n document.getElementById('fitem_id_duedate').style.display = '';\n document.getElementById('fitem_id_duedate_info').style.display = '';\n\n document.getElementById('fitem_id_unlimited_info').style.display = displayNone;\n document.getElementById('fitem_id_timelimit').style.display = displayNone;\n document.getElementById('fitem_id_timelimit_info').style.display = displayNone;\n } else if (durationSelector.value === 'timelimit') {\n document.getElementById('fitem_id_timelimit').style.display = '';\n document.getElementById('fitem_id_timelimit_info').style.display = '';\n\n document.getElementById('fitem_id_duedate').style.display = displayNone;\n document.getElementById('fitem_id_duedate_info').style.display = displayNone;\n document.getElementById('fitem_id_unlimited_info').style.display = displayNone;\n }\n }\n\n return {\n init: function() {\n durationSelector.onchange = function() {\n updateDurationElements();\n };\n window.addEventListener(\"load\", () => {\n updateDurationElements();\n });\n },\n setDurationElements: updateDurationElements,\n isUngraded: isUngraded,\n setGradedElemValue: function(isGraded) {\n gradedElem.value = isGraded ? '1' : '0';\n },\n getGradedElemValue: function() {\n return gradedElem.value;\n }\n };\n });\n\n const serverController = (function() {\n let serverStructure;\n const serverDropDown = document.getElementById(\"id_server\");\n\n return {\n init: function(structure) {\n serverStructure = structure;\n },\n getSelectedServer: function() {\n const selectedServerName = serverDropDown.options[serverDropDown.selectedIndex].text;\n return serverStructure.find(server => server.name === selectedServerName);\n },\n disable: function() {\n serverDropDown.disabled = true;\n removeChildElems(serverDropDown);\n }\n };\n })();\n\n const problemSelectorController = (function() {\n const problemSelectorButton = document.getElementById('id_prb_selector_btn');\n const multiProblemSelectorButton = document.getElementById('id_multi_problem_selector_btn');\n let problemSelectorWindow;\n const mumieOrg = document.getElementsByName('mumie_org')[0].value;\n\n /**\n * Send a message to the problem selector window.\n *\n * Don't do anything if there is no problem selector window.\n * @param {Object} response\n */\n function sendResponse(response) {\n if (!problemSelectorWindow) {\n return;\n }\n problemSelectorWindow.postMessage(JSON.stringify(response), lmsSelectorUrl);\n }\n\n /**\n * Send a success message to a problem selector window\n * @param {string} message\n */\n function sendSuccess(message = '') {\n sendResponse({\n success: true,\n message: message\n });\n }\n\n /**\n * Send a failure message to a problem selector window\n * @param {string} message\n */\n function sendFailure(message = '') {\n sendResponse({\n success: false,\n message: message\n });\n }\n\n /**\n * Add an event listener that accepts messages from LMS-Browser and updates the selected problem.\n */\n function addMessageListener() {\n window.addEventListener('message', (event) => {\n if (event.origin !== lmsSelectorUrl) {\n return;\n }\n const importObj = JSON.parse(event.data);\n const isGraded = importObj.isGraded !== false;\n const worksheet = importObj.worksheet ?? null;\n try {\n courseController.setCourse(importObj.path_to_coursefile);\n langController.setLanguage(importObj.language);\n taskController.setSelection(importObj.link, importObj.language, importObj.name);\n taskController.setIsGraded(isGraded);\n worksheetController.setWorksheet(worksheet);\n sendSuccess();\n window.focus();\n displayProblemSelectedMessage();\n } catch (error) {\n sendFailure(error.message);\n }\n }, false);\n }\n\n /**\n * Display a success message in Moodle that a problem was successfully selected.\n */\n function displayProblemSelectedMessage() {\n require(['core/str', \"core/notification\"], function(str, notification) {\n str.get_strings([{\n 'key': 'mumie_form_updated_selection',\n component: 'mod_mumie'\n }]).done(function(s) {\n notification.addNotification({\n message: s[0],\n type: \"info\"\n });\n }).fail(notification.exception);\n });\n }\n\n /**\n * Builds the URL to the Problem Selector\n * @returns {string} URL to the Problem Selector\n */\n function buildURL() {\n const gradingType = taskController.getGradingType();\n const selection = taskController.getDelocalizedTaskLink();\n const selectedServer = serverController.getSelectedServer().urlprefix;\n const useSSO = shouldUseSSO(lmsSelectorUrl, selectedServer);\n if (useSSO) {\n return '/auth/mumie/problem_selector.php?' +\n 'org=' +\n mumieOrg +\n '&serverurl=' +\n encodeURIComponent(selectedServer) +\n '&problemlang=' +\n langController.getSelectedLanguage() +\n '&origin=' + encodeURIComponent(window.location.origin) +\n '&gradingtype=' + gradingType +\n '&contextid=' + contextId +\n (selection ? '&selection=' + selection : '');\n }\n return lmsSelectorUrl +\n '/lms-problem-selector?' +\n 'org=' +\n mumieOrg +\n '&serverUrl=' +\n encodeURIComponent(selectedServer) +\n '&problemLang=' +\n langController.getSelectedLanguage() +\n '&origin=' + encodeURIComponent(window.location.origin) +\n '&uiLang=' + systemLanguage +\n '&gradingType=' + gradingType +\n '&multiCourse=true' +\n '&worksheet=true' +\n (selection ? '&selection=' + selection : '');\n }\n\n /**\n * Determines whether the Single Sign-On (SSO) should be used when opening the Problem Selector.\n * SSO is only supposed to be used when the Problem Selector URL has the same origin as the\n * URL of the selected MUMIE server.\n *\n * @param {string} problemSelectorUrl - The URL of the problem selector.\n * @param {string} selectedServerUrl - The URL of the selected MUMIE server\n * @returns {boolean} Whether SSO should be used for the Problem Selector or not\n */\n function shouldUseSSO(problemSelectorUrl, selectedServerUrl) {\n return new URL(problemSelectorUrl).origin === new URL(selectedServerUrl).origin;\n }\n\n return {\n init: function() {\n problemSelectorButton.onclick = function() {\n problemSelectorWindow = window.open(buildURL(), '_blank');\n };\n\n window.onclose = function() {\n sendSuccess();\n };\n\n window.addEventListener(\"beforeunload\", function() {\n sendSuccess();\n }, false);\n\n addMessageListener();\n\n multiProblemSelectorButton.onclick = function(e) {\n e.preventDefault();\n problemSelectorWindow = window.open(\n lmsSelectorUrl +\n '/lms-problem-selector?' +\n \"serverUrl=\" +\n encodeURIComponent(serverController.getSelectedServer().urlprefix) +\n '&gradingType=all',\n \"_blank\",\n 'toolbar=0,location=0,menubar=0'\n );\n };\n },\n disable: function() {\n problemSelectorButton.disabled = true;\n }\n };\n })();\n\n const courseController = (function() {\n const courseNameElem = document.getElementById(\"id_mumie_course\");\n const courseFileElem = document.getElementsByName(\"mumie_coursefile\")[0];\n\n\n /**\n * Update the hidden input field with the selected course's course file path\n *\n * @param {string} coursefile\n */\n function updateCourseFilePath(coursefile) {\n courseFileElem.value = coursefile;\n updateCourseName();\n }\n\n /**\n * Update displayed course name.\n */\n function updateCourseName() {\n const selectedCourse = courseController.getSelectedCourse();\n const selectedLanguage = langController.getSelectedLanguage();\n if (!selectedCourse || !selectedLanguage) {\n return;\n }\n courseNameElem.value = selectedCourse.name\n .find(translation => translation.language === selectedLanguage)?.value;\n }\n\n return {\n init: function() {\n updateCourseName();\n },\n getSelectedCourse: function() {\n const courses = serverController.getSelectedServer().courses;\n return courses.find(course => course.coursefile === courseFileElem.value);\n },\n setCourse: function(courseFile) {\n updateCourseFilePath(courseFile);\n }\n };\n })();\n\n const langController = (function() {\n const languageElem = document.getElementById(\"id_language\");\n return {\n getSelectedLanguage: function() {\n return languageElem.value;\n },\n setLanguage: function(lang) {\n languageElem.value = lang;\n }\n };\n })();\n\n const taskController = (function() {\n const taskSelectionInput = document.getElementsByName(\"taskurl\")[0];\n const nameElem = document.getElementById(\"id_name\");\n const taskDisplayElement = document.getElementById(\"id_task_display_element\");\n const LANG_REQUEST_PARAM_PREFIX = \"?lang=\";\n\n /**\n * Update the activity's name in the input field\n * @param {string} name\n */\n function updateName(name) {\n nameElem.value = name;\n }\n\n /**\n * @param {string} localizedLink\n */\n function updateTaskDisplayElement(localizedLink) {\n taskDisplayElement.value = localizedLink;\n }\n\n /**\n * Update task uri\n * @param {string} link\n * @param {string} language\n */\n function updateTaskUri(link, language) {\n const localizedLink = localizeLink(link, language);\n taskSelectionInput.value = localizedLink;\n updateTaskDisplayElement(localizedLink);\n }\n\n /**\n * Add lang request param to link\n * @param {string} link\n * @param {string} language\n * @returns {string} Link with lang request param\n */\n function localizeLink(link, language) {\n return link + LANG_REQUEST_PARAM_PREFIX + language;\n }\n\n /**\n * Remove lang request param from link\n * @param {string} link Link that may have lang request param\n * @returns {string} Link without lang request param\n */\n function delocalizeLink(link) {\n if (link.includes(LANG_REQUEST_PARAM_PREFIX)) {\n return link.split(LANG_REQUEST_PARAM_PREFIX)[0];\n }\n return link;\n }\n\n /**\n * Form inputs related to grades should be disabled if the MUMIE Task is not graded.\n */\n function updateGradeEditability() {\n const disabled = durationController().isUngraded();\n document.getElementById('id_points').disabled = disabled;\n document.getElementById('id_gradepass').disabled = disabled;\n document.getElementById('id_gradecat').disabled = disabled;\n durationController().setDurationElements();\n }\n\n return {\n init: function() {\n updateTaskDisplayElement(taskSelectionInput.value);\n },\n setSelection: function(link, language, name) {\n updateTaskUri(link, language);\n updateName(name);\n },\n setIsGraded: function(isGraded) {\n durationController().setGradedElemValue(isGraded);\n updateGradeEditability();\n },\n getGradingType: function() {\n const isGraded = durationController().getGradedElemValue();\n if (isGraded === '1') {\n return 'graded';\n } else if (isGraded === '0') {\n return 'ungraded';\n }\n return 'all';\n },\n getDelocalizedTaskLink: function() {\n return delocalizeLink(taskSelectionInput.value);\n }\n };\n })();\n\n\n const multiTaskEditController = (function() {\n const propertySelectionInputs = document.getElementsByName(\"mumie_multi_edit_property\");\n const selectedTaskProperties = document.getElementsByName(\"mumie_selected_task_properties\")[0];\n let selectedTaskProp = [];\n const taskSelectionInputs = document.getElementsByName(\"mumie_multi_edit_task\");\n const selectedTasks = document.getElementsByName(\"mumie_selected_tasks\")[0];\n let selectedTaskIds = [];\n const sectionInputs = document.getElementsByName(\"mumie_multi_edit_section\");\n\n /**\n * Push an element to an array, if it's not already included.\n *\n * @param {string[]} array\n * @param {string} element\n */\n function pushIfNotExists(array, element) {\n if (!array.includes(element)) {\n array.push(element);\n }\n }\n\n /**\n * Set selection listeners for other MUMIE Tasks in the course.\n */\n function setTaskSelectionListeners() {\n taskSelectionInputs.forEach(function(checkbox) {\n checkbox.onchange = function() {\n if (!checkbox.checked) {\n selectedTaskIds = selectedTaskIds.filter(elem => elem !== checkbox.value);\n } else {\n selectedTaskIds.push(checkbox.value);\n }\n selectedTasks.value = JSON.stringify(selectedTaskIds);\n };\n });\n }\n\n /**\n * Set selection listeners for properties to apply to MUMIE Tasks in the course.\n */\n function setPropertySelectionListeners() {\n propertySelectionInputs.forEach(function(checkbox) {\n checkbox.onchange = function() {\n if (!checkbox.checked) {\n selectedTaskProp = selectedTaskProp.filter(elem => elem !== checkbox.value);\n } else {\n selectedTaskProp.push(checkbox.value);\n }\n selectedTaskProperties.value = JSON.stringify(selectedTaskProp);\n };\n });\n }\n\n /**\n * Set selection listeners for entire section of MUMIE Tasks in the course\n */\n function setSectionSelectionListeners() {\n sectionInputs.forEach(function(sectionCheckbox) {\n sectionCheckbox.onchange = function() {\n if (!sectionCheckbox.checked) {\n taskSelectionInputs.forEach(function(taskCheckbox) {\n if (taskCheckbox.getAttribute('section') === sectionCheckbox.value) {\n taskCheckbox.checked = false;\n selectedTaskIds = selectedTaskIds.filter(elem => taskCheckbox.value !== elem);\n }\n });\n } else {\n taskSelectionInputs.forEach(function(taskCheckbox) {\n if (taskCheckbox.getAttribute('section') === sectionCheckbox.value) {\n taskCheckbox.checked = true;\n pushIfNotExists(selectedTaskIds, taskCheckbox.value);\n }\n });\n }\n selectedTasks.value = JSON.stringify(selectedTaskIds);\n };\n });\n }\n\n return {\n init: function() {\n setTaskSelectionListeners();\n setPropertySelectionListeners();\n setSectionSelectionListeners();\n },\n };\n })();\n\n const worksheetController = (function() {\n const worksheetElement = document.getElementById(\"id_mumie_worksheet\");\n return {\n setWorksheet: function(worksheet) {\n if (worksheet) {\n worksheetElement.setAttribute(\"value\", JSON.stringify(worksheet));\n } else {\n worksheetElement.removeAttribute(\"value\");\n }\n }\n };\n })();\n\n /**\n * Disable all dropdown menus and show notification\n * @param {string} errorKey\n */\n function disableDropDownMenus(errorKey) {\n require(['core/str', \"core/notification\"], function(str, notification) {\n str.get_strings([{\n 'key': errorKey,\n component: 'mod_mumie'\n }]).done(function(s) {\n notification.addNotification({\n message: s[0] + \"\" + missingConfig.getAttribute(\"value\") + \"\",\n type: \"problem\"\n });\n }).fail(notification.exception);\n });\n serverController.disable();\n problemSelectorController.disable();\n }\n\n return {\n init: function(contextIdParam, prbSelectorUrl, lang) {\n lmsSelectorUrl = prbSelectorUrl;\n systemLanguage = lang;\n contextId = contextIdParam;\n const isEdit = document.getElementById(\"id_name\").getAttribute('value');\n const serverStructure = JSON.parse(document.getElementsByName('mumie_server_structure')[0].value);\n if (isEdit && !serverConfigExists()) {\n disableDropDownMenus('mumie_form_missing_server');\n } else if (!serverStructure.length) {\n disableDropDownMenus('mumie_form_no_server_conf');\n } else {\n serverController.init(serverStructure);\n courseController.init();\n taskController.init();\n multiTaskEditController.init();\n problemSelectorController.init();\n durationController().init();\n }\n multiTaskEditController.init();\n if (addServerButton) {\n require(['auth_mumie/mumie_server_config'], function(MumieServer) {\n MumieServer.init(addServerButton, contextId);\n });\n }\n }\n };\n\n /**\n * Remove all child elements of a given html element\n * @param {Object} elem\n */\n function removeChildElems(elem) {\n while (elem.firstChild) {\n elem.removeChild(elem.firstChild);\n }\n }\n\n /**\n * Check, if the flag for an existing config is set\n * @returns {boolean}\n */\n function serverConfigExists() {\n return document.getElementsByName(\"mumie_missing_config\")[0].getAttribute(\"value\") === \"\";\n }\n });"],"names":["define","addServerButton","document","getElementById","missingConfig","getElementsByName","lmsSelectorUrl","systemLanguage","contextId","durationController","durationSelector","gradedElem","isUngraded","value","updateDurationElements","setAttribute","removeAttribute","style","display","init","onchange","window","addEventListener","setDurationElements","setGradedElemValue","isGraded","getGradedElemValue","serverController","serverStructure","serverDropDown","structure","getSelectedServer","selectedServerName","options","selectedIndex","text","find","server","name","disable","disabled","elem","firstChild","removeChild","removeChildElems","problemSelectorController","problemSelectorButton","multiProblemSelectorButton","problemSelectorWindow","mumieOrg","sendResponse","response","postMessage","JSON","stringify","sendSuccess","message","success","addMessageListener","event","origin","importObj","parse","data","worksheet","courseController","setCourse","path_to_coursefile","langController","setLanguage","language","taskController","setSelection","link","setIsGraded","worksheetController","setWorksheet","focus","require","str","notification","get_strings","component","done","s","addNotification","type","fail","exception","error","sendFailure","buildURL","gradingType","getGradingType","selection","getDelocalizedTaskLink","selectedServer","urlprefix","selectedServerUrl","URL","encodeURIComponent","getSelectedLanguage","location","onclick","open","onclose","e","preventDefault","courseNameElem","courseFileElem","updateCourseName","selectedCourse","getSelectedCourse","selectedLanguage","translation","_selectedCourse$name$","courses","course","coursefile","courseFile","languageElem","lang","taskSelectionInput","nameElem","taskDisplayElement","updateTaskDisplayElement","localizedLink","updateTaskUri","localizeLink","updateName","updateGradeEditability","includes","split","multiTaskEditController","propertySelectionInputs","selectedTaskProperties","selectedTaskProp","taskSelectionInputs","selectedTasks","selectedTaskIds","sectionInputs","forEach","checkbox","checked","push","filter","sectionCheckbox","taskCheckbox","array","element","getAttribute","worksheetElement","disableDropDownMenus","errorKey","contextIdParam","prbSelectorUrl","isEdit","length","MumieServer"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"mod_form.min.js","sources":["../src/mod_form.js"],"sourcesContent":["define(['jquery', 'core/templates', 'auth_mumie/mumie_server_config', 'core/ajax'],\n function() {\n const addServerButton = document.getElementById(\"id_add_server_button\");\n const missingConfig = document.getElementsByName(\"mumie_missing_config\")[0];\n let lmsSelectorUrl;\n let systemLanguage;\n let contextId;\n let section;\n\n\n const durationController = (function() {\n\n const durationSelector = document.getElementById('id_duration_selector');\n const gradedElem = document.getElementById('id_mumie_isgraded');\n\n /**\n * Returns if task is ungraded like courses and articles.\n */\n function isUngraded() {\n return gradedElem.value === '0';\n }\n\n /**\n * Updates the visibility of elements around duration selector.\n */\n function updateDurationElements() {\n const disabled = isUngraded();\n if (disabled) {\n durationSelector.setAttribute('disabled', 'disabled');\n durationSelector.value = 'unlimited';\n } else {\n durationSelector.removeAttribute('disabled');\n }\n\n const displayNone = 'none';\n\n if (durationSelector.value === 'unlimited') {\n document.getElementById('fitem_id_unlimited_info').style.display = '';\n\n document.getElementById('fitem_id_timelimit').style.display = displayNone;\n document.getElementById('fitem_id_timelimit_info').style.display = displayNone;\n document.getElementById('fitem_id_duedate').style.display = displayNone;\n document.getElementById('fitem_id_duedate_info').style.display = displayNone;\n } else if (durationSelector.value === 'duedate') {\n document.getElementById('fitem_id_duedate').style.display = '';\n document.getElementById('fitem_id_duedate_info').style.display = '';\n\n document.getElementById('fitem_id_unlimited_info').style.display = displayNone;\n document.getElementById('fitem_id_timelimit').style.display = displayNone;\n document.getElementById('fitem_id_timelimit_info').style.display = displayNone;\n } else if (durationSelector.value === 'timelimit') {\n document.getElementById('fitem_id_timelimit').style.display = '';\n document.getElementById('fitem_id_timelimit_info').style.display = '';\n\n document.getElementById('fitem_id_duedate').style.display = displayNone;\n document.getElementById('fitem_id_duedate_info').style.display = displayNone;\n document.getElementById('fitem_id_unlimited_info').style.display = displayNone;\n }\n\n const duedatePropRow = document\n .querySelector('[name=\"mumie_multi_edit_property\"][value=\"duedate\"]')\n ?.closest('tr');\n if (duedatePropRow) {\n const show = durationSelector.value === 'duedate';\n duedatePropRow.style.display = show ? '' : displayNone;\n if (!show) {\n const duedatePropertyCheckbox = duedatePropRow.querySelector('[name=\"mumie_multi_edit_property\"]');\n if (duedatePropertyCheckbox && duedatePropertyCheckbox.checked) {\n duedatePropertyCheckbox.checked = false;\n duedatePropertyCheckbox.dispatchEvent(new Event('change'));\n }\n }\n }\n }\n\n return {\n init: function() {\n durationSelector.onchange = function() {\n updateDurationElements();\n };\n window.addEventListener(\"load\", () => {\n updateDurationElements();\n });\n updateDurationElements();\n },\n setDurationElements: updateDurationElements,\n isUngraded: isUngraded,\n setGradedElemValue: function(isGraded) {\n gradedElem.value = isGraded ? '1' : '0';\n },\n getGradedElemValue: function() {\n return gradedElem.value;\n }\n };\n })();\n\n const serverController = (function() {\n let serverStructure;\n const serverDropDown = document.getElementById(\"id_server\");\n\n return {\n init: function(structure) {\n serverStructure = structure;\n },\n getSelectedServer: function() {\n const selectedServerName = serverDropDown.options[serverDropDown.selectedIndex].text;\n return serverStructure.find(server => server.name === selectedServerName);\n },\n disable: function() {\n serverDropDown.disabled = true;\n removeChildElems(serverDropDown);\n }\n };\n })();\n\n const problemSelectorController = (function() {\n const problemSelectorButton = document.getElementById('id_prb_selector_btn');\n const multiProblemSelectorButton = document.getElementById('id_multi_problem_selector_btn');\n let problemSelectorWindow;\n const mumieOrg = document.getElementsByName('mumie_org')[0].value;\n\n /**\n * Send a message to the problem selector window.\n *\n * Don't do anything if there is no problem selector window.\n * @param {Object} response\n */\n function sendResponse(response) {\n if (!problemSelectorWindow) {\n return;\n }\n problemSelectorWindow.postMessage(JSON.stringify(response), lmsSelectorUrl);\n }\n\n /**\n * Send a success message to a problem selector window\n * @param {string} message\n */\n function sendSuccess(message = '') {\n sendResponse({\n success: true,\n message: message\n });\n }\n\n /**\n * Send a failure message to a problem selector window\n * @param {string} message\n */\n function sendFailure(message = '') {\n sendResponse({\n success: false,\n message: message\n });\n }\n\n /**\n * Add an event listener that accepts messages from LMS-Browser and updates the selected problem.\n */\n function addMessageListener() {\n window.addEventListener('message', (event) => {\n if (event.origin !== lmsSelectorUrl) {\n return;\n }\n try {\n const importObj = JSON.parse(event.data);\n if (Array.isArray(importObj)) {\n taskController.setMultiSelection(importObj);\n } else {\n applyPickerPayloadToForm(importObj);\n displayProblemSelectedMessage();\n }\n sendSuccess();\n window.focus();\n } catch (error) {\n sendFailure(error.message);\n }\n }, false);\n }\n\n /**\n * Display a success message in Moodle that a problem was successfully selected.\n */\n function displayProblemSelectedMessage() {\n require(['core/str', \"core/notification\"], function(str, notification) {\n str.get_strings([{\n 'key': 'mumie_form_updated_selection',\n component: 'mod_mumie'\n }]).done(function(s) {\n notification.addNotification({\n message: s[0],\n type: \"info\"\n });\n }).fail(notification.exception);\n });\n }\n\n /**\n * Builds the URL to the Problem Selector\n * @param {boolean} multiSelect Whether to build a multi-select URL\n * @returns {string} URL to the Problem Selector\n */\n function buildURL(multiSelect = false) {\n const gradingType = multiSelect ? 'all' : taskController.getGradingType();\n const selectedServer = serverController.getSelectedServer().urlprefix;\n const useSSO = shouldUseSSO(lmsSelectorUrl, selectedServer);\n\n const base = useSSO\n ? '/auth/mumie/problem_selector.php?' +\n 'org=' + mumieOrg +\n '&serverurl=' + encodeURIComponent(selectedServer) +\n '&problemlang=' + langController.getSelectedLanguage() +\n '&origin=' + encodeURIComponent(window.location.origin) +\n '&gradingtype=' + gradingType +\n '&contextid=' + contextId\n : lmsSelectorUrl + '/lms-problem-selector?' +\n 'org=' + mumieOrg +\n '&serverUrl=' + encodeURIComponent(selectedServer) +\n '&problemLang=' + langController.getSelectedLanguage() +\n '&origin=' + encodeURIComponent(window.location.origin) +\n '&uiLang=' + systemLanguage +\n '&gradingType=' + gradingType +\n '&multiCourse=true' +\n '&worksheet=true';\n\n if (multiSelect) {\n return base + (useSSO ? '&multiselect=true' : '&multiSelect=true');\n }\n const selection = taskController.getDelocalizedTaskLink();\n return selection ? base + '&selection=' + selection : base;\n }\n\n /**\n * Determines whether the Single Sign-On (SSO) should be used when opening the Problem Selector.\n * SSO is only supposed to be used when the Problem Selector URL has the same origin as the\n * URL of the selected MUMIE server.\n *\n * @param {string} problemSelectorUrl - The URL of the problem selector.\n * @param {string} selectedServerUrl - The URL of the selected MUMIE server\n * @returns {boolean} Whether SSO should be used for the Problem Selector or not\n */\n function shouldUseSSO(problemSelectorUrl, selectedServerUrl) {\n return new URL(problemSelectorUrl).origin === new URL(selectedServerUrl).origin;\n }\n\n return {\n init: function() {\n problemSelectorButton.onclick = function() {\n problemSelectorWindow = window.open(buildURL(), '_blank');\n };\n\n window.onclose = function() {\n sendSuccess();\n };\n\n window.addEventListener(\"beforeunload\", function() {\n sendSuccess();\n }, false);\n\n addMessageListener();\n\n multiProblemSelectorButton.onclick = function(e) {\n e.preventDefault();\n problemSelectorWindow = window.open(buildURL(true), '_blank', 'toolbar=0,location=0,menubar=0');\n };\n },\n disable: function() {\n problemSelectorButton.disabled = true;\n },\n };\n })();\n\n /**\n * Apply a picker payload to the form's per-task fields.\n *\n * Single source of truth for picker-payload → form-field mapping.\n * Used by both the single-task postMessage handler and the multi-task\n * submit loop, so new picker fields only need to be wired here.\n *\n * @param {Object} payload picker payload for one task\n */\n function applyPickerPayloadToForm(payload) {\n const isGraded = payload.isGraded !== false;\n const worksheet = payload.worksheet ?? null;\n courseController.setCourse(payload.path_to_coursefile);\n langController.setLanguage(payload.language);\n taskController.setSelection(payload.link, payload.language, payload.name);\n taskController.setIsGraded(isGraded);\n worksheetController.setWorksheet(worksheet);\n }\n\n const courseController = (function() {\n const courseNameElem = document.getElementById(\"id_mumie_course\");\n const courseFileElem = document.getElementsByName(\"mumie_coursefile\")[0];\n\n\n /**\n * Update the hidden input field with the selected course's course file path\n *\n * @param {string} coursefile\n */\n function updateCourseFilePath(coursefile) {\n courseFileElem.value = coursefile;\n updateCourseName();\n }\n\n /**\n * Update displayed course name.\n */\n function updateCourseName() {\n const selectedCourse = courseController.getSelectedCourse();\n const selectedLanguage = langController.getSelectedLanguage();\n if (!selectedCourse || !selectedLanguage) {\n return;\n }\n courseNameElem.value = selectedCourse.name\n .find(translation => translation.language === selectedLanguage)?.value;\n }\n\n return {\n init: function() {\n updateCourseName();\n },\n getSelectedCourse: function() {\n const courses = serverController.getSelectedServer().courses;\n return courses.find(course => course.coursefile === courseFileElem.value);\n },\n setCourse: function(courseFile) {\n updateCourseFilePath(courseFile);\n }\n };\n })();\n\n const langController = (function() {\n const languageElem = document.getElementById(\"id_language\");\n return {\n getSelectedLanguage: function() {\n return languageElem.value;\n },\n setLanguage: function(lang) {\n languageElem.value = lang;\n }\n };\n })();\n\n const taskController = (function() {\n const taskSelectionInput = document.getElementsByName(\"taskurl\")[0];\n const nameElem = document.getElementById(\"id_name\");\n const taskDisplayElement = document.getElementById(\"id_task_display_element\");\n const LANG_REQUEST_PARAM_PREFIX = \"?lang=\";\n\n /**\n * Update the activity's name in the input field\n * @param {string} name\n */\n function updateName(name) {\n nameElem.value = name;\n }\n\n /**\n * @param {string} localizedLink\n */\n function updateTaskDisplayElement(localizedLink) {\n taskDisplayElement.value = localizedLink;\n }\n\n /**\n * Update task uri\n * @param {string} link\n * @param {string} language\n */\n function updateTaskUri(link, language) {\n const localizedLink = localizeLink(link, language);\n taskSelectionInput.value = localizedLink;\n updateTaskDisplayElement(localizedLink);\n }\n\n /**\n * Add lang request param to link\n * @param {string} link\n * @param {string} language\n * @returns {string} Link with lang request param\n */\n function localizeLink(link, language) {\n return link + LANG_REQUEST_PARAM_PREFIX + language;\n }\n\n /**\n * Remove lang request param from link\n * @param {string} link Link that may have lang request param\n * @returns {string} Link without lang request param\n */\n function delocalizeLink(link) {\n if (link.includes(LANG_REQUEST_PARAM_PREFIX)) {\n return link.split(LANG_REQUEST_PARAM_PREFIX)[0];\n }\n return link;\n }\n\n /**\n * Form inputs related to grades should be disabled if the MUMIE Task is not graded.\n */\n function updateGradeEditability() {\n const disabled = durationController.isUngraded();\n document.getElementById('id_points').disabled = disabled;\n document.getElementById('id_gradepass').disabled = disabled;\n document.getElementById('id_gradecat').disabled = disabled;\n durationController.setDurationElements();\n }\n\n /**\n * Store selected tasks and update UI for multi-task creation.\n * @param {Array} tasks array of task objects from the selector\n */\n function setMultiSelection(tasks) {\n const tasksField = document.getElementsByName('mumie_multi_tasks')[0];\n const summary = document.getElementById('mumie_multi_tasks_summary');\n if (tasksField) {\n tasksField.value = JSON.stringify(tasks);\n }\n if (summary) {\n const taskListItems = tasks.map(task => {\n const taskListItem = document.createElement('li');\n taskListItem.textContent = task.name;\n return taskListItem;\n });\n summary.innerHTML = '';\n const summaryHeading = document.createElement('div');\n const taskList = document.createElement('ul');\n require(['core/str'], function(Str) {\n Str.get_string('mumie_multi_tasks_selected', 'mod_mumie', tasks.length)\n .then(function(headingText) {\n summaryHeading.textContent = headingText;\n });\n });\n taskList.classList.add('mumie_multi_tasks_list');\n taskListItems.forEach(taskListItem => taskList.appendChild(taskListItem));\n summary.appendChild(summaryHeading);\n summary.appendChild(taskList);\n summary.style.display = 'block';\n }\n const nameField = document.getElementById('id_name');\n nameField.disabled = true;\n nameField.value = '';\n nameField.removeAttribute('required');\n const taskDisplayElem = document.getElementById('id_task_display_element');\n if (taskDisplayElem) { taskDisplayElem.value = ''; }\n const courseNameElem = document.getElementById('id_mumie_course');\n if (courseNameElem) { courseNameElem.value = ''; }\n const nameFieldContainer = document.getElementById('fitem_id_name');\n if (nameFieldContainer) {\n nameFieldContainer.querySelectorAll('.req, .text-danger, [title=\"Required field\"]')\n .forEach(requiredIndicator => { requiredIndicator.style.display = 'none'; });\n }\n const requiredLegend = document.querySelector('.fdescription.required');\n if (requiredLegend) {\n requiredLegend.style.display = 'none';\n }\n }\n\n /**\n * Serialize a form into a URL-encoded POST string, omitting form mechanics\n * the webservice does not need (sesskey, qf form marker).\n *\n * Disabled form controls are temporarily enabled so FormData includes them:\n * the picker writes values to display-only inputs (e.g. mumie_course) that\n * the server-side validator still expects to receive.\n *\n * @param {HTMLFormElement} form\n * @returns {string}\n */\n function serializeFormFields(form) {\n const disabledFields = Array.from(form.querySelectorAll(':disabled'));\n disabledFields.forEach(field => { field.disabled = false; });\n try {\n const omit = ['sesskey', '_qf__mod_mumie_mod_form'];\n return Array.from(new FormData(form))\n .filter(([key]) => !omit.includes(key))\n .map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value))\n .join('&');\n } finally {\n disabledFields.forEach(field => { field.disabled = true; });\n }\n }\n\n /**\n * Submit multi-tasks via AJAX, then redirect to course.\n *\n * Builds one full form payload per task by applying each picker payload\n * to the live form and serializing it. The server treats each entry as\n * an independent single-task submission, so picker-payload → form-field\n * mapping lives in applyPickerPayloadToForm and is shared with the\n * single-task path. Validation runs server-side via the shared form\n * pipeline; errors surface as a modal alert with the validator's\n * localized message.\n */\n function submitMultiTasks() {\n const tasksField = document.getElementsByName('mumie_multi_tasks')[0];\n const courseId = document.getElementsByName('course')[0]?.value;\n const submitButton = document.getElementById('id_submitbutton');\n const form = submitButton && submitButton.closest('form');\n const tasks = JSON.parse(tasksField.value);\n\n const tasksFormData = tasks.map(task => {\n applyPickerPayloadToForm(task);\n return serializeFormFields(form);\n });\n\n // applyPickerPayloadToForm leaves the form in single-task mode of the last task;\n // restore the multi-task UI so the form is coherent if the AJAX call fails.\n setMultiSelection(tasks);\n\n require(['core/ajax', 'core/notification'], function(Ajax, notification) {\n Ajax.call([{\n methodname: 'mod_mumie_create_multiple_mumie_tasks',\n args: {\n contextid: parseInt(contextId),\n section: section,\n tasksformdata: tasksFormData,\n },\n }])[0].done(function() {\n window.location.href = M.cfg.wwwroot + '/course/view.php?id=' + courseId;\n }).fail(function(error) {\n notification.alert('', error.message);\n });\n });\n }\n\n return {\n init: function() {\n updateTaskDisplayElement(taskSelectionInput.value);\n },\n setSelection: function(link, language, name) {\n const tasksField = document.getElementsByName('mumie_multi_tasks')[0];\n if (tasksField) {\n tasksField.value = '';\n }\n const summary = document.getElementById('mumie_multi_tasks_summary');\n if (summary) {\n summary.style.display = 'none';\n summary.innerHTML = '';\n }\n const nameField = document.getElementById('id_name');\n if (nameField) {\n nameField.disabled = false;\n nameField.setAttribute('required', 'required');\n }\n const nameFieldContainer = document.getElementById('fitem_id_name');\n if (nameFieldContainer) {\n nameFieldContainer.querySelectorAll('.req, .text-danger, [title=\"Required field\"]')\n .forEach(requiredIndicator => { requiredIndicator.style.display = ''; });\n }\n const requiredLegend = document.querySelector('.fdescription.required');\n if (requiredLegend) {\n requiredLegend.style.display = '';\n }\n updateTaskUri(link, language);\n updateName(name);\n },\n setIsGraded: function(isGraded) {\n durationController.setGradedElemValue(isGraded);\n updateGradeEditability();\n },\n getGradingType: function() {\n const isGraded = durationController.getGradedElemValue();\n if (isGraded === '1') {\n return 'graded';\n } else if (isGraded === '0') {\n return 'ungraded';\n }\n return 'all';\n },\n getDelocalizedTaskLink: function() {\n return delocalizeLink(taskSelectionInput.value);\n },\n setMultiSelection: setMultiSelection,\n hasMultiTasks: function() {\n const tasksField = document.getElementsByName('mumie_multi_tasks')[0];\n return tasksField && !!tasksField.value;\n },\n submitMultiTasks: submitMultiTasks,\n };\n })();\n\n\n const multiTaskEditController = (function() {\n const propertySelectionInputs = document.getElementsByName(\"mumie_multi_edit_property\");\n const selectedTaskProperties = document.getElementsByName(\"mumie_selected_task_properties\")[0];\n let selectedTaskProp = [];\n const taskSelectionInputs = document.getElementsByName(\"mumie_multi_edit_task\");\n const selectedTasks = document.getElementsByName(\"mumie_selected_tasks\")[0];\n let selectedTaskIds = [];\n const sectionInputs = document.getElementsByName(\"mumie_multi_edit_section\");\n\n /**\n * Push an element to an array, if it's not already included.\n *\n * @param {string[]} array\n * @param {string} element\n */\n function pushIfNotExists(array, element) {\n if (!array.includes(element)) {\n array.push(element);\n }\n }\n\n /**\n * Set selection listeners for other MUMIE Tasks in the course.\n */\n function setTaskSelectionListeners() {\n taskSelectionInputs.forEach(function(checkbox) {\n checkbox.onchange = function() {\n if (!checkbox.checked) {\n selectedTaskIds = selectedTaskIds.filter(elem => elem !== checkbox.value);\n } else {\n selectedTaskIds.push(checkbox.value);\n }\n selectedTasks.value = JSON.stringify(selectedTaskIds);\n };\n });\n }\n\n /**\n * Show or hide per-task \"Working period: not Deadline\" warnings based on whether the duedate property is selected.\n */\n function updateNoDuedateWarnings() {\n const duedateSelected = Array.from(propertySelectionInputs)\n .some(checkbox => checkbox.value === 'duedate' && checkbox.checked);\n document.querySelectorAll('.mumie-form-working-period-not-duedate-warning').forEach(function(elem) {\n elem.style.display = duedateSelected ? '' : 'none';\n });\n }\n\n /**\n * Set selection listeners for properties to apply to MUMIE Tasks in the course.\n */\n function setPropertySelectionListeners() {\n propertySelectionInputs.forEach(function(checkbox) {\n checkbox.onchange = function() {\n if (!checkbox.checked) {\n selectedTaskProp = selectedTaskProp.filter(elem => elem !== checkbox.value);\n } else {\n selectedTaskProp.push(checkbox.value);\n }\n selectedTaskProperties.value = JSON.stringify(selectedTaskProp);\n updateNoDuedateWarnings();\n };\n });\n }\n\n /**\n * Set selection listeners for entire section of MUMIE Tasks in the course\n */\n function setSectionSelectionListeners() {\n sectionInputs.forEach(function(sectionCheckbox) {\n sectionCheckbox.onchange = function() {\n if (!sectionCheckbox.checked) {\n taskSelectionInputs.forEach(function(taskCheckbox) {\n if (taskCheckbox.getAttribute('section') === sectionCheckbox.value) {\n taskCheckbox.checked = false;\n selectedTaskIds = selectedTaskIds.filter(elem => taskCheckbox.value !== elem);\n }\n });\n } else {\n taskSelectionInputs.forEach(function(taskCheckbox) {\n if (taskCheckbox.getAttribute('section') === sectionCheckbox.value) {\n taskCheckbox.checked = true;\n pushIfNotExists(selectedTaskIds, taskCheckbox.value);\n }\n });\n }\n selectedTasks.value = JSON.stringify(selectedTaskIds);\n };\n });\n }\n\n return {\n init: function() {\n setTaskSelectionListeners();\n setPropertySelectionListeners();\n setSectionSelectionListeners();\n },\n };\n })();\n\n const worksheetController = (function() {\n const worksheetElement = document.getElementById(\"id_mumie_worksheet\");\n return {\n setWorksheet: function(worksheet) {\n if (worksheet) {\n worksheetElement.setAttribute(\"value\", JSON.stringify(worksheet));\n } else {\n worksheetElement.removeAttribute(\"value\");\n }\n }\n };\n })();\n\n /**\n * Disable all dropdown menus and show notification\n * @param {string} errorKey\n */\n function disableDropDownMenus(errorKey) {\n require(['core/str', \"core/notification\"], function(str, notification) {\n str.get_strings([{\n 'key': errorKey,\n component: 'mod_mumie'\n }]).done(function(s) {\n notification.addNotification({\n message: s[0] + \"\" + missingConfig.getAttribute(\"value\") + \"\",\n type: \"problem\"\n });\n }).fail(notification.exception);\n });\n serverController.disable();\n problemSelectorController.disable();\n }\n\n return {\n init: function(contextIdParam, prbSelectorUrl, lang, sectionParam) {\n lmsSelectorUrl = prbSelectorUrl;\n systemLanguage = lang;\n contextId = contextIdParam;\n section = sectionParam;\n const isEdit = document.getElementById(\"id_name\").getAttribute('value');\n const serverStructure = JSON.parse(document.getElementsByName('mumie_server_structure')[0].value);\n if (isEdit && !serverConfigExists()) {\n disableDropDownMenus('mumie_form_missing_server');\n } else if (!serverStructure.length) {\n disableDropDownMenus('mumie_form_no_server_conf');\n } else {\n serverController.init(serverStructure);\n courseController.init();\n taskController.init();\n multiTaskEditController.init();\n problemSelectorController.init();\n durationController.init();\n }\n if (addServerButton) {\n require(['auth_mumie/mumie_server_config'], function(MumieServer) {\n MumieServer.init(addServerButton, contextId);\n });\n }\n\n const submitButton = document.getElementById('id_submitbutton');\n const form = submitButton && submitButton.closest('form');\n if (form) {\n let cancelClicked = false;\n const cancelButton = document.getElementById('id_cancel');\n if (cancelButton) {\n cancelButton.addEventListener('click', function() {\n cancelClicked = true;\n });\n }\n form.addEventListener('submit', function(e) {\n if (cancelClicked) {\n cancelClicked = false;\n return;\n }\n if (taskController.hasMultiTasks()) {\n e.preventDefault();\n taskController.submitMultiTasks();\n }\n });\n }\n }\n };\n\n /**\n * Remove all child elements of a given html element\n * @param {Object} elem\n */\n function removeChildElems(elem) {\n while (elem.firstChild) {\n elem.removeChild(elem.firstChild);\n }\n }\n\n /**\n * Check, if the flag for an existing config is set\n * @returns {boolean}\n */\n function serverConfigExists() {\n return document.getElementsByName(\"mumie_missing_config\")[0].getAttribute(\"value\") === \"\";\n }\n });\n"],"names":["define","addServerButton","document","getElementById","missingConfig","getElementsByName","lmsSelectorUrl","systemLanguage","contextId","section","durationController","durationSelector","gradedElem","isUngraded","value","updateDurationElements","setAttribute","removeAttribute","style","display","duedatePropRow","querySelector","_document$querySelect","closest","show","duedatePropertyCheckbox","checked","dispatchEvent","Event","init","onchange","window","addEventListener","setDurationElements","setGradedElemValue","isGraded","getGradedElemValue","serverController","serverStructure","serverDropDown","structure","getSelectedServer","selectedServerName","options","selectedIndex","text","find","server","name","disable","disabled","elem","firstChild","removeChild","removeChildElems","problemSelectorController","problemSelectorButton","multiProblemSelectorButton","problemSelectorWindow","mumieOrg","sendResponse","response","postMessage","JSON","stringify","sendSuccess","message","success","addMessageListener","event","origin","importObj","parse","data","Array","isArray","taskController","setMultiSelection","applyPickerPayloadToForm","require","str","notification","get_strings","component","done","s","addNotification","type","fail","exception","focus","error","sendFailure","buildURL","multiSelect","gradingType","getGradingType","selectedServer","urlprefix","useSSO","shouldUseSSO","base","encodeURIComponent","langController","getSelectedLanguage","location","selection","getDelocalizedTaskLink","problemSelectorUrl","selectedServerUrl","URL","onclick","open","onclose","e","preventDefault","payload","worksheet","courseController","setCourse","path_to_coursefile","setLanguage","language","setSelection","link","setIsGraded","worksheetController","setWorksheet","courseNameElem","courseFileElem","updateCourseName","selectedCourse","getSelectedCourse","selectedLanguage","translation","_selectedCourse$name$","courses","course","coursefile","courseFile","languageElem","lang","taskSelectionInput","nameElem","taskDisplayElement","updateTaskDisplayElement","localizedLink","updateTaskUri","localizeLink","tasks","tasksField","summary","taskListItems","map","task","taskListItem","createElement","textContent","innerHTML","summaryHeading","taskList","Str","get_string","length","then","headingText","classList","add","forEach","appendChild","nameField","taskDisplayElem","nameFieldContainer","querySelectorAll","requiredIndicator","requiredLegend","updateName","updateGradeEditability","includes","split","hasMultiTasks","submitMultiTasks","courseId","_document$getElements","submitButton","form","tasksFormData","disabledFields","from","field","omit","FormData","filter","_ref","key","_ref2","join","serializeFormFields","Ajax","call","methodname","args","contextid","parseInt","tasksformdata","href","M","cfg","wwwroot","alert","multiTaskEditController","propertySelectionInputs","selectedTaskProperties","selectedTaskProp","taskSelectionInputs","selectedTasks","selectedTaskIds","sectionInputs","setPropertySelectionListeners","checkbox","push","duedateSelected","some","updateNoDuedateWarnings","sectionCheckbox","taskCheckbox","array","element","getAttribute","worksheetElement","disableDropDownMenus","errorKey","contextIdParam","prbSelectorUrl","sectionParam","isEdit","MumieServer","cancelClicked","cancelButton"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/build/view.min.js b/amd/build/view.min.js
index ef3bc370..7465ea24 100644
--- a/amd/build/view.min.js
+++ b/amd/build/view.min.js
@@ -1,3 +1,3 @@
-define("mod_mumie/view",["jquery","core/templates","core/modal_factory","mod_mumie/duedate_form"],(function($){return{init:function(contextid){!function(contextid){const addBtns=$(".mumie_duedate_add_btn");addBtns.each((function(i){const btn=addBtns[i],data=JSON.parse(btn.children[1].textContent),formdata="&_qf__duedate_form=1&userid="+data.userid+"&mumie="+data.mumie;require(["mod_mumie/duedate_form"],(function(MumieDueDate){MumieDueDate.init(btn,contextid,formdata)}))}))}(contextid),function(contextid){const editBtns=$(".mumie_duedate_edit_btn");editBtns.each((function(i){const btn=editBtns[i],data=JSON.parse(btn.children[1].textContent),formdata="id="+data.id+"&_qf__duedate_form=1&userid="+data.userid+"&mumie="+data.mumie+"&duedate="+data.duedate;require(["mod_mumie/duedate_form"],(function(MumieDueDate){MumieDueDate.init(btn,contextid,formdata)}))}))}(contextid)}}}));
+define("mod_mumie/view",["jquery","core/templates","mod_mumie/duedate_form"],(function($){return{init:function(contextid){!function(contextid){const addBtns=$(".mumie_duedate_add_btn");addBtns.each((function(i){const btn=addBtns[i],data=JSON.parse(btn.children[1].textContent),formdata="&_qf__duedate_form=1&userid="+data.userid+"&mumie="+data.mumie;require(["mod_mumie/duedate_form"],(function(MumieDueDate){MumieDueDate.init(btn,contextid,formdata)}))}))}(contextid),function(contextid){const editBtns=$(".mumie_duedate_edit_btn");editBtns.each((function(i){const btn=editBtns[i],data=JSON.parse(btn.children[1].textContent),formdata="id="+data.id+"&_qf__duedate_form=1&userid="+data.userid+"&mumie="+data.mumie+"&duedate="+data.duedate;require(["mod_mumie/duedate_form"],(function(MumieDueDate){MumieDueDate.init(btn,contextid,formdata)}))}))}(contextid)}}}));
//# sourceMappingURL=view.min.js.map
\ No newline at end of file
diff --git a/amd/build/view.min.js.map b/amd/build/view.min.js.map
index 5feb4aa6..527937d4 100644
--- a/amd/build/view.min.js.map
+++ b/amd/build/view.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"view.min.js","sources":["../src/view.js"],"sourcesContent":["define(['jquery', 'core/templates', 'core/modal_factory', 'mod_mumie/duedate_form'],\n function($) {\n return {\n init: function(contextid) {\n setAddDuedateListeners(contextid);\n setEditDuedateListeners(contextid);\n }\n };\n\n /**\n * Set click listener to add a server button\n * @param {number} contextid\n */\n function setAddDuedateListeners(contextid) {\n const addBtns = $(\".mumie_duedate_add_btn\");\n\n addBtns.each(function(i) {\n const btn = addBtns[i];\n const data = JSON.parse(btn.children[1].textContent);\n const formdata = \"&_qf__duedate_form=1&userid=\" + data.userid + \"&mumie=\" + data.mumie;\n require(['mod_mumie/duedate_form'], function(MumieDueDate) {\n MumieDueDate.init(btn, contextid, formdata);\n });\n });\n }\n\n /**\n * Set click listeners for the edit buttons\n * @param {number} contextid\n */\n function setEditDuedateListeners(contextid) {\n const editBtns = $(\".mumie_duedate_edit_btn\");\n\n editBtns.each(function(i) {\n const btn = editBtns[i];\n const data = JSON.parse(btn.children[1].textContent);\n const formdata = \"id=\" + data.id +\n \"&_qf__duedate_form=1&userid=\" + data.userid + \"&mumie=\" + data.mumie + \"&duedate=\" + data.duedate;\n require(['mod_mumie/duedate_form'], function(MumieDueDate) {\n MumieDueDate.init(btn, contextid, formdata);\n });\n });\n }\n });"],"names":["define","$","init","contextid","addBtns","each","i","btn","data","JSON","parse","children","textContent","formdata","userid","mumie","require","MumieDueDate","setAddDuedateListeners","editBtns","id","duedate","setEditDuedateListeners"],"mappings":"AAAAA,wBAAO,CAAC,SAAU,iBAAkB,qBAAsB,2BACtD,SAASC,SACE,CACHC,KAAM,SAASC,qBAUaA,iBACtBC,QAAUH,EAAE,0BAElBG,QAAQC,MAAK,SAASC,SACZC,IAAMH,QAAQE,GACdE,KAAOC,KAAKC,MAAMH,IAAII,SAAS,GAAGC,aAClCC,SAAW,+BAAiCL,KAAKM,OAAS,UAAYN,KAAKO,MACjFC,QAAQ,CAAC,2BAA2B,SAASC,cACzCA,aAAaf,KAAKK,IAAKJ,UAAWU,gBAjBtCK,CAAuBf,oBA0BEA,iBACvBgB,SAAWlB,EAAE,2BAEnBkB,SAASd,MAAK,SAASC,SACbC,IAAMY,SAASb,GACfE,KAAOC,KAAKC,MAAMH,IAAII,SAAS,GAAGC,aAClCC,SAAW,MAAQL,KAAKY,GAC1B,+BAAiCZ,KAAKM,OAAS,UAAYN,KAAKO,MAAQ,YAAcP,KAAKa,QAC/FL,QAAQ,CAAC,2BAA2B,SAASC,cACzCA,aAAaf,KAAKK,IAAKJ,UAAWU,gBAlCtCS,CAAwBnB"}
\ No newline at end of file
+{"version":3,"file":"view.min.js","sources":["../src/view.js"],"sourcesContent":["define(['jquery', 'core/templates', 'mod_mumie/duedate_form'],\n function($) {\n return {\n init: function(contextid) {\n setAddDuedateListeners(contextid);\n setEditDuedateListeners(contextid);\n }\n };\n\n /**\n * Set click listener to add a server button\n * @param {number} contextid\n */\n function setAddDuedateListeners(contextid) {\n const addBtns = $(\".mumie_duedate_add_btn\");\n\n addBtns.each(function(i) {\n const btn = addBtns[i];\n const data = JSON.parse(btn.children[1].textContent);\n const formdata = \"&_qf__duedate_form=1&userid=\" + data.userid + \"&mumie=\" + data.mumie;\n require(['mod_mumie/duedate_form'], function(MumieDueDate) {\n MumieDueDate.init(btn, contextid, formdata);\n });\n });\n }\n\n /**\n * Set click listeners for the edit buttons\n * @param {number} contextid\n */\n function setEditDuedateListeners(contextid) {\n const editBtns = $(\".mumie_duedate_edit_btn\");\n\n editBtns.each(function(i) {\n const btn = editBtns[i];\n const data = JSON.parse(btn.children[1].textContent);\n const formdata = \"id=\" + data.id +\n \"&_qf__duedate_form=1&userid=\" + data.userid + \"&mumie=\" + data.mumie + \"&duedate=\" + data.duedate;\n require(['mod_mumie/duedate_form'], function(MumieDueDate) {\n MumieDueDate.init(btn, contextid, formdata);\n });\n });\n }\n });"],"names":["define","$","init","contextid","addBtns","each","i","btn","data","JSON","parse","children","textContent","formdata","userid","mumie","require","MumieDueDate","setAddDuedateListeners","editBtns","id","duedate","setEditDuedateListeners"],"mappings":"AAAAA,wBAAO,CAAC,SAAU,iBAAkB,2BAChC,SAASC,SACE,CACHC,KAAM,SAASC,qBAUaA,iBACtBC,QAAUH,EAAE,0BAElBG,QAAQC,MAAK,SAASC,SACZC,IAAMH,QAAQE,GACdE,KAAOC,KAAKC,MAAMH,IAAII,SAAS,GAAGC,aAClCC,SAAW,+BAAiCL,KAAKM,OAAS,UAAYN,KAAKO,MACjFC,QAAQ,CAAC,2BAA2B,SAASC,cACzCA,aAAaf,KAAKK,IAAKJ,UAAWU,gBAjBtCK,CAAuBf,oBA0BEA,iBACvBgB,SAAWlB,EAAE,2BAEnBkB,SAASd,MAAK,SAASC,SACbC,IAAMY,SAASb,GACfE,KAAOC,KAAKC,MAAMH,IAAII,SAAS,GAAGC,aAClCC,SAAW,MAAQL,KAAKY,GAC1B,+BAAiCZ,KAAKM,OAAS,UAAYN,KAAKO,MAAQ,YAAcP,KAAKa,QAC/FL,QAAQ,CAAC,2BAA2B,SAASC,cACzCA,aAAaf,KAAKK,IAAKJ,UAAWU,gBAlCtCS,CAAwBnB"}
\ No newline at end of file
diff --git a/amd/src/duedate_form.js b/amd/src/duedate_form.js
index 80316221..09e4ff49 100644
--- a/amd/src/duedate_form.js
+++ b/amd/src/duedate_form.js
@@ -1,5 +1,12 @@
-define(['jquery', 'core/str', 'core/modal_factory', 'core/modal_events', 'core/fragment', 'core/ajax', 'core/yui'],
- function($, Str, ModalFactory, ModalEvents, Fragment, Ajax, Y) {
+define([
+ 'jquery',
+ 'core/str',
+ 'core/modal_save_cancel',
+ 'core/modal_events',
+ 'core/fragment',
+ 'core/ajax',
+ 'core_form/changechecker',
+], function($, Str, ModalSaveCancel, ModalEvents, Fragment, Ajax, FormChangeChecker) {
var MumieDueDate = function(selector, contextid, formdata) {
this.contextid = contextid;
@@ -11,34 +18,27 @@ define(['jquery', 'core/str', 'core/modal_factory', 'core/modal_events', 'core/f
MumieDueDate.prototype.contextid = -1;
MumieDueDate.prototype.init = function(selector, formdata) {
- const triggers = $(selector);
- return Str.get_string('mumie_duedate_form', 'mod_mumie').then(function(title) {
- // Create the modal.
- return ModalFactory.create({
- type: ModalFactory.types.SAVE_CANCEL,
- title: title,
- body: this.getBody(formdata)
- }, triggers);
- }.bind(this)).then(function(modal) {
- // Keep a reference to the modal.
- this.modal = modal;
-
- // Forms are big, we want a big modal.
- this.modal.setLarge();
-
- // We want to reset the form every time it is opened.
- this.modal.getRoot().on(ModalEvents.hidden, function() {
- this.modal.setBody(this.getBody(formdata));
+ $(selector).on('click', function(e) {
+ e.preventDefault();
+ return Str.get_string('mumie_duedate_form', 'mod_mumie').then(function(title) {
+ return ModalSaveCancel.create({
+ title: title,
+ body: this.getBody(formdata),
+ large: true,
+ removeOnClose: true,
+ });
+ }.bind(this)).then(function(modal) {
+ this.modal = modal;
+
+ // We catch the modal save event, and use it to submit the form inside the modal.
+ // Triggering a form submission will give JS validation scripts a chance to check for errors.
+ modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
+ // We also catch the form submit event and use it to submit the form with ajax.
+ modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
+
+ modal.show();
+ return modal;
}.bind(this));
-
- // We catch the modal save event, and use it to submit the form inside the modal.
- // Triggering a form submission will give JS validation scripts a chance to check for errors.
- this.modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
- // We also catch the form submit event and use it to submit the form with ajax.
- this.modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
-
- return this.modal;
-
}.bind(this));
};
@@ -65,12 +65,7 @@ define(['jquery', 'core/str', 'core/modal_factory', 'core/modal_events', 'core/f
*/
MumieDueDate.prototype.handleFormSubmissionResponse = function() {
this.modal.hide();
- // We could trigger an event instead.
- // Yuk.
- Y.use('moodle-core-formchangechecker', function() {
- M.core_formchangechecker.reset_form_dirty_state();
- });
-
+ FormChangeChecker.resetAllFormDirtyStates();
document.location.reload();
};
diff --git a/amd/src/mod_form.js b/amd/src/mod_form.js
index 041124cc..0839a900 100644
--- a/amd/src/mod_form.js
+++ b/amd/src/mod_form.js
@@ -1,10 +1,11 @@
-define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_server_config', 'core/ajax'],
+define(['jquery', 'core/templates', 'auth_mumie/mumie_server_config', 'core/ajax'],
function() {
const addServerButton = document.getElementById("id_add_server_button");
const missingConfig = document.getElementsByName("mumie_missing_config")[0];
let lmsSelectorUrl;
let systemLanguage;
let contextId;
+ let section;
const durationController = (function() {
@@ -55,6 +56,21 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
document.getElementById('fitem_id_duedate_info').style.display = displayNone;
document.getElementById('fitem_id_unlimited_info').style.display = displayNone;
}
+
+ const duedatePropRow = document
+ .querySelector('[name="mumie_multi_edit_property"][value="duedate"]')
+ ?.closest('tr');
+ if (duedatePropRow) {
+ const show = durationSelector.value === 'duedate';
+ duedatePropRow.style.display = show ? '' : displayNone;
+ if (!show) {
+ const duedatePropertyCheckbox = duedatePropRow.querySelector('[name="mumie_multi_edit_property"]');
+ if (duedatePropertyCheckbox && duedatePropertyCheckbox.checked) {
+ duedatePropertyCheckbox.checked = false;
+ duedatePropertyCheckbox.dispatchEvent(new Event('change'));
+ }
+ }
+ }
}
return {
@@ -65,6 +81,7 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
window.addEventListener("load", () => {
updateDurationElements();
});
+ updateDurationElements();
},
setDurationElements: updateDurationElements,
isUngraded: isUngraded,
@@ -75,7 +92,7 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
return gradedElem.value;
}
};
- });
+ })();
const serverController = (function() {
let serverStructure;
@@ -145,18 +162,16 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
if (event.origin !== lmsSelectorUrl) {
return;
}
- const importObj = JSON.parse(event.data);
- const isGraded = importObj.isGraded !== false;
- const worksheet = importObj.worksheet ?? null;
try {
- courseController.setCourse(importObj.path_to_coursefile);
- langController.setLanguage(importObj.language);
- taskController.setSelection(importObj.link, importObj.language, importObj.name);
- taskController.setIsGraded(isGraded);
- worksheetController.setWorksheet(worksheet);
+ const importObj = JSON.parse(event.data);
+ if (Array.isArray(importObj)) {
+ taskController.setMultiSelection(importObj);
+ } else {
+ applyPickerPayloadToForm(importObj);
+ displayProblemSelectedMessage();
+ }
sendSuccess();
window.focus();
- displayProblemSelectedMessage();
} catch (error) {
sendFailure(error.message);
}
@@ -182,40 +197,37 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
/**
* Builds the URL to the Problem Selector
+ * @param {boolean} multiSelect Whether to build a multi-select URL
* @returns {string} URL to the Problem Selector
*/
- function buildURL() {
- const gradingType = taskController.getGradingType();
- const selection = taskController.getDelocalizedTaskLink();
+ function buildURL(multiSelect = false) {
+ const gradingType = multiSelect ? 'all' : taskController.getGradingType();
const selectedServer = serverController.getSelectedServer().urlprefix;
const useSSO = shouldUseSSO(lmsSelectorUrl, selectedServer);
- if (useSSO) {
- return '/auth/mumie/problem_selector.php?' +
- 'org=' +
- mumieOrg +
- '&serverurl=' +
- encodeURIComponent(selectedServer) +
- '&problemlang=' +
- langController.getSelectedLanguage() +
- '&origin=' + encodeURIComponent(window.location.origin) +
- '&gradingtype=' + gradingType +
- '&contextid=' + contextId +
- (selection ? '&selection=' + selection : '');
- }
- return lmsSelectorUrl +
- '/lms-problem-selector?' +
- 'org=' +
- mumieOrg +
- '&serverUrl=' +
- encodeURIComponent(selectedServer) +
- '&problemLang=' +
- langController.getSelectedLanguage() +
+
+ const base = useSSO
+ ? '/auth/mumie/problem_selector.php?' +
+ 'org=' + mumieOrg +
+ '&serverurl=' + encodeURIComponent(selectedServer) +
+ '&problemlang=' + langController.getSelectedLanguage() +
+ '&origin=' + encodeURIComponent(window.location.origin) +
+ '&gradingtype=' + gradingType +
+ '&contextid=' + contextId
+ : lmsSelectorUrl + '/lms-problem-selector?' +
+ 'org=' + mumieOrg +
+ '&serverUrl=' + encodeURIComponent(selectedServer) +
+ '&problemLang=' + langController.getSelectedLanguage() +
'&origin=' + encodeURIComponent(window.location.origin) +
'&uiLang=' + systemLanguage +
'&gradingType=' + gradingType +
'&multiCourse=true' +
- '&worksheet=true' +
- (selection ? '&selection=' + selection : '');
+ '&worksheet=true';
+
+ if (multiSelect) {
+ return base + (useSSO ? '&multiselect=true' : '&multiSelect=true');
+ }
+ const selection = taskController.getDelocalizedTaskLink();
+ return selection ? base + '&selection=' + selection : base;
}
/**
@@ -249,23 +261,34 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
multiProblemSelectorButton.onclick = function(e) {
e.preventDefault();
- problemSelectorWindow = window.open(
- lmsSelectorUrl +
- '/lms-problem-selector?' +
- "serverUrl=" +
- encodeURIComponent(serverController.getSelectedServer().urlprefix) +
- '&gradingType=all',
- "_blank",
- 'toolbar=0,location=0,menubar=0'
- );
+ problemSelectorWindow = window.open(buildURL(true), '_blank', 'toolbar=0,location=0,menubar=0');
};
},
disable: function() {
problemSelectorButton.disabled = true;
- }
+ },
};
})();
+ /**
+ * Apply a picker payload to the form's per-task fields.
+ *
+ * Single source of truth for picker-payload → form-field mapping.
+ * Used by both the single-task postMessage handler and the multi-task
+ * submit loop, so new picker fields only need to be wired here.
+ *
+ * @param {Object} payload picker payload for one task
+ */
+ function applyPickerPayloadToForm(payload) {
+ const isGraded = payload.isGraded !== false;
+ const worksheet = payload.worksheet ?? null;
+ courseController.setCourse(payload.path_to_coursefile);
+ langController.setLanguage(payload.language);
+ taskController.setSelection(payload.link, payload.language, payload.name);
+ taskController.setIsGraded(isGraded);
+ worksheetController.setWorksheet(worksheet);
+ }
+
const courseController = (function() {
const courseNameElem = document.getElementById("id_mumie_course");
const courseFileElem = document.getElementsByName("mumie_coursefile")[0];
@@ -378,11 +401,129 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
* Form inputs related to grades should be disabled if the MUMIE Task is not graded.
*/
function updateGradeEditability() {
- const disabled = durationController().isUngraded();
+ const disabled = durationController.isUngraded();
document.getElementById('id_points').disabled = disabled;
document.getElementById('id_gradepass').disabled = disabled;
document.getElementById('id_gradecat').disabled = disabled;
- durationController().setDurationElements();
+ durationController.setDurationElements();
+ }
+
+ /**
+ * Store selected tasks and update UI for multi-task creation.
+ * @param {Array} tasks array of task objects from the selector
+ */
+ function setMultiSelection(tasks) {
+ const tasksField = document.getElementsByName('mumie_multi_tasks')[0];
+ const summary = document.getElementById('mumie_multi_tasks_summary');
+ if (tasksField) {
+ tasksField.value = JSON.stringify(tasks);
+ }
+ if (summary) {
+ const taskListItems = tasks.map(task => {
+ const taskListItem = document.createElement('li');
+ taskListItem.textContent = task.name;
+ return taskListItem;
+ });
+ summary.innerHTML = '';
+ const summaryHeading = document.createElement('div');
+ const taskList = document.createElement('ul');
+ require(['core/str'], function(Str) {
+ Str.get_string('mumie_multi_tasks_selected', 'mod_mumie', tasks.length)
+ .then(function(headingText) {
+ summaryHeading.textContent = headingText;
+ });
+ });
+ taskList.classList.add('mumie_multi_tasks_list');
+ taskListItems.forEach(taskListItem => taskList.appendChild(taskListItem));
+ summary.appendChild(summaryHeading);
+ summary.appendChild(taskList);
+ summary.style.display = 'block';
+ }
+ const nameField = document.getElementById('id_name');
+ nameField.disabled = true;
+ nameField.value = '';
+ nameField.removeAttribute('required');
+ const taskDisplayElem = document.getElementById('id_task_display_element');
+ if (taskDisplayElem) { taskDisplayElem.value = ''; }
+ const courseNameElem = document.getElementById('id_mumie_course');
+ if (courseNameElem) { courseNameElem.value = ''; }
+ const nameFieldContainer = document.getElementById('fitem_id_name');
+ if (nameFieldContainer) {
+ nameFieldContainer.querySelectorAll('.req, .text-danger, [title="Required field"]')
+ .forEach(requiredIndicator => { requiredIndicator.style.display = 'none'; });
+ }
+ const requiredLegend = document.querySelector('.fdescription.required');
+ if (requiredLegend) {
+ requiredLegend.style.display = 'none';
+ }
+ }
+
+ /**
+ * Serialize a form into a URL-encoded POST string, omitting form mechanics
+ * the webservice does not need (sesskey, qf form marker).
+ *
+ * Disabled form controls are temporarily enabled so FormData includes them:
+ * the picker writes values to display-only inputs (e.g. mumie_course) that
+ * the server-side validator still expects to receive.
+ *
+ * @param {HTMLFormElement} form
+ * @returns {string}
+ */
+ function serializeFormFields(form) {
+ const disabledFields = Array.from(form.querySelectorAll(':disabled'));
+ disabledFields.forEach(field => { field.disabled = false; });
+ try {
+ const omit = ['sesskey', '_qf__mod_mumie_mod_form'];
+ return Array.from(new FormData(form))
+ .filter(([key]) => !omit.includes(key))
+ .map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value))
+ .join('&');
+ } finally {
+ disabledFields.forEach(field => { field.disabled = true; });
+ }
+ }
+
+ /**
+ * Submit multi-tasks via AJAX, then redirect to course.
+ *
+ * Builds one full form payload per task by applying each picker payload
+ * to the live form and serializing it. The server treats each entry as
+ * an independent single-task submission, so picker-payload → form-field
+ * mapping lives in applyPickerPayloadToForm and is shared with the
+ * single-task path. Validation runs server-side via the shared form
+ * pipeline; errors surface as a modal alert with the validator's
+ * localized message.
+ */
+ function submitMultiTasks() {
+ const tasksField = document.getElementsByName('mumie_multi_tasks')[0];
+ const courseId = document.getElementsByName('course')[0]?.value;
+ const submitButton = document.getElementById('id_submitbutton');
+ const form = submitButton && submitButton.closest('form');
+ const tasks = JSON.parse(tasksField.value);
+
+ const tasksFormData = tasks.map(task => {
+ applyPickerPayloadToForm(task);
+ return serializeFormFields(form);
+ });
+
+ // applyPickerPayloadToForm leaves the form in single-task mode of the last task;
+ // restore the multi-task UI so the form is coherent if the AJAX call fails.
+ setMultiSelection(tasks);
+
+ require(['core/ajax', 'core/notification'], function(Ajax, notification) {
+ Ajax.call([{
+ methodname: 'mod_mumie_create_multiple_mumie_tasks',
+ args: {
+ contextid: parseInt(contextId),
+ section: section,
+ tasksformdata: tasksFormData,
+ },
+ }])[0].done(function() {
+ window.location.href = M.cfg.wwwroot + '/course/view.php?id=' + courseId;
+ }).fail(function(error) {
+ notification.alert('', error.message);
+ });
+ });
}
return {
@@ -390,15 +531,38 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
updateTaskDisplayElement(taskSelectionInput.value);
},
setSelection: function(link, language, name) {
+ const tasksField = document.getElementsByName('mumie_multi_tasks')[0];
+ if (tasksField) {
+ tasksField.value = '';
+ }
+ const summary = document.getElementById('mumie_multi_tasks_summary');
+ if (summary) {
+ summary.style.display = 'none';
+ summary.innerHTML = '';
+ }
+ const nameField = document.getElementById('id_name');
+ if (nameField) {
+ nameField.disabled = false;
+ nameField.setAttribute('required', 'required');
+ }
+ const nameFieldContainer = document.getElementById('fitem_id_name');
+ if (nameFieldContainer) {
+ nameFieldContainer.querySelectorAll('.req, .text-danger, [title="Required field"]')
+ .forEach(requiredIndicator => { requiredIndicator.style.display = ''; });
+ }
+ const requiredLegend = document.querySelector('.fdescription.required');
+ if (requiredLegend) {
+ requiredLegend.style.display = '';
+ }
updateTaskUri(link, language);
updateName(name);
},
setIsGraded: function(isGraded) {
- durationController().setGradedElemValue(isGraded);
+ durationController.setGradedElemValue(isGraded);
updateGradeEditability();
},
getGradingType: function() {
- const isGraded = durationController().getGradedElemValue();
+ const isGraded = durationController.getGradedElemValue();
if (isGraded === '1') {
return 'graded';
} else if (isGraded === '0') {
@@ -408,7 +572,13 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
},
getDelocalizedTaskLink: function() {
return delocalizeLink(taskSelectionInput.value);
- }
+ },
+ setMultiSelection: setMultiSelection,
+ hasMultiTasks: function() {
+ const tasksField = document.getElementsByName('mumie_multi_tasks')[0];
+ return tasksField && !!tasksField.value;
+ },
+ submitMultiTasks: submitMultiTasks,
};
})();
@@ -450,6 +620,17 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
});
}
+ /**
+ * Show or hide per-task "Working period: not Deadline" warnings based on whether the duedate property is selected.
+ */
+ function updateNoDuedateWarnings() {
+ const duedateSelected = Array.from(propertySelectionInputs)
+ .some(checkbox => checkbox.value === 'duedate' && checkbox.checked);
+ document.querySelectorAll('.mumie-form-working-period-not-duedate-warning').forEach(function(elem) {
+ elem.style.display = duedateSelected ? '' : 'none';
+ });
+ }
+
/**
* Set selection listeners for properties to apply to MUMIE Tasks in the course.
*/
@@ -462,6 +643,7 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
selectedTaskProp.push(checkbox.value);
}
selectedTaskProperties.value = JSON.stringify(selectedTaskProp);
+ updateNoDuedateWarnings();
};
});
}
@@ -535,10 +717,11 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
}
return {
- init: function(contextIdParam, prbSelectorUrl, lang) {
+ init: function(contextIdParam, prbSelectorUrl, lang, sectionParam) {
lmsSelectorUrl = prbSelectorUrl;
systemLanguage = lang;
contextId = contextIdParam;
+ section = sectionParam;
const isEdit = document.getElementById("id_name").getAttribute('value');
const serverStructure = JSON.parse(document.getElementsByName('mumie_server_structure')[0].value);
if (isEdit && !serverConfigExists()) {
@@ -551,14 +734,35 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
taskController.init();
multiTaskEditController.init();
problemSelectorController.init();
- durationController().init();
+ durationController.init();
}
- multiTaskEditController.init();
if (addServerButton) {
require(['auth_mumie/mumie_server_config'], function(MumieServer) {
MumieServer.init(addServerButton, contextId);
});
}
+
+ const submitButton = document.getElementById('id_submitbutton');
+ const form = submitButton && submitButton.closest('form');
+ if (form) {
+ let cancelClicked = false;
+ const cancelButton = document.getElementById('id_cancel');
+ if (cancelButton) {
+ cancelButton.addEventListener('click', function() {
+ cancelClicked = true;
+ });
+ }
+ form.addEventListener('submit', function(e) {
+ if (cancelClicked) {
+ cancelClicked = false;
+ return;
+ }
+ if (taskController.hasMultiTasks()) {
+ e.preventDefault();
+ taskController.submitMultiTasks();
+ }
+ });
+ }
}
};
@@ -579,4 +783,4 @@ define(['jquery', 'core/templates', 'core/modal_factory', 'auth_mumie/mumie_serv
function serverConfigExists() {
return document.getElementsByName("mumie_missing_config")[0].getAttribute("value") === "";
}
- });
\ No newline at end of file
+ });
diff --git a/amd/src/view.js b/amd/src/view.js
index 32d1db10..22f36665 100644
--- a/amd/src/view.js
+++ b/amd/src/view.js
@@ -1,4 +1,4 @@
-define(['jquery', 'core/templates', 'core/modal_factory', 'mod_mumie/duedate_form'],
+define(['jquery', 'core/templates', 'mod_mumie/duedate_form'],
function($) {
return {
init: function(contextid) {
diff --git a/backup/moodle2/backup_mumie_stepslib.php b/backup/moodle2/backup_mumie_stepslib.php
index 5bc9ccc7..60b73ff4 100644
--- a/backup/moodle2/backup_mumie_stepslib.php
+++ b/backup/moodle2/backup_mumie_stepslib.php
@@ -31,7 +31,7 @@ protected function define_structure() {
'name', 'intro', 'introformat', 'timecreated',
'timemodified', 'taskurl', 'launchcontainer',
'mumie_course', 'language', 'server', 'mumie_coursefile',
- 'lastsync', 'points', 'completionpass', 'privategradepool', 'duedate', 'isgraded']);
+ 'lastsync', 'points', 'privategradepool', 'duedate', 'isgraded']);
$serverconfig = new backup_nested_element("serverconfig", ['id'], ['name', 'url_prefix']);
$mumie->add_child($serverconfig);
diff --git a/changelog.md b/changelog.md
index d1457474..26edf98b 100644
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,29 @@
All important changes to this plugin will be documented in this file.
+## [1.10.0] - 2026-06-29
+### Added
+- Teachers can now apply the deadline setting to multiple MUMIE Tasks at once using the multi-edit feature
+- Tasks without a 'Deadline' working period show a warning when deadline transfer is selected
+- Validation prevents transferring a deadline to tasks that were not created with working period type 'Deadline'
+- SSO when opening the Multi Problem Selector for the Problem Pool
+
+### Changed
+- Plugin now requires Moodle 4.3 or higher
+- Support Moodle 5.2
+- Multi Problem Selector now creates MUMIE Tasks via form submission instead of drag-and-drop
+- Settings configured on the form (points, deadline, container, etc.) are now applied to all tasks created via the Multi Problem Selector
+- Multi Problem Selector button is unavailable when editing an existing MUMIE Task
+
+### Removed
+- Drag-and-drop creation of MUMIE Tasks is no longer supported
+- The "Require passing grade" completion option in activity settings; use Moodle's built-in "Receive a grade" → "Passing grade" option instead.
+
+### Fixed
+- MUMIE Task no longer shows working period descriptions for all options simultaneously
+- Inconsistent working period description texts corrected
+- Deleting a due date extension no longer fails with a server error
+
## [v1.9.1] - 2025-07-03
### Changed
- Refactored code to comply with PHP 8.4
diff --git a/classes/mumie_dndupload_processor.php b/classes/mumie_dndupload_processor.php
deleted file mode 100644
index bd55925c..00000000
--- a/classes/mumie_dndupload_processor.php
+++ /dev/null
@@ -1,335 +0,0 @@
-.
-
-/**
- * This file describes a class used to create MUMIE Tasks via drag & drop.
- *
- * @package mod_mumie
- * @copyright 2017-2025 integral-learning GmbH (https://www.integral-learning.de/)
- * @author Tobias Goltz (tobias.goltz@integral-learning.de)
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-namespace mod_mumie;
-
-defined('MOODLE_INTERNAL') || die;
-
-require_once($CFG->dirroot . '/auth/mumie/classes/mumie_server.php');
-
-/**
- * This processor is responsible for the creation of MUMIE Task that are being imported via dnd_handler.
- *
- * Some functions are taken and adapted from course/dnduploadlib.php.
- *
- * @package mod_mumie
- * @copyright 2017-2020 integral-learning GmbH (https://www.integral-learning.de/)
- * @author Tobias Goltz (tobias.goltz@integral-learning.de)
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class mumie_dndupload_processor {
- /**
- * The number of the sections we want to add MUMIE Tasks to.
- * @var int
- */
- private $section;
-
- /**
- * The data type of the uploaded file.
- * @var string
- */
- private $type;
-
- /**
- * The uploaded file.
- * @var \stdClass
- */
- private $upload;
-
- /**
- * The context of the current course.
- * @var \context_course
- */
- private $coursecontext;
-
- /**
- * The course we want to create the MUMIE Tasks in.
- * @var \stdClass
- */
- private $course;
-
- /**
- * The gradepool settings we're going to use for the new MUMIE Tasks.
- * @var int
- */
- private $gradepoolsettings;
-
- /**
- * Constructor
- * @param int $courseid Id of course we want to create Tasks in
- * @param int $section The section number we are uploading to
- * @param string $type The type identifier of the uploaded data
- * @param stdClass $upload The object that has been uploaded
- */
- public function __construct($courseid, $section, $type, $upload) {
- $this->section = $section;
- $this->type = $type;
- $this->upload = $upload;
- $this->gradepoolsettings = $this->get_course_gradepool_setting();
- $this->coursecontext = \context_course::instance($courseid);
- global $COURSE;
- $this->course = $COURSE;
- }
-
- /**
- * Start the creation of MUMIE Tasks for the uploaded data.
- *
- * We're hooking into the regular dnd_upload process.
- * That's why the first uploaded problem needs to be handled differently form any further instances.
- * @return int id of newly added grade item or false
- */
- public function process() {
- require_capability('moodle/course:manageactivities', $this->coursecontext);
- require_sesskey();
-
- if ($this->type == 'mumie/json') {
- return $this->handle_single_upload();
- }
- if ($this->type == 'mumie/jsonArray') {
- return $this->handle_multi_upload();
- }
- return false;
- }
-
- /**
- * Handle uploads of type mumie/json. Only a single Mumie Task will be created.
- * This format is used by MUMIE servers.
- * @return int id of newly added grade item
- */
- private function handle_single_upload() {
- global $CFG;
- require_once($CFG->dirroot . '/auth/mumie/classes/mumie_server.php');
- $server = new \auth_mumie\mumie_server();
- $server->set_urlprefix($this->upload->server);
-
- $this->validate_upload_params($this->upload);
- $this->validate_servers([$server]);
- $this->create_missing_servers([$server]);
-
- $mumie = $this->create_mumie_from_uploadinstance($this->upload, $server);
- $mumie->server = $server->get_urlprefix();
-
- return mumie_add_instance($mumie, null);
- }
-
- /**
- * Handle uploads of type mumie/jsonArray. This function creates one or more instances of MUMIE Task.
- * This format is used by LEMON servers.
- * @return int id of newly added grade item
- */
- private function handle_multi_upload() {
- // This array holds unique server objects.
- $servers = [];
- $this->upload = (array) $this->upload;
- foreach ($this->upload as $uploadinstance) {
- $uploadinstance = json_decode($uploadinstance);
- $this->validate_upload_params($uploadinstance);
- $server = new \auth_mumie\mumie_server();
- $server->set_urlprefix($uploadinstance->server);
- $servers[$uploadinstance->server] = $server;
- }
-
- $this->validate_servers(array_values($servers));
- $this->create_missing_servers(array_values($servers));
-
- $result;
- $uploadlength = count($this->upload);
- for ($i = 0; $i < $uploadlength; $i++) {
- $uploadinstance = json_decode($this->upload[$i]);
- $mumie = $this->create_mumie_from_uploadinstance($uploadinstance, $servers[$uploadinstance->server]);
- $mumie->server = $servers[$uploadinstance->server]->get_urlprefix();
- if ($i == 0) {
- $result = mumie_add_instance($mumie, null);
- } else {
- $module = $this->create_mumie_course_module($mumie);
- }
- }
-
- // Rebuild the course cache after update action.
- rebuild_course_cache($this->course->id, true);
-
- // Return id of first mumie instance created.
- return $result;
- }
-
- /**
- * Check whether all servers are valid MUMIE/Lemon servers.
- *
- * If a URL is not valid, an exception is thrown.
- *
- * @param \auth_mumie\mumie_server[] $servers The list of server we want to validate
- */
- private function validate_servers($servers) {
- foreach ($servers as $server) {
- if (!$server->is_valid_mumie_server()) {
- throw new \moodle_exception('mumie_form_server_not_existing', 'auth_mumie');
- }
- }
- }
-
- /**
- * Make sure that there is a server configuration for every server used. If there aren't, create them.
- *
- * @param \auth_mumie\mumie_server[] $servers The list of servers for which we need server configurations.
- */
- private function create_missing_servers($servers) {
- $missingservers = array_filter(
- $servers,
- function ($server) {
- return !$server->config_exists_for_url();
- }
- );
-
- if (empty($missingservers)) {
- return;
- }
-
- if (!has_capability("auth/mumie:addserver", \context_course::instance($this->course->id), $USER)) {
- throw new \moodle_exception(get_string('server_config_missing', 'mod_mumie', $server->get_urlprefix()));
- }
- foreach ($missingservers as $server) {
- $server->set_name($server->get_urlprefix());
- $server->upsert();
- }
- }
-
- /**
- * Get the value for MUMIE Task's gradepool setting in this course.
- *
- * @return int gradepool value for the imported MUMIE Tasks.
- */
- private function get_course_gradepool_setting() {
- global $DB, $COURSE;
- $exitingtasks = array_values(
- $DB->get_records(MUMIE_TASK_TABLE, ["course" => $COURSE->id])
- );
- $adminsetting = get_config('auth_mumie', 'defaultgradepool');
- if (count($exitingtasks) > 0) {
- return $exitingtasks[0]->privategradepool;
- }
- if ($adminsetting != -1) {
- return $adminsetting;
- }
- return null;
- }
-
- /**
- * Make sure that there are all necessary parameters set in the uploaded problem instance.
- *
- * @param \stdClass $uploadinstance a problem instance that's being imported
- */
- private function validate_upload_params($uploadinstance) {
- if (
- empty($uploadinstance->link) || empty($uploadinstance->path_to_coursefile)
- || empty($uploadinstance->language) || empty($uploadinstance->name)
- || empty($uploadinstance->server) || empty($uploadinstance->course)
- ) {
- throw new \moodle_exception('parameter_missing', 'mod_mumie');
- }
- }
-
- /**
- * Get a mumie object from the uploaded problem instance.
- *
- * @param \stdClass $uploadinstance The uploaded problem.
- * @return \stdClass The mumie object that's going to be created.
- */
- private function create_mumie_from_uploadinstance($uploadinstance) {
- $mumie = new \stdClass();
- $mumie->taskurl = $uploadinstance->link . '?lang=' . $uploadinstance->language;
- $mumie->mumie_coursefile = $uploadinstance->path_to_coursefile;
- $mumie->language = $uploadinstance->language;
- $mumie->course = $this->course->id;
- $mumie->mumie_course = $uploadinstance->course;
- $mumie->intro = '';
- $mumie->points = 100;
- $mumie->name = \mod_mumie\locallib::get_default_name($uploadinstance) ?? $uploadinstance->name;
- $mumie->privategradepool = $this->gradepoolsettings;
- $mumie->isgraded = $uploadinstance->isGraded;
- return $mumie;
- }
-
- /**
- * Create database entries for the new mumie instance and the linked course module.
- *
- * @param \stdClass $mumie The mumie object we want to add to the course.
- */
- private function create_mumie_course_module($mumie) {
- $mumieinstance = mumie_add_instance($mumie, null);
- $cm = $this->create_course_module();
- $this->finish_setup_course_module($mumieinstance, $cm);
- }
-
- /**
- * Create a new course module in the database.
- */
- private function create_course_module() {
- global $CFG;
- require_once($CFG->dirroot . '/course/modlib.php');
- [$module, $context, $cw, $cm, $data] = prepare_new_moduleinfo_data($this->course, 'mumie', $this->section);
- $data->coursemodule = $data->id = add_course_module($data);
- return $data;
- }
-
- /**
- * Set missing values for course_module and finish the setup.
- *
- * @param int $instanceid id of newly created mumie instance
- * @param \stdClass $cm Newly created coursemodule instance
- */
- protected function finish_setup_course_module($instanceid, $cm) {
- global $DB;
-
- if (!$instanceid) {
- // Something has gone wrong - undo everything we can.
- course_delete_module($cm->id);
- throw new \moodle_exception('errorcreatingactivity', 'moodle', '', $this->module->name);
- }
-
- // Note the section visibility.
- $visible = get_fast_modinfo($this->course)->get_section_info($this->section)->visible;
-
- $DB->set_field('course_modules', 'instance', $instanceid, ['id' => $cm->id]);
-
- course_add_cm_to_section($this->course, $cm->id, $this->section);
-
- set_coursemodule_visible($cm->id, $visible);
- if (!$visible) {
- $DB->set_field('course_modules', 'visibleold', 1, ['id' => $cm->id]);
- }
-
- // Retrieve the final info about this module.
- $info = get_fast_modinfo($this->course);
- if (!isset($info->cms[$cm->id])) {
- // The course module has not been properly created in the course - undo everything.
- course_delete_module($cm->id);
- throw new \moodle_exception('errorcreatingactivity', 'moodle', '', "mod_mumie");
- }
- $mod = $info->get_cm($cm->id);
-
- // Trigger course module created event.
- $event = \core\event\course_module_created::create_from_cm($mod);
- $event->trigger();
- }
-}
diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php
index 48a7db41..1c0bda61 100644
--- a/classes/privacy/provider.php
+++ b/classes/privacy/provider.php
@@ -39,7 +39,10 @@
* @author Tobias Goltz (tobias.goltz@integral-learning.de)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class provider implements \core_privacy\local\request\core_userlist_provider, \core_privacy\local\metadata\provider, \core_privacy\local\request\plugin\provider {
+class provider implements
+ \core_privacy\local\metadata\provider,
+ \core_privacy\local\request\core_userlist_provider,
+ \core_privacy\local\request\plugin\provider {
/**
* Returns meta data about this system.
*
diff --git a/db/install.xml b/db/install.xml
index e0b9d56d..587d8bdb 100644
--- a/db/install.xml
+++ b/db/install.xml
@@ -21,7 +21,6 @@
Bitte beachten Sie:
-Noten für MUMIE Tasks werden nur aktualisiert, wenn sie im Moodle-Gradebook angezeigt werden. Wenn Sie aktuelle Informationen über den Aktivitätsabschluss aller Studierenden sehen möchten,
-sollten zu daher zuvor die Bewerterübersicht öffnen.';
-$string['dnd_addmessage'] = 'MUMIE-Task hinzufügen';
-$string['dnd_addmessage_multiple'] = 'MUMIE-Tasks hinzufügen';
-$string['dndupload_message'] = 'Als neue MUMIE Task hinzufügen';
-$string['gradetopassmustbeset'] = 'Die Bestehensgrenze kann nicht 0 sein, da der Aktivitätsabschluss vom Erreichen dieser Grenze abhängig ist. Bitte geben Sie daher einen Wert über 0 ein.';
-$string['gradetopassnotset'] = 'Dieser Test hat keine Bestehensgrenze.';
$string['modulename'] = 'MUMIE Task';
$string['modulename_help'] = '
Dieses Aktivität-Modul ermöglicht die Nutzung von Inhalten der MUMIE-Plattform auf Moodle und eine automatische Synchronisierung der dort erzielten Noten. -Ein MUMIE Task steht hierbei für eine einzelne benote Aufgabe eines MUMIE-Kurses.
+Ein MUMIE Task steht hierbei für eine einzelne benotete Aufgabe eines MUMIE-Kurses.Was ist MUMIE?
@@ -66,14 +55,14 @@
$string['mumie:viewgrades'] = 'Alle Noten einer MUMIE Task in einem Kurs sehen';
$string['mumie_calendar_duedate_desc'] = 'Später Abgaben werden nicht in die Moodle-Bewertungen übernommen';
$string['mumie_calendar_duedate_extension'] = 'Verlängerte Abgabefrist: {$a}';
-$string['mumie_calendar_duedate_name'] = 'Abgabefirst: {$a}';
+$string['mumie_calendar_duedate_name'] = 'Abgabefrist: {$a}';
$string['mumie_due_date'] = 'Abgabefrist';
-$string['mumie_due_date_help'] = 'Die Noten werden erst nach Ablauf der Frist mit Moodle synchronisiert.';
+$string['mumie_due_date_help'] = 'Noten, die nach der ausgewählten Frist erzielt wurden, werden nicht mit Moodle synchronisiert.';
$string['mumie_duedate_extension'] = 'Abfabefristverlängerung';
$string['mumie_duedate_form'] = 'Abgabefrist verlängern';
$string['mumie_duedate_not_set'] = 'Keine allgemeine Abgabefrist für diese MUMIE-Task festgelegt!';
$string['mumie_duration_selector'] = 'Bearbeitungszeitraum';
-$string['mumie_duration_selector_help'] = 'Bei einem begrenzten Bearbeitungszeitraum werden die Noten erst nach Ablauf der Fristen mit Moodle synchronisiert.';
+$string['mumie_duration_selector_help'] = 'Bei einem begrenzten Bearbeitungszeitraum werden nur die in diesem Zeitraum erzielten Noten mit Moodle synchronisiert.';
$string['mumie_form_activity_container'] = 'Startcontainer';
$string['mumie_form_activity_container_embedded'] = 'Eingebunden';
$string['mumie_form_activity_container_help'] = 'Bitte wählen Sie, ob diese Aktivität in die Moodle-Umgebung eingebunden oder in einem neuen Browser-Tab geöffnet werden soll.
Bitte beachten Sie, dass eingebettete MUMIE Tasks aus technischen Gründen nicht mit Safari bearbeitet werden können. Für diese User wird diese MUMIE Task daher im gesamten Tab dargestellt.';
@@ -93,7 +82,9 @@
$string['mumie_form_complete_course_help'] = 'Nutzer werden nur eingeloggt und dann zur Kursübersicht weitergeleitet. Leistungsdaten werden für diese MUMIE-Task nicht synchronisiert.';
$string['mumie_form_coursefile'] = 'Path to MUMIE course meta file';
$string['mumie_form_deadline_prohibited_for_worksheet_without_trigger_after_deadline'] = "Das ausgewählte Worksheet darf keinen begrenzten Bearbeitungszeitraum haben.
Wählen Sie ein Worksheet mit entsprechender Konfiguration aus, wenn Sie eine Frist setzen möchten.";
+$string['mumie_form_deadline_transfer_prohibited_for_tasks_without_deadline'] = 'Abgabefrist kann nur für MUMIE-Tasks übernommen werden, die bereits den Bearbeitungszeitraum Typ \'Abgabefrist\' haben.';
$string['mumie_form_deadline_required_for_trigger_after_deadline'] = "Das ausgewählte Worksheet setzt einen festgelegten Bearbeitungszeitraum voraus";
+$string['mumie_form_due_date_multi_edit_hint'] = 'Abgabefrist kann nur für MUMIE-Tasks übernommen werden, welche vorher bereits mit dem Bearbeitungszeitraum Typ \'Abgabefrist\' erstellt wurden.';
$string['mumie_form_due_date_must_be_future'] = 'Das Datum der Abgabefrist kann nicht in der Vergangenheit liegen!';
$string['mumie_form_filter'] = 'MUMIE-Tasks filtern';
$string['mumie_form_grade_pool'] = 'Punkte mit anderen Kursen teilen';
@@ -120,13 +111,16 @@
$string['mumie_form_topic'] = 'Thema: {$a}';
$string['mumie_form_updated_selection'] = 'Aufgabe erfolgreich ausgewählt';
$string['mumie_form_wiki_link'] = 'Bitte besuchen Sie unser Wiki für weiterführende Informationen!';
+$string['mumie_form_working_period_not_duedate_warning'] = 'Bearbeitungszeitraum: keine Abgabefrist';
$string['mumie_general_duedate'] = 'Allgemeine Abgabefrist:';
$string['mumie_general_timelimit'] = 'Zeitlimit: ';
$string['mumie_grade_invalid'] = 'Bewertung ungültig!';
$string['mumie_grade_overridden'] = 'Bewertung erfolgreich gespeichert!';
$string['mumie_grade_percentage'] = 'Bewerung in %';
$string['mumie_grading_settings'] = "Individuelle Bewertungseinstellungen";
-$string['mumie_multi_problem_selector'] = 'Alternativ können Sie auch mehrere MUMIE Tasks gleichzeitig mit der Mehrfach-Aufgabenauswahl erstellen. Ziehen Sie dazu einfach die gewünschten Aufgaben in ihren Kurs.
Für eine Kurzanleitung und weitere Informationen besuchen Sie unser Wiki';
+$string['mumie_multi_problem_selector'] = 'Alternativ können Sie mit der Mehrfach-Aufgabenauswahl auch mehrere MUMIE-Aufgaben auf einmal auswählen. Alle Aufgaben werden mit den eingestellten Einstellungen angelegt (Punkte, Frist, Startcontainer etc.). Jede Aufgabe behält ihren eigenen Namen — anders als bei der Einzelauswahl kann kein eigener Name vergeben werden.
Weitere Informationen finden Sie in unserem Wiki';
+$string['mumie_multi_task_validation_error'] = '{$a}';
+$string['mumie_multi_tasks_selected'] = 'Sie haben folgende {$a} Aufgaben ausgewählt:';
$string['mumie_no_other_task_found'] = 'Keine andere MUMIE Tasks in Kurs gefunden';
$string['mumie_no_submissions'] = 'Keine Antworten abgegeben';
$string['mumie_open_task'] = 'MUMIE-Task öffnen';
@@ -142,9 +136,9 @@
$string['mumie_task_updated'] = 'Es wurde eine weitere MUMIE-Task aktualisiert';
$string['mumie_tasks_updated'] = 'Es wurden {$a} weitere MUMIE-Tasks aktualisiert';
$string['mumie_timelimit'] = 'Zeitlimit';
-$string['mumie_timelimit_help'] = 'Die Note eines Studierenden wird erst mit Moodle synchronisiert, nachdem seine individuelle Frist abgelaufen ist. Das Zeitlimit beginnt in dem Moment, in dem ein Studierender eine MUMIE-Aufgabe zum ersten Mal öffnet.';
+$string['mumie_timelimit_help'] = 'Noten, die nach Ablauf des Zeitlimits erzielt wurden, werden nicht mit Moodle synchronisiert. Das Zeitlimit beginnt in dem Moment, in dem ein Studierender eine MUMIE-Aufgabe zum ersten Mal öffnet.';
$string['mumie_unlimited'] = 'Uneingeschränkt';
-$string['mumie_unlimited_help'] = 'Die Noten können zu jeder Zeit mit Moodle synchronisiert werden.';
+$string['mumie_unlimited_help'] = 'Es gibt keine zeitliche Beschränkung. Alle erzielten Noten werden mit Moodle synchronisiert.';
$string['mumieintro'] = 'Aktivitätsbeschreibung';
$string['name'] = 'Name';
$string['nomumieinstance'] = 'In diesem Kurs gibt es keine MUMIE-Instanzen';
diff --git a/lang/en/mumie.php b/lang/en/mumie.php
index ac863de7..bc9bacc8 100644
--- a/lang/en/mumie.php
+++ b/lang/en/mumie.php
@@ -24,15 +24,6 @@
*/
defined('MOODLE_INTERNAL') || die;
-$string['completionpass'] = 'Require passing grade';
-$string['completionpass_help'] = 'If enabled, this activity is considered completed when the student receives a passing grade, with the pass grade set in the gradebook.
-
Please Note:
-Grades for MUMIE Tasks are only updated, when the gradebook is opened. If you want the current completion status of all students, remember to open the gradebook overview page first';
-$string['dnd_addmessage'] = 'Add MUMIE Task here';
-$string['dnd_addmessage_multiple'] = 'Add MUMIE Tasks here';
-$string['dndupload_message'] = 'Add as a new MUMIE Task';
-$string['gradetopassmustbeset'] = 'Grade to pass cannot be zero as this activity has its completion method set to require passing grade. Please set a non-zero value.';
-$string['gradetopassnotset'] = 'This MUMIE task does not have a grade to pass set so you cannot use this option. Please use the require grade setting instead.';
$string['modulename'] = 'MUMIE Task';
$string['modulename_help'] = '
This activity module enables the usage of content from the MUMIE e-learning platform and automatic grade synchronization. A MUMIE tasks represents an individual graded problem from a MUMIE course.
What is MUMIE?
@@ -60,7 +51,7 @@ $string['mumie_duedate_form'] = 'Grant a due date extension'; $string['mumie_duedate_not_set'] = 'No general due date set for this MUMIE Task!'; $string['mumie_duration_selector'] = 'Working period'; -$string['mumie_duration_selector_help'] = 'If the working period is limited, grades are only synchronized with Moodle after the period ends.'; +$string['mumie_duration_selector_help'] = 'If the working period is limited, only grades achieved within that period are synchronized with Moodle.'; $string['mumie_form_activity_container'] = 'Launch container'; $string['mumie_form_activity_container_embedded'] = 'embedded'; $string['mumie_form_activity_container_help'] = 'Please select whether the activity should be opened in a new browser tab or embedded into moodle.