Merge branch 'MDL-71134-master-v03' of git://github.com/ferranrecio/moodle

This commit is contained in:
Andrew Nicols 2021-06-14 11:52:35 +08:00
commit a9b0f4dafe
28 changed files with 1978 additions and 8 deletions

2
course/amd/build/courseeditor.min.js vendored Normal file
View file

@ -0,0 +1,2 @@
define ("core_course/courseeditor",["exports","core_course/local/courseeditor/mutations","core_course/local/courseeditor/courseeditor","core_course/events"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=a.courseEditor=void 0;b=e(b);c=e(c);d=e(d);function e(a){return a&&a.__esModule?a:{default:a}}function f(a,b){if(b===void 0){b=document}b.dispatchEvent(new CustomEvent(d.default.stateChanged,{bubbles:!0,detail:a}))}var g=new c.default({name:"CourseEditor",eventName:d.default.stateChanged,eventDispatch:f,mutations:new b.default});a.courseEditor=g;a.init=function init(a){g.loadCourse(a)}});
//# sourceMappingURL=courseeditor.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"sources":["../src/courseeditor.js"],"names":["dispatchStateChangedEvent","detail","target","document","dispatchEvent","CustomEvent","events","stateChanged","bubbles","courseEditor","CourseEditor","name","eventName","eventDispatch","mutations","DefaultMutations","init","courseId","loadCourse"],"mappings":"0QAuBA,OACA,OACA,O,mDAYA,QAASA,CAAAA,CAAT,CAAmCC,CAAnC,CAA2CC,CAA3C,CAAmD,CAC/C,GAAIA,CAAM,SAAV,CAA0B,CACtBA,CAAM,CAAGC,QACZ,CACDD,CAAM,CAACE,aAAP,CAAqB,GAAIC,CAAAA,WAAJ,CAAgBC,UAAOC,YAAvB,CAAqC,CACtDC,OAAO,GAD+C,CAEtDP,MAAM,CAAEA,CAF8C,CAArC,CAArB,CAIH,CAKM,GAAMQ,CAAAA,CAAY,CAAG,GAAIC,UAAJ,CAAiB,CACzCC,IAAI,CAAE,cADmC,CAEzCC,SAAS,CAAEN,UAAOC,YAFuB,CAGzCM,aAAa,CAAEb,CAH0B,CAMzCc,SAAS,CAAE,GAAIC,UAN0B,CAAjB,CAArB,C,wBAca,QAAPC,CAAAA,IAAO,CAACC,CAAD,CAAc,CAC9BR,CAAY,CAACS,UAAb,CAAwBD,CAAxB,CACH,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Generic reactive module used in the course editor.\n *\n * @module core_course/courseeditor\n * @copyright 2021 Ferran Recio <ferran@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DefaultMutations from 'core_course/local/courseeditor/mutations';\nimport CourseEditor from 'core_course/local/courseeditor/courseeditor';\nimport events from 'core_course/events';\n\n/**\n * Trigger a state changed event.\n *\n * This function will be moved to core_course/events module\n * when the file is migrated to the new JS events structure proposed in MDL-70990.\n *\n * @method dispatchStateChangedEvent\n * @param {object} detail the full state\n * @param {object} target the custom event target (document if none provided)\n */\nfunction dispatchStateChangedEvent(detail, target) {\n if (target === undefined) {\n target = document;\n }\n target.dispatchEvent(new CustomEvent(events.stateChanged, {\n bubbles: true,\n detail: detail,\n }));\n}\n\n/**\n * This is the courseditor instance all components will register in.\n */\nexport const courseEditor = new CourseEditor({\n name: 'CourseEditor',\n eventName: events.stateChanged,\n eventDispatch: dispatchStateChangedEvent,\n // Mutations can be overridden by the format plugin using setMutations\n // but we need the default one at least.\n mutations: new DefaultMutations(),\n});\n\n/**\n * This method is called only once to load the initial state when the page is ready.\n *\n * @param {int} courseId the current course id\n */\nexport const init = (courseId) => {\n courseEditor.loadCourse(courseId);\n};\n"],"file":"courseeditor.min.js"}

View file

@ -1,2 +1,2 @@
define ("core_course/events",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;a.default={favourited:"core_course:favourited",unfavorited:"core_course:unfavorited",manualCompletionToggled:"core_course:manualcompletiontoggled"};return a.default});
define ("core_course/events",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;a.default={favourited:"core_course:favourited",unfavorited:"core_course:unfavorited",manualCompletionToggled:"core_course:manualcompletiontoggled",stateChanged:"core_course:stateChanged"};return a.default});
//# sourceMappingURL=events.min.js.map

View file

@ -1 +1 @@
{"version":3,"sources":["../src/events.js"],"names":["favourited","unfavorited","manualCompletionToggled"],"mappings":"8IAuBe,CACXA,UAAU,CAAE,wBADD,CAEXC,WAAW,CAAE,yBAFF,CAGXC,uBAAuB,CAAE,qCAHd,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Contain the events the course component can trigger.\n *\n * @module core_course/events\n * @package core_course\n * @copyright 2018 Simey Lameze <simey@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default {\n favourited: 'core_course:favourited',\n unfavorited: 'core_course:unfavorited',\n manualCompletionToggled: 'core_course:manualcompletiontoggled',\n};\n"],"file":"events.min.js"}
{"version":3,"sources":["../src/events.js"],"names":["favourited","unfavorited","manualCompletionToggled","stateChanged"],"mappings":"8IAuBe,CACXA,UAAU,CAAE,wBADD,CAEXC,WAAW,CAAE,yBAFF,CAGXC,uBAAuB,CAAE,qCAHd,CAIXC,YAAY,CAAE,0BAJH,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Contain the events the course component can trigger.\n *\n * @module core_course/events\n * @package core_course\n * @copyright 2018 Simey Lameze <simey@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default {\n favourited: 'core_course:favourited',\n unfavorited: 'core_course:unfavorited',\n manualCompletionToggled: 'core_course:manualcompletiontoggled',\n stateChanged: 'core_course:stateChanged',\n};\n"],"file":"events.min.js"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"version":3,"sources":["../../../src/local/courseeditor/courseeditor.js"],"names":["courseId","Error","getServerCourseState","stateData","log","error","_editing","course","editmode","setInitialState","ajax","call","methodname","args","courseid","courseState","JSON","parse","section","cm","notification","exception","Reactive"],"mappings":"gOAgBA,OACA,OACA,O,qrGAsBqBA,C,+FAET,KAAKA,Q,sBACC,IAAIC,CAAAA,KAAJ,uBAAyBD,CAAzB,4CAAoE,KAAKA,QAAzE,E,QAGV,KAAKA,QAAL,CAAgBA,CAAhB,C,wBAKsB,MAAKE,oBAAL,E,QAAlBC,C,2DAEAC,UAAIC,KAAJ,CAAU,2CAAV,EACAD,UAAIC,KAAJ,O,kCAMJ,KAAKC,QAAL,WAAgBH,CAAS,CAACI,MAAV,CAAiBC,QAAjC,mBAEA,KAAKC,eAAL,CAAqBN,CAArB,E,4TAS0BO,WAAKC,IAAL,CAAU,CAAC,CACjCC,UAAU,CAAE,uBADqB,CAEjCC,IAAI,CAAE,CACFC,QAAQ,CAAE,KAAKd,QADb,CAF2B,CAAD,CAAV,EAKtB,CALsB,C,QAApBe,C,QAOAZ,C,CAAYa,IAAI,CAACC,KAAL,CAAWF,CAAX,C,6BAGdR,MAAM,CAAE,E,CACRW,OAAO,CAAE,E,CACTC,EAAE,CAAE,E,EACDhB,C,6UAyBOU,C,uBAAAA,C,yFAEcA,C,4DAExBO,UAAaC,SAAb,O,0JAlBQ,OACZ,iBAAO,KAAKf,QAAZ,kBACH,C,cAlEwBgB,U","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\nimport {Reactive} from 'core/reactive';\nimport notification from 'core/notification';\nimport log from 'core/log';\nimport ajax from 'core/ajax';\n\n/**\n * Main course editor module.\n *\n * All formats can register new components on this object to create new reactive\n * UI components that watch the current course state.\n *\n * @module core_course/local/courseeditor/courseeditor\n * @class core_course/local/courseeditor/courseeditor\n * @copyright 2021 Ferran Recio <ferran@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class extends Reactive {\n\n /**\n * Set up the course editor when the page is ready.\n *\n * The course can only be loaded once per instance. Otherwise an error is thrown.\n *\n * @param {int} courseId course id\n */\n async loadCourse(courseId) {\n\n if (this.courseId) {\n throw new Error(`Cannot load ${courseId}, course already loaded with id ${this.courseId}`);\n }\n\n this.courseId = courseId;\n\n let stateData;\n\n try {\n stateData = await this.getServerCourseState();\n } catch (error) {\n log.error(\"EXCEPTION RAISED WHILE INIT COURSE EDITOR\");\n log.error(error);\n return;\n }\n\n // Edit mode is part of the state but it could change over time.\n // Components should use isEditing method to check the editing mode instead.\n this._editing = stateData.course.editmode ?? false;\n\n this.setInitialState(stateData);\n }\n\n /**\n * Load the current course state from the server.\n *\n * @returns {Object} the current course state\n */\n async getServerCourseState() {\n const courseState = await ajax.call([{\n methodname: 'core_course_get_state',\n args: {\n courseid: this.courseId,\n }\n }])[0];\n\n const stateData = JSON.parse(courseState);\n\n return {\n course: {},\n section: [],\n cm: [],\n ...stateData,\n };\n }\n\n /**\n * Return the current edit mode.\n *\n * Components should use this method to check if edit mode is active.\n *\n * @return {boolean} if edit is enabled\n */\n get isEditing() {\n return this._editing ?? false;\n }\n\n /**\n * Dispatch a change in the state.\n *\n * Usually reactive modules throw an error directly to the components when something\n * goes wrong. However, course editor can directly display a notification.\n *\n * @method dispatch\n * @param {string} actionName the action name (usually the mutation name)\n * @param {*} param any number of params the mutation needs.\n */\n async dispatch(...args) {\n try {\n await super.dispatch(...args);\n } catch (error) {\n notification.exception(error);\n }\n }\n}\n"],"file":"courseeditor.min.js"}

View file

@ -0,0 +1,2 @@
define ("core_course/local/courseeditor/mutations",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function d(a){return function(){var b=this,d=arguments;return new Promise(function(e,f){var i=a.apply(b,d);function g(a){c(i,e,f,g,h,"next",a)}function h(a){c(i,e,f,g,h,"throw",a)}g(void 0)})}}function e(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function f(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function g(a,b,c){if(b)f(a.prototype,b);if(c)f(a,c);return a}var h=function(){function a(){e(this,a)}g(a,[{key:"_callEditWebservice",value:function(){var a=d(regeneratorRuntime.mark(function a(c,d,e){var f;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:a.next=2;return b.default.call([{methodname:"core_course_update_course",args:{action:c,courseid:d,ids:e}}])[0];case 2:f=a.sent;return a.abrupt("return",JSON.parse(f));case 4:case"end":return a.stop();}}},a)}));return function _callEditWebservice(){return a.apply(this,arguments)}}()},{key:"cmState",value:function(){var a=d(regeneratorRuntime.mark(function a(b,c){var d,e;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:d=b.state;a.next=3;return this._callEditWebservice("cm_state",d.course.id,c);case 3:e=a.sent;b.setReadOnly(!1);this._processUpdates(b,e);case 6:case"end":return a.stop();}}},a,this)}));return function cmState(){return a.apply(this,arguments)}}()},{key:"sectionState",value:function(){var a=d(regeneratorRuntime.mark(function a(b,c){var d,e;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:d=b.state;a.next=3;return this._callEditWebservice("section_state",d.course.id,c);case 3:e=a.sent;this._processUpdates(b,e);case 5:case"end":return a.stop();}}},a,this)}));return function sectionState(){return a.apply(this,arguments)}}()},{key:"_processUpdates",value:function _processUpdates(a,b){var c=a.state;a.setReadOnly(!1);b.forEach(function(b){if(b.name===void 0){throw Error("Missing state update name")}var d=c[b.name];if(d instanceof Map){d=c[b.name].get(b.fields.id)}if(!d){b.action="create"}a.processUpdate(b.name,b.action,b.fields)});a.setReadOnly(!0)}}]);return a}();a.default=h;return a.default});
//# sourceMappingURL=mutations.min.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,67 @@
// 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/>.
/**
* Generic reactive module used in the course editor.
*
* @module core_course/courseeditor
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import DefaultMutations from 'core_course/local/courseeditor/mutations';
import CourseEditor from 'core_course/local/courseeditor/courseeditor';
import events from 'core_course/events';
/**
* Trigger a state changed event.
*
* This function will be moved to core_course/events module
* when the file is migrated to the new JS events structure proposed in MDL-70990.
*
* @method dispatchStateChangedEvent
* @param {object} detail the full state
* @param {object} target the custom event target (document if none provided)
*/
function dispatchStateChangedEvent(detail, target) {
if (target === undefined) {
target = document;
}
target.dispatchEvent(new CustomEvent(events.stateChanged, {
bubbles: true,
detail: detail,
}));
}
/**
* This is the courseditor instance all components will register in.
*/
export const courseEditor = new CourseEditor({
name: 'CourseEditor',
eventName: events.stateChanged,
eventDispatch: dispatchStateChangedEvent,
// Mutations can be overridden by the format plugin using setMutations
// but we need the default one at least.
mutations: new DefaultMutations(),
});
/**
* This method is called only once to load the initial state when the page is ready.
*
* @param {int} courseId the current course id
*/
export const init = (courseId) => {
courseEditor.loadCourse(courseId);
};

View file

@ -25,4 +25,5 @@ export default {
favourited: 'core_course:favourited',
unfavorited: 'core_course:unfavorited',
manualCompletionToggled: 'core_course:manualcompletiontoggled',
stateChanged: 'core_course:stateChanged',
};

View file

@ -0,0 +1,117 @@
// 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/>.
import {Reactive} from 'core/reactive';
import notification from 'core/notification';
import log from 'core/log';
import ajax from 'core/ajax';
/**
* Main course editor module.
*
* All formats can register new components on this object to create new reactive
* UI components that watch the current course state.
*
* @module core_course/local/courseeditor/courseeditor
* @class core_course/local/courseeditor/courseeditor
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export default class extends Reactive {
/**
* Set up the course editor when the page is ready.
*
* The course can only be loaded once per instance. Otherwise an error is thrown.
*
* @param {int} courseId course id
*/
async loadCourse(courseId) {
if (this.courseId) {
throw new Error(`Cannot load ${courseId}, course already loaded with id ${this.courseId}`);
}
this.courseId = courseId;
let stateData;
try {
stateData = await this.getServerCourseState();
} catch (error) {
log.error("EXCEPTION RAISED WHILE INIT COURSE EDITOR");
log.error(error);
return;
}
// Edit mode is part of the state but it could change over time.
// Components should use isEditing method to check the editing mode instead.
this._editing = stateData.course.editmode ?? false;
this.setInitialState(stateData);
}
/**
* Load the current course state from the server.
*
* @returns {Object} the current course state
*/
async getServerCourseState() {
const courseState = await ajax.call([{
methodname: 'core_course_get_state',
args: {
courseid: this.courseId,
}
}])[0];
const stateData = JSON.parse(courseState);
return {
course: {},
section: [],
cm: [],
...stateData,
};
}
/**
* Return the current edit mode.
*
* Components should use this method to check if edit mode is active.
*
* @return {boolean} if edit is enabled
*/
get isEditing() {
return this._editing ?? false;
}
/**
* Dispatch a change in the state.
*
* Usually reactive modules throw an error directly to the components when something
* goes wrong. However, course editor can directly display a notification.
*
* @method dispatch
* @param {string} actionName the action name (usually the mutation name)
* @param {*} param any number of params the mutation needs.
*/
async dispatch(...args) {
try {
await super.dispatch(...args);
} catch (error) {
notification.exception(error);
}
}
}

View file

@ -0,0 +1,111 @@
// 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/>.
import ajax from 'core/ajax';
/**
* Default mutation manager
*
* @module core_course/local/courseeditor/mutations
* @class core_course/local/courseeditor/mutations
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export default class {
// All course editor mutations for Moodle 4.0 will be located in this file.
/**
* Private method to call core_course_update_course webservice.
*
* @method _callEditWebservice
* @param {string} action
* @param {int} courseId
* @param {array} ids
*/
async _callEditWebservice(action, courseId, ids) {
let ajaxresult = await ajax.call([{
methodname: 'core_course_update_course',
args: {
action,
courseid: courseId,
ids,
}
}])[0];
return JSON.parse(ajaxresult);
}
/**
* Get updated state data related to some cm ids.
*
* @method cmState
* @param {StateManager} statemanager the current state
* @param {array} cmids the list of cm ids to update
*/
async cmState(statemanager, cmids) {
const state = statemanager.state;
const updates = await this._callEditWebservice('cm_state', state.course.id, cmids);
statemanager.setReadOnly(false);
this._processUpdates(statemanager, updates);
}
/**
* Get updated state data related to some section ids.
*
* @method sectionState
* @param {StateManager} statemanager the current state
* @param {array} sectionIds the list of section ids to update
*/
async sectionState(statemanager, sectionIds) {
const state = statemanager.state;
const updates = await this._callEditWebservice('section_state', state.course.id, sectionIds);
this._processUpdates(statemanager, updates);
}
/**
* Helper to propcess both section_state and cm_state action results.
*
* @param {StateManager} statemanager the current state
* @param {Array} updates of updates.
*/
_processUpdates(statemanager, updates) {
const state = statemanager.state;
statemanager.setReadOnly(false);
// The cm_state and section_state state action returns only updated states. However, most of the time we need this
// mutation to fix discrepancies between the course content and the course state because core_course_edit_module
// does not provide enough information to rebuild some state objects. This is the reason why we cannot use
// the batch method processUpdates as the rest of mutations do.
updates.forEach((update) => {
if (update.name === undefined) {
throw Error('Missing state update name');
}
// Compare the action with the current state.
let current = state[update.name];
if (current instanceof Map) {
current = state[update.name].get(update.fields.id);
}
if (!current) {
update.action = 'create';
}
statemanager.processUpdate(update.name, update.action, update.fields);
});
statemanager.setReadOnly(true);
}
}

View file

@ -33,6 +33,7 @@ use section_info;
use context_course;
use editsection_form;
use moodle_exception;
use coding_exception;
use moodle_url;
use lang_string;
use completion_info;
@ -1074,16 +1075,24 @@ abstract class course_format {
/**
* Returns instance of output compornent used by this plugin
*
* @throws coding_exception if the format class does not extends the original core one.
* @param string $outputname the element to render (section, activity...)
* @return string the output component classname
*/
public function get_output_classname(?string $outputname): string {
public function get_output_classname(string $outputname): string {
// The core output class.
$baseclass = "core_course\\output\\$outputname";
// Check if there is a specific format class.
$component = 'format_'. $this->get_format();
$outputclass = "$component\\output\\$outputname";
if (class_exists($outputclass)) {
// Check that the outputclass is a subclass of the base class.
if (!is_subclass_of($outputclass, $baseclass)) {
throw new coding_exception("The \"$outputclass\" must extend \"$baseclass\"");
}
return $outputclass;
}
return "core_course\\output\\$outputname";
return $baseclass;
}
/**

View file

@ -3215,6 +3215,11 @@ function include_course_ajax($course, $usedmodules = array(), $enabledmodules =
return false;
}
// All the new editor elements will be loaded after the course is presented and
// the initial course state will be generated using core_course_get_state webservice.
$PAGE->requires->js_call_amd('core_course/courseeditor', 'init', [$course->id]);
// TODO: as part of MDL-70907, add a way to indicate the plugin needs the legacy libraries (and get a deprecation message).
if (!$config) {
$config = new stdClass();
}

View file

@ -24,11 +24,19 @@
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/course/tests/fixtures/format_theunittest.php');
class courseformat_test extends advanced_testcase {
/**
* Setup to ensure that fixtures are loaded.
*/
public static function setupBeforeClass(): void {
global $CFG;
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/course/tests/fixtures/format_theunittest.php');
require_once($CFG->dirroot . '/course/tests/fixtures/format_theunittest_output_course_format_state.php');
require_once($CFG->dirroot . '/course/tests/fixtures/format_theunittest_output_course_format_invalidoutput.php');
}
class core_course_courseformat_testcase extends advanced_testcase {
public function test_available_hook() {
global $DB;
$this->resetAfterTest();
@ -200,6 +208,53 @@ class core_course_courseformat_testcase extends advanced_testcase {
$this->assertNotEmpty($format->get_view_url(1, ['navigation' => 1]));
$this->assertNotEmpty($format->get_view_url(0, ['navigation' => 1]));
}
/**
* Test for get_output_classname method.
*
* @dataProvider get_output_classname_provider
* @param string $find the class to find
* @param string $result the expected result classname
* @param bool $exception if the method will raise an exception
*/
public function test_get_output_classname($find, $result, $exception) {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course(['format' => 'theunittest']);
$courseformat = course_get_format($course);
if ($exception) {
$this->expectException(coding_exception::class);
}
$courseclass = $courseformat->get_output_classname($find);
$this->assertEquals($result, $courseclass);
}
/**
* Data provider for test_get_output_classname.
*
* @return array the testing scenarios
*/
public function get_output_classname_provider(): array {
return [
'overridden class' => [
'find' => 'course_format\\state',
'result' => 'format_theunittest\\output\\course_format\\state',
'exception' => false,
],
'original class' => [
'find' => 'section_format\\state',
'result' => 'core_course\\output\\section_format\\state',
'exception' => false,
],
'invalid overridden class' => [
'find' => 'course_format\\invalidoutput',
'result' => '',
'exception' => true,
],
];
}
}
/**

View file

@ -0,0 +1,43 @@
<?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_theunittest\output\course_format;
use renderable;
use templatable;
use stdClass;
/**
* Fixture for an invalid output for testing get_output_classname.
*
* @package core_course
* @copyright 2021 Ferran Recio (ferran@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class invalidoutput implements renderable, templatable {
/**
* Export some data.
*
* @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 {
return (object)[
'something' => 'invalid',
];
}
}