diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45dadf9..f91dc0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,8 +21,8 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.1', '8.2'] - moodle-branch: ['MOODLE_404_STABLE'] + php: ['8.1', '8.3'] + moodle-branch: ['MOODLE_405_STABLE'] database: [mariadb] steps: diff --git a/Changes.md b/Changes.md index cfd63f2..53be3a2 100644 --- a/Changes.md +++ b/Changes.md @@ -1,6 +1,12 @@ Version Information =================== +Version 405.0.1 - 24/10/2024 +---------------------------- +1. First Moodle 4.5 version. +2. Impact of MDL-81920 and MDL-81960. +3. Fix 'Add section actions JS failing'. + Version 404.1.2 - 24/09/2024 ---------------------------- 1. Fix 'Single page shows is_section_visible()' error. diff --git a/Readme.md b/Readme.md index 36d48bd..43c9fb9 100644 --- a/Readme.md +++ b/Readme.md @@ -3,29 +3,29 @@ Topic based course format with an individual 'toggle' for each topic except 0. This file contains general information about the format. If you're reading this as an educator then you can learn about the format -from the documentation on http://docs.moodle.org/403/en/Collapsed_Topics_course_format. If you get stuck and your Moodle support +from the documentation on http://docs.moodle.org/405/en/Collapsed_Topics_course_format. If you get stuck and your Moodle support are unable to help / not sure what to do, then please go to https://moodle.org/mod/forum/view.php?id=47. If you like the format, then please do spread the word to other educators. The main page for the format is https://moodle.org/plugins/format_topcoll. Required version of Moodle ========================== -This version works with Moodle 4.4 version 2024042200.00 (Build: 20240422) and above within the MOODLE_404_STABLE branch until the +This version works with Moodle 4.5 version 2024100700.00 (Build: 20241007) and above within the MOODLE_405_STABLE branch until the next release. -Please ensure that your hardware and software complies with 'Requirements' in '[Installing Moodle](https://docs.moodle.org/403/en/Installing_Moodle)'. +Please ensure that your hardware and software complies with 'Requirements' in '[Installing Moodle](https://docs.moodle.org/405/en/Installing_Moodle)'. Downloads and documentation =========================== The primary source for downloading this branch of the format is https://moodle.org/plugins/view.php?plugin=format_topcoll -with 'Select Moodle version:' set at 'Moodle 4.4'. +with 'Select Moodle version:' set at 'Moodle 4.5'. -The secondary source is a tagged version with the V404 prefix on https://github.com/gjb2048/moodle-format_topcoll/tags +The secondary source is a tagged version with the V405 prefix on https://github.com/gjbarnard/moodle-format_topcoll/tags -If you download from the development area - https://github.com/gjb2048/moodle-format_topcoll/ - consider that +If you download from the development area - https://github.com/gjbarnard/moodle-format_topcoll/ - consider that the code is unstable and not for use in production environments. This is because I develop the next version in stages and use GitHub as a means of backup. Therefore the code is not finished, subject to alteration and requires testing. -Documented on http://docs.moodle.org/403/en/Collapsed_Topics_course_format +Documented on http://docs.moodle.org/405/en/Collapsed_Topics_course_format Free software ============= @@ -39,7 +39,7 @@ FAQ - http://www.gnu.org/licenses/gpl-faq.html - is a good place to look. If you reuse any of the code then I kindly ask that you make reference to the format. If you make improvements or bug fixes then I would appreciate if you would send them back to me by forking from -https://github.com/gjb2048/moodle-format_topcoll and doing a 'Pull Request' so that the rest of the +https://github.com/gjbarnard/moodle-format_topcoll and doing a 'Pull Request' so that the rest of the Moodle community benefits. Support diff --git a/Support.md b/Support.md index 89eee2d..0c5ea7b 100644 --- a/Support.md +++ b/Support.md @@ -18,7 +18,7 @@ enhanced over the years in addition to coping with the API changes. If you'd like to sponsor, get support or fund improvements, then please do get in touch via: - gjbarnard | Gmail dt com address. -- GitHub | Please outline your issue / improvement on '[GitHub](https://github.com/gjb2048/moodle-format_topcoll/issues)'. +- GitHub | Please outline your issue / improvement on '[GitHub](https://github.com/gjbarnard/moodle-format_topcoll/issues)'. - @gjbarnard | '[X](https://twitter.com/gjbarnard)'. Sponsors @@ -36,7 +36,7 @@ to ask questions. Collapsed Topics can be obtained from: * [Moodle.org](https://moodle.org/plugins/view.php?plugin=format_topcoll). -* [GitHub](https://github.com/gjb2048/moodle-format_topcoll/releases). +* [GitHub](https://github.com/gjbarnard/moodle-format_topcoll/releases). You have all the rights granted to you by the GPLv3 license. If you are unsure about anything, then the FAQ - [GPL FAQ](https://www.gnu.org/licenses/gpl-faq.html) - is a good place to look. @@ -44,15 +44,15 @@ FAQ - [GPL FAQ](https://www.gnu.org/licenses/gpl-faq.html) - is a good place to If you reuse any of the code then I kindly ask that you make reference to the format. If you make improvements or bug fixes then I would appreciate if you would send them back to me by forking from -[GitHub](https://github.com/gjb2048/moodle-format_topcoll) and doing a 'Pull Request' so that the rest of the Moodle community +[GitHub](https://github.com/gjbarnard/moodle-format_topcoll) and doing a 'Pull Request' so that the rest of the Moodle community benefits. Required version of Moodle ========================== -This version works with Moodle 4.4 version 2024042200.00 (Build: 20240422) and above within the MOODLE_404_STABLE branch until the +This version works with Moodle 4.5 version 2024100700.00 (Build: 20241007) and above within the MOODLE_405_STABLE branch until the next release. -Please ensure that your hardware and software complies with 'Requirements' in '[Installing Moodle](https://docs.moodle.org/404/en/Installing_Moodle)'. +Please ensure that your hardware and software complies with 'Requirements' in '[Installing Moodle](https://docs.moodle.org/405/en/Installing_Moodle)'. Reporting issues ================ @@ -61,7 +61,7 @@ is essential that you are operating the required version of Moodle as stated abo that is out of its control. If you think you've discovered a genuine bug with the format then please look at the Moodle Course and course formats forum first to see if it -has already been repoted. Secondly, look at [GitHub](https://github.com/gjb2048/moodle-format_topcoll/issues), and thirdly [Moodle Tracker](https://tracker.moodle.org/issues/?jql=project+%3D+CONTRIB+AND+component+%3D+%22Course+format%3A+Topcoll%22). +has already been repoted. Secondly, look at [GitHub](https://github.com/gjbarnard/moodle-format_topcoll/issues), and thirdly [Moodle Tracker](https://tracker.moodle.org/issues/?jql=project+%3D+CONTRIB+AND+component+%3D+%22Course+format%3A+Topcoll%22). I operate a policy that I will fix all genuine issues in 'my' (not other developers of the format) code, when fully described and replicatable. diff --git a/amd/build/local/content.min.js b/amd/build/local/content.min.js index 75e516b..373f02c 100644 --- a/amd/build/local/content.min.js +++ b/amd/build/local/content.min.js @@ -1,4 +1,4 @@ -define("format_topcoll/local/content",["exports","core_courseformat/local/content","core_courseformat/courseeditor"],(function(_exports,_content,_courseeditor){var obj; +define("format_topcoll/local/content",["exports","core_courseformat/local/content","core_courseformat/courseeditor","format_topcoll/local/content/actions","core_course/events"],(function(_exports,_content,_courseeditor,_actions,CourseEvents){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Collapsed Topics Course index main component. * @@ -7,6 +7,6 @@ define("format_topcoll/local/content",["exports","core_courseformat/local/conten * @copyright 2022 G J Barnard based upon work done by: * @copyright 2020 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_content=(obj=_content)&&obj.__esModule?obj:{default:obj};class TopcollComponent extends _content.default{create(descriptor){super.create(descriptor)}static init(target,selectors,sectionReturn){return new TopcollComponent({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors,sectionReturn:sectionReturn})}getWatchers(){return this.reactive.sectionReturn=this.sectionReturn,this.reactive.supportComponents?[{watch:"cm.visible:updated",handler:this._reloadCm},{watch:"cm.stealth:updated",handler:this._reloadCm},{watch:"cm.sectionid:updated",handler:this._reloadCm},{watch:"cm.indent:updated",handler:this._reloadCm},{watch:"cm.groupmode:updated",handler:this._reloadCm},{watch:"cm.name:updated",handler:this._refreshCmName},{watch:"section.number:updated",handler:this._refreshSectionNumber},{watch:"section.title:updated",handler:this._refreshSectionTitle},{watch:"transaction:start",handler:this._startProcessing},{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionlist},{watch:"section.cmlist:updated",handler:this._refreshSectionCmlist},{watch:"section.visible:updated",handler:this._reloadSection},{watch:"state:updated",handler:this._indexContents}]:[]}_refreshCourseSectionlist(_ref){let{state:state}=_ref;if(null!==this.reactive.sectionReturn)return;const sectionlist=this.reactive.getExporter().listedSectionIds(state),listparent=this.getElement(this.selectors.COURSE_SECTIONLIST),createSection=this._createSectionItem.bind(this);listparent&&this._fixTopcollSectionOrder(listparent,sectionlist,this.selectors.SECTION,this.dettachedSections,createSection)}async _fixTopcollSectionOrder(container,neworder,selector,dettachedelements,createMethod){if(void 0===container)return;if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");let dndFakeActivity;for(container.classList.remove("hidden"),neworder.forEach(((itemid,index)=>{var _ref2,_this$getElement;let item=null!==(_ref2=null!==(_this$getElement=this.getElement(selector,itemid))&&void 0!==_this$getElement?_this$getElement:dettachedelements[itemid])&&void 0!==_ref2?_ref2:createMethod(container,itemid);if(!item)return;let itemno=this.getElement("#tcnoid-"+itemid);itemno&&(itemno.textContent=index+1);const currentitem=container.children[index];currentitem?currentitem!==item&&container.insertBefore(item,currentitem):container.append(item)}));container.children.length>neworder.length;){var _lastchild$classList;const lastchild=container.lastChild;var _lastchild$dataset$id,_lastchild$dataset;if(null!=lastchild&&null!==(_lastchild$classList=lastchild.classList)&&void 0!==_lastchild$classList&&_lastchild$classList.contains("dndupload-preview"))dndFakeActivity=lastchild;else dettachedelements[null!==(_lastchild$dataset$id=null==lastchild||null===(_lastchild$dataset=lastchild.dataset)||void 0===_lastchild$dataset?void 0:_lastchild$dataset.id)&&void 0!==_lastchild$dataset$id?_lastchild$dataset$id:0]=lastchild;container.removeChild(lastchild)}dndFakeActivity&&container.append(dndFakeActivity)}}return _exports.default=TopcollComponent,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_content=_interopRequireDefault(_content),_actions=_interopRequireDefault(_actions),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);class TopcollComponent extends _content.default{create(descriptor){super.create(descriptor)}static init(target,selectors,sectionReturn){return new TopcollComponent({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors,sectionReturn:sectionReturn})}stateReady(){this._indexContents(),this.reactive.supportComponents&&(this.reactive.isEditing&&new _actions.default(this),this.element.classList.add(this.classes.STATEDREADY)),this.addEventListener(this.element,CourseEvents.manualCompletionToggled,this._completionHandler),this.addEventListener(document,"scroll",this._scrollHandler)}getWatchers(){return this.reactive.sectionReturn=this.sectionReturn,this.reactive.supportComponents?[{watch:"cm.visible:updated",handler:this._reloadCm},{watch:"cm.stealth:updated",handler:this._reloadCm},{watch:"cm.sectionid:updated",handler:this._reloadCm},{watch:"cm.indent:updated",handler:this._reloadCm},{watch:"cm.groupmode:updated",handler:this._reloadCm},{watch:"cm.name:updated",handler:this._refreshCmName},{watch:"section.number:updated",handler:this._refreshSectionNumber},{watch:"section.title:updated",handler:this._refreshSectionTitle},{watch:"transaction:start",handler:this._startProcessing},{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionlist},{watch:"section.cmlist:updated",handler:this._refreshSectionCmlist},{watch:"section.visible:updated",handler:this._reloadSection},{watch:"state:updated",handler:this._indexContents}]:[]}_refreshCourseSectionlist(_ref){let{state:state}=_ref;if(null!==this.reactive.sectionReturn)return;const sectionlist=this.reactive.getExporter().listedSectionIds(state),listparent=this.getElement(this.selectors.COURSE_SECTIONLIST),createSection=this._createSectionItem.bind(this);listparent&&this._fixTopcollSectionOrder(listparent,sectionlist,this.selectors.SECTION,this.dettachedSections,createSection)}async _fixTopcollSectionOrder(container,neworder,selector,dettachedelements,createMethod){if(void 0===container)return;if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");let dndFakeActivity;for(container.classList.remove("hidden"),neworder.forEach(((itemid,index)=>{var _ref2,_this$getElement;let item=null!==(_ref2=null!==(_this$getElement=this.getElement(selector,itemid))&&void 0!==_this$getElement?_this$getElement:dettachedelements[itemid])&&void 0!==_ref2?_ref2:createMethod(container,itemid);if(!item)return;let itemno=this.getElement("#tcnoid-"+itemid);itemno&&(itemno.textContent=index+1);const currentitem=container.children[index];currentitem?currentitem!==item&&container.insertBefore(item,currentitem):container.append(item)}));container.children.length>neworder.length;){var _lastchild$classList;const lastchild=container.lastChild;var _lastchild$dataset$id,_lastchild$dataset;if(null!=lastchild&&null!==(_lastchild$classList=lastchild.classList)&&void 0!==_lastchild$classList&&_lastchild$classList.contains("dndupload-preview"))dndFakeActivity=lastchild;else dettachedelements[null!==(_lastchild$dataset$id=null==lastchild||null===(_lastchild$dataset=lastchild.dataset)||void 0===_lastchild$dataset?void 0:_lastchild$dataset.id)&&void 0!==_lastchild$dataset$id?_lastchild$dataset$id:0]=lastchild;container.removeChild(lastchild)}dndFakeActivity&&container.append(dndFakeActivity)}}return _exports.default=TopcollComponent,_exports.default})); //# sourceMappingURL=content.min.js.map \ No newline at end of file diff --git a/amd/build/local/content.min.js.map b/amd/build/local/content.min.js.map index 16442d2..7f97f2d 100644 --- a/amd/build/local/content.min.js.map +++ b/amd/build/local/content.min.js.map @@ -1 +1 @@ -{"version":3,"file":"content.min.js","sources":["../../src/local/content.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 * Collapsed Topics Course index main component.\n *\n * @module format_topcoll/local/content\n * @class format_topcoll/local/content\n * @copyright 2022 G J Barnard based upon work done by:\n * @copyright 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Component from 'core_courseformat/local/content';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\n\nexport default class TopcollComponent extends Component {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor the component descriptor\n */\n create(descriptor) {\n super.create(descriptor);\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @param {number} sectionReturn the content section return\n * @return {Component}\n */\n static init(target, selectors, sectionReturn) {\n return new TopcollComponent({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n sectionReturn,\n });\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n // Section return is a global page variable but most formats define it just before start printing\n // the course content. This is the reason why we define this page setting here.\n this.reactive.sectionReturn = this.sectionReturn;\n\n // Check if the course format is compatible with reactive components.\n if (!this.reactive.supportComponents) {\n return [];\n }\n return [\n // State changes that require to reload some course modules.\n {watch: `cm.visible:updated`, handler: this._reloadCm},\n {watch: `cm.stealth:updated`, handler: this._reloadCm},\n {watch: `cm.sectionid:updated`, handler: this._reloadCm},\n {watch: `cm.indent:updated`, handler: this._reloadCm},\n {watch: `cm.groupmode:updated`, handler: this._reloadCm},\n {watch: `cm.name:updated`, handler: this._refreshCmName},\n // Update section number and title.\n {watch: `section.number:updated`, handler: this._refreshSectionNumber},\n {watch: `section.title:updated`, handler: this._refreshSectionTitle},\n // Sections and cm sorting.\n {watch: `transaction:start`, handler: this._startProcessing},\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n // Section visibility.\n {watch: `section.visible:updated`, handler: this._reloadSection},\n // Reindex sections and cms.\n {watch: `state:updated`, handler: this._indexContents},\n ];\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} param\n * @param {Object} param.state the full state object.\n */\n _refreshCourseSectionlist({state}) {\n // If we have a section return means we only show a single section so no need to fix order.\n if (this.reactive.sectionReturn !== null) {\n return;\n }\n const sectionlist = this.reactive.getExporter().listedSectionIds(state);\n const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST);\n // For now section cannot be created at a frontend level.\n const createSection = this._createSectionItem.bind(this);\n if (listparent) {\n this._fixTopcollSectionOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections, createSection);\n }\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {Object} dettachedelements a list of dettached elements\n * @param {function} createMethod method to create missing elements\n */\n async _fixTopcollSectionOrder(container, neworder, selector, dettachedelements, createMethod) {\n if (container === undefined) {\n return;\n }\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n neworder.forEach((itemid, index) => {\n let item = this.getElement(selector, itemid) ?? dettachedelements[itemid] ?? createMethod(container, itemid);\n if (!item) {\n // Missing elements cannot be sorted.\n return;\n }\n let itemno = this.getElement('#tcnoid-'+itemid);\n if (itemno) {\n itemno.textContent = index + 1; // Update the section number in the 'left' part.\n }\n // Get the current elemnt at that position.\n const currentitem = container.children[index];\n if (!currentitem) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n });\n\n // Dndupload add a fake element we need to keep.\n let dndFakeActivity;\n\n // Remove the remaining elements.\n while (container.children.length > neworder.length) {\n const lastchild = container.lastChild;\n if (lastchild?.classList?.contains('dndupload-preview')) {\n dndFakeActivity = lastchild;\n } else {\n dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild;\n }\n container.removeChild(lastchild);\n }\n // Restore dndupload fake element.\n if (dndFakeActivity) {\n container.append(dndFakeActivity);\n }\n }\n}\n"],"names":["TopcollComponent","Component","create","descriptor","target","selectors","sectionReturn","element","document","getElementById","reactive","getWatchers","this","supportComponents","watch","handler","_reloadCm","_refreshCmName","_refreshSectionNumber","_refreshSectionTitle","_startProcessing","_refreshCourseSectionlist","_refreshSectionCmlist","_reloadSection","_indexContents","state","sectionlist","getExporter","listedSectionIds","listparent","getElement","COURSE_SECTIONLIST","createSection","_createSectionItem","bind","_fixTopcollSectionOrder","SECTION","dettachedSections","container","neworder","selector","dettachedelements","createMethod","undefined","length","classList","add","innerHTML","dndFakeActivity","remove","forEach","itemid","index","item","itemno","textContent","currentitem","children","insertBefore","append","lastchild","lastChild","_lastchild$classList","contains","dataset","_lastchild$dataset","id","removeChild"],"mappings":";;;;;;;;;qJA4BqBA,yBAAyBC,iBAO1CC,OAAOC,kBACGD,OAAOC,wBAWLC,OAAQC,UAAWC,sBACpB,IAAIN,iBAAiB,CACxBO,QAASC,SAASC,eAAeL,QACjCM,UAAU,0CACVL,UAAAA,UACAC,cAAAA,gBASRK,0BAGSD,SAASJ,cAAgBM,KAAKN,cAG9BM,KAAKF,SAASG,kBAGZ,CAEH,CAACC,2BAA6BC,QAASH,KAAKI,WAC5C,CAACF,2BAA6BC,QAASH,KAAKI,WAC5C,CAACF,6BAA+BC,QAASH,KAAKI,WAC9C,CAACF,0BAA4BC,QAASH,KAAKI,WAC3C,CAACF,6BAA+BC,QAASH,KAAKI,WAC9C,CAACF,wBAA0BC,QAASH,KAAKK,gBAEzC,CAACH,+BAAiCC,QAASH,KAAKM,uBAChD,CAACJ,8BAAgCC,QAASH,KAAKO,sBAE/C,CAACL,0BAA4BC,QAASH,KAAKQ,kBAC3C,CAACN,mCAAqCC,QAASH,KAAKS,2BACpD,CAACP,+BAAiCC,QAASH,KAAKU,uBAEhD,CAACR,gCAAkCC,QAASH,KAAKW,gBAEjD,CAACT,sBAAwBC,QAASH,KAAKY,iBApBhC,GA8BfH,oCAA0BI,MAACA,eAEa,OAAhCb,KAAKF,SAASJ,2BAGZoB,YAAcd,KAAKF,SAASiB,cAAcC,iBAAiBH,OAC3DI,WAAajB,KAAKkB,WAAWlB,KAAKP,UAAU0B,oBAE5CC,cAAgBpB,KAAKqB,mBAAmBC,KAAKtB,MAC/CiB,iBACKM,wBAAwBN,WAAYH,YAAad,KAAKP,UAAU+B,QAASxB,KAAKyB,kBAAmBL,6CAahFM,UAAWC,SAAUC,SAAUC,kBAAmBC,sBAC1DC,IAAdL,qBAKCC,SAASK,cACVN,UAAUO,UAAUC,IAAI,eACxBR,UAAUS,UAAY,QA8BtBC,oBAzBJV,UAAUO,UAAUI,OAAO,UAG3BV,SAASW,SAAQ,CAACC,OAAQC,wCAClBC,4CAAOzC,KAAKkB,WAAWU,SAAUW,qDAAWV,kBAAkBU,+BAAWT,aAAaJ,UAAWa,YAChGE,gBAIDC,OAAS1C,KAAKkB,WAAW,WAAWqB,QACpCG,SACAA,OAAOC,YAAcH,MAAQ,SAG3BI,YAAclB,UAAUmB,SAASL,OAClCI,YAIDA,cAAgBH,MAChBf,UAAUoB,aAAaL,KAAMG,aAJ7BlB,UAAUqB,OAAON,SAYlBf,UAAUmB,SAASb,OAASL,SAASK,QAAQ,gCAC1CgB,UAAYtB,UAAUuB,0DACxBD,MAAAA,wCAAAA,UAAWf,2CAAXiB,qBAAsBC,SAAS,qBAC/Bf,gBAAkBY,eAElBnB,gDAAkBmB,MAAAA,sCAAAA,UAAWI,6CAAXC,mBAAoBC,0DAAM,GAAKN,UAErDtB,UAAU6B,YAAYP,WAGtBZ,iBACAV,UAAUqB,OAAOX"} \ No newline at end of file +{"version":3,"file":"content.min.js","sources":["../../src/local/content.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 * Collapsed Topics Course index main component.\n *\n * @module format_topcoll/local/content\n * @class format_topcoll/local/content\n * @copyright 2022 G J Barnard based upon work done by:\n * @copyright 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Component from 'core_courseformat/local/content';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport TopcollDispatchActions from 'format_topcoll/local/content/actions';\nimport * as CourseEvents from 'core_course/events';\n\nexport default class TopcollComponent extends Component {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor the component descriptor\n */\n create(descriptor) {\n super.create(descriptor);\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @param {number} sectionReturn the content section return\n * @return {Component}\n */\n static init(target, selectors, sectionReturn) {\n return new TopcollComponent({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n sectionReturn,\n });\n }\n\n /**\n * Initial state ready method.\n */\n stateReady() {\n this._indexContents();\n\n if (this.reactive.supportComponents) {\n // Actions are only available in edit mode.\n if (this.reactive.isEditing) {\n new TopcollDispatchActions(this);\n }\n\n // Mark content as state ready.\n this.element.classList.add(this.classes.STATEDREADY);\n }\n\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n\n // Capture page scroll to update page item.\n this.addEventListener(\n document,\n \"scroll\",\n this._scrollHandler\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n // Section return is a global page variable but most formats define it just before start printing\n // the course content. This is the reason why we define this page setting here.\n this.reactive.sectionReturn = this.sectionReturn;\n\n // Check if the course format is compatible with reactive components.\n if (!this.reactive.supportComponents) {\n return [];\n }\n return [\n // State changes that require to reload some course modules.\n {watch: `cm.visible:updated`, handler: this._reloadCm},\n {watch: `cm.stealth:updated`, handler: this._reloadCm},\n {watch: `cm.sectionid:updated`, handler: this._reloadCm},\n {watch: `cm.indent:updated`, handler: this._reloadCm},\n {watch: `cm.groupmode:updated`, handler: this._reloadCm},\n {watch: `cm.name:updated`, handler: this._refreshCmName},\n // Update section number and title.\n {watch: `section.number:updated`, handler: this._refreshSectionNumber},\n {watch: `section.title:updated`, handler: this._refreshSectionTitle},\n // Sections and cm sorting.\n {watch: `transaction:start`, handler: this._startProcessing},\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n // Section visibility.\n {watch: `section.visible:updated`, handler: this._reloadSection},\n // Reindex sections and cms.\n {watch: `state:updated`, handler: this._indexContents},\n ];\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} param\n * @param {Object} param.state the full state object.\n */\n _refreshCourseSectionlist({state}) {\n // If we have a section return means we only show a single section so no need to fix order.\n if (this.reactive.sectionReturn !== null) {\n return;\n }\n const sectionlist = this.reactive.getExporter().listedSectionIds(state);\n const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST);\n // For now section cannot be created at a frontend level.\n const createSection = this._createSectionItem.bind(this);\n if (listparent) {\n this._fixTopcollSectionOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections, createSection);\n }\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {Object} dettachedelements a list of dettached elements\n * @param {function} createMethod method to create missing elements\n */\n async _fixTopcollSectionOrder(container, neworder, selector, dettachedelements, createMethod) {\n if (container === undefined) {\n return;\n }\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n neworder.forEach((itemid, index) => {\n let item = this.getElement(selector, itemid) ?? dettachedelements[itemid] ?? createMethod(container, itemid);\n if (!item) {\n // Missing elements cannot be sorted.\n return;\n }\n let itemno = this.getElement('#tcnoid-'+itemid);\n if (itemno) {\n itemno.textContent = index + 1; // Update the section number in the 'left' part.\n }\n // Get the current elemnt at that position.\n const currentitem = container.children[index];\n if (!currentitem) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n });\n\n // Dndupload add a fake element we need to keep.\n let dndFakeActivity;\n\n // Remove the remaining elements.\n while (container.children.length > neworder.length) {\n const lastchild = container.lastChild;\n if (lastchild?.classList?.contains('dndupload-preview')) {\n dndFakeActivity = lastchild;\n } else {\n dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild;\n }\n container.removeChild(lastchild);\n }\n // Restore dndupload fake element.\n if (dndFakeActivity) {\n container.append(dndFakeActivity);\n }\n }\n}\n"],"names":["TopcollComponent","Component","create","descriptor","target","selectors","sectionReturn","element","document","getElementById","reactive","stateReady","_indexContents","this","supportComponents","isEditing","TopcollDispatchActions","classList","add","classes","STATEDREADY","addEventListener","CourseEvents","manualCompletionToggled","_completionHandler","_scrollHandler","getWatchers","watch","handler","_reloadCm","_refreshCmName","_refreshSectionNumber","_refreshSectionTitle","_startProcessing","_refreshCourseSectionlist","_refreshSectionCmlist","_reloadSection","state","sectionlist","getExporter","listedSectionIds","listparent","getElement","COURSE_SECTIONLIST","createSection","_createSectionItem","bind","_fixTopcollSectionOrder","SECTION","dettachedSections","container","neworder","selector","dettachedelements","createMethod","undefined","length","innerHTML","dndFakeActivity","remove","forEach","itemid","index","item","itemno","textContent","currentitem","children","insertBefore","append","lastchild","lastChild","_lastchild$classList","contains","dataset","_lastchild$dataset","id","removeChild"],"mappings":";;;;;;;;;u1BA8BqBA,yBAAyBC,iBAO1CC,OAAOC,kBACGD,OAAOC,wBAWLC,OAAQC,UAAWC,sBACpB,IAAIN,iBAAiB,CACxBO,QAASC,SAASC,eAAeL,QACjCM,UAAU,0CACVL,UAAAA,UACAC,cAAAA,gBAORK,kBACSC,iBAEDC,KAAKH,SAASI,oBAEVD,KAAKH,SAASK,eACVC,iBAAuBH,WAI1BN,QAAQU,UAAUC,IAAIL,KAAKM,QAAQC,mBAIvCC,iBACDR,KAAKN,QACLe,aAAaC,wBACbV,KAAKW,yBAIJH,iBACDb,SACA,SACAK,KAAKY,gBASbC,0BAGShB,SAASJ,cAAgBO,KAAKP,cAG9BO,KAAKH,SAASI,kBAGZ,CAEH,CAACa,2BAA6BC,QAASf,KAAKgB,WAC5C,CAACF,2BAA6BC,QAASf,KAAKgB,WAC5C,CAACF,6BAA+BC,QAASf,KAAKgB,WAC9C,CAACF,0BAA4BC,QAASf,KAAKgB,WAC3C,CAACF,6BAA+BC,QAASf,KAAKgB,WAC9C,CAACF,wBAA0BC,QAASf,KAAKiB,gBAEzC,CAACH,+BAAiCC,QAASf,KAAKkB,uBAChD,CAACJ,8BAAgCC,QAASf,KAAKmB,sBAE/C,CAACL,0BAA4BC,QAASf,KAAKoB,kBAC3C,CAACN,mCAAqCC,QAASf,KAAKqB,2BACpD,CAACP,+BAAiCC,QAASf,KAAKsB,uBAEhD,CAACR,gCAAkCC,QAASf,KAAKuB,gBAEjD,CAACT,sBAAwBC,QAASf,KAAKD,iBApBhC,GA8BfsB,oCAA0BG,MAACA,eAEa,OAAhCxB,KAAKH,SAASJ,2BAGZgC,YAAczB,KAAKH,SAAS6B,cAAcC,iBAAiBH,OAC3DI,WAAa5B,KAAK6B,WAAW7B,KAAKR,UAAUsC,oBAE5CC,cAAgB/B,KAAKgC,mBAAmBC,KAAKjC,MAC/C4B,iBACKM,wBAAwBN,WAAYH,YAAazB,KAAKR,UAAU2C,QAASnC,KAAKoC,kBAAmBL,6CAahFM,UAAWC,SAAUC,SAAUC,kBAAmBC,sBAC1DC,IAAdL,qBAKCC,SAASK,cACVN,UAAUjC,UAAUC,IAAI,eACxBgC,UAAUO,UAAY,QA8BtBC,oBAzBJR,UAAUjC,UAAU0C,OAAO,UAG3BR,SAASS,SAAQ,CAACC,OAAQC,wCAClBC,4CAAOlD,KAAK6B,WAAWU,SAAUS,qDAAWR,kBAAkBQ,+BAAWP,aAAaJ,UAAWW,YAChGE,gBAIDC,OAASnD,KAAK6B,WAAW,WAAWmB,QACpCG,SACAA,OAAOC,YAAcH,MAAQ,SAG3BI,YAAchB,UAAUiB,SAASL,OAClCI,YAIDA,cAAgBH,MAChBb,UAAUkB,aAAaL,KAAMG,aAJ7BhB,UAAUmB,OAAON,SAYlBb,UAAUiB,SAASX,OAASL,SAASK,QAAQ,gCAC1Cc,UAAYpB,UAAUqB,0DACxBD,MAAAA,wCAAAA,UAAWrD,2CAAXuD,qBAAsBC,SAAS,qBAC/Bf,gBAAkBY,eAElBjB,gDAAkBiB,MAAAA,sCAAAA,UAAWI,6CAAXC,mBAAoBC,0DAAM,GAAKN,UAErDpB,UAAU2B,YAAYP,WAGtBZ,iBACAR,UAAUmB,OAAOX"} \ No newline at end of file diff --git a/amd/build/local/content/actions.min.js b/amd/build/local/content/actions.min.js new file mode 100644 index 0000000..66f1ecb --- /dev/null +++ b/amd/build/local/content/actions.min.js @@ -0,0 +1,12 @@ +define("format_topcoll/local/content/actions",["exports","core_courseformat/local/content/actions"],(function(_exports,_actions){var obj; +/** + * Collapsed Topics Course state actions dispatcher. + * + * @module format_topcoll/local/content/actions + * @class format_topcoll/local/content/actions + * @copyright 2024 G J Barnard based upon work done by: + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_actions=(obj=_actions)&&obj.__esModule?obj:{default:obj};class _default extends _actions.default{create(){super.create(),this.selectors.ADDSECTION=".increase-sections"}_setAddSectionLocked(locked){const courseAddSection=this.getElement(this.selectors.COURSEADDSECTION);if(courseAddSection){const addSection=courseAddSection.querySelector(this.selectors.ADDSECTION);addSection&&addSection.classList.toggle(this.classes.DISPLAYNONE,locked);courseAddSection.querySelector(this.selectors.MAXSECTIONSWARNING).classList.toggle(this.classes.DISPLAYNONE,!locked)}}}return _exports.default=_default,_exports.default})); + +//# sourceMappingURL=actions.min.js.map \ No newline at end of file diff --git a/amd/build/local/content/actions.min.js.map b/amd/build/local/content/actions.min.js.map new file mode 100644 index 0000000..5e8bb5d --- /dev/null +++ b/amd/build/local/content/actions.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"actions.min.js","sources":["../../../src/local/content/actions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\r\n//\r\n// Moodle is free software: you can redistribute it and/or modify\r\n// it under the terms of the GNU General Public License as published by\r\n// the Free Software Foundation, either version 3 of the License, or\r\n// (at your option) any later version.\r\n//\r\n// Moodle is distributed in the hope that it will be useful,\r\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\n// GNU General Public License for more details.\r\n//\r\n// You should have received a copy of the GNU General Public License\r\n// along with Moodle. If not, see .\r\n\r\n/**\r\n * Collapsed Topics Course state actions dispatcher.\r\n *\r\n * @module format_topcoll/local/content/actions\r\n * @class format_topcoll/local/content/actions\r\n * @copyright 2024 G J Barnard based upon work done by:\r\n * @copyright 2021 Ferran Recio \r\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\r\n */\r\n\r\nimport BaseActions from 'core_courseformat/local/content/actions';\r\n\r\nexport default class extends BaseActions {\r\n\r\n /**\r\n * Constructor hook.\r\n */\r\n create() {\r\n super.create();\r\n this.selectors.ADDSECTION = \".increase-sections\";\r\n }\r\n\r\n /**\r\n * Disable all add sections actions.\r\n *\r\n * @param {boolean} locked the new locked value.\r\n */\r\n _setAddSectionLocked(locked) {\r\n const courseAddSection = this.getElement(this.selectors.COURSEADDSECTION);\r\n if (courseAddSection) {\r\n const addSection = courseAddSection.querySelector(this.selectors.ADDSECTION);\r\n if (addSection) {\r\n addSection.classList.toggle(this.classes.DISPLAYNONE, locked);\r\n }\r\n const noMoreSections = courseAddSection.querySelector(this.selectors.MAXSECTIONSWARNING);\r\n noMoreSections.classList.toggle(this.classes.DISPLAYNONE, !locked);\r\n }\r\n }\r\n}\r\n"],"names":["BaseActions","create","selectors","ADDSECTION","_setAddSectionLocked","locked","courseAddSection","this","getElement","COURSEADDSECTION","addSection","querySelector","classList","toggle","classes","DISPLAYNONE","MAXSECTIONSWARNING"],"mappings":";;;;;;;;;sKA2B6BA,iBAKzBC,eACUA,cACDC,UAAUC,WAAa,qBAQhCC,qBAAqBC,cACXC,iBAAmBC,KAAKC,WAAWD,KAAKL,UAAUO,qBACpDH,iBAAkB,OACZI,WAAaJ,iBAAiBK,cAAcJ,KAAKL,UAAUC,YAC7DO,YACAA,WAAWE,UAAUC,OAAON,KAAKO,QAAQC,YAAaV,QAEnCC,iBAAiBK,cAAcJ,KAAKL,UAAUc,oBACtDJ,UAAUC,OAAON,KAAKO,QAAQC,aAAcV"} \ No newline at end of file diff --git a/amd/src/local/content.js b/amd/src/local/content.js index 53e65ee..3659c40 100644 --- a/amd/src/local/content.js +++ b/amd/src/local/content.js @@ -25,6 +25,8 @@ import Component from 'core_courseformat/local/content'; import {getCurrentCourseEditor} from 'core_courseformat/courseeditor'; +import TopcollDispatchActions from 'format_topcoll/local/content/actions'; +import * as CourseEvents from 'core_course/events'; export default class TopcollComponent extends Component { @@ -54,6 +56,37 @@ export default class TopcollComponent extends Component { }); } + /** + * Initial state ready method. + */ + stateReady() { + this._indexContents(); + + if (this.reactive.supportComponents) { + // Actions are only available in edit mode. + if (this.reactive.isEditing) { + new TopcollDispatchActions(this); + } + + // Mark content as state ready. + this.element.classList.add(this.classes.STATEDREADY); + } + + // Capture completion events. + this.addEventListener( + this.element, + CourseEvents.manualCompletionToggled, + this._completionHandler + ); + + // Capture page scroll to update page item. + this.addEventListener( + document, + "scroll", + this._scrollHandler + ); + } + /** * Return the component watchers. * diff --git a/amd/src/local/content/actions.js b/amd/src/local/content/actions.js new file mode 100644 index 0000000..3678109 --- /dev/null +++ b/amd/src/local/content/actions.js @@ -0,0 +1,54 @@ +// 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 . + +/** + * Collapsed Topics Course state actions dispatcher. + * + * @module format_topcoll/local/content/actions + * @class format_topcoll/local/content/actions + * @copyright 2024 G J Barnard based upon work done by: + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import BaseActions from 'core_courseformat/local/content/actions'; + +export default class extends BaseActions { + + /** + * Constructor hook. + */ + create() { + super.create(); + this.selectors.ADDSECTION = ".increase-sections"; + } + + /** + * Disable all add sections actions. + * + * @param {boolean} locked the new locked value. + */ + _setAddSectionLocked(locked) { + const courseAddSection = this.getElement(this.selectors.COURSEADDSECTION); + if (courseAddSection) { + const addSection = courseAddSection.querySelector(this.selectors.ADDSECTION); + if (addSection) { + addSection.classList.toggle(this.classes.DISPLAYNONE, locked); + } + const noMoreSections = courseAddSection.querySelector(this.selectors.MAXSECTIONSWARNING); + noMoreSections.classList.toggle(this.classes.DISPLAYNONE, !locked); + } + } +} diff --git a/classes/activity.php b/classes/activity.php index 599b637..55fd8d4 100644 --- a/classes/activity.php +++ b/classes/activity.php @@ -46,7 +46,15 @@ defined('MOODLE_INTERNAL') || die(); +use assign; +use cache; use cm_info; +use context_course; +use context_module; +use core\lock\lock_config; +use grade_item; +use grade_grade; +use moodle_exception; require_once($CFG->dirroot . '/mod/assign/locallib.php'); @@ -66,7 +74,7 @@ class activity { * * Main method that calls relevant activity-related method based on the mod name. * - * @param \cm_info $mod + * @param cm_info $mod * @return activity_meta */ public static function module_meta(cm_info $mod) { @@ -143,21 +151,21 @@ protected static function std_meta( $graderow = self::grade_row($courseid, $mod); if ($graderow) { - $coursecontext = \context_course::instance($courseid); + $coursecontext = context_course::instance($courseid); if (has_capability('moodle/grade:viewhidden', $coursecontext)) { $meta = new activity_meta(); $meta->grade = true; } else { global $USER; - $gradeitem = \grade_item::fetch([ + $gradeitem = grade_item::fetch([ 'itemtype' => 'mod', 'itemmodule' => $mod->modname, 'iteminstance' => $mod->instance, 'outcomeid' => null, ]); - $grade = new \grade_grade(['itemid' => $gradeitem->id, 'userid' => $USER->id]); + $grade = new grade_grade(['itemid' => $gradeitem->id, 'userid' => $USER->id]); if (!$grade->is_hidden()) { $meta = new activity_meta(); $meta->grade = true; @@ -284,7 +292,7 @@ protected static function std_num_submissions( // Results are not cached, so lets get them. // Get people who are typically not students (people who can view grader report) so that we can exclude them! - [$graderids, $params] = get_enrolled_sql(\context_course::instance($courseid), 'moodle/grade:viewall'); + [$graderids, $params] = get_enrolled_sql(context_course::instance($courseid), 'moodle/grade:viewall'); $params['courseid'] = $courseid; // Get the number of submissions for all $maintable activities in this course. @@ -318,9 +326,9 @@ protected static function std_num_submissions( */ protected static function assign_nums($courseid, $mod) { // Ref: get_assign_grading_summary_renderable(). - $coursemodulecontext = \context_module::instance($mod->id); + $coursemodulecontext = context_module::instance($mod->id); $course = get_course($courseid); - $assign = new \assign($coursemodulecontext, $mod, $course); + $assign = new assign($coursemodulecontext, $mod, $course); $activitygroup = groups_get_activity_group($mod); $instance = $assign->get_default_instance(); if ($instance->teamsubmission) { @@ -400,7 +408,7 @@ protected static function forum_num_submissions($courseid, $mod) { 'posted' in / started them and thus should be graded if they have not been. */ $params['forumid'] = $mod->instance; - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $students = $studentscache->get($courseid); $userids = implode(',', $students); @@ -450,7 +458,7 @@ protected static function quiz_num_submissions($courseid, $mod) { protected static function forum_num_submissions_ungraded($courseid, $mod) { global $DB; - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $students = $studentscache->get($courseid); $userids = implode(',', $students); @@ -485,7 +493,7 @@ protected static function quiz_num_submissions_ungraded($courseid, $mod) { static $totalsbyquizid = null; - $coursecontext = \context_course::instance($courseid); + $coursecontext = context_course::instance($courseid); // Get people who are typically not students (people who can view grader report) so that we can exclude them! [$graderids, $params] = get_enrolled_sql($coursecontext, 'moodle/grade:viewall'); $params['courseid'] = $courseid; @@ -588,16 +596,16 @@ protected static function course_participant_count($courseid, $mod) { $students = self::course_get_students($courseid); // New users? - $usercreatedcache = \cache::make('format_topcoll', 'activityusercreatedcache'); + $usercreatedcache = cache::make('format_topcoll', 'activityusercreatedcache'); $createdusers = $usercreatedcache->get($courseid); $lock = null; $newstudents = []; if (!empty($createdusers)) { $lock = self::lockcaches($courseid); - $studentrolescache = \cache::make('format_topcoll', 'activitystudentrolescache'); + $studentrolescache = cache::make('format_topcoll', 'activitystudentrolescache'); $studentroles = $studentrolescache->get('roles'); - $context = \context_course::instance($courseid); + $context = context_course::instance($courseid); $alluserroles = get_users_roles($context, $createdusers, false); foreach ($createdusers as $userid) { @@ -629,18 +637,18 @@ protected static function course_participant_count($courseid, $mod) { $students[$newstudent] = $newstudent; } } - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $studentscache->set($courseid, $students); } else if (!empty($newstudents)) { $students = $newstudents; - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $studentscache->set($courseid, $students); } } if (is_array($students)) { // We have students! - $modulecountcache = \cache::make('format_topcoll', 'activitymodulecountcache'); + $modulecountcache = cache::make('format_topcoll', 'activitymodulecountcache'); $modulecountcourse = $modulecountcache->get($courseid); if (empty($modulecountcourse)) { $modulecountcourse = self::calulatecoursemodules($courseid, $students); @@ -673,7 +681,7 @@ protected static function course_participant_count($courseid, $mod) { * @return array / string 0 or more student id's in an array or 'nostudents' string. */ public static function course_get_students($courseid) { - $studentrolescache = \cache::make('format_topcoll', 'activitystudentrolescache'); + $studentrolescache = cache::make('format_topcoll', 'activitystudentrolescache'); $studentroles = $studentrolescache->get('roles'); if (empty($studentroles)) { @@ -685,11 +693,11 @@ public static function course_get_students($courseid) { $studentrolescache->set('roles', $studentroles); } - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $students = $studentscache->get($courseid); if (empty($students)) { $students = []; - $context = \context_course::instance($courseid); + $context = context_course::instance($courseid); $enrolledusers = get_enrolled_users($context, '', 0, 'u.id', null, 0, 0, true); $users = array_keys($enrolledusers); $alluserroles = get_users_roles($context, $users, false); @@ -760,7 +768,7 @@ public static function maxstudentsnotexceeded($courseid, $extrainfo = false) { * Invalidates the activity student roles cache. */ public static function invalidatestudentrolescache() { - $modulecountcache = \cache::make('format_topcoll', 'activitymodulecountcache'); + $modulecountcache = cache::make('format_topcoll', 'activitymodulecountcache'); $modulecountcache->purge(); } @@ -768,7 +776,7 @@ public static function invalidatestudentrolescache() { * Invalidates the activity module count cache. */ public static function invalidatemodulecountcache() { - $studentrolescache = \cache::make('format_topcoll', 'activitystudentrolescache'); + $studentrolescache = cache::make('format_topcoll', 'activitystudentrolescache'); $studentrolescache->purge(); } @@ -776,7 +784,7 @@ public static function invalidatemodulecountcache() { * Invalidates the activity students cache. */ public static function invalidatestudentscache() { - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $studentscache->purge(); } @@ -834,7 +842,7 @@ private static function userenrolmentchanged($userid, $courseid, $type) { // Created. /* Note: At the time of the event, the DB has not been updated to know that the given user has been assigned a role of 'student' - role_assignments table with data relating to that contained in the event itself. */ - $usercreatedcache = \cache::make('format_topcoll', 'activityusercreatedcache'); + $usercreatedcache = cache::make('format_topcoll', 'activityusercreatedcache'); $createdusers = $usercreatedcache->get($courseid); if (empty($createdusers)) { $createdusers = []; @@ -843,13 +851,13 @@ private static function userenrolmentchanged($userid, $courseid, $type) { $usercreatedcache->set($courseid, $createdusers); } else if ($type == -1) { // Deleted. - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $students = $studentscache->get($courseid); if (!empty($students)) { if (array_key_exists($userid, $students)) { unset($students[$userid]); $studentscache->set($courseid, $students); - $modulecountcache = \cache::make('format_topcoll', 'activitymodulecountcache'); + $modulecountcache = cache::make('format_topcoll', 'activitymodulecountcache'); $modulecountcourse = $modulecountcache->get($courseid); if (empty($modulecountcourse)) { if (!empty($students)) { @@ -901,10 +909,10 @@ public static function moduleupdated($modid, $courseid, $courseformat) { private static function modulechanged($modid, $courseid, $courseformat) { if (self::activitymetaenabled() && self::activitymetaused($courseformat)) { $lock = self::lockcaches($courseid); - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $students = $studentscache->get($courseid); if (is_array($students)) { - $modulecountcache = \cache::make('format_topcoll', 'activitymodulecountcache'); + $modulecountcache = cache::make('format_topcoll', 'activitymodulecountcache'); $modulecountcourse = $modulecountcache->get($courseid); if (!empty($modulecountcourse)) { $updated = self::calulatecoursemodules($courseid, $students, $modid); @@ -925,7 +933,7 @@ private static function modulechanged($modid, $courseid, $courseformat) { public static function moduledeleted($modid, $courseid, $courseformat) { if (self::activitymetaenabled() && self::activitymetaused($courseformat)) { $lock = self::lockcaches($courseid); - $modulecountcache = \cache::make('format_topcoll', 'activitymodulecountcache'); + $modulecountcache = cache::make('format_topcoll', 'activitymodulecountcache'); $modulecountcourse = $modulecountcache->get($courseid); if (!empty($modulecountcourse)) { unset($modulecountcourse[$modid]); @@ -943,9 +951,9 @@ public static function moduledeleted($modid, $courseid, $courseformat) { */ private static function clearcoursemodulecount($courseid) { $lock = self::lockcaches($courseid); - $modulecountcache = \cache::make('format_topcoll', 'activitymodulecountcache'); + $modulecountcache = cache::make('format_topcoll', 'activitymodulecountcache'); $modulecountcache->set($courseid, null); - $studentscache = \cache::make('format_topcoll', 'activitystudentscache'); + $studentscache = cache::make('format_topcoll', 'activitystudentscache'); $studentscache->set($courseid, null); $lock->release(); } @@ -1002,11 +1010,11 @@ private static function calulatecoursemodules($courseid, $students, $modid = nul * @return object The lock to release when complete. */ private static function lockcaches($courseid) { - $lockfactory = \core\lock\lock_config::get_lock_factory('format_topcoll'); + $lockfactory = lock_config::get_lock_factory('format_topcoll'); if ($lock = $lockfactory->get_lock('courseid' . $courseid, 5)) { return $lock; } - throw new \moodle_exception( + throw new moodle_exception( 'cannotgetactivitycacheslock', 'format_topcoll', '', diff --git a/classes/admin_setting_information.php b/classes/admin_setting_information.php index dff3665..fd0fbd8 100644 --- a/classes/admin_setting_information.php +++ b/classes/admin_setting_information.php @@ -31,13 +31,17 @@ namespace format_topcoll; +use admin_setting; +use core_plugin_manager; +use core\output\html_writer; + /** * Setting that displays information. Based on admin_setting_description in adminlib.php. * * @copyright © 2022-onwards G J Barnard. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. */ -class admin_setting_information extends \admin_setting { +class admin_setting_information extends admin_setting { /** @var int The branch this is for. */ protected $mbranch; @@ -94,11 +98,11 @@ public function write_setting($data) { public function output_html($data, $query = '') { global $CFG, $OUTPUT; - $formats = \core_plugin_manager::instance()->get_present_plugins('format'); + $formats = core_plugin_manager::instance()->get_present_plugins('format'); if (!empty($formats['topcoll'])) { $plugininfo = $formats['topcoll']; } else { - $plugininfo = \core_plugin_manager::instance()->get_plugin_info('format_topcoll'); + $plugininfo = core_plugin_manager::instance()->get_plugin_info('format_topcoll'); $plugininfo->version = $plugininfo->versiondisk; } @@ -107,8 +111,8 @@ public function output_html($data, $query = '') { $attributes['aria-hidden'] = 'true'; $attributes['class'] = 'fa fa-heart'; $attributes['title'] = get_string('love', 'format_topcoll'); - $content = \html_writer::tag('span', $attributes['title'], ['class' => 'sr-only']); - $content = \html_writer::tag('span', $content, $attributes); + $content = html_writer::tag('span', $attributes['title'], ['class' => 'sr-only']); + $content = html_writer::tag('span', $content, $attributes); $context['versioninfo'] = get_string( 'versioninfo', 'format_topcoll', diff --git a/classes/admin_setting_markdown.php b/classes/admin_setting_markdown.php index c2bb93c..883962c 100644 --- a/classes/admin_setting_markdown.php +++ b/classes/admin_setting_markdown.php @@ -31,13 +31,16 @@ namespace format_topcoll; +use admin_setting; +use stdClass; + /** * Setting that displays markdown files. Based on admin_setting_description in adminlib.php. * * @copyright © 2022-onwards G J Barnard. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. */ -class admin_setting_markdown extends \admin_setting { +class admin_setting_markdown extends admin_setting { /** @var string Filename */ private $filename; @@ -94,7 +97,7 @@ public function write_setting($data) { public function output_html($data, $query = '') { global $CFG, $OUTPUT; - $context = new \stdClass(); + $context = new stdClass(); $context->title = $this->visiblename; $context->description = $this->description; diff --git a/classes/observer.php b/classes/observer.php index 08cab99..8e28902 100644 --- a/classes/observer.php +++ b/classes/observer.php @@ -29,6 +29,11 @@ * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace format_topcoll; + +use format_topcoll\activity; +use format_topcoll\togglelib; + defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/course/format/lib.php'); // For course_get_format. @@ -36,7 +41,7 @@ /** * Event observers supported by this format. */ -class format_topcoll_observer { +class observer { /** * Observer for the course_content_deleted event. * @@ -48,7 +53,7 @@ class format_topcoll_observer { public static function course_content_deleted(\core\event\course_content_deleted $event) { global $DB; $DB->delete_records("user_preferences", - ["name" => \format_topcoll\togglelib::TOPCOLL_TOGGLE.'_' . $event->objectid]); // This is the $courseid. + ["name" => togglelib::TOPCOLL_TOGGLE.'_' . $event->objectid]); // This is the $courseid. } /* Events observed for the purpose of the activty functionality. @@ -64,27 +69,27 @@ public static function role_allow_view_updated() { /* Subsitute for a 'role created' event that does not exist in core! But this seems to happen when a role is created. See 'create_role' in lib/accesslib.php. */ - \format_topcoll\activity::invalidatestudentrolescache(); - \format_topcoll\activity::invalidatemodulecountcache(); - \format_topcoll\activity::invalidatestudentscache(); + activity::invalidatestudentrolescache(); + activity::invalidatemodulecountcache(); + activity::invalidatestudentscache(); } /** * Observer for the role_updated event. */ public static function role_updated() { - \format_topcoll\activity::invalidatestudentrolescache(); - \format_topcoll\activity::invalidatemodulecountcache(); - \format_topcoll\activity::invalidatestudentscache(); + activity::invalidatestudentrolescache(); + activity::invalidatemodulecountcache(); + activity::invalidatestudentscache(); } /** * Observer for the role_deleted event. */ public static function role_deleted() { - \format_topcoll\activity::invalidatestudentrolescache(); - \format_topcoll\activity::invalidatemodulecountcache(); - \format_topcoll\activity::invalidatestudentscache(); + activity::invalidatestudentrolescache(); + activity::invalidatemodulecountcache(); + activity::invalidatestudentscache(); } /** @@ -94,7 +99,7 @@ public static function role_deleted() { */ public static function user_enrolment_created(\core\event\user_enrolment_created $event) { if ($courseformat = self::istopcoll($event->courseid)) { - \format_topcoll\activity::userenrolmentcreated($event->relateduserid, $event->courseid, $courseformat); + activity::userenrolmentcreated($event->relateduserid, $event->courseid, $courseformat); } } @@ -105,7 +110,7 @@ public static function user_enrolment_created(\core\event\user_enrolment_created */ public static function user_enrolment_updated(\core\event\user_enrolment_updated $event) { if ($courseformat = self::istopcoll($event->courseid)) { - \format_topcoll\activity::userenrolmentupdated($event->relateduserid, $event->courseid, $courseformat); + activity::userenrolmentupdated($event->relateduserid, $event->courseid, $courseformat); } } @@ -116,7 +121,7 @@ public static function user_enrolment_updated(\core\event\user_enrolment_updated */ public static function user_enrolment_deleted(\core\event\user_enrolment_deleted $event) { if ($courseformat = self::istopcoll($event->courseid)) { - \format_topcoll\activity::userenrolmentdeleted($event->relateduserid, $event->courseid, $courseformat); + activity::userenrolmentdeleted($event->relateduserid, $event->courseid, $courseformat); } } @@ -127,7 +132,7 @@ public static function user_enrolment_deleted(\core\event\user_enrolment_deleted */ public static function course_module_created(\core\event\course_module_created $event) { if ($courseformat = self::istopcoll($event->courseid)) { - \format_topcoll\activity::modulecreated($event->objectid, $event->courseid, $courseformat); + activity::modulecreated($event->objectid, $event->courseid, $courseformat); } } @@ -138,7 +143,7 @@ public static function course_module_created(\core\event\course_module_created $ */ public static function course_module_updated(\core\event\course_module_updated $event) { if ($courseformat = self::istopcoll($event->courseid)) { - \format_topcoll\activity::moduleupdated($event->objectid, $event->courseid, $courseformat); + activity::moduleupdated($event->objectid, $event->courseid, $courseformat); } } @@ -149,7 +154,7 @@ public static function course_module_updated(\core\event\course_module_updated $ */ public static function course_module_deleted(\core\event\course_module_deleted $event) { if ($courseformat = self::istopcoll($event->courseid)) { - \format_topcoll\activity::moduledeleted($event->objectid, $event->courseid, $courseformat); + activity::moduledeleted($event->objectid, $event->courseid, $courseformat); } } diff --git a/classes/output/courseformat/content/addsection.php b/classes/output/courseformat/content/addsection.php new file mode 100644 index 0000000..0a55dd6 --- /dev/null +++ b/classes/output/courseformat/content/addsection.php @@ -0,0 +1,50 @@ +. + +/** + * Class to render a course add section buttons. + * + * @package format_topcoll + * @copyright © 2024-onwards G J Barnard in respect to modifications of standard topics format. + * @author G J Barnard - {@link https://moodle.org/user/profile.php?id=442195} + * @copyright 2020 Ferran Recio + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace format_topcoll\output\courseformat\content; + +/** + * Collapsed Topics + * + * Class to render a course add section buttons. + * + * @package format_topcoll + * @copyright © 2024-onwards G J Barnard in respect to modifications of standard topics format. + * @author G J Barnard - {@link https://moodle.org/user/profile.php?id=442195} + * @copyright 2020 Ferran Recio + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class addsection extends \core_courseformat\output\local\content\addsection { + /** + * Get the name of the template to use for this templatable. + * + * @param renderer_base $renderer The renderer requesting the template name. + * @return string. + */ + public function get_template_name(\renderer_base $renderer): string { + return 'format_topcoll/local/content/addsection'; + } +} diff --git a/classes/output/courseformat/content/cm.php b/classes/output/courseformat/content/cm.php index b127cdb..504c85c 100644 --- a/classes/output/courseformat/content/cm.php +++ b/classes/output/courseformat/content/cm.php @@ -27,6 +27,12 @@ namespace format_topcoll\output\courseformat\content; use core_courseformat\output\local\content\cm as cm_base; +use cm_info; +use context_course; +use core\output\renderer_base; +use core\url; +use format_topcoll\activity; +use stdClass; /** * Base class to render a course module inside a course format. @@ -39,19 +45,19 @@ class cm extends cm_base { /** * Export this data so it can be used as the context for a mustache template. * - * @param \renderer_base $output typically, the renderer that's calling this function + * @param renderer_base $output typically, the renderer that's calling this function * @return stdClass data context for a mustache template */ - public function export_for_template(\renderer_base $output): \stdClass { + public function export_for_template(renderer_base $output): stdClass { $data = parent::export_for_template($output); // Get further information. - if (\format_topcoll\activity::activitymetaenabled()) { + if (activity::activitymetaenabled()) { $courseformat = $this->format; - if (\format_topcoll\activity::activitymetaused($courseformat)) { + if (activity::activitymetaused($courseformat)) { $courseid = $this->mod->course; - if (\format_topcoll\activity::maxstudentsnotexceeded($courseid)) { + if (activity::maxstudentsnotexceeded($courseid)) { $settingname = 'coursesectionactivityfurtherinformation' . $this->mod->modname; $setting = get_config('format_topcoll', $settingname); if ((!empty($setting)) && ($setting == 2)) { @@ -76,9 +82,9 @@ public function export_for_template(\renderer_base $output): \stdClass { * @param cm_info $mod The module. * @return string The markup. */ - protected function course_section_cm_get_meta(\cm_info $mod) { + protected function course_section_cm_get_meta(cm_info $mod) { $courseid = $mod->course; - if (is_guest(\context_course::instance($courseid))) { + if (is_guest(context_course::instance($courseid))) { return ''; } @@ -88,7 +94,7 @@ protected function course_section_cm_get_meta(\cm_info $mod) { } // Do we have an activity function for this module for returning meta data? - $meta = \format_topcoll\activity::module_meta($mod); + $meta = activity::module_meta($mod); if ($meta == null) { // Can't get meta data for this module. return ''; @@ -147,7 +153,7 @@ protected function course_section_cm_get_meta(\cm_info $mod) { 'linkclass' => 'ct-activity-action', 'linkicon' => $OUTPUT->pix_icon('docs', get_string('info')), 'linktext' => implode(', ', $engagementmeta), - 'linkurl' => new \moodle_url("/mod/{$mod->modname}/{$file}.php", $params), + 'linkurl' => new url("/mod/{$mod->modname}/{$file}.php", $params), 'type' => 'engagement', ]; $content = $OUTPUT->render_from_template('format_topcoll/sectioncmmeta', $sectioncmmetacontext); @@ -156,9 +162,9 @@ protected function course_section_cm_get_meta(\cm_info $mod) { // Feedback meta. if (!empty($meta->grade)) { if (in_array($mod->modname, ['quiz', 'assign'])) { - $url = new \moodle_url('/mod/' . $mod->modname . '/view.php?id=' . $mod->id); + $url = new url('/mod/' . $mod->modname . '/view.php?id=' . $mod->id); } else { - $url = new \moodle_url('/grade/report/user/index.php', ['id' => $courseid]); + $url = new url('/grade/report/user/index.php', ['id' => $courseid]); } $sectioncmmetacontext = [ diff --git a/classes/output/courseformat/content/section/cmitem.php b/classes/output/courseformat/content/section/cmitem.php index f4fc839..119c5f0 100644 --- a/classes/output/courseformat/content/section/cmitem.php +++ b/classes/output/courseformat/content/section/cmitem.php @@ -26,6 +26,9 @@ namespace format_topcoll\output\courseformat\content\section; +use core\output\renderer_base; +use stdClass; + /** * Class to render a section activity item. * @@ -37,10 +40,10 @@ class cmitem extends \core_courseformat\output\local\content\section\cmitem { /** * Get the name of the template to use for this templatable. * - * @param \renderer_base $renderer The renderer requesting the template name + * @param renderer_base $renderer The renderer requesting the template name * @return string */ - public function get_template_name(\renderer_base $renderer): string { + public function get_template_name(renderer_base $renderer): string { return 'format_topcoll/local/content/section/cmitem'; } @@ -50,7 +53,7 @@ public function get_template_name(\renderer_base $renderer): string { * @param renderer_base $output typically, the renderer that's calling this function * @return stdClass data context for a mustache template */ - public function export_for_template(\renderer_base $output): \stdClass { + public function export_for_template(renderer_base $output): stdClass { $context = parent::export_for_template($output); $tcsettings = $this->format->get_settings(); diff --git a/classes/output/courseformat/content/section/cmlist.php b/classes/output/courseformat/content/section/cmlist.php index 50ede31..149b7d2 100644 --- a/classes/output/courseformat/content/section/cmlist.php +++ b/classes/output/courseformat/content/section/cmlist.php @@ -26,6 +26,9 @@ namespace format_topcoll\output\courseformat\content\section; +use core\output\renderer_base; +use stdClass; + /** * Class to render a section activity list. * @@ -37,10 +40,10 @@ class cmlist extends \core_courseformat\output\local\content\section\cmlist { /** * Get the name of the template to use for this templatable. * - * @param \renderer_base $renderer The renderer requesting the template name + * @param renderer_base $renderer The renderer requesting the template name * @return string */ - public function get_template_name(\renderer_base $renderer): string { + public function get_template_name(renderer_base $renderer): string { return 'format_topcoll/local/content/section/cmlist'; } @@ -50,7 +53,7 @@ public function get_template_name(\renderer_base $renderer): string { * @param renderer_base $output typically, the renderer that's calling this function * @return array data context for a mustache template */ - public function export_for_template(\renderer_base $output): \stdClass { + public function export_for_template(renderer_base $output): stdClass { global $PAGE; $data = parent::export_for_template($output); $data->editing = $PAGE->user_is_editing(); diff --git a/classes/output/courseformat/content/section/controlmenu.php b/classes/output/courseformat/content/section/controlmenu.php index 1fc0946..467b902 100644 --- a/classes/output/courseformat/content/section/controlmenu.php +++ b/classes/output/courseformat/content/section/controlmenu.php @@ -28,10 +28,11 @@ use core_courseformat\output\local\content\section\controlmenu as controlmenu_base; use context_course; -use moodle_url; -use pix_icon; -use action_menu_link_secondary; -use action_menu; +use core\output\action_menu; +use core\output\action_menu\link_secondary; +use core\output\renderer_base; +use core\output\pix_icon; +use core\url; use stdClass; /** @@ -54,7 +55,7 @@ class controlmenu extends controlmenu_base { * @param renderer_base $output typically, the renderer that's calling this function * @return array data context for a mustache template */ - public function export_for_template(\renderer_base $output): stdClass { + public function export_for_template(renderer_base $output): stdClass { $section = $this->section; $controls = $this->section_control_items(); @@ -74,8 +75,8 @@ public function export_for_template(\renderer_base $output): stdClass { $name = empty($value['name']) ? '' : $value['name']; $attr = empty($value['attr']) ? [] : $value['attr']; $class = empty($value['pixattr']['class']) ? '' : $value['pixattr']['class']; - $al = new action_menu_link_secondary( - new moodle_url($url), + $al = new link_secondary( + new url($url), new pix_icon($icon, '', null, ['class' => "smallicon " . $class]), $name, $attr diff --git a/classes/output/courseformat/content/sectionnavigation.php b/classes/output/courseformat/content/sectionnavigation.php index 4c0c1bc..2a5857d 100644 --- a/classes/output/courseformat/content/sectionnavigation.php +++ b/classes/output/courseformat/content/sectionnavigation.php @@ -28,6 +28,10 @@ namespace format_topcoll\output\courseformat\content; +use context_course; +use core\output\renderer_base; +use stdClass; + /** * Base class to render a course add section navigation. */ @@ -41,7 +45,7 @@ class sectionnavigation extends \core_courseformat\output\local\content\sectionn * @param renderer_base $output typically, the renderer that's calling this function * @return stdClass data context for a mustache template */ - public function export_for_template(\renderer_base $output): \stdClass { + public function export_for_template(renderer_base $output): stdClass { global $USER; if ($this->data !== null) { @@ -50,7 +54,7 @@ public function export_for_template(\renderer_base $output): \stdClass { $format = $this->format; $course = $format->get_course(); - $context = \context_course::instance($course->id); + $context = context_course::instance($course->id); $modinfo = $this->format->get_modinfo(); $sections = $modinfo->get_section_info_all(); @@ -101,10 +105,10 @@ public function export_for_template(\renderer_base $output): \stdClass { /** * Get the name of the template to use for this templatable. * - * @param \renderer_base $renderer The renderer requesting the template name + * @param renderer_base $renderer The renderer requesting the template name * @return string */ - public function get_template_name(\renderer_base $renderer): string { + public function get_template_name(renderer_base $renderer): string { return 'format_topcoll/local/content/sectionnavigation'; } } diff --git a/classes/output/courseformat/content/sectionselector.php b/classes/output/courseformat/content/sectionselector.php index 4d42b85..6e5e1d0 100644 --- a/classes/output/courseformat/content/sectionselector.php +++ b/classes/output/courseformat/content/sectionselector.php @@ -28,6 +28,10 @@ namespace format_topcoll\output\courseformat\content; +use core\output\renderer_base; +use core\output\url_select; +use stdClass; + /** * Represents the section selector. */ @@ -35,10 +39,10 @@ class sectionselector extends \core_courseformat\output\local\content\sectionsel /** * Get the name of the template to use for this templatable. * - * @param \renderer_base $renderer The renderer requesting the template name + * @param renderer_base $renderer The renderer requesting the template name * @return string */ - public function get_template_name(\renderer_base $renderer): string { + public function get_template_name(renderer_base $renderer): string { return 'format_topcoll/local/content/sectionselector'; } @@ -48,7 +52,7 @@ public function get_template_name(\renderer_base $renderer): string { * @param renderer_base $output typically, the renderer that's calling this function * @return stdClass data context for a mustache template */ - public function export_for_template(\renderer_base $output): \stdClass { + public function export_for_template(renderer_base $output): stdClass { $format = $this->format; $course = $format->get_course(); @@ -73,7 +77,7 @@ public function export_for_template(\renderer_base $output): \stdClass { $section++; } - $select = new \url_select($sectionmenu, '', ['' => get_string('jumpto')]); + $select = new url_select($sectionmenu, '', ['' => get_string('jumpto')]); $select->class = 'jumpmenu'; $select->formid = 'sectionmenu'; diff --git a/classes/output/renderer.php b/classes/output/renderer.php index 3a5f63e..c572615 100644 --- a/classes/output/renderer.php +++ b/classes/output/renderer.php @@ -33,12 +33,18 @@ defined('MOODLE_INTERNAL') || die(); -use context_course; use core_courseformat\base as course_format; +use context_course; +use core\output\html_writer; +use core\url; use core_courseformat\output\section_renderer; -use html_writer; -use moodle_url; +use core_useragent; +use format_topcoll\togglelib; +use format_topcoll\toolbox; +use moodle_exception; +use moodle_page; use section_info; +use stdClass; require_once($CFG->dirroot . '/course/format/lib.php'); // For course_get_format. @@ -89,9 +95,9 @@ class renderer extends section_renderer { * @param moodle_page $page The page. * @param string $target One of rendering target constants. */ - public function __construct(\moodle_page $page, $target) { + public function __construct(moodle_page $page, $target) { parent::__construct($page, $target); - $this->togglelib = new \format_topcoll\togglelib(); + $this->togglelib = new togglelib(); $this->courseformat = course_get_format($page->course); // Needed for collapsed topics settings retrieval. $this->course = $this->courseformat->get_course(); @@ -108,7 +114,7 @@ public function __construct(\moodle_page $page, $target) { $this->rtl = right_to_left(); // Portable. - $devicetype = \core_useragent::get_device_type(); // In /lib/classes/useragent.php. + $devicetype = core_useragent::get_device_type(); // In /lib/classes/useragent.php. if ($devicetype == "mobile") { $this->mobiletheme = true; } else if ($devicetype == "tablet") { @@ -267,7 +273,7 @@ protected function section_right_content($section, $course, $onsectionpage, $sec if (empty($this->tcsettings)) { $this->tcsettings = $this->courseformat->get_settings(); } - $url = new moodle_url('/course/view.php', ['id' => $course->id, 'section' => $section->section]); + $url = new url('/course/view.php', ['id' => $course->id, 'section' => $section->section]); // Get the specific words from the language files. $topictext = null; if (($this->tcsettings['layoutstructure'] == 1) || ($this->tcsettings['layoutstructure'] == 4)) { @@ -529,7 +535,7 @@ protected function topcoll_section($section, $course, $onsectionpage, $sectionre if ($this->userisediting && has_capability('moodle/course:update', $context)) { $sectioncontext['usereditingicon'] = $this->output->pix_icon('t/edit', get_string('edit')); - $sectioncontext['usereditingurl'] = new moodle_url( + $sectioncontext['usereditingurl'] = new url( '/course/editsection.php', ['id' => $section->id, 'sr' => $sectionreturn] ); @@ -721,11 +727,11 @@ public function single_section_page($displaysection) { if (!($thissection = $modinfo->get_section_info($displaysection))) { /* This section doesn't exist or is not available for the user. We actually already check this in course/view.php but just in case exit from this function as well. */ - print_error( + throw new moodle_exception( 'unknowncoursesection', 'error', course_get_url($course), - format_string($course->fullname) + format_string($course->fullname. ' - id='.$course->id) ); } @@ -739,7 +745,7 @@ public function single_section_page($displaysection) { $singlesectioncontext = [ 'maincoursepageicon' => $this->output->pix_icon('t/less', $maincoursepage), 'maincoursepagestr' => $maincoursepage, - 'maincoursepageurl' => new moodle_url('/course/view.php', ['id' => $course->id]), + 'maincoursepageurl' => new url('/course/view.php', ['id' => $course->id]), 'sectionnavlinks' => $this->section_nav_links(), // Title with section navigation links and jump to menu. 'sectionnavselection' => $this->section_nav_selection($course, null, $displaysection), @@ -884,7 +890,7 @@ public function multiple_section_page() { $nextweekdate = $weekdate - ($weekofseconds); } $thissection = $modinfo->get_section_info($section); - $extrasectioninfo[$thissection->id] = new \stdClass(); + $extrasectioninfo[$thissection->id] = new stdClass(); /* Show the section if the user is permitted to access it, OR if it's not available but there is some available info text which explains the reason & should display. */ @@ -1101,7 +1107,7 @@ public function multiple_section_page() { $this->userisediting, ]); /* Make sure the database has the correct state of the toggles if changed by the code. This ensures that a no-change page reload is correct. */ - set_user_preference(\format_topcoll\togglelib::TOPCOLL_TOGGLE.'_' . $course->id, $toggles); + set_user_preference(togglelib::TOPCOLL_TOGGLE.'_' . $course->id, $toggles); return $content; } @@ -1162,11 +1168,11 @@ protected function course_styles() { } $coursestylescontext = []; - $coursestylescontext['togglebackground'] = \format_topcoll\toolbox::hex2rgba( + $coursestylescontext['togglebackground'] = toolbox::hex2rgba( $this->tcsettings['togglebackgroundcolour'], $this->tcsettings['togglebackgroundopacity'] ); - $coursestylescontext['toggleforegroundcolour'] = \format_topcoll\toolbox::hex2rgba( + $coursestylescontext['toggleforegroundcolour'] = toolbox::hex2rgba( $this->tcsettings['toggleforegroundcolour'], $this->tcsettings['toggleforegroundopacity'] ); @@ -1202,11 +1208,11 @@ protected function course_styles() { $coursestylescontext['toggleiconposition'] = 'left'; } } - $coursestylescontext['toggleforegroundhovercolour'] = \format_topcoll\toolbox::hex2rgba( + $coursestylescontext['toggleforegroundhovercolour'] = toolbox::hex2rgba( $this->tcsettings['toggleforegroundhovercolour'], $this->tcsettings['toggleforegroundhoveropacity'] ); - $coursestylescontext['togglebackgroundhovercolour'] = \format_topcoll\toolbox::hex2rgba( + $coursestylescontext['togglebackgroundhovercolour'] = toolbox::hex2rgba( $this->tcsettings['togglebackgroundhovercolour'], $this->tcsettings['togglebackgroundhoveropacity'] ); @@ -1267,8 +1273,8 @@ protected function set_user_preferences() { if ($this->defaulttogglepersistence == 1) { global $USER; - $USER->topcoll_user_pref[\format_topcoll\togglelib::TOPCOLL_TOGGLE.'_' . $this->course->id] = PARAM_RAW; - $userpreference = get_user_preferences(\format_topcoll\togglelib::TOPCOLL_TOGGLE.'_' . $this->course->id); + $USER->topcoll_user_pref[togglelib::TOPCOLL_TOGGLE.'_' . $this->course->id] = PARAM_RAW; + $userpreference = get_user_preferences(togglelib::TOPCOLL_TOGGLE.'_' . $this->course->id); } else { $userpreference = null; } diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 7cebbbc..d7e9d49 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -28,6 +28,7 @@ use core_privacy\local\request\writer; use core_privacy\local\metadata\collection; +use format_topcoll\togglelib; /** * Implementation of the privacy subsystem plugin provider. @@ -45,7 +46,7 @@ class provider implements * @return collection A listing of user data stored through this system. */ public static function get_metadata(collection $items): collection { - $items->add_user_preference(\format_topcoll\togglelib::TOPCOLL_TOGGLE, 'privacy:metadata:preference:toggle'); + $items->add_user_preference(togglelib::TOPCOLL_TOGGLE, 'privacy:metadata:preference:toggle'); return $items; } @@ -57,11 +58,11 @@ public static function get_metadata(collection $items): collection { */ public static function export_user_preferences(int $userid) { $preferences = get_user_preferences(null, null, $userid); - $togglelib = new \format_topcoll\togglelib(); + $togglelib = new togglelib(); foreach ($preferences as $name => $value) { $courseid = null; - if (strpos($name, \format_topcoll\togglelib::TOPCOLL_TOGGLE) === 0) { - $courseid = substr($name, strlen(\format_topcoll\togglelib::TOPCOLL_TOGGLE) + 1); + if (strpos($name, togglelib::TOPCOLL_TOGGLE) === 0) { + $courseid = substr($name, strlen(togglelib::TOPCOLL_TOGGLE) + 1); writer::export_user_preference( 'format_topcoll', diff --git a/db/events.php b/db/events.php index 34f22a4..1fcc195 100644 --- a/db/events.php +++ b/db/events.php @@ -35,42 +35,42 @@ $observers = [ [ 'eventname' => '\core\event\course_content_deleted', - 'callback' => 'format_topcoll_observer::course_content_deleted', + 'callback' => '\format_topcoll\observer::course_content_deleted', ], [ 'eventname' => '\core\event\role_allow_view_updated', - 'callback' => 'format_topcoll_observer::role_allow_view_updated', + 'callback' => '\format_topcoll\observer::role_allow_view_updated', ], [ 'eventname' => '\core\event\role_updated', - 'callback' => 'format_topcoll_observer::role_updated', + 'callback' => '\format_topcoll\observer::role_updated', ], [ 'eventname' => '\core\event\role_deleted', - 'callback' => 'format_topcoll_observer::role_deleted', + 'callback' => '\format_topcoll\observer::role_deleted', ], [ 'eventname' => '\core\event\user_enrolment_created', - 'callback' => 'format_topcoll_observer::user_enrolment_created', + 'callback' => '\format_topcoll\observer::user_enrolment_created', ], [ 'eventname' => '\core\event\user_enrolment_updated', - 'callback' => 'format_topcoll_observer::user_enrolment_updated', + 'callback' => '\format_topcoll\observer::user_enrolment_updated', ], [ 'eventname' => '\core\event\user_enrolment_deleted', - 'callback' => 'format_topcoll_observer::user_enrolment_deleted', + 'callback' => '\format_topcoll\observer::user_enrolment_deleted', ], [ 'eventname' => '\core\event\course_module_created', - 'callback' => 'format_topcoll_observer::course_module_created', + 'callback' => '\format_topcoll\observer::course_module_created', ], [ 'eventname' => '\core\event\course_module_updated', - 'callback' => 'format_topcoll_observer::course_module_updated', + 'callback' => '\format_topcoll\observer::course_module_updated', ], [ 'eventname' => '\core\event\course_module_deleted', - 'callback' => 'format_topcoll_observer::course_module_deleted', + 'callback' => '\format_topcoll\observer::course_module_deleted', ], ]; diff --git a/lib.php b/lib.php index f53b6b3..a3e6ce8 100644 --- a/lib.php +++ b/lib.php @@ -1887,12 +1887,12 @@ public function can_delete_section($section) { /** * Prepares the templateable object to display section name. * - * @param \section_info|\stdClass $section + * @param section_info|stdClass $section * @param bool $linkifneeded * @param bool $editable * @param null|lang_string|string $edithint * @param null|lang_string|string $editlabel - * @return \core\output\inplace_editable + * @return core\output\inplace_editable */ public function inplace_editable_render_section_name( $section, diff --git a/templates/local/content/addsection.mustache b/templates/local/content/addsection.mustache new file mode 100644 index 0000000..e00679e --- /dev/null +++ b/templates/local/content/addsection.mustache @@ -0,0 +1,74 @@ +{{! + 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 format_topcoll/local/content/addsection + + Displays the add section button inside a course. + + Example context (json): + { + "showaddsection": true, + "id": 42, + "insertafter": true, + "num": 0, + "increase": { + "url": "#" + }, + "decrease": { + "url": "#" + }, + "addsections": { + "url": "#", + "title": "Add section", + "newsection": 3 + } + } +}} +{{#showaddsection}} +
+ {{#increase}} + + {{#pix}}t/switch_plus, moodle, {{#str}} increasesections, moodle {{/str}}{{/pix}} + + {{/increase}} + {{#addsections}} + + {{#pix}} t/add, core {{/pix}} + {{title}} + + {{/addsections}} +
+
+ {{#pix}}t/block, moodle{{/pix}} +
+
+ {{#str}}maxsectionaddmessage, core_courseformat{{/str}} +
+
+ {{#decrease}} + + {{#pix}}t/switch_minus, moodle, {{#str}} reducesections, moodle {{/str}}{{/pix}} + + {{/decrease}} +
+{{/showaddsection}} diff --git a/templates/local/content/cm.mustache b/templates/local/content/cm.mustache index 3706753..83101da 100644 --- a/templates/local/content/cm.mustache +++ b/templates/local/content/cm.mustache @@ -21,9 +21,11 @@ See core file for example context. CT adds 'cmmeta' to this, this is generated markup if any. }} {{#editing}} -
- {{> core_course/activitychooserbuttonactivity}} -
+ {{< core_courseformat/local/content/divider}} + {{$content}} + {{#activitychooserbutton}}{{> core_course/activitychooserbutton}}{{/activitychooserbutton}} + {{/content}} + {{/ core_courseformat/local/content/divider}} {{/editing}}