first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
define("mod_data/deletepreset",["exports","core/notification","core/prefetch","core/str","core/ajax","core/url"],(function(_exports,_notification,_prefetch,_str,_ajax,_url){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript module for deleting a database as a preset.
*
* @module mod_data/deletepreset
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url);const selectors_deletePresetButton='[data-action="deletepreset"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["deleteconfirm","deletewarning"]),(0,_prefetch.prefetchStrings)("core",["delete"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const deleteOption=event.target.closest(selectors_deletePresetButton);deleteOption&&(event.preventDefault(),deletePresetConfirm(deleteOption))}))},deletePresetConfirm=deleteOption=>{const presetName=deleteOption.getAttribute("data-presetname"),dataId=deleteOption.getAttribute("data-dataid");_notification.default.deleteCancelPromise((0,_str.getString)("deleteconfirm","mod_data",presetName),(0,_str.getString)("deletewarning","mod_data")).then((()=>async function(dataId,presetName){var request={methodname:"mod_data_delete_saved_preset",args:{dataid:dataId,presetnames:{presetname:presetName}}};try{await _ajax.default.call([request])[0],window.location.href=_url.default.relativeUrl("mod/data/preset.php",{d:dataId},!1)}catch(error){_notification.default.exception(error)}}(dataId,presetName))).catch((()=>{}))}}));
//# sourceMappingURL=deletepreset.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"deletepreset.min.js","sources":["../src/deletepreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module for deleting a database as a preset.\n *\n * @module mod_data/deletepreset\n * @copyright 2022 Amaia Anabitarte <amaia@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Url from 'core/url';\n\nconst selectors = {\n deletePresetButton: '[data-action=\"deletepreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'deleteconfirm',\n 'deletewarning',\n ]);\n prefetchStrings('core', [\n 'delete',\n ]);\n\n registerEventListeners();\n};\n\n/**\n * Register events for delete preset option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const deleteOption = event.target.closest(selectors.deletePresetButton);\n if (deleteOption) {\n event.preventDefault();\n deletePresetConfirm(deleteOption);\n }\n });\n};\n\n/**\n * Show the confirmation modal to delete the preset.\n *\n * @param {HTMLElement} deleteOption the element to delete.\n */\nconst deletePresetConfirm = (deleteOption) => {\n const presetName = deleteOption.getAttribute('data-presetname');\n const dataId = deleteOption.getAttribute('data-dataid');\n\n Notification.deleteCancelPromise(\n getString('deleteconfirm', 'mod_data', presetName),\n getString('deletewarning', 'mod_data'),\n ).then(() => {\n return deletePreset(dataId, presetName);\n }).catch(() => {\n return;\n });\n};\n\n/**\n * Delete site user preset.\n *\n * @param {int} dataId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {promise} Resolved with the result and warnings of deleting a preset.\n */\nasync function deletePreset(dataId, presetName) {\n var request = {\n methodname: 'mod_data_delete_saved_preset',\n args: {\n dataid: dataId,\n presetnames: {presetname: presetName},\n }\n };\n try {\n await Ajax.call([request])[0];\n window.location.href = Url.relativeUrl(\n 'mod/data/preset.php',\n {\n d: dataId,\n },\n false\n );\n } catch (error) {\n Notification.exception(error);\n }\n}\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","deleteOption","target","closest","preventDefault","deletePresetConfirm","presetName","getAttribute","dataId","deleteCancelPromise","then","request","methodname","args","dataid","presetnames","presetname","Ajax","call","window","location","href","Url","relativeUrl","d","error","exception","deletePreset","catch"],"mappings":";;;;;;;kNA6BMA,6BACkB,6CAMJ,mCACA,WAAY,CACxB,gBACA,gDAEY,OAAQ,CACpB,WAGJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,aAAeD,MAAME,OAAOC,QAAQP,8BACtCK,eACAD,MAAMI,iBACNC,oBAAoBJ,mBAU1BI,oBAAuBJ,qBACnBK,WAAaL,aAAaM,aAAa,mBACvCC,OAASP,aAAaM,aAAa,qCAE5BE,qBACT,kBAAU,gBAAiB,WAAYH,aACvC,kBAAU,gBAAiB,aAC7BI,MAAK,mBAciBF,OAAQF,gBAC5BK,QAAU,CACVC,WAAY,+BACZC,KAAM,CACFC,OAAQN,OACRO,YAAa,CAACC,WAAYV,wBAIxBW,cAAKC,KAAK,CAACP,UAAU,GAC3BQ,OAAOC,SAASC,KAAOC,aAAIC,YACvB,sBACA,CACIC,EAAGhB,SAEP,GAEN,MAAOiB,6BACQC,UAAUD,QA/BhBE,CAAanB,OAAQF,cAC7BsB,OAAM"}
+10
View File
@@ -0,0 +1,10 @@
define("mod_data/editpreset",["exports","core_form/modalform","core/notification","core/str"],(function(_exports,_modalform,_notification,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript module for editing a database preset.
*
* @module mod_data/editpreset
* @copyright 2022 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_editPresetButton='[data-action="editpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const editAction=event.target.closest(selectors_editPresetButton);editAction&&(event.preventDefault(),showEditPresetModal(editAction))}))},showEditPresetModal=editAction=>{const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("editpreset","mod_data")},formClass:"mod_data\\form\\save_as_preset",args:{d:editAction.getAttribute("data-dataid"),action:editAction.getAttribute("data-action"),presetname:editAction.getAttribute("data-presetname"),presetdescription:editAction.getAttribute("data-presetdescription")},saveButtonText:(0,_str.getString)("save"),returnFocus:editAction});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.reload():_notification.default.addNotification({type:"error",message:event.detail.errors.join("<br>")})})),modalForm.show()}}));
//# sourceMappingURL=editpreset.min.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"editpreset.min.js","sources":["../src/editpreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module for editing a database preset.\n *\n * @module mod_data/editpreset\n * @copyright 2022 Sara Arjona <sara@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n editPresetButton: '[data-action=\"editpreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n registerEventListeners();\n};\n\n/**\n * Register events for update/delete links.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const editAction = event.target.closest(selectors.editPresetButton);\n if (editAction) {\n event.preventDefault();\n showEditPresetModal(editAction);\n }\n });\n};\n\n/**\n * Show the edit preset modal.\n *\n * @param {HTMLElement} editAction the edit action element.\n */\nconst showEditPresetModal = (editAction) => {\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('editpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\save_as_preset',\n args: {\n d: editAction.getAttribute('data-dataid'),\n action: editAction.getAttribute('data-action'),\n presetname: editAction.getAttribute('data-presetname'),\n presetdescription: editAction.getAttribute('data-presetdescription')\n },\n saveButtonText: getString('save'),\n returnFocus: editAction,\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('<br>')\n });\n }\n });\n\n modalForm.show();\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","editAction","target","closest","preventDefault","showEditPresetModal","modalForm","ModalForm","modalConfig","title","formClass","args","d","getAttribute","action","presetname","presetdescription","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","reload","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,2BACgB,2CAMF,KAChBC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,WAAaD,MAAME,OAAOC,QAAQP,4BACpCK,aACAD,MAAMI,iBACNC,oBAAoBJ,iBAU1BI,oBAAuBJ,mBACnBK,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,aAAc,aAEnCC,UAAW,iCACXC,KAAM,CACFC,EAAGX,WAAWY,aAAa,eAC3BC,OAAQb,WAAWY,aAAa,eAChCE,WAAYd,WAAWY,aAAa,mBACpCG,kBAAmBf,WAAWY,aAAa,2BAE/CI,gBAAgB,kBAAU,QAC1BC,YAAajB,aAGjBK,UAAUP,iBAAiBO,UAAUa,OAAOC,gBAAgBpB,QACpDA,MAAMqB,OAAOC,OACbC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAAS5B,MAAMqB,OAAOQ,OAAOC,KAAK,aAK9CxB,UAAUyB"}
+10
View File
@@ -0,0 +1,10 @@
define("mod_data/importmappingdialogue",["exports","core/notification","core/ajax","core/url","core/templates","core/modal","core/prefetch","core/str"],(function(_exports,_notification,_ajax,_url,_templates,_modal,_prefetch,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript module for deleting a database as a preset.
*
* @module mod_data/importmappingdialogue
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url),_templates=_interopRequireDefault(_templates),_modal=_interopRequireDefault(_modal),(0,_prefetch.prefetchStrings)("mod_data",["mapping:dialogtitle:usepreset"]);const selectors_selectPreset='[data-action="selectpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const preset=event.target.closest(selectors_selectPreset);preset&&(event.preventDefault(),showMappingDialogue(preset))}))},showMappingDialogue=usepreset=>{const presetName=usepreset.dataset.presetname,cmId=usepreset.dataset.cmid;getMappingInformation(cmId,presetName).then((result=>(result.data&&result.data.needsmapping?buildModal({title:(0,_str.getString)("mapping:dialogtitle:usepreset","mod_data",result.data.presetname),body:_templates.default.render("mod_data/fields_mapping_body",result.data),footer:_templates.default.render("mod_data/fields_mapping_footer",getMappingButtons(cmId,presetName)),large:!0,show:!0}):window.location.href=_url.default.relativeUrl("mod/data/field.php",{id:cmId,mode:"usepreset",fullname:presetName},!1),!0))).catch(_notification.default.exception)},buildModal=params=>_modal.default.create({...params}).then((modal=>(modal.showFooter(),modal.registerCloseOnCancel(),modal))).catch(_notification.default.exception),getMappingButtons=(cmId,presetName)=>{const data={};return data.mapfieldsbutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"select"},!1),data.applybutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"notmapping"},!1),data},getMappingInformation=(cmId,presetName)=>{const request={methodname:"mod_data_get_mapping_information",args:{cmid:cmId,importedpreset:presetName}};return _ajax.default.call([request])[0]}}));
//# sourceMappingURL=importmappingdialogue.min.js.map
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
define("mod_data/importpresets",["exports","core_form/modalform","core/notification","core/str"],(function(_exports,_modalform,_notification,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript module for importing presets.
*
* @module mod_data/importpresets
* @copyright 2022 Laurent David <laurent.david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_importPresetButton='[data-action="importpresets"]';_exports.init=()=>{document.addEventListener("click",(event=>{const importPresetButton=event.target.closest(selectors_importPresetButton);if(!importPresetButton)return;event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("importpreset","mod_data")},formClass:"mod_data\\form\\import_presets",args:{cmid:importPresetButton.dataset.dataid},saveButtonText:(0,_str.getString)("importandapply","mod_data")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:event.detail.errors.join("<br>")})})),modalForm.show()}))}}));
//# sourceMappingURL=importpresets.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"importpresets.min.js","sources":["../src/importpresets.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module for importing presets.\n *\n * @module mod_data/importpresets\n * @copyright 2022 Laurent David <laurent.david@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n importPresetButton: '[data-action=\"importpresets\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n document.addEventListener('click', (event) => {\n const importPresetButton = event.target.closest(selectors.importPresetButton);\n\n if (!importPresetButton) {\n return;\n }\n\n event.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('importpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\import_presets',\n args: {cmid: importPresetButton.dataset.dataid},\n saveButtonText: getString('importandapply', 'mod_data'),\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('<br>')\n });\n }\n });\n modalForm.show();\n });\n};\n"],"names":["selectors","document","addEventListener","event","importPresetButton","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","cmid","dataset","dataid","saveButtonText","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,8CAMJ,KAChBC,SAASC,iBAAiB,SAAUC,cAC1BC,mBAAqBD,MAAME,OAAOC,QAAQN,kCAE3CI,0BAILD,MAAMI,uBACAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,eAAgB,aAErCC,UAAW,iCACXC,KAAM,CAACC,KAAMV,mBAAmBW,QAAQC,QACxCC,gBAAgB,kBAAU,iBAAkB,cAGhDT,UAAUN,iBAAiBM,UAAUU,OAAOC,gBAAgBhB,QACpDA,MAAMiB,OAAOC,OACbC,OAAOC,SAASC,OAAOrB,MAAMiB,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,QAASzB,MAAMiB,OAAOS,OAAOC,KAAK,aAI9CtB,UAAUuB"}
+10
View File
@@ -0,0 +1,10 @@
define("mod_data/resetalltemplates",["exports","core/notification","core/prefetch","core/str"],(function(_exports,_notification,_prefetch,_str){var obj;
/**
* Javascript module for reseting all templates.
*
* @module mod_data/resetalltemplates
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};const selectors_resetAllTemplatesAction='[data-action="resetalltemplates"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["resetalltemplatesconfirmtitle","resetalltemplatesconfirm"]),(0,_prefetch.prefetchStrings)("core",["reset"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const actionLink=event.target.closest(selectors_resetAllTemplatesAction);actionLink&&(event.preventDefault(),resetAllTemplatesConfirm(actionLink))}))},resetAllTemplatesConfirm=async actionLink=>{try{await _notification.default.saveCancelPromise((0,_str.getString)("resetalltemplatesconfirmtitle","mod_data"),(0,_str.getString)("resetalltemplatesconfirm","mod_data"),(0,_str.getString)("reset","core")),window.location=actionLink.href}catch(error){return}}}));
//# sourceMappingURL=resetalltemplates.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"resetalltemplates.min.js","sources":["../src/resetalltemplates.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module for reseting all templates.\n *\n * @module mod_data/resetalltemplates\n * @copyright 2022 Ferran Recio <ferran@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\n\nconst selectors = {\n resetAllTemplatesAction: '[data-action=\"resetalltemplates\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'resetalltemplatesconfirmtitle',\n 'resetalltemplatesconfirm',\n ]);\n prefetchStrings('core', [\n 'reset',\n ]);\n registerEventListeners();\n};\n\n/**\n * Register events for option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const actionLink = event.target.closest(selectors.resetAllTemplatesAction);\n if (actionLink) {\n event.preventDefault();\n resetAllTemplatesConfirm(actionLink);\n }\n });\n};\n\n/**\n * Show the confirmation modal to reset all the templates.\n *\n * @param {HTMLElement} actionLink the element that triggers the action.\n */\nconst resetAllTemplatesConfirm = async(actionLink) => {\n try {\n await Notification.saveCancelPromise(\n getString('resetalltemplatesconfirmtitle', 'mod_data'),\n getString('resetalltemplatesconfirm', 'mod_data'),\n getString('reset', 'core'),\n );\n window.location = actionLink.href;\n } catch (error) {\n return;\n }\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","actionLink","target","closest","preventDefault","resetAllTemplatesConfirm","async","Notification","saveCancelPromise","window","location","href","error"],"mappings":";;;;;;;4JA2BMA,kCACuB,kDAMT,mCACA,WAAY,CACxB,gCACA,2DAEY,OAAQ,CACpB,UAEJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,WAAaD,MAAME,OAAOC,QAAQP,mCACpCK,aACAD,MAAMI,iBACNC,yBAAyBJ,iBAU/BI,yBAA2BC,MAAAA,uBAEnBC,sBAAaC,mBACf,kBAAU,gCAAiC,aAC3C,kBAAU,2BAA4B,aACtC,kBAAU,QAAS,SAEvBC,OAAOC,SAAWT,WAAWU,KAC/B,MAAOC"}
+10
View File
@@ -0,0 +1,10 @@
define("mod_data/saveaspreset",["exports","core_form/modalform","core/notification","core/str"],(function(_exports,_modalform,_notification,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript module for saving a database as a preset.
*
* @module mod_data/saveaspreset
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_saveAsPresetButton='[data-action="saveaspreset"]';_exports.init=()=>{document.addEventListener("click",(event=>{const saveAsPresetButton=event.target.closest(selectors_saveAsPresetButton);if(!saveAsPresetButton)return;event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("savedataaspreset","mod_data")},formClass:"mod_data\\form\\save_as_preset",args:{d:saveAsPresetButton.dataset.dataid},saveButtonText:(0,_str.getString)("save"),returnFocus:saveAsPresetButton});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.reload():_notification.default.addNotification({type:"error",message:event.detail.errors.join("<br>")})})),modalForm.show()}))}}));
//# sourceMappingURL=saveaspreset.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"saveaspreset.min.js","sources":["../src/saveaspreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module for saving a database as a preset.\n *\n * @module mod_data/saveaspreset\n * @copyright 2021 Mihail Geshoski <mihail@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n saveAsPresetButton: '[data-action=\"saveaspreset\"]',\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n\n document.addEventListener('click', (event) => {\n const saveAsPresetButton = event.target.closest(selectors.saveAsPresetButton);\n\n if (!saveAsPresetButton) {\n return;\n }\n\n event.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('savedataaspreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\save_as_preset',\n args: {d: saveAsPresetButton.dataset.dataid},\n saveButtonText: getString('save'),\n returnFocus: saveAsPresetButton,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('<br>')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["selectors","document","addEventListener","event","saveAsPresetButton","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","d","dataset","dataid","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","reload","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,6CAMJ,KAEhBC,SAASC,iBAAiB,SAAUC,cAC1BC,mBAAqBD,MAAME,OAAOC,QAAQN,kCAE3CI,0BAILD,MAAMI,uBACAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,mBAAoB,aAEzCC,UAAW,iCACXC,KAAM,CAACC,EAAGV,mBAAmBW,QAAQC,QACrCC,gBAAgB,kBAAU,QAC1BC,YAAad,qBAIjBI,UAAUN,iBAAiBM,UAAUW,OAAOC,gBAAgBjB,QACpDA,MAAMkB,OAAOC,OACbC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAAUzB,MAAMkB,OAAOQ,OAAOC,KAAK,aAK/CtB,UAAUuB"}
+11
View File
@@ -0,0 +1,11 @@
define("mod_data/selectpreset",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;
/**
* Javascript module to control the form responsible for selecting a preset.
*
* @module mod_data/selectpreset
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const selectors_presetRadioButton='input[name="fullname"]',selectors_selectPresetButton='input[name="selectpreset"]',selectors_selectedPresetRadioButton='input[name="fullname"]:checked';_exports.init=()=>{const radioButton=document.querySelectorAll(selectors_presetRadioButton);disableUsePresetButton(),radioButton.forEach((elem=>{elem.addEventListener("change",(function(event){event.preventDefault(),disableUsePresetButton()}))}))};const disableUsePresetButton=()=>{let selectPresetButton=document.querySelector(selectors_selectPresetButton);const selectedRadioButton=document.querySelector(selectors_selectedPresetRadioButton);selectedRadioButton?(selectPresetButton.removeAttribute("disabled"),selectPresetButton.classList.remove("btn-secondary"),selectPresetButton.classList.add("btn-primary"),selectPresetButton.setAttribute("data-presetname",selectedRadioButton.getAttribute("value")),selectPresetButton.setAttribute("data-cmid",selectedRadioButton.getAttribute("data-cmid"))):(selectPresetButton.setAttribute("disabled",!0),selectPresetButton.classList.remove("btn-primary"),selectPresetButton.classList.add("btn-secondary"),selectPresetButton.removeAttribute("data-presetname"),selectPresetButton.removeAttribute("data-cmid"))}}));
//# sourceMappingURL=selectpreset.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"selectpreset.min.js","sources":["../src/selectpreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module to control the form responsible for selecting a preset.\n *\n * @module mod_data/selectpreset\n * @copyright 2021 Mihail Geshoski <mihail@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst selectors = {\n presetRadioButton: 'input[name=\"fullname\"]',\n selectPresetButton: 'input[name=\"selectpreset\"]',\n selectedPresetRadioButton: 'input[name=\"fullname\"]:checked',\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const radioButton = document.querySelectorAll(selectors.presetRadioButton);\n\n // Initialize the \"Use a preset\" button properly.\n disableUsePresetButton();\n\n radioButton.forEach((elem) => {\n elem.addEventListener('change', function(event) {\n event.preventDefault();\n // Enable the \"Use a preset\" button when any of the radio buttons in the presets list is checked.\n disableUsePresetButton();\n });\n });\n\n};\n\n/**\n * Decide whether to disable or not the \"Use a preset\" button.\n * When there is no preset selected, the button should be displayed disabled; otherwise, it will appear enabled as a primary button.\n *\n * @method\n * @private\n */\nconst disableUsePresetButton = () => {\n let selectPresetButton = document.querySelector(selectors.selectPresetButton);\n const selectedRadioButton = document.querySelector(selectors.selectedPresetRadioButton);\n\n if (selectedRadioButton) {\n // There is one preset selected, so the button should be enabled.\n selectPresetButton.removeAttribute('disabled');\n selectPresetButton.classList.remove('btn-secondary');\n selectPresetButton.classList.add('btn-primary');\n selectPresetButton.setAttribute('data-presetname', selectedRadioButton.getAttribute('value'));\n selectPresetButton.setAttribute('data-cmid', selectedRadioButton.getAttribute('data-cmid'));\n } else {\n // There is no any preset selected, so the button should be disabled.\n selectPresetButton.setAttribute('disabled', true);\n selectPresetButton.classList.remove('btn-primary');\n selectPresetButton.classList.add('btn-secondary');\n selectPresetButton.removeAttribute('data-presetname');\n selectPresetButton.removeAttribute('data-cmid');\n }\n};\n"],"names":["selectors","radioButton","document","querySelectorAll","disableUsePresetButton","forEach","elem","addEventListener","event","preventDefault","selectPresetButton","querySelector","selectedRadioButton","removeAttribute","classList","remove","add","setAttribute","getAttribute"],"mappings":";;;;;;;;MAuBMA,4BACiB,yBADjBA,6BAEkB,6BAFlBA,oCAGyB,+CAMX,WACVC,YAAcC,SAASC,iBAAiBH,6BAG9CI,yBAEAH,YAAYI,SAASC,OACjBA,KAAKC,iBAAiB,UAAU,SAASC,OACrCA,MAAMC,iBAENL,sCAaNA,uBAAyB,SACvBM,mBAAqBR,SAASS,cAAcX,oCAC1CY,oBAAsBV,SAASS,cAAcX,qCAE/CY,qBAEAF,mBAAmBG,gBAAgB,YACnCH,mBAAmBI,UAAUC,OAAO,iBACpCL,mBAAmBI,UAAUE,IAAI,eACjCN,mBAAmBO,aAAa,kBAAmBL,oBAAoBM,aAAa,UACpFR,mBAAmBO,aAAa,YAAaL,oBAAoBM,aAAa,gBAG9ER,mBAAmBO,aAAa,YAAY,GAC5CP,mBAAmBI,UAAUC,OAAO,eACpCL,mBAAmBI,UAAUE,IAAI,iBACjCN,mBAAmBG,gBAAgB,mBACnCH,mBAAmBG,gBAAgB"}
+10
View File
@@ -0,0 +1,10 @@
define("mod_data/templateseditor",["exports","core/str","core/prefetch","core/url","core/notification","core/templates"],(function(_exports,_str,_prefetch,_url,_notification,_templates){var obj;
/**
* Javascript module to control the template editor.
*
* @module mod_data/templateseditor
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("admin",["confirmation"]),(0,_prefetch.prefetchStrings)("mod_data",["resettemplateconfirmtitle","enabletemplateeditorcheck","editorenable"]),(0,_prefetch.prefetchStrings)("core",["reset"]);const selectors_toggleTemplateEditor='input[name="useeditor"]',selectors_resetTemplateAction='[data-action="resettemplate"]',selectors_resetTemplate='input[name="defaultform"]',selectors_resetAllTemplates='input[name="resetall"]',selectors_resetAllCheck='input[name="resetallcheck"]',selectors_editForm="#edittemplateform",registerResetButton=mode=>{const editForm=document.querySelector(selectors_editForm),resetTemplate=document.querySelector(selectors_resetTemplate),resetAllTemplates=document.querySelector(selectors_resetAllTemplates),resetTemplateAction=document.querySelector(selectors_resetTemplateAction);resetTemplateAction&&resetTemplate&&editForm&&((0,_prefetch.prefetchStrings)("mod_data",[mode]),resetTemplateAction.addEventListener("click",(async event=>{event.preventDefault();const params={resetallname:"resetallcheck",templatename:await(0,_str.getString)(mode,"mod_data")};(0,_notification.saveCancel)((0,_str.getString)("resettemplateconfirmtitle","mod_data"),_templates.default.render("mod_data/template_editor_resetmodal",params),(0,_str.getString)("reset","core"),(()=>{resetTemplate.value="true",editForm.submit()}),null,{triggerElement:event.target})})),resetAllTemplates&&document.addEventListener("change",(event=>{event.target.matches(selectors_resetAllCheck)&&(resetAllTemplates.value=event.target.checked?"true":"")})))},registerEditorToggler=(instanceId,mode)=>{const toggleTemplateEditor=document.querySelector(selectors_toggleTemplateEditor);toggleTemplateEditor&&toggleTemplateEditor.addEventListener("click",(async event=>{event.preventDefault();event.target.checked?(0,_notification.saveCancel)((0,_str.getString)("confirmation","admin"),(0,_str.getString)("enabletemplateeditorcheck","mod_data"),(0,_str.getString)("editorenable","mod_data"),(()=>{window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!0})}),null,{triggerElement:event.target}):window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!1})}))};_exports.init=(instanceId,mode)=>{((instanceId,mode)=>{registerResetButton(mode),registerEditorToggler(instanceId,mode)})(instanceId,mode)}}));
//# sourceMappingURL=templateseditor.min.js.map
File diff suppressed because one or more lines are too long
+108
View File
@@ -0,0 +1,108 @@
// 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/>.
/**
* Javascript module for deleting a database as a preset.
*
* @module mod_data/deletepreset
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Notification from 'core/notification';
import {prefetchStrings} from 'core/prefetch';
import {getString} from 'core/str';
import Ajax from 'core/ajax';
import Url from 'core/url';
const selectors = {
deletePresetButton: '[data-action="deletepreset"]',
};
/**
* Initialize module
*/
export const init = () => {
prefetchStrings('mod_data', [
'deleteconfirm',
'deletewarning',
]);
prefetchStrings('core', [
'delete',
]);
registerEventListeners();
};
/**
* Register events for delete preset option in action menu.
*/
const registerEventListeners = () => {
document.addEventListener('click', (event) => {
const deleteOption = event.target.closest(selectors.deletePresetButton);
if (deleteOption) {
event.preventDefault();
deletePresetConfirm(deleteOption);
}
});
};
/**
* Show the confirmation modal to delete the preset.
*
* @param {HTMLElement} deleteOption the element to delete.
*/
const deletePresetConfirm = (deleteOption) => {
const presetName = deleteOption.getAttribute('data-presetname');
const dataId = deleteOption.getAttribute('data-dataid');
Notification.deleteCancelPromise(
getString('deleteconfirm', 'mod_data', presetName),
getString('deletewarning', 'mod_data'),
).then(() => {
return deletePreset(dataId, presetName);
}).catch(() => {
return;
});
};
/**
* Delete site user preset.
*
* @param {int} dataId The id of the current database activity.
* @param {string} presetName The preset name to delete.
* @return {promise} Resolved with the result and warnings of deleting a preset.
*/
async function deletePreset(dataId, presetName) {
var request = {
methodname: 'mod_data_delete_saved_preset',
args: {
dataid: dataId,
presetnames: {presetname: presetName},
}
};
try {
await Ajax.call([request])[0];
window.location.href = Url.relativeUrl(
'mod/data/preset.php',
{
d: dataId,
},
false
);
} catch (error) {
Notification.exception(error);
}
}
+85
View File
@@ -0,0 +1,85 @@
// 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/>.
/**
* Javascript module for editing a database preset.
*
* @module mod_data/editpreset
* @copyright 2022 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import ModalForm from 'core_form/modalform';
import Notification from 'core/notification';
import {getString} from 'core/str';
const selectors = {
editPresetButton: '[data-action="editpreset"]',
};
/**
* Initialize module
*/
export const init = () => {
registerEventListeners();
};
/**
* Register events for update/delete links.
*/
const registerEventListeners = () => {
document.addEventListener('click', (event) => {
const editAction = event.target.closest(selectors.editPresetButton);
if (editAction) {
event.preventDefault();
showEditPresetModal(editAction);
}
});
};
/**
* Show the edit preset modal.
*
* @param {HTMLElement} editAction the edit action element.
*/
const showEditPresetModal = (editAction) => {
const modalForm = new ModalForm({
modalConfig: {
title: getString('editpreset', 'mod_data'),
},
formClass: 'mod_data\\form\\save_as_preset',
args: {
d: editAction.getAttribute('data-dataid'),
action: editAction.getAttribute('data-action'),
presetname: editAction.getAttribute('data-presetname'),
presetdescription: editAction.getAttribute('data-presetdescription')
},
saveButtonText: getString('save'),
returnFocus: editAction,
});
modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {
if (event.detail.result) {
window.location.reload();
} else {
Notification.addNotification({
type: 'error',
message: event.detail.errors.join('<br>')
});
}
});
modalForm.show();
};
+163
View File
@@ -0,0 +1,163 @@
// 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/>.
/**
* Javascript module for deleting a database as a preset.
*
* @module mod_data/importmappingdialogue
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Notification from 'core/notification';
import Ajax from 'core/ajax';
import Url from 'core/url';
import Templates from 'core/templates';
import Modal from 'core/modal';
import {prefetchStrings} from 'core/prefetch';
import {getString} from 'core/str';
// Load global strings.
prefetchStrings('mod_data', ['mapping:dialogtitle:usepreset']);
const selectors = {
selectPreset: '[data-action="selectpreset"]',
};
/**
* Initialize module
*/
export const init = () => {
registerEventListeners();
};
/**
* Register events.
*/
const registerEventListeners = () => {
document.addEventListener('click', (event) => {
const preset = event.target.closest(selectors.selectPreset);
if (preset) {
event.preventDefault();
showMappingDialogue(preset);
}
});
};
/**
* Show the confirmation modal for uploading a preset.
*
* @param {HTMLElement} usepreset the preset to import.
*/
const showMappingDialogue = (usepreset) => {
const presetName = usepreset.dataset.presetname;
const cmId = usepreset.dataset.cmid;
getMappingInformation(cmId, presetName).then((result) => {
if (result.data && result.data.needsmapping) {
buildModal({
title: getString('mapping:dialogtitle:usepreset', 'mod_data', result.data.presetname),
body: Templates.render('mod_data/fields_mapping_body', result.data),
footer: Templates.render('mod_data/fields_mapping_footer', getMappingButtons(cmId, presetName)),
large: true,
show: true,
});
} else {
window.location.href = Url.relativeUrl(
'mod/data/field.php',
{
id: cmId,
mode: 'usepreset',
fullname: presetName,
},
false
);
}
return true;
}).catch(Notification.exception);
};
/**
* Given an object we want to build a modal ready to show
*
* @method buildModal
* @param {Object} params the modal params
* @param {Promise} params.title
* @param {Promise} params.body
* @param {Promise} params.footer
* @return {Object} The modal ready to display immediately and render body in later.
*/
const buildModal = (params) => {
return Modal.create({
...params,
}).then(modal => {
modal.showFooter();
modal.registerCloseOnCancel();
return modal;
}).catch(Notification.exception);
};
/**
* Add buttons to render on mapping modal.
*
* @param {int} cmId The id of the current database activity.
* @param {string} presetName The preset name to delete.
* @return {array} Same data with buttons.
*/
const getMappingButtons = (cmId, presetName) => {
const data = {};
data.mapfieldsbutton = Url.relativeUrl(
'mod/data/field.php',
{
id: cmId,
fullname: presetName,
mode: 'usepreset',
action: 'select',
},
false
);
data.applybutton = Url.relativeUrl(
'mod/data/field.php',
{
id: cmId,
fullname: presetName,
mode: 'usepreset',
action: 'notmapping'
},
false
);
return data;
};
/**
* Check whether we should show the mapping dialogue or not.
*
* @param {int} cmId The id of the current database activity.
* @param {string} presetName The preset name to delete.
* @return {promise} Resolved with the result and warnings of deleting a preset.
*/
const getMappingInformation = (cmId, presetName) => {
const request = {
methodname: 'mod_data_get_mapping_information',
args: {
cmid: cmId,
importedpreset: presetName,
}
};
return Ajax.call([request])[0];
};
+65
View File
@@ -0,0 +1,65 @@
// 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/>.
/**
* Javascript module for importing presets.
*
* @module mod_data/importpresets
* @copyright 2022 Laurent David <laurent.david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import ModalForm from 'core_form/modalform';
import Notification from 'core/notification';
import {getString} from 'core/str';
const selectors = {
importPresetButton: '[data-action="importpresets"]',
};
/**
* Initialize module
*/
export const init = () => {
document.addEventListener('click', (event) => {
const importPresetButton = event.target.closest(selectors.importPresetButton);
if (!importPresetButton) {
return;
}
event.preventDefault();
const modalForm = new ModalForm({
modalConfig: {
title: getString('importpreset', 'mod_data'),
},
formClass: 'mod_data\\form\\import_presets',
args: {cmid: importPresetButton.dataset.dataid},
saveButtonText: getString('importandapply', 'mod_data'),
});
modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {
if (event.detail.result) {
window.location.assign(event.detail.url);
} else {
Notification.addNotification({
type: 'error',
message: event.detail.errors.join('<br>')
});
}
});
modalForm.show();
});
};
+75
View File
@@ -0,0 +1,75 @@
// 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/>.
/**
* Javascript module for reseting all templates.
*
* @module mod_data/resetalltemplates
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Notification from 'core/notification';
import {prefetchStrings} from 'core/prefetch';
import {getString} from 'core/str';
const selectors = {
resetAllTemplatesAction: '[data-action="resetalltemplates"]',
};
/**
* Initialize module
*/
export const init = () => {
prefetchStrings('mod_data', [
'resetalltemplatesconfirmtitle',
'resetalltemplatesconfirm',
]);
prefetchStrings('core', [
'reset',
]);
registerEventListeners();
};
/**
* Register events for option in action menu.
*/
const registerEventListeners = () => {
document.addEventListener('click', (event) => {
const actionLink = event.target.closest(selectors.resetAllTemplatesAction);
if (actionLink) {
event.preventDefault();
resetAllTemplatesConfirm(actionLink);
}
});
};
/**
* Show the confirmation modal to reset all the templates.
*
* @param {HTMLElement} actionLink the element that triggers the action.
*/
const resetAllTemplatesConfirm = async(actionLink) => {
try {
await Notification.saveCancelPromise(
getString('resetalltemplatesconfirmtitle', 'mod_data'),
getString('resetalltemplatesconfirm', 'mod_data'),
getString('reset', 'core'),
);
window.location = actionLink.href;
} catch (error) {
return;
}
};
+69
View File
@@ -0,0 +1,69 @@
// 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/>.
/**
* Javascript module for saving a database as a preset.
*
* @module mod_data/saveaspreset
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import ModalForm from 'core_form/modalform';
import Notification from 'core/notification';
import {getString} from 'core/str';
const selectors = {
saveAsPresetButton: '[data-action="saveaspreset"]',
};
/**
* Initialize module.
*/
export const init = () => {
document.addEventListener('click', (event) => {
const saveAsPresetButton = event.target.closest(selectors.saveAsPresetButton);
if (!saveAsPresetButton) {
return;
}
event.preventDefault();
const modalForm = new ModalForm({
modalConfig: {
title: getString('savedataaspreset', 'mod_data'),
},
formClass: 'mod_data\\form\\save_as_preset',
args: {d: saveAsPresetButton.dataset.dataid},
saveButtonText: getString('save'),
returnFocus: saveAsPresetButton,
});
// Show a toast notification when the form is submitted.
modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {
if (event.detail.result) {
window.location.reload();
} else {
Notification.addNotification({
type: 'error',
message: event.detail.errors.join('<br>')
});
}
});
modalForm.show();
});
};
+75
View File
@@ -0,0 +1,75 @@
// 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/>.
/**
* Javascript module to control the form responsible for selecting a preset.
*
* @module mod_data/selectpreset
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const selectors = {
presetRadioButton: 'input[name="fullname"]',
selectPresetButton: 'input[name="selectpreset"]',
selectedPresetRadioButton: 'input[name="fullname"]:checked',
};
/**
* Initialize module.
*/
export const init = () => {
const radioButton = document.querySelectorAll(selectors.presetRadioButton);
// Initialize the "Use a preset" button properly.
disableUsePresetButton();
radioButton.forEach((elem) => {
elem.addEventListener('change', function(event) {
event.preventDefault();
// Enable the "Use a preset" button when any of the radio buttons in the presets list is checked.
disableUsePresetButton();
});
});
};
/**
* Decide whether to disable or not the "Use a preset" button.
* When there is no preset selected, the button should be displayed disabled; otherwise, it will appear enabled as a primary button.
*
* @method
* @private
*/
const disableUsePresetButton = () => {
let selectPresetButton = document.querySelector(selectors.selectPresetButton);
const selectedRadioButton = document.querySelector(selectors.selectedPresetRadioButton);
if (selectedRadioButton) {
// There is one preset selected, so the button should be enabled.
selectPresetButton.removeAttribute('disabled');
selectPresetButton.classList.remove('btn-secondary');
selectPresetButton.classList.add('btn-primary');
selectPresetButton.setAttribute('data-presetname', selectedRadioButton.getAttribute('value'));
selectPresetButton.setAttribute('data-cmid', selectedRadioButton.getAttribute('data-cmid'));
} else {
// There is no any preset selected, so the button should be disabled.
selectPresetButton.setAttribute('disabled', true);
selectPresetButton.classList.remove('btn-primary');
selectPresetButton.classList.add('btn-secondary');
selectPresetButton.removeAttribute('data-presetname');
selectPresetButton.removeAttribute('data-cmid');
}
};
+143
View File
@@ -0,0 +1,143 @@
// 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/>.
/**
* Javascript module to control the template editor.
*
* @module mod_data/templateseditor
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {getString} from 'core/str';
import {prefetchStrings} from 'core/prefetch';
import {relativeUrl} from 'core/url';
import {saveCancel} from 'core/notification';
import Templates from 'core/templates';
prefetchStrings('admin', ['confirmation']);
prefetchStrings('mod_data', [
'resettemplateconfirmtitle',
'enabletemplateeditorcheck',
'editorenable'
]);
prefetchStrings('core', [
'reset',
]);
/**
* Template editor constants.
*/
const selectors = {
toggleTemplateEditor: 'input[name="useeditor"]',
resetTemplateAction: '[data-action="resettemplate"]',
resetTemplate: 'input[name="defaultform"]',
resetAllTemplates: 'input[name="resetall"]',
resetAllCheck: 'input[name="resetallcheck"]',
editForm: '#edittemplateform',
};
/**
* Register event listeners for the module.
*
* @param {Number} instanceId The database ID
* @param {string} mode The template mode
*/
const registerEventListeners = (instanceId, mode) => {
registerResetButton(mode);
registerEditorToggler(instanceId, mode);
};
const registerResetButton = (mode) => {
const editForm = document.querySelector(selectors.editForm);
const resetTemplate = document.querySelector(selectors.resetTemplate);
const resetAllTemplates = document.querySelector(selectors.resetAllTemplates);
const resetTemplateAction = document.querySelector(selectors.resetTemplateAction);
if (!resetTemplateAction || !resetTemplate || !editForm) {
return;
}
prefetchStrings('mod_data', [
mode
]);
resetTemplateAction.addEventListener('click', async(event) => {
event.preventDefault();
const params = {
resetallname: "resetallcheck",
templatename: await getString(mode, 'mod_data'),
};
saveCancel(
getString('resettemplateconfirmtitle', 'mod_data'),
Templates.render('mod_data/template_editor_resetmodal', params),
getString('reset', 'core'),
() => {
resetTemplate.value = "true";
editForm.submit();
},
null,
{triggerElement: event.target}
);
});
// The reset all checkbox is inside a modal so we need to capture at document level.
if (!resetAllTemplates) {
return;
}
document.addEventListener('change', (event) => {
if (event.target.matches(selectors.resetAllCheck)) {
resetAllTemplates.value = (event.target.checked) ? "true" : "";
}
});
};
const registerEditorToggler = (instanceId, mode) => {
const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);
if (!toggleTemplateEditor) {
return;
}
toggleTemplateEditor.addEventListener('click', async(event) => {
event.preventDefault();
// Whether the event action attempts to enable or disable the template editor.
const enableTemplateEditor = event.target.checked;
if (enableTemplateEditor) {
// Display a confirmation dialog before enabling the template editor.
saveCancel(
getString('confirmation', 'admin'),
getString('enabletemplateeditorcheck', 'mod_data'),
getString('editorenable', 'mod_data'),
() => {
window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: true});
},
null,
{triggerElement: event.target}
);
} else {
window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: false});
}
});
};
/**
* Initialize the module.
*
* @param {int} instanceId The database ID
* @param {string} mode The template mode
*/
export const init = (instanceId, mode) => {
registerEventListeners(instanceId, mode);
};
+172
View File
@@ -0,0 +1,172 @@
<?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/>.
/**
* Provides support for the conversion of moodle1 backup to the moodle2 format
* Based off of a template @ http://docs.moodle.org/dev/Backup_1.9_conversion_for_developers
*
* @package mod_data
* @copyright 2011 Aparup Banerjee <aparup@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Database conversion handler
*/
class moodle1_mod_data_handler extends moodle1_mod_handler {
/** @var moodle1_file_manager */
protected $fileman = null;
/** @var int cmid */
protected $moduleid = null;
/**
* Declare the paths in moodle.xml we are able to convert
*
* The method returns list of {@link convert_path} instances.
* For each path returned, the corresponding conversion method must be
* defined.
*
* Note that the path /MOODLE_BACKUP/COURSE/MODULES/MOD/DATA does not
* actually exist in the file. The last element with the module name was
* appended by the moodle1_converter class.
*
* @return array of {@link convert_path} instances
*/
public function get_paths() {
return array(
new convert_path('data', '/MOODLE_BACKUP/COURSE/MODULES/MOD/DATA',
array(
'newfields' => array(
'introformat' => 0,
'assesstimestart' => 0,
'assesstimefinish' => 0,
)
)
),
new convert_path('data_field', '/MOODLE_BACKUP/COURSE/MODULES/MOD/DATA/FIELDS/FIELD')
);
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/DATA
* data available
*/
public function process_data($data) {
global $CFG;
// get the course module id and context id
$instanceid = $data['id'];
$cminfo = $this->get_cminfo($instanceid);
$this->moduleid = $cminfo['id'];
$contextid = $this->converter->get_contextid(CONTEXT_MODULE, $this->moduleid);
// replay the upgrade step 2007101512
if (!array_key_exists('asearchtemplate', $data)) {
$data['asearchtemplate'] = null;
}
// replay the upgrade step 2007101513
if (is_null($data['notification'])) {
$data['notification'] = 0;
}
// conditionally migrate to html format in intro
if ($CFG->texteditors !== 'textarea') {
$data['intro'] = text_to_html($data['intro'], false, false, true);
$data['introformat'] = FORMAT_HTML;
}
// get a fresh new file manager for this instance
$this->fileman = $this->converter->get_file_manager($contextid, 'mod_data');
// convert course files embedded into the intro
$this->fileman->filearea = 'intro';
$this->fileman->itemid = 0;
$data['intro'] = moodle1_converter::migrate_referenced_files($data['intro'], $this->fileman);
// @todo: user data - upgrade content to new file storage
// add 'export' tag to list and single template.
$pattern = '/\#\#delete\#\#(\s+)\#\#approve\#\#/';
$replacement = '##delete##$1##approve##$1##export##';
$data['listtemplate'] = preg_replace($pattern, $replacement, $data['listtemplate']);
$data['singletemplate'] = preg_replace($pattern, $replacement, $data['singletemplate']);
//@todo: user data - move data comments to comments table
//@todo: user data - move data ratings to ratings table
// start writing data.xml
$this->open_xml_writer("activities/data_{$this->moduleid}/data.xml");
$this->xmlwriter->begin_tag('activity', array('id' => $instanceid, 'moduleid' => $this->moduleid,
'modulename' => 'data', 'contextid' => $contextid));
$this->xmlwriter->begin_tag('data', array('id' => $instanceid));
foreach ($data as $field => $value) {
if ($field <> 'id') {
$this->xmlwriter->full_tag($field, $value);
}
}
$this->xmlwriter->begin_tag('fields');
return $data;
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/DATA/FIELDS/FIELD
* data available
*/
public function process_data_field($data) {
// process database fields
$this->write_xml('field', $data, array('/field/id'));
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/DATA/RECORDS/RECORD
* data available
*/
public function process_data_record($data) {
//@todo process user data, and define the convert path in get_paths() above.
//$this->write_xml('record', $data, array('/record/id'));
}
/**
* This is executed when we reach the closing </MOD> tag of our 'data' path
*/
public function on_data_end() {
// finish writing data.xml
$this->xmlwriter->end_tag('fields');
$this->xmlwriter->end_tag('data');
$this->xmlwriter->end_tag('activity');
$this->close_xml_writer();
// write inforef.xml
$this->open_xml_writer("activities/data_{$this->moduleid}/inforef.xml");
$this->xmlwriter->begin_tag('inforef');
$this->xmlwriter->begin_tag('fileref');
foreach ($this->fileman->get_fileids() as $fileid) {
$this->write_xml('file', array('id' => $fileid));
}
$this->xmlwriter->end_tag('fileref');
$this->xmlwriter->end_tag('inforef');
$this->close_xml_writer();
}
}
@@ -0,0 +1,78 @@
<?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/>.
/**
* Defines backup_data_activity_task
*
* @package mod_data
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/data/backup/moodle2/backup_data_stepslib.php');
/**
* Provides the steps to perform one complete backup of the Database instance
*/
class backup_data_activity_task extends backup_activity_task {
/**
* No specific settings for this activity
*/
protected function define_my_settings() {
}
/**
* Defines a backup step to store the instance data in the data.xml file
*/
protected function define_my_steps() {
$this->add_step(new backup_data_activity_structure_step('data_structure', 'data.xml'));
}
/**
* Encodes URLs to the index.php and view.php scripts
*
* @param string $content some HTML text that eventually contains URLs to the activity instance scripts
* @return string the content with the URLs encoded
*/
public static function encode_content_links($content) {
global $CFG;
$base = preg_quote($CFG->wwwroot,"/");
// Link to the list of datas
$search="/(".$base."\/mod\/data\/index.php\?id\=)([0-9]+)/";
$content= preg_replace($search, '$@DATAINDEX*$2@$', $content);
// Link to data view by moduleid
$search="/(".$base."\/mod\/data\/view.php\?id\=)([0-9]+)/";
$content= preg_replace($search, '$@DATAVIEWBYID*$2@$', $content);
/// Link to database view by databaseid
$search="/(".$base."\/mod\/data\/view.php\?d\=)([0-9]+)/";
$content= preg_replace($search,'$@DATAVIEWBYD*$2@$', $content);
/// Link to one "record" of the database
$search="/(".$base."\/mod\/data\/view.php\?d\=)([0-9]+)\&(amp;)rid\=([0-9]+)/";
$content= preg_replace($search,'$@DATAVIEWRECORD*$2*$4@$', $content);
return $content;
}
}
@@ -0,0 +1,143 @@
<?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/>.
/**
* @package mod_data
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Define all the backup steps that will be used by the backup_data_activity_task
*/
/**
* Define the complete data structure for backup, with file and id annotations
*/
class backup_data_activity_structure_step extends backup_activity_structure_step {
protected function define_structure() {
// To know if we are including userinfo
$userinfo = $this->get_setting_value('userinfo');
// Define each element separated
$data = new backup_nested_element('data', array('id'), array(
'name', 'intro', 'introformat', 'comments',
'timeavailablefrom', 'timeavailableto', 'timeviewfrom', 'timeviewto',
'requiredentries', 'requiredentriestoview', 'maxentries', 'rssarticles',
'singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter',
'addtemplate', 'rsstemplate', 'rsstitletemplate', 'csstemplate',
'jstemplate', 'asearchtemplate', 'approval', 'manageapproved', 'scale',
'assessed', 'assesstimestart', 'assesstimefinish', 'defaultsort',
'defaultsortdir', 'editany', 'notification', 'timemodified', 'config', 'completionentries'));
$tags = new backup_nested_element('recordstags');
$tag = new backup_nested_element('tag', array('id'), array('itemid', 'rawname'));
$fields = new backup_nested_element('fields');
$field = new backup_nested_element('field', array('id'), array(
'type', 'name', 'description', 'required', 'param1', 'param2',
'param3', 'param4', 'param5', 'param6',
'param7', 'param8', 'param9', 'param10'));
$records = new backup_nested_element('records');
$record = new backup_nested_element('record', array('id'), array(
'userid', 'groupid', 'timecreated', 'timemodified',
'approved'));
$contents = new backup_nested_element('contents');
$content = new backup_nested_element('content', array('id'), array(
'fieldid', 'content', 'content1', 'content2',
'content3', 'content4'));
$ratings = new backup_nested_element('ratings');
$rating = new backup_nested_element('rating', array('id'), array(
'component', 'ratingarea', 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
// Build the tree
$data->add_child($fields);
$fields->add_child($field);
$data->add_child($records);
$records->add_child($record);
$record->add_child($contents);
$contents->add_child($content);
$record->add_child($ratings);
$ratings->add_child($rating);
$data->add_child($tags);
$tags->add_child($tag);
// Define sources
$data->set_source_table('data', array('id' => backup::VAR_ACTIVITYID));
$field->set_source_sql('
SELECT *
FROM {data_fields}
WHERE dataid = ?',
array(backup::VAR_PARENTID));
// All the rest of elements only happen if we are including user info
if ($userinfo) {
$record->set_source_table('data_records', array('dataid' => backup::VAR_PARENTID));
$content->set_source_table('data_content', array('recordid' => backup::VAR_PARENTID));
$rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
'itemid' => backup::VAR_PARENTID,
'component' => backup_helper::is_sqlparam('mod_data'),
'ratingarea' => backup_helper::is_sqlparam('entry')));
$rating->set_source_alias('rating', 'value');
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
$tag->set_source_sql('SELECT t.id, ti.itemid, t.rawname
FROM {tag} t
JOIN {tag_instance} ti
ON ti.tagid = t.id
WHERE ti.itemtype = ?
AND ti.component = ?
AND ti.contextid = ?', array(
backup_helper::is_sqlparam('data_records'),
backup_helper::is_sqlparam('mod_data'),
backup::VAR_CONTEXTID));
}
}
// Define id annotations
$data->annotate_ids('scale', 'scale');
$record->annotate_ids('user', 'userid');
$record->annotate_ids('group', 'groupid');
$rating->annotate_ids('scale', 'scaleid');
$rating->annotate_ids('user', 'userid');
// Define file annotations
$data->annotate_files('mod_data', 'intro', null); // This file area hasn't itemid
$content->annotate_files('mod_data', 'content', 'id'); // By content->id
// Return the root element (data), wrapped into standard activity structure
return $this->prepare_activity_structure($data);
}
}
@@ -0,0 +1,138 @@
<?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/>.
/**
* @package mod_data
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/data/backup/moodle2/restore_data_stepslib.php'); // Because it exists (must)
/**
* data restore task that provides all the settings and steps to perform one
* complete restore of the activity
*/
class restore_data_activity_task extends restore_activity_task {
/**
* Define (add) particular settings this activity can have
*/
protected function define_my_settings() {
// No particular settings for this activity
}
/**
* Define (add) particular steps this activity can have
*/
protected function define_my_steps() {
// Data only has one structure step
$this->add_step(new restore_data_activity_structure_step('data_structure', 'data.xml'));
}
/**
* Define the contents in the activity that must be
* processed by the link decoder
*/
public static function define_decode_contents() {
$contents = array();
$contents[] = new restore_decode_content('data', array(
'intro', 'singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter',
'addtemplate', 'rsstemplate', 'rsstitletemplate', 'asearchtemplate'), 'data');
$contents[] = new restore_decode_content('data_fields', array(
'description', 'param1', 'param2', 'param3',
'param4', 'param5', 'param6', 'param7',
'param8', 'param9', 'param10'), 'data_field');
$contents[] = new restore_decode_content('data_content', array(
'content', 'content1', 'content2', 'content3', 'content4'));
return $contents;
}
/**
* Define the decoding rules for links belonging
* to the activity to be executed by the link decoder
*/
public static function define_decode_rules() {
$rules = array();
$rules[] = new restore_decode_rule('DATAVIEWBYID', '/mod/data/view.php?id=$1', 'course_module');
$rules[] = new restore_decode_rule('DATAVIEWBYD', '/mod/data/view.php?d=$1', 'data');
$rules[] = new restore_decode_rule('DATAINDEX', '/mod/data/index.php?id=$1', 'course');
$rules[] = new restore_decode_rule('DATAVIEWRECORD', '/mod/data/view.php?d=$1&amp;rid=$2', array('data', 'data_record'));
return $rules;
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* data logs. It must return one array
* of {@link restore_log_rule} objects
*/
public static function define_restore_log_rules() {
$rules = array();
$rules[] = new restore_log_rule('data', 'add', 'view.php?d={data}&rid={data_record}', '{data}');
$rules[] = new restore_log_rule('data', 'update', 'view.php?d={data}&rid={data_record}', '{data}');
$rules[] = new restore_log_rule('data', 'view', 'view.php?id={course_module}', '{data}');
$rules[] = new restore_log_rule('data', 'record delete', 'view.php?id={course_module}', '{data}');
$rules[] = new restore_log_rule('data', 'fields add', 'field.php?d={data}&mode=display&fid={data_field}', '{data_field}');
$rules[] = new restore_log_rule('data', 'fields update', 'field.php?d={data}&mode=display&fid={data_field}', '{data_field}');
$rules[] = new restore_log_rule('data', 'fields delete', 'field.php?d={data}', '[name]');
return $rules;
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* course logs. It must return one array
* of {@link restore_log_rule} objects
*
* Note this rules are applied when restoring course logs
* by the restore final task, but are defined here at
* activity level. All them are rules not linked to any module instance (cmid = 0)
*/
public static function define_restore_log_rules_for_course() {
$rules = array();
$rules[] = new restore_log_rule('data', 'view all', 'index.php?id={course}', null);
return $rules;
}
/**
* Given a commment area, return the itemname that contains the itemid mappings.
*
* @param string $commentarea Comment area name e.g. database_entry.
* @return string name of the mapping used to determine the itemid.
*/
public function get_comment_mapping_itemname($commentarea) {
if ($commentarea == 'database_entry') {
$itemname = 'data_record';
} else {
$itemname = parent::get_comment_mapping_itemname($commentarea);
}
return $itemname;
}
}
@@ -0,0 +1,186 @@
<?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/>.
/**
* @package mod_data
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Define all the restore steps that will be used by the restore_data_activity_task
*/
/**
* Structure step to restore one data activity
*/
class restore_data_activity_structure_step extends restore_activity_structure_step {
protected function define_structure() {
$paths = array();
$userinfo = $this->get_setting_value('userinfo');
$paths[] = new restore_path_element('data', '/activity/data');
$paths[] = new restore_path_element('data_field', '/activity/data/fields/field');
if ($userinfo) {
$paths[] = new restore_path_element('data_record', '/activity/data/records/record');
$paths[] = new restore_path_element('data_content', '/activity/data/records/record/contents/content');
$paths[] = new restore_path_element('data_rating', '/activity/data/records/record/ratings/rating');
$paths[] = new restore_path_element('data_record_tag', '/activity/data/recordstags/tag');
}
// Return the paths wrapped into standard activity structure
return $this->prepare_activity_structure($paths);
}
protected function process_data($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->course = $this->get_courseid();
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
// See MDL-9367.
$data->timeavailablefrom = $this->apply_date_offset($data->timeavailablefrom);
$data->timeavailableto = $this->apply_date_offset($data->timeavailableto);
$data->timeviewfrom = $this->apply_date_offset($data->timeviewfrom);
$data->timeviewto = $this->apply_date_offset($data->timeviewto);
$data->assesstimestart = $this->apply_date_offset($data->assesstimestart);
$data->assesstimefinish = $this->apply_date_offset($data->assesstimefinish);
if ($data->scale < 0) { // scale found, get mapping
$data->scale = -($this->get_mappingid('scale', abs($data->scale)));
}
// Some old backups can arrive with data->notification = null (MDL-24470)
// convert them to proper column default (zero)
if (is_null($data->notification)) {
$data->notification = 0;
}
// insert the data record
$newitemid = $DB->insert_record('data', $data);
$this->apply_activity_instance($newitemid);
}
protected function process_data_field($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->dataid = $this->get_new_parentid('data');
$data->type = clean_param($data->type, PARAM_ALPHA);
// insert the data_fields record
$newitemid = $DB->insert_record('data_fields', $data);
$this->set_mapping('data_field', $oldid, $newitemid, false); // no files associated
}
protected function process_data_record($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->userid = $this->get_mappingid('user', $data->userid);
$data->groupid = $this->get_mappingid('group', $data->groupid);
$data->dataid = $this->get_new_parentid('data');
// insert the data_records record
$newitemid = $DB->insert_record('data_records', $data);
$this->set_mapping('data_record', $oldid, $newitemid, false); // no files associated
}
protected function process_data_content($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->fieldid = $this->get_mappingid('data_field', $data->fieldid);
$data->recordid = $this->get_new_parentid('data_record');
// insert the data_content record
$newitemid = $DB->insert_record('data_content', $data);
$this->set_mapping('data_content', $oldid, $newitemid, true); // files by this itemname
}
/**
* Add tags to restored records.
*
* @param stdClass $data Tag
*/
protected function process_data_record_tag($data) {
$data = (object)$data;
if (!core_tag_tag::is_enabled('mod_data', 'data_records')) { // Tags disabled in server, nothing to process.
return;
}
if (!$itemid = $this->get_mappingid('data_record', $data->itemid)) {
// Some orphaned tag, we could not find the data record for it - ignore.
return;
}
$tag = $data->rawname;
$context = context_module::instance($this->task->get_moduleid());
core_tag_tag::add_item_tag('mod_data', 'data_records', $itemid, $context, $tag);
}
protected function process_data_rating($data) {
global $DB;
$data = (object)$data;
// Cannot use ratings API, cause, it's missing the ability to specify times (modified/created)
$data->contextid = $this->task->get_contextid();
$data->itemid = $this->get_new_parentid('data_record');
if ($data->scaleid < 0) { // scale found, get mapping
$data->scaleid = -($this->get_mappingid('scale', abs($data->scaleid)));
}
$data->rating = $data->value;
$data->userid = $this->get_mappingid('user', $data->userid);
// We need to check that component and ratingarea are both set here.
if (empty($data->component)) {
$data->component = 'mod_data';
}
if (empty($data->ratingarea)) {
$data->ratingarea = 'entry';
}
$newitemid = $DB->insert_record('rating', $data);
}
protected function after_execute() {
global $DB;
// Add data related files, no need to match by itemname (just internally handled context)
$this->add_related_files('mod_data', 'intro', null);
// Add content related files, matching by itemname (data_content)
$this->add_related_files('mod_data', 'content', 'data_content');
// Adjust the data->defaultsort field
if ($defaultsort = $DB->get_field('data', 'defaultsort', array('id' => $this->get_new_parentid('data')))) {
if ($defaultsort = $this->get_mappingid('data_field', $defaultsort)) {
$DB->set_field('data', 'defaultsort', $defaultsort, array('id' => $this->get_new_parentid('data')));
}
}
}
}
@@ -0,0 +1,46 @@
<?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/>.
/**
* Activity base class.
*
* @package mod_data
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Activity base class.
*
* @package mod_data
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class activity_base extends \core_analytics\local\indicator\community_of_inquiry_activity {
/**
* Returns the name of the field that controls activity availability.
*
* @return null|string
*/
protected function get_timeclose_field() {
return 'timeavailableto';
}
}
@@ -0,0 +1,56 @@
<?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/>.
/**
* Cognitive depth indicator - data.
*
* @package mod_data
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Cognitive depth indicator - data.
*
* @package mod_data
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cognitive_depth extends activity_base {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:cognitivedepth', 'mod_data');
}
public function get_indicator_type() {
return self::INDICATOR_COGNITIVE;
}
public function get_cognitive_depth_level(\cm_info $cm) {
return self::COGNITIVE_LEVEL_2;
}
}
@@ -0,0 +1,56 @@
<?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/>.
/**
* Social breadth indicator - data.
*
* @package mod_data
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Social breadth indicator - data.
*
* @package mod_data
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class social_breadth extends activity_base {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:socialbreadth', 'mod_data');
}
public function get_indicator_type() {
return self::INDICATOR_SOCIAL;
}
public function get_social_breadth_level(\cm_info $cm) {
return self::SOCIAL_LEVEL_1;
}
}
@@ -0,0 +1,86 @@
<?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/>.
declare(strict_types=1);
namespace mod_data\completion;
use core_completion\activity_custom_completion;
/**
* Activity custom completion subclass for the data activity.
*
* Class for defining mod_data's custom completion rules and fetching the completion statuses
* of the custom completion rules for a given data instance and a user.
*
* @package mod_data
* @copyright Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_completion extends activity_custom_completion {
/**
* Fetches the completion state for a given completion rule.
*
* @param string $rule The completion rule.
* @return int The completion state.
*/
public function get_state(string $rule): int {
global $DB;
$this->validate_rule($rule);
$userentries = $DB->count_records('data_records', ['dataid' => $this->cm->instance, 'userid' => $this->userid]);
$completionentries = $this->cm->customdata['customcompletionrules']['completionentries'];
return ($completionentries <= $userentries) ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE;
}
/**
* Fetch the list of custom completion rules that this module defines.
*
* @return array
*/
public static function get_defined_custom_rules(): array {
return ['completionentries'];
}
/**
* Returns an associative array of the descriptions of custom completion rules.
*
* @return array
*/
public function get_custom_rule_descriptions(): array {
$entries = $this->cm->customdata['customcompletionrules']['completionentries'] ?? 0;
return [
'completionentries' => get_string('completiondetail:entries', 'data', $entries),
];
}
/**
* Returns an array of all completion rules, in the order they should be displayed to users.
*
* @return array
*/
public function get_sort_order(): array {
return [
'completionview',
'completionentries',
'completionusegrade',
'completionpassgrade',
];
}
}
+70
View File
@@ -0,0 +1,70 @@
<?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/>.
/**
* Contains the class for fetching the important dates in mod_data for a given module instance and a user.
*
* @package mod_data
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_data;
use core\activity_dates;
/**
* Class for fetching the important dates in mod_data for a given module instance and a user.
*
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dates extends activity_dates {
/**
* Returns a list of important dates in mod_data
*
* @return array
*/
protected function get_dates(): array {
$timeopen = $this->cm->customdata['timeavailablefrom'] ?? null;
$timeclose = $this->cm->customdata['timeavailableto'] ?? null;
$now = time();
$dates = [];
if ($timeopen) {
$openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
$dates[] = [
'dataid' => 'timeavailablefrom',
'label' => get_string($openlabelid, 'course'),
'timestamp' => (int) $timeopen,
];
}
if ($timeclose) {
$closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
$dates[] = [
'dataid' => 'timeavailableto',
'label' => get_string($closelabelid, 'course'),
'timestamp' => (int) $timeclose,
];
}
return $dates;
}
}
@@ -0,0 +1,55 @@
<?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/>.
/**
* The mod_data comment created event.
*
* @package mod_data
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data comment created event class.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class comment_created extends \core\event\comment_created {
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' added the comment with id '$this->objectid' to the database activity with " .
"course module id '$this->contextinstanceid'.";
}
}
@@ -0,0 +1,55 @@
<?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/>.
/**
* The mod_data comment deleted event.
*
* @package mod_data
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data comment deleted event class.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class comment_deleted extends \core\event\comment_deleted {
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the comment with id '$this->objectid' from the database activity with " .
"course module id '$this->contextinstanceid'.";
}
}
@@ -0,0 +1,41 @@
<?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/>.
/**
* The mod_data instance list viewed event.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data instance list viewed event class.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {
// No code required here as the parent class handles it all.
}
@@ -0,0 +1,54 @@
<?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/>.
/**
* The mod_data course module viewed event.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data course module viewed event class.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_viewed extends \core\event\course_module_viewed {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
public static function get_objectid_mapping() {
return array('db' => 'data', 'restore' => 'data');
}
}
+114
View File
@@ -0,0 +1,114 @@
<?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/>.
/**
* The mod_data field created event.
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data field created event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string fieldname: the name of the field.
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_created extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_fields';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventfieldcreated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the field with id '$this->objectid' for the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/field.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['fieldname'])) {
throw new \coding_exception('The \'fieldname\' value must be set in other.');
}
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_fields', 'restore' => 'data_field');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+113
View File
@@ -0,0 +1,113 @@
<?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/>.
/**
* The mod_data field deleted event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data field deleted event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string fieldname: the name of the field.
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_deleted extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_fields';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventfielddeleted', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the field with id '$this->objectid' in the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/field.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['fieldname'])) {
throw new \coding_exception('The \'fieldname\' value must be set in other.');
}
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_fields', 'restore' => 'data_field');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+113
View File
@@ -0,0 +1,113 @@
<?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/>.
/**
* The mod_data field updated event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data field updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string fieldname: the name of the field.
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_updated extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_fields';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventfieldupdated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the field with id '$this->objectid' in the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/field.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['fieldname'])) {
throw new \coding_exception('The \'fieldname\' value must be set in other.');
}
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_fields', 'restore' => 'data_field');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?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/>.
/**
* The mod_data record created event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data record created event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class record_created extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_records';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventrecordcreated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the data record with id '$this->objectid' for the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('d' => $this->other['dataid'], 'rid' => $this->objectid));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_records', 'restore' => 'data_record');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?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/>.
/**
* The mod_data record deleted event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data record deleted event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class record_deleted extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_records';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventrecorddeleted', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the data record with id '$this->objectid' in the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_records', 'restore' => 'data_record');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?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/>.
/**
* The mod_data record updated event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data record updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class record_updated extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'data_records';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventrecordupdated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the data record with id '$this->objectid' in the data activity " .
"with course module id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/view.php', array('d' => $this->other['dataid'], 'rid' => $this->objectid));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'data_records', 'restore' => 'data_record');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?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/>.
/**
* The mod_data template updated event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data template updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid: the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_updated extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventtemplateupdated', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the template for the data activity with course module " .
"id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/templates.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?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/>.
/**
* The mod_data template viewed event.
*
* @package mod_data
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_data template viewed event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int dataid the id of the data activity.
* }
*
* @package mod_data
* @since Moodle 2.7
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_viewed extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventtemplateviewed', 'mod_data');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' viewed the template for the data activity with course module " .
"id '$this->contextinstanceid'.";
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/data/templates.php', array('d' => $this->other['dataid']));
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['dataid'])) {
throw new \coding_exception('The \'dataid\' value must be set in other.');
}
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['dataid'] = array('db' => 'data', 'restore' => 'data');
return $othermapped;
}
}
File diff suppressed because it is too large Load Diff
+106
View File
@@ -0,0 +1,106 @@
<?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/>.
/**
* Class for exporting content associated to a record.
*
* @package mod_data
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use core_external\external_files;
use core_external\util as external_util;
/**
* Class for exporting content associated to a record.
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class content_exporter extends exporter {
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Content id.',
),
'fieldid' => array(
'type' => PARAM_INT,
'description' => 'The field type of the content.',
'default' => 0,
),
'recordid' => array(
'type' => PARAM_INT,
'description' => 'The record this content belongs to.',
'default' => 0,
),
'content' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content1' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content2' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content3' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content4' => array(
'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
);
}
protected static function define_related() {
return array(
'context' => 'context',
);
}
protected static function define_other_properties() {
return array(
'files' => array(
'type' => external_files::get_properties_for_exporter(),
'multiple' => true,
'optional' => true,
),
);
}
protected function get_other_values(renderer_base $output) {
$values = ['files' => external_util::get_area_files($this->related['context']->id, 'mod_data', 'content', $this->data->id)];
return $values;
}
}
+248
View File
@@ -0,0 +1,248 @@
<?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/>.
/**
* Class for exporting partial database data.
*
* @package mod_data
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use core_external\external_files;
use core_external\util as external_util;
/**
* Class for exporting partial database data (some fields are only viewable by admins).
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class database_summary_exporter extends exporter {
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Database id'),
'course' => array(
'type' => PARAM_INT,
'description' => 'Course id'),
'name' => array(
'type' => PARAM_RAW,
'description' => 'Database name'),
'intro' => array(
'type' => PARAM_RAW,
'description' => 'The Database intro',
),
'introformat' => array(
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
'type' => PARAM_INT,
'default' => FORMAT_MOODLE
),
'lang' => array(
'type' => PARAM_LANG,
'description' => 'Forced activity language',
'null' => NULL_ALLOWED,
),
'comments' => array(
'type' => PARAM_BOOL,
'description' => 'comments enabled',
),
'timeavailablefrom' => array(
'type' => PARAM_INT,
'description' => 'timeavailablefrom field',
),
'timeavailableto' => array(
'type' => PARAM_INT,
'description' => 'timeavailableto field',
),
'timeviewfrom' => array(
'type' => PARAM_INT,
'description' => 'timeviewfrom field',
),
'timeviewto' => array(
'type' => PARAM_INT,
'description' => 'timeviewto field',
),
'requiredentries' => array(
'type' => PARAM_INT,
'description' => 'requiredentries field',
),
'requiredentriestoview' => array(
'type' => PARAM_INT,
'description' => 'requiredentriestoview field',
),
'maxentries' => array(
'type' => PARAM_INT,
'description' => 'maxentries field',
),
'rssarticles' => array(
'type' => PARAM_INT,
'description' => 'rssarticles field',
),
'singletemplate' => array(
'type' => PARAM_RAW,
'description' => 'singletemplate field',
'null' => NULL_ALLOWED,
),
'listtemplate' => array(
'type' => PARAM_RAW,
'description' => 'listtemplate field',
'null' => NULL_ALLOWED,
),
'listtemplateheader' => array(
'type' => PARAM_RAW,
'description' => 'listtemplateheader field',
'null' => NULL_ALLOWED,
),
'listtemplatefooter' => array(
'type' => PARAM_RAW,
'description' => 'listtemplatefooter field',
'null' => NULL_ALLOWED,
),
'addtemplate' => array(
'type' => PARAM_RAW,
'description' => 'addtemplate field',
'null' => NULL_ALLOWED,
),
'rsstemplate' => array(
'type' => PARAM_RAW,
'description' => 'rsstemplate field',
'null' => NULL_ALLOWED,
),
'rsstitletemplate' => array(
'type' => PARAM_RAW,
'description' => 'rsstitletemplate field',
'null' => NULL_ALLOWED,
),
'csstemplate' => array(
'type' => PARAM_RAW,
'description' => 'csstemplate field',
'null' => NULL_ALLOWED,
),
'jstemplate' => array(
'type' => PARAM_RAW,
'description' => 'jstemplate field',
'null' => NULL_ALLOWED,
),
'asearchtemplate' => array(
'type' => PARAM_RAW,
'description' => 'asearchtemplate field',
'null' => NULL_ALLOWED,
),
'approval' => array(
'type' => PARAM_BOOL,
'description' => 'approval field',
),
'manageapproved' => array(
'type' => PARAM_BOOL,
'description' => 'manageapproved field',
),
'scale' => array(
'type' => PARAM_INT,
'description' => 'scale field',
'optional' => true,
),
'assessed' => array(
'type' => PARAM_INT,
'description' => 'assessed field',
'optional' => true,
),
'assesstimestart' => array(
'type' => PARAM_INT,
'description' => 'assesstimestart field',
'optional' => true,
),
'assesstimefinish' => array(
'type' => PARAM_INT,
'description' => 'assesstimefinish field',
'optional' => true,
),
'defaultsort' => array(
'type' => PARAM_INT,
'description' => 'defaultsort field',
),
'defaultsortdir' => array(
'type' => PARAM_INT,
'description' => 'defaultsortdir field',
),
'editany' => array(
'type' => PARAM_BOOL,
'description' => 'editany field (not used any more)',
'optional' => true,
),
'notification' => array(
'type' => PARAM_INT,
'description' => 'notification field (not used any more)',
'optional' => true,
),
'timemodified' => array(
'type' => PARAM_INT,
'description' => 'Time modified',
'optional' => true,
),
);
}
protected static function define_related() {
return array(
'context' => 'context'
);
}
protected static function define_other_properties() {
return array(
'coursemodule' => array(
'type' => PARAM_INT
),
'introfiles' => array(
'type' => external_files::get_properties_for_exporter(),
'multiple' => true,
'optional' => true,
),
);
}
protected function get_other_values(renderer_base $output) {
$context = $this->related['context'];
$values = array(
'coursemodule' => $context->instanceid,
'introfiles' => external_util::get_area_files($context->id, 'mod_data', 'intro', false, false),
);
return $values;
}
/**
* Get the formatting parameters for the intro.
*
* @return array
*/
protected function get_format_parameters_for_intro() {
return [
'component' => 'mod_data',
'filearea' => 'intro',
'options' => array('noclean' => true),
];
}
}
+123
View File
@@ -0,0 +1,123 @@
<?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 mod_data\external;
use core\notification;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use mod_data\manager;
use mod_data\preset;
/**
* This is the external method for deleting a saved preset.
*
* @package mod_data
* @since Moodle 4.1
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class delete_saved_preset extends external_api {
/**
* Parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'dataid' => new external_value(PARAM_INT, 'Id of the data activity', VALUE_REQUIRED),
'presetnames' => new external_multiple_structure(
new external_value(PARAM_TEXT, 'The preset name to delete', VALUE_REQUIRED)
)
]);
}
/**
* Delete saved preset from the file system.
*
* @param int $dataid Id of the data activity to check context and permissions.
* @param array $presetnames List of saved preset names to delete.
* @return array True if the content has been deleted; false and the warning, otherwise.
*/
public static function execute(int $dataid, array $presetnames): array {
global $DB;
$result = false;
$warnings = [];
$params = self::validate_parameters(self::execute_parameters(), ['dataid' => $dataid, 'presetnames' => $presetnames]);
$instance = $DB->get_record('data', ['id' => $params['dataid']], '*', MUST_EXIST);
$manager = manager::create_from_instance($instance);
foreach ($params['presetnames'] as $presetname) {
try {
$preset = preset::create_from_instance($manager, $presetname);
if ($preset->can_manage()) {
if ($preset->delete()) {
notification::success(get_string('presetdeleted', 'mod_data'));
$result = true;
} else {
// An error ocurred while deleting the preset.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'failedpresetdelete',
'message' => get_string('failedpresetdelete', 'mod_data')
];
notification::error(get_string('failedpresetdelete', 'mod_data'));
}
} else {
// The user has no permission to delete the preset.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'cannotdeletepreset',
'message' => get_string('cannotdeletepreset', 'mod_data')
];
notification::error(get_string('cannotdeletepreset', 'mod_data'));
}
} catch (\moodle_exception $e) {
// The saved preset has not been deleted.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'exception',
'message' => $e->getMessage()
];
notification::error($e->getMessage());
}
}
return [
'result' => $result,
'warnings' => $warnings
];
}
/**
* Return.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'result' => new external_value(PARAM_BOOL, 'The processing result'),
'warnings' => new external_warnings()
]);
}
}
+85
View File
@@ -0,0 +1,85 @@
<?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/>.
/**
* Class for exporting field data.
*
* @package mod_data
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
/**
* Class for exporting field data.
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_exporter extends exporter {
protected static function define_properties() {
$properties = array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Field id.',
),
'dataid' => array(
'type' => PARAM_INT,
'description' => 'The field type of the content.',
'default' => 0,
),
'type' => array(
'type' => PARAM_PLUGIN,
'description' => 'The field type.',
),
'name' => array(
'type' => PARAM_TEXT,
'description' => 'The field name.',
),
'description' => array(
'type' => PARAM_RAW,
'description' => 'The field description.',
),
'required' => array(
'type' => PARAM_BOOL,
'description' => 'Whether is a field required or not.',
'default' => 0,
),
);
// Field possible parameters.
for ($i = 1; $i <= 10; $i++) {
$properties["param$i"] = array(
'type' => PARAM_RAW,
'description' => 'Field parameters',
'null' => NULL_ALLOWED,
);
}
return $properties;
}
protected static function define_related() {
// Context is required for text formatting.
return array(
'context' => 'context',
);
}
}
+97
View File
@@ -0,0 +1,97 @@
<?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 mod_data\external;
use core\notification;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use mod_data\local\importer\preset_importer;
use mod_data\manager;
/**
* This is the external method for deleting a saved preset.
*
* @package mod_data
* @since Moodle 4.1
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class get_mapping_information extends external_api {
/**
* Parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'cmid' => new external_value(PARAM_INT, 'Id of the data activity', VALUE_REQUIRED),
'importedpreset' => new external_value(PARAM_TEXT, 'Preset to be imported'),
]);
}
/**
* Get importing information for the given database course module.
*
* @param int $cmid Id of the course module where to import the preset.
* @param string $importedpreset Plugin or saved preset to be imported.
* @return array Information needed to decide whether to show the dialogue or not.
*/
public static function execute(int $cmid, string $importedpreset): array {
$params = self::validate_parameters(
self::execute_parameters(),
['cmid' => $cmid, 'importedpreset' => $importedpreset]
);
try {
// Let's get the manager.
list($course, $cm) = get_course_and_cm_from_cmid($params['cmid'], manager::MODULE);
$manager = manager::create_from_coursemodule($cm);
$importer = preset_importer::create_from_plugin_or_directory($manager, $params['importedpreset']);
$result['data'] = $importer->get_mapping_information();
} catch (\moodle_exception $e) {
$result['warnings'][] = [
'item' => $importedpreset,
'warningcode' => 'exception',
'message' => $e->getMessage()
];
notification::error($e->getMessage());
}
return $result;
}
/**
* Return.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'data' => new external_single_structure([
'needsmapping' => new external_value(PARAM_BOOL, 'Whether the importing needs mapping or not'),
'presetname' => new external_value(PARAM_TEXT, 'Name of the applied preset'),
'fieldstocreate' => new external_value(PARAM_TEXT, 'List of field names to create'),
'fieldstoremove' => new external_value(PARAM_TEXT, 'List of field names to remove'),
], 'Information to import if everything went fine', VALUE_OPTIONAL),
'warnings' => new external_warnings(),
]);
}
}
+143
View File
@@ -0,0 +1,143 @@
<?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/>.
/**
* Class for exporting record data.
*
* @package mod_data
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use core_user;
use core_tag\external\tag_item_exporter;
/**
* Class for exporting record data.
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class record_exporter extends exporter {
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Record id.',
),
'userid' => array(
'type' => PARAM_INT,
'description' => 'The id of the user who created the record.',
'default' => 0,
),
'groupid' => array(
'type' => PARAM_INT,
'description' => 'The group id this record belongs to (0 for no groups).',
'default' => 0,
),
'dataid' => array(
'type' => PARAM_INT,
'description' => 'The database id this record belongs to.',
'default' => 0,
),
'timecreated' => array(
'type' => PARAM_INT,
'description' => 'Time the record was created.',
'default' => 0,
),
'timemodified' => array(
'type' => PARAM_INT,
'description' => 'Last time the record was modified.',
'default' => 0,
),
'approved' => array(
'type' => PARAM_BOOL,
'description' => 'Whether the entry has been approved (if the database is configured in that way).',
'default' => 0,
),
);
}
protected static function define_related() {
return array(
'database' => 'stdClass',
'user' => 'stdClass?',
'context' => 'context',
'contents' => 'stdClass[]?',
);
}
protected static function define_other_properties() {
return array(
'canmanageentry' => array(
'type' => PARAM_BOOL,
'description' => 'Whether the current user can manage this entry',
),
'fullname' => array(
'type' => PARAM_TEXT,
'description' => 'The user who created the entry fullname.',
'optional' => true,
),
'contents' => array(
'type' => content_exporter::read_properties_definition(),
'description' => 'The record contents.',
'multiple' => true,
'optional' => true,
),
'tags' => array(
'type' => tag_item_exporter::read_properties_definition(),
'description' => 'Tags.',
'multiple' => true,
'optional' => true,
),
);
}
protected function get_other_values(renderer_base $output) {
global $PAGE;
$values = array(
'canmanageentry' => data_user_can_manage_entry($this->data, $this->related['database'], $this->related['context']),
);
if (!empty($this->related['user']) and !empty($this->related['user']->id)) {
$values['fullname'] = fullname($this->related['user']);
} else if ($this->data->userid) {
$user = core_user::get_user($this->data->userid);
$values['fullname'] = fullname($user);
}
if (!empty($this->related['contents'])) {
$contents = [];
foreach ($this->related['contents'] as $content) {
$related = array('context' => $this->related['context']);
$exporter = new content_exporter($content, $related);
$contents[] = $exporter->export($PAGE->get_renderer('core'));
}
$values['contents'] = $contents;
}
$values['tags'] = \core_tag\external\util::get_item_tags('mod_data', 'data_records', $this->data->id);
return $values;
}
}
+115
View File
@@ -0,0 +1,115 @@
<?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 mod_data\form;
use context;
use moodle_exception;
use moodle_url;
use core_form\dynamic_form;
/**
* Import presets form.
*
* @package mod_data
* @copyright 2022 Laurent David <laurent.david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class import_presets extends dynamic_form {
/**
* Process the form submission
*
* @return array
* @throws moodle_exception
*/
public function process_dynamic_submission(): array {
global $CFG;
$filepath = $this->save_temp_file('importfile');
$context = $this->get_context_for_dynamic_submission();
$returnurl = new moodle_url('/mod/data/preset.php', [
'id' => $context->instanceid,
'action' => 'importzip',
'filepath' => str_replace($CFG->tempdir, '', $filepath)
]);
return [
'result' => true,
'url' => $returnurl->out(false),
];
}
/**
* Get context
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
$cmid = $this->optional_param('cmid', null, PARAM_INT);
$cm = get_coursemodule_from_id('data', $cmid);
$context = \context_module::instance($cm->id);
return $context;
}
/**
* Set data
*
* @return void
*/
public function set_data_for_dynamic_submission(): void {
$data = (object) [
'cmid' => $this->optional_param('cmid', 0, PARAM_INT),
];
$this->set_data($data);
}
/**
* Has access ?
*
* @return void
* @throws moodle_exception
*/
protected function check_access_for_dynamic_submission(): void {
if (!has_capability('mod/data:managetemplates', $this->get_context_for_dynamic_submission())) {
throw new moodle_exception('importpresetmissingcapability', 'data');
}
}
/**
* Get page URL
*
* @return moodle_url
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$cmid = $this->optional_param('cmid', null, PARAM_INT);
return new moodle_url('/mod/data/preset.php', ['id' => $cmid]);
}
/**
* Form definition
*
* @return void
*/
protected function definition() {
$mform = $this->_form;
$mform->addElement('html', \html_writer::div(get_string('importpreset_desc', 'mod_data'), 'py-3'));
$mform->addElement('hidden', 'cmid');
$mform->setType('cmid', PARAM_INT);
$mform->addElement('filepicker', 'importfile', get_string('choosepreset', 'mod_data'), null,
['accepted_types' => '.zip']);
$mform->addRule('importfile', null, 'required');
}
}
+239
View File
@@ -0,0 +1,239 @@
<?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 mod_data\form;
use context;
use core\notification;
use moodle_exception;
use moodle_url;
use core_form\dynamic_form;
use mod_data\manager;
use mod_data\preset;
/**
* Save database as preset form.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class save_as_preset extends dynamic_form {
/**
* Form definition
*/
protected function definition() {
$this->_form->addElement('hidden', 'd');
$this->_form->setType('d', PARAM_INT);
$this->_form->addElement('hidden', 'action', 'save');
$this->_form->setType('action', PARAM_ALPHANUM);
$this->_form->addElement('hidden', 'oldpresetname', '');
$this->_form->setType('oldpresetname', PARAM_FILE);
$this->_form->addElement('text', 'name', get_string('name'), ['size' => 60]);
$this->_form->setType('name', PARAM_FILE);
$this->_form->addRule('name', null, 'required');
// Overwrite checkbox will be hidden by default. It will only appear if there is an error when saving the preset.
$this->_form->addElement('checkbox', 'overwrite', '', get_string('overrwritedesc', 'data'), ['class' => 'hidden']);
$this->_form->addElement('textarea', 'description', get_string('description'), ['rows' => 5, 'cols' => 60]);
$this->_form->setType('name', PARAM_TEXT);
}
/**
* Return form context
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
global $DB;
$d = $this->optional_param('d', null, PARAM_INT);
$data = $DB->get_record('data', array('id' => $d), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id' => $data->course), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('data', $data->id, $course->id, null, MUST_EXIST);
return \context_module::instance($cm->id, MUST_EXIST);
}
/**
* Perform some validation.
*
* @param array $formdata
* @param array $files
* @return array
*/
public function validation($formdata, $files): array {
$errors = parent::validation($formdata, $files);
$context = $this->get_context_for_dynamic_submission();
$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
$manager = manager::create_from_coursemodule($cm);
if (!empty($formdata['overwrite'])) {
$presets = $manager->get_available_presets();
$selectedpreset = new \stdClass();
foreach ($presets as $preset) {
if ($preset->name == $formdata['name']) {
$selectedpreset = $preset;
break;
}
}
if (!$selectedpreset instanceof preset || !$selectedpreset->can_manage()) {
$errors['name'] = get_string('cannotoverwritepreset', 'data');
}
} else if ($formdata['action'] == 'saveaspreset' || $formdata['oldpresetname'] != $formdata['name']) {
// If the preset exists when a new preset is saved or name has changed, then we need to throw an error.
$sitepresets = $manager->get_available_saved_presets();
$usercandelete = false;
foreach ($sitepresets as $preset) {
if ($formdata['name'] == $preset->name) {
if ($preset->can_manage()) {
$errors['name'] = get_string('errorpresetexists', 'data');
$usercandelete = true;
} else {
$errors['name'] = get_string('errorpresetexistsbutnotoverwrite', 'data');
}
break;
}
}
// If there are some errors, the checkbox should be displayed, to let users overwrite the preset.
if (!empty($errors) && $usercandelete) {
$this->_form->getElement('overwrite')->removeAttribute('class');
}
}
return $errors;
}
/**
* Check if current user has access to this form, otherwise throw exception.
*
* @return void
* @throws moodle_exception
*/
protected function check_access_for_dynamic_submission(): void {
global $DB;
if (!has_capability('mod/data:managetemplates', $this->get_context_for_dynamic_submission())) {
throw new moodle_exception('saveaspresetmissingcapability', 'data');
}
$action = $this->optional_param('action', '', PARAM_ALPHANUM);
if ($action == 'saveaspreset') {
// For saving it as a new preset, some fields need to be created; otherwise, an exception will be raised.
$instanceid = $this->optional_param('d', null, PARAM_INT);
$hasfields = $DB->record_exists('data_fields', ['dataid' => $instanceid]);
if (!$hasfields) {
throw new moodle_exception('nofieldindatabase', 'data');
}
}
}
/**
* Process the form submission, used if form was submitted via AJAX.
*
* @return array
*/
public function process_dynamic_submission(): array {
global $DB, $CFG;
require_once($CFG->dirroot . '/mod/data/lib.php');
$formdata = $this->get_data();
$result = false;
$errors = [];
$data = $DB->get_record('data', array('id' => $formdata->d), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id' => $data->course), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('data', $data->id, $course->id, null, MUST_EXIST);
$context = \context_module::instance($cm->id, MUST_EXIST);
try {
$manager = manager::create_from_instance($data);
if (!empty($formdata->overwrite)) {
$presets = $manager->get_available_presets();
$selectedpreset = new \stdClass();
foreach ($presets as $preset) {
if ($preset->name == $formdata->name) {
$selectedpreset = $preset;
break;
}
}
if ($selectedpreset instanceof preset && $selectedpreset->can_manage()) {
$selectedpreset->delete();
}
}
$presetname = $formdata->name;
if (!empty($formdata->oldpresetname)) {
$presetname = $formdata->oldpresetname;
}
$preset = preset::create_from_instance($manager, $presetname, $formdata->description);
if (!empty($formdata->oldpresetname)) {
// Update the name and the description, to save the new data.
$preset->name = $formdata->name;
$preset->description = $formdata->description;
}
$result = $preset->save();
if ($result) {
// Add notification in the session to be shown when the page is reloaded on the JS side.
$previewurl = new moodle_url(
'/mod/data/preset.php',
['id' => $cm->id, 'fullname' => $preset->get_fullname(), 'action' => 'preview']
);
notification::success(get_string('savesuccess', 'mod_data', (object)['url' => $previewurl->out()]));
}
} catch (\Exception $exception) {
$errors[] = $exception->getMessage();
}
return [
'result' => $result,
'errors' => $errors,
];
}
/**
* Load in existing data as form defaults.
*
* @return void
*/
public function set_data_for_dynamic_submission(): void {
$data = (object)[
'd' => $this->optional_param('d', 0, PARAM_INT),
'action' => $this->optional_param('action', '', PARAM_ALPHANUM),
'oldpresetname' => $this->optional_param('presetname', '', PARAM_FILE),
'name' => $this->optional_param('presetname', '', PARAM_FILE),
'description' => $this->optional_param('presetdescription', '', PARAM_TEXT),
];
$this->set_data($data);
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$d = $this->optional_param('d', null, PARAM_INT);
return new moodle_url('/user/field.php', ['d' => $d]);
}
}
@@ -0,0 +1,81 @@
<?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 mod_data\local\exporter;
use coding_exception;
use csv_export_writer;
/**
* CSV entries exporter for mod_data.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class csv_entries_exporter extends entries_exporter {
/** @var string[] Possible delimiter names. Only used internally to check if a valid delimiter name
* has been specified.
*/
private const POSSIBLE_DELIMITER_NAMES = ['comma', 'tab', 'semicolon', 'colon', 'cfg'];
/**
* @var string name of the delimiter to use for the csv export. Possible values:
* 'comma', 'tab', 'semicolon', 'colon' or 'cfg'.
*/
private string $delimitername = 'comma';
/**
* Returns the csv data exported by the csv_export_writer for further handling.
*
* @see \mod_data\local\exporter\entries_exporter::get_data_file_content()
*/
public function get_data_file_content(): string {
global $CFG;
require_once($CFG->libdir . '/csvlib.class.php');
return csv_export_writer::print_array($this->exportdata, $this->delimitername, '"', true);
}
/**
* Returns the file extension of this entries exporter.
*
* @see \mod_data\local\exporter\entries_exporter::get_export_data_file_extension()
*/
public function get_export_data_file_extension(): string {
return 'csv';
}
/**
* Setter for the delimiter name which should be used in this csv_entries_exporter object.
*
* Calling this setter is optional, the delimiter name defaults to 'comma'.
*
* @param string $delimitername one of 'comma', 'tab', 'semicolon', 'colon' or 'cfg'
* @return void
* @throws coding_exception if a wrong delimiter name has been specified
*/
public function set_delimiter_name(string $delimitername): void {
if (!in_array($delimitername, self::POSSIBLE_DELIMITER_NAMES)) {
throw new coding_exception('Wrong delimiter type',
'Please choose on of the following delimiters: '
. '\"comma\", \"tab\", \"semicolon\", \"colon\", \"cfg\"');
}
$this->delimitername = $delimitername;
}
}
@@ -0,0 +1,277 @@
<?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 mod_data\local\exporter;
use file_serving_exception;
use moodle_exception;
use zip_archive;
/**
* Exporter class for exporting data and - if needed - files as well in a zip archive.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class entries_exporter {
/** @var int Tracks the currently edited row of the export data file. */
private int $currentrow;
/**
* @var array The data structure containing the data for exporting. It's a 2-dimensional array of
* rows and columns.
*/
protected array $exportdata;
/** @var string Name of the export file name without extension. */
protected string $exportfilename;
/** @var zip_archive The zip archive object we store all the files in, if we need to export files as well. */
private zip_archive $ziparchive;
/** @var bool Tracks the state if the zip archive already has been closed. */
private bool $isziparchiveclosed;
/** @var string full path of the zip archive. */
private string $zipfilepath;
/** @var array Array to store all filenames in the zip archive for export. */
private array $filenamesinzip;
/**
* Creates an entries_exporter object.
*
* This object can be used to export data to different formats including files. If files are added,
* everything will be bundled up in a zip archive.
*/
public function __construct() {
$this->currentrow = 0;
$this->exportdata = [];
$this->exportfilename = 'Exportfile';
$this->filenamesinzip = [];
$this->isziparchiveclosed = true;
}
/**
* Adds a row (array of strings) to the export data.
*
* @param array $row the row to add, $row has to be a plain array of strings
* @return void
*/
public function add_row(array $row): void {
$this->exportdata[] = $row;
$this->currentrow++;
}
/**
* Adds a data string (so the content for a "cell") to the current row.
*
* @param string $cellcontent the content to add to the current row
* @return void
*/
public function add_to_current_row(string $cellcontent): void {
$this->exportdata[$this->currentrow][] = $cellcontent;
}
/**
* Signal the entries_exporter to finish the current row and jump to the next row.
*
* @return void
*/
public function next_row(): void {
$this->currentrow++;
}
/**
* Sets the name of the export file.
*
* Only use the basename without path and without extension here.
*
* @param string $exportfilename name of the file without path and extension
* @return void
*/
public function set_export_file_name(string $exportfilename): void {
$this->exportfilename = $exportfilename;
}
/**
* The entries_exporter will prepare a data file from the rows and columns being added.
* Overwrite this method to generate the data file as string.
*
* @return string the data file as a string
*/
abstract protected function get_data_file_content(): string;
/**
* Overwrite the method to return the file extension your data file will have, for example
* <code>return 'csv';</code> for a csv file entries_exporter.
*
* @return string the file extension of the data file your entries_exporter is using
*/
abstract protected function get_export_data_file_extension(): string;
/**
* Returns the count of currently stored records (rows excluding header row).
*
* @return int the count of records/rows
*/
public function get_records_count(): int {
// The attribute $this->exportdata also contains a header. If only one row is present, this
// usually is the header, so record count should be 0.
if (count($this->exportdata) <= 1) {
return 0;
}
return count($this->exportdata) - 1;
}
/**
* Use this method to add a file which should be exported to the entries_exporter.
*
* @param string $filename the name of the file which should be added
* @param string $filecontent the content of the file as a string
* @param string $zipsubdir the subdirectory in the zip archive. Defaults to 'files/'.
* @return void
* @throws moodle_exception if there is an error adding the file to the zip archive
*/
public function add_file_from_string(string $filename, string $filecontent, string $zipsubdir = 'files/'): void {
if (empty($this->filenamesinzip)) {
// No files added yet, so we need to create a zip archive.
$this->create_zip_archive();
}
if (!str_ends_with($zipsubdir, '/')) {
$zipsubdir .= '/';
}
$zipfilename = $zipsubdir . $filename;
$this->filenamesinzip[] = $zipfilename;
$this->ziparchive->add_file_from_string($zipfilename, $filecontent);
}
/**
* Sends the generated export file.
*
* Care: By default this function finishes the current PHP request and directly serves the file to the user as download.
*
* @param bool $sendtouser true if the file should be sent directly to the user, if false the file content will be returned
* as string
* @return string|null file content as string if $sendtouser is true
* @throws moodle_exception if there is an issue adding the data file
* @throws file_serving_exception if the file could not be served properly
*/
public function send_file(bool $sendtouser = true): null|string {
if (empty($this->filenamesinzip)) {
if ($sendtouser) {
send_file($this->get_data_file_content(),
$this->exportfilename . '.' . $this->get_export_data_file_extension(),
null, 0, true, true);
return null;
} else {
return $this->get_data_file_content();
}
}
$this->add_file_from_string($this->exportfilename . '.' . $this->get_export_data_file_extension(),
$this->get_data_file_content(), '/');
$this->finish_zip_archive();
if ($this->isziparchiveclosed) {
if ($sendtouser) {
send_file($this->zipfilepath, $this->exportfilename . '.zip', null, 0, false, true);
return null;
} else {
return file_get_contents($this->zipfilepath);
}
} else {
throw new file_serving_exception('Could not serve zip file, it could not be closed properly.');
}
}
/**
* Checks if a file with the given name has already been added to the file export bundle.
*
* Care: Filenames are compared to all files in the specified zip subdirectory which
* defaults to 'files/'.
*
* @param string $filename the filename containing the zip path of the file to check
* @param string $zipsubdir The subdirectory in which the filename should be looked for,
* defaults to 'files/'
* @return bool true if file with the given name already exists, false otherwise
*/
public function file_exists(string $filename, string $zipsubdir = 'files/'): bool {
if (!str_ends_with($zipsubdir, '/')) {
$zipsubdir .= '/';
}
if (empty($filename)) {
return false;
}
return in_array($zipsubdir . $filename, $this->filenamesinzip, true);
}
/**
* Creates a unique filename based on the given filename.
*
* This method adds "_1", "_2", ... to the given file name until the newly generated filename
* is not equal to any of the already saved ones in the export file bundle.
*
* @param string $filename the filename based on which a unique filename should be generated
* @return string the unique filename
*/
public function create_unique_filename(string $filename): string {
if (!$this->file_exists($filename)) {
return $filename;
}
$extension = pathinfo($filename, PATHINFO_EXTENSION);
$filenamewithoutextension = empty($extension)
? $filename
: substr($filename, 0,strlen($filename) - strlen($extension) - 1);
$filenamewithoutextension = $filenamewithoutextension . '_1';
$i = 1;
$filename = empty($extension) ? $filenamewithoutextension : $filenamewithoutextension . '.' . $extension;
while ($this->file_exists($filename)) {
// In case we have already a file ending with '_XX' where XX is an ascending number, we have to
// remove '_XX' first before adding '_YY' again where YY is the successor of XX.
$filenamewithoutextension = preg_replace('/_' . $i . '$/', '_' . ($i + 1), $filenamewithoutextension);
$filename = empty($extension) ? $filenamewithoutextension : $filenamewithoutextension . '.' . $extension;
$i++;
}
return $filename;
}
/**
* Prepares the zip archive.
*
* @return void
*/
private function create_zip_archive(): void {
$tmpdir = make_request_directory();
$this->zipfilepath = $tmpdir . '/' . $this->exportfilename . '.zip';
$this->ziparchive = new zip_archive();
$this->isziparchiveclosed = !$this->ziparchive->open($this->zipfilepath);
}
/**
* Closes the zip archive.
*
* @return void
*/
private function finish_zip_archive(): void {
if (!$this->isziparchiveclosed) {
$this->isziparchiveclosed = $this->ziparchive->close();
}
}
}
@@ -0,0 +1,65 @@
<?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 mod_data\local\exporter;
use MoodleODSWorkbook;
use MoodleODSWriter;
/**
* ODS entries exporter for mod_data.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class ods_entries_exporter extends entries_exporter {
/**
* Returns the file extension of this entries exporter.
*
* @see \mod_data\local\exporter\entries_exporter::get_export_data_file_extension()
*/
public function get_export_data_file_extension(): string {
return 'ods';
}
/**
* Returns the ods data exported by the ODS library for further handling.
*
* @see \mod_data\local\exporter\entries_exporter::get_data_file_content()
*/
public function get_data_file_content(): string {
global $CFG;
require_once("$CFG->libdir/odslib.class.php");
$filearg = '-';
$workbook = new MoodleODSWorkbook($filearg);
$worksheet = [];
$worksheet[0] = $workbook->add_worksheet('');
$rowno = 0;
foreach ($this->exportdata as $row) {
$colno = 0;
foreach ($row as $col) {
$worksheet[0]->write($rowno, $colno, $col);
$colno++;
}
$rowno++;
}
$writer = new MoodleODSWriter($worksheet);
return $writer->get_file_content();
}
}
+141
View File
@@ -0,0 +1,141 @@
<?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 mod_data\local\exporter;
use context;
use context_system;
/**
* Utility class for exporting data from a mod_data instance.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class utils {
/**
* Exports the data of the mod_data instance to an entries_exporter object which then can export it to a file format.
*
* @param int $dataid
* @param array $fields
* @param array $selectedfields
* @param entries_exporter $exporter the entries_exporter object used
* @param int $currentgroup group ID of the current group. This is used for
* exporting data while maintaining group divisions.
* @param context|null $context the context in which the operation is performed (for capability checks)
* @param bool $userdetails whether to include the details of the record author
* @param bool $time whether to include time created/modified
* @param bool $approval whether to include approval status
* @param bool $tags whether to include tags
* @param bool $includefiles whether files should be exported as well
* @return void
*/
public static function data_exportdata(int $dataid, array $fields, array $selectedfields, entries_exporter $exporter,
int $currentgroup = 0, context $context = null, bool $userdetails = false, bool $time = false, bool $approval = false,
bool $tags = false, bool $includefiles = true): void {
global $DB;
if (is_null($context)) {
$context = context_system::instance();
}
// Exporting user data needs special permission.
$userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context);
// Populate the header in first row of export.
$header = [];
foreach ($fields as $key => $field) {
if (!in_array($field->field->id, $selectedfields)) {
// Ignore values we aren't exporting.
unset($fields[$key]);
} else {
$header[] = $field->field->name;
}
}
if ($tags) {
$header[] = get_string('tags', 'data');
}
if ($userdetails) {
$header[] = get_string('user');
$header[] = get_string('username');
$header[] = get_string('email');
}
if ($time) {
$header[] = get_string('timeadded', 'data');
$header[] = get_string('timemodified', 'data');
}
if ($approval) {
$header[] = get_string('approved', 'data');
}
$exporter->add_row($header);
$datarecords = $DB->get_records('data_records', array('dataid' => $dataid));
ksort($datarecords);
$line = 1;
foreach ($datarecords as $record) {
// Get content indexed by fieldid.
if ($currentgroup) {
$select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, '
. '{data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?';
$where = array($record->id, $currentgroup);
} else {
$select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?';
$where = array($record->id);
}
if ($content = $DB->get_records_sql($select, $where)) {
foreach ($fields as $field) {
$contents = '';
if (isset($content[$field->field->id])) {
$contents = $field->export_text_value($content[$field->field->id]);
if (!empty($contents) && $field->file_export_supported() && $includefiles
&& !is_null($field->export_file_value($record))) {
// For exporting overwrite the content of the column with a unique
// filename, even it is not exactly the name of the file in the
// mod_data instance content. But it's more important to match the name
// of the exported file.
$contents = $exporter->create_unique_filename($contents);
$exporter->add_file_from_string($contents, $field->export_file_value($record));
}
}
// Just be double sure.
$contents = !empty($contents) ? $contents : '';
$exporter->add_to_current_row($contents);
}
if ($tags) {
$itemtags = \core_tag_tag::get_item_tags_array('mod_data', 'data_records', $record->id);
$exporter->add_to_current_row(implode(', ', $itemtags));
}
if ($userdetails) { // Add user details to the export data.
$userdata = get_complete_user_data('id', $record->userid);
$exporter->add_to_current_row(fullname($userdata));
$exporter->add_to_current_row($userdata->username);
$exporter->add_to_current_row($userdata->email);
}
if ($time) { // Add time added / modified.
$exporter->add_to_current_row(userdate($record->timecreated));
$exporter->add_to_current_row(userdate($record->timemodified));
}
if ($approval) { // Add approval status.
$exporter->add_to_current_row((int) $record->approved);
}
}
$exporter->next_row();
}
}
}
@@ -0,0 +1,209 @@
<?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 mod_data\local\importer;
use context_module;
use core_php_time_limit;
use core_tag_tag;
use core_user;
use csv_import_reader;
use moodle_exception;
use stdClass;
/**
* CSV entries_importer class for importing data and - if needed - files as well from a zip archive.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class csv_entries_importer extends entries_importer {
/** @var array Log entries for successfully added records. */
private array $addedrecordsmessages = [];
/**
* Declares the entries_importer to use a csv file as data file.
*
* @see entries_importer::get_import_data_file_extension()
*/
public function get_import_data_file_extension(): string {
return 'csv';
}
/**
* Import records for a data instance from csv data.
*
* @param stdClass $cm Course module of the data instance.
* @param stdClass $data The data instance.
* @param string $encoding The encoding of csv data.
* @param string $fielddelimiter The delimiter of the csv data.
*
* @throws moodle_exception
*/
public function import_csv(stdClass $cm, stdClass $data, string $encoding, string $fielddelimiter): void {
global $CFG, $DB;
// Large files are likely to take their time and memory. Let PHP know
// that we'll take longer, and that the process should be recycled soon
// to free up memory.
core_php_time_limit::raise();
raise_memory_limit(MEMORY_HUGE);
$iid = csv_import_reader::get_new_iid('moddata');
$cir = new csv_import_reader($iid, 'moddata');
$context = context_module::instance($cm->id);
$readcount = $cir->load_csv_content($this->get_data_file_content(), $encoding, $fielddelimiter);
if (empty($readcount)) {
throw new \moodle_exception('csvfailed', 'data', "{$CFG->wwwroot}/mod/data/edit.php?d={$data->id}");
} else {
if (!$fieldnames = $cir->get_columns()) {
throw new \moodle_exception('cannotreadtmpfile', 'error');
}
// Check the fieldnames are valid.
$rawfields = $DB->get_records('data_fields', ['dataid' => $data->id], '', 'name, id, type');
$fields = [];
$errorfield = '';
$usernamestring = get_string('username');
$safetoskipfields = [get_string('user'), get_string('email'),
get_string('timeadded', 'data'), get_string('timemodified', 'data'),
get_string('approved', 'data'), get_string('tags', 'data')];
$userfieldid = null;
foreach ($fieldnames as $id => $name) {
if (!isset($rawfields[$name])) {
if ($name == $usernamestring) {
$userfieldid = $id;
} else if (!in_array($name, $safetoskipfields)) {
$errorfield .= "'$name' ";
}
} else {
// If this is the second time, a field with this name comes up, it must be a field not provided by the user...
// like the username.
if (isset($fields[$name])) {
if ($name == $usernamestring) {
$userfieldid = $id;
}
unset($fieldnames[$id]); // To ensure the user provided content fields remain in the array once flipped.
} else {
$field = $rawfields[$name];
$field->type = clean_param($field->type, PARAM_ALPHA);
$filepath = "$CFG->dirroot/mod/data/field/$field->type/field.class.php";
if (!file_exists($filepath)) {
$errorfield .= "'$name' ";
continue;
}
require_once($filepath);
$classname = 'data_field_' . $field->type;
$fields[$name] = new $classname($field, $data, $cm);
}
}
}
if (!empty($errorfield)) {
throw new \moodle_exception('fieldnotmatched', 'data',
"{$CFG->wwwroot}/mod/data/edit.php?d={$data->id}", $errorfield);
}
$fieldnames = array_flip($fieldnames);
$cir->init();
while ($record = $cir->next()) {
$authorid = null;
if ($userfieldid) {
if (!($author = core_user::get_user_by_username($record[$userfieldid], 'id'))) {
$authorid = null;
} else {
$authorid = $author->id;
}
}
// Determine presence of "approved" field within the record to import.
$approved = true;
if (array_key_exists(get_string('approved', 'data'), $fieldnames)) {
$approvedindex = $fieldnames[get_string('approved', 'data')];
$approved = !empty($record[$approvedindex]);
}
if ($recordid = data_add_record($data, 0, $authorid, $approved)) { // Add instance to data_record.
foreach ($fields as $field) {
$fieldid = $fieldnames[$field->field->name];
if (isset($record[$fieldid])) {
$value = $record[$fieldid];
} else {
$value = '';
}
if (method_exists($field, 'update_content_import')) {
$field->update_content_import($recordid, $value, 'field_' . $field->field->id);
} else {
$content = new stdClass();
$content->fieldid = $field->field->id;
$content->content = $value;
$content->recordid = $recordid;
if ($field->file_import_supported() && $this->importfiletype === 'zip') {
$filecontent = $this->get_file_content_from_zip($content->content);
if (!$filecontent) {
// No corresponding file in zip archive, so no record for this field being added at all.
continue;
}
$contentid = $DB->insert_record('data_content', $content);
$field->import_file_value($contentid, $filecontent, $content->content);
} else {
$DB->insert_record('data_content', $content);
}
}
}
if (core_tag_tag::is_enabled('mod_data', 'data_records') &&
isset($fieldnames[get_string('tags', 'data')])) {
$columnindex = $fieldnames[get_string('tags', 'data')];
$rawtags = $record[$columnindex];
$tags = explode(',', $rawtags);
foreach ($tags as $tag) {
$tag = trim($tag);
if (empty($tag)) {
continue;
}
core_tag_tag::add_item_tag('mod_data', 'data_records', $recordid, $context, $tag);
}
}
$this->addedrecordsmessages[] = get_string('added', 'moodle',
count($this->addedrecordsmessages) + 1)
. ". " . get_string('entry', 'data')
. " (ID $recordid)\n";
}
}
$cir->close();
$cir->cleanup(true);
}
}
/**
* Getter for the array of messages for added records.
*
* For each successfully added record the array contains a log message.
*
* @return array Array of message strings: For each added record one message string
*/
public function get_added_records_messages(): array {
return $this->addedrecordsmessages;
}
}
@@ -0,0 +1,144 @@
<?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 mod_data\local\importer;
use coding_exception;
use core_php_time_limit;
use file_packer;
/**
* Importer class for importing data and - if needed - files as well from a zip archive.
*
* @package mod_data
* @copyright 2023 ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class entries_importer {
/** @var string The import file path of the file which data should be imported from. */
protected string $importfilepath;
/** @var string The original name of the import file name including extension of the file which data should be imported from. */
protected string $importfilename;
/** @var string $importfiletype The file type of the import file. */
protected string $importfiletype;
/** @var file_packer Zip file packer to extract files from a zip archive. */
private file_packer $packer;
/** @var bool Tracks state if zip archive has been extracted already. */
private bool $zipfileextracted;
/** @var string Temporary directory where zip archive is being extracted to. */
private string $extracteddir;
/**
* Creates an entries_importer object.
*
* This object can be used to import data from data files (like csv) and zip archives both including a data file and files to be
* stored in the course module context.
*
* @param string $importfilepath the complete path of the import file including filename
* @param string $importfilename the import file name as uploaded by the user
* @throws coding_exception if a wrong file type is being used
*/
public function __construct(string $importfilepath, string $importfilename) {
$this->importfilepath = $importfilepath;
$this->importfilename = $importfilename;
$this->importfiletype = pathinfo($importfilename, PATHINFO_EXTENSION);
$this->zipfileextracted = false;
if ($this->importfiletype !== $this->get_import_data_file_extension() && $this->importfiletype !== 'zip') {
throw new coding_exception('Only "zip" or "' . $this->get_import_data_file_extension() . '" files are '
. 'allowed.');
}
}
/**
* Return the file extension of the import data file which is being used, for example 'csv' for a csv entries_importer.
*
* @return string the file extension of the export data file
*/
abstract public function get_import_data_file_extension(): string;
/**
* Returns the file content of the data file.
*
* Returns the content of the file directly if the entries_importer's file is a data file itself.
* If the entries_importer's file is a zip archive, the content of the first found data file in the
* zip archive's root will be returned.
*
* @return false|string the data file content as string; false, if file cannot be found/read
*/
public function get_data_file_content(): false|string {
if ($this->importfiletype !== 'zip') {
// We have no zip archive, so the file itself must be the data file.
return file_get_contents($this->importfilepath);
}
// So we have a zip archive and need to find the right data file in the root of the zip archive.
$this->extract_zip();
$datafilenames = array_filter($this->packer->list_files($this->importfilepath),
fn($file) => pathinfo($file->pathname, PATHINFO_EXTENSION) === $this->get_import_data_file_extension()
&& !str_contains($file->pathname, '/'));
if (empty($datafilenames) || count($datafilenames) > 1) {
return false;
}
return file_get_contents($this->extracteddir . reset($datafilenames)->pathname);
}
/**
* Returns the file content from a file which has been stored in the zip archive.
*
* @param string $filename
* @param string $zipsubdir
* @return false|string the file content as string, false if the file could not be found/read
*/
public function get_file_content_from_zip(string $filename, string $zipsubdir = 'files/'): false|string {
if (empty($filename)) {
// Nothing to return.
return false;
}
// Just to be sure extract if not extracted yet.
$this->extract_zip();
if (!str_ends_with($zipsubdir, '/')) {
$zipsubdir .= '/';
}
$filepathinextractedzip = $this->extracteddir . $zipsubdir . $filename;
return file_exists($filepathinextractedzip) ? file_get_contents($filepathinextractedzip) : false;
}
/**
* Extracts (if not already done and if we have a zip file to deal with) the zip file to a temporary directory.
*
* @return void
*/
private function extract_zip(): void {
if ($this->zipfileextracted || $this->importfiletype !== 'zip') {
return;
}
$this->packer = get_file_packer();
core_php_time_limit::raise(180);
$this->extracteddir = make_request_directory();
if (!str_ends_with($this->extracteddir, '/')) {
$this->extracteddir .= '/';
}
$this->packer->extract_to_pathname($this->importfilepath, $this->extracteddir);
$this->zipfileextracted = true;
}
}
@@ -0,0 +1,79 @@
<?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 mod_data\local\importer;
use mod_data\manager;
/**
* Data preset importer for existing presets
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preset_existing_importer extends preset_importer {
/** @var int user id. */
protected $userid;
/** @var string fullname of the preset. */
private $fullname;
/**
* Constructor
*
* @param manager $manager
* @param string $fullname
*/
public function __construct(manager $manager, string $fullname) {
global $USER;
list($userid, $shortname) = explode('/', $fullname, 2);
$context = $manager->get_context();
if ($userid &&
($userid != $USER->id) &&
!has_capability('mod/data:manageuserpresets', $context) &&
!has_capability('mod/data:viewalluserpresets', $context)
) {
throw new \coding_exception('Invalid preset provided');
}
$this->userid = intval($userid);
$this->fullname = $fullname;
$cm = $manager->get_coursemodule();
$course = $cm->get_course();
$filepath = data_preset_path($course, $userid, $shortname);
parent::__construct($manager, $filepath);
}
/**
* Returns user ID
*
* @return int userid
*/
public function get_userid(): int {
return $this->userid;
}
/**
* Returns the information we need to build the importer selector.
*
* @return array Value and name for the preset importer selector
*/
public function get_preset_selector(): array {
return ['name' => 'fullname', 'value' => $this->get_userid().'/'.$this->get_directory()];
}
}
@@ -0,0 +1,506 @@
<?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 mod_data\local\importer;
use core\notification;
use mod_data\manager;
use mod_data\preset;
use stdClass;
use html_writer;
/**
* Abstract class used for data preset importers
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class preset_importer {
/** @var manager manager instance. */
private $manager;
/** @var string directory where to find the preset. */
protected $directory;
/** @var array fields to remove. */
public $fieldstoremove;
/** @var array fields to update. */
public $fieldstoupdate;
/** @var array fields to create. */
public $fieldstocreate;
/** @var array settings to be imported. */
public $settings;
/**
* Constructor
*
* @param manager $manager
* @param string $directory
*/
public function __construct(manager $manager, string $directory) {
$this->manager = $manager;
$this->directory = $directory;
// Read the preset and saved result.
$this->settings = $this->get_preset_settings();
}
/**
* Returns the name of the directory the preset is located in
*
* @return string
*/
public function get_directory(): string {
return basename($this->directory);
}
/**
* Retreive the contents of a file. That file may either be in a conventional directory of the Moodle file storage
*
* @param \file_storage|null $filestorage . Should be null if using a conventional directory
* @param \stored_file|null $fileobj the directory to look in. null if using a conventional directory
* @param string|null $dir the directory to look in. null if using the Moodle file storage
* @param string $filename the name of the file we want
* @return string|null the contents of the file or null if the file doesn't exist.
*/
public function get_file_contents(
?\file_storage &$filestorage,
?\stored_file &$fileobj,
?string $dir,
string $filename
): ?string {
if (empty($filestorage) || empty($fileobj)) {
if (substr($dir, -1) != '/') {
$dir .= '/';
}
if (file_exists($dir.$filename)) {
return file_get_contents($dir.$filename);
} else {
return null;
}
} else {
if ($filestorage->file_exists(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$fileobj->get_filepath(),
$filename)
) {
$file = $filestorage->get_file(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$fileobj->get_filepath(),
$filename
);
return $file->get_content();
} else {
return null;
}
}
}
/**
* Gets the preset settings
*
* @return stdClass Settings to be imported.
*/
public function get_preset_settings(): stdClass {
global $CFG;
require_once($CFG->libdir.'/xmlize.php');
$fs = null;
$fileobj = null;
if (!preset::is_directory_a_preset($this->directory)) {
// Maybe the user requested a preset stored in the Moodle file storage.
$fs = get_file_storage();
$files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
// Preset name to find will be the final element of the directory.
$explodeddirectory = explode('/', $this->directory);
$presettofind = end($explodeddirectory);
// Now go through the available files available and see if we can find it.
foreach ($files as $file) {
if (($file->is_directory() && $file->get_filepath() == '/') || !$file->is_directory()) {
continue;
}
$presetname = trim($file->get_filepath(), '/');
if ($presetname == $presettofind) {
$this->directory = $presetname;
$fileobj = $file;
}
}
if (empty($fileobj)) {
throw new \moodle_exception('invalidpreset', 'data', '', $this->directory);
}
}
$allowedsettings = [
'intro',
'comments',
'requiredentries',
'requiredentriestoview',
'maxentries',
'rssarticles',
'approval',
'defaultsortdir',
'defaultsort'
];
$module = $this->manager->get_instance();
$result = new stdClass;
$result->settings = new stdClass;
$result->importfields = [];
$result->currentfields = $this->manager->get_field_records();
// Grab XML.
$presetxml = $this->get_file_contents($fs, $fileobj, $this->directory, 'preset.xml');
$parsedxml = xmlize($presetxml, 0);
// First, do settings. Put in user friendly array.
$settingsarray = $parsedxml['preset']['#']['settings'][0]['#'];
$result->settings = new StdClass();
foreach ($settingsarray as $setting => $value) {
if (!is_array($value) || !in_array($setting, $allowedsettings)) {
// Unsupported setting.
continue;
}
$result->settings->$setting = $value[0]['#'];
}
// Now work out fields to user friendly array.
if (
array_key_exists('preset', $parsedxml) &&
array_key_exists('#', $parsedxml['preset']) &&
array_key_exists('field', $parsedxml['preset']['#'])) {
$fieldsarray = $parsedxml['preset']['#']['field'];
foreach ($fieldsarray as $field) {
if (!is_array($field)) {
continue;
}
$fieldstoimport = new StdClass();
foreach ($field['#'] as $param => $value) {
if (!is_array($value)) {
continue;
}
$fieldstoimport->$param = $value[0]['#'];
}
$fieldstoimport->dataid = $module->id;
$fieldstoimport->type = clean_param($fieldstoimport->type, PARAM_ALPHA);
$result->importfields[] = $fieldstoimport;
}
}
// Calculate default mapping.
if (is_null($this->fieldstoremove) && is_null($this->fieldstocreate) && is_null($this->fieldstoupdate)) {
$this->set_affected_fields($result->importfields, $result->currentfields);
}
// Now add the HTML templates to the settings array so we can update d.
foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
$result->settings->$templatename = $this->get_file_contents(
$fs,
$fileobj,
$this->directory,
$templatefile
);
}
$result->settings->instance = $module->id;
return $result;
}
/**
* Import the preset into the given database module
*
* @param bool $overwritesettings Whether to overwrite activity settings or not.
* @return bool Wether the importing has been successful.
*/
public function import(bool $overwritesettings): bool {
global $DB, $OUTPUT, $CFG;
$settings = $this->settings->settings;
$currentfields = $this->settings->currentfields;
$missingfieldtypes = [];
$module = $this->manager->get_instance();
foreach ($this->fieldstoupdate as $currentid => $updatable) {
if ($currentid != -1 && isset($currentfields[$currentid])) {
$fieldobject = data_get_field_from_id($currentfields[$currentid]->id, $module);
$toupdate = false;
foreach ($updatable as $param => $value) {
if ($param != "id" && $fieldobject->field->$param !== $value) {
$fieldobject->field->$param = $value;
}
}
unset($fieldobject->field->similarfield);
$fieldobject->update_field();
unset($fieldobject);
}
}
foreach ($this->fieldstocreate as $newfield) {
/* Make a new field */
$filepath = $CFG->dirroot."/mod/data/field/$newfield->type/field.class.php";
if (!file_exists($filepath)) {
$missingfieldtypes[] = $newfield->name;
continue;
}
include_once($filepath);
if (!isset($newfield->description)) {
$newfield->description = '';
}
$classname = 'data_field_' . $newfield->type;
$fieldclass = new $classname($newfield, $module);
$fieldclass->insert_field();
unset($fieldclass);
}
if (!empty($missingfieldtypes)) {
echo $OUTPUT->notification(get_string('missingfieldtypeimport', 'data') . html_writer::alist($missingfieldtypes));
}
// Get rid of all old unused data.
foreach ($currentfields as $cid => $currentfield) {
if (!array_key_exists($cid, $this->fieldstoupdate)) {
// Delete all information related to fields.
$todelete = data_get_field_from_id($currentfield->id, $module);
$todelete->delete_field();
}
}
// Handle special settings here.
if (!empty($settings->defaultsort)) {
if (is_numeric($settings->defaultsort)) {
// Old broken value.
$settings->defaultsort = 0;
} else {
$settings->defaultsort = (int)$DB->get_field(
'data_fields',
'id',
['dataid' => $module->id, 'name' => $settings->defaultsort]
);
}
} else {
$settings->defaultsort = 0;
}
// Do we want to overwrite all current database settings?
if ($overwritesettings) {
// All supported settings.
$overwrite = array_keys((array)$settings);
} else {
// Only templates and sorting.
$overwrite = ['singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter',
'addtemplate', 'rsstemplate', 'rsstitletemplate', 'csstemplate', 'jstemplate',
'asearchtemplate', 'defaultsortdir', 'defaultsort'];
}
// Now overwrite current data settings.
foreach ($module as $prop => $unused) {
if (in_array($prop, $overwrite)) {
$module->$prop = $settings->$prop;
}
}
data_update_instance($module);
return $this->cleanup();
}
/**
* Returns information about the fields needs to be removed, updated or created.
*
* @param array $newfields Array of new fields to be applied.
* @param array $currentfields Array of current fields on database activity.
* @return void
*/
public function set_affected_fields(array $newfields = [], array $currentfields = []): void {
$fieldstoremove = [];
$fieldstocreate = [];
$preservedfields = [];
// Maps fields and makes new ones.
if (!empty($newfields)) {
// We require an injective mapping, and need to know what to protect.
foreach ($newfields as $newid => $newfield) {
$preservedfieldid = optional_param("field_$newid", -1, PARAM_INT);
if (array_key_exists($preservedfieldid, $preservedfields)) {
throw new \moodle_exception('notinjectivemap', 'data');
}
if ($preservedfieldid == -1) {
// Let's check if there is any field with same type and name that we could map to.
foreach ($currentfields as $currentid => $currentfield) {
if (($currentfield->type == $newfield->type) &&
($currentfield->name == $newfield->name) && !array_key_exists($currentid, $preservedfields)) {
// We found a possible default map.
$preservedfieldid = $currentid;
$preservedfields[$currentid] = $newfield;
}
}
}
if ($preservedfieldid == -1) {
// We need to create a new field.
$fieldstocreate[] = $newfield;
} else {
$preservedfields[$preservedfieldid] = $newfield;
}
}
}
foreach ($currentfields as $currentid => $currentfield) {
if (!array_key_exists($currentid, $preservedfields)) {
$fieldstoremove[] = $currentfield;
}
}
$this->fieldstocreate = $fieldstocreate;
$this->fieldstoremove = $fieldstoremove;
$this->fieldstoupdate = $preservedfields;
}
/**
* Any clean up routines should go here
*
* @return bool Wether the preset has been successfully cleaned up.
*/
public function cleanup(): bool {
return true;
}
/**
* Check if the importing process needs fields mapping.
*
* @return bool True if the current database needs to map the fields imported.
*/
public function needs_mapping(): bool {
if (!$this->manager->has_fields()) {
return false;
}
return (!empty($this->fieldstocreate) || !empty($this->fieldstoremove));
}
/**
* Returns the information we need to build the importer selector.
*
* @return array Value and name for the preset importer selector
*/
public function get_preset_selector(): array {
return ['name' => 'directory', 'value' => $this->get_directory()];
}
/**
* Helper function to finish up the import routine.
*
* Called from fields and presets pages.
*
* @param bool $overwritesettings Whether to overwrite activity settings or not.
* @param stdClass $instance database instance object
* @return void
*/
public function finish_import_process(bool $overwritesettings, stdClass $instance): void {
$result = $this->import($overwritesettings);
if ($result) {
notification::success(get_string('importsuccess', 'mod_data'));
} else {
notification::error(get_string('cannotapplypreset', 'mod_data'));
}
$backurl = new \moodle_url('/mod/data/field.php', ['d' => $instance->id]);
redirect($backurl);
}
/**
* Get the right importer instance from the provided parameters (POST or GET)
*
* @param manager $manager the current database manager
* @return preset_importer the relevant preset_importer instance
* @throws \moodle_exception when the file provided as parameter (POST or GET) does not exist
*/
public static function create_from_parameters(manager $manager): preset_importer {
$fullname = optional_param('fullname', '', PARAM_PATH); // Directory the preset is in.
if (!$fullname) {
$fullname = required_param('directory', PARAM_FILE);
}
return self::create_from_plugin_or_directory($manager, $fullname);
}
/**
* Get the right importer instance from the provided parameters (POST or GET)
*
* @param manager $manager the current database manager
* @param string $pluginordirectory The plugin name or directory to create the importer from.
* @return preset_importer the relevant preset_importer instance
*/
public static function create_from_plugin_or_directory(manager $manager, string $pluginordirectory): preset_importer {
global $CFG;
if (!$pluginordirectory) {
throw new \moodle_exception('emptypresetname', 'mod_data');
}
try {
$presetdir = $CFG->tempdir . '/forms/' . $pluginordirectory;
if (file_exists($presetdir) && is_dir($presetdir)) {
return new preset_upload_importer($manager, $presetdir);
} else {
return new preset_existing_importer($manager, $pluginordirectory);
}
} catch (\moodle_exception $e) {
throw new \moodle_exception('errorpresetnotfound', 'mod_data', '', $pluginordirectory);
}
}
/**
* Get the information needed to decide the modal
*
* @return array An array with all the information to decide the mapping
*/
public function get_mapping_information(): array {
return [
'needsmapping' => $this->needs_mapping(),
'presetname' => preset::get_name_from_plugin($this->get_directory()),
'fieldstocreate' => $this->get_field_names($this->fieldstocreate),
'fieldstoremove' => $this->get_field_names($this->fieldstoremove),
];
}
/**
* Returns a list of the fields
*
* @param array $fields Array of fields to get name from.
* @return string A string listing the names of the fields.
*/
public function get_field_names(array $fields): string {
$fieldnames = array_map(function($field) {
return $field->name;
}, $fields);
return implode(', ', $fieldnames);
}
}
@@ -0,0 +1,55 @@
<?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 mod_data\local\importer;
use mod_data\manager;
/**
* Data preset importer for uploaded presets
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preset_upload_importer extends preset_importer {
/**
* Constructor
*
* @param manager $manager
* @param string $filepath
*/
public function __construct(manager $manager, string $filepath) {
if (is_file($filepath)) {
$fp = get_file_packer();
if ($fp->extract_to_pathname($filepath, $filepath.'_extracted')) {
fulldelete($filepath);
}
$filepath .= '_extracted';
}
parent::__construct($manager, $filepath);
}
/**
* Clean uploaded files up
*
* @return bool Wether the preset has been successfully cleaned up.
*/
public function cleanup(): bool {
return fulldelete($this->directory);
}
}
+501
View File
@@ -0,0 +1,501 @@
<?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 mod_data;
use cm_info;
use context_module;
use completion_info;
use data_field_base;
use mod_data_renderer;
use mod_data\event\course_module_viewed;
use mod_data\event\template_viewed;
use mod_data\event\template_updated;
use moodle_page;
use core_component;
use stdClass;
/**
* Class manager for database activity
*
* @package mod_data
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class manager {
/** Module name. */
const MODULE = 'data';
/** The plugin name. */
const PLUGINNAME = 'mod_data';
/** Template list with their files required to save the information of a preset. */
const TEMPLATES_LIST = [
'listtemplate' => 'listtemplate.html',
'singletemplate' => 'singletemplate.html',
'asearchtemplate' => 'asearchtemplate.html',
'addtemplate' => 'addtemplate.html',
'rsstemplate' => 'rsstemplate.html',
'csstemplate' => 'csstemplate.css',
'jstemplate' => 'jstemplate.js',
'listtemplateheader' => 'listtemplateheader.html',
'listtemplatefooter' => 'listtemplatefooter.html',
'rsstitletemplate' => 'rsstitletemplate.html',
];
/** @var string plugin path. */
public $path;
/** @var stdClass course_module record. */
private $instance;
/** @var context_module the current context. */
private $context;
/** @var cm_info course_modules record. */
private $cm;
/** @var array the current data_fields records.
* Do not access this attribute directly, use $this->get_field_records instead
*/
private $_fieldrecords = null;
/**
* Class constructor.
*
* @param cm_info $cm course module info object
* @param stdClass $instance activity instance object.
*/
public function __construct(cm_info $cm, stdClass $instance) {
global $CFG;
$this->cm = $cm;
$this->instance = $instance;
$this->context = context_module::instance($cm->id);
$this->instance->cmidnumber = $cm->idnumber;
$this->path = $CFG->dirroot . '/mod/' . self::MODULE;
}
/**
* Create a manager instance from an instance record.
*
* @param stdClass $instance an activity record
* @return manager
*/
public static function create_from_instance(stdClass $instance): self {
$cm = get_coursemodule_from_instance(self::MODULE, $instance->id);
// Ensure that $this->cm is a cm_info object.
$cm = cm_info::create($cm);
return new self($cm, $instance);
}
/**
* Create a manager instance from a course_modules record.
*
* @param stdClass|cm_info $cm an activity record
* @return manager
*/
public static function create_from_coursemodule($cm): self {
global $DB;
// Ensure that $this->cm is a cm_info object.
$cm = cm_info::create($cm);
$instance = $DB->get_record(self::MODULE, ['id' => $cm->instance], '*', MUST_EXIST);
return new self($cm, $instance);
}
/**
* Create a manager instance from a data_record entry.
*
* @param stdClass $record the data_record record
* @return manager
*/
public static function create_from_data_record($record): self {
global $DB;
$instance = $DB->get_record(self::MODULE, ['id' => $record->dataid], '*', MUST_EXIST);
$cm = get_coursemodule_from_instance(self::MODULE, $instance->id);
$cm = cm_info::create($cm);
return new self($cm, $instance);
}
/**
* Return the current context.
*
* @return context_module
*/
public function get_context(): context_module {
return $this->context;
}
/**
* Return the current instance.
*
* @return stdClass the instance record
*/
public function get_instance(): stdClass {
return $this->instance;
}
/**
* Return the current cm_info.
*
* @return cm_info the course module
*/
public function get_coursemodule(): cm_info {
return $this->cm;
}
/**
* Return the current module renderer.
*
* @param moodle_page|null $page the current page
* @return mod_data_renderer the module renderer
*/
public function get_renderer(?moodle_page $page = null): mod_data_renderer {
global $PAGE;
$page = $page ?? $PAGE;
return $page->get_renderer(self::PLUGINNAME);
}
/**
* Trigger module viewed event and set the module viewed for completion.
*
* @param stdClass $course course object
*/
public function set_module_viewed(stdClass $course) {
global $CFG;
require_once($CFG->libdir . '/completionlib.php');
// Trigger module viewed event.
$event = course_module_viewed::create([
'objectid' => $this->instance->id,
'context' => $this->context,
]);
$event->add_record_snapshot('course', $course);
$event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot(self::MODULE, $this->instance);
$event->trigger();
// Completion.
$completion = new completion_info($course);
$completion->set_module_viewed($this->cm);
}
/**
* Trigger module template viewed event.
*/
public function set_template_viewed() {
// Trigger an event for viewing templates.
$event = template_viewed::create([
'context' => $this->context,
'courseid' => $this->cm->course,
'other' => [
'dataid' => $this->instance->id,
],
]);
$event->add_record_snapshot(self::MODULE, $this->instance);
$event->trigger();
}
/**
* Return if the database has records.
*
* @return bool true if the database has records
*/
public function has_records(): bool {
global $DB;
return $DB->record_exists('data_records', ['dataid' => $this->instance->id]);
}
/**
* Return if the database has fields.
*
* @return bool true if the database has fields
*/
public function has_fields(): bool {
global $DB;
if ($this->_fieldrecords === null) {
return $DB->record_exists('data_fields', ['dataid' => $this->instance->id]);
}
return !empty($this->_fieldrecords);
}
/**
* Return the database fields.
*
* @return data_field_base[] the field instances.
*/
public function get_fields(): array {
$result = [];
$fieldrecords = $this->get_field_records();
foreach ($fieldrecords as $fieldrecord) {
$result[$fieldrecord->id] = $this->get_field($fieldrecord);
}
return $result;
}
/**
* Return the field records (the current data_fields records).
*
* @return stdClass[] an array of records
*/
public function get_field_records() {
global $DB;
if ($this->_fieldrecords === null) {
$this->_fieldrecords = $DB->get_records('data_fields', ['dataid' => $this->instance->id], 'id');
}
return $this->_fieldrecords;
}
/**
* Return a specific field instance from a field record.
*
* @param stdClass $fieldrecord the fieldrecord to convert
* @return data_field_base the data field class instance
*/
public function get_field(stdClass $fieldrecord): data_field_base {
global $CFG; // Some old field plugins require $CFG to be in the scope.
$filepath = "{$this->path}/field/{$fieldrecord->type}/field.class.php";
$classname = "data_field_{$fieldrecord->type}";
if (!file_exists($filepath)) {
return new data_field_base($fieldrecord, $this->instance, $this->cm);
}
require_once($filepath);
if (!class_exists($classname)) {
return new data_field_base($fieldrecord, $this->instance, $this->cm);
}
$newfield = new $classname($fieldrecord, $this->instance, $this->cm);
return $newfield;
}
/**
* Return a specific template.
*
* NOTE: this method returns a default template if the module template is empty.
* However, it won't update the template database field.
*
* Some possible options:
* - search: string with the current searching text.
* - page: integer repesenting the current pagination page numbre (if any)
* - baseurl: a moodle_url object to the current page.
*
* @param string $templatename
* @param array $options extra display options array
* @return template the template instance
*/
public function get_template(string $templatename, array $options = []): template {
if ($templatename === 'single') {
$templatename = 'singletemplate';
}
$instance = $this->instance;
$templatecontent = $instance->{$templatename} ?? '';
if (empty($templatecontent)) {
$templatecontent = data_generate_default_template($instance, $templatename, 0, false, false);
}
$options['templatename'] = $templatename;
// Some templates have extra options.
$options = array_merge($options, template::get_default_display_options($templatename));
return new template($this, $templatecontent, $options);
}
/** Check if the user can manage templates on the current context.
*
* @param int $userid the user id to check ($USER->id if null).
* @return bool if the user can manage templates on current context.
*/
public function can_manage_templates(?int $userid = null): bool {
global $USER;
if (!$userid) {
$userid = $USER->id;
}
return has_capability('mod/data:managetemplates', $this->context, $userid);
}
/** Check if the user can export entries on the current context.
*
* @param int $userid the user id to check ($USER->id if null).
* @return bool if the user can export entries on current context.
*/
public function can_export_entries(?int $userid = null): bool {
global $USER, $DB;
if (!$userid) {
$userid = $USER->id;
}
// Exportallentries and exportentry are basically the same capability.
return has_capability('mod/data:exportallentries', $this->context) ||
has_capability('mod/data:exportentry', $this->context) ||
(has_capability('mod/data:exportownentry', $this->context) &&
$DB->record_exists('data_records', ['userid' => $userid, 'dataid' => $this->instance->id]));
}
/**
* Update the database templates.
*
* @param stdClass $newtemplates an object with all the new templates
* @return bool if updated successfully.
*/
public function update_templates(stdClass $newtemplates): bool {
global $DB;
$record = (object)[
'id' => $this->instance->id,
];
foreach (self::TEMPLATES_LIST as $templatename => $templatefile) {
if (!isset($newtemplates->{$templatename})) {
continue;
}
$record->{$templatename} = $newtemplates->{$templatename};
}
// The add entry form cannot repeat tags.
if (isset($record->addtemplate) && !data_tags_check($this->instance->id, $record->addtemplate)) {
return false;
}
$DB->update_record(self::MODULE, $record);
$this->instance = $DB->get_record(self::MODULE, ['id' => $this->cm->instance], '*', MUST_EXIST);
// Trigger an event for saving the templates.
$event = template_updated::create(array(
'context' => $this->context,
'courseid' => $this->cm->course,
'other' => array(
'dataid' => $this->instance->id,
)
));
$event->trigger();
return true;
}
/**
* Reset all templates.
*
* @return bool if the reset is done or not
*/
public function reset_all_templates(): bool {
$newtemplates = new stdClass();
foreach (self::TEMPLATES_LIST as $templatename => $templatefile) {
$newtemplates->{$templatename} = '';
}
return $this->update_templates($newtemplates);
}
/**
* Reset all templates related to a specific template.
*
* @param string $templatename the template name
* @return bool if the reset is done or not
*/
public function reset_template(string $templatename): bool {
$newtemplates = new stdClass();
// Reset the template to default.
$newtemplates->{$templatename} = '';
if ($templatename == 'listtemplate') {
$newtemplates->listtemplateheader = '';
$newtemplates->listtemplatefooter = '';
}
if ($templatename == 'rsstemplate') {
$newtemplates->rsstitletemplate = '';
}
return $this->update_templates($newtemplates);
}
/** Check if the user can view a specific preset.
*
* @param preset $preset the preset instance.
* @param int $userid the user id to check ($USER->id if null).
* @return bool if the user can view the preset.
*/
public function can_view_preset(preset $preset, ?int $userid = null): bool {
global $USER;
if (!$userid) {
$userid = $USER->id;
}
$presetuserid = $preset->get_userid();
if ($presetuserid && $presetuserid != $userid) {
return has_capability('mod/data:viewalluserpresets', $this->context, $userid);
}
return true;
}
/**
* Returns an array of all the available presets.
*
* @return array A list with the datapreset plugins and the presets saved by users.
*/
public function get_available_presets(): array {
// First load the datapreset plugins that exist within the modules preset dir.
$pluginpresets = static::get_available_plugin_presets();
// Then find the presets that people have saved.
$savedpresets = static::get_available_saved_presets();
return array_merge($pluginpresets, $savedpresets);
}
/**
* Returns an array of all the presets that users have saved to the site.
*
* @return array A list with the preset saved by the users.
*/
public function get_available_saved_presets(): array {
global $USER;
$presets = [];
$fs = get_file_storage();
$files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
if (empty($files)) {
return $presets;
}
$canviewall = has_capability('mod/data:viewalluserpresets', $this->get_context());
foreach ($files as $file) {
$isnotdirectory = ($file->is_directory() && $file->get_filepath() == '/') || !$file->is_directory();
$userid = $file->get_userid();
$cannotviewfile = !$canviewall && $userid != $USER->id;
if ($isnotdirectory || $cannotviewfile) {
continue;
}
$preset = preset::create_from_storedfile($this, $file);
$presets[] = $preset;
}
return $presets;
}
/**
* Returns an array of all the available plugin presets.
*
* @return array A list with the datapreset plugins.
*/
public static function get_available_plugin_presets(): array {
$presets = [];
$dirs = core_component::get_plugin_list('datapreset');
foreach ($dirs as $dir => $fulldir) {
if (preset::is_directory_a_preset($fulldir)) {
$preset = preset::create_from_plugin(null, $dir);
$presets[] = $preset;
}
}
return $presets;
}
}
+337
View File
@@ -0,0 +1,337 @@
<?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 mod_data\output;
use mod_data\manager;
use mod_data\preset;
use moodle_url;
use url_select;
/**
* Class responsible for generating the action bar elements in the database module pages.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class action_bar {
/** @var int $id The database module id. */
private $id;
/** @var int $cmid The database course module id. */
private $cmid;
/** @var moodle_url $currenturl The URL of the current page. */
private $currenturl;
/**
* The class constructor.
*
* @param int $id The database module id.
* @param moodle_url $pageurl The URL of the current page.
*/
public function __construct(int $id, moodle_url $pageurl) {
$this->id = $id;
[$course, $cm] = get_course_and_cm_from_instance($this->id, 'data');
$this->cmid = $cm->id;
$this->currenturl = $pageurl;
}
/**
* Generate the output for the action bar in the field page.
*
* @param bool $hasfieldselect Whether the field selector element should be rendered.
* @param null $unused1 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused2 This parameter has been deprecated since 4.1 and should not be used anymore.
* @return string The HTML code for the action bar.
*/
public function get_fields_action_bar(
bool $hasfieldselect = false,
?bool $unused1 = null,
?bool $unused2 = null
): string {
global $PAGE;
if ($unused1 !== null || $unused2 !== null) {
debugging('Deprecated argument passed to get_fields_action_bar method', DEBUG_DEVELOPER);
}
$renderer = $PAGE->get_renderer('mod_data');
$fieldsactionbar = new fields_action_bar($this->id);
return $renderer->render_fields_action_bar($fieldsactionbar);
}
/**
* Generate the output for the action bar in the field mappings page.
*
* @return string The HTML code for the action bar.
*/
public function get_fields_mapping_action_bar(): string {
global $PAGE;
$renderer = $PAGE->get_renderer('mod_data');
$fieldsactionbar = new fields_mappings_action_bar($this->id);
$data = $fieldsactionbar->export_for_template($renderer);
return $renderer->render_from_template('mod_data/fields_action_bar', $data);
}
/**
* Generate the output for the create a new field action menu.
*
* @param bool $isprimarybutton is the action trigger a primary or secondary button?
* @return \action_menu Action menu to create a new field
*/
public function get_create_fields(bool $isprimarybutton = false): \action_menu {
// Get the list of possible fields (plugins).
$plugins = \core_component::get_plugin_list('datafield');
$menufield = [];
foreach ($plugins as $plugin => $fulldir) {
$menufield[$plugin] = get_string('pluginname', "datafield_{$plugin}");
}
asort($menufield);
$fieldselect = new \action_menu();
$triggerclasses = ['btn'];
$triggerclasses[] = $isprimarybutton ? 'btn-primary' : 'btn-secondary';
$fieldselect->set_menu_trigger(get_string('newfield', 'mod_data'), join(' ', $triggerclasses));
$fieldselectparams = ['id' => $this->cmid, 'mode' => 'new'];
foreach ($menufield as $fieldtype => $fieldname) {
$fieldselectparams['newtype'] = $fieldtype;
$fieldselect->add(new \action_menu_link(
new moodle_url('/mod/data/field.php', $fieldselectparams),
new \pix_icon('field/' . $fieldtype, $fieldname, 'data'),
$fieldname,
false
));
}
$fieldselect->set_additional_classes('singlebutton');
return $fieldselect;
}
/**
* Generate the output for the action selector in the view page.
*
* @param bool $hasentries Whether entries exist.
* @param string $mode The current view mode (list, view...).
* @return string The HTML code for the action selector.
*/
public function get_view_action_bar(bool $hasentries, string $mode): string {
global $PAGE;
$viewlistlink = new moodle_url('/mod/data/view.php', ['d' => $this->id]);
$viewsinglelink = new moodle_url('/mod/data/view.php', ['d' => $this->id, 'mode' => 'single']);
$menu = [
$viewlistlink->out(false) => get_string('listview', 'mod_data'),
$viewsinglelink->out(false) => get_string('singleview', 'mod_data'),
];
$activeurl = $this->currenturl;
if ($this->currenturl->get_param('rid') || $this->currenturl->get_param('mode') == 'single') {
$activeurl = $viewsinglelink;
}
$urlselect = new url_select($menu, $activeurl->out(false), null, 'viewactionselect');
$urlselect->set_label(get_string('viewnavigation', 'mod_data'), ['class' => 'sr-only']);
$renderer = $PAGE->get_renderer('mod_data');
$viewactionbar = new view_action_bar($this->id, $urlselect, $hasentries, $mode);
return $renderer->render_view_action_bar($viewactionbar);
}
/**
* Generate the output for the action selector in the templates page.
*
* @return string The HTML code for the action selector.
*/
public function get_templates_action_bar(): string {
global $PAGE;
$listtemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id,
'mode' => 'listtemplate']);
$singletemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id,
'mode' => 'singletemplate']);
$advancedsearchtemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id,
'mode' => 'asearchtemplate']);
$addtemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id, 'mode' => 'addtemplate']);
$rsstemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id, 'mode' => 'rsstemplate']);
$csstemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id, 'mode' => 'csstemplate']);
$jstemplatelink = new moodle_url('/mod/data/templates.php', ['d' => $this->id, 'mode' => 'jstemplate']);
$menu = [
$addtemplatelink->out(false) => get_string('addtemplate', 'mod_data'),
$singletemplatelink->out(false) => get_string('singletemplate', 'mod_data'),
$listtemplatelink->out(false) => get_string('listtemplate', 'mod_data'),
$advancedsearchtemplatelink->out(false) => get_string('asearchtemplate', 'mod_data'),
$csstemplatelink->out(false) => get_string('csstemplate', 'mod_data'),
$jstemplatelink->out(false) => get_string('jstemplate', 'mod_data'),
$rsstemplatelink->out(false) => get_string('rsstemplate', 'mod_data'),
];
$selectmenu = new \core\output\select_menu('presetsactions', $menu, $this->currenturl->out(false));
$selectmenu->set_label(get_string('templatesnavigation', 'mod_data'), ['class' => 'sr-only']);
$renderer = $PAGE->get_renderer('mod_data');
$presetsactions = $this->get_presets_actions_select(false);
// Reset single template action.
$resetcurrrent = new moodle_url($this->currenturl);
$resetcurrrent->param('action', 'resettemplate');
$presetsactions->add(new \action_menu_link(
$resetcurrrent,
null,
get_string('resettemplate', 'mod_data'),
false,
['data-action' => 'resettemplate', 'data-dataid' => $this->id]
));
// Reset all templates action.
$resetallurl = new moodle_url($this->currenturl);
$resetallurl->params([
'action' => 'resetalltemplates',
'sesskey' => sesskey(),
]);
$presetsactions->add(new \action_menu_link(
$resetallurl,
null,
get_string('resetalltemplates', 'mod_data'),
false,
['data-action' => 'resetalltemplates', 'data-dataid' => $this->id]
));
$templatesactionbar = new templates_action_bar($this->id, $selectmenu, null, null, $presetsactions);
return $renderer->render_templates_action_bar($templatesactionbar);
}
/**
* Generate the output for the action selector in the presets page.
*
* @return string The HTML code for the action selector.
*/
public function get_presets_action_bar(): string {
global $PAGE;
$renderer = $PAGE->get_renderer('mod_data');
$presetsactionbar = new presets_action_bar($this->cmid, $this->get_presets_actions_select(true));
return $renderer->render_presets_action_bar($presetsactionbar);
}
/**
* Generate the output for the action selector in the presets preview page.
*
* @param manager $manager the manager instance
* @param string $fullname the preset fullname
* @param string $current the current template name
* @return string The HTML code for the action selector
*/
public function get_presets_preview_action_bar(manager $manager, string $fullname, string $current): string {
global $PAGE;
$renderer = $PAGE->get_renderer(manager::PLUGINNAME);
$cm = $manager->get_coursemodule();
$menu = [];
$selected = null;
foreach (['listtemplate', 'singletemplate'] as $templatename) {
$link = new moodle_url('/mod/data/preset.php', [
'd' => $this->id,
'template' => $templatename,
'fullname' => $fullname,
'action' => 'preview',
]);
$menu[$link->out(false)] = get_string($templatename, manager::PLUGINNAME);
if (!$selected || $templatename == $current) {
$selected = $link->out(false);
}
}
$urlselect = new url_select($menu, $selected, null);
$urlselect->set_label(get_string('templatesnavigation', manager::PLUGINNAME), ['class' => 'sr-only']);
$data = [
'title' => get_string('preview', manager::PLUGINNAME, preset::get_name_from_plugin($fullname)),
'hasback' => true,
'backtitle' => get_string('back'),
'backurl' => new moodle_url('/mod/data/preset.php', ['id' => $cm->id]),
'extraurlselect' => $urlselect->export_for_template($renderer),
];
return $renderer->render_from_template('mod_data/action_bar', $data);
}
/**
* Helper method to get the selector for the presets action.
*
* @param bool $hasimport Whether the Import buttons must be included or not.
* @return \action_menu|null The selector object used to display the presets actions. Null when the import button is not
* displayed and the database hasn't any fields.
*/
protected function get_presets_actions_select(bool $hasimport = false): ?\action_menu {
global $DB;
$hasfields = $DB->record_exists('data_fields', ['dataid' => $this->id]);
// Early return if the database has no fields and the import action won't be displayed.
if (!$hasfields && !$hasimport) {
return null;
}
$actionsselect = new \action_menu();
$actionsselect->set_menu_trigger(get_string('actions'), 'btn btn-secondary');
if ($hasimport) {
// Import.
$actionsselectparams = ['id' => $this->cmid];
$actionsselect->add(new \action_menu_link(
new moodle_url('/mod/data/preset.php', $actionsselectparams),
null,
get_string('importpreset', 'mod_data'),
false,
['data-action' => 'importpresets', 'data-dataid' => $this->cmid]
));
}
// If the database has no fields, export and save as preset options shouldn't be displayed.
if ($hasfields) {
// Export.
$actionsselectparams = ['id' => $this->cmid, 'action' => 'export'];
$actionsselect->add(new \action_menu_link(
new moodle_url('/mod/data/preset.php', $actionsselectparams),
null,
get_string('exportpreset', 'mod_data'),
false
));
// Save as preset.
$actionsselect->add(new \action_menu_link(
new moodle_url('/mod/data/preset.php', $actionsselectparams),
null,
get_string('saveaspreset', 'mod_data'),
false,
['data-action' => 'saveaspreset', 'data-dataid' => $this->id]
));
}
return $actionsselect;
}
}
@@ -0,0 +1,67 @@
<?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 mod_data\output;
use moodle_url;
use templatable;
use renderable;
/**
* Renderable class for the Add entries button in the database activity.
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class add_entries_action implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/**
* The class constructor.
*
* @param int $id The database module id.
* @param bool $hasentries Whether entries exist.
*/
public function __construct(int $id) {
$this->id = $id;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the add entries button.
* @return \stdClass or null if the user has no permission to add new entries.
*/
public function export_for_template(\renderer_base $output): ?\stdClass {
global $PAGE, $DB;
$database = $DB->get_record('data', ['id' => $this->id]);
$cm = get_coursemodule_from_instance('data', $this->id);
$currentgroup = groups_get_activity_group($cm);
$groupmode = groups_get_activity_groupmode($cm);
if (data_user_can_add_entry($database, $currentgroup, $groupmode, $PAGE->context)) {
$addentrylink = new moodle_url('/mod/data/edit.php', ['d' => $this->id, 'backto' => $PAGE->url->out(false)]);
$button = new \single_button($addentrylink, get_string('add', 'mod_data'), 'get', \single_button::BUTTON_PRIMARY);
return $button->export_for_template($output);
}
return null;
}
}
+102
View File
@@ -0,0 +1,102 @@
<?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 mod_data\output;
use core_tag_tag;
use mod_data\manager;
use templatable;
use renderable;
/**
* Renderable class for the default templates in the database activity.
*
* @package mod_data
* @copyright 2022 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class defaulttemplate implements templatable, renderable {
/** @var array $fields The array containing the existing fields. */
private $fields;
/** @var string $templatename The template name (addtemplate, listtemplate...). */
private $templatename;
/** @var bool $isform Whether a form should be displayed instead of data. */
private $isform;
/**
* The class constructor.
*
* @param array $fields The array containing the existing fields.
* @param string $templatename The template name (addtemplate, listtemplate...).
* @param bool $isform Whether a form should be displayed instead of data.
*/
public function __construct(array $fields, string $templatename, bool $isform) {
$this->fields = $fields;
$this->templatename = $templatename;
$this->isform = $isform;
}
/**
* Obtains the mustache template name for this database template.
*
* @return string the file mustache path for this template.
*/
public function get_templatename(): string {
return 'mod_data/defaulttemplate_' . $this->templatename;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array The data to display.
*/
public function export_for_template(\renderer_base $output): array {
$result = [];
$exportedfields = [];
foreach ($this->fields as $field) {
$fieldname = $field->field->name;
if ($this->isform) {
$fieldcontent = $field->display_add_field();
} else {
$fieldcontent = '[[' . $fieldname . ']]';
}
$exportedfields[] = [
'fieldname' => $fieldname,
'fieldcontent' => $fieldcontent,
];
}
if (!empty($exportedfields)) {
$result['fields'] = $exportedfields;
}
if (core_tag_tag::is_enabled(manager::PLUGINNAME, 'data_records')) {
// Add tags information only if they are enabled.
if ($this->isform) {
$tags = data_generate_tag_form();
} else {
$tags = '##tags##';
}
$result['tags'] = $tags;
}
return $result;
}
}
@@ -0,0 +1,70 @@
<?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 mod_data\output;
use mod_data\manager;
use moodle_url;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements for an empty database activity.
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class empty_database_action_bar implements templatable, renderable {
/** @var manager The manager instance. */
protected $manager;
/**
* The class constructor.
*
* @param int $id The database module id.
*/
public function __construct(manager $manager) {
$this->manager = $manager;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE;
$instance = $this->manager->get_instance();
$addentrybutton = new add_entries_action($instance->id);
$data = ['addentrybutton' => $addentrybutton->export_for_template($output)];
if (has_capability('mod/data:manageentries', $PAGE->context)) {
$params = ['d' => $instance->id, 'backto' => $PAGE->url->out(false)];
$importentrieslink = new moodle_url('/mod/data/import.php', $params);
$importentriesbutton = new \single_button($importentrieslink,
get_string('importentries', 'mod_data'), 'get');
$data['importentriesbutton'] = $importentriesbutton->export_for_template($output);
}
return $data;
}
}
@@ -0,0 +1,70 @@
<?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 mod_data\output;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the field pages in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fields_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/**
* The class constructor.
*
* @param int $id The database module id
* @param null $unused1 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused2 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused3 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused4 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param \action_menu|null $unused5 This parameter has been deprecated since 4.2 and should not be used anymore.
*/
public function __construct(int $id, $unused1 = null, $unused2 = null,
$unused3 = null, $unused4 = null,
?\action_menu $unused5 = null) {
if ($unused1 !== null || $unused2 !== null || $unused3 !== null || $unused4 !== null || $unused5 !== null) {
debugging('Deprecated argument passed to fields_action_bar constructor', DEBUG_DEVELOPER);
}
$this->id = $id;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$data = [
'd' => $this->id,
'title' => get_string('managefields', 'mod_data'),
];
return $data;
}
}
@@ -0,0 +1,57 @@
<?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 mod_data\output;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the fields mapping page in the database activity.
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fields_mappings_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/**
* The class constructor.
*
* @param int $id The database module id
*/
public function __construct(int $id) {
$this->id = $id;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
return [
'tertiarytitle' => get_string('fieldmappings', 'mod_data'),
'hasback' => true,
'backtitle' => get_string('back'),
'backurl' => new \moodle_url('/mod/data/preset.php', ['d' => $this->id]),
];
}
}
+118
View File
@@ -0,0 +1,118 @@
<?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 mod_data\output;
use templatable;
use renderable;
use mod_data\manager;
use mod_data\preset;
use mod_data\template;
use moodle_page;
use moodle_url;
/**
* Preset preview output class.
*
* @package mod_data
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preset_preview implements templatable, renderable {
/** @var manager manager instance. */
private $manager;
/** @var preset the preset. */
private $preset;
/** @var string the template to preview. */
private $templatename;
/**
* The class constructor.
*
* @param manager $manager the activity instance manager
* @param preset $preset the preset
* @param string $templatename the templatename
*/
public function __construct(manager $manager, preset $preset, string $templatename) {
$this->manager = $manager;
$this->preset = $preset;
$this->templatename = $templatename;
}
/**
* Add the preset CSS and JS to the page.
*
* @param moodle_page $page the current page instance
*/
public function prepare_page(moodle_page $page) {
$instance = $this->manager->get_instance();
$preset = $this->preset;
// Add CSS and JS.
$csscontent = $preset->get_template_content('csstemplate');
if (!empty($csscontent)) {
$url = new moodle_url('/mod/data/css.php', ['d' => $instance->id, 'preset' => $preset->get_fullname()]);
$page->requires->css($url);
}
$jscontent = $preset->get_template_content('jstemplate');
if (!empty($jscontent)) {
$url = new moodle_url('/mod/data/js.php', ['d' => $instance->id, 'preset' => $preset->get_fullname()]);
$page->requires->js($url);
}
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$coursemodule = $this->manager->get_coursemodule();
$preset = $this->preset;
// Get fields for preview.
$count = ($this->templatename == 'listtemplate') ? 2 : 1;
$fields = $preset->get_fields(true);
$entries = $preset->get_sample_entries($count);
$templatecontent = $preset->get_template_content($this->templatename);
$useurl = new moodle_url('/mod/data/field.php');
// Generate preview content.
$options = ['templatename' => $this->templatename];
if ($this->templatename == 'listtemplate') {
$options['showmore'] = true;
}
$parser = new template($this->manager, $templatecontent, $options, $fields);
$content = $parser->parse_entries($entries);
if ($this->templatename == 'listtemplate') {
$listtemplateheader = $preset->get_template_content('listtemplateheader');
$listtemplatefooter = $preset->get_template_content('listtemplatefooter');
$content = $listtemplateheader . $content . $listtemplatefooter;
}
return [
'cmid' => $coursemodule->id,
'description' => $preset->description ?? '',
'preview' => $content,
'formactionurl' => $useurl->out(),
'userid' => $preset->get_userid() ?? 0,
'shortname' => $preset->shortname,
];
}
}
+238
View File
@@ -0,0 +1,238 @@
<?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 mod_data\output;
use action_menu;
use action_menu_link_secondary;
use mod_data\manager;
use mod_data\preset;
use moodle_url;
use templatable;
use renderable;
use renderer_base;
use stdClass;
/**
* Renderable class for the presets table in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class presets implements templatable, renderable {
/** @var array $presets The array containing the existing presets. */
private $presets;
/** @var moodle_url $formactionurl The action url for the form. */
private $formactionurl;
/** @var bool $manage Whether the manage preset options should be displayed. */
private $manage;
/** @var int $id instance id */
private $id;
/** @var int $cmid course module id */
private $cmid;
/**
* The class constructor.
*
* @param manager $manager The database manager
* @param array $presets The array containing the existing presets
* @param moodle_url $formactionurl The action url for the form
* @param bool $manage Whether the manage preset options should be displayed
*/
public function __construct(manager $manager, array $presets, moodle_url $formactionurl, bool $manage = false) {
$this->id = $manager->get_instance()->id;
$this->cmid = $manager->get_coursemodule()->id;
$this->presets = $presets;
$this->formactionurl = $formactionurl;
$this->manage = $manage;
}
/**
* Export the data for the mustache template.
*
* @param renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(renderer_base $output): array {
$presets = $this->get_presets($output);
return [
'id' => $this->cmid,
'formactionurl' => $this->formactionurl->out(),
'showmanage' => $this->manage,
'presets' => $presets,
];
}
/**
* Returns the presets list with the information required to display them.
*
* @param renderer_base $output The renderer to be used to render the action bar elements.
* @return array Presets list.
*/
private function get_presets(renderer_base $output): array {
$presets = [];
foreach ($this->presets as $index => $preset) {
$presetname = $preset->name;
$userid = $preset instanceof preset ? $preset->get_userid() : $preset->userid;
if (!empty($userid)) {
// If the preset has the userid field, the full name of creator it will be added to the end of the name.
$userfieldsapi = \core_user\fields::for_name();
$namefields = $userfieldsapi->get_sql('', false, '', '', false)->selects;
$fields = 'id, ' . $namefields;
$presetuser = \core_user::get_user($userid, $fields, MUST_EXIST);
$username = fullname($presetuser, true);
$presetname = "{$presetname} ({$username})";
}
$actions = $this->get_preset_action_menu($output, $preset, $userid);
$fullname = $preset->get_fullname();
$previewurl = new moodle_url(
'/mod/data/preset.php',
['d' => $this->id, 'fullname' => $fullname, 'action' => 'preview']
);
$presets[] = [
'id' => $this->id,
'cmid' => $this->cmid,
'name' => $preset->name,
'url' => $previewurl->out(),
'shortname' => $preset->shortname,
'fullname' => $presetname,
'description' => $preset->description,
'userid' => $userid,
'actions' => $actions,
'presetindex' => $index,
];
}
return $presets;
}
/**
* Return the preset action menu data.
*
* @param renderer_base $output The renderer to be used to render the action bar elements.
* @param preset|stdClass $preset the preset object
* @param int|null $userid the user id (null for plugin presets)
* @return stdClass the resulting action menu
*/
private function get_preset_action_menu(renderer_base $output, $preset, ?int $userid): stdClass {
$actions = new stdClass();
// If we cannot manage then return an empty menu.
if (!$this->manage) {
return $actions;
}
$actionmenu = new action_menu();
$actionmenu->set_kebab_trigger();
$actionmenu->set_action_label(get_string('actions'));
$actionmenu->set_additional_classes('presets-actions');
$canmanage = $preset->can_manage();
$usepreseturl = new moodle_url('/mod/data/preset.php', [
'action' => 'usepreset',
'cmid' => $this->cmid,
]);
$this->add_action_menu($actionmenu, get_string('usepreset', 'mod_data'), $usepreseturl, [
'data-action' => 'selectpreset',
'data-presetname' => $preset->get_fullname(),
'data-cmid' => $this->cmid,
]
);
// Attention: the id here is the cm->id, not d->id.
$previewpreseturl = new moodle_url('/mod/data/preset.php', [
'fullname' => $preset->get_fullname(),
'action' => 'preview',
'id' => $this->cmid,
]);
$this->add_action_menu($actionmenu, get_string('previewaction', 'mod_data'), $previewpreseturl, [
'data-action' => 'preview',
]
);
// Presets saved by users can be edited or removed.
if (!$preset->isplugin) {
// Edit.
if ($canmanage) {
$editactionurl = new moodle_url('/mod/data/preset.php', [
'action' => 'edit',
'd' => $this->id,
]);
$this->add_action_menu($actionmenu, get_string('edit'), $editactionurl, [
'data-action' => 'editpreset',
'data-presetname' => $preset->name,
'data-presetdescription' => $preset->description,
]);
}
// Export.
$exporturl = new moodle_url('/mod/data/preset.php', [
'presetname' => $preset->name,
'action' => 'export',
'd' => $this->id,
]);
$this->add_action_menu($actionmenu, get_string('export', 'mod_data'), $exporturl, [
'data-action' => 'exportpreset',
'data-presetname' => $preset->name,
'data-presetdescription' => $preset->description,
]);
// Delete.
if ($canmanage) {
$deleteactionurl = new moodle_url('/mod/data/preset.php', [
'action' => 'delete',
'd' => $this->id,
]);
$this->add_action_menu($actionmenu, get_string('delete'), $deleteactionurl, [
'data-action' => 'deletepreset',
'data-presetname' => $preset->name,
]);
}
}
$actions = $actionmenu->export_for_template($output);
return $actions;
}
/**
* Add action to the action menu
*
* @param action_menu $actionmenu
* @param string $actionlabel
* @param moodle_url $actionurl
* @param array $otherattributes
* @return void
*/
private function add_action_menu(action_menu &$actionmenu, string $actionlabel, moodle_url $actionurl,
array $otherattributes) {
$attributes = [
'data-dataid' => $this->id,
];
$actionmenu->add(new action_menu_link_secondary(
$actionurl,
null,
$actionlabel,
array_merge($attributes, $otherattributes),
));
}
}
@@ -0,0 +1,65 @@
<?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 mod_data\output;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the presets page in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class presets_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $cmid;
/** @var \action_menu $actionsselect The presets actions selector object. */
private $actionsselect;
/**
* The class constructor.
*
* @param int $cmid The database module id
* @param \action_menu|null $actionsselect The presets actions selector object.
*/
public function __construct(int $cmid, ?\action_menu $actionsselect) {
$this->cmid = $cmid;
$this->actionsselect = $actionsselect;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$data = [
'id' => $this->cmid,
];
if ($this->actionsselect) {
$data['actionsselect'] = $this->actionsselect->export_for_template($output);
}
return $data;
}
}
+193
View File
@@ -0,0 +1,193 @@
<?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 mod_data\output;
use templatable;
use renderable;
use mod_data\manager;
use moodle_url;
use texteditor;
/**
* Renderable class for template editor.
*
* @package mod_data
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_editor implements templatable, renderable {
/** @var manager manager instance. */
private $manager;
/** @var string the template name. */
private $templatename;
/**
* The class constructor.
*
* @param manager $manager the activity instance manager
* @param string $templatename the template to edit
*/
public function __construct(manager $manager, string $templatename) {
$this->manager = $manager;
$this->templatename = $templatename;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$instance = $this->manager->get_instance();
$cm = $this->manager->get_coursemodule();
$data = [
'title' => get_string('header' . $this->templatename, 'data'),
'sesskey' => sesskey(),
'disableeditor' => true,
'url' => new moodle_url('/mod/data/templates.php', ['id' => $cm->id, 'mode' => $this->templatename]),
];
// Determine whether to use HTML editors.
$usehtmleditor = false;
$disableeditor = false;
if (($this->templatename !== 'csstemplate') && ($this->templatename !== 'jstemplate')) {
$usehtmleditor = data_get_config($instance, "editor_{$this->templatename}", true);
$disableeditor = true;
}
$data['usehtmleditor'] = $usehtmleditor;
// Some templates, like CSS, cannot enable the wysiwyg editor.
$data['disableeditor'] = $disableeditor;
$tools = new template_editor_tools($this->manager, $this->templatename);
$data['toolbar'] = $tools->export_for_template($output);
$data['editors'] = $this->get_editors_data($usehtmleditor);
return $data;
}
/**
* Get the editors data.
*
* @param bool $usehtmleditor if the user wants wysiwyg editor or not
* @return array editors data
*/
private function get_editors_data(bool $usehtmleditor): array {
global $PAGE;
$result = [];
$manager = $this->manager;
$instance = $manager->get_instance();
// Setup editor.
editors_head_setup();
$PAGE->requires->js_call_amd(
'mod_data/templateseditor',
'init',
['d' => $instance->id, 'mode' => $this->templatename]
);
$format = FORMAT_PLAIN;
if ($usehtmleditor) {
$format = FORMAT_HTML;
}
$editor = editors_get_preferred_editor($format);
// Add editors.
if ($this->templatename === 'listtemplate') {
$template = $manager->get_template('listtemplateheader');
$result[] = $this->generate_editor_data(
$editor,
'header',
'listtemplateheader',
$template->get_template_content()
);
$maineditorname = 'multientry';
} else {
$maineditorname = $this->templatename;
}
$template = $manager->get_template($this->templatename);
$result[] = $this->generate_editor_data(
$editor,
$maineditorname,
$this->templatename,
$template->get_template_content()
);
if ($this->templatename === 'listtemplate') {
$template = $manager->get_template('listtemplatefooter');
$result[] = $this->generate_editor_data(
$editor,
'footer',
'listtemplatefooter',
$template->get_template_content()
);
}
if ($this->templatename === 'rsstemplate') {
$template = $manager->get_template('rsstitletemplate');
$result[] = $this->generate_editor_data(
$editor,
'rsstitletemplate',
'rsstitletemplate',
$template->get_template_content()
);
}
return $result;
}
/**
* Generate a single editor data.
*
* @param texteditor $editor the editor object
* @param string $name the editor name
* @param string $fieldname the field name
* @param string|null $value the current value
* @return array the editor data
*/
private function generate_editor_data(
texteditor $editor,
string $name,
string $fieldname,
?string $value
): array {
$options = [
'trusttext' => false,
'forcehttps' => false,
'subdirs' => false,
'maxfiles' => 0,
'maxbytes' => 0,
'changeformat' => 0,
'noclean' => false,
];
$result = [
'name' => get_string($name, 'data'),
'fieldname' => $fieldname,
'value' => $value,
];
$editor->set_text($value);
$editor->use_editor($fieldname, $options);
return $result;
}
}
@@ -0,0 +1,206 @@
<?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 mod_data\output;
use templatable;
use renderable;
use core_tag_tag;
use mod_data\manager;
/**
* Renderable class for template editor tools.
*
* @package mod_data
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_editor_tools implements templatable, renderable {
/** @var manager manager instance. */
private $manager;
/** @var string the template name. */
private $templatename;
/**
* The class constructor.
*
* @param manager $manager the activity instance manager
* @param string $templatename the template to edit
*/
public function __construct(manager $manager, string $templatename) {
$this->manager = $manager;
$this->templatename = $templatename;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$tools = [
$this->get_field_tags($this->templatename),
$this->get_field_info_tags($this->templatename),
$this->get_action_tags($this->templatename),
$this->get_other_tags($this->templatename),
];
$tools = array_filter($tools, static function ($value) {
return !empty($value['tags']);
});
return [
'toolshelp' => $output->help_icon('availabletags', 'data'),
'hastools' => !empty($tools),
'tools' => array_values($tools),
];
}
/**
* Return the field template tags.
*
* @param string $templatename the template name
* @return array|null array of tags.
*/
protected function get_field_tags(string $templatename): array {
$name = get_string('fields', 'data');
if ($templatename == 'csstemplate' || $templatename == 'jstemplate') {
return $this->get_optgroup_data($name, []);
}
$taglist = [];
$fields = $this->manager->get_fields();
foreach ($fields as $field) {
if ($field->type === 'unknown') {
continue;
}
$fieldname = $field->get_name();
$taglist["[[$fieldname]]"] = $fieldname;
}
$taglist['##otherfields##'] = get_string('otherfields', 'data');
return $this->get_optgroup_data($name, $taglist);
}
/**
* Return the field information template tags.
*
* @param string $templatename the template name
* @return array|null array of tags.
*/
protected function get_field_info_tags(string $templatename): array {
$name = get_string('fieldsinformationtags', 'data');
$taglist = [];
$fields = $this->manager->get_fields();
foreach ($fields as $field) {
if ($field->type === 'unknown') {
continue;
}
$fieldname = $field->get_name();
if ($templatename == 'addtemplate') {
$taglist["[[$fieldname#id]]"] = get_string('fieldtagid', 'mod_data', $fieldname);
}
$taglist["[[$fieldname#name]]"] = get_string('fieldtagname', 'mod_data', $fieldname);
$taglist["[[$fieldname#description]]"] = get_string('fieldtagdescription', 'mod_data', $fieldname);
}
return $this->get_optgroup_data($name, $taglist);
}
/**
* Return the field action tags.
*
* @param string $templatename the template name
* @return array|null array of tags.
*/
protected function get_action_tags(string $templatename): array {
$name = get_string('actions');
if ($templatename == 'addtemplate' || $templatename == 'asearchtemplate') {
return $this->get_optgroup_data($name, []);
}
$taglist = [
'##actionsmenu##' => get_string('actionsmenu', 'data'),
'##edit##' => get_string('edit', 'data'),
'##delete##' => get_string('delete', 'data'),
'##approve##' => get_string('approve', 'data'),
'##disapprove##' => get_string('disapprove', 'data'),
];
if ($templatename != 'rsstemplate') {
$taglist['##export##'] = get_string('export', 'data');
}
if ($templatename != 'singletemplate') {
$taglist['##more##'] = get_string('more', 'data');
$taglist['##moreurl##'] = get_string('moreurl', 'data');
$taglist['##delcheck##'] = get_string('delcheck', 'data');
}
return $this->get_optgroup_data($name, $taglist);
}
/**
* Return the available other tags
*
* @param string $templatename the template name
* @return array associative array of tags => tag name
*/
protected function get_other_tags(string $templatename): array {
$name = get_string('other', 'data');
$taglist = [];
if ($templatename == 'asearchtemplate') {
$taglist['##firstname##'] = get_string('firstname');
$taglist['##lastname##'] = get_string('lastname');
return $this->get_optgroup_data($name, $taglist);
}
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
$taglist['##tags##'] = get_string('tags');
}
if ($templatename == 'addtemplate') {
return $this->get_optgroup_data($name, $taglist);
}
$taglist['##timeadded##'] = get_string('timeadded', 'data');
$taglist['##timemodified##'] = get_string('timemodified', 'data');
$taglist['##user##'] = get_string('user');
$taglist['##userpicture##'] = get_string('userpic');
$taglist['##approvalstatus##'] = get_string('approvalstatus', 'data');
$taglist['##id##'] = get_string('id', 'data');
if ($templatename == 'singletemplate') {
return $this->get_optgroup_data($name, $taglist);
}
$taglist['##comments##'] = get_string('comments', 'data');
return $this->get_optgroup_data($name, $taglist);
}
/**
* Generate a valid optgroup data.
*
* @param string $name the optgroup name
* @param array $taglist the indexed array of taglists ($tag => $tagname)
* @return array of optgroup data
*/
protected function get_optgroup_data(string $name, array $taglist): array {
$tags = [];
foreach ($taglist as $tag => $tagname) {
$tags[] = [
'tag' => "$tag",
'tagname' => $tagname . ' - ' . $tag,
];
}
return [
'name' => $name,
'tags' => $tags,
];
}
}
@@ -0,0 +1,70 @@
<?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 mod_data\output;
use core\output\select_menu;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the template pages in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class templates_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/** @var select_menu $selectmenu The URL selector object. */
private $selectmenu;
/** @var \action_menu $actionsselect The presets actions selector object. */
private $actionsselect;
/**
* The class constructor.
*
* @param int $id The database module id.
* @param select_menu $selectmenu The URL selector object.
* @param null $unused1 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param null $unused2 This parameter has been deprecated since 4.1 and should not be used anymore.
* @param \action_menu $actionsselect The presets actions selector object.
*/
public function __construct(int $id, select_menu $selectmenu, $unused1, $unused2, \action_menu $actionsselect) {
$this->id = $id;
$this->selectmenu = $selectmenu;
$this->actionsselect = $actionsselect;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
return [
'd' => $this->id,
'selectmenu' => $this->selectmenu->export_for_template($output),
'actionsselect' => $this->actionsselect->export_for_template($output),
];
}
}
+148
View File
@@ -0,0 +1,148 @@
<?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 mod_data\output;
use data_portfolio_caller;
use mod_data\manager;
use moodle_url;
use portfolio_add_button;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the view pages in the database activity.
*
* @package mod_data
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class view_action_bar implements templatable, renderable {
/** @var int $id The database module id. */
private $id;
/** @var \url_select $urlselect The URL selector object. */
private $urlselect;
/** @var bool $hasentries Whether entries exist. */
private $hasentries;
/** @var bool $mode The current view mode (list, view...). */
private $mode;
/**
* The class constructor.
*
* @param int $id The database module id.
* @param \url_select $urlselect The URL selector object.
* @param bool $hasentries Whether entries exist.
* @param string $mode The current view mode (list, view...).
*/
public function __construct(int $id, \url_select $urlselect, bool $hasentries, string $mode) {
$this->id = $id;
$this->urlselect = $urlselect;
$this->hasentries = $hasentries;
$this->mode = $mode;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE, $DB, $CFG;
$data = [
'urlselect' => $this->urlselect->export_for_template($output),
];
$activity = $DB->get_record('data', ['id' => $this->id], '*', MUST_EXIST);
$manager = manager::create_from_instance($activity);
$actionsselect = null;
// Import entries.
if (has_capability('mod/data:manageentries', $manager->get_context())) {
$actionsselect = new \action_menu();
$actionsselect->set_menu_trigger(get_string('actions'), 'btn btn-secondary');
$importentrieslink = new moodle_url('/mod/data/import.php', ['d' => $this->id, 'backto' => $PAGE->url->out(false)]);
$actionsselect->add(new \action_menu_link(
$importentrieslink,
null,
get_string('importentries', 'mod_data'),
false
));
}
// Export entries.
if (has_capability(DATA_CAP_EXPORT, $manager->get_context()) && $this->hasentries) {
if (!$actionsselect) {
$actionsselect = new \action_menu();
$actionsselect->set_menu_trigger(get_string('actions'), 'btn btn-secondary');
}
$exportentrieslink = new moodle_url('/mod/data/export.php', ['d' => $this->id, 'backto' => $PAGE->url->out(false)]);
$actionsselect->add(new \action_menu_link(
$exportentrieslink,
null,
get_string('exportentries', 'mod_data'),
false
));
}
// Export to portfolio. This is for exporting all records, not just the ones in the search.
if ($this->mode == '' && !empty($CFG->enableportfolios) && $this->hasentries) {
if ($manager->can_export_entries()) {
// Add the portfolio export button.
require_once($CFG->libdir . '/portfoliolib.php');
$cm = $manager->get_coursemodule();
$button = new portfolio_add_button();
$button->set_callback_options(
'data_portfolio_caller',
['id' => $cm->id],
'mod_data'
);
if (data_portfolio_caller::has_files($activity)) {
// No plain HTML.
$button->set_formats([PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_LEAP2A]);
}
$exporturl = $button->to_html(PORTFOLIO_ADD_MOODLE_URL);
if (!is_null($exporturl)) {
if (!$actionsselect) {
$actionsselect = new \action_menu();
$actionsselect->set_menu_trigger(get_string('actions'), 'btn btn-secondary');
}
$actionsselect->add(new \action_menu_link(
$exporturl,
null,
get_string('addtoportfolio', 'portfolio'),
false
));
}
}
}
if ($actionsselect) {
$data['actionsselect'] = $actionsselect->export_for_template($output);
}
return $data;
}
}
+156
View File
@@ -0,0 +1,156 @@
<?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 mod_data\output;
use action_link;
use core\output\sticky_footer;
use html_writer;
use mod_data\manager;
use mod_data\template;
use moodle_url;
use renderer_base;
/**
* Renderable class for sticky footer in the view pages of the database activity.
*
* @package mod_data
* @copyright 2022 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class view_footer extends sticky_footer {
/** @var int $totalcount the total records count. */
private $totalcount;
/** @var int $currentpage the current page */
private $currentpage;
/** @var int $nowperpage the number of elements per page */
private $nowperpage;
/** @var moodle_url $baseurl the page base url */
private $baseurl;
/** @var template $parser the template name */
private $parser;
/** @var manager $manager if the user can manage capabilities or not */
private $manager;
/**
* The class constructor.
*
* @param manager $manager the activity manager
* @param int $totalcount the total records count
* @param int $currentpage the current page
* @param int $nowperpage the number of elements per page
* @param moodle_url $baseurl the page base url
* @param template $parser the current template name
*/
public function __construct(
manager $manager,
int $totalcount,
int $currentpage,
int $nowperpage,
moodle_url $baseurl,
template $parser
) {
$this->manager = $manager;
$this->totalcount = $totalcount;
$this->currentpage = $currentpage;
$this->nowperpage = $nowperpage;
$this->baseurl = $baseurl;
$this->parser = $parser;
}
/**
* Export this data so it can be used as the context for a mustache template (core/inplace_editable).
*
* @param renderer_base $output typically, the renderer that's calling this function
* @return array data context for a mustache template
*/
public function export_for_template(renderer_base $output) {
$this->set_content(
$this->get_footer_output($output)
);
return parent::export_for_template($output);
}
/**
* Generate the pre-rendered footer content.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return string the rendered content
*/
public function get_footer_output(renderer_base $output): string {
$data = [];
$cm = $this->manager->get_coursemodule();
$instance = $this->manager->get_instance();
$currentgroup = groups_get_activity_group($cm);
$groupmode = groups_get_activity_groupmode($cm);
$context = $this->manager->get_context();
$canmanageentries = has_capability('mod/data:manageentries', $context);
$parser = $this->parser;
// Sticky footer content.
$data['pagination'] = $output->paging_bar(
$this->totalcount,
$this->currentpage,
$this->nowperpage,
$this->baseurl
);
if ($parser->get_template_name() != 'singletemplate' && $parser->has_tag('delcheck') && $canmanageentries) {
// Build the select/deselect all control.
$selectallid = 'selectall-listview-entries';
$togglegroup = 'listview-entries';
$mastercheckbox = new \core\output\checkbox_toggleall($togglegroup, true, [
'id' => $selectallid,
'name' => $selectallid,
'value' => 1,
'label' => get_string('selectall'),
'classes' => 'btn-secondary mx-1',
], true);
$data['selectall'] = $output->render($mastercheckbox);
$data['deleteselected'] = html_writer::empty_tag('input', [
'class' => 'btn btn-secondary mx-1',
'type' => 'submit',
'value' => get_string('deleteselected'),
'disabled' => true,
'data-action' => 'toggle',
'data-togglegroup' => $togglegroup,
'data-toggle' => 'action',
]);
}
if (data_user_can_add_entry($instance, $currentgroup, $groupmode, $context)) {
$addentrylink = new moodle_url(
'/mod/data/edit.php',
['id' => $cm->id, 'backto' => $this->baseurl]
);
$addentrybutton = new action_link(
$addentrylink,
get_string('add', 'mod_data'),
null,
['class' => 'btn btn-primary mx-1', 'role' => 'button']
);
$data['addentrybutton'] = $addentrybutton->export_for_template($output);
}
return $output->render_from_template('mod_data/view_footer', $data);
}
}
@@ -0,0 +1,80 @@
<?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 mod_data\output;
use mod_data\manager;
use moodle_url;
use templatable;
use renderable;
/**
* Renderable class for the action bar elements in the zero state (no fields created) pages in the database activity.
*
* @package mod_data
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class zero_state_action_bar implements templatable, renderable {
/** @var manager The manager instance. */
protected $manager;
/**
* The class constructor.
*
* @param manager $manager The manager instance.
*/
public function __construct(manager $manager) {
$this->manager = $manager;
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE;
$data = [];
if ($this->manager->can_manage_templates()) {
$cm = $this->manager->get_coursemodule();
$instance = $this->manager->get_instance();
$params = ['id' => $cm->id, 'backto' => $PAGE->url->out(false)];
$usepresetlink = new moodle_url('/mod/data/preset.php', $params);
$usepresetbutton = new \single_button($usepresetlink,
get_string('usestandard', 'mod_data'), 'get', \single_button::BUTTON_PRIMARY);
$data['usepresetbutton'] = $usepresetbutton->export_for_template($output);
$actionbar = new \mod_data\output\action_bar($instance->id, $PAGE->url);
$createfieldbutton = $actionbar->get_create_fields();
$data['createfieldbutton'] = $createfieldbutton->export_for_template($output);
$importpresetlink = new moodle_url('/mod/data/preset.php', $params);
$importpresetbutton = new \single_button($importpresetlink,
get_string('importapreset', 'mod_data'), 'get', \single_button::BUTTON_SECONDARY, [
'data-action' => 'importpresets',
'data-dataid' => $cm->id,
]);
$data['importpresetbutton'] = $importpresetbutton->export_for_template($output);
}
return $data;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?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/>.
/**
* Subplugin info class.
*
* @package mod_data
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\plugininfo;
use core\plugininfo\base;
defined('MOODLE_INTERNAL') || die();
class datafield extends base {
public function is_uninstall_allowed() {
global $DB;
return !$DB->record_exists('data_fields', array('type'=>$this->name));
}
}
@@ -0,0 +1,35 @@
<?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/>.
/**
* Subplugin info class.
*
* @package mod_data
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\plugininfo;
use core\plugininfo\base;
defined('MOODLE_INTERNAL') || die();
class datapreset extends base {
public function is_uninstall_allowed() {
return true;
}
}
+827
View File
@@ -0,0 +1,827 @@
<?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 mod_data;
use core_component;
use invalid_parameter_exception;
use data_field_base;
use moodle_exception;
use SimpleXMLElement;
use stdClass;
use stored_file;
/**
* Class preset for database activity.
*
* @package mod_data
* @copyright 2022 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preset {
/** @var manager manager instance. */
private $manager;
/** @var bool whether the preset is a plugin or has been saved by the user. */
public $isplugin;
/** @var string The preset name. */
public $name;
/** @var string The preset shortname. For datapreset plugins that is the folder; for saved presets, that's the preset name. */
public $shortname;
/** @var string The preset description. */
public $description;
/** @var stored_file For saved presets that's the file object for the root folder. It's null for plugins or for presets that
* haven't been saved yet. */
public $storedfile;
/** @var array|null the field sample instances. */
private $fields = null;
/** @var stdClass|null the preset.xml parsed information. */
protected $xmlinfo = null;
/**
* Class constructor.
*
* @param manager|null $manager the current instance manager
* @param bool $isplugin whether the preset is a plugin or has been saved by the user
* @param string $name the preset name
* @param string $shortname the preset shortname
* @param string|null $description the preset description
* @param stored_file|null $storedfile for saved presets, that's the file for the root folder
* @throws invalid_parameter_exception
*/
protected function __construct(
?manager $manager,
bool $isplugin,
string $name,
string $shortname,
?string $description = '',
?stored_file $storedfile = null
) {
if (!$isplugin && is_null($manager)) {
throw new invalid_parameter_exception('The $manager parameter can only be null for plugin presets.');
}
$this->manager = $manager;
$this->isplugin = $isplugin;
$this->name = $name;
$this->shortname = $shortname;
$this->description = $description;
$this->storedfile = $storedfile;
}
/**
* Create a preset instance from a stored file.
*
* @param manager $manager the current instance manager
* @param stored_file $file the preset root folder
* @return preset|null If the given file doesn't belong to the expected component/filearea/context, null will be returned
*/
public static function create_from_storedfile(manager $manager, stored_file $file): ?self {
if ($file->get_component() != DATA_PRESET_COMPONENT
|| $file->get_filearea() != DATA_PRESET_FILEAREA
|| $file->get_contextid() != DATA_PRESET_CONTEXT) {
return null;
}
$isplugin = false;
$name = trim($file->get_filepath(), '/');
$description = static::get_attribute_value($file->get_filepath(), 'description');
return new self($manager, $isplugin, $name, $name, $description, $file);
}
/**
* Create a preset instance from a plugin.
*
* @param manager|null $manager the current instance manager
* @param string $pluginname the datapreset plugin name
* @return preset|null The plugin preset or null if there is no datapreset plugin with the given name.
*/
public static function create_from_plugin(?manager $manager, string $pluginname): ?self {
$found = false;
$plugins = array_keys(core_component::get_plugin_list('datapreset'));
foreach ($plugins as $plugin) {
if ($plugin == $pluginname) {
$found = true;
break;
}
}
if (!$found) {
// If there is no datapreset plugin with this name, return null.
return null;
}
$name = static::get_name_from_plugin($pluginname);
$description = static::get_description_from_plugin($pluginname);
return new self($manager, true, $name, $pluginname, $description);
}
/**
* Create a preset instance from a data_record entry, a preset name and a description.
*
* @param manager $manager the current instance manager
* @param string $presetname the preset name
* @param string|null $description the preset description
* @return preset
*/
public static function create_from_instance(manager $manager, string $presetname, ?string $description = ''): self {
$isplugin = false;
$path = '/' . $presetname . '/';
$file = static::get_file($path, '.');
if (!is_null($file)) {
// If the file is not empty, create the instance based on the storedfile.
return self::create_from_storedfile($manager, $file);
}
return new self($manager, $isplugin, $presetname, $presetname, $description, $file);
}
/**
* Create a preset instance from the preset fullname.
*
* The preset fullname is a concatenation of userid and pluginname|presetname used by most
* preset pages. Plugins uses userid zero while preset instances has the owner as identifier.
*
* This method will throw an exception if the preset instance has a different userid thant the one
* from the $fullname. However, it won't check the current user capabilities.
*
* @param manager $manager the current instance manager
* @param string $fullname the preset full name
* @return preset
*/
public static function create_from_fullname(manager $manager, string $fullname): self {
$parts = explode('/', $fullname, 2);
$userid = empty($parts[0]) ? 0 : (int)$parts[0];
$shortname = empty($parts[1]) ? '' : $parts[1];
// Shortnames with userid zero are plugins.
if ($userid == 0) {
return static::create_from_plugin($manager, $shortname);
}
$path = '/' . $shortname . '/';
$file = static::get_file($path, '.');
$result = static::create_from_storedfile($manager, $file);
if ($result->get_userid() != $userid) {
throw new moodle_exception('invalidpreset', manager::PLUGINNAME);
}
return $result;
}
/**
* Save this preset.
*
* @return bool true if the preset has been saved; false otherwise.
*/
public function save(): bool {
global $USER;
if ($this->isplugin) {
// Plugin presets can't be saved.
return false;
}
if (!is_null($this->storedfile)) {
// It's a pre-existing preset, so it needs to be updated.
return $this->update_user_preset();
}
// The preset hasn't been saved before.
$fs = get_file_storage();
// Create and save the preset.xml file, with the description, settings, fields...
$filerecord = static::get_filerecord('preset.xml', $this->get_path(), $USER->id);
$fs->create_file_from_string($filerecord, $this->generate_preset_xml());
// Create and save the template files.
$instance = $this->manager->get_instance();
foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
$filerecord->filename = $templatefile;
$fs->create_file_from_string($filerecord, $instance->{$templatename});
}
// Update the storedfile with the one we've just saved.
$this->storedfile = static::get_file($this->get_path(), '.');
return true;
}
/**
* Update the stored user preset.
* This method is used internally by the save method.
*
* @return bool true if the preset has been saved; false otherwise.
*/
private function update_user_preset(): bool {
global $USER;
$result = false;
$shouldbesaved = false;
// Update description (if required).
$oldpresetfile = static::get_file($this->storedfile->get_filepath(), 'preset.xml');
$presetxml = $oldpresetfile->get_content();
$parsedxml = simplexml_load_string($presetxml);
if (property_exists($parsedxml, 'description')) {
if ($parsedxml->description != $this->description) {
$parsedxml->description = $this->description;
$shouldbesaved = true;
}
} else {
if (!is_null($this->description)) {
$parsedxml->addChild('description', $this->description);
$shouldbesaved = true;
}
}
// Update name (if required).
$oldname = trim($this->storedfile->get_filepath(), '/');
$newpath = '/' . $this->name . '/';
if ($oldname != $this->name) {
// Preset name has changed, so files need to be updated too because the preset name is saved in the filepath.
foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
$oldfile = static::get_file($this->storedfile->get_filepath(), $templatefile);
$oldfile->rename($newpath, $templatefile);
}
// The root folder should also be renamed.
$this->storedfile->rename($newpath, $this->storedfile->get_filename());
$shouldbesaved = true;
}
// Only save the new preset.xml if there are changes.
if ($shouldbesaved) {
// Before saving preset.xml, the old preset.xml file should be removed.
$oldpresetfile->delete();
// Create the new file with the new content.
$filerecord = static::get_filerecord('preset.xml', $newpath, $USER->id);
$presetcontent = $parsedxml->asXML();
$fs = get_file_storage();
$fs->create_file_from_string($filerecord, $presetcontent);
$result = true;
}
return $result;
}
/**
* Export this preset.
*
* @return string the full path to the exported preset file.
*/
public function export(): string {
if ($this->isplugin) {
// For now, only saved presets can be exported.
return '';
}
$presetname = clean_filename($this->name) . '-preset-' . gmdate("Ymd_Hi");
$exportsubdir = "mod_data/presetexport/$presetname";
$exportdir = make_temp_directory($exportsubdir);
// Generate and write the preset.xml file.
$presetxmldata = static::generate_preset_xml();
$presetxmlfile = fopen($exportdir . '/preset.xml', 'w');
fwrite($presetxmlfile, $presetxmldata);
fclose($presetxmlfile);
// Write the template files.
$instance = $this->manager->get_instance();
foreach (manager::TEMPLATES_LIST as $templatename => $templatefilename) {
$templatefile = fopen("$exportdir/$templatefilename", 'w');
fwrite($templatefile, $instance->{$templatename} ?? '');
fclose($templatefile);
}
// Check if all files have been generated.
if (! static::is_directory_a_preset($exportdir)) {
throw new \moodle_exception('generateerror', 'data');
}
$presetfilenames = array_merge(array_values(manager::TEMPLATES_LIST), ['preset.xml']);
$filelist = [];
foreach ($presetfilenames as $filename) {
$filelist[$filename] = $exportdir . '/' . $filename;
}
$exportfile = $exportdir.'.zip';
file_exists($exportfile) && unlink($exportfile);
$fp = get_file_packer('application/zip');
$fp->archive_to_pathname($filelist, $exportfile);
foreach ($filelist as $file) {
unlink($file);
}
rmdir($exportdir);
return $exportfile;
}
/**
* Return the preset author.
*
* Preset plugins do not have any user id.
*
* @return int|null the userid or null if it is a plugin
*/
public function get_userid(): ?int {
if (!empty($this->storedfile)) {
return $this->storedfile->get_userid();
}
return null;
}
/**
* Return the preset fullname.
*
* Preset fullname is used mostly for urls.
*
* @return string the preset fullname
*/
public function get_fullname(): string {
$userid = $this->get_userid() ?? '0';
return "{$userid}/{$this->shortname}";
}
/**
* Returns the preset path.
*
* @return string|null the preset path is null for plugins and /presetname/ for saved presets.
*/
public function get_path(): ?string {
if ($this->isplugin) {
return null;
}
if (!empty($this->storedfile)) {
return $this->storedfile->get_filepath();
}
return '/' . $this->name . '/';
}
/**
* Return the field instances of the preset.
*
* @param bool $forpreview if the fields are only for preview
* @return data_field_base[] and array with field objects
*/
public function get_fields(bool $forpreview = false): array {
if ($this->fields !== null) {
return $this->fields;
}
// Parse the preset.xml file.
$this->load_preset_xml();
if (empty($this->xmlinfo) || empty($this->xmlinfo->field)) {
$this->fields = [];
return $this->fields;
}
// Generate field instances.
$result = [];
foreach ($this->xmlinfo->field as $fieldinfo) {
$result[(string) $fieldinfo->name] = $this->get_field_instance($fieldinfo, count($result), $forpreview);
}
$this->fields = $result;
return $result;
}
/**
* Convert a preset.xml field data into field instance.
*
* @param SimpleXMLElement $fieldinfo the field xml information
* @param int $id the field id to use
* @param bool $forpreview if the field should support preview
* @return data_field_base the field instance
*/
private function get_field_instance(
SimpleXMLElement $fieldinfo,
int $id = 0,
bool $forpreview = false
): data_field_base {
global $CFG; // Some old field plugins require $CFG to be in the scope.
$fieldrecord = $this->get_fake_field_record($fieldinfo, $id);
$instance = $this->manager->get_instance();
$cm = $this->manager->get_coursemodule();
// Include the plugin.
$filepath = "{$this->manager->path}/field/{$fieldrecord->type}/field.class.php";
if (file_exists($filepath)) {
require_once($filepath);
}
$classname = "data_field_{$fieldrecord->type}";
$newfield = null;
if (class_exists($classname)) {
$newfield = new $classname($fieldrecord, $instance, $cm);
if ($forpreview && !$newfield->supports_preview()) {
$newfield = new data_field_base($fieldrecord, $instance, $cm);
}
} else {
$newfield = new data_field_base($fieldrecord, $instance, $cm);
}
if ($forpreview) {
$newfield->set_preview(true);
}
return $newfield;
}
/**
* Generate a fake field record fomr the preset.xml field data.
*
* @param SimpleXMLElement $fieldinfo the field xml information
* @param int $id the field id to use
* @return stdClass the fake record
*/
private function get_fake_field_record(SimpleXMLElement $fieldinfo, int $id = 0): stdClass {
$instance = $this->manager->get_instance();
// Generate stub record.
$fieldrecord = (object)[
'id' => $id,
'dataid' => $instance->id,
'type' => (string) $fieldinfo->type,
'name' => (string) $fieldinfo->name,
'description' => (string) $fieldinfo->description ?? '',
'required' => (int) $fieldinfo->required ?? 0,
];
for ($i = 1; $i < 11; $i++) {
$name = "param{$i}";
$fieldrecord->{$name} = null;
if (property_exists($fieldinfo, $name)) {
$fieldrecord->{$name} = (string) $fieldinfo->{$name};
}
}
return $fieldrecord;
}
/**
* Return sample entries to preview this preset.
*
* @param int $count the number of entries to generate.
* @return array of sample entries
*/
public function get_sample_entries(int $count = 1): array {
global $USER;
$fields = $this->get_fields();
$instance = $this->manager->get_instance();
$entries = [];
for ($current = 1; $current <= $count; $current++) {
$entry = (object)[
'id' => $current,
'userid' => $USER->id,
'groupid' => 0,
'dataid' => $instance->id,
'timecreated' => time(),
'timemodified' => time(),
'approved' => 1,
];
// Add all necessary user fields.
$userfieldsapi = \core_user\fields::for_userpic()->excluding('id');
$fields = $userfieldsapi->get_required_fields();
foreach ($fields as $field) {
$entry->{$field} = $USER->{$field};
}
$entries[$current] = $entry;
}
return $entries;
}
/**
* Load all the information from the preset.xml.
*/
protected function load_preset_xml() {
if (!empty($this->xmlinfo)) {
return;
}
// Load everything from the XML.
$presetxml = null;
if ($this->isplugin) {
$path = $this->manager->path . '/preset/' . $this->shortname . '/preset.xml';
$presetxml = file_get_contents($path);
} else {
$presetxml = static::get_content_from_file($this->storedfile->get_filepath(), 'preset.xml');
}
$this->xmlinfo = simplexml_load_string($presetxml);
}
/**
* Return the template content from the preset.
*
* @param string $templatename the template name
* @return string the template content
*/
public function get_template_content(string $templatename): string {
$filename = "{$templatename}.html";
if ($templatename == 'csstemplate') {
$filename = "{$templatename}.css";
}
if ($templatename == 'jstemplate') {
$filename = "{$templatename}.js";
}
if ($this->isplugin) {
$path = $this->manager->path . '/preset/' . $this->shortname . '/' . $filename;
$result = file_get_contents($path);
} else {
$result = static::get_content_from_file($this->storedfile->get_filepath(), $filename);
}
if (empty($result)) {
return '';
}
return $result;
}
/**
* Checks if a directory contains all the required files to define a preset.
*
* @param string $directory The patch to check if it contains the preset files or not.
* @return bool True if the directory contains all the preset files; false otherwise.
*/
public static function is_directory_a_preset(string $directory): bool {
$status = true;
$directory = rtrim($directory, '/\\') . '/';
$presetfilenames = array_merge(array_values(manager::TEMPLATES_LIST), ['preset.xml']);
foreach ($presetfilenames as $filename) {
$status &= file_exists($directory.$filename);
}
return $status;
}
/**
* Returns the best name to show for a datapreset plugin.
*
* @param string $pluginname The datapreset plugin name.
* @return string The plugin preset name to display.
*/
public static function get_name_from_plugin(string $pluginname): string {
$pos = strpos($pluginname, '/');
if ($pos !== false) {
$pluginname = substr($pluginname, $pos + 1);
}
if (!strpos(trim($pluginname), ' ') && get_string_manager()->string_exists('modulename', 'datapreset_'.$pluginname)) {
return get_string('modulename', 'datapreset_'.$pluginname);
} else {
return $pluginname;
}
}
/**
* Returns the description to show for a datapreset plugin.
*
* @param string $pluginname The datapreset plugin name.
* @return string The plugin preset description to display.
*/
public static function get_description_from_plugin(string $pluginname): string {
if (get_string_manager()->string_exists('modulename_help', 'datapreset_'.$pluginname)) {
return get_string('modulename_help', 'datapreset_'.$pluginname);
} else {
return '';
}
}
/**
* Helper to get the value of one of the elements in the presets.xml file.
*
* @param string $filepath The preset filepath.
* @param string $name Attribute name to return.
* @return string|null The attribute value; null if the it doesn't exist or the file is not a valid XML.
*/
protected static function get_attribute_value(string $filepath, string $name): ?string {
$value = null;
$presetxml = static::get_content_from_file($filepath, 'preset.xml');
$parsedxml = simplexml_load_string($presetxml);
if ($parsedxml) {
switch ($name) {
case 'description':
if (property_exists($parsedxml, 'description')) {
$value = $parsedxml->description;
}
break;
}
}
return $value;
}
/**
* Helper method to get a file record given a filename, a filepath and a userid, for any of the preset files.
*
* @param string $filename The filename for the filerecord that will be returned.
* @param string $filepath The filepath for the filerecord that will be returned.
* @param int $userid The userid for the filerecord that will be returned.
* @return stdClass A filerecord object with the datapreset context, component and filearea and the given information.
*/
protected static function get_filerecord(string $filename, string $filepath, int $userid): stdClass {
$filerecord = new stdClass;
$filerecord->contextid = DATA_PRESET_CONTEXT;
$filerecord->component = DATA_PRESET_COMPONENT;
$filerecord->filearea = DATA_PRESET_FILEAREA;
$filerecord->itemid = 0;
$filerecord->filepath = $filepath;
$filerecord->userid = $userid;
$filerecord->filename = $filename;
return $filerecord;
}
/**
* Helper method to retrieve a file.
*
* @param string $filepath the directory to look in
* @param string $filename the name of the file we want
* @return stored_file|null the file or null if the file doesn't exist.
*/
public static function get_file(string $filepath, string $filename): ?stored_file {
$file = null;
$fs = get_file_storage();
$fileexists = $fs->file_exists(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$filepath,
$filename
);
if ($fileexists) {
$file = $fs->get_file(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$filepath,
$filename
);
}
return $file;
}
/**
* Helper method to retrieve the contents of a file.
*
* @param string $filepath the directory to look in
* @param string $filename the name of the file we want
* @return string|null the contents of the file or null if the file doesn't exist.
*/
protected static function get_content_from_file(string $filepath, string $filename): ?string {
$templatefile = static::get_file($filepath, $filename);
if ($templatefile) {
return $templatefile->get_content();
}
return null;
}
/**
* Helper method to generate the XML for this preset.
*
* @return string The XML for the preset
*/
protected function generate_preset_xml(): string {
global $DB;
if ($this->isplugin) {
// Only saved presets can generate the preset.xml file.
return '';
}
$presetxmldata = "<preset>\n\n";
// Add description.
$presetxmldata .= '<description>' . htmlspecialchars($this->description ?? '', ENT_COMPAT) . "</description>\n\n";
// Add settings.
// Raw settings are not preprocessed during saving of presets.
$rawsettings = [
'intro',
'comments',
'requiredentries',
'requiredentriestoview',
'maxentries',
'rssarticles',
'approval',
'manageapproved',
'defaultsortdir',
];
$presetxmldata .= "<settings>\n";
$instance = $this->manager->get_instance();
// First, settings that do not require any conversion.
foreach ($rawsettings as $setting) {
$presetxmldata .= "<$setting>" . htmlspecialchars($instance->$setting, ENT_COMPAT) . "</$setting>\n";
}
// Now specific settings.
if ($instance->defaultsort > 0 && $sortfield = data_get_field_from_id($instance->defaultsort, $instance)) {
$presetxmldata .= '<defaultsort>' . htmlspecialchars($sortfield->field->name, ENT_COMPAT) . "</defaultsort>\n";
} else {
$presetxmldata .= "<defaultsort>0</defaultsort>\n";
}
$presetxmldata .= "</settings>\n\n";
// Add fields. Grab all that are non-empty.
$fields = $DB->get_records('data_fields', ['dataid' => $instance->id]);
ksort($fields);
if (!empty($fields)) {
foreach ($fields as $field) {
$presetxmldata .= "<field>\n";
foreach ($field as $key => $value) {
if ($value != '' && $key != 'id' && $key != 'dataid') {
$presetxmldata .= "<$key>" . htmlspecialchars($value, ENT_COMPAT) . "</$key>\n";
}
}
$presetxmldata .= "</field>\n\n";
}
}
$presetxmldata .= '</preset>';
// Check this content is a valid XML.
$preset = new SimpleXMLElement($presetxmldata);
return $preset->asXML();
}
/**
* Checks to see if the user has permission to manage the preset.
*
* @return bool Returns true if the user can manage this preset, false otherwise.
*/
public function can_manage(): bool {
global $USER;
if ($this->isplugin) {
// Plugin presets can't be removed or edited.
return false;
}
$context = $this->manager->get_context();
if (has_capability('mod/data:manageuserpresets', $context)) {
return true;
} else {
if ($this->get_userid() == $USER->id) {
return true;
}
}
return false;
}
/**
* Deletes all files related to a saved preset.
*
* @return bool True if the preset is a saved preset and the file exists in the file system; false otherwise.
*/
public function delete(): bool {
if ($this->isplugin) {
// Plugin presets can't be removed.
return false;
}
$exists = false;
$filepath = $this->get_path();
$dir = self::get_file($filepath, '.');
if (!empty($dir)) {
$exists = true;
$fs = get_file_storage();
$files = $fs->get_directory_files(
$dir->get_contextid(),
$dir->get_component(),
$dir->get_filearea(),
$dir->get_itemid(),
$filepath
);
if (!empty($files)) {
foreach ($files as $file) {
$file->delete();
}
}
$dir->delete();
// Reseting storedfile property because the file has been removed.
$this->storedfile = null;
}
return $exists;
}
}
@@ -0,0 +1,78 @@
<?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/>.
/**
* Contains interface datafield_provider
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Interface datafield_provider, all datafield plugins need to implement it
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface datafield_provider extends
\core_privacy\local\request\plugin\subplugin_provider,
// The data subplugins do not need to do anything themselves for the shared_userlist.
// This is all handled by the parent plugin.
\core_privacy\local\request\shared_userlist_provider
{
/**
* Exports data about one record in {data_content} table.
*
* Datafield plugins providers should implement this method to:
* - preprocess references to files in the response (examples - textarea, picture, file)
* - make content more human-readable (example - replace values separators in multimenu, format date in date)
* - add more information about the field itself (example - list all options for menu, multimenu, radio)
*
* Sample implementation (from datafield_textarea):
*
* $defaultvalue->content = writer::with_context($context)
* ->rewrite_pluginfile_urls([$recordobj->id, $contentobj->id], 'mod_data', 'content', $contentobj->id,
* $defaultvalue->content);
* writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
*
* @param \context_module $context
* @param \stdClass $recordobj record from DB table {data_records}
* @param \stdClass $fieldobj record from DB table {data_fields}
* @param \stdClass $contentobj record from DB table {data_content}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue);
/**
* Allows plugins to delete locally stored data.
*
* Usually datafield plugins do not store anything and this method will be empty.
*
* @param \context_module $context
* @param \stdClass $recordobj record from DB table {data_records}
* @param \stdClass $fieldobj record from DB table {data_fields}
* @param \stdClass $contentobj record from DB table {data_content}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj);
}
+573
View File
@@ -0,0 +1,573 @@
<?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/>.
/**
* Privacy Subsystem implementation for mod_data.
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\privacy;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\helper;
use core_privacy\local\request\transform;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
use core_privacy\manager;
defined('MOODLE_INTERNAL') || die();
/**
* Implementation of the privacy subsystem plugin provider for the database activity module.
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
// This plugin stores personal data.
\core_privacy\local\metadata\provider,
// This plugin is capable of determining which users have data within it.
\core_privacy\local\request\core_userlist_provider,
// This plugin is a core_user_data_provider.
\core_privacy\local\request\plugin\provider {
/**
* Return the fields which contain personal data.
*
* @param collection $collection a reference to the collection to use to store the metadata.
* @return collection the updated collection of metadata items.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_database_table(
'data_records',
[
'userid' => 'privacy:metadata:data_records:userid',
'groupid' => 'privacy:metadata:data_records:groupid',
'timecreated' => 'privacy:metadata:data_records:timecreated',
'timemodified' => 'privacy:metadata:data_records:timemodified',
'approved' => 'privacy:metadata:data_records:approved',
],
'privacy:metadata:data_records'
);
$collection->add_database_table(
'data_content',
[
'fieldid' => 'privacy:metadata:data_content:fieldid',
'content' => 'privacy:metadata:data_content:content',
'content1' => 'privacy:metadata:data_content:content1',
'content2' => 'privacy:metadata:data_content:content2',
'content3' => 'privacy:metadata:data_content:content3',
'content4' => 'privacy:metadata:data_content:content4',
],
'privacy:metadata:data_content'
);
// Link to subplugins.
$collection->add_plugintype_link('datafield', [], 'privacy:metadata:datafieldnpluginsummary');
// Subsystems used.
$collection->link_subsystem('core_comment', 'privacy:metadata:commentpurpose');
$collection->link_subsystem('core_files', 'privacy:metadata:filepurpose');
$collection->link_subsystem('core_tag', 'privacy:metadata:tagpurpose');
$collection->link_subsystem('core_rating', 'privacy:metadata:ratingpurpose');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid the userid.
* @return contextlist the list of contexts containing user info for the user.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$contextlist = new contextlist();
// Fetch all data records that the user rote.
$sql = "SELECT c.id
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
WHERE dr.userid = :userid";
$params = [
'contextlevel' => CONTEXT_MODULE,
'modname' => 'data',
'userid' => $userid,
];
$contextlist->add_from_sql($sql, $params);
// Fetch contexts where the user commented.
$sql = "SELECT c.id
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
JOIN {comments} com ON com.commentarea = :commentarea and com.itemid = dr.id
WHERE com.userid = :userid";
$params = [
'contextlevel' => CONTEXT_MODULE,
'modname' => 'data',
'commentarea' => 'database_entry',
'userid' => $userid,
];
$contextlist->add_from_sql($sql, $params);
// Fetch all data records.
$ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_data', 'entry', 'dr.id', $userid, true);
$sql = "SELECT c.id
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
{$ratingquery->join}
WHERE {$ratingquery->userwhere}";
$params = [
'contextlevel' => CONTEXT_MODULE,
'modname' => 'data',
] + $ratingquery->params;
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!is_a($context, \context_module::class)) {
return;
}
// Find users with data records.
$sql = "SELECT dr.userid
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
WHERE c.id = :contextid";
$params = [
'modname' => 'data',
'contextid' => $context->id,
'contextlevel' => CONTEXT_MODULE,
];
$userlist->add_from_sql('userid', $sql, $params);
// Find users with comments.
\core_comment\privacy\provider::get_users_in_context_from_sql($userlist, 'com', 'mod_data', 'database_entry', $context->id);
// Find users with ratings.
$sql = "SELECT dr.id
FROM {context} c
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
WHERE c.id = :contextid";
$params = [
'modname' => 'data',
'contextid' => $context->id,
'contextlevel' => CONTEXT_MODULE,
];
\core_rating\privacy\provider::get_users_in_context_from_sql($userlist, 'rat', 'mod_data', 'entry', $sql, $params);
}
/**
* Creates an object from all fields in the $record where key starts with $prefix
*
* @param \stdClass $record
* @param string $prefix
* @param array $additionalfields
* @return \stdClass
*/
protected static function extract_object_from_record($record, $prefix, $additionalfields = []) {
$object = new \stdClass();
foreach ($record as $key => $value) {
if (preg_match('/^'.preg_quote($prefix, '/').'(.*)/', $key, $matches)) {
$object->{$matches[1]} = $value;
}
}
if ($additionalfields) {
foreach ($additionalfields as $key => $value) {
$object->$key = $value;
}
}
return $object;
}
/**
* Export one field answer in a record in database activity module
*
* @param \context $context
* @param \stdClass $recordobj record from DB table {data_records}
* @param \stdClass $fieldobj record from DB table {data_fields}
* @param \stdClass $contentobj record from DB table {data_content}
*/
protected static function export_data_content($context, $recordobj, $fieldobj, $contentobj) {
$value = (object)[
'field' => [
// Name and description are displayed in mod_data without applying format_string().
'name' => $fieldobj->name,
'description' => $fieldobj->description,
'type' => $fieldobj->type,
'required' => transform::yesno($fieldobj->required),
],
'content' => $contentobj->content
];
foreach (['content1', 'content2', 'content3', 'content4'] as $key) {
if ($contentobj->$key !== null) {
$value->$key = $contentobj->$key;
}
}
$classname = manager::get_provider_classname_for_component('datafield_' . $fieldobj->type);
if (class_exists($classname) && is_subclass_of($classname, datafield_provider::class)) {
component_class_callback($classname, 'export_data_content',
[$context, $recordobj, $fieldobj, $contentobj, $value]);
} else {
// Data field plugin does not implement datafield_provider, just export default value.
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $value);
}
writer::with_context($context)->export_area_files([$recordobj->id, $contentobj->id], 'mod_data',
'content', $contentobj->id);
}
/**
* SQL query that returns all fields from {data_content}, {data_fields} and {data_records} tables
*
* @return string
*/
protected static function sql_fields() {
return 'd.id AS dataid, dc.id AS contentid, dc.fieldid, df.type AS fieldtype, df.name AS fieldname,
df.description AS fielddescription, df.required AS fieldrequired,
df.param1 AS fieldparam1, df.param2 AS fieldparam2, df.param3 AS fieldparam3, df.param4 AS fieldparam4,
df.param5 AS fieldparam5, df.param6 AS fieldparam6, df.param7 AS fieldparam7, df.param8 AS fieldparam8,
df.param9 AS fieldparam9, df.param10 AS fieldparam10,
dc.content AS contentcontent, dc.content1 AS contentcontent1, dc.content2 AS contentcontent2,
dc.content3 AS contentcontent3, dc.content4 AS contentcontent4,
dc.recordid, dr.timecreated AS recordtimecreated, dr.timemodified AS recordtimemodified,
dr.approved AS recordapproved, dr.groupid AS recordgroupid, dr.userid AS recorduserid';
}
/**
* Export personal data for the given approved_contextlist. User and context information is contained within the contextlist.
*
* @param approved_contextlist $contextlist a list of contexts approved for export.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
if (!$contextlist->count()) {
return;
}
$user = $contextlist->get_user();
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$sql = "SELECT cm.id AS cmid, d.name AS dataname, cm.course AS courseid, " . self::sql_fields() . "
FROM {context} ctx
JOIN {course_modules} cm ON cm.id = ctx.instanceid
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
JOIN {data_content} dc ON dc.recordid = dr.id
JOIN {data_fields} df ON df.id = dc.fieldid
WHERE ctx.id {$contextsql} AND ctx.contextlevel = :contextlevel
AND dr.userid = :userid OR
EXISTS (SELECT 1 FROM {comments} com WHERE com.commentarea=:commentarea
AND com.itemid = dr.id AND com.userid = :userid1) OR
EXISTS (SELECT 1 FROM {rating} r WHERE r.contextid = ctx.id AND r.itemid = dr.id AND r.component = :moddata
AND r.ratingarea = :ratingarea AND r.userid = :userid2)
ORDER BY cm.id, dr.id, dc.fieldid";
$rs = $DB->get_recordset_sql($sql, $contextparams + ['contextlevel' => CONTEXT_MODULE,
'modname' => 'data', 'userid' => $user->id, 'userid1' => $user->id, 'commentarea' => 'database_entry',
'userid2' => $user->id, 'ratingarea' => 'entry', 'moddata' => 'mod_data']);
$context = null;
$recordobj = null;
foreach ($rs as $row) {
if (!$context || $context->instanceid != $row->cmid) {
// This row belongs to the different data module than the previous row.
// Export the data for the previous module.
self::export_data($context, $user);
// Start new data module.
$context = \context_module::instance($row->cmid);
}
if (!$recordobj || $row->recordid != $recordobj->id) {
// Export previous data record.
self::export_data_record($context, $user, $recordobj);
// Prepare for exporting new data record.
$recordobj = self::extract_object_from_record($row, 'record', ['dataid' => $row->dataid]);
}
$fieldobj = self::extract_object_from_record($row, 'field', ['dataid' => $row->dataid]);
$contentobj = self::extract_object_from_record($row, 'content',
['fieldid' => $fieldobj->id, 'recordid' => $recordobj->id]);
self::export_data_content($context, $recordobj, $fieldobj, $contentobj);
}
$rs->close();
self::export_data_record($context, $user, $recordobj);
self::export_data($context, $user);
}
/**
* Export one entry in the database activity module (one record in {data_records} table)
*
* @param \context $context
* @param \stdClass $user
* @param \stdClass $recordobj
*/
protected static function export_data_record($context, $user, $recordobj) {
if (!$recordobj) {
return;
}
$data = [
'userid' => transform::user($user->id),
'groupid' => $recordobj->groupid,
'timecreated' => transform::datetime($recordobj->timecreated),
'timemodified' => transform::datetime($recordobj->timemodified),
'approved' => transform::yesno($recordobj->approved),
];
// Data about the record.
writer::with_context($context)->export_data([$recordobj->id], (object)$data);
// Related tags.
\core_tag\privacy\provider::export_item_tags($user->id, $context, [$recordobj->id],
'mod_data', 'data_records', $recordobj->id);
// Export comments. For records that were not made by this user export only this user's comments, for own records
// export comments made by everybody.
\core_comment\privacy\provider::export_comments($context, 'mod_data', 'database_entry', $recordobj->id,
[$recordobj->id], $recordobj->userid != $user->id);
// Export ratings. For records that were not made by this user export only this user's ratings, for own records
// export ratings from everybody.
\core_rating\privacy\provider::export_area_ratings($user->id, $context, [$recordobj->id], 'mod_data', 'entry',
$recordobj->id, $recordobj->userid != $user->id);
}
/**
* Export basic info about database activity module
*
* @param \context $context
* @param \stdClass $user
*/
protected static function export_data($context, $user) {
if (!$context) {
return;
}
$contextdata = helper::get_context_data($context, $user);
helper::export_context_files($context, $user);
writer::with_context($context)->export_data([], $contextdata);
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context the context to delete in.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
global $DB;
if (!$context instanceof \context_module) {
return;
}
$recordstobedeleted = [];
$sql = "SELECT " . self::sql_fields() . "
FROM {course_modules} cm
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id
LEFT JOIN {data_content} dc ON dc.recordid = dr.id
LEFT JOIN {data_fields} df ON df.id = dc.fieldid
WHERE cm.id = :cmid
ORDER BY dr.id";
$rs = $DB->get_recordset_sql($sql, ['cmid' => $context->instanceid, 'modname' => 'data']);
foreach ($rs as $row) {
self::mark_data_content_for_deletion($context, $row);
$recordstobedeleted[$row->recordid] = $row->recordid;
}
$rs->close();
self::delete_data_records($context, $recordstobedeleted);
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist a list of contexts approved for deletion.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$user = $contextlist->get_user();
$recordstobedeleted = [];
foreach ($contextlist->get_contexts() as $context) {
$sql = "SELECT " . self::sql_fields() . "
FROM {context} ctx
JOIN {course_modules} cm ON cm.id = ctx.instanceid
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id AND dr.userid = :userid
LEFT JOIN {data_content} dc ON dc.recordid = dr.id
LEFT JOIN {data_fields} df ON df.id = dc.fieldid
WHERE ctx.id = :ctxid AND ctx.contextlevel = :contextlevel
ORDER BY dr.id";
$rs = $DB->get_recordset_sql($sql, ['ctxid' => $context->id, 'contextlevel' => CONTEXT_MODULE,
'modname' => 'data', 'userid' => $user->id]);
foreach ($rs as $row) {
self::mark_data_content_for_deletion($context, $row);
$recordstobedeleted[$row->recordid] = $row->recordid;
}
$rs->close();
self::delete_data_records($context, $recordstobedeleted);
}
// Additionally remove comments this user made on other entries.
\core_comment\privacy\provider::delete_comments_for_user($contextlist, 'mod_data', 'database_entry');
// We do not delete ratings made by this user on other records because it may change grades.
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
$recordstobedeleted = [];
list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
$sql = "SELECT " . self::sql_fields() . "
FROM {context} ctx
JOIN {course_modules} cm ON cm.id = ctx.instanceid
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {data} d ON d.id = cm.instance
JOIN {data_records} dr ON dr.dataid = d.id AND dr.userid {$userinsql}
LEFT JOIN {data_content} dc ON dc.recordid = dr.id
LEFT JOIN {data_fields} df ON df.id = dc.fieldid
WHERE ctx.id = :ctxid AND ctx.contextlevel = :contextlevel
ORDER BY dr.id";
$params = [
'ctxid' => $context->id,
'contextlevel' => CONTEXT_MODULE,
'modname' => 'data',
];
$params += $userinparams;
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $row) {
self::mark_data_content_for_deletion($context, $row);
$recordstobedeleted[$row->recordid] = $row->recordid;
}
$rs->close();
self::delete_data_records($context, $recordstobedeleted);
// Additionally remove comments these users made on other entries.
\core_comment\privacy\provider::delete_comments_for_users($userlist, 'mod_data', 'database_entry');
// We do not delete ratings made by users on other records because it may change grades.
}
/**
* Marks a data_record/data_content for deletion
*
* Also invokes callback from datafield plugin in case it stores additional data that needs to be deleted
*
* @param \context $context
* @param \stdClass $row result of SQL query - tables data_content, data_record, data_fields join together
*/
protected static function mark_data_content_for_deletion($context, $row) {
$recordobj = self::extract_object_from_record($row, 'record', ['dataid' => $row->dataid]);
if ($row->contentid && $row->fieldid) {
$fieldobj = self::extract_object_from_record($row, 'field', ['dataid' => $row->dataid]);
$contentobj = self::extract_object_from_record($row, 'content',
['fieldid' => $fieldobj->id, 'recordid' => $recordobj->id]);
// Allow datafield plugin to implement their own deletion.
$classname = manager::get_provider_classname_for_component('datafield_' . $fieldobj->type);
if (class_exists($classname) && is_subclass_of($classname, datafield_provider::class)) {
component_class_callback($classname, 'delete_data_content',
[$context, $recordobj, $fieldobj, $contentobj]);
}
}
}
/**
* Deletes records marked for deletion and all associated data
*
* Should be executed after all records were marked by {@link mark_data_content_for_deletion()}
*
* Deletes records from data_content and data_records tables, associated files, tags, comments and ratings.
*
* @param \context $context
* @param array $recordstobedeleted list of ids of the data records that need to be deleted
*/
protected static function delete_data_records($context, $recordstobedeleted) {
global $DB;
if (empty($recordstobedeleted)) {
return;
}
list($sql, $params) = $DB->get_in_or_equal($recordstobedeleted, SQL_PARAMS_NAMED);
// Delete files.
get_file_storage()->delete_area_files_select($context->id, 'mod_data', 'data_records',
"IN (SELECT dc.id FROM {data_content} dc WHERE dc.recordid $sql)", $params);
// Delete from data_content.
$DB->delete_records_select('data_content', 'recordid ' . $sql, $params);
// Delete from data_records.
$DB->delete_records_select('data_records', 'id ' . $sql, $params);
// Delete tags.
\core_tag\privacy\provider::delete_item_tags_select($context, 'mod_data', 'data_records', $sql, $params);
// Delete comments.
\core_comment\privacy\provider::delete_comments_for_all_users_select($context, 'mod_data', 'database_entry', $sql, $params);
// Delete ratings.
\core_rating\privacy\provider::delete_ratings_select($context, 'mod_data', 'entry', $sql, $params);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?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/>.
/**
* Search area for mod_data activities.
*
* @package mod_data
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\search;
defined('MOODLE_INTERNAL') || die();
/**
* Search area for mod_data activities.
*
* @package mod_data
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activity extends \core_search\base_activity {
/**
* Returns true if this area uses file indexing.
*
* @return bool
*/
public function uses_file_indexing() {
return true;
}
}
+398
View File
@@ -0,0 +1,398 @@
<?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/>.
/**
* Search area for mod_data activity entries.
*
* @package mod_data
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\search;
use mod_data\manager;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/data/lib.php');
require_once($CFG->dirroot . '/lib/grouplib.php');
/**
* Search area for mod_data activity entries.
*
* @package mod_data
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class entry extends \core_search\base_mod {
/**
* @var array Internal quick static cache.
*/
protected $entriesdata = array();
/**
* Returns recordset containing required data for indexing database entries.
*
* @param int $modifiedfrom timestamp
* @param \context|null $context Optional context to restrict scope of returned results
* @return moodle_recordset|null Recordset (or null if no results)
*/
public function get_document_recordset($modifiedfrom = 0, \context $context = null) {
global $DB;
list ($contextjoin, $contextparams) = $this->get_context_restriction_sql(
$context, 'data', 'd', SQL_PARAMS_NAMED);
if ($contextjoin === null) {
return null;
}
$sql = "SELECT dr.*, d.course
FROM {data_records} dr
JOIN {data} d ON d.id = dr.dataid
$contextjoin
WHERE dr.timemodified >= :timemodified";
return $DB->get_recordset_sql($sql,
array_merge($contextparams, ['timemodified' => $modifiedfrom]));
}
/**
* Returns the documents associated with this glossary entry id.
*
* @param stdClass $entry glossary entry.
* @param array $options
* @return \core_search\document
*/
public function get_document($entry, $options = array()) {
try {
$cm = $this->get_cm('data', $entry->dataid, $entry->course);
$context = \context_module::instance($cm->id);
} catch (\dml_missing_record_exception $ex) {
// Notify it as we run here as admin, we should see everything.
debugging('Error retrieving mod_data ' . $entry->id . ' document, not all required data is available: ' .
$ex->getMessage(), DEBUG_DEVELOPER);
return false;
} catch (\dml_exception $ex) {
// Notify it as we run here as admin, we should see everything.
debugging('Error retrieving mod_data' . $entry->id . ' document: ' . $ex->getMessage(), DEBUG_DEVELOPER);
return false;
}
// Prepare associative array with data from DB.
$doc = \core_search\document_factory::instance($entry->id, $this->componentname, $this->areaname);
$doc->set('contextid', $context->id);
$doc->set('courseid', $entry->course);
$doc->set('userid', $entry->userid);
if ($entry->groupid > 0) {
$doc->set('groupid', $entry->groupid);
}
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
$doc->set('modified', $entry->timemodified);
$indexfields = $this->get_fields_for_entries($entry);
if (count($indexfields) < 2) {
return false;
}
// All fields should be already returned as plain text by data_field_base::get_content_value.
$doc->set('title', $indexfields[0]);
$doc->set('content', $indexfields[1]);
if (isset($indexfields[2])) {
$doc->set('description1', $indexfields[2]);
}
if (isset($indexfields[3])) {
$doc->set('description2', $indexfields[3]);
}
return $doc;
}
/**
* Whether the user can access the document or not.
*
* @throws \dml_missing_record_exception
* @throws \dml_exception
* @param int $id Glossary entry id
* @return bool
*/
public function check_access($id) {
global $DB, $USER;
if (isguestuser()) {
return \core_search\manager::ACCESS_DENIED;
}
$now = time();
$sql = "SELECT dr.*, d.*
FROM {data_records} dr
JOIN {data} d ON d.id = dr.dataid
WHERE dr.id = ?";
$entry = $DB->get_record_sql($sql, array( $id ), IGNORE_MISSING);
if (!$entry) {
return \core_search\manager::ACCESS_DELETED;
}
if (($entry->timeviewfrom && $now < $entry->timeviewfrom) || ($entry->timeviewto && $now > $entry->timeviewto)) {
return \core_search\manager::ACCESS_DENIED;
}
$cm = $this->get_cm('data', $entry->dataid, $entry->course);
$context = \context_module::instance($cm->id);
$canmanageentries = has_capability('mod/data:manageentries', $context);
if (!has_capability('mod/data:viewentry', $context)) {
return \core_search\manager::ACCESS_DENIED;
}
$numberofentriesindb = $DB->count_records('data_records', array('dataid' => $entry->dataid));
$requiredentriestoview = $entry->requiredentriestoview;
if ($requiredentriestoview && ($requiredentriestoview > $numberofentriesindb) &&
($USER->id != $entry->userid) && !$canmanageentries) {
return \core_search\manager::ACCESS_DENIED;
}
if ($entry->approval && !$entry->approved && ($entry->userid != $USER->id) && !$canmanageentries) {
return \core_search\manager::ACCESS_DENIED;
}
$currentgroup = groups_get_activity_group($cm, true);
$groupmode = groups_get_activity_groupmode($cm);
if (($groupmode == 1) && ($entry->groupid != $currentgroup) && !$canmanageentries) {
return \core_search\manager::ACCESS_DENIED;
}
return \core_search\manager::ACCESS_GRANTED;
}
/**
* Link to database entry.
*
* @param \core_search\document $doc
* @return \moodle_url
*/
public function get_doc_url(\core_search\document $doc) {
$entry = $this->get_entry($doc->get('itemid'));
return new \moodle_url('/mod/data/view.php', array( 'd' => $entry->dataid, 'rid' => $entry->id ));
}
/**
* Link to the database activity.
*
* @param \core_search\document $doc
* @return \moodle_url
*/
public function get_context_url(\core_search\document $doc) {
$entry = $this->get_entry($doc->get('itemid'));
return new \moodle_url('/mod/data/view.php', array('d' => $entry->dataid));
}
/**
* Returns true if this area uses file indexing.
*
* @return bool
*/
public function uses_file_indexing() {
return true;
}
/**
* Add the database entries attachments.
*
* @param \core_search\document $doc
* @return void
*/
public function attach_files($doc) {
global $DB;
$entryid = $doc->get('itemid');
try {
$entry = $this->get_entry($entryid);
} catch (\dml_missing_record_exception $e) {
debugging('Could not get record to attach files to '.$doc->get('id'), DEBUG_DEVELOPER);
return;
}
$cm = $this->get_cm('data', $entry->dataid, $doc->get('courseid'));
$context = \context_module::instance($cm->id);
// Get all content fields which have files in them.
$contentssql = "
SELECT con.*
FROM {data_content} con
JOIN {files} fil
ON fil.component = :component
AND fil.filearea = :filearea
AND fil.itemid = con.id
WHERE con.recordid = :recordid
";
$contents = $DB->get_recordset_sql($contentssql, [
'recordid' => $entryid,
'component' => 'mod_data',
'filearea' => 'content',
]);
foreach ($contents as $content) {
// Get the files and attach them.
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'mod_data', 'content', $content->id, 'filename', false);
foreach ($files as $file) {
$doc->add_stored_file($file);
}
}
$contents->close();
}
/**
* Get database entry data
*
* @throws \dml_exception
* @param int $entryid
* @return stdClass
*/
protected function get_entry($entryid) {
global $DB;
if (empty($this->entriesdata[$entryid])) {
$this->entriesdata[$entryid] = $DB->get_record('data_records', array( 'id' => $entryid ), '*', MUST_EXIST);
}
return $this->entriesdata[$entryid];
}
/**
* get_fields_for_entries
*
* @param StdClass $entry
* @return array
*/
protected function get_fields_for_entries($entry) {
global $DB;
$indexfields = array();
$validfieldtypes = array('text', 'textarea', 'menu', 'radiobutton', 'checkbox', 'multimenu', 'url');
$sql = "SELECT dc.*, df.name AS fldname,
df.type AS fieldtype, df.required
FROM {data_content} dc, {data_fields} df
WHERE dc.fieldid = df.id
AND dc.recordid = :recordid";
$contents = $DB->get_records_sql($sql, ['recordid' => $entry->id]);
$filteredcontents = [];
$data = $DB->get_record('data', ['id' => $entry->dataid]);
$manager = manager::create_from_instance($data);
$template = $manager->get_template('addtemplate');
$template = $template->get_template_content();
// Filtering out the data_content records having invalid fieldtypes.
foreach ($contents as $content) {
if (in_array($content->fieldtype, $validfieldtypes)) {
$filteredcontents[] = $content;
}
}
foreach ($filteredcontents as $content) {
$classname = $this->get_field_class_name($content->fieldtype);
if (!$classname) {
$content->addtemplateposition = -1;
continue;
}
$content->priority = $classname::get_priority();
$content->addtemplateposition = strpos($template ?? '', '[['.$content->fldname.']]');
}
$orderqueue = new \SPLPriorityQueue();
// Filtering out contents which belong to fields that aren't present in the addtemplate of the database activity instance.
foreach ($filteredcontents as $content) {
if ($content->addtemplateposition >= 0) {
$orderqueue->insert($content, $content->addtemplateposition);
}
}
$filteredcontents = array();
while ($orderqueue->valid()) {
$filteredcontents[] = $orderqueue->extract();
}
// SPLPriorityQueue sorts according to descending order of the priority (here, addtemplateposition).
$filteredcontents = array_reverse($filteredcontents);
// Using a CUSTOM SPLPriorityQueure instance to sort out the filtered contents according to these rules :
// 1. Priorities in $fieldtypepriorities
// 2. Compulsory fieldtypes are to be given the top priority.
$contentqueue = new sortedcontentqueue($filteredcontents);
foreach ($filteredcontents as $key => $content) {
$contentqueue->insert($content, $key);
}
while ($contentqueue->valid()) {
$content = $contentqueue->extract();
$classname = $this->get_field_class_name($content->fieldtype);
$indexfields[] = $classname::get_content_value($content);
}
// Limited to 4 fields as a document only has 4 content fields.
if (count($indexfields) > 4) {
$indexfields[3] = implode(' ', array_slice($indexfields, 3));
}
return $indexfields;
}
/**
* Returns the class name for the given field type and includes it.
*
* @param string $fieldtype
* @return string|null It will return the class name or null if the field type is not available.
*/
protected function get_field_class_name(string $fieldtype): ?string {
global $CFG;
$fieldtype = trim($fieldtype);
$fieldpath = $CFG->dirroot . '/mod/data/field/' . $fieldtype . '/field.class.php';
if (!file_exists($fieldpath)) {
return null;
}
require_once($fieldpath);
return 'data_field_' . $fieldtype;
}
/**
* Confirms that data entries support group restrictions.
*
* @return bool True
*/
public function supports_group_restriction() {
return true;
}
}
@@ -0,0 +1,80 @@
<?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/>.
/**
* Priority Queue class to sort out db entry contents.
*
* @package mod_data
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\search;
defined('MOODLE_INTERNAL') || die();
/**
* Priority Queue class to sort out db entry contents.
*
* @package mod_data
* @copyright 2016 Devang Gaur
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sortedcontentqueue extends \SPLPriorityQueue {
/**
* @var array All contents that will be sorted.
*/
private $contents;
/**
* contructor
*
* @param array $contents
* @return void
*/
public function __construct($contents) {
$this->contents = $contents;
}
/**
* comparator function overriden for sorting the records
* ...as per 'required' and 'priotirity' field values
*
* @param int $key1
* @param int $key2
* @return int
*/
public function compare($key1 , $key2): int {
$record1 = $this->contents[$key1];
$record2 = $this->contents[$key2];
// If a content's fieldtype is compulsory in the database than it would have priority than any other noncompulsory content.
if ( ($record1->required && $record2->required) || (!$record1->required && !$record2->required)) {
if ($record1->priority === $record2->priority) {
return $key1 < $key2 ? 1 : -1;
}
return $record1->priority < $record2->priority ? -1 : 1;
} else if ($record1->required && !$record2->required) {
return 1;
} else {
return -1;
}
}
}
File diff suppressed because it is too large Load Diff
+69
View File
@@ -0,0 +1,69 @@
<?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/>.
/**
* This file is part of the Database module for Moodle
*
* @copyright 2005 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package mod_data
*/
use mod_data\manager;
use mod_data\preset;
define('NO_MOODLE_COOKIES', true); // Session not used here.
require_once('../../config.php');
$id = optional_param('id', 0, PARAM_INT); // Course module id.
$d = optional_param('d', 0, PARAM_INT); // Database id.
$presetfullname = optional_param('preset', '', PARAM_PATH); // The directory the preset is in.
$lifetime = 600; // Seconds to cache this stylesheet.
$url = new moodle_url('/mod/data/css.php');
$manager = null;
if ($id) {
list($course, $cm) = get_course_and_cm_from_cmid($id, manager::MODULE);
$manager = manager::create_from_coursemodule($cm);
$instance = $manager->get_instance();
} else {
// We must have the database activity id.
$d = required_param('d', PARAM_INT);
$instance = $DB->get_record('data', ['id' => $d], '*', MUST_EXIST);
$manager = manager::create_from_instance($instance);
}
$url->param('d', $instance->id);
// Get the content.
if ($presetfullname) {
$url->param('preset', $presetfullname);
$preset = preset::create_from_fullname($manager, $presetfullname);
$content = $preset->get_template_content('csstemplate');
$lifetime = 60; // Preset preview does not need a long cache.
} else {
$content = $instance->csstemplate;
}
$PAGE->set_url($url);
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
header('Cache-control: max_age = '. $lifetime);
header('Pragma: ');
header('Content-type: text/css; charset=utf-8'); // Correct MIME type.
echo $content;
+65
View File
@@ -0,0 +1,65 @@
/**
* Javascript to insert the field tags into the textarea.
* Used when editing a data template
*/
function insert_field_tags(selectlist) {
var value = selectlist.options[selectlist.selectedIndex].value;
var editorname = 'template';
if (typeof tinyMCE == 'undefined') {
if (document.execCommand('insertText')) {
document.execCommand('insertText', false, value);
} else {
var element = document.getElementsByName(editorname)[0];
// For inserting when in normal textareas
insertAtCursor(element, value);
}
} else {
tinyMCE.execInstanceCommand(editorname, 'mceInsertContent', false, value);
}
}
/**
* javascript for hiding/displaying advanced search form when viewing
*/
function showHideAdvSearch(checked) {
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
if(divs[i].id.match('data_adv_form')) {
if(checked) {
divs[i].style.display = 'inline';
} else {
divs[i].style.display = 'none';
}
} else if (divs[i].id.match('reg_search')) {
if (!checked) {
divs[i].style.display = 'inline';
} else {
divs[i].style.display = 'none';
}
} else if (divs[i].id.match('advsearch-save-sec')) {
if (!checked) {
divs[i].style.display = 'inline';
} else {
divs[i].style.display = 'none';
}
}
}
}
M.data_urlpicker = {};
M.data_urlpicker.init = function(Y, options) {
options.formcallback = M.data_urlpicker.callback;
if (!M.core_filepicker.instances[options.client_id]) {
M.core_filepicker.init(Y, options);
}
Y.on('click', function(e, client_id) {
e.preventDefault();
M.core_filepicker.instances[client_id].show();
}, '#filepicker-button-'+options.client_id, null, options.client_id);
};
M.data_urlpicker.callback = function (params) {
document.getElementById('field_url_'+params.client_id).value = params.url;
};
+268
View File
@@ -0,0 +1,268 @@
<?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/>.
/**
* Plugin capabilities
*
* @package mod_data
* @copyright 2006 Martin Dougiamas
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'mod/data:addinstance' => array(
'riskbitmask' => RISK_XSS,
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/course:manageactivities'
),
'mod/data:viewentry' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'frontpage' => CAP_ALLOW, // needed for databases on the frontpage
'guest' => CAP_ALLOW,
'student' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:writeentry' => array(
'riskbitmask' => RISK_SPAM,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'student' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:comment' => array(
'riskbitmask' => RISK_SPAM,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'student' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:rate' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:viewrating' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:viewanyrating' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
),
'clonepermissionsfrom' => 'mod/data:viewrating'
),
'mod/data:viewallratings' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
),
'clonepermissionsfrom' => 'mod/data:viewrating'
),
'mod/data:approve' => array(
'riskbitmask' => RISK_SPAM,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:manageentries' => array(
'riskbitmask' => RISK_SPAM,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:managecomments' => array(
'riskbitmask' => RISK_SPAM,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:managetemplates' => array(
'riskbitmask' => RISK_SPAM | RISK_XSS,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:viewalluserpresets' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/data:manageuserpresets' => array(
'riskbitmask' => RISK_SPAM | RISK_XSS,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'manager' => CAP_ALLOW
)
),
'mod/data:exportentry' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'manager' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
)
),
'mod/data:exportownentry' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'manager' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'student' => CAP_ALLOW,
)
),
'mod/data:exportallentries' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'manager' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
)
),
'mod/data:exportuserinfo' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'manager' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
)
),
'mod/data:view' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'guest' => CAP_ALLOW,
'student' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
)
);
+115
View File
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/data/db" VERSION="20220530" COMMENT="XMLDB file for Moodle mod/data"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="data" COMMENT="all database activities">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="intro" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="comments" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timeavailablefrom" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timeavailableto" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timeviewfrom" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timeviewto" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="requiredentries" TYPE="int" LENGTH="8" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="requiredentriestoview" TYPE="int" LENGTH="8" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="maxentries" TYPE="int" LENGTH="8" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="rssarticles" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="singletemplate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="listtemplate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="listtemplateheader" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="listtemplatefooter" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="addtemplate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="rsstemplate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="rsstitletemplate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="csstemplate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="jstemplate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="asearchtemplate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="approval" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="manageapproved" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="scale" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="assessed" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="assesstimestart" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="assesstimefinish" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="defaultsort" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="defaultsortdir" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="editany" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="notification" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Notify people when things change"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The time the settings for this database module instance were last modified."/>
<FIELD NAME="config" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="completionentries" TYPE="int" LENGTH="10" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Number of entries required for completion"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
</INDEXES>
</TABLE>
<TABLE NAME="data_fields" COMMENT="every field available">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="dataid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="type" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="description" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="required" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Required fields must have a value when inserted by a user"/>
<FIELD NAME="param1" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param2" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param3" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param4" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param5" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param6" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param7" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param8" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param9" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="param10" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="dataid" TYPE="foreign" FIELDS="dataid" REFTABLE="data" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="type-dataid" UNIQUE="false" FIELDS="type, dataid" COMMENT="used by data linking filter"/>
</INDEXES>
</TABLE>
<TABLE NAME="data_records" COMMENT="every record introduced">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="dataid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="approved" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="dataid" TYPE="foreign" FIELDS="dataid" REFTABLE="data" REFFIELDS="id"/>
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="data_content" COMMENT="the content introduced in each record/fields">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="fieldid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="recordid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="content" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="content1" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="content2" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="content3" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="content4" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="recordid" TYPE="foreign" FIELDS="recordid" REFTABLE="data_records" REFFIELDS="id"/>
<KEY NAME="fieldid" TYPE="foreign" FIELDS="fieldid" REFTABLE="data_fields" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
+38
View File
@@ -0,0 +1,38 @@
<?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/>.
/**
* Definition of log events
*
* @package mod_data
* @category log
* @copyright 2010 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$logs = array(
array('module'=>'data', 'action'=>'view', 'mtable'=>'data', 'field'=>'name'),
array('module'=>'data', 'action'=>'add', 'mtable'=>'data', 'field'=>'name'),
array('module'=>'data', 'action'=>'update', 'mtable'=>'data', 'field'=>'name'),
array('module'=>'data', 'action'=>'record delete', 'mtable'=>'data', 'field'=>'name'),
array('module'=>'data', 'action'=>'fields add', 'mtable'=>'data_fields', 'field'=>'name'),
array('module'=>'data', 'action'=>'fields update', 'mtable'=>'data_fields', 'field'=>'name'),
array('module'=>'data', 'action'=>'templates saved', 'mtable'=>'data', 'field'=>'name'),
array('module'=>'data', 'action'=>'templates def', 'mtable'=>'data', 'field'=>'name'),
);
+132
View File
@@ -0,0 +1,132 @@
<?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/>.
/**
* Database external functions and service definitions.
*
* @package mod_data
* @category external
* @copyright 2015 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 2.9
*/
$functions = array(
'mod_data_get_databases_by_courses' => array(
'classname' => 'mod_data_external',
'methodname' => 'get_databases_by_courses',
'description' => 'Returns a list of database instances in a provided set of courses, if
no courses are provided then all the database instances the user has access to will be returned.',
'type' => 'read',
'capabilities' => 'mod/data:viewentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_view_database' => array(
'classname' => 'mod_data_external',
'methodname' => 'view_database',
'description' => 'Simulate the view.php web interface data: trigger events, completion, etc...',
'type' => 'write',
'capabilities' => 'mod/data:viewentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_get_data_access_information' => array(
'classname' => 'mod_data_external',
'methodname' => 'get_data_access_information',
'description' => 'Return access information for a given database.',
'type' => 'read',
'capabilities' => 'mod/data:viewentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_get_entries' => array(
'classname' => 'mod_data_external',
'methodname' => 'get_entries',
'description' => 'Return the complete list of entries of the given database.',
'type' => 'read',
'capabilities' => 'mod/data:viewentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_get_entry' => array(
'classname' => 'mod_data_external',
'methodname' => 'get_entry',
'description' => 'Return one entry record from the database, including contents optionally.',
'type' => 'read',
'capabilities' => 'mod/data:viewentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_get_fields' => array(
'classname' => 'mod_data_external',
'methodname' => 'get_fields',
'description' => 'Return the list of configured fields for the given database.',
'type' => 'read',
'capabilities' => 'mod/data:viewentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_search_entries' => array(
'classname' => 'mod_data_external',
'methodname' => 'search_entries',
'description' => 'Search for entries in the given database.',
'type' => 'read',
'capabilities' => 'mod/data:viewentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_approve_entry' => array(
'classname' => 'mod_data_external',
'methodname' => 'approve_entry',
'description' => 'Approves or unapproves an entry.',
'type' => 'write',
'capabilities' => 'mod/data:approve',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_delete_entry' => array(
'classname' => 'mod_data_external',
'methodname' => 'delete_entry',
'description' => 'Deletes an entry.',
'type' => 'write',
'capabilities' => 'mod/data:manageentries',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_add_entry' => array(
'classname' => 'mod_data_external',
'methodname' => 'add_entry',
'description' => 'Adds a new entry.',
'type' => 'write',
'capabilities' => 'mod/data:writeentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_update_entry' => array(
'classname' => 'mod_data_external',
'methodname' => 'update_entry',
'description' => 'Updates an existing entry.',
'type' => 'write',
'capabilities' => 'mod/data:writeentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_delete_saved_preset' => array(
'classname' => 'mod_data\external\delete_saved_preset',
'description' => 'Delete site user preset.',
'type' => 'write',
'ajax' => true,
'capabilities' => 'mod/data:manageuserpresets',
),
'mod_data_get_mapping_information' => array(
'classname' => 'mod_data\external\get_mapping_information',
'description' => 'Get importing information',
'type' => 'read',
'ajax' => true,
'capabilities' => 'mod/data:managetemplates',
),
);
+6
View File
@@ -0,0 +1,6 @@
{
"plugintypes": {
"datafield": "mod\/data\/field",
"datapreset": "mod\/data\/preset"
}
}
+34
View File
@@ -0,0 +1,34 @@
<?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/>.
/**
* Tag areas in component mod_data
*
* @package mod_data
* @copyright 2017 Andrew Hancox <andrewdchancox@googlemail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$tagareas = array(
array(
'itemtype' => 'data_records',
'component' => 'mod_data',
'callback' => 'mod_data_get_tagged_records',
'callbackfile' => '/mod/data/locallib.php',
),
);
+74
View File
@@ -0,0 +1,74 @@
<?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/>.
/**
* This file keeps track of upgrades to the data module
*
* Sometimes, changes between versions involve
* alterations to database structures and other
* major things that may break installations.
*
* The upgrade function in this file will attempt
* to perform all the necessary actions to upgrade
* your older installation to the current version.
*
* If there's something it cannot do itself, it
* will tell you what you need to do.
*
* The commands in here will all be database-neutral,
* using the methods of database_manager class
*
* Please do not forget to use upgrade_set_timeout()
* before any action that may take longer time to finish.
*
* @package mod_data
* @copyright 2006 Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function xmldb_data_upgrade($oldversion) {
global $DB;
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
if ($oldversion < 2023061300) {
// Clean orphan data_records.
$sql = "SELECT d.id FROM {data} d
LEFT JOIN {data_fields} f ON d.id = f.dataid
WHERE f.id IS NULL";
$emptydatas = $DB->get_records_sql($sql);
if (!empty($emptydatas)) {
$dataids = array_keys($emptydatas);
[$datainsql, $dataparams] = $DB->get_in_or_equal($dataids, SQL_PARAMS_NAMED, 'data');
$DB->delete_records_select('data_records', "dataid $datainsql", $dataparams);
}
// Data savepoint reached.
upgrade_mod_savepoint(true, 2023061300, 'data');
}
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+264
View File
@@ -0,0 +1,264 @@
<?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/>.
/**
* List of deprecated mod_data functions.
*
* @package mod_data
* @copyright 2021 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @deprecated since Moodle 3.11
*/
function data_get_completion_state() {
$completionclass = \mod_data\completion\custom_completion::class;
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
}
/**
* @deprecated since Moodle 4.3.
* @global object
* @param array $export
* @param string $dataname
* @param int $count
* @return string
*/
function data_export_xls($export, $dataname, $count) {
global $CFG;
debugging('Function data_export_xls() has been deprecated, because xls export has been dropped.',
DEBUG_DEVELOPER);
require_once("$CFG->libdir/excellib.class.php");
$filename = clean_filename("{$dataname}-{$count}_record");
if ($count > 1) {
$filename .= 's';
}
$filename .= clean_filename('-' . gmdate("Ymd_Hi"));
$filename .= '.xls';
$filearg = '-';
$workbook = new MoodleExcelWorkbook($filearg);
$workbook->send($filename);
$worksheet = array();
$worksheet[0] = $workbook->add_worksheet('');
$rowno = 0;
foreach ($export as $row) {
$colno = 0;
foreach($row as $col) {
$worksheet[0]->write($rowno, $colno, $col);
$colno++;
}
$rowno++;
}
$workbook->close();
return $filename;
}
/**
* @deprecated since Moodle 4.3, exporting is now being done by \mod_data\local\exporter\csv_entries_exporter
* @global object
* @param array $export
* @param string $delimiter_name
* @param object $database
* @param int $count
* @param bool $return
* @return string|void
*/
function data_export_csv($export, $delimiter_name, $database, $count, $return=false) {
global $CFG;
debugging('Function data_export_csv has been deprecated. Exporting is now being done by '
. '\mod_data\local\csv_exporter.', DEBUG_DEVELOPER);
require_once($CFG->libdir . '/csvlib.class.php');
$filename = $database . '-' . $count . '-record';
if ($count > 1) {
$filename .= 's';
}
if ($return) {
return csv_export_writer::print_array($export, $delimiter_name, '"', true);
} else {
csv_export_writer::download_array($filename, $export, $delimiter_name);
}
}
/**
* @deprecated since Moodle 4.3, exporting is now being done by \mod_data\local\exporter\ods_entries_exporter
* @global object
* @param array $export
* @param string $dataname
* @param int $count
* @param string
*/
function data_export_ods($export, $dataname, $count) {
global $CFG;
debugging('Function data_export_ods has been deprecated. Exporting is now being done by '
. '\mod_data\local\ods_exporter.', DEBUG_DEVELOPER);
require_once("$CFG->libdir/odslib.class.php");
$filename = clean_filename("{$dataname}-{$count}_record");
if ($count > 1) {
$filename .= 's';
}
$filename .= clean_filename('-' . gmdate("Ymd_Hi"));
$filename .= '.ods';
$filearg = '-';
$workbook = new MoodleODSWorkbook($filearg);
$workbook->send($filename);
$worksheet = array();
$worksheet[0] = $workbook->add_worksheet('');
$rowno = 0;
foreach ($export as $row) {
$colno = 0;
foreach($row as $col) {
$worksheet[0]->write($rowno, $colno, $col);
$colno++;
}
$rowno++;
}
$workbook->close();
return $filename;
}
/**
* @deprecated since Moodle 4.3, use \mod_data\local\exporter\utils::data_exportdata with a \mod_data\local\exporter\entries_exporter object
* @global object
* @param int $dataid
* @param array $fields
* @param array $selectedfields
* @param int $currentgroup group ID of the current group. This is used for
* exporting data while maintaining group divisions.
* @param object $context the context in which the operation is performed (for capability checks)
* @param bool $userdetails whether to include the details of the record author
* @param bool $time whether to include time created/modified
* @param bool $approval whether to include approval status
* @param bool $tags whether to include tags
* @return array
*/
function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0, $context=null,
$userdetails=false, $time=false, $approval=false, $tags = false) {
global $DB;
debugging('Function data_get_exportdata has been deprecated. Use '
. '\mod_data\local\exporter_utils::data_exportdata with a \mod_data\local\exporter object instead',
DEBUG_DEVELOPER);
if (is_null($context)) {
$context = context_system::instance();
}
// exporting user data needs special permission
$userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context);
$exportdata = array();
// populate the header in first row of export
foreach($fields as $key => $field) {
if (!in_array($field->field->id, $selectedfields)) {
// ignore values we aren't exporting
unset($fields[$key]);
} else {
$exportdata[0][] = $field->field->name;
}
}
if ($tags) {
$exportdata[0][] = get_string('tags', 'data');
}
if ($userdetails) {
$exportdata[0][] = get_string('user');
$exportdata[0][] = get_string('username');
$exportdata[0][] = get_string('email');
}
if ($time) {
$exportdata[0][] = get_string('timeadded', 'data');
$exportdata[0][] = get_string('timemodified', 'data');
}
if ($approval) {
$exportdata[0][] = get_string('approved', 'data');
}
$datarecords = $DB->get_records('data_records', array('dataid'=>$dataid));
ksort($datarecords);
$line = 1;
foreach($datarecords as $record) {
// get content indexed by fieldid
if ($currentgroup) {
$select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, {data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?';
$where = array($record->id, $currentgroup);
} else {
$select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?';
$where = array($record->id);
}
if( $content = $DB->get_records_sql($select, $where) ) {
foreach($fields as $field) {
$contents = '';
if(isset($content[$field->field->id])) {
$contents = $field->export_text_value($content[$field->field->id]);
}
$exportdata[$line][] = $contents;
}
if ($tags) {
$itemtags = \core_tag_tag::get_item_tags_array('mod_data', 'data_records', $record->id);
$exportdata[$line][] = implode(', ', $itemtags);
}
if ($userdetails) { // Add user details to the export data
$userdata = get_complete_user_data('id', $record->userid);
$exportdata[$line][] = fullname($userdata);
$exportdata[$line][] = $userdata->username;
$exportdata[$line][] = $userdata->email;
}
if ($time) { // Add time added / modified
$exportdata[$line][] = userdate($record->timecreated);
$exportdata[$line][] = userdate($record->timemodified);
}
if ($approval) { // Add approval status
$exportdata[$line][] = (int) $record->approved;
}
}
$line++;
}
$line--;
return $exportdata;
}
/**
* @deprecated since Moodle 4.3, importing is now being done by \mod_data\local\importer\csv_importer::import_csv
* Import records for a data instance from csv data.
*
* @param object $cm Course module of the data instance.
* @param object $data The data instance.
* @param string $csvdata The csv data to be imported.
* @param string $encoding The encoding of csv data.
* @param string $fielddelimiter The delimiter of the csv data.
* @return int Number of records added.
*/
function data_import_csv($cm, $data, &$csvdata, $encoding, $fielddelimiter) {
debugging('Function data_import_csv has been deprecated. '
. 'Importing is now being done by \mod_data\local\csv_importer::import_csv.',
DEBUG_DEVELOPER);
// New function needs a file, not the file content, so we have to temporarily put the content into a file.
$tmpdir = make_request_directory();
$tmpfilename = 'tmpfile.csv';
$tmpfilepath = $tmpdir . '/tmpfile.csv';
file_put_contents($tmpfilepath, $csvdata);
$importer = new \mod_data\local\importer\csv_entries_importer($tmpfilepath, $tmpfilename);
$importer->import_csv($cm, $data, $encoding, $fielddelimiter);
return 0;
}
+242
View File
@@ -0,0 +1,242 @@
<?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/>.
/**
* This file is part of the Database module for Moodle
*
* @copyright 2005 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package mod_data
*/
use mod_data\manager;
require_once('../../config.php');
require_once('locallib.php');
require_once("$CFG->libdir/rsslib.php");
require_once("$CFG->libdir/form/filemanager.php");
$id = optional_param('id', 0, PARAM_INT); // Course module id.
$d = optional_param('d', 0, PARAM_INT); // Database id.
$rid = optional_param('rid', 0, PARAM_INT); // Record id.
$mode = 'addtemplate'; // Define the mode for this page, only 1 mode available.
$tags = optional_param_array('tags', [], PARAM_TAGLIST);
$redirectbackto = optional_param('backto', '', PARAM_LOCALURL); // The location to redirect back.
$url = new moodle_url('/mod/data/edit.php');
$record = null;
if ($id) {
list($course, $cm) = get_course_and_cm_from_cmid($id, manager::MODULE);
$manager = manager::create_from_coursemodule($cm);
} else { // We must have $d.
$data = $DB->get_record('data', ['id' => $d], '*', MUST_EXIST);
$manager = manager::create_from_instance($data);
$cm = $manager->get_coursemodule();
$course = get_course($cm->course);
}
$data = $manager->get_instance();
$context = $manager->get_context();
$url->param('id', $cm->id);
if ($rid !== 0) {
$record = $DB->get_record(
'data_records',
['id' => $rid, 'dataid' => $data->id],
'*',
MUST_EXIST
);
$url->param('rid', $rid);
}
$PAGE->set_url($url);
require_login($course, false, $cm);
$url->param('backto', $redirectbackto);
if (isguestuser()) {
redirect('view.php?d='.$data->id);
}
/// Can't use this if there are no fields
if ($manager->can_manage_templates()) {
if (!$manager->has_fields()) {
redirect($CFG->wwwroot.'/mod/data/field.php?d='.$data->id); // Redirect to field entry.
}
}
if ($rid) {
// When editing an existing record, we require the session key.
require_sesskey();
}
// Get Group information for permission testing and record creation.
$currentgroup = groups_get_activity_group($cm);
$groupmode = groups_get_activity_groupmode($cm);
if (!has_capability('mod/data:manageentries', $context)) {
if ($rid) {
// User is editing an existing record.
if (!data_user_can_manage_entry($record, $data, $context)) {
throw new \moodle_exception('noaccess', 'data');
}
} else if (!data_user_can_add_entry($data, $currentgroup, $groupmode, $context)) {
// User is trying to create a new record.
throw new \moodle_exception('noaccess', 'data');
}
}
// RSS and CSS and JS meta.
if (!empty($CFG->enablerssfeeds) && !empty($CFG->data_enablerssfeeds) && $data->rssarticles > 0) {
$courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
$rsstitle = $courseshortname . ': ' . format_string($data->name);
rss_add_http_header($context, 'mod_data', $data, $rsstitle);
}
if ($data->csstemplate) {
$PAGE->requires->css('/mod/data/css.php?d='.$data->id);
}
if ($data->jstemplate) {
$PAGE->requires->js('/mod/data/js.php?d='.$data->id, true);
}
// Define page variables.
$strdata = get_string('modulenameplural','data');
if ($rid) {
$PAGE->navbar->add(get_string('editentry', 'data'));
}
$PAGE->add_body_class('mediumwidth');
if ($rid) {
$pagename = get_string('editentry', 'data');
} else {
$pagename = get_string('newentry', 'data');
}
$PAGE->navbar->add($pagename);
$titleparts = [
$pagename,
format_string($data->name),
format_string($course->fullname),
];
$PAGE->set_title(implode(moodle_page::TITLE_SEPARATOR, $titleparts));
$PAGE->force_settings_menu(true);
$PAGE->set_secondary_active_tab('modulepage');
$PAGE->activityheader->disable();
// Process incoming data for adding/updating records.
// Keep track of any notifications ad submitted data.
$processeddata = null;
$datarecord = data_submitted() ?: null;
// Process the submitted form.
if ($datarecord && confirm_sesskey()) {
// Validate the form to ensure that enough data was submitted.
$fields = $manager->get_field_records();
$processeddata = data_process_submission($data, $fields, $datarecord);
if ($processeddata->validated) {
if ($rid) {
$recordid = $rid;
// Updating an existing record.
data_update_record_fields_contents($data, $record, $context, $datarecord, $processeddata);
} else {
// Add instance to data_record.
$recordid = data_add_record($data, $currentgroup);
if ($recordid) {
// Now populate the fields contents of the new record.
data_add_fields_contents_to_new_record($data, $context, $recordid, $fields, $datarecord, $processeddata);
}
}
if ($recordid) {
core_tag_tag::set_item_tags('mod_data', 'data_records', $recordid, $context, $tags);
if (!empty($datarecord->saveandadd)) {
// User has clicked "Save and add another". Reset all of the fields.
$datarecord = null;
} else {
$viewurl = new moodle_url('/mod/data/view.php', [
'd' => $data->id,
'rid' => $recordid,
]);
redirect($viewurl);
}
}
}
}
// End of form processing.
echo $OUTPUT->header();
groups_print_activity_menu($cm, $CFG->wwwroot.'/mod/data/edit.php?d='.$data->id);
// Form goes here first in case add template is empty.
echo '<form enctype="multipart/form-data" action="edit.php" method="post">';
echo '<div>';
echo '<input name="d" value="'.$data->id.'" type="hidden" />';
echo '<input name="rid" value="'.$rid.'" type="hidden" />';
echo '<input name="sesskey" value="'.sesskey().'" type="hidden" />';
echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
echo $OUTPUT->heading($pagename);
$template = $manager->get_template($mode);
echo $template->parse_add_entry($processeddata, $rid, $datarecord);
if (empty($redirectbackto)) {
$redirectbackto = new \moodle_url('/mod/data/view.php', ['id' => $cm->id]);
}
$actionbuttons = html_writer::link(
$redirectbackto,
get_string('cancel'),
['class' => 'btn btn-secondary mx-1', 'role' => 'button']
);
$actionbuttons .= html_writer::empty_tag('input', [
'type' => 'submit',
'name' => 'saveandview',
'value' => get_string('save'),
'class' => 'btn btn-primary mx-1'
]);
if (!$rid && ((!$data->maxentries) ||
has_capability('mod/data:manageentries', $context) ||
(data_numentries($data) < ($data->maxentries - 1)))) {
$actionbuttons .= html_writer::empty_tag('input', [
'type' => 'submit', 'name' => 'saveandadd',
'value' => get_string('saveandadd', 'data'), 'class' => 'btn btn-primary mx-1'
]);
}
$stickyfooter = new core\output\sticky_footer($actionbuttons);
echo $OUTPUT->render($stickyfooter);
echo $OUTPUT->box_end();
echo '</div></form>';
$possiblefields = $manager->get_fields();
foreach ($possiblefields as $field) {
$field->print_after_form();
}
// Finish the page.
if (empty($possiblefields)) {
throw new \moodle_exception('nofieldindatabase', 'data');
}
echo $OUTPUT->footer();

Some files were not shown because too many files have changed in this diff Show More