mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 14:27:22 +01:00
Merge branch 'MDL-76188-master' of https://github.com/ferranrecio/moodle
This commit is contained in:
commit
42926069da
2
course/amd/build/actions.min.js
vendored
2
course/amd/build/actions.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -60,7 +60,7 @@ define(
|
||||
// Meanwhile, we filter the migrated actions.
|
||||
const componentActions = [
|
||||
'moveSection', 'moveCm', 'addSection', 'deleteSection', 'sectionHide', 'sectionShow',
|
||||
'cmHide', 'cmShow', 'cmStealth',
|
||||
'cmHide', 'cmShow', 'cmStealth', 'sectionHighlight', 'sectionUnhighlight',
|
||||
];
|
||||
|
||||
// The course reactive instance.
|
||||
|
@ -6,6 +6,6 @@ define("core_courseformat/local/content/section",["exports","core_courseformat/l
|
||||
* @class core_courseformat/local/content/section
|
||||
* @copyright 2021 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_header=_interopRequireDefault(_header),_dndsection=_interopRequireDefault(_dndsection),_templates=_interopRequireDefault(_templates);class _default extends _dndsection.default{create(){this.name="content_section",this.selectors={SECTION_ITEM:"[data-for='section_title']",CM:'[data-for="cmitem"]',SECTIONINFO:'[data-for="sectioninfo"]',SECTIONBADGES:'[data-region="sectionbadges"]',SHOWSECTION:'[data-action="sectionShow"]',HIDESECTION:'[data-action="sectionHide"]',ACTIONTEXT:".menu-action-text",ICON:".icon"},this.classes={LOCKED:"editinprogress",HASDESCRIPTION:"description",HIDE:"d-none",HIDDEN:"hidden"},this.id=this.element.dataset.id}stateReady(state){if(this.configState(state),this.reactive.isEditing&&this.reactive.supportComponents){const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(sectionItem){const headerComponent=new _header.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(headerComponent)}}}getWatchers(){return[{watch:"section[".concat(this.id,"]:updated"),handler:this._refreshSection}]}validateDropData(dropdata){return("section"!==(null==dropdata?void 0:dropdata.type)||0==this.reactive.sectionReturn)&&super.validateDropData(dropdata)}getLastCm(){const cms=this.getElements(this.selectors.CM);return cms&&0!==cms.length?cms[cms.length-1]:null}_refreshSection(_ref){var _element$dragging,_element$locked,_element$visible;let{element:element}=_ref;this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.element.classList.toggle(this.classes.HIDDEN,null!==(_element$visible=!element.visible)&&void 0!==_element$visible&&_element$visible),this.locked=element.locked;const sectioninfo=this.getElement(this.selectors.SECTIONINFO);sectioninfo&§ioninfo.classList.toggle(this.classes.HASDESCRIPTION,element.hasrestrictions),this._updateBadges(element),this._updateActionsMenu(element)}_updateBadges(section){const current=this.getElement("".concat(this.selectors.SECTIONBADGES," [data-type='iscurrent']"));null==current||current.classList.toggle(this.classes.HIDE,!section.current);const hiddenFromStudents=this.getElement("".concat(this.selectors.SECTIONBADGES," [data-type='hiddenfromstudents']"));null==hiddenFromStudents||hiddenFromStudents.classList.toggle(this.classes.HIDE,section.visible)}async _updateActionsMenu(section){var _affectedAction$datas,_affectedAction$datas2;let selector,newAction;section.visible?(selector=this.selectors.SHOWSECTION,newAction="sectionHide"):(selector=this.selectors.HIDESECTION,newAction="sectionShow");const affectedAction=this.getElement(selector);if(!affectedAction)return;affectedAction.dataset.action=newAction;const actionText=affectedAction.querySelector(this.selectors.ACTIONTEXT);if(null!==(_affectedAction$datas=affectedAction.dataset)&&void 0!==_affectedAction$datas&&_affectedAction$datas.swapname&&actionText){const oldText=null==actionText?void 0:actionText.innerText;actionText.innerText=affectedAction.dataset.swapname,affectedAction.dataset.swapname=oldText}const icon=affectedAction.querySelector(this.selectors.ICON);if(null!==(_affectedAction$datas2=affectedAction.dataset)&&void 0!==_affectedAction$datas2&&_affectedAction$datas2.swapicon&&icon){const newIcon=affectedAction.dataset.swapicon;if(newIcon){const pixHtml=await _templates.default.renderPix(newIcon,"core");_templates.default.replaceNode(icon,pixHtml,"")}}}}return _exports.default=_default,_exports.default}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_header=_interopRequireDefault(_header),_dndsection=_interopRequireDefault(_dndsection),_templates=_interopRequireDefault(_templates);class _default extends _dndsection.default{create(){this.name="content_section",this.selectors={SECTION_ITEM:"[data-for='section_title']",CM:'[data-for="cmitem"]',SECTIONINFO:'[data-for="sectioninfo"]',SECTIONBADGES:'[data-region="sectionbadges"]',SHOWSECTION:'[data-action="sectionShow"]',HIDESECTION:'[data-action="sectionHide"]',ACTIONTEXT:".menu-action-text",ICON:".icon"},this.classes={LOCKED:"editinprogress",HASDESCRIPTION:"description",HIDE:"d-none",HIDDEN:"hidden",CURRENT:"current"},this.id=this.element.dataset.id}stateReady(state){if(this.configState(state),this.reactive.isEditing&&this.reactive.supportComponents){const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(sectionItem){const headerComponent=new _header.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(headerComponent)}}}getWatchers(){return[{watch:"section[".concat(this.id,"]:updated"),handler:this._refreshSection}]}validateDropData(dropdata){return("section"!==(null==dropdata?void 0:dropdata.type)||0==this.reactive.sectionReturn)&&super.validateDropData(dropdata)}getLastCm(){const cms=this.getElements(this.selectors.CM);return cms&&0!==cms.length?cms[cms.length-1]:null}_refreshSection(_ref){var _element$dragging,_element$locked,_element$visible,_element$current;let{element:element}=_ref;this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.element.classList.toggle(this.classes.HIDDEN,null!==(_element$visible=!element.visible)&&void 0!==_element$visible&&_element$visible),this.element.classList.toggle(this.classes.CURRENT,null!==(_element$current=element.current)&&void 0!==_element$current&&_element$current),this.locked=element.locked;const sectioninfo=this.getElement(this.selectors.SECTIONINFO);sectioninfo&§ioninfo.classList.toggle(this.classes.HASDESCRIPTION,element.hasrestrictions),this._updateBadges(element),this._updateActionsMenu(element)}_updateBadges(section){const current=this.getElement("".concat(this.selectors.SECTIONBADGES," [data-type='iscurrent']"));null==current||current.classList.toggle(this.classes.HIDE,!section.current);const hiddenFromStudents=this.getElement("".concat(this.selectors.SECTIONBADGES," [data-type='hiddenfromstudents']"));null==hiddenFromStudents||hiddenFromStudents.classList.toggle(this.classes.HIDE,section.visible)}async _updateActionsMenu(section){var _affectedAction$datas,_affectedAction$datas2;let selector,newAction;section.visible?(selector=this.selectors.SHOWSECTION,newAction="sectionHide"):(selector=this.selectors.HIDESECTION,newAction="sectionShow");const affectedAction=this.getElement(selector);if(!affectedAction)return;affectedAction.dataset.action=newAction;const actionText=affectedAction.querySelector(this.selectors.ACTIONTEXT);if(null!==(_affectedAction$datas=affectedAction.dataset)&&void 0!==_affectedAction$datas&&_affectedAction$datas.swapname&&actionText){const oldText=null==actionText?void 0:actionText.innerText;actionText.innerText=affectedAction.dataset.swapname,affectedAction.dataset.swapname=oldText}const icon=affectedAction.querySelector(this.selectors.ICON);if(null!==(_affectedAction$datas2=affectedAction.dataset)&&void 0!==_affectedAction$datas2&&_affectedAction$datas2.swapicon&&icon){const newIcon=affectedAction.dataset.swapicon;if(newIcon){const pixHtml=await _templates.default.renderPix(newIcon,"core");_templates.default.replaceNode(icon,pixHtml,"")}}}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=section.min.js.map
|
File diff suppressed because one or more lines are too long
@ -51,6 +51,7 @@ export default class extends DndSection {
|
||||
HASDESCRIPTION: 'description',
|
||||
HIDE: 'd-none',
|
||||
HIDDEN: 'hidden',
|
||||
CURRENT: 'current',
|
||||
};
|
||||
|
||||
// We need our id to watch specific events.
|
||||
@ -130,6 +131,7 @@ export default class extends DndSection {
|
||||
this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);
|
||||
this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);
|
||||
this.element.classList.toggle(this.classes.HIDDEN, !element.visible ?? false);
|
||||
this.element.classList.toggle(this.classes.CURRENT, element.current ?? false);
|
||||
this.locked = element.locked;
|
||||
// The description box classes depends on the section state.
|
||||
const sectioninfo = this.getElement(this.selectors.SECTIONINFO);
|
||||
|
@ -286,6 +286,15 @@ abstract class base {
|
||||
return $this->courseid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the course context.
|
||||
*
|
||||
* @return context_course the course context
|
||||
*/
|
||||
final public function get_context(): context_course {
|
||||
return context_course::instance($this->courseid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a record from course database table plus additional fields
|
||||
* that course format defines
|
||||
|
3
course/format/topics/amd/build/mutations.min.js
vendored
Normal file
3
course/format/topics/amd/build/mutations.min.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
define("format_topics/mutations",["exports","core_courseformat/courseeditor","core_courseformat/local/courseeditor/mutations","core_courseformat/local/content/actions"],(function(_exports,_courseeditor,_mutations,_actions){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_mutations=_interopRequireDefault(_mutations),_actions=_interopRequireDefault(_actions);class TopicsMutations extends _mutations.default{constructor(){super(...arguments),_defineProperty(this,"sectionHighlight",(async function(stateManager,sectionIds){const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice("section_highlight",course.id,sectionIds);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)})),_defineProperty(this,"sectionUnhighlight",(async function(stateManager,sectionIds){const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice("section_unhighlight",course.id,sectionIds);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}))}}_exports.init=()=>{(0,_courseeditor.getCurrentCourseEditor)().addMutations(new TopicsMutations),_actions.default.addActions({sectionHighlight:"sectionHighlight",sectionUnhighlight:"sectionUnhighlight"})}}));
|
||||
|
||||
//# sourceMappingURL=mutations.min.js.map
|
1
course/format/topics/amd/build/mutations.min.js.map
Normal file
1
course/format/topics/amd/build/mutations.min.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"mutations.min.js","sources":["../src/mutations.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Format topics mutations.\n *\n * An instance of this class will be used to add custom mutations to the course editor.\n * To make sure the addMutations method find the proper functions, all functions must\n * be declared as class attributes, not a simple methods. The reason is because many\n * plugins can add extra mutations to the course editor.\n *\n * @module format_topics/mutations\n * @copyright 2022 Ferran Recio <ferran@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport DefaultMutations from 'core_courseformat/local/courseeditor/mutations';\nimport CourseActions from 'core_courseformat/local/content/actions';\n\nclass TopicsMutations extends DefaultMutations {\n\n /**\n * Highlight sections.\n *\n * It is important to note this mutation method is declared as a class attribute,\n * See the class jsdoc for more details on why.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n sectionHighlight = async function(stateManager, sectionIds) {\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice('section_highlight', course.id, sectionIds);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n };\n\n /**\n * Unhighlight sections.\n *\n * It is important to note this mutation method is declared as a class attribute,\n * See the class jsdoc for more details on why.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n sectionUnhighlight = async function(stateManager, sectionIds) {\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice('section_unhighlight', course.id, sectionIds);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n };\n}\n\nexport const init = () => {\n const courseEditor = getCurrentCourseEditor();\n // Some plugin (activity or block) may have their own mutations already registered.\n // This is why we use addMutations instead of setMutations here.\n courseEditor.addMutations(new TopicsMutations());\n // Add direct mutation content actions.\n CourseActions.addActions({\n sectionHighlight: 'sectionHighlight',\n sectionUnhighlight: 'sectionUnhighlight',\n });\n};\n"],"names":["TopicsMutations","DefaultMutations","async","stateManager","sectionIds","course","get","sectionLock","updates","this","_callEditWebservice","id","processUpdates","addMutations","addActions","sectionHighlight","sectionUnhighlight"],"mappings":"goBAgCMA,wBAAwBC,8FAWPC,eAAeC,aAAcC,kBACtCC,OAASF,aAAaG,IAAI,eAC3BC,YAAYJ,aAAcC,YAAY,SACrCI,cAAgBC,KAAKC,oBAAoB,oBAAqBL,OAAOM,GAAIP,YAC/ED,aAAaS,eAAeJ,cACvBD,YAAYJ,aAAcC,YAAY,iDAY1BF,eAAeC,aAAcC,kBACxCC,OAASF,aAAaG,IAAI,eAC3BC,YAAYJ,aAAcC,YAAY,SACrCI,cAAgBC,KAAKC,oBAAoB,sBAAuBL,OAAOM,GAAIP,YACjFD,aAAaS,eAAeJ,cACvBD,YAAYJ,aAAcC,YAAY,qBAI/B,MACK,0CAGRS,aAAa,IAAIb,kCAEhBc,WAAW,CACrBC,iBAAkB,mBAClBC,mBAAoB"}
|
10
course/format/topics/amd/build/section.min.js
vendored
Normal file
10
course/format/topics/amd/build/section.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
define("format_topics/section",["exports","core/reactive","core_courseformat/courseeditor","core/templates"],(function(_exports,_reactive,_courseeditor,_templates){var obj;
|
||||
/**
|
||||
* Format topics section extra logic component.
|
||||
*
|
||||
* @module format_topics/mutations
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};class HighlightSection extends _reactive.BaseComponent{create(){this.name="format_topics_section",this.selectors={SECTION:"[data-for='section']",SETMARKER:'[data-action="sectionHighlight"]',REMOVEMARKER:'[data-action="sectionUnhighlight"]',ACTIONTEXT:".menu-action-text",ICON:".icon"},this.classes={HIDE:"d-none"},this.formatActions={HIGHLIGHT:"sectionHighlight",UNHIGHLIGHT:"sectionUnhighlight"}}getWatchers(){return[{watch:"section.current:updated",handler:this._refreshHighlight}]}async _refreshHighlight(_ref){var _affectedAction$datas,_affectedAction$datas2;let selector,newAction,{element:element}=_ref;element.current?(selector=this.selectors.SETMARKER,newAction=this.formatActions.UNHIGHLIGHT):(selector=this.selectors.REMOVEMARKER,newAction=this.formatActions.HIGHLIGHT);const affectedAction=this.getElement("".concat(this.selectors.SECTION," ").concat(selector),element.id);if(!affectedAction)return;affectedAction.dataset.action=newAction;const actionText=affectedAction.querySelector(this.selectors.ACTIONTEXT);if(null!==(_affectedAction$datas=affectedAction.dataset)&&void 0!==_affectedAction$datas&&_affectedAction$datas.swapname&&actionText){const oldText=null==actionText?void 0:actionText.innerText;actionText.innerText=affectedAction.dataset.swapname,affectedAction.dataset.swapname=oldText}const icon=affectedAction.querySelector(this.selectors.ICON);if(null!==(_affectedAction$datas2=affectedAction.dataset)&&void 0!==_affectedAction$datas2&&_affectedAction$datas2.swapicon&&icon){const newIcon=affectedAction.dataset.swapicon;if(newIcon){const pixHtml=await _templates.default.renderPix(newIcon,"core");_templates.default.replaceNode(icon,pixHtml,"")}}}}_exports.init=()=>{const courseEditor=(0,_courseeditor.getCurrentCourseEditor)();courseEditor.supportComponents&&courseEditor.isEditing&&new HighlightSection({element:document.getElementById("region-main"),reactive:courseEditor})}}));
|
||||
|
||||
//# sourceMappingURL=section.min.js.map
|
1
course/format/topics/amd/build/section.min.js.map
Normal file
1
course/format/topics/amd/build/section.min.js.map
Normal file
File diff suppressed because one or more lines are too long
80
course/format/topics/amd/src/mutations.js
Normal file
80
course/format/topics/amd/src/mutations.js
Normal file
@ -0,0 +1,80 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Format topics mutations.
|
||||
*
|
||||
* An instance of this class will be used to add custom mutations to the course editor.
|
||||
* To make sure the addMutations method find the proper functions, all functions must
|
||||
* be declared as class attributes, not a simple methods. The reason is because many
|
||||
* plugins can add extra mutations to the course editor.
|
||||
*
|
||||
* @module format_topics/mutations
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {getCurrentCourseEditor} from 'core_courseformat/courseeditor';
|
||||
import DefaultMutations from 'core_courseformat/local/courseeditor/mutations';
|
||||
import CourseActions from 'core_courseformat/local/content/actions';
|
||||
|
||||
class TopicsMutations extends DefaultMutations {
|
||||
|
||||
/**
|
||||
* Highlight sections.
|
||||
*
|
||||
* It is important to note this mutation method is declared as a class attribute,
|
||||
* See the class jsdoc for more details on why.
|
||||
*
|
||||
* @param {StateManager} stateManager the current state manager
|
||||
* @param {array} sectionIds the list of section ids
|
||||
*/
|
||||
sectionHighlight = async function(stateManager, sectionIds) {
|
||||
const course = stateManager.get('course');
|
||||
this.sectionLock(stateManager, sectionIds, true);
|
||||
const updates = await this._callEditWebservice('section_highlight', course.id, sectionIds);
|
||||
stateManager.processUpdates(updates);
|
||||
this.sectionLock(stateManager, sectionIds, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unhighlight sections.
|
||||
*
|
||||
* It is important to note this mutation method is declared as a class attribute,
|
||||
* See the class jsdoc for more details on why.
|
||||
*
|
||||
* @param {StateManager} stateManager the current state manager
|
||||
* @param {array} sectionIds the list of section ids
|
||||
*/
|
||||
sectionUnhighlight = async function(stateManager, sectionIds) {
|
||||
const course = stateManager.get('course');
|
||||
this.sectionLock(stateManager, sectionIds, true);
|
||||
const updates = await this._callEditWebservice('section_unhighlight', course.id, sectionIds);
|
||||
stateManager.processUpdates(updates);
|
||||
this.sectionLock(stateManager, sectionIds, false);
|
||||
};
|
||||
}
|
||||
|
||||
export const init = () => {
|
||||
const courseEditor = getCurrentCourseEditor();
|
||||
// Some plugin (activity or block) may have their own mutations already registered.
|
||||
// This is why we use addMutations instead of setMutations here.
|
||||
courseEditor.addMutations(new TopicsMutations());
|
||||
// Add direct mutation content actions.
|
||||
CourseActions.addActions({
|
||||
sectionHighlight: 'sectionHighlight',
|
||||
sectionUnhighlight: 'sectionUnhighlight',
|
||||
});
|
||||
};
|
115
course/format/topics/amd/src/section.js
Normal file
115
course/format/topics/amd/src/section.js
Normal file
@ -0,0 +1,115 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Format topics section extra logic component.
|
||||
*
|
||||
* @module format_topics/mutations
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {BaseComponent} from 'core/reactive';
|
||||
import {getCurrentCourseEditor} from 'core_courseformat/courseeditor';
|
||||
import Templates from 'core/templates';
|
||||
|
||||
class HighlightSection extends BaseComponent {
|
||||
|
||||
/**
|
||||
* Constructor hook.
|
||||
*/
|
||||
create() {
|
||||
// Optional component name for debugging.
|
||||
this.name = 'format_topics_section';
|
||||
// Default query selectors.
|
||||
this.selectors = {
|
||||
SECTION: `[data-for='section']`,
|
||||
SETMARKER: `[data-action="sectionHighlight"]`,
|
||||
REMOVEMARKER: `[data-action="sectionUnhighlight"]`,
|
||||
ACTIONTEXT: `.menu-action-text`,
|
||||
ICON: `.icon`,
|
||||
};
|
||||
// Default classes to toggle on refresh.
|
||||
this.classes = {
|
||||
HIDE: 'd-none',
|
||||
};
|
||||
// The topics format section specific actions.
|
||||
this.formatActions = {
|
||||
HIGHLIGHT: 'sectionHighlight',
|
||||
UNHIGHLIGHT: 'sectionUnhighlight',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Component watchers.
|
||||
*
|
||||
* @returns {Array} of watchers
|
||||
*/
|
||||
getWatchers() {
|
||||
return [
|
||||
{watch: `section.current:updated`, handler: this._refreshHighlight},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a content section using the state information.
|
||||
*
|
||||
* @param {object} param
|
||||
* @param {Object} param.element details the update details.
|
||||
*/
|
||||
async _refreshHighlight({element}) {
|
||||
let selector;
|
||||
let newAction;
|
||||
if (element.current) {
|
||||
selector = this.selectors.SETMARKER;
|
||||
newAction = this.formatActions.UNHIGHLIGHT;
|
||||
} else {
|
||||
selector = this.selectors.REMOVEMARKER;
|
||||
newAction = this.formatActions.HIGHLIGHT;
|
||||
}
|
||||
// Find the affected action.
|
||||
const affectedAction = this.getElement(`${this.selectors.SECTION} ${selector}`, element.id);
|
||||
if (!affectedAction) {
|
||||
return;
|
||||
}
|
||||
// Change action, text and icon.
|
||||
affectedAction.dataset.action = newAction;
|
||||
const actionText = affectedAction.querySelector(this.selectors.ACTIONTEXT);
|
||||
if (affectedAction.dataset?.swapname && actionText) {
|
||||
const oldText = actionText?.innerText;
|
||||
actionText.innerText = affectedAction.dataset.swapname;
|
||||
affectedAction.dataset.swapname = oldText;
|
||||
}
|
||||
const icon = affectedAction.querySelector(this.selectors.ICON);
|
||||
if (affectedAction.dataset?.swapicon && icon) {
|
||||
const newIcon = affectedAction.dataset.swapicon;
|
||||
if (newIcon) {
|
||||
const pixHtml = await Templates.renderPix(newIcon, 'core');
|
||||
Templates.replaceNode(icon, pixHtml, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const init = () => {
|
||||
// Add component to the section.
|
||||
const courseEditor = getCurrentCourseEditor();
|
||||
if (courseEditor.supportComponents && courseEditor.isEditing) {
|
||||
new HighlightSection({
|
||||
element: document.getElementById('region-main'),
|
||||
reactive: courseEditor,
|
||||
});
|
||||
}
|
||||
};
|
117
course/format/topics/classes/courseformat/stateactions.php
Normal file
117
course/format/topics/classes/courseformat/stateactions.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace format_topics\courseformat;
|
||||
|
||||
use core_courseformat\stateupdates;
|
||||
use core_courseformat\stateactions as stateactions_base;
|
||||
use core\event\course_module_updated;
|
||||
use cm_info;
|
||||
use section_info;
|
||||
use stdClass;
|
||||
use course_modinfo;
|
||||
use moodle_exception;
|
||||
use context_module;
|
||||
use context_course;
|
||||
|
||||
/**
|
||||
* Contains the core course state actions specific to topics format.
|
||||
*
|
||||
* @package format_topics
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class stateactions extends stateactions_base {
|
||||
|
||||
/**
|
||||
* Highlight course section.
|
||||
*
|
||||
* @param stateupdates $updates the affected course elements track
|
||||
* @param stdClass $course the course object
|
||||
* @param int[] $ids section ids (only ther first one will be highlighted)
|
||||
* @param int $targetsectionid not used
|
||||
* @param int $targetcmid not used
|
||||
*/
|
||||
public function section_highlight(
|
||||
stateupdates $updates,
|
||||
stdClass $course,
|
||||
array $ids = [],
|
||||
?int $targetsectionid = null,
|
||||
?int $targetcmid = null
|
||||
): void {
|
||||
global $DB;
|
||||
|
||||
$this->validate_sections($course, $ids, __FUNCTION__);
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
require_capability('moodle/course:setcurrentsection', $coursecontext);
|
||||
|
||||
// Get the previous marked section.
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
$previousmarker = $DB->get_field("course", "marker", ['id' => $course->id]);
|
||||
|
||||
$section = $modinfo->get_section_info_by_id(reset($ids), MUST_EXIST);
|
||||
if ($section->section == $previousmarker) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the new one.
|
||||
course_set_marker($course->id, $section->section);
|
||||
$updates->add_section_put($section->id);
|
||||
if ($previousmarker) {
|
||||
$section = $modinfo->get_section_info($previousmarker);
|
||||
$updates->add_section_put($section->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove highlight from a course sections.
|
||||
*
|
||||
* @param stateupdates $updates the affected course elements track
|
||||
* @param stdClass $course the course object
|
||||
* @param int[] $ids optional extra section ids to refresh
|
||||
* @param int $targetsectionid not used
|
||||
* @param int $targetcmid not used
|
||||
*/
|
||||
public function section_unhighlight(
|
||||
stateupdates $updates,
|
||||
stdClass $course,
|
||||
array $ids = [],
|
||||
?int $targetsectionid = null,
|
||||
?int $targetcmid = null
|
||||
): void {
|
||||
global $DB;
|
||||
|
||||
$this->validate_sections($course, $ids, __FUNCTION__);
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
require_capability('moodle/course:setcurrentsection', $coursecontext);
|
||||
|
||||
$affectedsections = [];
|
||||
|
||||
// Get the previous marked section and unmark it.
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
$previousmarker = $DB->get_field("course", "marker", ['id' => $course->id]);
|
||||
course_set_marker($course->id, 0);
|
||||
$section = $modinfo->get_section_info($previousmarker, MUST_EXIST);
|
||||
$updates->add_section_put($section->id);
|
||||
|
||||
foreach ($ids as $sectionid) {
|
||||
$section = $modinfo->get_section_info_by_id($sectionid, MUST_EXIST);
|
||||
if ($section->section != $previousmarker) {
|
||||
$updates->add_section_put($section->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
namespace format_topics\output\courseformat;
|
||||
|
||||
use core_courseformat\output\local\content as content_base;
|
||||
use renderer_base;
|
||||
|
||||
/**
|
||||
* Base class to render a course content.
|
||||
@ -42,4 +43,17 @@ class content extends content_base {
|
||||
*/
|
||||
protected $hasaddsection = false;
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template (core/inplace_editable).
|
||||
*
|
||||
* @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) {
|
||||
global $PAGE;
|
||||
$PAGE->requires->js_call_amd('format_topics/mutations', 'init');
|
||||
$PAGE->requires->js_call_amd('format_topics/section', 'init');
|
||||
return parent::export_for_template($output);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
|
||||
namespace format_topics\output\courseformat\content\section;
|
||||
|
||||
use context_course;
|
||||
use core_courseformat\output\local\content\section\controlmenu as controlmenu_base;
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
* Base class to render a course section menu.
|
||||
@ -36,10 +36,10 @@ use core_courseformat\output\local\content\section\controlmenu as controlmenu_ba
|
||||
*/
|
||||
class controlmenu extends controlmenu_base {
|
||||
|
||||
/** @var course_format the course format class */
|
||||
/** @var \core_courseformat\base the course format class */
|
||||
protected $format;
|
||||
|
||||
/** @var section_info the course section class */
|
||||
/** @var \section_info the course section class */
|
||||
protected $section;
|
||||
|
||||
/**
|
||||
@ -53,47 +53,11 @@ class controlmenu extends controlmenu_base {
|
||||
|
||||
$format = $this->format;
|
||||
$section = $this->section;
|
||||
$course = $format->get_course();
|
||||
$sectionreturn = $format->get_section_number();
|
||||
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
|
||||
if ($sectionreturn) {
|
||||
$url = course_get_url($course, $section->section);
|
||||
} else {
|
||||
$url = course_get_url($course);
|
||||
}
|
||||
$url->param('sesskey', sesskey());
|
||||
$coursecontext = $format->get_context();
|
||||
|
||||
$controls = [];
|
||||
if ($section->section && has_capability('moodle/course:setcurrentsection', $coursecontext)) {
|
||||
if ($course->marker == $section->section) { // Show the "light globe" on/off.
|
||||
$url->param('marker', 0);
|
||||
$highlightoff = get_string('highlightoff');
|
||||
$controls['highlight'] = [
|
||||
'url' => $url,
|
||||
'icon' => 'i/marked',
|
||||
'name' => $highlightoff,
|
||||
'pixattr' => ['class' => ''],
|
||||
'attr' => [
|
||||
'class' => 'editing_highlight',
|
||||
'data-action' => 'removemarker'
|
||||
],
|
||||
];
|
||||
} else {
|
||||
$url->param('marker', $section->section);
|
||||
$highlight = get_string('highlight');
|
||||
$controls['highlight'] = [
|
||||
'url' => $url,
|
||||
'icon' => 'i/marker',
|
||||
'name' => $highlight,
|
||||
'pixattr' => ['class' => ''],
|
||||
'attr' => [
|
||||
'class' => 'editing_highlight',
|
||||
'data-action' => 'setmarker'
|
||||
],
|
||||
];
|
||||
}
|
||||
$controls['highlight'] = $this->get_highlight_control();
|
||||
}
|
||||
|
||||
$parentcontrols = parent::section_control_items();
|
||||
@ -116,4 +80,72 @@ class controlmenu extends controlmenu_base {
|
||||
return array_merge($controls, $parentcontrols);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the course url.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
protected function get_course_url(): moodle_url {
|
||||
$format = $this->format;
|
||||
$section = $this->section;
|
||||
$course = $format->get_course();
|
||||
$sectionreturn = $format->get_section_number();
|
||||
|
||||
if ($sectionreturn) {
|
||||
$url = course_get_url($course, $section->section);
|
||||
} else {
|
||||
$url = course_get_url($course);
|
||||
}
|
||||
$url->param('sesskey', sesskey());
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specific section highlight action.
|
||||
*
|
||||
* @return array the action element.
|
||||
*/
|
||||
protected function get_highlight_control(): array {
|
||||
$format = $this->format;
|
||||
$section = $this->section;
|
||||
$course = $format->get_course();
|
||||
$url = $this->get_course_url();
|
||||
|
||||
$highlightoff = get_string('highlightoff');
|
||||
$highlighton = get_string('highlight');
|
||||
|
||||
if ($course->marker == $section->section) { // Show the "light globe" on/off.
|
||||
$url->param('marker', 0);
|
||||
$result = [
|
||||
'url' => $url,
|
||||
'icon' => 'i/marked',
|
||||
'name' => $highlightoff,
|
||||
'pixattr' => ['class' => ''],
|
||||
'attr' => [
|
||||
'class' => 'editing_highlight',
|
||||
'data-action' => 'sectionUnhighlight',
|
||||
'data-id' => $section->id,
|
||||
'data-swapname' => $highlighton,
|
||||
'data-swapicon' => 'i/marker',
|
||||
],
|
||||
];
|
||||
} else {
|
||||
$url->param('marker', $section->section);
|
||||
$result = [
|
||||
'url' => $url,
|
||||
'icon' => 'i/marker',
|
||||
'name' => $highlighton,
|
||||
'pixattr' => ['class' => ''],
|
||||
'attr' => [
|
||||
'class' => 'editing_highlight',
|
||||
'data-action' => 'sectionHighlight',
|
||||
'data-id' => $section->id,
|
||||
'data-swapname' => $highlightoff,
|
||||
'data-swapicon' => 'i/marked',
|
||||
],
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
49
course/format/topics/tests/behat/highlight_sections.feature
Normal file
49
course/format/topics/tests/behat/highlight_sections.feature
Normal file
@ -0,0 +1,49 @@
|
||||
@format @format_topics
|
||||
Feature: Sections can be highlighted
|
||||
In order to mark sections
|
||||
As a teacher
|
||||
I need to highlight and unhighlight sections
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format | coursedisplay | numsections |
|
||||
| Course 1 | C1 | topics | 0 | 5 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | section |
|
||||
| assign | Test assignment name | Test assignment description | C1 | assign1 | 0 |
|
||||
| book | Test book name | Test book description | C1 | book1 | 1 |
|
||||
| chat | Test chat name | Test chat description | C1 | chat1 | 4 |
|
||||
| choice | Test choice name | Test choice description | C1 | choice1 | 5 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
|
||||
@javascript
|
||||
Scenario: Highlight a section
|
||||
When I open section "2" edit menu
|
||||
And I click on "Highlight" "link" in the "Topic 2" "section"
|
||||
Then I should see "Highlighted" in the "Topic 2" "section"
|
||||
|
||||
@javascript
|
||||
Scenario: Highlight a section when another section is already highlighted
|
||||
Given I open section "3" edit menu
|
||||
And I click on "Highlight" "link" in the "Topic 3" "section"
|
||||
And I should see "Highlighted" in the "Topic 3" "section"
|
||||
When I open section "2" edit menu
|
||||
And I click on "Highlight" "link" in the "Topic 2" "section"
|
||||
Then I should see "Highlighted" in the "Topic 2" "section"
|
||||
And I should not see "Highlighted" in the "Topic 3" "section"
|
||||
|
||||
@javascript
|
||||
Scenario: Unhighlight a section
|
||||
Given I open section "3" edit menu
|
||||
And I click on "Highlight" "link" in the "Topic 3" "section"
|
||||
And I should see "Highlighted" in the "Topic 3" "section"
|
||||
When I open section "3" edit menu
|
||||
And I click on "Remove highlight" "link" in the "Topic 3" "section"
|
||||
Then I should not see "Highlighted" in the "Topic 3" "section"
|
222
course/format/topics/tests/courseformat/stateactions_test.php
Normal file
222
course/format/topics/tests/courseformat/stateactions_test.php
Normal file
@ -0,0 +1,222 @@
|
||||
<?php
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace format_topics\courseformat;
|
||||
|
||||
use core_courseformat\stateupdates;
|
||||
use moodle_exception;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Topics course format related unit tests.
|
||||
*
|
||||
* @package format_topics
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class stateactions_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Enrol a user into a course and login as this user.
|
||||
*
|
||||
* @param stdClass $course the course object
|
||||
* @param string $rolename the rolename
|
||||
*/
|
||||
private function enrol_user(stdClass $course, string $rolename): void {
|
||||
// Create and enrol user using given role.
|
||||
if ($rolename == 'admin') {
|
||||
$this->setAdminUser();
|
||||
} else {
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
if ($rolename != 'unenroled') {
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, $rolename);
|
||||
}
|
||||
$this->setUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for section_highlight method.
|
||||
*
|
||||
* @dataProvider basic_role_provider
|
||||
* @covers ::section_highlight
|
||||
* @param string $rolename The role of the user that will execute the method.
|
||||
* @param bool $expectedexception If this call will raise an exception.
|
||||
*/
|
||||
public function test_section_highlight(string $rolename, bool $expectedexception = false): void {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
$course = $generator->create_course(
|
||||
['numsections' => 4, 'format' => 'topics'],
|
||||
['createsections' => true]
|
||||
);
|
||||
|
||||
$this->enrol_user($course, $rolename);
|
||||
|
||||
$sectionrecords = $DB->get_records('course_sections', ['course' => $course->id], 'section');
|
||||
$sectionids = [];
|
||||
foreach ($sectionrecords as $section) {
|
||||
$sectionids[] = $section->id;
|
||||
}
|
||||
|
||||
// Initialise stateupdates.
|
||||
$courseformat = course_get_format($course->id);
|
||||
$updates = new stateupdates($courseformat);
|
||||
|
||||
// All state actions accepts batch editing (an array of sections in this case). However,
|
||||
// only one course section can be marked as highlighted. This means that if we send more
|
||||
// than one section id only the first one will be highlighted and the rest will be ignored.
|
||||
$methodparam = [
|
||||
$sectionids[1],
|
||||
$sectionids[2],
|
||||
$sectionids[3],
|
||||
];
|
||||
|
||||
// Actions have an array of ids as param but only the first one will be highlighted.
|
||||
$highlightid = reset($methodparam);
|
||||
$highlight = $sectionrecords[$highlightid];
|
||||
|
||||
if ($expectedexception) {
|
||||
$this->expectException(moodle_exception::class);
|
||||
}
|
||||
|
||||
// Execute given method.
|
||||
$actions = new stateactions();
|
||||
$actions->section_highlight(
|
||||
$updates,
|
||||
$course,
|
||||
$methodparam
|
||||
);
|
||||
|
||||
// Check state returned after executing given action.
|
||||
$updatelist = $updates->jsonSerialize();
|
||||
$this->assertCount(1, $updatelist);
|
||||
$update = reset($updatelist);
|
||||
$this->assertEquals('section', $update->name);
|
||||
$this->assertEquals('put', $update->action);
|
||||
$this->assertEquals($highlightid, $update->fields->id);
|
||||
$this->assertEquals(1, $update->fields->current);
|
||||
|
||||
// Check DB sections.
|
||||
$this->assertEquals($highlight->section, $DB->get_field("course", "marker", ['id' => $course->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for section_unhighlight method.
|
||||
*
|
||||
* @dataProvider basic_role_provider
|
||||
* @covers ::section_unhighlight
|
||||
* @param string $rolename The role of the user that will execute the method.
|
||||
* @param bool $expectedexception If this call will raise an exception.
|
||||
*/
|
||||
public function test_section_unhighlight(string $rolename, bool $expectedexception = false): void {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
$course = $generator->create_course(
|
||||
['numsections' => 4, 'format' => 'topics'],
|
||||
['createsections' => true]
|
||||
);
|
||||
|
||||
// Highlight section 1.
|
||||
course_set_marker($course->id, 1);
|
||||
|
||||
$this->enrol_user($course, $rolename);
|
||||
|
||||
$sectionrecords = $DB->get_records('course_sections', ['course' => $course->id], 'section');
|
||||
$sectionids = [];
|
||||
foreach ($sectionrecords as $section) {
|
||||
$sectionids[] = $section->id;
|
||||
}
|
||||
|
||||
// Initialise stateupdates.
|
||||
$courseformat = course_get_format($course->id);
|
||||
$updates = new stateupdates($courseformat);
|
||||
|
||||
// The section_unhighlight accepts extra sections to refresh the state data.
|
||||
$methodparam = [
|
||||
$sectionids[3],
|
||||
$sectionids[4],
|
||||
];
|
||||
|
||||
if ($expectedexception) {
|
||||
$this->expectException(moodle_exception::class);
|
||||
}
|
||||
|
||||
// Execute given method.
|
||||
$actions = new stateactions();
|
||||
$actions->section_unhighlight(
|
||||
$updates,
|
||||
$course,
|
||||
$methodparam
|
||||
);
|
||||
|
||||
// The Unhilight mutation always return the previous highlighted
|
||||
// section (1) and all the extra sections passed (3, and 4) to ensure
|
||||
// all of them are updated.
|
||||
$returnedsectionnumbers = [1, 3, 4];
|
||||
|
||||
// Check state returned after executing given action.
|
||||
$updatelist = $updates->jsonSerialize();
|
||||
$this->assertCount(3, $updatelist);
|
||||
foreach ($updatelist as $update) {
|
||||
$this->assertEquals('section', $update->name);
|
||||
$this->assertEquals('put', $update->action);
|
||||
$this->assertContains($update->fields->number, $returnedsectionnumbers);
|
||||
$this->assertEquals(0, $update->fields->current);
|
||||
}
|
||||
|
||||
// Check DB sections.
|
||||
$this->assertEquals(0, $DB->get_field("course", "marker", ['id' => $course->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for basic role tests.
|
||||
*
|
||||
* @return array the testing scenarios
|
||||
*/
|
||||
public function basic_role_provider(): array {
|
||||
return [
|
||||
'admin' => [
|
||||
'role' => 'admin',
|
||||
'expectedexception' => false,
|
||||
],
|
||||
'editingteacher' => [
|
||||
'role' => 'editingteacher',
|
||||
'expectedexception' => false,
|
||||
],
|
||||
'teacher' => [
|
||||
'role' => 'teacher',
|
||||
'expectedexception' => true,
|
||||
],
|
||||
'student' => [
|
||||
'role' => 'student',
|
||||
'expectedexception' => true,
|
||||
],
|
||||
'guest' => [
|
||||
'role' => 'guest',
|
||||
'expectedexception' => true,
|
||||
],
|
||||
'unenroled' => [
|
||||
'role' => 'unenroled',
|
||||
'expectedexception' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -2,6 +2,9 @@ This files describes API changes for course formats
|
||||
|
||||
Overview of this plugin type at http://docs.moodle.org/dev/Course_formats
|
||||
|
||||
=== 4.2 ===
|
||||
* New core_courseformat\base::get_context() to get the course context directly from the format instance.
|
||||
|
||||
=== 4.1 ===
|
||||
* New \core_courseformat\stateupdates methods add_section_remove() and add_cm_remove() have been added to replace
|
||||
the deprecated methods add_section_delete() and add_cm_delete().
|
||||
|
Loading…
x
Reference in New Issue
Block a user