mirror of
https://github.com/moodle/moodle.git
synced 2025-08-08 02:16:41 +02:00
Merge branch 'MDL-71134-master-v03' of git://github.com/ferranrecio/moodle
This commit is contained in:
commit
a9b0f4dafe
28 changed files with 1978 additions and 8 deletions
2
course/amd/build/courseeditor.min.js
vendored
Normal file
2
course/amd/build/courseeditor.min.js
vendored
Normal 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
|
1
course/amd/build/courseeditor.min.js.map
Normal file
1
course/amd/build/courseeditor.min.js.map
Normal 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"}
|
2
course/amd/build/events.min.js
vendored
2
course/amd/build/events.min.js
vendored
|
@ -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
|
||||
|
|
|
@ -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"}
|
2
course/amd/build/local/courseeditor/courseeditor.min.js
vendored
Normal file
2
course/amd/build/local/courseeditor/courseeditor.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -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"}
|
2
course/amd/build/local/courseeditor/mutations.min.js
vendored
Normal file
2
course/amd/build/local/courseeditor/mutations.min.js
vendored
Normal 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
|
1
course/amd/build/local/courseeditor/mutations.min.js.map
Normal file
1
course/amd/build/local/courseeditor/mutations.min.js.map
Normal file
File diff suppressed because one or more lines are too long
67
course/amd/src/courseeditor.js
Normal file
67
course/amd/src/courseeditor.js
Normal 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);
|
||||
};
|
|
@ -25,4 +25,5 @@ export default {
|
|||
favourited: 'core_course:favourited',
|
||||
unfavorited: 'core_course:unfavorited',
|
||||
manualCompletionToggled: 'core_course:manualcompletiontoggled',
|
||||
stateChanged: 'core_course:stateChanged',
|
||||
};
|
||||
|
|
117
course/amd/src/local/courseeditor/courseeditor.js
Normal file
117
course/amd/src/local/courseeditor/courseeditor.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
111
course/amd/src/local/courseeditor/mutations.js
Normal file
111
course/amd/src/local/courseeditor/mutations.js
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
43
course/tests/fixtures/format_theunittest_output_course_format_invalidoutput.php
vendored
Normal file
43
course/tests/fixtures/format_theunittest_output_course_format_invalidoutput.php
vendored
Normal 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',
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue