diff --git a/.upgradenotes/MDL-83872-2025021312020327.yml b/.upgradenotes/MDL-83872-2025021312020327.yml new file mode 100644 index 00000000000..ce0d1f9bcd3 --- /dev/null +++ b/.upgradenotes/MDL-83872-2025021312020327.yml @@ -0,0 +1,10 @@ +issueNumber: MDL-83872 +notes: + core_course: + - message: >- + Now the core_courseformat\local\content\cm\completion output is more + reusable. All the HTML has been moved to its own mustache file, + and the output class has a new set_smallbutton method to decide wether + to rendered it as a small button (like in the course page) or as a + normal one (for other types of pages). + type: improved diff --git a/course/amd/build/manual_completion_toggle.min.js b/course/amd/build/manual_completion_toggle.min.js index 18c7cdd69a9..4a1e5f346b6 100644 --- a/course/amd/build/manual_completion_toggle.min.js +++ b/course/amd/build/manual_completion_toggle.min.js @@ -6,6 +6,6 @@ define("core_course/manual_completion_toggle",["exports","core/templates","core/ * @module core_course/manual_completion_toggle * @copyright 2021 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending);const SELECTORS_MANUAL_TOGGLE="button[data-action=toggle-manual-completion]",TOGGLE_TYPES_TOGGLE_MARK_DONE="manual:mark-done";let registered=!1;_exports.init=()=>{registered||(document.addEventListener("click",(e=>{const toggleButton=e.target.closest(SELECTORS_MANUAL_TOGGLE);toggleButton&&(e.preventDefault(),toggleManualCompletionState(toggleButton).catch(_notification.default.exception))})),registered=!0)};const toggleManualCompletionState=async toggleButton=>{const pendingPromise=new _pending.default("core_course:toggleManualCompletionState"),originalInnerHtml=toggleButton.innerHTML;toggleButton.setAttribute("disabled","disabled");const toggleType=toggleButton.getAttribute("data-toggletype"),cmid=toggleButton.getAttribute("data-cmid"),activityname=toggleButton.getAttribute("data-activityname"),completed=toggleType===TOGGLE_TYPES_TOGGLE_MARK_DONE;_templates.default.renderForPromise("core/loading",{}).then((loadingHtml=>{_templates.default.replaceNodeContents(toggleButton,loadingHtml,"")})).catch((()=>{}));try{await(0,_repository.toggleManualCompletion)(cmid,completed);const templateContext={cmid:cmid,activityname:activityname,overallcomplete:completed,overallincomplete:!completed,istrackeduser:!0},renderObject=await _templates.default.renderForPromise("core_course/completion_manual",templateContext),newToggleButton=(await _templates.default.replaceNode(toggleButton,renderObject.html,renderObject.js)).pop(),withAvailability=toggleButton.getAttribute("data-withavailability"),toggledEvent=new CustomEvent(CourseEvents.manualCompletionToggled,{bubbles:!0,detail:{cmid:cmid,activityname:activityname,completed:completed,withAvailability:withAvailability}});newToggleButton.dispatchEvent(toggledEvent)}catch(exception){toggleButton.removeAttribute("disabled"),toggleButton.innerHTML=originalInnerHtml,_notification.default.exception(exception)}pendingPromise.resolve()}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending);const SELECTORS_MANUAL_TOGGLE="button[data-action=toggle-manual-completion]",TOGGLE_TYPES_TOGGLE_MARK_DONE="manual:mark-done";let registered=!1;_exports.init=()=>{registered||(document.addEventListener("click",(e=>{const toggleButton=e.target.closest(SELECTORS_MANUAL_TOGGLE);toggleButton&&(e.preventDefault(),toggleManualCompletionState(toggleButton).catch(_notification.default.exception))})),registered=!0)};const toggleManualCompletionState=async toggleButton=>{const pendingPromise=new _pending.default("core_course:toggleManualCompletionState"),originalInnerHtml=toggleButton.innerHTML;toggleButton.setAttribute("disabled","disabled");const toggleType=toggleButton.getAttribute("data-toggletype"),cmid=toggleButton.getAttribute("data-cmid"),activityname=toggleButton.getAttribute("data-activityname"),completed=toggleType===TOGGLE_TYPES_TOGGLE_MARK_DONE;_templates.default.renderForPromise("core/loading",{}).then((loadingHtml=>{_templates.default.replaceNodeContents(toggleButton,loadingHtml,"")})).catch((()=>{}));try{await(0,_repository.toggleManualCompletion)(cmid,completed);const templateContext={cmid:cmid,activityname:activityname,overallcomplete:completed,overallincomplete:!completed,istrackeduser:!0,normalbutton:!toggleButton.classList.contains("btn-sm")},renderObject=await _templates.default.renderForPromise("core_course/completion_manual",templateContext),newToggleButton=(await _templates.default.replaceNode(toggleButton,renderObject.html,renderObject.js)).pop(),withAvailability=toggleButton.getAttribute("data-withavailability"),toggledEvent=new CustomEvent(CourseEvents.manualCompletionToggled,{bubbles:!0,detail:{cmid:cmid,activityname:activityname,completed:completed,withAvailability:withAvailability}});newToggleButton.dispatchEvent(toggledEvent)}catch(exception){toggleButton.removeAttribute("disabled"),toggleButton.innerHTML=originalInnerHtml,_notification.default.exception(exception)}pendingPromise.resolve()}})); //# sourceMappingURL=manual_completion_toggle.min.js.map \ No newline at end of file diff --git a/course/amd/build/manual_completion_toggle.min.js.map b/course/amd/build/manual_completion_toggle.min.js.map index 7b22322106c..25a64a0a659 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,"file":"manual_completion_toggle.min.js","sources":["../src/manual_completion_toggle.js"],"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 * @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';\nimport Pending from 'core/pending';\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 const pendingPromise = new Pending('core_course:toggleManualCompletionState');\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 Templates.renderForPromise('core/loading', {})\n .then((loadingHtml) => {\n Templates.replaceNodeContents(toggleButton, loadingHtml, '');\n return;\n }).catch(() => {});\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 pendingPromise.resolve();\n};\n"],"names":["SELECTORS","TOGGLE_TYPES","registered","document","addEventListener","e","toggleButton","target","closest","preventDefault","toggleManualCompletionState","catch","Notification","exception","async","pendingPromise","Pending","originalInnerHtml","innerHTML","setAttribute","toggleType","getAttribute","cmid","activityname","completed","renderForPromise","then","loadingHtml","replaceNodeContents","templateContext","overallcomplete","overallincomplete","istrackeduser","renderObject","Templates","newToggleButton","replaceNode","html","js","pop","withAvailability","toggledEvent","CustomEvent","CourseEvents","manualCompletionToggled","bubbles","detail","dispatchEvent","removeAttribute","resolve"],"mappings":";;;;;;;;44BAmCMA,wBACa,+CAQbC,8BACgB,uBASlBC,YAAa,gBAKG,KACZA,aAGJC,SAASC,iBAAiB,SAAUC,UAC1BC,aAAeD,EAAEE,OAAOC,QAAQR,yBAClCM,eACAD,EAAEI,iBACFC,4BAA4BJ,cAAcK,MAAMC,sBAAaC,eAGrEX,YAAa,UASXQ,4BAA8BI,MAAAA,qBAC1BC,eAAiB,IAAIC,iBAAQ,2CAE7BC,kBAAoBX,aAAaY,UAGvCZ,aAAaa,aAAa,WAAY,kBAGhCC,WAAad,aAAae,aAAa,mBACvCC,KAAOhB,aAAae,aAAa,aACjCE,aAAejB,aAAae,aAAa,qBAEzCG,UAAYJ,aAAenB,iDAGvBwB,iBAAiB,eAAgB,IAC1CC,MAAMC,iCACOC,oBAAoBtB,aAAcqB,YAAa,OAE1DhB,OAAM,mBAIC,sCAAuBW,KAAME,iBAG7BK,gBAAkB,CACpBP,KAAMA,KACNC,aAAcA,aACdO,gBAAiBN,UACjBO,mBAAoBP,UACpBQ,eAAe,GAEbC,mBAAqBC,mBAAUT,iBAAiB,gCAAiCI,iBAIjFM,uBADqBD,mBAAUE,YAAY9B,aAAc2B,aAAaI,KAAMJ,aAAaK,KAC1DC,MAG/BC,iBAAmBlC,aAAae,aAAa,yBAC7CoB,aAAe,IAAIC,YAAYC,aAAaC,wBAAyB,CACvEC,SAAS,EACTC,OAAQ,CACJxB,KAAAA,KACAC,aAAAA,aACAC,UAAAA,UACAgB,iBAAAA,oBAIRL,gBAAgBY,cAAcN,cAEhC,MAAO5B,WAELP,aAAa0C,gBAAgB,YAC7B1C,aAAaY,UAAYD,wCAGZJ,UAAUA,WAE3BE,eAAekC"} \ No newline at end of file +{"version":3,"file":"manual_completion_toggle.min.js","sources":["../src/manual_completion_toggle.js"],"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 * @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';\nimport Pending from 'core/pending';\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 const pendingPromise = new Pending('core_course:toggleManualCompletionState');\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 Templates.renderForPromise('core/loading', {})\n .then((loadingHtml) => {\n Templates.replaceNodeContents(toggleButton, loadingHtml, '');\n return;\n }).catch(() => {});\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 normalbutton: !toggleButton.classList.contains('btn-sm'),\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 pendingPromise.resolve();\n};\n"],"names":["SELECTORS","TOGGLE_TYPES","registered","document","addEventListener","e","toggleButton","target","closest","preventDefault","toggleManualCompletionState","catch","Notification","exception","async","pendingPromise","Pending","originalInnerHtml","innerHTML","setAttribute","toggleType","getAttribute","cmid","activityname","completed","renderForPromise","then","loadingHtml","replaceNodeContents","templateContext","overallcomplete","overallincomplete","istrackeduser","normalbutton","classList","contains","renderObject","Templates","newToggleButton","replaceNode","html","js","pop","withAvailability","toggledEvent","CustomEvent","CourseEvents","manualCompletionToggled","bubbles","detail","dispatchEvent","removeAttribute","resolve"],"mappings":";;;;;;;;44BAmCMA,wBACa,+CAQbC,8BACgB,uBASlBC,YAAa,gBAKG,KACZA,aAGJC,SAASC,iBAAiB,SAAUC,UAC1BC,aAAeD,EAAEE,OAAOC,QAAQR,yBAClCM,eACAD,EAAEI,iBACFC,4BAA4BJ,cAAcK,MAAMC,sBAAaC,eAGrEX,YAAa,UASXQ,4BAA8BI,MAAAA,qBAC1BC,eAAiB,IAAIC,iBAAQ,2CAE7BC,kBAAoBX,aAAaY,UAGvCZ,aAAaa,aAAa,WAAY,kBAGhCC,WAAad,aAAae,aAAa,mBACvCC,KAAOhB,aAAae,aAAa,aACjCE,aAAejB,aAAae,aAAa,qBAEzCG,UAAYJ,aAAenB,iDAGvBwB,iBAAiB,eAAgB,IAC1CC,MAAMC,iCACOC,oBAAoBtB,aAAcqB,YAAa,OAE1DhB,OAAM,mBAIC,sCAAuBW,KAAME,iBAG7BK,gBAAkB,CACpBP,KAAMA,KACNC,aAAcA,aACdO,gBAAiBN,UACjBO,mBAAoBP,UACpBQ,eAAe,EACfC,cAAe3B,aAAa4B,UAAUC,SAAS,WAE7CC,mBAAqBC,mBAAUZ,iBAAiB,gCAAiCI,iBAIjFS,uBADqBD,mBAAUE,YAAYjC,aAAc8B,aAAaI,KAAMJ,aAAaK,KAC1DC,MAG/BC,iBAAmBrC,aAAae,aAAa,yBAC7CuB,aAAe,IAAIC,YAAYC,aAAaC,wBAAyB,CACvEC,SAAS,EACTC,OAAQ,CACJ3B,KAAAA,KACAC,aAAAA,aACAC,UAAAA,UACAmB,iBAAAA,oBAIRL,gBAAgBY,cAAcN,cAEhC,MAAO/B,WAELP,aAAa6C,gBAAgB,YAC7B7C,aAAaY,UAAYD,wCAGZJ,UAAUA,WAE3BE,eAAeqC"} \ 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 12200f7a878..ac32ab3280a 100644 --- a/course/amd/src/manual_completion_toggle.js +++ b/course/amd/src/manual_completion_toggle.js @@ -110,6 +110,7 @@ const toggleManualCompletionState = async(toggleButton) => { overallcomplete: completed, overallincomplete: !completed, istrackeduser: true, // We know that we're tracking completion for this user given the presence of this button. + normalbutton: !toggleButton.classList.contains('btn-sm'), }; const renderObject = await Templates.renderForPromise('core_course/completion_manual', templateContext); diff --git a/course/classes/output/activity_completion.php b/course/classes/output/activity_completion.php index b2475902edc..4022841469a 100644 --- a/course/classes/output/activity_completion.php +++ b/course/classes/output/activity_completion.php @@ -42,8 +42,12 @@ class activity_completion implements renderable, templatable { * @param cm_completion_details $cmcompletion The course module information. */ public function __construct( + /** @var cm_info $cminfo the activity cm_info. */ protected cm_info $cminfo, - protected cm_completion_details $cmcompletion + /** @var cm_completion_details $cmcompletion the activity completion details. */ + protected cm_completion_details $cmcompletion, + /** @var bool $smallbutton if the button is rendered small (like in course page). */ + protected bool $smallbutton = true, ) { } @@ -84,6 +88,8 @@ class activity_completion implements renderable, templatable { 'overrideby' => $overrideby, 'completiondetails' => $this->get_completion_details($overrideby), 'accessibledescription' => $this->get_accessible_description($overrideby, $overallcompletion), + // For backward compatibility, the template uses small button by default when not set normal size. + 'normalbutton' => !$this->smallbutton, ]; } diff --git a/course/format/classes/output/local/content/cm/completion.php b/course/format/classes/output/local/content/cm/completion.php index ce82cb6082b..32252e865d3 100644 --- a/course/format/classes/output/local/content/cm/completion.php +++ b/course/format/classes/output/local/content/cm/completion.php @@ -38,6 +38,9 @@ class completion implements named_templatable, renderable { use courseformat_named_templatable; + /** @var bool $smallbutton if the button is rendered small (like in course page). */ + private bool $smallbutton = true; + /** * Constructor. * @@ -52,6 +55,19 @@ class completion implements named_templatable, renderable { ) { } + /** + * Set if the button is rendered small. + * + * By default, the button is rendered small like in the course page. However, + * in other pages like the activities overview, the button is rendered like + * a regular button. + * + * @param bool $smallbutton if the button is rendered small (like in course page). + */ + public function set_smallbutton(bool $smallbutton): void { + $this->smallbutton = $smallbutton; + } + /** * Export this data so it can be used as the context for a mustache template. * @@ -76,7 +92,7 @@ class completion implements named_templatable, renderable { return null; } - $completion = new activity_completion($this->mod, $completiondetails); + $completion = new activity_completion($this->mod, $completiondetails, $this->smallbutton); $completiondata = $completion->export_for_template($output); if ($completiondata->isautomatic || ($completiondata->ismanual && !$completiondata->istrackeduser)) { @@ -108,6 +124,7 @@ class completion implements named_templatable, renderable { $buttoncontent = get_string('completionmenuitem', 'completion'); $buttonclass = ''; if ($completioninfo->istrackeduser) { + $buttonclass = 'btn-outline-secondary'; $buttoncontent = get_string('todo', 'completion'); if ($completioninfo->overallcomplete) { $buttoncontent = $output->pix_icon('i/checked', '') . " " . get_string('completion_manual:done', 'core_course'); @@ -115,9 +132,11 @@ class completion implements named_templatable, renderable { } } + $buttonclass .= $this->smallbutton ? ' btn-sm' : ''; + $completiondialog = new dropdown_dialog($buttoncontent, $dialogcontent, [ 'classes' => 'completion-dropdown', - 'buttonclasses' => 'btn btn-sm dropdown-toggle icon-no-margin ' . $buttonclass, + 'buttonclasses' => 'btn dropdown-toggle icon-no-margin ' . $buttonclass, 'dropdownposition' => dropdown_dialog::POSITION['end'], ]); diff --git a/course/format/templates/local/content/cm/activity_info.mustache b/course/format/templates/local/content/cm/activity_info.mustache index e355cc6bd56..3733c23e7fd 100644 --- a/course/format/templates/local/content/cm/activity_info.mustache +++ b/course/format/templates/local/content/cm/activity_info.mustache @@ -36,7 +36,7 @@ "isautomatic": true, "showmanualcompletion": true, "completiondetails": [ - { + { "statuscomplete": 1, "description": "Viewed" }, @@ -48,23 +48,5 @@ } }}
- {{#uservisible}} -
- {{! Completion dropdown dialog }} - {{#completiondialog}} -
- {{> core/local/dropdown/dialog }} -
- {{/completiondialog}} - - {{! Manual completion button }} - {{#showmanualcompletion}} - {{#istrackeduser}} - {{$ core_course/completion_manual }} - {{> core_course/completion_manual }} - {{/ core_course/completion_manual }} - {{/istrackeduser}} - {{/showmanualcompletion}} -
- {{/uservisible}} + {{> core_courseformat/local/content/cm/completion }}
diff --git a/course/format/templates/local/content/cm/completion.mustache b/course/format/templates/local/content/cm/completion.mustache new file mode 100644 index 00000000000..ef53768fedb --- /dev/null +++ b/course/format/templates/local/content/cm/completion.mustache @@ -0,0 +1,69 @@ +{{! + 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 . +}} +{{! + @template core_courseformat/local/content/cm/completion + + TODO describe template completion + + Example context (json): + { + "cmid": 123, + "activityname": "Course announcements", + "hascompletion": true, + "uservisible": true, + "completiondialog": { + "buttoncontent": "To do", + "dialogcontent": "You must view this activity to complete it.", + "buttonclasses": "btn btn-sm btn-outline-secondary dropdown-toggle", + "classes": "completion-dropdown" + }, + "isautomatic": true, + "showmanualcompletion": true, + "completiondetails": [ + { + "statuscomplete": 1, + "description": "Viewed" + }, + { + "statusincomplete": 1, + "description": "Receive a grade" + } + ] + } +}} +{{#uservisible}} +
+ {{! Completion dropdown dialog }} + {{#completiondialog}} +
+ {{> core/local/dropdown/dialog }} +
+ {{/completiondialog}} + + {{! Manual completion button }} + {{#showmanualcompletion}} + {{#istrackeduser}} + {{$ core_course/completion_manual }} + {{> core_course/completion_manual }} + {{/ core_course/completion_manual }} + {{/istrackeduser}} + {{/showmanualcompletion}} +
+{{/uservisible}} diff --git a/course/templates/completion_manual.mustache b/course/templates/completion_manual.mustache index cfc78571d51..8cfe1b387f4 100644 --- a/course/templates/completion_manual.mustache +++ b/course/templates/completion_manual.mustache @@ -23,45 +23,63 @@ { "cmid": 0, "overallcomplete": true, - "overallincomplete": false + "overallincomplete": false, + "normalbutton": true } }} {{#istrackeduser}} {{#overallcomplete}} - {{/overallcomplete}} {{#overallincomplete}} - {{/overallincomplete}} {{/istrackeduser}} {{^istrackeduser}} - {{/istrackeduser}} {{#js}} require(['core_course/manual_completion_toggle'], toggle => { - toggle.init() + toggle.init(); }); {{/js}}