From 863dc40ddaa231e8c42a903b00cc60dfcc38f91b Mon Sep 17 00:00:00 2001 From: ferranrecio Date: Tue, 8 Oct 2024 20:09:31 +0200 Subject: [PATCH] MDL-82349 course: fix guest access to course --- .../amd/build/local/courseeditor/courseeditor.min.js | 4 ++-- .../build/local/courseeditor/courseeditor.min.js.map | 2 +- .../format/amd/src/local/courseeditor/courseeditor.js | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/course/format/amd/build/local/courseeditor/courseeditor.min.js b/course/format/amd/build/local/courseeditor/courseeditor.min.js index e6f2f0436c6f9..bd42ce87bed5b 100644 --- a/course/format/amd/build/local/courseeditor/courseeditor.min.js +++ b/course/format/amd/build/local/courseeditor/courseeditor.min.js @@ -1,4 +1,4 @@ -define("core_courseformat/local/courseeditor/courseeditor",["exports","core/str","core/reactive","core/notification","core_courseformat/local/courseeditor/exporter","core/log","core/ajax","core/sessionstorage","core_courseformat/local/courseeditor/fileuploader"],(function(_exports,_str,_reactive,_notification,_exporter,_log,_ajax,Storage,_fileuploader){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}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} +define("core_courseformat/local/courseeditor/courseeditor",["exports","core/config","core/str","core/reactive","core/notification","core_courseformat/local/courseeditor/exporter","core/log","core/ajax","core/sessionstorage","core_courseformat/local/courseeditor/fileuploader"],(function(_exports,_config,_str,_reactive,_notification,_exporter,_log,_ajax,Storage,_fileuploader){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}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} /** * Main course editor module. * @@ -9,6 +9,6 @@ define("core_courseformat/local/courseeditor/courseeditor",["exports","core/str" * @class core_courseformat/local/courseeditor/courseeditor * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_exporter=_interopRequireDefault(_exporter),_log=_interopRequireDefault(_log),_ajax=_interopRequireDefault(_ajax),Storage=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Storage);class _default extends _reactive.Reactive{constructor(){super(...arguments),_defineProperty(this,"stateKey",1),_defineProperty(this,"sectionReturn",null)}async loadCourse(courseId,serverStateKey){if(this.courseId)throw new Error("Cannot load ".concat(courseId,", course already loaded with id ").concat(this.courseId));let stateData;serverStateKey||(serverStateKey="invalidStateKey_".concat(Date.now())),this._editing=!1,this._supportscomponents=!1,this._fileHandlers=null,this.courseId=courseId;const storeStateKey=Storage.get("course/".concat(courseId,"/stateKey"));try{this.isEditing||serverStateKey!=storeStateKey||(stateData=JSON.parse(Storage.get("course/".concat(courseId,"/staticState")))),stateData||(stateData=await this.getServerCourseState())}catch(error){return _log.default.error("EXCEPTION RAISED WHILE INIT COURSE EDITOR"),void _log.default.error(error)}if(stateData.bulk={enabled:!1,selectedType:"",selection:[]},this.setInitialState(stateData),this.isEditing)this.stateKey=null;else{const newState=JSON.stringify(stateData);var _stateData$course$sta,_stateData,_stateData$course;if(Storage.get("course/".concat(courseId,"/staticState"))!==newState||storeStateKey!==serverStateKey)Storage.set("course/".concat(courseId,"/staticState"),newState),Storage.set("course/".concat(courseId,"/stateKey"),null!==(_stateData$course$sta=null===(_stateData=stateData)||void 0===_stateData||null===(_stateData$course=_stateData.course)||void 0===_stateData$course?void 0:_stateData$course.statekey)&&void 0!==_stateData$course$sta?_stateData$course$sta:serverStateKey);this.stateKey=Storage.get("course/".concat(courseId,"/stateKey"))}this._loadFileHandlers(),this._pageAnchorCmInfo=this._scanPageAnchorCmInfo()}_loadFileHandlers(){this._fileHandlersPromise=new Promise((resolve=>{if(!this.isEditing)return void resolve([]);const handlersCacheKey="course/".concat(this.courseId,"/fileHandlers"),cacheValue=Storage.get(handlersCacheKey);if(cacheValue)try{const cachedHandlers=JSON.parse(cacheValue);return void resolve(cachedHandlers)}catch(error){_log.default.error("ERROR PARSING CACHED FILE HANDLERS")}_ajax.default.call([{methodname:"core_courseformat_file_handlers",args:{courseid:this.courseId}}])[0].then((handlers=>{Storage.set(handlersCacheKey,JSON.stringify(handlers)),resolve(handlers)})).catch((error=>{_log.default.error(error),resolve([])}))}))}setViewFormat(setup){var _setup$editing,_setup$supportscompon,_setup$overriddenStri;this._editing=null!==(_setup$editing=setup.editing)&&void 0!==_setup$editing&&_setup$editing,this._supportscomponents=null!==(_setup$supportscompon=setup.supportscomponents)&&void 0!==_setup$supportscompon&&_setup$supportscompon;const overriddenStrings=null!==(_setup$overriddenStri=setup.overriddenStrings)&&void 0!==_setup$overriddenStri?_setup$overriddenStri:[];this._overriddenStrings=overriddenStrings.reduce(((indexed,currentValue)=>indexed.set(currentValue.key,currentValue)),new Map)}getFormatString(key,param){if(this._overriddenStrings.has(key)){var _override$component;const override=this._overriddenStrings.get(key);return(0,_str.getString)(key,null!==(_override$component=override.component)&&void 0!==_override$component?_override$component:"core_courseformat",param)}return(0,_str.getString)(key,"core_courseformat",param)}async getServerCourseState(){const courseState=await _ajax.default.call([{methodname:"core_courseformat_get_state",args:{courseid:this.courseId}}])[0];return{course:{},section:[],cm:[],...JSON.parse(courseState)}}get isEditing(){var _this$_editing;return null!==(_this$_editing=this._editing)&&void 0!==_this$_editing&&_this$_editing}getExporter(){return new _exporter.default(this)}get supportComponents(){var _this$_supportscompon;return null!==(_this$_supportscompon=this._supportscomponents)&&void 0!==_this$_supportscompon&&_this$_supportscompon}async getFileHandlersPromise(){var _this$_fileHandlersPr;return null!==(_this$_fileHandlersPr=this._fileHandlersPromise)&&void 0!==_this$_fileHandlersPr?_this$_fileHandlersPr:[]}uploadFiles(sectionId,sectionNum,files){return(0,_fileuploader.uploadFilesToCourse)(this.courseId,sectionId,sectionNum,files)}getStorageValue(key){if(this.isEditing||!this.stateKey)return!1;const dataJson=Storage.get("course/".concat(this.courseId,"/").concat(key));if(!dataJson)return!1;try{const data=JSON.parse(dataJson);return(null==data?void 0:data.stateKey)===this.stateKey&&data.value}catch(error){return!1}}setStorageValue(key,value){if(this.isEditing)return!1;const data={stateKey:this.stateKey,value:value};return Storage.set("course/".concat(this.courseId,"/").concat(key),JSON.stringify(data))}getFilesDraggableData(dataTransfer){return this.getExporter().fileDraggableData(this.state,dataTransfer)}async dispatch(){try{await super.dispatch(...arguments)}catch(error){_notification.default.exception(error),super.dispatch("unlockAll")}}_scanPageAnchorCmInfo(){const anchor=new URL(window.location.href).hash;if(!anchor.startsWith("#module-"))return null;const cmid=anchor.split("-")[1];return this.stateManager.get("cm",parseInt(cmid))}getPageAnchorCmInfo(){return this._pageAnchorCmInfo}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_config=_interopRequireDefault(_config),_notification=_interopRequireDefault(_notification),_exporter=_interopRequireDefault(_exporter),_log=_interopRequireDefault(_log),_ajax=_interopRequireDefault(_ajax),Storage=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Storage);class _default extends _reactive.Reactive{constructor(){super(...arguments),_defineProperty(this,"stateKey",1),_defineProperty(this,"sectionReturn",null)}async loadCourse(courseId,serverStateKey){if(this.courseId)throw new Error("Cannot load ".concat(courseId,", course already loaded with id ").concat(this.courseId));let stateData;serverStateKey||(serverStateKey="invalidStateKey_".concat(Date.now())),this._editing=!1,this._supportscomponents=!1,this._fileHandlers=null,this.courseId=courseId;const storeStateKey=Storage.get("course/".concat(courseId,"/stateKey"));try{this.isEditing||serverStateKey!=storeStateKey||(stateData=JSON.parse(Storage.get("course/".concat(courseId,"/staticState")))),stateData||(stateData=await this.getServerCourseState())}catch(error){return _log.default.error("EXCEPTION RAISED WHILE INIT COURSE EDITOR"),void _log.default.error(error)}if(stateData.bulk={enabled:!1,selectedType:"",selection:[]},this.setInitialState(stateData),this.isEditing)this.stateKey=null;else{const newState=JSON.stringify(stateData);var _stateData$course$sta,_stateData,_stateData$course;if(Storage.get("course/".concat(courseId,"/staticState"))!==newState||storeStateKey!==serverStateKey)Storage.set("course/".concat(courseId,"/staticState"),newState),Storage.set("course/".concat(courseId,"/stateKey"),null!==(_stateData$course$sta=null===(_stateData=stateData)||void 0===_stateData||null===(_stateData$course=_stateData.course)||void 0===_stateData$course?void 0:_stateData$course.statekey)&&void 0!==_stateData$course$sta?_stateData$course$sta:serverStateKey);this.stateKey=Storage.get("course/".concat(courseId,"/stateKey"))}this._loadFileHandlers(),this._pageAnchorCmInfo=this._scanPageAnchorCmInfo()}_loadFileHandlers(){this._fileHandlersPromise=new Promise((resolve=>{if(!this.isEditing)return void resolve([]);const handlersCacheKey="course/".concat(this.courseId,"/fileHandlers"),cacheValue=Storage.get(handlersCacheKey);if(cacheValue)try{const cachedHandlers=JSON.parse(cacheValue);return void resolve(cachedHandlers)}catch(error){_log.default.error("ERROR PARSING CACHED FILE HANDLERS")}_ajax.default.call([{methodname:"core_courseformat_file_handlers",args:{courseid:this.courseId}}])[0].then((handlers=>{Storage.set(handlersCacheKey,JSON.stringify(handlers)),resolve(handlers)})).catch((error=>{_log.default.error(error),resolve([])}))}))}setViewFormat(setup){var _setup$editing,_setup$supportscompon,_setup$overriddenStri;this._editing=null!==(_setup$editing=setup.editing)&&void 0!==_setup$editing&&_setup$editing,this._supportscomponents=null!==(_setup$supportscompon=setup.supportscomponents)&&void 0!==_setup$supportscompon&&_setup$supportscompon;const overriddenStrings=null!==(_setup$overriddenStri=setup.overriddenStrings)&&void 0!==_setup$overriddenStri?_setup$overriddenStri:[];this._overriddenStrings=overriddenStrings.reduce(((indexed,currentValue)=>indexed.set(currentValue.key,currentValue)),new Map)}getFormatString(key,param){if(this._overriddenStrings.has(key)){var _override$component;const override=this._overriddenStrings.get(key);return(0,_str.getString)(key,null!==(_override$component=override.component)&&void 0!==_override$component?_override$component:"core_courseformat",param)}return(0,_str.getString)(key,"core_courseformat",param)}async getServerCourseState(){if(0==_config.default.userId)return{course:{},section:[],cm:[]};const courseState=await _ajax.default.call([{methodname:"core_courseformat_get_state",args:{courseid:this.courseId}}])[0];return{course:{},section:[],cm:[],...JSON.parse(courseState)}}get isEditing(){var _this$_editing;return null!==(_this$_editing=this._editing)&&void 0!==_this$_editing&&_this$_editing}getExporter(){return new _exporter.default(this)}get supportComponents(){var _this$_supportscompon;return null!==(_this$_supportscompon=this._supportscomponents)&&void 0!==_this$_supportscompon&&_this$_supportscompon}async getFileHandlersPromise(){var _this$_fileHandlersPr;return null!==(_this$_fileHandlersPr=this._fileHandlersPromise)&&void 0!==_this$_fileHandlersPr?_this$_fileHandlersPr:[]}uploadFiles(sectionId,sectionNum,files){return(0,_fileuploader.uploadFilesToCourse)(this.courseId,sectionId,sectionNum,files)}getStorageValue(key){if(this.isEditing||!this.stateKey)return!1;const dataJson=Storage.get("course/".concat(this.courseId,"/").concat(key));if(!dataJson)return!1;try{const data=JSON.parse(dataJson);return(null==data?void 0:data.stateKey)===this.stateKey&&data.value}catch(error){return!1}}setStorageValue(key,value){if(this.isEditing)return!1;const data={stateKey:this.stateKey,value:value};return Storage.set("course/".concat(this.courseId,"/").concat(key),JSON.stringify(data))}getFilesDraggableData(dataTransfer){return this.getExporter().fileDraggableData(this.state,dataTransfer)}async dispatch(){try{await super.dispatch(...arguments)}catch(error){_notification.default.exception(error),super.dispatch("unlockAll")}}_scanPageAnchorCmInfo(){const anchor=new URL(window.location.href).hash;if(!anchor.startsWith("#module-"))return null;const cmid=anchor.split("-")[1];return this.stateManager.get("cm",parseInt(cmid))}getPageAnchorCmInfo(){return this._pageAnchorCmInfo}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=courseeditor.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/courseeditor.min.js.map b/course/format/amd/build/local/courseeditor/courseeditor.min.js.map index b84053be7863a..18578444ac916 100644 --- a/course/format/amd/build/local/courseeditor/courseeditor.min.js.map +++ b/course/format/amd/build/local/courseeditor/courseeditor.min.js.map @@ -1 +1 @@ -{"version":3,"file":"courseeditor.min.js","sources":["../../../src/local/courseeditor/courseeditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport {getString} from 'core/str';\nimport {Reactive} from 'core/reactive';\nimport notification from 'core/notification';\nimport Exporter from 'core_courseformat/local/courseeditor/exporter';\nimport log from 'core/log';\nimport ajax from 'core/ajax';\nimport * as Storage from 'core/sessionstorage';\nimport {uploadFilesToCourse} from 'core_courseformat/local/courseeditor/fileuploader';\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_courseformat/local/courseeditor/courseeditor\n * @class core_courseformat/local/courseeditor/courseeditor\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class extends Reactive {\n\n /**\n * The current state cache key\n *\n * The state cache is considered dirty if the state changes from the last page or\n * if the page has editing mode on.\n *\n * @attribute stateKey\n * @type number|null\n * @default 1\n * @package\n */\n stateKey = 1;\n\n /**\n * The current page section return\n * @attribute sectionReturn\n * @type number\n * @default null\n */\n sectionReturn = null;\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 * The backend can inform the module of the current state key. This key changes every time some\n * update in the course affect the current user state. Some examples are:\n * - The course content has been edited\n * - The user marks some activity as completed\n * - The user collapses or uncollapses a section (it is stored as a user preference)\n *\n * @param {number} courseId course id\n * @param {string} serverStateKey the current backend course cache reference\n */\n async loadCourse(courseId, serverStateKey) {\n\n if (this.courseId) {\n throw new Error(`Cannot load ${courseId}, course already loaded with id ${this.courseId}`);\n }\n\n if (!serverStateKey) {\n // The server state key is not provided, we use a invalid statekey to force reloading.\n serverStateKey = `invalidStateKey_${Date.now()}`;\n }\n\n // Default view format setup.\n this._editing = false;\n this._supportscomponents = false;\n this._fileHandlers = null;\n\n this.courseId = courseId;\n\n let stateData;\n\n const storeStateKey = Storage.get(`course/${courseId}/stateKey`);\n try {\n // Check if the backend state key is the same we have in our session storage.\n if (!this.isEditing && serverStateKey == storeStateKey) {\n stateData = JSON.parse(Storage.get(`course/${courseId}/staticState`));\n }\n if (!stateData) {\n stateData = await this.getServerCourseState();\n }\n\n } catch (error) {\n log.error(\"EXCEPTION RAISED WHILE INIT COURSE EDITOR\");\n log.error(error);\n return;\n }\n\n // The bulk editing only applies to the frontend and the state data is not created in the backend.\n stateData.bulk = {\n enabled: false,\n selectedType: '',\n selection: [],\n };\n\n this.setInitialState(stateData);\n\n // In editing mode, the session cache is considered dirty always.\n if (this.isEditing) {\n this.stateKey = null;\n } else {\n // Check if the last state is the same as the cached one.\n const newState = JSON.stringify(stateData);\n const previousState = Storage.get(`course/${courseId}/staticState`);\n if (previousState !== newState || storeStateKey !== serverStateKey) {\n Storage.set(`course/${courseId}/staticState`, newState);\n Storage.set(`course/${courseId}/stateKey`, stateData?.course?.statekey ?? serverStateKey);\n }\n this.stateKey = Storage.get(`course/${courseId}/stateKey`);\n }\n\n this._loadFileHandlers();\n\n this._pageAnchorCmInfo = this._scanPageAnchorCmInfo();\n }\n\n /**\n * Load the file hanlders promise.\n */\n _loadFileHandlers() {\n // Load the course file extensions.\n this._fileHandlersPromise = new Promise((resolve) => {\n if (!this.isEditing) {\n resolve([]);\n return;\n }\n // Check the cache.\n const handlersCacheKey = `course/${this.courseId}/fileHandlers`;\n\n const cacheValue = Storage.get(handlersCacheKey);\n if (cacheValue) {\n try {\n const cachedHandlers = JSON.parse(cacheValue);\n resolve(cachedHandlers);\n return;\n } catch (error) {\n log.error(\"ERROR PARSING CACHED FILE HANDLERS\");\n }\n }\n // Call file handlers webservice.\n ajax.call([{\n methodname: 'core_courseformat_file_handlers',\n args: {\n courseid: this.courseId,\n }\n }])[0].then((handlers) => {\n Storage.set(handlersCacheKey, JSON.stringify(handlers));\n resolve(handlers);\n return;\n }).catch(error => {\n log.error(error);\n resolve([]);\n return;\n });\n });\n }\n\n /**\n * Setup the current view settings\n *\n * @param {Object} setup format, page and course settings\n * @param {boolean} setup.editing if the page is in edit mode\n * @param {boolean} setup.supportscomponents if the format supports components for content\n * @param {string} setup.cacherev the backend cached state revision\n * @param {Array} setup.overriddenStrings optional overridden strings\n */\n setViewFormat(setup) {\n this._editing = setup.editing ?? false;\n this._supportscomponents = setup.supportscomponents ?? false;\n const overriddenStrings = setup.overriddenStrings ?? [];\n this._overriddenStrings = overriddenStrings.reduce(\n (indexed, currentValue) => indexed.set(currentValue.key, currentValue),\n new Map()\n );\n }\n\n /**\n * Execute a get string for a possible format overriden editor string.\n *\n * Return the proper getString promise for an editor string using the core_courseformat\n * of the format_PLUGINNAME compoment depending on the current view format setup.\n * @param {String} key the string key\n * @param {string|undefined} param The param for variable expansion in the string.\n * @returns {Promise} a getString promise\n */\n getFormatString(key, param) {\n if (this._overriddenStrings.has(key)) {\n const override = this._overriddenStrings.get(key);\n return getString(key, override.component ?? 'core_courseformat', param);\n }\n // All format overridable strings are from core_courseformat lang file.\n return getString(key, 'core_courseformat', param);\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_courseformat_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 * Return a data exporter to transform state part into mustache contexts.\n *\n * @return {Exporter} the exporter class\n */\n getExporter() {\n return new Exporter(this);\n }\n\n /**\n * Return if the current course support components to refresh the content.\n *\n * @returns {boolean} if the current content support components\n */\n get supportComponents() {\n return this._supportscomponents ?? false;\n }\n\n /**\n * Return the course file handlers promise.\n * @returns {Promise} the promise for file handlers.\n */\n async getFileHandlersPromise() {\n return this._fileHandlersPromise ?? [];\n }\n\n /**\n * Upload a file list to the course.\n *\n * This method is a wrapper to the course file uploader.\n *\n * @param {number} sectionId the section id\n * @param {number} sectionNum the section number\n * @param {Array} files and array of files\n * @return {Promise} the file queue promise\n */\n uploadFiles(sectionId, sectionNum, files) {\n return uploadFilesToCourse(this.courseId, sectionId, sectionNum, files);\n }\n\n /**\n * Get a value from the course editor static storage if any.\n *\n * The course editor static storage uses the sessionStorage to store values from the\n * components. This is used to prevent unnecesary template loadings on every page. However,\n * the storage does not work if no sessionStorage can be used (in debug mode for example),\n * if the page is in editing mode or if the initial state change from the last page.\n *\n * @param {string} key the key to get\n * @return {boolean|string} the storage value or false if cannot be loaded\n */\n getStorageValue(key) {\n if (this.isEditing || !this.stateKey) {\n return false;\n }\n const dataJson = Storage.get(`course/${this.courseId}/${key}`);\n if (!dataJson) {\n return false;\n }\n // Check the stateKey.\n try {\n const data = JSON.parse(dataJson);\n if (data?.stateKey !== this.stateKey) {\n return false;\n }\n return data.value;\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Stores a value into the course editor static storage if available\n *\n * @param {String} key the key to store\n * @param {*} value the value to store (must be compatible with JSON,stringify)\n * @returns {boolean} true if the value is stored\n */\n setStorageValue(key, value) {\n // Values cannot be stored on edit mode.\n if (this.isEditing) {\n return false;\n }\n const data = {\n stateKey: this.stateKey,\n value,\n };\n return Storage.set(`course/${this.courseId}/${key}`, JSON.stringify(data));\n }\n\n /**\n * Convert a file dragging event into a proper dragging file list.\n * @param {DataTransfer} dataTransfer the event to convert\n * @return {Array} of file list info.\n */\n getFilesDraggableData(dataTransfer) {\n const exporter = this.getExporter();\n return exporter.fileDraggableData(this.state, dataTransfer);\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 {mixed} args any number of params the mutation needs.\n */\n async dispatch(...args) {\n try {\n await super.dispatch(...args);\n } catch (error) {\n // Display error modal.\n notification.exception(error);\n // Force unlock all elements.\n super.dispatch('unlockAll');\n }\n }\n\n /**\n * Calculate the cm info from the current page anchor.\n *\n * @returns {Object|null} the cm info or null if not found.\n */\n _scanPageAnchorCmInfo() {\n const anchor = new URL(window.location.href).hash;\n if (!anchor.startsWith('#module-')) {\n return null;\n }\n // The anchor is always #module-CMID.\n const cmid = anchor.split('-')[1];\n return this.stateManager.get('cm', parseInt(cmid));\n }\n\n /**\n * Return the current page anchor cm info.\n */\n getPageAnchorCmInfo() {\n return this._pageAnchorCmInfo;\n }\n}\n"],"names":["Reactive","courseId","serverStateKey","this","Error","stateData","Date","now","_editing","_supportscomponents","_fileHandlers","storeStateKey","Storage","get","isEditing","JSON","parse","getServerCourseState","error","bulk","enabled","selectedType","selection","setInitialState","stateKey","newState","stringify","set","_stateData","course","_stateData$course","statekey","_loadFileHandlers","_pageAnchorCmInfo","_scanPageAnchorCmInfo","_fileHandlersPromise","Promise","resolve","handlersCacheKey","cacheValue","cachedHandlers","call","methodname","args","courseid","then","handlers","catch","setViewFormat","setup","editing","supportscomponents","overriddenStrings","_overriddenStrings","reduce","indexed","currentValue","key","Map","getFormatString","param","has","override","component","courseState","ajax","section","cm","getExporter","Exporter","supportComponents","uploadFiles","sectionId","sectionNum","files","getStorageValue","dataJson","data","value","setStorageValue","getFilesDraggableData","dataTransfer","fileDraggableData","state","super","dispatch","exception","anchor","URL","window","location","href","hash","startsWith","cmid","split","stateManager","parseInt","getPageAnchorCmInfo"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"courseeditor.min.js","sources":["../../../src/local/courseeditor/courseeditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport Config from 'core/config';\nimport {getString} from 'core/str';\nimport {Reactive} from 'core/reactive';\nimport notification from 'core/notification';\nimport Exporter from 'core_courseformat/local/courseeditor/exporter';\nimport log from 'core/log';\nimport ajax from 'core/ajax';\nimport * as Storage from 'core/sessionstorage';\nimport {uploadFilesToCourse} from 'core_courseformat/local/courseeditor/fileuploader';\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_courseformat/local/courseeditor/courseeditor\n * @class core_courseformat/local/courseeditor/courseeditor\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class extends Reactive {\n\n /**\n * The current state cache key\n *\n * The state cache is considered dirty if the state changes from the last page or\n * if the page has editing mode on.\n *\n * @attribute stateKey\n * @type number|null\n * @default 1\n * @package\n */\n stateKey = 1;\n\n /**\n * The current page section return\n * @attribute sectionReturn\n * @type number\n * @default null\n */\n sectionReturn = null;\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 * The backend can inform the module of the current state key. This key changes every time some\n * update in the course affect the current user state. Some examples are:\n * - The course content has been edited\n * - The user marks some activity as completed\n * - The user collapses or uncollapses a section (it is stored as a user preference)\n *\n * @param {number} courseId course id\n * @param {string} serverStateKey the current backend course cache reference\n */\n async loadCourse(courseId, serverStateKey) {\n\n if (this.courseId) {\n throw new Error(`Cannot load ${courseId}, course already loaded with id ${this.courseId}`);\n }\n\n if (!serverStateKey) {\n // The server state key is not provided, we use a invalid statekey to force reloading.\n serverStateKey = `invalidStateKey_${Date.now()}`;\n }\n\n // Default view format setup.\n this._editing = false;\n this._supportscomponents = false;\n this._fileHandlers = null;\n\n this.courseId = courseId;\n\n let stateData;\n\n const storeStateKey = Storage.get(`course/${courseId}/stateKey`);\n try {\n // Check if the backend state key is the same we have in our session storage.\n if (!this.isEditing && serverStateKey == storeStateKey) {\n stateData = JSON.parse(Storage.get(`course/${courseId}/staticState`));\n }\n if (!stateData) {\n stateData = await this.getServerCourseState();\n }\n\n } catch (error) {\n log.error(\"EXCEPTION RAISED WHILE INIT COURSE EDITOR\");\n log.error(error);\n return;\n }\n\n // The bulk editing only applies to the frontend and the state data is not created in the backend.\n stateData.bulk = {\n enabled: false,\n selectedType: '',\n selection: [],\n };\n\n this.setInitialState(stateData);\n\n // In editing mode, the session cache is considered dirty always.\n if (this.isEditing) {\n this.stateKey = null;\n } else {\n // Check if the last state is the same as the cached one.\n const newState = JSON.stringify(stateData);\n const previousState = Storage.get(`course/${courseId}/staticState`);\n if (previousState !== newState || storeStateKey !== serverStateKey) {\n Storage.set(`course/${courseId}/staticState`, newState);\n Storage.set(`course/${courseId}/stateKey`, stateData?.course?.statekey ?? serverStateKey);\n }\n this.stateKey = Storage.get(`course/${courseId}/stateKey`);\n }\n\n this._loadFileHandlers();\n\n this._pageAnchorCmInfo = this._scanPageAnchorCmInfo();\n }\n\n /**\n * Load the file hanlders promise.\n */\n _loadFileHandlers() {\n // Load the course file extensions.\n this._fileHandlersPromise = new Promise((resolve) => {\n if (!this.isEditing) {\n resolve([]);\n return;\n }\n // Check the cache.\n const handlersCacheKey = `course/${this.courseId}/fileHandlers`;\n\n const cacheValue = Storage.get(handlersCacheKey);\n if (cacheValue) {\n try {\n const cachedHandlers = JSON.parse(cacheValue);\n resolve(cachedHandlers);\n return;\n } catch (error) {\n log.error(\"ERROR PARSING CACHED FILE HANDLERS\");\n }\n }\n // Call file handlers webservice.\n ajax.call([{\n methodname: 'core_courseformat_file_handlers',\n args: {\n courseid: this.courseId,\n }\n }])[0].then((handlers) => {\n Storage.set(handlersCacheKey, JSON.stringify(handlers));\n resolve(handlers);\n return;\n }).catch(error => {\n log.error(error);\n resolve([]);\n return;\n });\n });\n }\n\n /**\n * Setup the current view settings\n *\n * @param {Object} setup format, page and course settings\n * @param {boolean} setup.editing if the page is in edit mode\n * @param {boolean} setup.supportscomponents if the format supports components for content\n * @param {string} setup.cacherev the backend cached state revision\n * @param {Array} setup.overriddenStrings optional overridden strings\n */\n setViewFormat(setup) {\n this._editing = setup.editing ?? false;\n this._supportscomponents = setup.supportscomponents ?? false;\n const overriddenStrings = setup.overriddenStrings ?? [];\n this._overriddenStrings = overriddenStrings.reduce(\n (indexed, currentValue) => indexed.set(currentValue.key, currentValue),\n new Map()\n );\n }\n\n /**\n * Execute a get string for a possible format overriden editor string.\n *\n * Return the proper getString promise for an editor string using the core_courseformat\n * of the format_PLUGINNAME compoment depending on the current view format setup.\n * @param {String} key the string key\n * @param {string|undefined} param The param for variable expansion in the string.\n * @returns {Promise} a getString promise\n */\n getFormatString(key, param) {\n if (this._overriddenStrings.has(key)) {\n const override = this._overriddenStrings.get(key);\n return getString(key, override.component ?? 'core_courseformat', param);\n }\n // All format overridable strings are from core_courseformat lang file.\n return getString(key, 'core_courseformat', param);\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 // Only logged users can get the course state. Filtering here will prevent unnecessary\n // calls to the server and login page redirects. Especially for home activities with\n // guest access.\n if (Config.userId == 0) {\n return {\n course: {},\n section: [],\n cm: [],\n };\n }\n const courseState = await ajax.call([{\n methodname: 'core_courseformat_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 * Return a data exporter to transform state part into mustache contexts.\n *\n * @return {Exporter} the exporter class\n */\n getExporter() {\n return new Exporter(this);\n }\n\n /**\n * Return if the current course support components to refresh the content.\n *\n * @returns {boolean} if the current content support components\n */\n get supportComponents() {\n return this._supportscomponents ?? false;\n }\n\n /**\n * Return the course file handlers promise.\n * @returns {Promise} the promise for file handlers.\n */\n async getFileHandlersPromise() {\n return this._fileHandlersPromise ?? [];\n }\n\n /**\n * Upload a file list to the course.\n *\n * This method is a wrapper to the course file uploader.\n *\n * @param {number} sectionId the section id\n * @param {number} sectionNum the section number\n * @param {Array} files and array of files\n * @return {Promise} the file queue promise\n */\n uploadFiles(sectionId, sectionNum, files) {\n return uploadFilesToCourse(this.courseId, sectionId, sectionNum, files);\n }\n\n /**\n * Get a value from the course editor static storage if any.\n *\n * The course editor static storage uses the sessionStorage to store values from the\n * components. This is used to prevent unnecesary template loadings on every page. However,\n * the storage does not work if no sessionStorage can be used (in debug mode for example),\n * if the page is in editing mode or if the initial state change from the last page.\n *\n * @param {string} key the key to get\n * @return {boolean|string} the storage value or false if cannot be loaded\n */\n getStorageValue(key) {\n if (this.isEditing || !this.stateKey) {\n return false;\n }\n const dataJson = Storage.get(`course/${this.courseId}/${key}`);\n if (!dataJson) {\n return false;\n }\n // Check the stateKey.\n try {\n const data = JSON.parse(dataJson);\n if (data?.stateKey !== this.stateKey) {\n return false;\n }\n return data.value;\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Stores a value into the course editor static storage if available\n *\n * @param {String} key the key to store\n * @param {*} value the value to store (must be compatible with JSON,stringify)\n * @returns {boolean} true if the value is stored\n */\n setStorageValue(key, value) {\n // Values cannot be stored on edit mode.\n if (this.isEditing) {\n return false;\n }\n const data = {\n stateKey: this.stateKey,\n value,\n };\n return Storage.set(`course/${this.courseId}/${key}`, JSON.stringify(data));\n }\n\n /**\n * Convert a file dragging event into a proper dragging file list.\n * @param {DataTransfer} dataTransfer the event to convert\n * @return {Array} of file list info.\n */\n getFilesDraggableData(dataTransfer) {\n const exporter = this.getExporter();\n return exporter.fileDraggableData(this.state, dataTransfer);\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 {mixed} args any number of params the mutation needs.\n */\n async dispatch(...args) {\n try {\n await super.dispatch(...args);\n } catch (error) {\n // Display error modal.\n notification.exception(error);\n // Force unlock all elements.\n super.dispatch('unlockAll');\n }\n }\n\n /**\n * Calculate the cm info from the current page anchor.\n *\n * @returns {Object|null} the cm info or null if not found.\n */\n _scanPageAnchorCmInfo() {\n const anchor = new URL(window.location.href).hash;\n if (!anchor.startsWith('#module-')) {\n return null;\n }\n // The anchor is always #module-CMID.\n const cmid = anchor.split('-')[1];\n return this.stateManager.get('cm', parseInt(cmid));\n }\n\n /**\n * Return the current page anchor cm info.\n */\n getPageAnchorCmInfo() {\n return this._pageAnchorCmInfo;\n }\n}\n"],"names":["Reactive","courseId","serverStateKey","this","Error","stateData","Date","now","_editing","_supportscomponents","_fileHandlers","storeStateKey","Storage","get","isEditing","JSON","parse","getServerCourseState","error","bulk","enabled","selectedType","selection","setInitialState","stateKey","newState","stringify","set","_stateData","course","_stateData$course","statekey","_loadFileHandlers","_pageAnchorCmInfo","_scanPageAnchorCmInfo","_fileHandlersPromise","Promise","resolve","handlersCacheKey","cacheValue","cachedHandlers","call","methodname","args","courseid","then","handlers","catch","setViewFormat","setup","editing","supportscomponents","overriddenStrings","_overriddenStrings","reduce","indexed","currentValue","key","Map","getFormatString","param","has","override","component","Config","userId","section","cm","courseState","ajax","getExporter","Exporter","supportComponents","uploadFiles","sectionId","sectionNum","files","getStorageValue","dataJson","data","value","setStorageValue","getFilesDraggableData","dataTransfer","fileDraggableData","state","super","dispatch","exception","anchor","URL","window","location","href","hash","startsWith","cmid","split","stateManager","parseInt","getPageAnchorCmInfo"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/src/local/courseeditor/courseeditor.js b/course/format/amd/src/local/courseeditor/courseeditor.js index 6be871a155d87..76c9203c38fec 100644 --- a/course/format/amd/src/local/courseeditor/courseeditor.js +++ b/course/format/amd/src/local/courseeditor/courseeditor.js @@ -13,6 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +import Config from 'core/config'; import {getString} from 'core/str'; import {Reactive} from 'core/reactive'; import notification from 'core/notification'; @@ -218,6 +219,16 @@ export default class extends Reactive { * @returns {Object} the current course state */ async getServerCourseState() { + // Only logged users can get the course state. Filtering here will prevent unnecessary + // calls to the server and login page redirects. Especially for home activities with + // guest access. + if (Config.userId == 0) { + return { + course: {}, + section: [], + cm: [], + }; + } const courseState = await ajax.call([{ methodname: 'core_courseformat_get_state', args: {