From 13f88df351b9067889982ca1023362f076582b2b Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Tue, 23 Mar 2021 20:18:38 +0800 Subject: [PATCH] MDL-70821 course: Cover availability conditions for manual completion When an activity has manual completion tracking, pressing the manual completion checkbox reloads the page after toggling the completion state when the activity is linked to availability conditions. The "Mark as done" button needs to mimic this behaviour as well. The approach being taken here is to add a core_course/view JS module for the course homepage which listens for the manualCompletionToggled event and reloads the page when the activity module has availability conditions tied to it. Perhaps for future development, instead of reloading the page, the container of the restricted course sections/activities can reloaded via AJAX as well. --- .../amd/build/manual_completion_toggle.min.js | 2 +- .../build/manual_completion_toggle.min.js.map | 2 +- course/amd/build/view.min.js | 2 + course/amd/build/view.min.js.map | 1 + course/amd/src/manual_completion_toggle.js | 2 + course/amd/src/view.js | 50 +++++++++++++++++++ .../classes/output/activity_information.php | 13 +++++ course/templates/completion_manual.mustache | 4 +- course/view.php | 5 ++ 9 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 course/amd/build/view.min.js create mode 100644 course/amd/build/view.min.js.map create mode 100644 course/amd/src/view.js diff --git a/course/amd/build/manual_completion_toggle.min.js b/course/amd/build/manual_completion_toggle.min.js index 0bd933bdf6b..2dce9b36bfa 100644 --- a/course/amd/build/manual_completion_toggle.min.js +++ b/course/amd/build/manual_completion_toggle.min.js @@ -1,2 +1,2 @@ -function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_course/manual_completion_toggle",["exports","core/templates","core/notification","core_course/repository","core_course/events"],function(a,b,c,d,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=h(b);c=h(c);e=g(e);function f(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;f=function(){return a};return a}function g(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=f();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var g=d?Object.getOwnPropertyDescriptor(a,e):null;if(g&&(g.get||g.set)){Object.defineProperty(c,e,g)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function h(a){return a&&a.__esModule?a:{default:a}}function i(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function j(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){i(h,d,e,f,g,"next",a)}function g(a){i(h,d,e,f,g,"throw",a)}f(void 0)})}}var k={MANUAL_TOGGLE:"button[data-action=toggle-manual-completion]"},l={TOGGLE_MARK_DONE:"manual:mark-done",TOGGLE_UNDO:"manual:undo"},m=!1,n=function(){if(m){return}document.addEventListener("click",function(a){var b=a.target.closest(k.MANUAL_TOGGLE);if(b){a.preventDefault();o(b).catch(c.default.exception)}});m=!0};a.init=n;var o=function(){var a=j(regeneratorRuntime.mark(function a(f){var g,h,i,j,k,m,n,o,p,q,r;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:g=f.innerHTML;f.setAttribute("disabled","disabled");h=f.getAttribute("data-toggletype");i=f.getAttribute("data-cmid");j=f.getAttribute("data-activityname");k=h===l.TOGGLE_MARK_DONE;a.next=8;return b.default.render("core/loading",{});case 8:m=a.sent;a.next=11;return b.default.replaceNodeContents(f,m,"");case 11:a.prev=11;a.next=14;return(0,d.toggleManualCompletion)(i,k);case 14:n={cmid:i,activityname:j,overallcomplete:k,overallincomplete:!k,istrackeduser:!0};a.next=17;return b.default.renderForPromise("core_course/completion_manual",n);case 17:o=a.sent;a.next=20;return b.default.replaceNode(f,o.html,o.js);case 20:p=a.sent;q=p.pop();r=new CustomEvent(e.manualCompletionToggled,{bubbles:!0,detail:{cmid:i,activityname:j,completed:k}});q.dispatchEvent(r);a.next=31;break;case 26:a.prev=26;a.t0=a["catch"](11);f.removeAttribute("disabled");f.innerHTML=g;c.default.exception(a.t0);case 31:case"end":return a.stop();}}},a,null,[[11,26]])}));return function(){return a.apply(this,arguments)}}()}); +function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_course/manual_completion_toggle",["exports","core/templates","core/notification","core_course/repository","core_course/events"],function(a,b,c,d,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=h(b);c=h(c);e=g(e);function f(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;f=function(){return a};return a}function g(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=f();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var g=d?Object.getOwnPropertyDescriptor(a,e):null;if(g&&(g.get||g.set)){Object.defineProperty(c,e,g)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function h(a){return a&&a.__esModule?a:{default:a}}function i(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function j(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){i(h,d,e,f,g,"next",a)}function g(a){i(h,d,e,f,g,"throw",a)}f(void 0)})}}var k={MANUAL_TOGGLE:"button[data-action=toggle-manual-completion]"},l={TOGGLE_MARK_DONE:"manual:mark-done",TOGGLE_UNDO:"manual:undo"},m=!1,n=function(){if(m){return}document.addEventListener("click",function(a){var b=a.target.closest(k.MANUAL_TOGGLE);if(b){a.preventDefault();o(b).catch(c.default.exception)}});m=!0};a.init=n;var o=function(){var a=j(regeneratorRuntime.mark(function a(f){var g,h,i,j,k,m,n,o,p,q,r,s;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:g=f.innerHTML;f.setAttribute("disabled","disabled");h=f.getAttribute("data-toggletype");i=f.getAttribute("data-cmid");j=f.getAttribute("data-activityname");k=h===l.TOGGLE_MARK_DONE;a.next=8;return b.default.render("core/loading",{});case 8:m=a.sent;a.next=11;return b.default.replaceNodeContents(f,m,"");case 11:a.prev=11;a.next=14;return(0,d.toggleManualCompletion)(i,k);case 14:n={cmid:i,activityname:j,overallcomplete:k,overallincomplete:!k,istrackeduser:!0};a.next=17;return b.default.renderForPromise("core_course/completion_manual",n);case 17:o=a.sent;a.next=20;return b.default.replaceNode(f,o.html,o.js);case 20:p=a.sent;q=p.pop();r=f.getAttribute("data-withavailability");s=new CustomEvent(e.manualCompletionToggled,{bubbles:!0,detail:{cmid:i,activityname:j,completed:k,withAvailability:r}});q.dispatchEvent(s);a.next=32;break;case 27:a.prev=27;a.t0=a["catch"](11);f.removeAttribute("disabled");f.innerHTML=g;c.default.exception(a.t0);case 32:case"end":return a.stop();}}},a,null,[[11,27]])}));return function(){return a.apply(this,arguments)}}()}); //# sourceMappingURL=manual_completion_toggle.min.js.map diff --git a/course/amd/build/manual_completion_toggle.min.js.map b/course/amd/build/manual_completion_toggle.min.js.map index a82ad987a34..a9a8ec8915a 100644 --- a/course/amd/build/manual_completion_toggle.min.js.map +++ b/course/amd/build/manual_completion_toggle.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/manual_completion_toggle.js"],"names":["SELECTORS","MANUAL_TOGGLE","TOGGLE_TYPES","TOGGLE_MARK_DONE","TOGGLE_UNDO","registered","init","document","addEventListener","e","toggleButton","target","closest","preventDefault","toggleManualCompletionState","catch","Notification","exception","originalInnerHtml","innerHTML","setAttribute","toggleType","getAttribute","cmid","activityname","completed","Templates","render","loadingHtml","replaceNodeContents","templateContext","overallcomplete","overallincomplete","istrackeduser","renderForPromise","renderObject","replaceNode","html","js","replacedNode","newToggleButton","pop","toggledEvent","CustomEvent","CourseEvents","manualCompletionToggled","bubbles","detail","dispatchEvent","removeAttribute"],"mappings":"ihBAyBA,OACA,OAEA,O,25BAOMA,CAAAA,CAAS,CAAG,CACdC,aAAa,CAAE,8CADD,C,CASZC,CAAY,CAAG,CACjBC,gBAAgB,CAAE,kBADD,CAEjBC,WAAW,CAAE,aAFI,C,CAUjBC,CAAU,G,CAKDC,CAAI,CAAG,UAAM,CACtB,GAAID,CAAJ,CAAgB,CACZ,MACH,CACDE,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAACC,CAAD,CAAO,CACtC,GAAMC,CAAAA,CAAY,CAAGD,CAAC,CAACE,MAAF,CAASC,OAAT,CAAiBZ,CAAS,CAACC,aAA3B,CAArB,CACA,GAAIS,CAAJ,CAAkB,CACdD,CAAC,CAACI,cAAF,GACAC,CAA2B,CAACJ,CAAD,CAA3B,CAA0CK,KAA1C,CAAgDC,UAAaC,SAA7D,CACH,CACJ,CAND,EAOAZ,CAAU,GACb,C,UAQD,GAAMS,CAAAA,CAA2B,4CAAG,WAAMJ,CAAN,6GAE1BQ,CAF0B,CAENR,CAAY,CAACS,SAFP,CAKhCT,CAAY,CAACU,YAAb,CAA0B,UAA1B,CAAsC,UAAtC,EAGMC,CAR0B,CAQbX,CAAY,CAACY,YAAb,CAA0B,iBAA1B,CARa,CAS1BC,CAT0B,CASnBb,CAAY,CAACY,YAAb,CAA0B,WAA1B,CATmB,CAU1BE,CAV0B,CAUXd,CAAY,CAACY,YAAb,CAA0B,mBAA1B,CAVW,CAY1BG,CAZ0B,CAYdJ,CAAU,GAAKnB,CAAY,CAACC,gBAZd,gBAeNuB,WAAUC,MAAV,CAAiB,cAAjB,CAAiC,EAAjC,CAfM,QAe1BC,CAf0B,wBAgB1BF,WAAUG,mBAAV,CAA8BnB,CAA9B,CAA4CkB,CAA5C,CAAyD,EAAzD,CAhB0B,mCAoBtB,6BAAuBL,CAAvB,CAA6BE,CAA7B,CApBsB,SAuBtBK,CAvBsB,CAuBJ,CACpBP,IAAI,CAAEA,CADc,CAEpBC,YAAY,CAAEA,CAFM,CAGpBO,eAAe,CAAEN,CAHG,CAIpBO,iBAAiB,CAAE,CAACP,CAJA,CAKpBQ,aAAa,GALO,CAvBI,iBA8BDP,WAAUQ,gBAAV,CAA2B,+BAA3B,CAA4DJ,CAA5D,CA9BC,SA8BtBK,CA9BsB,wBAiCDT,WAAUU,WAAV,CAAsB1B,CAAtB,CAAoCyB,CAAY,CAACE,IAAjD,CAAuDF,CAAY,CAACG,EAApE,CAjCC,SAiCtBC,CAjCsB,QAkCtBC,CAlCsB,CAkCJD,CAAY,CAACE,GAAb,EAlCI,CAqCtBC,CArCsB,CAqCP,GAAIC,CAAAA,WAAJ,CAAgBC,CAAY,CAACC,uBAA7B,CAAsD,CACvEC,OAAO,GADgE,CAEvEC,MAAM,CAAE,CACJxB,IAAI,CAAJA,CADI,CAEJC,YAAY,CAAZA,CAFI,CAGJC,SAAS,CAATA,CAHI,CAF+D,CAAtD,CArCO,CA8C5Be,CAAe,CAACQ,aAAhB,CAA8BN,CAA9B,EA9C4B,sDAkD5BhC,CAAY,CAACuC,eAAb,CAA6B,UAA7B,EACAvC,CAAY,CAACS,SAAb,CAAyBD,CAAzB,CAGAF,UAAaC,SAAb,OAtD4B,wDAAH,uD","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Provides the functionality for toggling the manual completion state of a course module through\n * the manual completion button.\n *\n * @module core_course/manual_completion_toggle\n * @package core_course\n * @copyright 2021 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport {toggleManualCompletion} from 'core_course/repository';\nimport * as CourseEvents from 'core_course/events';\n\n/**\n * Selectors in the manual completion template.\n *\n * @type {{MANUAL_TOGGLE: string}}\n */\nconst SELECTORS = {\n MANUAL_TOGGLE: 'button[data-action=toggle-manual-completion]',\n};\n\n/**\n * Toggle type values for the data-toggletype attribute in the core_course/completion_manual template.\n *\n * @type {{TOGGLE_UNDO: string, TOGGLE_MARK_DONE: string}}\n */\nconst TOGGLE_TYPES = {\n TOGGLE_MARK_DONE: 'manual:mark-done',\n TOGGLE_UNDO: 'manual:undo',\n};\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n/**\n * Registers the click event listener for the manual completion toggle button.\n */\nexport const init = () => {\n if (registered) {\n return;\n }\n document.addEventListener('click', (e) => {\n const toggleButton = e.target.closest(SELECTORS.MANUAL_TOGGLE);\n if (toggleButton) {\n e.preventDefault();\n toggleManualCompletionState(toggleButton).catch(Notification.exception);\n }\n });\n registered = true;\n};\n\n/**\n * Toggles the manual completion state of the module for the given user.\n *\n * @param {HTMLElement} toggleButton\n * @returns {Promise}\n */\nconst toggleManualCompletionState = async(toggleButton) => {\n // Make a copy of the original content of the button.\n const originalInnerHtml = toggleButton.innerHTML;\n\n // Disable the button to prevent double clicks.\n toggleButton.setAttribute('disabled', 'disabled');\n\n // Get button data.\n const toggleType = toggleButton.getAttribute('data-toggletype');\n const cmid = toggleButton.getAttribute('data-cmid');\n const activityname = toggleButton.getAttribute('data-activityname');\n // Get the target completion state.\n const completed = toggleType === TOGGLE_TYPES.TOGGLE_MARK_DONE;\n\n // Replace the button contents with the loading icon.\n const loadingHtml = await Templates.render('core/loading', {});\n await Templates.replaceNodeContents(toggleButton, loadingHtml, '');\n\n try {\n // Call the webservice to update the manual completion status.\n await toggleManualCompletion(cmid, completed);\n\n // All good so far. Refresh the manual completion button to reflect its new state by re-rendering the template.\n const templateContext = {\n cmid: cmid,\n activityname: activityname,\n overallcomplete: completed,\n overallincomplete: !completed,\n istrackeduser: true, // We know that we're tracking completion for this user given the presence of this button.\n };\n const renderObject = await Templates.renderForPromise('core_course/completion_manual', templateContext);\n\n // Replace the toggle button with the newly loaded template.\n const replacedNode = await Templates.replaceNode(toggleButton, renderObject.html, renderObject.js);\n const newToggleButton = replacedNode.pop();\n\n // Build manualCompletionToggled custom event.\n const toggledEvent = new CustomEvent(CourseEvents.manualCompletionToggled, {\n bubbles: true,\n detail: {\n cmid,\n activityname,\n completed,\n }\n });\n // Dispatch the manualCompletionToggled custom event.\n newToggleButton.dispatchEvent(toggledEvent);\n\n } catch (exception) {\n // In case of an error, revert the original state and appearance of the button.\n toggleButton.removeAttribute('disabled');\n toggleButton.innerHTML = originalInnerHtml;\n\n // Show the exception.\n Notification.exception(exception);\n }\n};\n"],"file":"manual_completion_toggle.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/manual_completion_toggle.js"],"names":["SELECTORS","MANUAL_TOGGLE","TOGGLE_TYPES","TOGGLE_MARK_DONE","TOGGLE_UNDO","registered","init","document","addEventListener","e","toggleButton","target","closest","preventDefault","toggleManualCompletionState","catch","Notification","exception","originalInnerHtml","innerHTML","setAttribute","toggleType","getAttribute","cmid","activityname","completed","Templates","render","loadingHtml","replaceNodeContents","templateContext","overallcomplete","overallincomplete","istrackeduser","renderForPromise","renderObject","replaceNode","html","js","replacedNode","newToggleButton","pop","withAvailability","toggledEvent","CustomEvent","CourseEvents","manualCompletionToggled","bubbles","detail","dispatchEvent","removeAttribute"],"mappings":"ihBAyBA,OACA,OAEA,O,25BAOMA,CAAAA,CAAS,CAAG,CACdC,aAAa,CAAE,8CADD,C,CASZC,CAAY,CAAG,CACjBC,gBAAgB,CAAE,kBADD,CAEjBC,WAAW,CAAE,aAFI,C,CAUjBC,CAAU,G,CAKDC,CAAI,CAAG,UAAM,CACtB,GAAID,CAAJ,CAAgB,CACZ,MACH,CACDE,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAACC,CAAD,CAAO,CACtC,GAAMC,CAAAA,CAAY,CAAGD,CAAC,CAACE,MAAF,CAASC,OAAT,CAAiBZ,CAAS,CAACC,aAA3B,CAArB,CACA,GAAIS,CAAJ,CAAkB,CACdD,CAAC,CAACI,cAAF,GACAC,CAA2B,CAACJ,CAAD,CAA3B,CAA0CK,KAA1C,CAAgDC,UAAaC,SAA7D,CACH,CACJ,CAND,EAOAZ,CAAU,GACb,C,UAQD,GAAMS,CAAAA,CAA2B,4CAAG,WAAMJ,CAAN,+GAE1BQ,CAF0B,CAENR,CAAY,CAACS,SAFP,CAKhCT,CAAY,CAACU,YAAb,CAA0B,UAA1B,CAAsC,UAAtC,EAGMC,CAR0B,CAQbX,CAAY,CAACY,YAAb,CAA0B,iBAA1B,CARa,CAS1BC,CAT0B,CASnBb,CAAY,CAACY,YAAb,CAA0B,WAA1B,CATmB,CAU1BE,CAV0B,CAUXd,CAAY,CAACY,YAAb,CAA0B,mBAA1B,CAVW,CAY1BG,CAZ0B,CAYdJ,CAAU,GAAKnB,CAAY,CAACC,gBAZd,gBAeNuB,WAAUC,MAAV,CAAiB,cAAjB,CAAiC,EAAjC,CAfM,QAe1BC,CAf0B,wBAgB1BF,WAAUG,mBAAV,CAA8BnB,CAA9B,CAA4CkB,CAA5C,CAAyD,EAAzD,CAhB0B,mCAoBtB,6BAAuBL,CAAvB,CAA6BE,CAA7B,CApBsB,SAuBtBK,CAvBsB,CAuBJ,CACpBP,IAAI,CAAEA,CADc,CAEpBC,YAAY,CAAEA,CAFM,CAGpBO,eAAe,CAAEN,CAHG,CAIpBO,iBAAiB,CAAE,CAACP,CAJA,CAKpBQ,aAAa,GALO,CAvBI,iBA8BDP,WAAUQ,gBAAV,CAA2B,+BAA3B,CAA4DJ,CAA5D,CA9BC,SA8BtBK,CA9BsB,wBAiCDT,WAAUU,WAAV,CAAsB1B,CAAtB,CAAoCyB,CAAY,CAACE,IAAjD,CAAuDF,CAAY,CAACG,EAApE,CAjCC,SAiCtBC,CAjCsB,QAkCtBC,CAlCsB,CAkCJD,CAAY,CAACE,GAAb,EAlCI,CAqCtBC,CArCsB,CAqCHhC,CAAY,CAACY,YAAb,CAA0B,uBAA1B,CArCG,CAsCtBqB,CAtCsB,CAsCP,GAAIC,CAAAA,WAAJ,CAAgBC,CAAY,CAACC,uBAA7B,CAAsD,CACvEC,OAAO,GADgE,CAEvEC,MAAM,CAAE,CACJzB,IAAI,CAAJA,CADI,CAEJC,YAAY,CAAZA,CAFI,CAGJC,SAAS,CAATA,CAHI,CAIJiB,gBAAgB,CAAhBA,CAJI,CAF+D,CAAtD,CAtCO,CAgD5BF,CAAe,CAACS,aAAhB,CAA8BN,CAA9B,EAhD4B,sDAoD5BjC,CAAY,CAACwC,eAAb,CAA6B,UAA7B,EACAxC,CAAY,CAACS,SAAb,CAAyBD,CAAzB,CAGAF,UAAaC,SAAb,OAxD4B,wDAAH,uD","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Provides the functionality for toggling the manual completion state of a course module through\n * the manual completion button.\n *\n * @module core_course/manual_completion_toggle\n * @package core_course\n * @copyright 2021 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport {toggleManualCompletion} from 'core_course/repository';\nimport * as CourseEvents from 'core_course/events';\n\n/**\n * Selectors in the manual completion template.\n *\n * @type {{MANUAL_TOGGLE: string}}\n */\nconst SELECTORS = {\n MANUAL_TOGGLE: 'button[data-action=toggle-manual-completion]',\n};\n\n/**\n * Toggle type values for the data-toggletype attribute in the core_course/completion_manual template.\n *\n * @type {{TOGGLE_UNDO: string, TOGGLE_MARK_DONE: string}}\n */\nconst TOGGLE_TYPES = {\n TOGGLE_MARK_DONE: 'manual:mark-done',\n TOGGLE_UNDO: 'manual:undo',\n};\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n/**\n * Registers the click event listener for the manual completion toggle button.\n */\nexport const init = () => {\n if (registered) {\n return;\n }\n document.addEventListener('click', (e) => {\n const toggleButton = e.target.closest(SELECTORS.MANUAL_TOGGLE);\n if (toggleButton) {\n e.preventDefault();\n toggleManualCompletionState(toggleButton).catch(Notification.exception);\n }\n });\n registered = true;\n};\n\n/**\n * Toggles the manual completion state of the module for the given user.\n *\n * @param {HTMLElement} toggleButton\n * @returns {Promise}\n */\nconst toggleManualCompletionState = async(toggleButton) => {\n // Make a copy of the original content of the button.\n const originalInnerHtml = toggleButton.innerHTML;\n\n // Disable the button to prevent double clicks.\n toggleButton.setAttribute('disabled', 'disabled');\n\n // Get button data.\n const toggleType = toggleButton.getAttribute('data-toggletype');\n const cmid = toggleButton.getAttribute('data-cmid');\n const activityname = toggleButton.getAttribute('data-activityname');\n // Get the target completion state.\n const completed = toggleType === TOGGLE_TYPES.TOGGLE_MARK_DONE;\n\n // Replace the button contents with the loading icon.\n const loadingHtml = await Templates.render('core/loading', {});\n await Templates.replaceNodeContents(toggleButton, loadingHtml, '');\n\n try {\n // Call the webservice to update the manual completion status.\n await toggleManualCompletion(cmid, completed);\n\n // All good so far. Refresh the manual completion button to reflect its new state by re-rendering the template.\n const templateContext = {\n cmid: cmid,\n activityname: activityname,\n overallcomplete: completed,\n overallincomplete: !completed,\n istrackeduser: true, // We know that we're tracking completion for this user given the presence of this button.\n };\n const renderObject = await Templates.renderForPromise('core_course/completion_manual', templateContext);\n\n // Replace the toggle button with the newly loaded template.\n const replacedNode = await Templates.replaceNode(toggleButton, renderObject.html, renderObject.js);\n const newToggleButton = replacedNode.pop();\n\n // Build manualCompletionToggled custom event.\n const withAvailability = toggleButton.getAttribute('data-withavailability');\n const toggledEvent = new CustomEvent(CourseEvents.manualCompletionToggled, {\n bubbles: true,\n detail: {\n cmid,\n activityname,\n completed,\n withAvailability,\n }\n });\n // Dispatch the manualCompletionToggled custom event.\n newToggleButton.dispatchEvent(toggledEvent);\n\n } catch (exception) {\n // In case of an error, revert the original state and appearance of the button.\n toggleButton.removeAttribute('disabled');\n toggleButton.innerHTML = originalInnerHtml;\n\n // Show the exception.\n Notification.exception(exception);\n }\n};\n"],"file":"manual_completion_toggle.min.js"} \ No newline at end of file diff --git a/course/amd/build/view.min.js b/course/amd/build/view.min.js new file mode 100644 index 00000000000..4add734de96 --- /dev/null +++ b/course/amd/build/view.min.js @@ -0,0 +1,2 @@ +function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_course/view",["exports","core_course/events"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=d(b);function c(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;c=function(){return a};return a}function d(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=c();if(b&&b.has(a)){return b.get(a)}var d={},e=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var g=e?Object.getOwnPropertyDescriptor(a,f):null;if(g&&(g.get||g.set)){Object.defineProperty(d,f,g)}else{d[f]=a[f]}}}d.default=a;if(b){b.set(a,d)}return d}var e=!1,f=function(){if(e){return}document.addEventListener(b.manualCompletionToggled,function(a){var b=parseInt(a.detail.withAvailability);if(b){window.location.reload()}});e=!0};a.init=f}); +//# sourceMappingURL=view.min.js.map diff --git a/course/amd/build/view.min.js.map b/course/amd/build/view.min.js.map new file mode 100644 index 00000000000..49e5e62c599 --- /dev/null +++ b/course/amd/build/view.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/view.js"],"names":["registered","init","document","addEventListener","CourseEvents","manualCompletionToggled","e","withAvailability","parseInt","detail","window","location","reload"],"mappings":"ybAwBA,O,yiBAOIA,CAAAA,CAAU,G,CAKDC,CAAI,CAAG,UAAM,CACtB,GAAID,CAAJ,CAAgB,CACZ,MACH,CAEDE,QAAQ,CAACC,gBAAT,CAA0BC,CAAY,CAACC,uBAAvC,CAAgE,SAACC,CAAD,CAAO,CACnE,GAAMC,CAAAA,CAAgB,CAAGC,QAAQ,CAACF,CAAC,CAACG,MAAF,CAASF,gBAAV,CAAjC,CACA,GAAIA,CAAJ,CAAsB,CAElBG,MAAM,CAACC,QAAP,CAAgBC,MAAhB,EACH,CACJ,CAND,EAOAZ,CAAU,GACb,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JS module for the course homepage.\n *\n * @module core_course/view\n * @package core_course\n * @copyright 2021 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as CourseEvents from 'core_course/events';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n/**\n * Function to intialise and register event listeners for this module.\n */\nexport const init = () => {\n if (registered) {\n return;\n }\n // Listen for toggled manual completion states of activities.\n document.addEventListener(CourseEvents.manualCompletionToggled, (e) => {\n const withAvailability = parseInt(e.detail.withAvailability);\n if (withAvailability) {\n // Reload the page when the toggled manual completion button has availability conditions linked to it.\n window.location.reload();\n }\n });\n registered = true;\n};\n"],"file":"view.min.js"} \ No newline at end of file diff --git a/course/amd/src/manual_completion_toggle.js b/course/amd/src/manual_completion_toggle.js index 4429bee2d75..20506de5fcd 100644 --- a/course/amd/src/manual_completion_toggle.js +++ b/course/amd/src/manual_completion_toggle.js @@ -114,12 +114,14 @@ const toggleManualCompletionState = async(toggleButton) => { const newToggleButton = replacedNode.pop(); // Build manualCompletionToggled custom event. + const withAvailability = toggleButton.getAttribute('data-withavailability'); const toggledEvent = new CustomEvent(CourseEvents.manualCompletionToggled, { bubbles: true, detail: { cmid, activityname, completed, + withAvailability, } }); // Dispatch the manualCompletionToggled custom event. diff --git a/course/amd/src/view.js b/course/amd/src/view.js new file mode 100644 index 00000000000..721067ddc60 --- /dev/null +++ b/course/amd/src/view.js @@ -0,0 +1,50 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * JS module for the course homepage. + * + * @module core_course/view + * @package core_course + * @copyright 2021 Jun Pataleta + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import * as CourseEvents from 'core_course/events'; + +/** + * Whether the event listener has already been registered for this module. + * + * @type {boolean} + */ +let registered = false; + +/** + * Function to intialise and register event listeners for this module. + */ +export const init = () => { + if (registered) { + return; + } + // Listen for toggled manual completion states of activities. + document.addEventListener(CourseEvents.manualCompletionToggled, (e) => { + const withAvailability = parseInt(e.detail.withAvailability); + if (withAvailability) { + // Reload the page when the toggled manual completion button has availability conditions linked to it. + window.location.reload(); + } + }); + registered = true; +}; diff --git a/course/classes/output/activity_information.php b/course/classes/output/activity_information.php index 2d281c31b22..6847ca7b65a 100644 --- a/course/classes/output/activity_information.php +++ b/course/classes/output/activity_information.php @@ -29,6 +29,7 @@ use cm_info; use completion_info; use context; use core\activity_dates; +use core_availability\info; use core_completion\cm_completion_details; use core_user; use core_user\fields; @@ -90,6 +91,8 @@ class activity_information implements renderable, templatable { * @return stdClass */ protected function build_completion_data(): stdClass { + global $CFG; + $data = new stdClass(); $data->hascompletion = $this->cmcompletion->has_completion(); @@ -124,6 +127,16 @@ class activity_information implements renderable, templatable { $data->accessibledescription = get_string($setbylangkey, 'course', $setbydata); } + // Whether the completion of this activity controls the availability of other activities/sections in the course. + $data->withavailability = false; + $course = $this->cminfo->get_course(); + // An activity with manual completion tracking which is used to enable access to other activities/sections in + // the course needs to refresh the page after having its completion state toggled. This withavailability flag will enable + // this functionality on the course homepage. Otherwise, the completion toggling will just happen normally via ajax. + if ($this->cmcompletion->has_completion() && !$this->cmcompletion->is_automatic()) { + $data->withavailability = !empty($CFG->enableavailability) && info::completion_value_used($course, $this->cminfo->id); + } + // Build automatic completion details. $details = []; foreach ($this->cmcompletion->get_details() as $key => $detail) { diff --git a/course/templates/completion_manual.mustache b/course/templates/completion_manual.mustache index c8b38b1ac37..fd1f57b28e9 100644 --- a/course/templates/completion_manual.mustache +++ b/course/templates/completion_manual.mustache @@ -28,7 +28,7 @@ }} {{#istrackeduser}} {{#overallcomplete}} -