From 43d1898085bafc62b3b7a387447cf0260bffacf1 Mon Sep 17 00:00:00 2001 From: Laurent David Date: Thu, 22 Aug 2024 12:34:00 +0200 Subject: [PATCH 1/2] MDL-82407 core_courseformat: Fix course index for subsections * Subsection should be exported in the course index in the right order with all submodules. --- .../build/local/courseeditor/exporter.min.js | 2 +- .../local/courseeditor/exporter.min.js.map | 2 +- .../amd/src/local/courseeditor/exporter.js | 62 +++++++++++++++++-- theme/boost/scss/moodle/courseindex.scss | 21 ++++--- theme/boost/style/moodle.css | 33 +++++++--- theme/classic/style/moodle.css | 33 +++++++--- 6 files changed, 120 insertions(+), 33 deletions(-) diff --git a/course/format/amd/build/local/courseeditor/exporter.min.js b/course/format/amd/build/local/courseeditor/exporter.min.js index 781cd247e18..f6a62d56063 100644 --- a/course/format/amd/build/local/courseeditor/exporter.min.js +++ b/course/format/amd/build/local/courseeditor/exporter.min.js @@ -8,6 +8,6 @@ define("core_courseformat/local/courseeditor/exporter",["exports"],(function(_ex * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class{constructor(reactive){this.reactive=reactive,this.COMPLETIONS=["incomplete","complete","complete","fail"]}course(state){var _state$course$highlig;const data={sections:[],editmode:this.reactive.isEditing,highlighted:null!==(_state$course$highlig=state.course.highlighted)&&void 0!==_state$course$highlig?_state$course$highlig:""};return this.listedSectionIds(state).forEach((sectionid=>{var _state$section$get;const sectioninfo=null!==(_state$section$get=state.section.get(sectionid))&&void 0!==_state$section$get?_state$section$get:{},section=this.section(state,sectioninfo);data.sections.push(section)})),data.hassections=0!=data.sections.length,data}listedSectionIds(state){var _state$course$section;return(null!==(_state$course$section=state.course.sectionlist)&&void 0!==_state$course$section?_state$course$section:[]).filter((sectionid=>{var _state$section$get2;return null===(null!==(_state$section$get2=state.section.get(sectionid))&&void 0!==_state$section$get2?_state$section$get2:{}).component}))}section(state,sectioninfo){var _state$course$highlig2,_sectioninfo$cmlist;const section={...sectioninfo,highlighted:null!==(_state$course$highlig2=state.course.highlighted)&&void 0!==_state$course$highlig2?_state$course$highlig2:"",cms:[]};return(null!==(_sectioninfo$cmlist=sectioninfo.cmlist)&&void 0!==_sectioninfo$cmlist?_sectioninfo$cmlist:[]).forEach((cmid=>{const cminfo=state.cm.get(cmid),cm=this.cm(state,cminfo);section.cms.push(cm)})),section.hascms=0!=section.cms.length,section}cm(state,cminfo){const cm={...cminfo,isactive:!1,sectioninfo:!1};if(cminfo.hasdelegatedsection){const sectioninfo=state.section.get(cminfo.delegatesectionid);cm.sectioninfo=this.section(state,sectioninfo)}return cm}cmDraggableData(state,cmid){const cminfo=state.cm.get(cmid);if(!cminfo)return null;let nextcmid;const section=state.section.get(cminfo.sectionid),currentindex=null==section?void 0:section.cmlist.indexOf(cminfo.id);return void 0!==currentindex&&(nextcmid=null==section?void 0:section.cmlist[currentindex+1]),{type:"cm",id:cminfo.id,name:cminfo.name,sectionid:cminfo.sectionid,hasdelegatedsection:cminfo.hasdelegatedsection,nextcmid:nextcmid}}sectionDraggableData(state,sectionid){const sectioninfo=state.section.get(sectionid);return sectioninfo?{type:"section",id:sectioninfo.id,name:sectioninfo.name,number:sectioninfo.number}:null}fileDraggableData(state,dataTransfer){var _dataTransfer$files;const files=[];return(null===(_dataTransfer$files=dataTransfer.files)||void 0===_dataTransfer$files?void 0:_dataTransfer$files.length)>0&&dataTransfer.files.forEach((file=>{files.push(file)})),{type:"files",files:files}}cmCompletion(state,cminfo){const data={statename:"",state:"NaN"};if(void 0!==cminfo.completionstate){var _this$COMPLETIONS$cmi;data.state=cminfo.completionstate,data.hasstate=!0;let statename=null!==(_this$COMPLETIONS$cmi=this.COMPLETIONS[cminfo.completionstate])&&void 0!==_this$COMPLETIONS$cmi?_this$COMPLETIONS$cmi:"NaN";void 0!==cminfo.isoverallcomplete&&!0===cminfo.isoverallcomplete&&(statename="complete"),data["is".concat(statename)]=!0}return data}allItemsArray(state){var _state$course$section2;const items=[];return(null!==(_state$course$section2=state.course.sectionlist)&&void 0!==_state$course$section2?_state$course$section2:[]).forEach((sectionid=>{var _sectioninfo$cmlist2;const sectioninfo=state.section.get(sectionid);items.push({type:"section",id:sectioninfo.id,url:sectioninfo.sectionurl});(null!==(_sectioninfo$cmlist2=sectioninfo.cmlist)&&void 0!==_sectioninfo$cmlist2?_sectioninfo$cmlist2:[]).forEach((cmid=>{const cminfo=state.cm.get(cmid);items.push({type:"cm",id:cminfo.id,url:cminfo.url})}))})),items}canUseStealth(state,cmIds){return cmIds.some((cmId=>{var _cminfo$allowstealth;const cminfo=state.cm.get(cmId);return null!==(_cminfo$allowstealth=null==cminfo?void 0:cminfo.allowstealth)&&void 0!==_cminfo$allowstealth&&_cminfo$allowstealth}))}},_exports.default})); +class{constructor(reactive){this.reactive=reactive,this.COMPLETIONS=["incomplete","complete","complete","fail"]}course(state){var _state$course$highlig;const data={sections:[],editmode:this.reactive.isEditing,highlighted:null!==(_state$course$highlig=state.course.highlighted)&&void 0!==_state$course$highlig?_state$course$highlig:""};return this.listedSectionIds(state).forEach((sectionid=>{var _state$section$get;const sectioninfo=null!==(_state$section$get=state.section.get(sectionid))&&void 0!==_state$section$get?_state$section$get:{},section=this.section(state,sectioninfo);data.sections.push(section)})),data.hassections=0!=data.sections.length,data}listedSectionIds(state){var _state$course$section;return(null!==(_state$course$section=state.course.sectionlist)&&void 0!==_state$course$section?_state$course$section:[]).filter((sectionid=>{var _state$section$get2;return null===(null!==(_state$section$get2=state.section.get(sectionid))&&void 0!==_state$section$get2?_state$section$get2:{}).component}))}section(state,sectioninfo){var _state$course$highlig2,_sectioninfo$cmlist;const section={...sectioninfo,highlighted:null!==(_state$course$highlig2=state.course.highlighted)&&void 0!==_state$course$highlig2?_state$course$highlig2:"",cms:[]};return(null!==(_sectioninfo$cmlist=sectioninfo.cmlist)&&void 0!==_sectioninfo$cmlist?_sectioninfo$cmlist:[]).forEach((cmid=>{const cminfo=state.cm.get(cmid),cm=this.cm(state,cminfo);section.cms.push(cm)})),section.hascms=0!=section.cms.length,section}cm(state,cminfo){const cm={...cminfo,isactive:!1,sectioninfo:!1};if(cminfo.hasdelegatedsection){const sectioninfo=state.section.get(cminfo.delegatesectionid);cm.sectioninfo=this.section(state,sectioninfo)}return cm}cmDraggableData(state,cmid){const cminfo=state.cm.get(cmid);if(!cminfo)return null;let nextcmid;const section=state.section.get(cminfo.sectionid),currentindex=null==section?void 0:section.cmlist.indexOf(cminfo.id);return void 0!==currentindex&&(nextcmid=null==section?void 0:section.cmlist[currentindex+1]),{type:"cm",id:cminfo.id,name:cminfo.name,sectionid:cminfo.sectionid,hasdelegatedsection:cminfo.hasdelegatedsection,nextcmid:nextcmid}}sectionDraggableData(state,sectionid){const sectioninfo=state.section.get(sectionid);return sectioninfo?{type:"section",id:sectioninfo.id,name:sectioninfo.name,number:sectioninfo.number}:null}fileDraggableData(state,dataTransfer){var _dataTransfer$files;const files=[];return(null===(_dataTransfer$files=dataTransfer.files)||void 0===_dataTransfer$files?void 0:_dataTransfer$files.length)>0&&dataTransfer.files.forEach((file=>{files.push(file)})),{type:"files",files:files}}cmCompletion(state,cminfo){const data={statename:"",state:"NaN"};if(void 0!==cminfo.completionstate){var _this$COMPLETIONS$cmi;data.state=cminfo.completionstate,data.hasstate=!0;let statename=null!==(_this$COMPLETIONS$cmi=this.COMPLETIONS[cminfo.completionstate])&&void 0!==_this$COMPLETIONS$cmi?_this$COMPLETIONS$cmi:"NaN";void 0!==cminfo.isoverallcomplete&&!0===cminfo.isoverallcomplete&&(statename="complete"),data["is".concat(statename)]=!0}return data}allItemsArray(state){var _state$course$section2;const items=[];return(null!==(_state$course$section2=state.course.sectionlist)&&void 0!==_state$course$section2?_state$course$section2:[]).forEach((sectionid=>{var _sectioninfo$cmlist2;const sectioninfo=state.section.get(sectionid);if(null!==sectioninfo.component)return;items.push({type:"section",id:sectioninfo.id,url:sectioninfo.sectionurl});(null!==(_sectioninfo$cmlist2=sectioninfo.cmlist)&&void 0!==_sectioninfo$cmlist2?_sectioninfo$cmlist2:[]).forEach((cmid=>{const cmInfo=state.cm.get(cmid);items.push(...this.cmItemsArray(state,cmInfo))}))})),items}cmItemsArray(state,cmInfo){if(cmInfo.hasdelegatedsection){var _delegatedsection$cml;const items=[],delegatedsection=state.section.get(cmInfo.delegatesectionid);items.push({type:"section",id:delegatedsection.id,url:delegatedsection.sectionurl});return(null!==(_delegatedsection$cml=delegatedsection.cmlist)&&void 0!==_delegatedsection$cml?_delegatedsection$cml:[]).forEach((cmid=>{const cmInfo=state.cm.get(cmid);items.push({type:"cm",id:cmInfo.id,url:cmInfo.url})})),items}return[{type:"cm",id:cmInfo.id,url:cmInfo.url}]}canUseStealth(state,cmIds){return cmIds.some((cmId=>{var _cminfo$allowstealth;const cminfo=state.cm.get(cmId);return null!==(_cminfo$allowstealth=null==cminfo?void 0:cminfo.allowstealth)&&void 0!==_cminfo$allowstealth&&_cminfo$allowstealth}))}},_exports.default})); //# sourceMappingURL=exporter.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/exporter.min.js.map b/course/format/amd/build/local/courseeditor/exporter.min.js.map index 0b9fd79c4ab..6c9c78ecd0e 100644 --- a/course/format/amd/build/local/courseeditor/exporter.min.js.map +++ b/course/format/amd/build/local/courseeditor/exporter.min.js.map @@ -1 +1 @@ -{"version":3,"file":"exporter.min.js","sources":["../../../src/local/courseeditor/exporter.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 * Module to export parts of the state and transform them to be used in templates\n * and as draggable data.\n *\n * @module core_courseformat/local/courseeditor/exporter\n * @class core_courseformat/local/courseeditor/exporter\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n\n /**\n * Class constructor.\n *\n * @param {CourseEditor} reactive the course editor object\n */\n constructor(reactive) {\n this.reactive = reactive;\n\n // Completions states are defined in lib/completionlib.php. There are 4 different completion\n // state values, however, the course index uses the same state for complete and complete_pass.\n // This is the reason why completed appears twice in the array.\n this.COMPLETIONS = ['incomplete', 'complete', 'complete', 'fail'];\n }\n\n /**\n * Generate the course export data from the state.\n *\n * @param {Object} state the current state.\n * @returns {Object}\n */\n course(state) {\n // Collect section information from the state.\n const data = {\n sections: [],\n editmode: this.reactive.isEditing,\n highlighted: state.course.highlighted ?? '',\n };\n const sectionlist = this.listedSectionIds(state);\n sectionlist.forEach(sectionid => {\n const sectioninfo = state.section.get(sectionid) ?? {};\n const section = this.section(state, sectioninfo);\n data.sections.push(section);\n });\n data.hassections = (data.sections.length != 0);\n\n return data;\n }\n\n /**\n * Get the IDs of the sections that are listed as regular sections.\n * @param {Object} state the current state.\n * @returns {Number[]} the list of section ids that are listed.\n */\n listedSectionIds(state) {\n const fullSectionList = state.course.sectionlist ?? [];\n return fullSectionList.filter(sectionid => {\n const sectioninfo = state.section.get(sectionid) ?? {};\n // Delegated sections (controlled by a component) are not listed in course.\n return sectioninfo.component === null;\n });\n }\n\n /**\n * Generate a section export data from the state.\n *\n * @param {Object} state the current state.\n * @param {Object} sectioninfo the section state data.\n * @returns {Object}\n */\n section(state, sectioninfo) {\n const section = {\n ...sectioninfo,\n highlighted: state.course.highlighted ?? '',\n cms: [],\n };\n const cmlist = sectioninfo.cmlist ?? [];\n cmlist.forEach(cmid => {\n const cminfo = state.cm.get(cmid);\n const cm = this.cm(state, cminfo);\n section.cms.push(cm);\n });\n section.hascms = (section.cms.length != 0);\n\n return section;\n }\n\n /**\n * Generate a cm export data from the state.\n *\n * @param {Object} state the current state.\n * @param {Object} cminfo the course module state data.\n * @returns {Object}\n */\n cm(state, cminfo) {\n const cm = {\n ...cminfo,\n isactive: false,\n sectioninfo: false, // Init to false to prevent mustache recursion loops.\n };\n if (cminfo.hasdelegatedsection) {\n const sectioninfo = state.section.get(cminfo.delegatesectionid);\n cm.sectioninfo = this.section(state, sectioninfo);\n }\n return cm;\n }\n\n /**\n * Generate a dragable cm data structure.\n *\n * This method is used by any draggable course module element to generate drop data\n * for its reactive/dragdrop instance.\n *\n * @param {*} state the state object\n * @param {*} cmid the cours emodule id\n * @returns {Object|null}\n */\n cmDraggableData(state, cmid) {\n const cminfo = state.cm.get(cmid);\n if (!cminfo) {\n return null;\n }\n\n // Drop an activity over the next activity is the same as doing anything.\n let nextcmid;\n const section = state.section.get(cminfo.sectionid);\n const currentindex = section?.cmlist.indexOf(cminfo.id);\n if (currentindex !== undefined) {\n nextcmid = section?.cmlist[currentindex + 1];\n }\n\n return {\n type: 'cm',\n id: cminfo.id,\n name: cminfo.name,\n sectionid: cminfo.sectionid,\n hasdelegatedsection: cminfo.hasdelegatedsection,\n nextcmid,\n };\n }\n\n /**\n * Generate a dragable cm data structure.\n *\n * This method is used by any draggable section element to generate drop data\n * for its reactive/dragdrop instance.\n *\n * @param {*} state the state object\n * @param {*} sectionid the cours section id\n * @returns {Object|null}\n */\n sectionDraggableData(state, sectionid) {\n const sectioninfo = state.section.get(sectionid);\n if (!sectioninfo) {\n return null;\n }\n return {\n type: 'section',\n id: sectioninfo.id,\n name: sectioninfo.name,\n number: sectioninfo.number,\n };\n }\n\n /**\n * Generate a file draggable structure.\n *\n * This method is used when files are dragged on the browser.\n *\n * @param {*} state the state object\n * @param {*} dataTransfer the current data tranfer data\n * @returns {Object|null}\n */\n fileDraggableData(state, dataTransfer) {\n const files = [];\n // Browsers do not provide the file list until the drop event.\n if (dataTransfer.files?.length > 0) {\n dataTransfer.files.forEach(file => {\n files.push(file);\n });\n }\n return {\n type: 'files',\n files,\n };\n }\n\n /**\n * Generate a completion export data from the cm element.\n *\n * @param {Object} state the current state.\n * @param {Object} cminfo the course module state data.\n * @returns {Object}\n */\n cmCompletion(state, cminfo) {\n const data = {\n statename: '',\n state: 'NaN',\n };\n if (cminfo.completionstate !== undefined) {\n data.state = cminfo.completionstate;\n data.hasstate = true;\n let statename = this.COMPLETIONS[cminfo.completionstate] ?? 'NaN';\n if (cminfo.isoverallcomplete !== undefined && cminfo.isoverallcomplete === true) {\n statename = 'complete';\n }\n data[`is${statename}`] = true;\n }\n return data;\n }\n\n /**\n * Return a sorted list of all sections and cms items in the state.\n *\n * @param {Object} state the current state.\n * @returns {Array} all sections and cms items in the state.\n */\n allItemsArray(state) {\n const items = [];\n const sectionlist = state.course.sectionlist ?? [];\n // Add sections.\n sectionlist.forEach(sectionid => {\n const sectioninfo = state.section.get(sectionid);\n items.push({type: 'section', id: sectioninfo.id, url: sectioninfo.sectionurl});\n // Add cms.\n const cmlist = sectioninfo.cmlist ?? [];\n cmlist.forEach(cmid => {\n const cminfo = state.cm.get(cmid);\n items.push({type: 'cm', id: cminfo.id, url: cminfo.url});\n });\n });\n return items;\n }\n\n /**\n * Check is some activities of a list can be stealth.\n *\n * @param {Object} state the current state.\n * @param {Number[]} cmIds the module ids to check\n * @returns {Boolean} if any of the activities can be stealth.\n */\n canUseStealth(state, cmIds) {\n return cmIds.some(cmId => {\n const cminfo = state.cm.get(cmId);\n return cminfo?.allowstealth ?? false;\n });\n }\n}\n"],"names":["constructor","reactive","COMPLETIONS","course","state","data","sections","editmode","this","isEditing","highlighted","listedSectionIds","forEach","sectionid","sectioninfo","section","get","push","hassections","length","sectionlist","filter","component","cms","cmlist","cmid","cminfo","cm","hascms","isactive","hasdelegatedsection","delegatesectionid","cmDraggableData","nextcmid","currentindex","indexOf","id","undefined","type","name","sectionDraggableData","number","fileDraggableData","dataTransfer","files","file","cmCompletion","statename","completionstate","hasstate","isoverallcomplete","allItemsArray","items","url","sectionurl","canUseStealth","cmIds","some","cmId","allowstealth"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"exporter.min.js","sources":["../../../src/local/courseeditor/exporter.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 * A configuration to provide to the modal.\n *\n * @typedef {Object} courseItem\n *\n * @property {String} type The type of element (section, cm).\n * @property {Number} id Element ID.\n * @property {String} url Element URL.\n */\n\n/**\n * Module to export parts of the state and transform them to be used in templates\n * and as draggable data.\n *\n * @module core_courseformat/local/courseeditor/exporter\n * @class core_courseformat/local/courseeditor/exporter\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n\n /**\n * Class constructor.\n *\n * @param {CourseEditor} reactive the course editor object\n */\n constructor(reactive) {\n this.reactive = reactive;\n\n // Completions states are defined in lib/completionlib.php. There are 4 different completion\n // state values, however, the course index uses the same state for complete and complete_pass.\n // This is the reason why completed appears twice in the array.\n this.COMPLETIONS = ['incomplete', 'complete', 'complete', 'fail'];\n }\n\n /**\n * Generate the course export data from the state.\n *\n * @param {Object} state the current state.\n * @returns {Object}\n */\n course(state) {\n // Collect section information from the state.\n const data = {\n sections: [],\n editmode: this.reactive.isEditing,\n highlighted: state.course.highlighted ?? '',\n };\n const sectionlist = this.listedSectionIds(state);\n sectionlist.forEach(sectionid => {\n const sectioninfo = state.section.get(sectionid) ?? {};\n const section = this.section(state, sectioninfo);\n data.sections.push(section);\n });\n data.hassections = (data.sections.length != 0);\n\n return data;\n }\n\n /**\n * Get the IDs of the sections that are listed as regular sections.\n * @param {Object} state the current state.\n * @returns {Number[]} the list of section ids that are listed.\n */\n listedSectionIds(state) {\n const fullSectionList = state.course.sectionlist ?? [];\n return fullSectionList.filter(sectionid => {\n const sectioninfo = state.section.get(sectionid) ?? {};\n // Delegated sections (controlled by a component) are not listed in course.\n return sectioninfo.component === null;\n });\n }\n\n /**\n * Generate a section export data from the state.\n *\n * @param {Object} state the current state.\n * @param {Object} sectioninfo the section state data.\n * @returns {Object}\n */\n section(state, sectioninfo) {\n const section = {\n ...sectioninfo,\n highlighted: state.course.highlighted ?? '',\n cms: [],\n };\n const cmlist = sectioninfo.cmlist ?? [];\n cmlist.forEach(cmid => {\n const cminfo = state.cm.get(cmid);\n const cm = this.cm(state, cminfo);\n section.cms.push(cm);\n });\n section.hascms = (section.cms.length != 0);\n\n return section;\n }\n\n /**\n * Generate a cm export data from the state.\n *\n * @param {Object} state the current state.\n * @param {Object} cminfo the course module state data.\n * @returns {Object}\n */\n cm(state, cminfo) {\n const cm = {\n ...cminfo,\n isactive: false,\n sectioninfo: false, // Init to false to prevent mustache recursion loops.\n };\n if (cminfo.hasdelegatedsection) {\n const sectioninfo = state.section.get(cminfo.delegatesectionid);\n cm.sectioninfo = this.section(state, sectioninfo);\n }\n return cm;\n }\n\n /**\n * Generate a dragable cm data structure.\n *\n * This method is used by any draggable course module element to generate drop data\n * for its reactive/dragdrop instance.\n *\n * @param {*} state the state object\n * @param {*} cmid the cours emodule id\n * @returns {Object|null}\n */\n cmDraggableData(state, cmid) {\n const cminfo = state.cm.get(cmid);\n if (!cminfo) {\n return null;\n }\n\n // Drop an activity over the next activity is the same as doing anything.\n let nextcmid;\n const section = state.section.get(cminfo.sectionid);\n const currentindex = section?.cmlist.indexOf(cminfo.id);\n if (currentindex !== undefined) {\n nextcmid = section?.cmlist[currentindex + 1];\n }\n\n return {\n type: 'cm',\n id: cminfo.id,\n name: cminfo.name,\n sectionid: cminfo.sectionid,\n hasdelegatedsection: cminfo.hasdelegatedsection,\n nextcmid,\n };\n }\n\n /**\n * Generate a dragable cm data structure.\n *\n * This method is used by any draggable section element to generate drop data\n * for its reactive/dragdrop instance.\n *\n * @param {*} state the state object\n * @param {*} sectionid the cours section id\n * @returns {Object|null}\n */\n sectionDraggableData(state, sectionid) {\n const sectioninfo = state.section.get(sectionid);\n if (!sectioninfo) {\n return null;\n }\n return {\n type: 'section',\n id: sectioninfo.id,\n name: sectioninfo.name,\n number: sectioninfo.number,\n };\n }\n\n /**\n * Generate a file draggable structure.\n *\n * This method is used when files are dragged on the browser.\n *\n * @param {*} state the state object\n * @param {*} dataTransfer the current data tranfer data\n * @returns {Object|null}\n */\n fileDraggableData(state, dataTransfer) {\n const files = [];\n // Browsers do not provide the file list until the drop event.\n if (dataTransfer.files?.length > 0) {\n dataTransfer.files.forEach(file => {\n files.push(file);\n });\n }\n return {\n type: 'files',\n files,\n };\n }\n\n /**\n * Generate a completion export data from the cm element.\n *\n * @param {Object} state the current state.\n * @param {Object} cminfo the course module state data.\n * @returns {Object}\n */\n cmCompletion(state, cminfo) {\n const data = {\n statename: '',\n state: 'NaN',\n };\n if (cminfo.completionstate !== undefined) {\n data.state = cminfo.completionstate;\n data.hasstate = true;\n let statename = this.COMPLETIONS[cminfo.completionstate] ?? 'NaN';\n if (cminfo.isoverallcomplete !== undefined && cminfo.isoverallcomplete === true) {\n statename = 'complete';\n }\n data[`is${statename}`] = true;\n }\n return data;\n }\n\n /**\n * Return a sorted list of all sections and cms items in the state.\n *\n * @param {Object} state the current state.\n * @returns {courseItem[]} all sections and cms items in the state.\n */\n allItemsArray(state) {\n const items = [];\n const sectionlist = state.course.sectionlist ?? [];\n // Add sections.\n sectionlist.forEach(sectionid => {\n const sectioninfo = state.section.get(sectionid);\n // Skip delegated sections because components are responsible for them.\n if (sectioninfo.component !== null) {\n return;\n }\n\n items.push({\n type: 'section',\n id: sectioninfo.id,\n url: sectioninfo.sectionurl\n });\n // Add cms.\n const cmlist = sectioninfo.cmlist ?? [];\n cmlist.forEach(cmid => {\n const cmInfo = state.cm.get(cmid);\n items.push(...this.cmItemsArray(state, cmInfo));\n });\n });\n return items;\n }\n\n /**\n * Return a list of all items associated with an activity.\n *\n * @private\n * @param {Object} state the full current state.\n * @param {Object} cmInfo the course module state data.\n * @return {courseItem[]} the items array associated with that cm.\n */\n cmItemsArray(state, cmInfo) {\n // Activities with delegated sections are exported as sections.\n if (cmInfo.hasdelegatedsection) {\n const items = [];\n const delegatedsection = state.section.get(cmInfo.delegatesectionid);\n items.push({\n type: 'section',\n id: delegatedsection.id,\n url: delegatedsection.sectionurl\n });\n const delegatedCmlist = delegatedsection.cmlist ?? [];\n delegatedCmlist.forEach(cmid => {\n const cmInfo = state.cm.get(cmid);\n items.push({\n type: 'cm',\n id: cmInfo.id,\n url: cmInfo.url\n });\n });\n return items;\n }\n\n return [\n {type: 'cm', id: cmInfo.id, url: cmInfo.url},\n ];\n }\n\n /**\n * Check is some activities of a list can be stealth.\n *\n * @param {Object} state the current state.\n * @param {Number[]} cmIds the module ids to check\n * @returns {Boolean} if any of the activities can be stealth.\n */\n canUseStealth(state, cmIds) {\n return cmIds.some(cmId => {\n const cminfo = state.cm.get(cmId);\n return cminfo?.allowstealth ?? false;\n });\n }\n}\n"],"names":["constructor","reactive","COMPLETIONS","course","state","data","sections","editmode","this","isEditing","highlighted","listedSectionIds","forEach","sectionid","sectioninfo","section","get","push","hassections","length","sectionlist","filter","component","cms","cmlist","cmid","cminfo","cm","hascms","isactive","hasdelegatedsection","delegatesectionid","cmDraggableData","nextcmid","currentindex","indexOf","id","undefined","type","name","sectionDraggableData","number","fileDraggableData","dataTransfer","files","file","cmCompletion","statename","completionstate","hasstate","isoverallcomplete","allItemsArray","items","url","sectionurl","cmInfo","cmItemsArray","delegatedsection","canUseStealth","cmIds","some","cmId","allowstealth"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/src/local/courseeditor/exporter.js b/course/format/amd/src/local/courseeditor/exporter.js index cd8dbc3fa1a..b4b48698486 100644 --- a/course/format/amd/src/local/courseeditor/exporter.js +++ b/course/format/amd/src/local/courseeditor/exporter.js @@ -13,6 +13,16 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * A configuration to provide to the modal. + * + * @typedef {Object} courseItem + * + * @property {String} type The type of element (section, cm). + * @property {Number} id Element ID. + * @property {String} url Element URL. + */ + /** * Module to export parts of the state and transform them to be used in templates * and as draggable data. @@ -228,7 +238,7 @@ export default class { * Return a sorted list of all sections and cms items in the state. * * @param {Object} state the current state. - * @returns {Array} all sections and cms items in the state. + * @returns {courseItem[]} all sections and cms items in the state. */ allItemsArray(state) { const items = []; @@ -236,17 +246,61 @@ export default class { // Add sections. sectionlist.forEach(sectionid => { const sectioninfo = state.section.get(sectionid); - items.push({type: 'section', id: sectioninfo.id, url: sectioninfo.sectionurl}); + // Skip delegated sections because components are responsible for them. + if (sectioninfo.component !== null) { + return; + } + + items.push({ + type: 'section', + id: sectioninfo.id, + url: sectioninfo.sectionurl + }); // Add cms. const cmlist = sectioninfo.cmlist ?? []; cmlist.forEach(cmid => { - const cminfo = state.cm.get(cmid); - items.push({type: 'cm', id: cminfo.id, url: cminfo.url}); + const cmInfo = state.cm.get(cmid); + items.push(...this.cmItemsArray(state, cmInfo)); }); }); return items; } + /** + * Return a list of all items associated with an activity. + * + * @private + * @param {Object} state the full current state. + * @param {Object} cmInfo the course module state data. + * @return {courseItem[]} the items array associated with that cm. + */ + cmItemsArray(state, cmInfo) { + // Activities with delegated sections are exported as sections. + if (cmInfo.hasdelegatedsection) { + const items = []; + const delegatedsection = state.section.get(cmInfo.delegatesectionid); + items.push({ + type: 'section', + id: delegatedsection.id, + url: delegatedsection.sectionurl + }); + const delegatedCmlist = delegatedsection.cmlist ?? []; + delegatedCmlist.forEach(cmid => { + const cmInfo = state.cm.get(cmid); + items.push({ + type: 'cm', + id: cmInfo.id, + url: cmInfo.url + }); + }); + return items; + } + + return [ + {type: 'cm', id: cmInfo.id, url: cmInfo.url}, + ]; + } + /** * Check is some activities of a list can be stealth. * diff --git a/theme/boost/scss/moodle/courseindex.scss b/theme/boost/scss/moodle/courseindex.scss index 57e23cae180..9eebf951981 100644 --- a/theme/boost/scss/moodle/courseindex.scss +++ b/theme/boost/scss/moodle/courseindex.scss @@ -33,6 +33,18 @@ $courseindex-item-current: $primary !default; &.draggable { cursor: move; } + &.pageitem, + .pageitem { + background-color: darken($courseindex-item-page-bg, 7.5%); + color: darken($courseindex-link-color-selected, 10%); + .courseindex-link, + .courseindex-chevron { + color: darken($courseindex-link-color-selected, 10%); + a { + color: darken($courseindex-link-color-selected, 10%); + } + } + } } } @@ -114,15 +126,6 @@ $courseindex-item-current: $primary !default; a { color: $courseindex-link-color-selected; } - &:hover, - &:focus { - background-color: darken($courseindex-item-page-bg, 7.5%); - color: darken($courseindex-link-color-selected, 10%); - .courseindex-link, - .courseindex-chevron { - color: darken($courseindex-link-color-selected, 10%); - } - } } // Completion. diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 4761954be70..765c8b958b0 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -38604,6 +38604,30 @@ div.editor_atto_toolbar button .icon { .courseindex .courseindex-item:hover.draggable, .courseindex .courseindex-item:focus.draggable { cursor: move; } +.courseindex .courseindex-item:hover.pageitem, +.courseindex .courseindex-item:hover .pageitem, .courseindex .courseindex-item:focus.pageitem, +.courseindex .courseindex-item:focus .pageitem { + background-color: #0c589c; + color: #e6e6e6; +} +.courseindex .courseindex-item:hover.pageitem .courseindex-link, +.courseindex .courseindex-item:hover.pageitem .courseindex-chevron, +.courseindex .courseindex-item:hover .pageitem .courseindex-link, +.courseindex .courseindex-item:hover .pageitem .courseindex-chevron, .courseindex .courseindex-item:focus.pageitem .courseindex-link, +.courseindex .courseindex-item:focus.pageitem .courseindex-chevron, +.courseindex .courseindex-item:focus .pageitem .courseindex-link, +.courseindex .courseindex-item:focus .pageitem .courseindex-chevron { + color: #e6e6e6; +} +.courseindex .courseindex-item:hover.pageitem .courseindex-link a, +.courseindex .courseindex-item:hover.pageitem .courseindex-chevron a, +.courseindex .courseindex-item:hover .pageitem .courseindex-link a, +.courseindex .courseindex-item:hover .pageitem .courseindex-chevron a, .courseindex .courseindex-item:focus.pageitem .courseindex-link a, +.courseindex .courseindex-item:focus.pageitem .courseindex-chevron a, +.courseindex .courseindex-item:focus .pageitem .courseindex-link a, +.courseindex .courseindex-item:focus .pageitem .courseindex-chevron a { + color: #e6e6e6; +} .courseindex .courseindex-item.dragging { border: 1px solid #b8dce2; background-color: #e0f0f2; @@ -38639,15 +38663,6 @@ div.editor_atto_toolbar button .icon { .courseindex .courseindex-item.pageitem a { color: #fff; } -.courseindex .courseindex-item.pageitem:hover, .courseindex .courseindex-item.pageitem:focus { - background-color: #0c589c; - color: #e6e6e6; -} -.courseindex .courseindex-item.pageitem:hover .courseindex-link, -.courseindex .courseindex-item.pageitem:hover .courseindex-chevron, .courseindex .courseindex-item.pageitem:focus .courseindex-link, -.courseindex .courseindex-item.pageitem:focus .courseindex-chevron { - color: #e6e6e6; -} .courseindex .courseindex-item .completioninfo { min-width: 24px; } diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 13d2c61ba6b..f5926e4256e 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -38538,6 +38538,30 @@ div.editor_atto_toolbar button .icon { .courseindex .courseindex-item:hover.draggable, .courseindex .courseindex-item:focus.draggable { cursor: move; } +.courseindex .courseindex-item:hover.pageitem, +.courseindex .courseindex-item:hover .pageitem, .courseindex .courseindex-item:focus.pageitem, +.courseindex .courseindex-item:focus .pageitem { + background-color: #0c589c; + color: #e6e6e6; +} +.courseindex .courseindex-item:hover.pageitem .courseindex-link, +.courseindex .courseindex-item:hover.pageitem .courseindex-chevron, +.courseindex .courseindex-item:hover .pageitem .courseindex-link, +.courseindex .courseindex-item:hover .pageitem .courseindex-chevron, .courseindex .courseindex-item:focus.pageitem .courseindex-link, +.courseindex .courseindex-item:focus.pageitem .courseindex-chevron, +.courseindex .courseindex-item:focus .pageitem .courseindex-link, +.courseindex .courseindex-item:focus .pageitem .courseindex-chevron { + color: #e6e6e6; +} +.courseindex .courseindex-item:hover.pageitem .courseindex-link a, +.courseindex .courseindex-item:hover.pageitem .courseindex-chevron a, +.courseindex .courseindex-item:hover .pageitem .courseindex-link a, +.courseindex .courseindex-item:hover .pageitem .courseindex-chevron a, .courseindex .courseindex-item:focus.pageitem .courseindex-link a, +.courseindex .courseindex-item:focus.pageitem .courseindex-chevron a, +.courseindex .courseindex-item:focus .pageitem .courseindex-link a, +.courseindex .courseindex-item:focus .pageitem .courseindex-chevron a { + color: #e6e6e6; +} .courseindex .courseindex-item.dragging { border: 1px solid #b8dce2; background-color: #e0f0f2; @@ -38573,15 +38597,6 @@ div.editor_atto_toolbar button .icon { .courseindex .courseindex-item.pageitem a { color: #fff; } -.courseindex .courseindex-item.pageitem:hover, .courseindex .courseindex-item.pageitem:focus { - background-color: #0c589c; - color: #e6e6e6; -} -.courseindex .courseindex-item.pageitem:hover .courseindex-link, -.courseindex .courseindex-item.pageitem:hover .courseindex-chevron, .courseindex .courseindex-item.pageitem:focus .courseindex-link, -.courseindex .courseindex-item.pageitem:focus .courseindex-chevron { - color: #e6e6e6; -} .courseindex .courseindex-item .completioninfo { min-width: 24px; } From f7b3f6930ffc9585743b55b149d50eab8055c135 Mon Sep 17 00:00:00 2001 From: Laurent David Date: Tue, 3 Sep 2024 12:19:46 +0200 Subject: [PATCH 2/2] MDL-82407 core_courseformat: Fix positionning for course index * ScrollSpy relies on offset to determine the current element in in the course index. We need to make sure that those elements are not positionned so the offset is calculated from the top of the page. --- theme/boost/scss/moodle/course.scss | 6 ++---- theme/boost/style/moodle.css | 6 ++---- theme/classic/style/moodle.css | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/theme/boost/scss/moodle/course.scss b/theme/boost/scss/moodle/course.scss index 911805ded70..0d07937bade 100644 --- a/theme/boost/scss/moodle/course.scss +++ b/theme/boost/scss/moodle/course.scss @@ -1302,7 +1302,6 @@ $divider-hover-color: $primary !default; .activity { list-style: none; - position: relative; padding-top: map-get($spacers, 1); margin-top: map-get($spacers, 1); border-top: $border-width solid $border-color; @@ -1317,7 +1316,6 @@ $divider-hover-color: $primary !default; } .activity-item { - position: relative; padding: $activity-item-padding; @include border-radius($activity-border-radius); &.activityinline { @@ -1684,8 +1682,8 @@ $divider-hover-color: $primary !default; } .activity-item { .bulkselect { - position: absolute; - left: -2rem; + float: left; + margin-left: -2rem; } // Delegated sections are not available for bulk editing. &:has(.delegated-section):hover { diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 765c8b958b0..d651bb263ff 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -29178,7 +29178,6 @@ span.editinstructions .alert-link { /* Activity cards */ .activity { list-style: none; - position: relative; padding-top: 0.25rem; margin-top: 0.25rem; border-top: 1px solid #dee2e6; @@ -29190,7 +29189,6 @@ span.editinstructions .alert-link { } .activity-item { - position: relative; padding: 0.75rem; border-radius: 1rem; } @@ -29911,8 +29909,8 @@ span.editinstructions .alert-link { margin-top: 1.5rem; } .bulkenabled .activity-item .bulkselect { - position: absolute; - left: -2rem; + float: left; + margin-left: -2rem; } .bulkenabled .activity-item:has(.delegated-section):hover { outline: none !important; diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index f5926e4256e..5176526c554 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -29178,7 +29178,6 @@ span.editinstructions .alert-link { /* Activity cards */ .activity { list-style: none; - position: relative; padding-top: 0.25rem; margin-top: 0.25rem; border-top: 1px solid #dee2e6; @@ -29190,7 +29189,6 @@ span.editinstructions .alert-link { } .activity-item { - position: relative; padding: 0.75rem; border-radius: 1rem; } @@ -29911,8 +29909,8 @@ span.editinstructions .alert-link { margin-top: 1.5rem; } .bulkenabled .activity-item .bulkselect { - position: absolute; - left: -2rem; + float: left; + margin-left: -2rem; } .bulkenabled .activity-item:has(.delegated-section):hover { outline: none !important;