diff --git a/actions/notification/amd/build/chaptersource.min.js b/actions/notification/amd/build/chaptersource.min.js deleted file mode 100644 index 685335a..0000000 --- a/actions/notification/amd/build/chaptersource.min.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Frameworks datasource. - * - * This module is compatible with core/form-autocomplete. - * - * @module tool_lp/frameworks_datasource - * @copyright 2016 Frédéric Massart - FMCorz.net - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -define("pulseaction_notification/chaptersource",["jquery","core/ajax","core/notification","core/modal_factory","core/fragment","core/str","core/modal_events"],(function($,Ajax,Notification,ModalFactory,Fragment,Str,ModalEvents){const previewModalBody=function(contextID){let userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;var params;params=void 0!==window.tinyMCE&&window.tinyMCE.get("id_pulsenotification_headercontent_editor")?{contentheader:window.tinyMCE.get("id_pulsenotification_headercontent_editor").getContent(),contentstatic:window.tinyMCE.get("id_pulsenotification_staticcontent_editor").getContent(),contentfooter:window.tinyMCE.get("id_pulsenotification_footercontent_editor").getContent(),userid:userid}:{contentheader:document.querySelector("#id_pulsenotification_headercontent_editoreditable").innerHTML,contentstatic:document.querySelector("#id_pulsenotification_staticcontent_editoreditable").innerHTML,contentfooter:document.querySelector("#id_pulsenotification_footercontent_editoreditable").innerHTML,userid:userid};var formData,dynamicparams={};null!==document.querySelector("[name=pulsenotification_dynamiccontent]")&&(dynamicparams={contentdynamic:document.querySelector("[name=pulsenotification_dynamiccontent]").value,contenttype:document.querySelector("[name=pulsenotification_contenttype]").value,chapterid:document.querySelector("[name=pulsenotification_chapterid]").value,contentlength:document.querySelector("[name=pulsenotification_contentlength]").value});var form=document.forms["pulse-automation-template"],formdata=new FormData(form);formData={formdata:formdata=new URLSearchParams(formdata).toString()};var finalParams={...params,...dynamicparams,...formData};return Fragment.loadFragment("pulseaction_notification","preview_content",contextID,finalParams)};return{processResults:function(selector,modules){return modules},transport:function(selector,query,success,failure){var mod=document.querySelector("#id_pulsenotification_dynamiccontent");Ajax.call([{methodname:"pulseaction_notification_get_chapters",args:{mod:mod.value}}])[0].then((function(result){success(result)})).fail(failure)},updateChapter:function(ctxID,contentMods){const SELECTORS_chaperType="#id_pulsenotification_contenttype",SELECTORS_mod="#id_pulsenotification_dynamiccontent";if(null!==contentMods){var type=document.querySelector(SELECTORS_chaperType);document.querySelector(SELECTORS_mod).addEventListener("change",(e=>{var selected=e.currentTarget.value;contentMods.includes(selected.toString())?Array.prototype.find.call(type.options,(function(cmid){"2"==cmid.value&&(cmid.disabled=!1)})):Array.prototype.find.call(type.options,(function(cmid){"2"==cmid.value&&(cmid.disabled=!0)}))}))}document.querySelector(SELECTORS_chaperType).addEventListener("change",(()=>resetChapter())),document.querySelector(SELECTORS_mod).addEventListener("change",(()=>resetChapter()));var chapter=document.querySelector("#id_pulsenotification_chapterid");function resetChapter(){chapter.innerHTML="",chapter.value="";var event=new Event("change");chapter.dispatchEvent(event)}},previewNotification:function(contextid){var btn=document.querySelector('[name="pulsenotification_preview"]');null!==btn&&btn.addEventListener("click",(function(){var contextID;contextID=contextid,ModalFactory.create({title:Str.get_string("preview","pulseaction_notification"),body:previewModalBody(contextID),large:!0}).then((modal=>{modal.show(),modal.getRoot().on(ModalEvents.bodyRendered,(function(){modal.getRoot().get(0).querySelector("[name=userselector]").addEventListener("change",(e=>{e.preventDefault();var target=e.target;modal.setBody(previewModalBody(contextID,target.value))}))}))})).catch()}))},reportModal:function(contextID){document.addEventListener("click",(function(e){if(null!==e.target.closest('[data-target="view-content"]')){var target=e.target.closest("a"),instance=target.dataset.instanceid,userid=target.dataset.userid;!function(contextID,instance,userid){var params={instanceid:instance,userid:userid};ModalFactory.create({title:Str.get_string("preview","pulseaction_notification"),body:Fragment.loadFragment("pulseaction_notification","preview_instance_content",contextID,params),large:!0}).then((modal=>{modal.show()})).catch()}(contextID,instance,userid)}}))}}})); - -//# sourceMappingURL=chaptersource.min.js.map \ No newline at end of file diff --git a/actions/notification/amd/build/chaptersource.min.js.map b/actions/notification/amd/build/chaptersource.min.js.map deleted file mode 100644 index 1b95448..0000000 --- a/actions/notification/amd/build/chaptersource.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"chaptersource.min.js","sources":["../src/chaptersource.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Frameworks datasource.\n *\n * This module is compatible with core/form-autocomplete.\n *\n * @module tool_lp/frameworks_datasource\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/notification', 'core/modal_factory', 'core/fragment', 'core/str', 'core/modal_events'],\n function ($, Ajax, Notification, ModalFactory, Fragment, Str, ModalEvents) {\n\n const previewModalBody = function (contextID, userid = null) {\n\n var params;\n if (window.tinyMCE !== undefined && window.tinyMCE.get('id_pulsenotification_headercontent_editor')) {\n // EditorPlugin = window.tinyMCE;\n params = {\n contentheader: window.tinyMCE.get('id_pulsenotification_headercontent_editor').getContent(),\n contentstatic: window.tinyMCE.get('id_pulsenotification_staticcontent_editor').getContent(),\n contentfooter: window.tinyMCE.get('id_pulsenotification_footercontent_editor').getContent(),\n userid: userid\n };\n } else {\n // EditorPlugin = document;\n params = {\n contentheader: document.querySelector('#id_pulsenotification_headercontent_editoreditable').innerHTML,\n contentstatic: document.querySelector('#id_pulsenotification_staticcontent_editoreditable').innerHTML,\n contentfooter: document.querySelector('#id_pulsenotification_footercontent_editoreditable').innerHTML,\n userid: userid\n };\n }\n\n var dynamicparams = {};\n\n if (document.querySelector('[name=pulsenotification_dynamiccontent]') !== null) {\n dynamicparams = {\n contentdynamic: document.querySelector('[name=pulsenotification_dynamiccontent]').value,\n contenttype: document.querySelector('[name=pulsenotification_contenttype]').value,\n chapterid: document.querySelector('[name=pulsenotification_chapterid]').value,\n contentlength: document.querySelector('[name=pulsenotification_contentlength]').value,\n };\n }\n // Get the form data.\n var formData;\n var form = document.forms['pulse-automation-template'];\n var formdata = new FormData(form);\n formdata = new URLSearchParams(formdata).toString();\n formData = {\n formdata: formdata\n };\n\n var finalParams = { ...params, ...dynamicparams, ...formData };\n\n return Fragment.loadFragment('pulseaction_notification', 'preview_content', contextID, finalParams);\n };\n\n const previewModal = function (contextID) {\n\n ModalFactory.create({\n title: Str.get_string('preview', 'pulseaction_notification'),\n body: previewModalBody(contextID),\n large: true,\n }).then((modal) => {\n modal.show();\n\n modal.getRoot().on(ModalEvents.bodyRendered, function () {\n modal.getRoot().get(0).querySelector('[name=userselector]').addEventListener('change', (e) => {\n e.preventDefault();\n var target = e.target;\n modal.setBody(previewModalBody(contextID, target.value));\n });\n });\n\n return;\n }).catch();\n };\n\n const notificationModal = function (contextID, instance, userid) {\n\n var params = {\n instanceid: instance,\n userid: userid\n };\n\n ModalFactory.create({\n title: Str.get_string('preview', 'pulseaction_notification'),\n body: Fragment.loadFragment('pulseaction_notification', 'preview_instance_content', contextID, params),\n large: true,\n }).then((modal) => {\n modal.show();\n return;\n }).catch();\n };\n\n return {\n\n processResults: function (selector, modules) {\n return modules;\n },\n\n transport: function (selector, query, success, failure) {\n\n var mod = document.querySelector(\"#id_pulsenotification_dynamiccontent\");\n\n var promise = Ajax.call([{\n methodname: 'pulseaction_notification_get_chapters',\n args: { mod: mod.value }\n }]);\n\n promise[0].then(function (result) {\n success(result);\n return;\n }).fail(failure);\n },\n\n updateChapter: function (ctxID, contentMods) {\n\n const SELECTORS = {\n chaperType: \"#id_pulsenotification_contenttype\",\n mod: \"#id_pulsenotification_dynamiccontent\"\n };\n\n // Disable the content type option for modules other than book and page.\n if (contentMods !== null) {\n var type = document.querySelector(SELECTORS.chaperType);\n document.querySelector(SELECTORS.mod).addEventListener(\"change\", (e) => {\n var target = e.currentTarget;\n var selected = target.value;\n if (contentMods.includes(selected.toString())) {\n Array.prototype.find.call(type.options, function (cmid) {\n if (cmid.value == '2') {\n cmid.disabled = false;\n }\n });\n } else {\n Array.prototype.find.call(type.options, function (cmid) {\n if (cmid.value == '2') {\n cmid.disabled = true;\n }\n });\n }\n });\n }\n\n document.querySelector(SELECTORS.chaperType).addEventListener(\"change\", () => resetChapter());\n document.querySelector(SELECTORS.mod).addEventListener(\"change\", () => resetChapter());\n var chapter = document.querySelector(\"#id_pulsenotification_chapterid\");\n\n /**\n *\n */\n function resetChapter() {\n chapter.innerHTML = '';\n chapter.value = '';\n var event = new Event('change');\n chapter.dispatchEvent(event);\n }\n },\n\n previewNotification: function (contextid) {\n\n var btn = document.querySelector('[name=\"pulsenotification_preview\"]');\n\n if (btn === null) {\n return;\n }\n\n btn.addEventListener('click', function () {\n previewModal(contextid);\n });\n },\n\n reportModal: function (contextID) {\n // View content.\n /* var btn = document.querySelectorAll('[data-target=\"view-content\"]');\n\n if (btn === null) {\n return;\n }\n\n btn.forEach((element) => {\n element.addEventListener('click', function(e) {\n\n var target = e.target.closest('a');\n\n var instance = target.dataset.instanceid;\n var userid = target.dataset.userid;\n\n notificationModal(contextID, instance, userid); // Notification modal.\n });\n });\n */\n\n document.addEventListener('click', function (e) {\n\n if (e.target.closest('[data-target=\"view-content\"]') !== null) {\n\n var target = e.target.closest('a');\n\n var instance = target.dataset.instanceid;\n var userid = target.dataset.userid;\n\n notificationModal(contextID, instance, userid); // Notification modal.\n }\n\n })\n }\n };\n\n });\n"],"names":["define","$","Ajax","Notification","ModalFactory","Fragment","Str","ModalEvents","previewModalBody","contextID","userid","params","undefined","window","tinyMCE","get","contentheader","getContent","contentstatic","contentfooter","document","querySelector","innerHTML","formData","dynamicparams","contentdynamic","value","contenttype","chapterid","contentlength","form","forms","formdata","FormData","URLSearchParams","toString","finalParams","loadFragment","processResults","selector","modules","transport","query","success","failure","mod","call","methodname","args","then","result","fail","updateChapter","ctxID","contentMods","SELECTORS","type","addEventListener","e","selected","currentTarget","includes","Array","prototype","find","options","cmid","disabled","resetChapter","chapter","event","Event","dispatchEvent","previewNotification","contextid","btn","create","title","get_string","body","large","modal","show","getRoot","on","bodyRendered","preventDefault","target","setBody","catch","reportModal","closest","instance","dataset","instanceid","notificationModal"],"mappings":";;;;;;;;;AAyBAA,gDAAO,CAAC,SAAU,YAAa,oBAAqB,qBAAsB,gBAAiB,WAAY,sBACnG,SAAUC,EAAGC,KAAMC,aAAcC,aAAcC,SAAUC,IAAKC,mBAEpDC,iBAAmB,SAAUC,eAAWC,8DAAS,SAE/CC,OAGAA,YAFmBC,IAAnBC,OAAOC,SAAyBD,OAAOC,QAAQC,IAAI,6CAE1C,CACLC,cAAeH,OAAOC,QAAQC,IAAI,6CAA6CE,aAC/EC,cAAeL,OAAOC,QAAQC,IAAI,6CAA6CE,aAC/EE,cAAeN,OAAOC,QAAQC,IAAI,6CAA6CE,aAC/EP,OAAQA,QAIH,CACLM,cAAeI,SAASC,cAAc,sDAAsDC,UAC5FJ,cAAeE,SAASC,cAAc,sDAAsDC,UAC5FH,cAAeC,SAASC,cAAc,sDAAsDC,UAC5FZ,OAAQA,YAeZa,SAXAC,cAAgB,GAEsD,OAAtEJ,SAASC,cAAc,6CACvBG,cAAgB,CACZC,eAAgBL,SAASC,cAAc,2CAA2CK,MAClFC,YAAaP,SAASC,cAAc,wCAAwCK,MAC5EE,UAAWR,SAASC,cAAc,sCAAsCK,MACxEG,cAAeT,SAASC,cAAc,0CAA0CK,YAKpFI,KAAOV,SAASW,MAAM,6BACtBC,SAAW,IAAIC,SAASH,MAE5BP,SAAW,CACPS,SAFJA,SAAW,IAAIE,gBAAgBF,UAAUG,gBAKrCC,YAAc,IAAKzB,UAAWa,iBAAkBD,iBAE7ClB,SAASgC,aAAa,2BAA4B,kBAAmB5B,UAAW2B,oBAyCpF,CAEHE,eAAgB,SAAUC,SAAUC,gBACzBA,SAGXC,UAAW,SAAUF,SAAUG,MAAOC,QAASC,aAEvCC,IAAMzB,SAASC,cAAc,wCAEnBnB,KAAK4C,KAAK,CAAC,CACrBC,WAAY,wCACZC,KAAM,CAAEH,IAAKA,IAAInB,UAGb,GAAGuB,MAAK,SAAUC,QACtBP,QAAQO,WAETC,KAAKP,UAGZQ,cAAe,SAAUC,MAAOC,mBAEtBC,qBACU,oCADVA,cAEG,0CAIW,OAAhBD,YAAsB,KAClBE,KAAOpC,SAASC,cAAckC,sBAClCnC,SAASC,cAAckC,eAAeE,iBAAiB,UAAWC,QAE1DC,SADSD,EAAEE,cACOlC,MAClB4B,YAAYO,SAASF,SAASxB,YAC9B2B,MAAMC,UAAUC,KAAKlB,KAAKU,KAAKS,SAAS,SAAUC,MAC5B,KAAdA,KAAKxC,QACLwC,KAAKC,UAAW,MAIxBL,MAAMC,UAAUC,KAAKlB,KAAKU,KAAKS,SAAS,SAAUC,MAC5B,KAAdA,KAAKxC,QACLwC,KAAKC,UAAW,SAOpC/C,SAASC,cAAckC,sBAAsBE,iBAAiB,UAAU,IAAMW,iBAC9EhD,SAASC,cAAckC,eAAeE,iBAAiB,UAAU,IAAMW,qBACnEC,QAAUjD,SAASC,cAAc,4CAK5B+C,eACLC,QAAQ/C,UAAY,GACpB+C,QAAQ3C,MAAQ,OACZ4C,MAAQ,IAAIC,MAAM,UACtBF,QAAQG,cAAcF,SAI9BG,oBAAqB,SAAUC,eAEvBC,IAAMvD,SAASC,cAAc,sCAErB,OAARsD,KAIJA,IAAIlB,iBAAiB,SAAS,WA/GjB,IAAUhD,UAAAA,UAgHNiE,UA9GrBtE,aAAawE,OAAO,CAChBC,MAAOvE,IAAIwE,WAAW,UAAW,4BACjCC,KAAMvE,iBAAiBC,WACvBuE,OAAO,IACR/B,MAAMgC,QACLA,MAAMC,OAEND,MAAME,UAAUC,GAAG7E,YAAY8E,cAAc,WACzCJ,MAAME,UAAUpE,IAAI,GAAGM,cAAc,uBAAuBoC,iBAAiB,UAAWC,IACpFA,EAAE4B,qBACEC,OAAS7B,EAAE6B,OACfN,MAAMO,QAAQhF,iBAAiBC,UAAW8E,OAAO7D,iBAK1D+D,YAkGHC,YAAa,SAAUjF,WAqBnBW,SAASqC,iBAAiB,SAAS,SAAUC,MAEgB,OAArDA,EAAE6B,OAAOI,QAAQ,gCAA0C,KAEvDJ,OAAS7B,EAAE6B,OAAOI,QAAQ,KAE1BC,SAAWL,OAAOM,QAAQC,WAC1BpF,OAAS6E,OAAOM,QAAQnF,QA3HlB,SAAUD,UAAWmF,SAAUlF,YAEjDC,OAAS,CACTmF,WAAYF,SACZlF,OAAQA,QAGZN,aAAawE,OAAO,CAChBC,MAAOvE,IAAIwE,WAAW,UAAW,4BACjCC,KAAM1E,SAASgC,aAAa,2BAA4B,2BAA4B5B,UAAWE,QAC/FqE,OAAO,IACR/B,MAAMgC,QACLA,MAAMC,UAEPO,QA+GSM,CAAkBtF,UAAWmF,SAAUlF"} \ No newline at end of file diff --git a/actions/notification/amd/build/notification.min.js b/actions/notification/amd/build/notification.min.js deleted file mode 100644 index bbf4f8b..0000000 --- a/actions/notification/amd/build/notification.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("pulseaction_notification/notification", ["jquery", "core/fragment"], (function ($, Fragment) { var contextID; const SELECTORS_chaperType = "#id_pulsenotification_contenttype", SELECTORS_mod = "#id_pulsenotification_dynamiccontent", updateChapter = method => { var type = document.querySelector("#id_pulsenotification_contenttype").value, mod = document.querySelector("#id_pulsenotification_dynamiccontent"), chapter = document.querySelector("#id_pulsenotification_chapter"); if (2 != parseInt(type)) return !0; var params = { mod: mod.value }; Fragment.loadFragment("pulseaction_notification", "update_chapters", contextID, params).then(((html, js) => { chapter.innerHTML = html })).catch() }; return { init: function () { }, updateChapter: function (contextid) { (contextid => { contextID = contextid, document.querySelector(SELECTORS_chaperType).addEventListener("change", (e => updateChapter())), document.querySelector(SELECTORS_mod).addEventListener("change", (e => updateChapter())) })(contextid) } } })); - -//# sourceMappingURL=notification.min.js.map diff --git a/actions/notification/amd/build/notification.min.js.map b/actions/notification/amd/build/notification.min.js.map deleted file mode 100644 index 81c21eb..0000000 --- a/actions/notification/amd/build/notification.min.js.map +++ /dev/null @@ -1,39 +0,0 @@ -{ - "version": 3, - "file": "notification.min.js", - "sources": [ - "../src/notification.js" - ], - "sourcesContent": [ - "define('pulseaction_notification/notification', ['jquery', 'core/fragment'], function($, Fragment) {\r\n\r\n var contextID;\r\n\r\n const SELECTORS = {\r\n chaperType : \"#id_pulsenotification_contenttype\",\r\n mod: \"#id_pulsenotification_dynamiccontent\"\r\n };\r\n\r\n const addChapterEventListners = (contextid) => {\r\n\r\n contextID = contextid\r\n\r\n document.querySelector(SELECTORS.chaperType).addEventListener(\"change\", (e) => updateChapter());\r\n document.querySelector(SELECTORS.mod).addEventListener(\"change\", (e) => updateChapter());\r\n }\r\n\r\n const updateChapter = (method) => {\r\n var type = document.querySelector(\"#id_pulsenotification_contenttype\").value;\r\n var mod = document.querySelector(\"#id_pulsenotification_dynamiccontent\");\r\n var chapter = document.querySelector(\"#id_pulsenotification_chapter\");\r\n\r\n if (parseInt(type) != 2) {\r\n return true;\r\n }\r\n\r\n var params = {mod: mod.value};\r\n\r\n // TODO: Loading icon near.\r\n Fragment.loadFragment('pulseaction_notification', 'update_chapters', contextID, params).then((html, js) => {\r\n chapter.innerHTML = html;\r\n }).catch();\r\n }\r\n\r\n\r\n return {\r\n\r\n init: function() {\r\n\r\n },\r\n\r\n updateChapter: function(contextid) {\r\n addChapterEventListners(contextid);\r\n }\r\n }\r\n})\r\n" - ], - "names": [ - "define", - "$", - "Fragment", - "contextID", - "SELECTORS", - "updateChapter", - "method", - "type", - "document", - "querySelector", - "value", - "mod", - "chapter", - "parseInt", - "params", - "loadFragment", - "then", - "html", - "js", - "innerHTML", - "catch", - "init", - "contextid", - "addEventListener", - "e", - "addChapterEventListners" - ], - "mappings": "AAAAA,+CAAgD,CAAC,SAAU,kBAAkB,SAASC,EAAGC,cAEjFC,gBAEEC,qBACW,oCADXA,cAEG,uCAWHC,cAAiBC,aACfC,KAAOC,SAASC,cAAc,qCAAqCC,MACnEC,IAAMH,SAASC,cAAc,wCAC7BG,QAAUJ,SAASC,cAAc,oCAEf,GAAlBI,SAASN,aACF,MAGPO,OAAS,CAACH,IAAKA,IAAID,OAGvBR,SAASa,aAAa,2BAA4B,kBAAmBZ,UAAWW,QAAQE,MAAK,CAACC,KAAMC,MAChGN,QAAQO,UAAYF,QACrBG,eAIA,CAEHC,KAAM,aAINhB,cAAe,SAASiB,WAhCKA,CAAAA,YAE7BnB,UAAYmB,UAEZd,SAASC,cAAcL,sBAAsBmB,iBAAiB,UAAWC,GAAMnB,kBAC/EG,SAASC,cAAcL,eAAemB,iBAAiB,UAAWC,GAAMnB,mBA4BpEoB,CAAwBH" -} diff --git a/actions/notification/amd/src/chaptersource.js b/actions/notification/amd/src/chaptersource.js deleted file mode 100644 index f5faf76..0000000 --- a/actions/notification/amd/src/chaptersource.js +++ /dev/null @@ -1,227 +0,0 @@ -// 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 . - -/** - * Frameworks datasource. - * - * This module is compatible with core/form-autocomplete. - * - * @module tool_lp/frameworks_datasource - * @copyright 2016 Frédéric Massart - FMCorz.net - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -define(['jquery', 'core/ajax', 'core/notification', 'core/modal_factory', 'core/fragment', 'core/str', 'core/modal_events'], - function ($, Ajax, Notification, ModalFactory, Fragment, Str, ModalEvents) { - - const previewModalBody = function (contextID, userid = null) { - - var params; - if (window.tinyMCE !== undefined && window.tinyMCE.get('id_pulsenotification_headercontent_editor')) { - // EditorPlugin = window.tinyMCE; - params = { - contentheader: window.tinyMCE.get('id_pulsenotification_headercontent_editor').getContent(), - contentstatic: window.tinyMCE.get('id_pulsenotification_staticcontent_editor').getContent(), - contentfooter: window.tinyMCE.get('id_pulsenotification_footercontent_editor').getContent(), - userid: userid - }; - } else { - // EditorPlugin = document; - params = { - contentheader: document.querySelector('#id_pulsenotification_headercontent_editoreditable').innerHTML, - contentstatic: document.querySelector('#id_pulsenotification_staticcontent_editoreditable').innerHTML, - contentfooter: document.querySelector('#id_pulsenotification_footercontent_editoreditable').innerHTML, - userid: userid - }; - } - - var dynamicparams = {}; - - if (document.querySelector('[name=pulsenotification_dynamiccontent]') !== null) { - dynamicparams = { - contentdynamic: document.querySelector('[name=pulsenotification_dynamiccontent]').value, - contenttype: document.querySelector('[name=pulsenotification_contenttype]').value, - chapterid: document.querySelector('[name=pulsenotification_chapterid]').value, - contentlength: document.querySelector('[name=pulsenotification_contentlength]').value, - }; - } - // Get the form data. - var formData; - var form = document.forms['pulse-automation-template']; - var formdata = new FormData(form); - formdata = new URLSearchParams(formdata).toString(); - formData = { - formdata: formdata - }; - - var finalParams = { ...params, ...dynamicparams, ...formData }; - - return Fragment.loadFragment('pulseaction_notification', 'preview_content', contextID, finalParams); - }; - - const previewModal = function (contextID) { - - ModalFactory.create({ - title: Str.get_string('preview', 'pulseaction_notification'), - body: previewModalBody(contextID), - large: true, - }).then((modal) => { - modal.show(); - - modal.getRoot().on(ModalEvents.bodyRendered, function () { - modal.getRoot().get(0).querySelector('[name=userselector]').addEventListener('change', (e) => { - e.preventDefault(); - var target = e.target; - modal.setBody(previewModalBody(contextID, target.value)); - }); - }); - - return; - }).catch(); - }; - - const notificationModal = function (contextID, instance, userid) { - - var params = { - instanceid: instance, - userid: userid - }; - - ModalFactory.create({ - title: Str.get_string('preview', 'pulseaction_notification'), - body: Fragment.loadFragment('pulseaction_notification', 'preview_instance_content', contextID, params), - large: true, - }).then((modal) => { - modal.show(); - return; - }).catch(); - }; - - return { - - processResults: function (selector, modules) { - return modules; - }, - - transport: function (selector, query, success, failure) { - - var mod = document.querySelector("#id_pulsenotification_dynamiccontent"); - - var promise = Ajax.call([{ - methodname: 'pulseaction_notification_get_chapters', - args: { mod: mod.value } - }]); - - promise[0].then(function (result) { - success(result); - return; - }).fail(failure); - }, - - updateChapter: function (ctxID, contentMods) { - - const SELECTORS = { - chaperType: "#id_pulsenotification_contenttype", - mod: "#id_pulsenotification_dynamiccontent" - }; - - // Disable the content type option for modules other than book and page. - if (contentMods !== null) { - var type = document.querySelector(SELECTORS.chaperType); - document.querySelector(SELECTORS.mod).addEventListener("change", (e) => { - var target = e.currentTarget; - var selected = target.value; - if (contentMods.includes(selected.toString())) { - Array.prototype.find.call(type.options, function (cmid) { - if (cmid.value == '2') { - cmid.disabled = false; - } - }); - } else { - Array.prototype.find.call(type.options, function (cmid) { - if (cmid.value == '2') { - cmid.disabled = true; - } - }); - } - }); - } - - document.querySelector(SELECTORS.chaperType).addEventListener("change", () => resetChapter()); - document.querySelector(SELECTORS.mod).addEventListener("change", () => resetChapter()); - var chapter = document.querySelector("#id_pulsenotification_chapterid"); - - /** - * - */ - function resetChapter() { - chapter.innerHTML = ''; - chapter.value = ''; - var event = new Event('change'); - chapter.dispatchEvent(event); - } - }, - - previewNotification: function (contextid) { - - var btn = document.querySelector('[name="pulsenotification_preview"]'); - - if (btn === null) { - return; - } - - btn.addEventListener('click', function () { - previewModal(contextid); - }); - }, - - reportModal: function (contextID) { - // View content. - /* var btn = document.querySelectorAll('[data-target="view-content"]'); - - if (btn === null) { - return; - } - - btn.forEach((element) => { - element.addEventListener('click', function(e) { - - var target = e.target.closest('a'); - - var instance = target.dataset.instanceid; - var userid = target.dataset.userid; - - notificationModal(contextID, instance, userid); // Notification modal. - }); - }); - */ - - document.addEventListener('click', function (e) { - - if (e.target.closest('[data-target="view-content"]') !== null) { - - var target = e.target.closest('a'); - - var instance = target.dataset.instanceid; - var userid = target.dataset.userid; - - notificationModal(contextID, instance, userid); // Notification modal. - } - - }) - } - }; - - }); diff --git a/actions/notification/backup/moodle2/backup_pulseaction_notification_subplugin.class.php b/actions/notification/backup/moodle2/backup_pulseaction_notification_subplugin.class.php deleted file mode 100644 index 09e6bae..0000000 --- a/actions/notification/backup/moodle2/backup_pulseaction_notification_subplugin.class.php +++ /dev/null @@ -1,86 +0,0 @@ -. - -/** - * This file contains the class for backup of this pulse action notification plugin. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -/** - * Provides the information to backup of the pulse action notification. - */ -class backup_pulseaction_notification_subplugin extends backup_subplugin { - - /** - * Returns the subplugin information to attach to submission element - * @return backup_subplugin_element - */ - protected function define_pulse_autoinstances_subplugin_structure() { - - // Create XML elements. - $subplugin = $this->get_subplugin_element(); - - // NOtification action template. - $action = new \backup_nested_element('notificationaction'); - $actionfields = new \backup_nested_element('pulseaction_notification', ['id'], [ - "templateid", "sender", "senderemail", "notifyinterval", "week", "month", "time", "notifydelay", "delayduration", - "notifylimit", "recipients", "cc", "bcc", "subject", "headercontent", "headercontentformat", "staticcontent", - "staticcontentformat", "dynamiccontent", "contentlength", "contenttype", "footercontent", "footercontentformat", - "timemodified", - ]); - - $actionins = new \backup_nested_element('notificationactionins'); - $actioninsfields = new \backup_nested_element('pulseaction_notification_ins', ['id'], [ - "instanceid", "sender", "senderemail", "notifyinterval", "week", "month", "time", "notifydelay", "delayduration", - "suppress", "suppressoperator", "notifylimit", "recipients", "cc", "bcc", "subject", "headercontent", "staticcontent", - "dynamiccontent", "contentlength", "contenttype", "chapterid", "footercontent", "timemodified", - ]); - - $subplugin->add_child($action); - $action->add_child($actionfields); - - $subplugin->add_child($actionins); - $actionins->add_child($actioninsfields); - - // Notification template data source query. - $actionfields->set_source_sql(' - SELECT pn.* - FROM {pulseaction_notification} pn - JOIN {pulse_autotemplates} at ON at.id = pn.templateid - WHERE at.id IN ( - SELECT templateid - FROM {pulse_autoinstances} - WHERE courseid = :courseid - ) - ', ['courseid' => backup::VAR_COURSEID]); - - $actioninsfields->set_source_table('pulseaction_notification_ins', ['instanceid' => \backup::VAR_PARENTID]); - - // Define file annotations. - $subplugin->annotate_files('mod_pulse', 'pulsenotification_headercontent', null); - $subplugin->annotate_files('mod_pulse', 'pulsenotification_staticcontent', null); - $subplugin->annotate_files('mod_pulse', 'pulsenotification_footercontent', null); - $subplugin->annotate_files('mod_pulse', 'pulsenotification_headercontent_instance', null); - $subplugin->annotate_files('mod_pulse', 'pulsenotification_staticcontent_instance', null); - $subplugin->annotate_files('mod_pulse', 'pulsenotification_footercontent_instance', null); - - return $subplugin; - } - -} diff --git a/actions/notification/backup/moodle2/restore_pulseaction_notification_subplugin.class.php b/actions/notification/backup/moodle2/restore_pulseaction_notification_subplugin.class.php deleted file mode 100644 index b6bf013..0000000 --- a/actions/notification/backup/moodle2/restore_pulseaction_notification_subplugin.class.php +++ /dev/null @@ -1,150 +0,0 @@ -. - -/** - * This file contains the class for restore of this pulse action notification plugin - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -/** - * Restore pulse action subplugin class. - * - */ -class restore_pulseaction_notification_subplugin extends restore_subplugin { - - /** - * Returns the paths to be handled by the subplugin. - * @return array - */ - protected function define_pulse_autoinstances_subplugin_structure() { - - $paths = []; - - // We used get_recommended_name() so this works. - $paths[] = new restore_path_element('pulseaction_notification', - '/activity/pulse_autoinstances/notificationaction/pulseaction_notification' - ); - - $paths[] = new restore_path_element('pulseaction_notification_ins', - '/activity/pulse_autoinstances/notificationactionins/pulseaction_notification_ins' - ); - - return $paths; - } - - /** - * Processes one pulseaction_notification element - * @param mixed $data - * @return void - */ - public function process_pulseaction_notification($data) { - global $DB; - - $data = (object) $data; - // Get the new template id. - $data->templateid = $this->get_mappingid('pulse_autotemplates', $data->templateid); - - // If already notification is created for template then no need to include again. - if (!$DB->record_exists('pulseaction_notification', ['templateid' => $data->templateid])) { - $DB->insert_record('pulseaction_notification', $data); - } - } - - /** - * Processes one pulseaction_notification_ins element - * @param mixed $data - * @return void - */ - public function process_pulseaction_notification_ins($data) { - global $DB; - - $data = (object)$data; - - $data->instanceid = $this->get_new_parentid('pulse_autoinstances'); - - if (!$DB->record_exists('pulseaction_notification_ins', ['instanceid' => $data->instanceid])) { - $DB->insert_record('pulseaction_notification_ins', $data); - } - } - - /** - * Update editors Methods for after the pulse automation instances restore. - * - * @return void - */ - public function launch_after_restore_methods() { - global $DB; - - $instances = $DB->get_records('backup_ids_temp', [ - 'backupid' => $this->get_restoreid(), 'itemname' => 'pulse_autoinstances', - ]); - - foreach ($instances as $instance) { - $oldinstances = $DB->get_records('pulseaction_notification_ins', ['instanceid' => $instance->newitemid]); - foreach ($oldinstances as $ins) { - $record = (object) ['id' => $ins->id]; - $suppress = $ins->suppress ? json_decode($ins->suppress, true) : []; - if ($suppress) { - array_walk($suppress, function(&$sup) { - $sup = $this->get_mappingid('course_module', $sup, $sup); - }); - $record->suppress = json_encode($suppress); - } - - if (!empty($ins->dynamiccontent)) { - $record->dynamiccontent = $this->get_mappingid('course_module', $ins->dynamiccontent, $ins->dynamiccontent); - } - $DB->update_record('pulseaction_notification_ins', $record); - - // Add pulse related files. - $this->add_related_files('mod_pulse', 'pulsenotification_headercontent', 'pulse_autoinstances', null, - $instance->itemid); - $this->add_related_files('mod_pulse', 'pulsenotification_staticcontent', 'pulse_autoinstances', null, - $instance->itemid); - $this->add_related_files('mod_pulse', 'pulsenotification_footercontent', 'pulse_autoinstances', null, - $instance->itemid); - // Add pulse related files. - $this->add_related_files('mod_pulse', 'pulsenotification_headercontent_instance', 'pulse_autoinstances', null, - $instance->itemid); - $this->add_related_files('mod_pulse', 'pulsenotification_staticcontent_instance', 'pulse_autoinstances', null, - $instance->itemid); - $this->add_related_files('mod_pulse', 'pulsenotification_footercontent_instance', 'pulse_autoinstances', null, - $instance->itemid); - } - } - } - - /** - * Update the files of editors after restore execution. - * - * @param array $contents - * @return void - */ - public static function decode_contents(&$contents) { - - $contents[] = new restore_decode_content('pulseaction_notification_ins', [ - 'headercontent', 'staticcontent', 'footercontent', - ], 'pulse_autoinstances'); - - $contents[] = new restore_decode_content('pulseaction_notification', [ - 'headercontent', 'staticcontent', 'footercontent', - ], 'pulse_autotemplates'); - } - -} diff --git a/actions/notification/classes/actionform.php b/actions/notification/classes/actionform.php deleted file mode 100644 index 599aede..0000000 --- a/actions/notification/classes/actionform.php +++ /dev/null @@ -1,861 +0,0 @@ -. - -/** - * Notification pulse action form. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace pulseaction_notification; - -use html_writer; -use moodle_exception; -use DateTime; -use DatePeriod; -use mod_pulse\automation\helper; -use pulseaction_notification\notification; -use pulseaction_notification\schedule; -use pulseaction_notification\task\notify_users; - -/** - * Notification action form, contains important method and basic plugin details. - */ -class actionform extends \mod_pulse\automation\action_base { - - /** - * Shortname for the config used in the form field. - * - * @return string - */ - public function config_shortname() { - return 'pulsenotification'; - } - - /** - * Get the icon for this component, displayed on the instances list on the course autotemplates sections. - * - * @return string - */ - public function get_action_icon() { - global $OUTPUT; - return $OUTPUT->pix_icon("i/notifications", get_string('notifications')); - } - - /** - * Delete notification instances and schedule data for this instance. - * - * @param int $instanceid - * @return void - */ - public function delete_instance_action(int $instanceid) { - global $DB; - - parent::delete_instance_action($instanceid); - $instancetable = 'pulseaction_notification_sch'; - return $DB->delete_records($instancetable, ['instanceid' => $instanceid]); - } - - /** - * Instances disabled, then disable all the schedules of the instances. - * - * @param stdclass $instancedata - * @param bool $status - * @return void - */ - public function instance_status_updated($instancedata, $status) { - global $DB; - - $notificationid = $instancedata->actions['notification']['id']; - $notification = notification::instance($notificationid); - $notification->set_notification_data($instancedata->actions['notification'], $instancedata); - - $notification->create_schedule_forinstance(); - } - - /** - * Prepare editor fileareas. - * - * @param stdclass $data Instance/Templates data of the notification. - * @param \context $context - * @return void - */ - public function prepare_editor_fileareas(&$data, \context $context) { - - $data = (object) ($data ?: ['id' => 0]); // Create empty data set if empty. - - $context = \context_system::instance(); - $templateid = $data->templateid ?? $data->id; - - // Support for the instance form. use the course context to prepare and update editor incase it's override in the instance. - if (isset($data->courseid) && isset($data->instanceid)) { - $prefix = '_instance'; - } - // List of editors need to prepare for forms. - $editor = [ - "pulsenotification_headercontent", - "pulsenotification_staticcontent", - "pulsenotification_footercontent", - ]; - - foreach ($editor as $field) { - // Create empty data set for the new template. - if (!isset($data->$field)) { - $data->$field = ''; - $data->{$field."format"} = editors_get_preferred_format(); - } - - $id = isset($data->instanceid) && isset($data->override[$field.'_editor']) ? $data->instanceid : $templateid; - $filearea = isset($prefix) && isset($data->override[$field.'_editor']) ? $field.$prefix : $field; - - $data = file_prepare_standard_editor( - $data, $field, $this->get_editor_options($context), $context, 'mod_pulse', $filearea, $id - ); - } - - } - - /** - * Prepare editor fileareas. - * - * @param stdclass $data Instance/Templates data of the notification. - * @param \context $context - * @return void - */ - public function postupdate_editor_fileareas(&$data, \context $context) { - $data = (object) $data; - - $context = \context_system::instance(); - $templateid = $data->templateid ?? $data->id; - - $editor = [ - "pulsenotification_headercontent", - "pulsenotification_staticcontent", - "pulsenotification_footercontent", - ]; - - // Use the prefix for the instance. - if (isset($data->courseid) && isset($data->instanceid) ) { - $prefix = '_instance'; - } - - foreach ($editor as $field) { - - if (!isset($data->$field) && !isset($data->{$field.'_editor'})) { - continue; - } - - $id = $data->instanceid ?? $templateid; - $filearea = isset($prefix) ? $field.$prefix : $field; - - $data = file_postupdate_standard_editor( - $data, $field, $this->get_editor_options($context), $context, 'mod_pulse', $filearea, $id - ); - } - } - - /** - * Get text editor options to manage files. - * - * @param \stdclass $context - * @return array - */ - protected function get_editor_options($context=null) { - global $PAGE; - - return [ - 'trusttext' => true, - 'subdirs' => true, - 'maxfiles' => 50, - 'context' => $context ?: $PAGE->context, - ]; - } - - /** - * Delete the notification instance. - * - * @param int $templateid - * @return void - */ - public function delete_template_action($templateid) { - global $DB; - - $instances = $this->get_template_instances($templateid); - // Remove its instances and schedules when the template is deleted. - foreach ($instances as $instanceid => $instance) { - $DB->delete_records('pulseaction_notification_ins', ['instanceid' => $instanceid]); - $DB->delete_records('pulseaction_notification_sch', ['instanceid' => $instanceid]); - } - - return $DB->delete_records('pulseaction_notification', ['templateid' => $templateid]); - } - - /** - * Action is triggered for the instance. when triggered notification will create a schedule for the triggered users. - * - * Create the notification instance and initiate the schedule for this instance. - * - * @param stdclass $instancedata - * @param int $userid - * @param int $expectedtime - * @param bool $newuser - * - * @return void - */ - public function trigger_action($instancedata, $userid, $expectedtime=null, $newuser=false) { - - if (!isset($instancedata->pulsenotification_id)) { - return false; - } - - $notification = notification::instance($instancedata->pulsenotification_id); - $notificationinstance = (object) helper::filter_record_byprefix($instancedata, $this->config_shortname()); - - $notification->set_notification_data($notificationinstance, $instancedata); - - // Find the suppress conditions are reached. - $course = $instancedata->course; - $suppressreached = notify_users::is_suppress_reached($notificationinstance, $userid, $course, null); - if ($suppressreached) { // Suppress reached not need to setup new schedules. - return ''; - } - // Create a schedule for user. This method verify the user activity completion before creating schedules. - $notification->create_schedule_foruser($userid, '', null, $expectedtime ?? null, $newuser); - - // Send the scheduled notifications for this user. - schedule::instance()->send_scheduled_notification($userid); - } - - /** - * Remove the user schedules when the user is deleted. - * - * Observe the events, triggered from the main pulse. - * - * @param stdclass $instancedata Automation Instance data. - * @param string $method Name of the triggered event. - * @param stdclass $eventdata Triggered event data. - * - * @return void - */ - public function trigger_action_event($instancedata, $method, $eventdata) { - - if ($method == 'user_enrolment_deleted') { - - $notificationid = $instancedata->actions['notification']['id']; - $notification = notification::instance($notificationid); - $notification->set_notification_data($instancedata->actions['notification'], $instancedata); - $userid = $eventdata->relateduserid; - - $notification->remove_user_schedules($userid); - } - } - - /** - * Get the notification record to attach the template create form. - * - * @param int $templateid - * @return stdclass - */ - public function get_data_fortemplate($templateid) { - global $DB; - // Notification data for template. - $actiondata = $DB->get_record('pulseaction_notification', ['templateid' => $templateid]); - return $actiondata; - } - - /** - * Get the notification instance record. - * - * @param int $instanceid - * @return stdclass Data of the notification instance. - */ - public function get_data_forinstance($instanceid) { - global $DB; - $instancedata = $DB->get_record('pulseaction_notification_ins', ['instanceid' => $instanceid]); - return $instancedata ?: []; - } - - /** - * Decode the json encoded notification data. - * - * @param array $actiondata - * @return void - */ - public function update_encode_data(&$actiondata) { - - $actiondata = (array) $actiondata; - - $actiondata['recipients'] = json_decode($actiondata['recipients']); - $actiondata['bcc'] = json_decode($actiondata['bcc']); - $actiondata['cc'] = json_decode($actiondata['cc']); - $actiondata['suppress'] = isset($actiondata['suppress']) ? json_decode($actiondata['suppress']) : []; - $actiondata['notifyinterval'] = json_decode($actiondata['notifyinterval'], true); - } - - /** - * Encode the array fields to json type. - * - * @param array $actiondata - * @return void - */ - protected function update_data_structure(&$actiondata) { - - // Testing the action data. - array_walk($actiondata, function(&$value) { - if (is_array($value)) { - $value = json_encode($value); - } - }); - } - - /** - * Recreate the schedule for the notification instance, It mostly used when the template is updated. - * - * @param int $templateid Updated temmplate ID. - * - * @return void - */ - protected function recreate_instance_schedules(int $templateid) { - global $DB; - - $instances = $this->get_template_instances($templateid); - - foreach ($instances as $instanceid => $instance) { - $notification = $DB->get_field('pulseaction_notification_ins', 'id', ['instanceid' => $instanceid]); - if ($notification) { - notification::instance($notification)->recreate_schedule_forinstance(); - } - } - - } - - /** - * Generate the warnings if the instance is not compatibile to send notifications. - * - * @param \stdclass $course - * @return array - */ - public function display_instance_warnings(\stdclass $course): array { - - // Course visibility warnings. - if (!$course->visible) { - $warning[] = get_string('coursehidden', 'pulseaction_notification'); - } - - // Course active users warnings. - $coursecontext = \context_course::instance($course->id); - if (!count_enrolled_users($coursecontext, '', null, true)) { - $warning[] = get_string('noactiveusers', 'pulseaction_notification'); - } - - // Course is not started. - if ($course->startdate > time()) { - $warning[] = get_string('coursenotstarted', 'pulseaction_notification'); - } - - // Course is not started. - if ($course->enddate && $course->enddate < time()) { - $warning[] = get_string('courseenddatereached', 'pulseaction_notification'); - } - - return $warning ?? []; - } - /** - * Save the template config. - * - * @param stdclass $record - * @param string $component - * - * @return bool - */ - public function process_save($record, $component) { - global $DB; - - $context = \context_system::instance(); - - $this->postupdate_editor_fileareas($record, $context); - - // Filter the current action data from the templates data by its shortname. - $actiondata = $this->filter_action_data((array) $record); - - $actiondata->templateid = $record->templateid; - - if (!empty($actiondata)) { - - // Update the data strucured before save. - $this->update_data_structure($actiondata); - - try { - // In moodle, the main table should be the name of the component. - // Therefore, generate the table name based on the component name. - $tablename = 'pulseaction_'. $component; - // Get the record by using the templateid, one instance is allowed for each template. - // Manage the action component data for this template. - if ($notification = $DB->get_record($tablename, ['templateid' => $record->templateid])) { - $actiondata->id = $notification->id; - // Update the latest data into the action component. - $DB->update_record($tablename, $actiondata); - } else { - // Create new instance for this tempalte. - $DB->insert_record($tablename, $actiondata); - } - - // Recreate the schedules for the instance. - $templateid = $actiondata->templateid; - $this->recreate_instance_schedules($templateid); - - } catch (\Exception $e) { - // Throw an error incase of issue with manage the data update. - throw new \moodle_exception('actiondatanotsave', $component); - } - } - return true; - } - - /** - * Save the submitted instance data for the notification action. Update the array values to json. - * After insert/update the data to DB then trigger the notification schedule for the instance course. - * - * @param int $instanceid - * @param stdclass $record - * @return bool - */ - public function process_instance_save($instanceid, $record) { - global $DB; - - // Filter the current action data from the templates data by its shortname. - $actiondata = $this->filter_action_data((array) $record); - // Update the data strucured before save. - $this->update_data_structure($actiondata); - $actiondata->instanceid = $instanceid; - - try { - // In moodle, the main table should be the name of the component. - // Therefore, generate the table name based on the component name. - $tablename = 'pulseaction_notification_ins'; - // Get the record by using the templateid, one instance is allowed for each template. - // Manage the action component data for this template. - if (isset($instanceid) && $notifyinstance = $DB->get_record($tablename, ['instanceid' => $instanceid])) { - $actiondata->id = $notifyinstance->id; - - $notificationinstance = $actiondata->id; - // Update the latest data into the action component. - $DB->update_record($tablename, $actiondata); - } else { - // Create new instance for this tempalte. - $notificationinstance = $DB->insert_record($tablename, $actiondata); - } - // Create a schedules based on receipents role. - notification::instance($notificationinstance)->create_schedule_forinstance(); - } catch (\Exception $e) { - // Throw an error incase of issue with manage the data update. - throw new \moodle_exception('actiondatanotsave', 'pulseaction_notification'); - } - - return true; - } - - /** - * Default override elements. - * - * @return array - */ - public function default_override_elements() { - // List of pulse notification elements those are available in only instances. - return [ - 'pulsenotification_suppress', - 'pulsenotification_suppressoperator', - 'pulsenotification_dynamiccontent', - 'pulsenotification_contenttype', - 'pulsenotification_chapterid', - 'pulsenotification_contentlength', - ]; - } - - /** - * Load the notification elements for the instance form. - * - * @param moodle_form $mform - * @param actionform $forminstance - * @return void - */ - public function load_instance_form(&$mform, $forminstance) { - global $CFG, $PAGE, $DB; - - require_once($CFG->dirroot.'/lib/modinfolib.php'); - - $this->load_global_form($mform, $forminstance); - - // Dynamic Content Group - // Add 'dynamic_content' element with all activities in the course. - $courseid = $forminstance->get_customdata('courseid') ?? ''; - $modinfo = \course_modinfo::instance($courseid); - - // Include the suppress activity settings for the instance. - $completion = new \completion_info(get_course($courseid)); - $activities = $completion->get_activities(); - array_walk($activities, function(&$value) { - $value = format_string($value->name); - }); - - $suppress = $mform->createElement('autocomplete', 'pulsenotification_suppress', - get_string('suppressmodule', 'pulseaction_notification'), $activities, ['multiple' => 'multiple']); - - $mform->insertElementBefore($suppress, 'pulsenotification_notifylimit'); - $mform->addHelpButton('pulsenotification_suppress', 'suppressmodule', 'pulseaction_notification'); - - // Operator element. - $operators = [ - \mod_pulse\automation\action_base::OPERATOR_ALL => get_string('all', 'pulse'), - \mod_pulse\automation\action_base::OPERATOR_ANY => get_string('any', 'pulse'), - ]; - $suppressopertor = $mform->createElement('select', 'pulsenotification_suppressoperator', - get_string('suppressoperator', 'pulseaction_notification'), $operators); - $mform->setDefault('suppressoperator', \mod_pulse\automation\action_base::OPERATOR_ANY); - $mform->insertElementBefore($suppressopertor, 'pulsenotification_notifylimit'); - $mform->addHelpButton('pulsenotification_suppressoperator', 'suppressoperator', 'pulseaction_notification'); - - $modules = [0 => get_string('none')]; - $list = $modinfo->get_instances(); - $contentmods = []; - foreach ($list as $modname => $mods) { - foreach ($mods as $mod) { - $modules[$mod->id] = $mod->get_formatted_name(); - if ($mod->modname == 'page' || $mod->modname == 'book') { - $contentmods[] = $mod->id; - } - } - } - // PAGE modules in this course. - $pages = $modinfo->get_instances_of('page'); - - $dynamic = $mform->createElement('select', 'pulsenotification_dynamiccontent', - get_string('dynamiccontent', 'pulseaction_notification'), $modules); - $mform->insertElementBefore($dynamic, 'pulsenotification_footercontent_editor'); - $mform->addHelpButton('pulsenotification_dynamiccontent', 'dynamiccontent', 'pulseaction_notification'); - - // Add 'content_type' element with the following options. - $contenttypeoptions = [ - notification::DYNAMIC_PLACEHOLDER => get_string('dynamicplacholder', 'pulseaction_notification'), - notification::DYNAMIC_DESCRIPTION => get_string('dynamicdescription', 'pulseaction_notification'), - notification::DYNAMIC_CONTENT => get_string('dynamiccontent', 'pulseaction_notification'), - ]; - $dynamic2 = $mform->createElement('select', 'pulsenotification_contenttype', - get_string('contenttype', 'pulseaction_notification'), $contenttypeoptions); - $mform->insertElementBefore($dynamic2, 'pulsenotification_footercontent_editor'); - $mform->hideIf('pulsenotification_contenttype', 'pulsenotification_dynamiccontent', 'eq', 0); - $mform->addHelpButton('pulsenotification_contenttype', 'contenttype', 'pulseaction_notification'); - - // Load Chapters for selected book. - $instanceid = $forminstance->get_customdata('instanceid'); - $cmid = $instanceid ? $DB->get_field('pulseaction_notification_ins', 'dynamiccontent', ['instanceid' => $instanceid]) : ''; - if (!empty($cmid)) { - $sql = 'SELECT bc.id, bc.title FROM {course_modules} cm - JOIN {book_chapters} bc ON bc.bookid = cm.instance - WHERE cm.id = :cmid'; - $chapters = $DB->get_records_sql_menu($sql, ['cmid' => $cmid]); - } - $options['ajax'] = 'pulseaction_notification/chaptersource'; - $chapter = $mform->createElement('autocomplete', 'pulsenotification_chapterid', - get_string('chapters', 'pulseaction_notification'), $chapters ?? [], $options); - $mform->insertElementBefore($chapter, 'pulsenotification_footercontent_editor'); - $mform->addHelpButton('pulsenotification_chapterid', 'chapters', 'pulseaction_notification'); - $mform->hideIf('pulsenotification_chapterid', 'pulsenotification_dynamiccontent', 'eq', 0); - foreach ($pages as $page) { - $mform->hideIf('pulsenotification_chapterid', 'pulsenotification_dynamiccontent', 'eq', $page->id); - } - - // Content Length Group. - $contentlengthoptions = [ - notification::LENGTH_TEASER => get_string('teaser', 'pulseaction_notification'), - notification::LENGTH_LINKED => get_string('full_linked', 'pulseaction_notification'), - notification::LENGTH_NOTLINKED => get_string('full_not_linked', 'pulseaction_notification'), - ]; - $dynamic3 = $mform->createElement('select', 'pulsenotification_contentlength', - get_string('contentlength', 'pulseaction_notification'), $contentlengthoptions); - $mform->insertElementBefore($dynamic3, 'pulsenotification_footercontent_editor'); - $mform->addHelpButton('pulsenotification_contentlength', 'contentlength', 'pulseaction_notification'); - - $mform->hideIf('pulsenotification_contentlength', 'pulsenotification_dynamiccontent', 'eq', 0); - - asort($mform->_elementIndex); - - $PAGE->requires->js_call_amd('pulseaction_notification/chaptersource', 'updateChapter', - ['contextid' => $PAGE->context->id, 'contentmods' => $contentmods] - ); - } - - /** - * Global form elements for notification action. - * - * @param moodle_form $mform - * @param \automation_instance_form $forminstance - * @return void - */ - public function load_global_form(&$mform, $forminstance) { - global $CFG, $PAGE; - - require_once($CFG->dirroot.'/course/lib.php'); - require_once($CFG->dirroot.'/mod/pulse/lib.php'); - - // Sender Group. - $senderoptions = [ - notification::SENDERCOURSETEACHER => get_string('courseteacher', 'pulseaction_notification'), - notification::SENDERGROUPTEACHER => get_string('groupteacher', 'pulseaction_notification'), - notification::SENDERTENANTROLE => get_string('tenantrole', 'pulseaction_notification'), - notification::SENDERCUSTOM => get_string('custom', 'pulseaction_notification'), - ]; - $mform->addElement('select', 'pulsenotification_sender', get_string('sender', 'pulseaction_notification'), $senderoptions); - $mform->addHelpButton('pulsenotification_sender', 'sender', 'pulseaction_notification'); - - // Add additional settings for the 'custom' option, if selected. - $mform->addElement('text', 'pulsenotification_senderemail', get_string('senderemail', 'pulseaction_notification')); - $mform->setType('pulsenotification_senderemail', PARAM_EMAIL); - $mform->hideIf('pulsenotification_senderemail', 'pulsenotification_sender', 'neq', notification::SENDERCUSTOM); - - $interval = []; - // Schedule Group. - $intervaloptions = [ - notification::INTERVALONCE => get_string('once', 'pulseaction_notification'), - notification::INTERVALDAILY => get_string('daily', 'pulseaction_notification'), - notification::INTERVALWEEKLY => get_string('weekly', 'pulseaction_notification'), - notification::INTERVALMONTHLY => get_string('monthly', 'pulseaction_notification'), - ]; - $interval[] =& $mform->createElement('select', 'pulsenotification_notifyinterval[interval]', - get_string('interval', 'pulseaction_notification'), $intervaloptions); - - // Add additional settings based on the selected interval. - $dayweeks = [ - 'monday' => get_string('monday', 'pulseaction_notification'), - 'tuesday' => get_string('tuesday', 'pulseaction_notification'), - 'wednesday' => get_string('wednesday', 'pulseaction_notification'), - 'thursday' => get_string('thursday', 'pulseaction_notification'), - 'friday' => get_string('friday', 'pulseaction_notification'), - 'saturday' => get_string('saturday', 'pulseaction_notification'), - 'sunday' => get_string('sunday', 'pulseaction_notification'), - ]; - - // Add 'day_of_week' element if 'weekly' is selected in the 'interval' element. - $interval[] =& $mform->createElement('select', 'pulsenotification_notifyinterval[weekday]', - get_string('interval', 'pulseaction_notification'), $dayweeks); - $mform->hideIf('pulsenotification_notifyinterval[weekday]', 'pulsenotification_notifyinterval[interval]', - 'neq', notification::INTERVALWEEKLY); - - $dates = range(1, 31); - // Add 'day_of_month' element if 'monthly' is selected in the 'interval' element. - $interval[] =& $mform->createElement('select', 'pulsenotification_notifyinterval[monthdate]', - get_string('interval', 'pulseaction_notification'), $dates); - $mform->hideIf('pulsenotification_notifyinterval[monthdate]', 'pulsenotification_notifyinterval[interval]', - 'neq', notification::INTERVALMONTHLY); - - // Time to send notification. - $dates = $this->get_times(); - // Add 'time_of_day' element. - // Add 'day_of_month' element if 'monthly' is selected in the 'interval' element. - $interval[] =& $mform->createElement('select', 'pulsenotification_notifyinterval[time]', - get_string('interval', 'pulseaction_notification'), $dates); - $mform->hideIf('pulsenotification_notifyinterval[time]', 'pulsenotification_notifyinterval[interval]', - 'eq', notification::INTERVALONCE); - - // Notification interval button groups. - $mform->addGroup($interval, 'pulsenotification_notifyinterval', - get_string('interval', 'pulseaction_notification'), [' '], false); - $mform->addHelpButton('pulsenotification_notifyinterval', 'interval', 'pulseaction_notification'); - - // Notification delay. - $delayoptions = [ - notification::DELAYNONE => get_string('none', 'pulseaction_notification'), - notification::DELAYBEFORE => get_string('before', 'pulseaction_notification'), - notification::DELAYAFTER => get_string('after', 'pulseaction_notification'), - ]; - $mform->addElement('select', 'pulsenotification_notifydelay', - get_string('delay', 'pulseaction_notification'), $delayoptions); - $mform->setDefault('delay', 'none'); - $mform->addHelpButton('pulsenotification_notifydelay', 'delay', 'pulseaction_notification'); - - // Delay duration. - $mform->addElement('duration', 'pulsenotification_delayduration', get_string('delayduraion', 'pulseaction_notification')); - $mform->hideIf('pulsenotification_delayduration', 'pulsenotification_notifydelay', 'eq', notification::DELAYNONE); - $mform->addHelpButton('pulsenotification_delayduration', 'delayduraion', 'pulseaction_notification'); - - // Limit no of notifications Group. - $mform->addElement('text', 'pulsenotification_notifylimit', get_string('limit', 'pulseaction_notification')); - $mform->setType('pulsenotification_notifylimit', PARAM_INT); - $mform->addHelpButton('pulsenotification_notifylimit', 'limit', 'pulseaction_notification'); - $mform->hideIf('pulsenotification_notifylimit', 'pulsenotification_notifyinterval[interval]', - 'eq', notification::INTERVALONCE); - - // Recipients Group. - // Add 'recipients' element with all roles that can receive notifications. - $roles = get_roles_with_capability('pulseaction/notification:receivenotification'); - $rolenames = role_fix_names($roles); - $roleoptions = array_combine(array_column($rolenames, 'id'), array_column($rolenames, 'localname')); - $mform->addElement('autocomplete', 'pulsenotification_recipients', - get_string('recipients', 'pulseaction_notification'), $roleoptions, ['multiple' => 'multiple']); - $mform->addHelpButton('pulsenotification_recipients', 'recipients', 'pulseaction_notification'); - - // CC Group. - // Add 'cc' element with all course context and user context roles. - $courseroles = $forminstance->course_roles(); - $mform->addElement('autocomplete', 'pulsenotification_cc', - get_string('ccrecipients', 'pulseaction_notification'), $courseroles, ['multiple' => 'multiple']); - $mform->addHelpButton('pulsenotification_cc', 'ccrecipients', 'pulseaction_notification'); - - // Set BCC. - $mform->addElement('autocomplete', 'pulsenotification_bcc', - get_string('bccrecipients', 'pulseaction_notification'), $courseroles, ['multiple' => 'multiple']); - $mform->addHelpButton('pulsenotification_bcc', 'bccrecipients', 'pulseaction_notification'); - - // Subject. - $mform->addElement('text', 'pulsenotification_subject', - get_string('subject', 'pulseaction_notification'), ['size' => 100]); - $mform->setType('pulsenotification_subject', PARAM_TEXT); - $mform->addHelpButton('pulsenotification_subject', 'subject', 'pulseaction_notification'); - - $context = \context_system::instance(); - $mform->addElement('editor', 'pulsenotification_headercontent_editor', - get_string('headercontent', 'pulseaction_notification'), - ['class' => 'fitem_id_templatevars_editor'], - $this->get_editor_options($context) - ); - $mform->addHelpButton('pulsenotification_headercontent_editor', 'headercontent', 'pulseaction_notification'); - $placeholders = pulse_email_placeholders('header', true); - $mform->addElement('html', $placeholders); - - // Statecontent editor. - $mform->addElement('editor', 'pulsenotification_staticcontent_editor', - get_string('staticcontent', 'pulseaction_notification'), - ['class' => 'fitem_id_templatevars_editor'], - $this->get_editor_options($context) - ); - $mform->addHelpButton('pulsenotification_staticcontent_editor', 'staticcontent', 'pulseaction_notification'); - $placeholders = pulse_email_placeholders('static', true); - $mform->addElement('html', $placeholders); - - // Footer Content. - $mform->addElement('editor', 'pulsenotification_footercontent_editor', - get_string('footercontent', 'pulseaction_notification'), - ['class' => 'fitem_id_templatevars_editor'], $this->get_editor_options($context)); - $mform->addHelpButton('pulsenotification_footercontent_editor', 'footercontent', 'pulseaction_notification'); - $placeholders = pulse_email_placeholders('footer', true); - $mform->addElement('html', $placeholders); - - // Preview Button. - $test = $mform->addElement('button', 'pulsenotification_preview', get_string('preview', 'pulseaction_notification')); - $mform->addHelpButton('pulsenotification_preview', 'preview', 'pulseaction_notification'); - - // Email tempalte placholders. - $contextid = $PAGE->context->id; - $PAGE->requires->js_call_amd('pulseaction_notification/chaptersource', 'previewNotification', ['contextid' => $contextid]); - } - - /** - * Get list of options in 30 mins timeinterval for 24 hrs. - * - * @return array - */ - public function get_times() { - - $starttime = new DateTime('00:00'); // Set the start time to midnight. - $endtime = new DateTime('23:59'); // Set the end time to just before midnight of the next day. - - // Create an interval of 30 minutes. - $interval = new \DateInterval('PT30M'); // PT30M represents 30 minutes. - - // Create a DatePeriod to iterate through the day with the specified interval. - $timeperiod = new DatePeriod($starttime, $interval, $endtime); - - // Loop through the DatePeriod and add each timestamp to the array. - $timelist = []; - foreach ($timeperiod as $time) { - $timelist[$time->format('H:i')] = $time->format('H:i'); // Format the timestamp as HH:MM. - } - - return $timelist; - } - - /** - * Get a email placeholders data fields. - * - * @return array - */ - public function get_email_placeholders() { - - $result = [ - 'Assignment' => $this->assignment_data_fields(), - ]; - - return $result; - } - - /** - * Assignment extension data fields. - * - * @return array - */ - public function assignment_data_fields() { - return ['Assignment_Extensions']; - } - - /** - * Get the assignment extenstion placeholders values. - * - * @param int $courseid Course ID. - * @param int $userid User ID. - * @return array - */ - public function get_assignment_extension($courseid, $userid) { - global $DB, $CFG; - require_once($CFG->libdir . '/completionlib.php'); - - // Get only assignments from the course of the instance shall be included. - $assignments = $DB->get_records('assign', ['course' => $courseid]); - $extension = ''; - - // Get a course completeion. - $completion = new \completion_info(get_course($courseid)); - - foreach ($assignments as $assignment) { - // Get the assignments only not completed by the user. - $cmid = $DB->get_record('course_modules', ['module' => 1, 'instance' => $assignment->id]); - $cm = get_coursemodule_from_id('assign', $cmid->id, 0, false, MUST_EXIST); - $cminfo = \cm_info::create($cm, $userid); - $assigncompletion = $completion->get_data($cminfo, true, $userid); - - if ($assigncompletion->completionstate != COMPLETION_COMPLETE) { - // Get only assignments where the assignment submission deadline was extended. - if ($DB->record_exists('assign_user_flags', ['assignment' => $assignment->id, 'userid' => $userid])) { - $extensionassignments = $DB->get_records('assign_user_flags', ['assignment' => $assignment->id, - 'userid' => $userid, ]); - foreach ($extensionassignments as $extensionassign) { - if ($extensionassign->extensionduedate != 0) { - $extensionduedate = !empty($extensionassign->extensionduedate) ? - userdate($extensionassign->extensionduedate) : ''; - $extension .= $assignment->name.": ". $extensionduedate .'('. - get_string('previously', 'pulse').': '.userdate($assignment->duedate).')'."
"; - } - } - } - } - } - - return (object) [ - 'extensions' => (!empty($extension)) ? $extension : get_string('noextensiongranted', 'pulse'), - ]; - } -} diff --git a/actions/notification/classes/external.php b/actions/notification/classes/external.php deleted file mode 100644 index 50e1443..0000000 --- a/actions/notification/classes/external.php +++ /dev/null @@ -1,87 +0,0 @@ -. - -/** - * Notification pulse action external functions defined. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -namespace pulseaction_notification; - -use core_external\external_api; -use core_external\external_function_parameters; -use core_external\external_value; -use core_external\external_multiple_structure; -use core_external\external_single_structure; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->libdir.'/externallib.php'); - -/** - * Pulse Notification action external methods. - */ -class external extends \external_api { - - /** - * Get list of chapters for the book module function parameters. - * @return object type of the badge type. - */ - public static function get_chapters_parameters() { - return new \external_function_parameters( - ['mod' => new \external_value(PARAM_INT, 'Book module cmid ', VALUE_OPTIONAL)] - ); - } - - /** - * Get list of badges based on the requested type. - * - * @param string $mod ID of the course module. - * @return array $type List of badge types. - */ - public static function get_chapters($mod = null) { - global $CFG; - - if (isset($mod)) { - $cmid = $mod; - $chapters = \pulseaction_notification\notification::load_book_chapters($cmid); - foreach ($chapters as $chapterid => $chapter) { - $list[] = ['value' => $chapter->id, 'label' => $chapter->title]; - } - } - - return $list ?? []; - } - - /** - * Return chapters list data definition. - * - * @return array list of chapaters. - */ - public static function get_chapters_returns() { - return new \external_multiple_structure( - new \external_single_structure( - [ - 'value' => new \external_value(PARAM_INT, 'Chapter ID', VALUE_OPTIONAL), - 'label' => new \external_value(PARAM_TEXT, 'Chapter title', VALUE_OPTIONAL), - ] - ), '', VALUE_OPTIONAL - ); - } - -} diff --git a/actions/notification/classes/helper.php b/actions/notification/classes/helper.php deleted file mode 100644 index daa254d..0000000 --- a/actions/notification/classes/helper.php +++ /dev/null @@ -1,403 +0,0 @@ -. - -/** - * Pulse notification action helper - Contains methods to send pulse notifications to users. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace pulseaction_notification; - -use stdClass; -use core_user; -use cache; - -/** - * Pulse notification action helper to send messages to users. - */ -class helper { - - /** - * Send pulse notifications to the users. - * - * @param mixed $userto - * @param mixed $subject - * @param mixed $messageplain - * @param mixed $messagehtml - * @param mixed $pulse - * @param mixed $sender - * @param array $cc List of cc users. - * @param array $bcc List of bcc users. - * - * @return void - */ - public static function messagetouser($userto, $subject, $messageplain, $messagehtml, $pulse, $sender=true, $cc=[], $bcc=[]) { - global $CFG; - - require_once($CFG->dirroot.'/mod/pulse/lib.php'); - - $eventdata = new \core\message\message(); - $eventdata->name = 'mod_pulse'; - $eventdata->component = 'mod_pulse'; - $eventdata->courseid = $pulse->course; - $eventdata->modulename = 'pulse'; - $eventdata->userfrom = $sender ? $sender : core_user::get_support_user(); - $eventdata->userto = $userto; - $eventdata->subject = $subject; - $eventdata->fullmessage = $messageplain; - $eventdata->fullmessageformat = FORMAT_HTML; - $eventdata->fullmessagehtml = $messagehtml; - $eventdata->smallmessage = $subject; - $eventdata->customdata = ['cc' => $cc, 'bcc' => $bcc]; - if (self::message_send($eventdata)) { - pulse_mtrace( "Pulse send to the user."); - return true; - } else { - pulse_mtrace( "Failed - Pulse send to the user. -".fullname($userto), true); - return false; - } - } - - /** - * MODIFIED the core message_send method to send the message using pulse notification manager instead of core/mesage/manager. - * - * Called when a message provider wants to send a message. - * This functions checks the message recipient's message processor configuration then - * sends the message to the configured processors - * - * Required parameters of the $eventdata object: - * component string component name. must exist in message_providers - * name string message type name. must exist in message_providers - * userfrom object|int the user sending the message - * userto object|int the message recipient - * subject string the message subject - * fullmessage string the full message in a given format - * fullmessageformat int the format if the full message (FORMAT_MOODLE, FORMAT_HTML, ..) - * fullmessagehtml string the full version (the message processor will choose with one to use) - * smallmessage string the small version of the message - * - * Optional parameters of the $eventdata object: - * notification bool should the message be considered as a notification rather than a personal message - * contexturl string if this is a notification then you can specify a url to view the event. - * For example the forum post the user is being notified of. - * contexturlname string the display text for contexturl - * - * Note: processor failure will not reported as false return value in all scenarios, - * for example when it is called while a database transaction is open, - * earlier versions did not do it consistently either. - * - * @copyright 2008 Luis Rodrigues and Martin Dougiamas - * @category message - * @param \core\message\message $eventdata information about the message (component, userfrom, userto, ...) - * @return mixed the integer ID of the new message or false if there was a problem - */ - public static function message_send(\core\message\message $eventdata) { - global $CFG, $DB, $SITE; - - require_once($CFG->dirroot. '/lib/messagelib.php'); - - // New message ID to return. - $messageid = false; - - // Fetch default (site) preferences. - $defaultpreferences = get_message_output_default_preferences(); - $preferencebase = $eventdata->component.'_'.$eventdata->name; - - // If the message provider is disabled via preferences, then don't send the message. - if (!empty($defaultpreferences->{$preferencebase.'_disable'})) { - return $messageid; - } - - // By default a message is a notification. Only personal/private messages aren't notifications. - if (!isset($eventdata->notification)) { - $eventdata->notification = 1; - } - - if (!is_object($eventdata->userfrom)) { - $eventdata->userfrom = core_user::get_user($eventdata->userfrom); - } - if (!$eventdata->userfrom) { - debugging('Attempt to send msg from unknown user', DEBUG_NORMAL); - return false; - } - - // Legacy messages (FROM a single user TO a single user) must be converted into conversation messages. - // Then, these will be passed through the conversation messages code below. - if (!$eventdata->notification && !$eventdata->convid) { - // If messaging is disabled at the site level, then the 'instantmessage' provider is always disabled. - // Given this is the only 'message' type message provider, we can exit now if this is the case. - // Don't waste processing time trying to work out the other conversation member, - // If it's an individual conversation, just throw a generic debugging notice and return. - if (empty($CFG->messaging) || $eventdata->component !== 'moodle' || $eventdata->name !== 'instantmessage') { - debugging('Attempt to send msg from a provider '.$eventdata->component.'/'.$eventdata->name. - ' that is inactive or not allowed for the user id='.$eventdata->userto->id, DEBUG_NORMAL); - return false; - } - - if (!is_object($eventdata->userto)) { - $eventdata->userto = core_user::get_user($eventdata->userto); - } - if (!$eventdata->userto) { - debugging('Attempt to send msg to unknown user', DEBUG_NORMAL); - return false; - } - - // Verify all necessary data fields are present. - if (!isset($eventdata->userto->auth) || !isset($eventdata->userto->suspended) - || !isset($eventdata->userto->deleted) || !isset($eventdata->userto->emailstop)) { - - debugging('Necessary properties missing in userto object, fetching full record', DEBUG_DEVELOPER); - $eventdata->userto = core_user::get_user($eventdata->userto->id); - } - - $usertoisrealuser = (core_user::is_real_user($eventdata->userto->id) != false); - // If recipient is internal user (noreply user), and emailstop is set then don't send any msg. - if (!$usertoisrealuser && !empty($eventdata->userto->emailstop)) { - debugging('Attempt to send msg to internal (noreply) user', DEBUG_NORMAL); - return false; - } - - if ($eventdata->userfrom->id == $eventdata->userto->id) { - // It's a self conversation. - $conversation = \core_message\api::get_self_conversation($eventdata->userfrom->id); - if (empty($conversation)) { - $conversation = \core_message\api::create_conversation( - \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF, - [$eventdata->userfrom->id] - ); - } - } else { - if (!$conversationid = \core_message\api::get_conversation_between_users([$eventdata->userfrom->id, - $eventdata->userto->id, ])) { - // It's a private conversation between users. - $conversation = \core_message\api::create_conversation( - \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, - [ - $eventdata->userfrom->id, - $eventdata->userto->id, - ] - ); - } - } - // We either have found a conversation, or created one. - $conversationid = !empty($conversationid) ? $conversationid : $conversation->id; - $eventdata->convid = $conversationid; - } - - // This is a message directed to a conversation, not a specific user as was the way in legacy messaging. - // The above code has adapted the legacy messages into conversation messages. - // We must call send_message_to_conversation(), which handles per-member processor iteration and triggers - // a per-conversation event. - // All eventdata for messages should now have a convid, as we fixed this above. - if (!$eventdata->notification) { - - // Only one message will be saved to the DB. - $conversationid = $eventdata->convid; - $table = 'messages'; - $tabledata = new stdClass(); - $tabledata->courseid = $eventdata->courseid; - $tabledata->useridfrom = $eventdata->userfrom->id; - $tabledata->conversationid = $conversationid; - $tabledata->subject = $eventdata->subject; - $tabledata->fullmessage = $eventdata->fullmessage; - $tabledata->fullmessageformat = $eventdata->fullmessageformat; - $tabledata->fullmessagehtml = $eventdata->fullmessagehtml; - $tabledata->smallmessage = $eventdata->smallmessage; - $tabledata->timecreated = time(); - $tabledata->customdata = $eventdata->customdata; - - // The Trusted Content system. - // Texts created or uploaded by such users will be marked as trusted and will not be cleaned before display. - if (trusttext_active()) { - // Individual conversations are always in system context. - $messagecontext = \context_system::instance(); - // We need to know the type of conversation and the contextid if it is a group conversation. - if ($conv = $DB->get_record('message_conversations', ['id' => $conversationid], 'id, type, contextid')) { - if ($conv->type == \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP && $conv->contextid) { - $messagecontext = \context::instance_by_id($conv->contextid); - } - } - $tabledata->fullmessagetrust = trusttext_trusted($messagecontext); - } else { - $tabledata->fullmessagetrust = false; - } - - if ($messageid = message_handle_phpunit_redirection($eventdata, $table, $tabledata)) { - return $messageid; - } - - // Cache messages. - if (!empty($eventdata->convid)) { - // Cache the timecreated value of the last message in this conversation. - $cache = cache::make('core', 'message_time_last_message_between_users'); - $key = \core_message\helper::get_last_message_time_created_cache_key($eventdata->convid); - $cache->set($key, $tabledata->timecreated); - } - - // Store unread message just in case we get a fatal error any time later. - $tabledata->id = $DB->insert_record($table, $tabledata); - $eventdata->savedmessageid = $tabledata->id; - - return \core\message\manager::send_message_to_conversation($eventdata, $tabledata); - } - - // Else the message is a notification. - if (!is_object($eventdata->userto)) { - $eventdata->userto = core_user::get_user($eventdata->userto); - } - if (!$eventdata->userto) { - debugging('Attempt to send msg to unknown user', DEBUG_NORMAL); - return false; - } - - // If the provider's component is disabled or the user can't receive messages from it, don't send the message. - $isproviderallowed = false; - foreach (message_get_providers_for_user($eventdata->userto->id) as $provider) { - if ($provider->component === $eventdata->component && $provider->name === $eventdata->name) { - $isproviderallowed = true; - break; - } - } - if (!$isproviderallowed) { - debugging('Attempt to send msg from a provider '.$eventdata->component.'/'.$eventdata->name. - ' that is inactive or not allowed for the user id='.$eventdata->userto->id, DEBUG_NORMAL); - return false; - } - - // Verify all necessary data fields are present. - if (!isset($eventdata->userto->auth) || !isset($eventdata->userto->suspended) - || !isset($eventdata->userto->deleted) || !isset($eventdata->userto->emailstop)) { - - debugging('Necessary properties missing in userto object, fetching full record', DEBUG_DEVELOPER); - $eventdata->userto = core_user::get_user($eventdata->userto->id); - } - - $usertoisrealuser = (core_user::is_real_user($eventdata->userto->id) != false); - // If recipient is internal user (noreply user), and emailstop is set then don't send any msg. - if (!$usertoisrealuser && !empty($eventdata->userto->emailstop)) { - debugging('Attempt to send msg to internal (noreply) user', DEBUG_NORMAL); - return false; - } - - // Check if we are creating a notification or message. - $table = 'notifications'; - - $tabledata = new stdClass(); - $tabledata->useridfrom = $eventdata->userfrom->id; - $tabledata->useridto = $eventdata->userto->id; - $tabledata->subject = $eventdata->subject; - $tabledata->fullmessage = $eventdata->fullmessage; - $tabledata->fullmessageformat = $eventdata->fullmessageformat; - $tabledata->fullmessagehtml = $eventdata->fullmessagehtml; - $tabledata->smallmessage = $eventdata->smallmessage; - $tabledata->eventtype = $eventdata->name; - $tabledata->component = $eventdata->component; - $tabledata->timecreated = time(); - $tabledata->customdata = $eventdata->customdata; - if (!empty($eventdata->contexturl)) { - $tabledata->contexturl = (string)$eventdata->contexturl; - } else { - $tabledata->contexturl = null; - } - - if (!empty($eventdata->contexturlname)) { - $tabledata->contexturlname = (string)$eventdata->contexturlname; - } else { - $tabledata->contexturlname = null; - } - - if ($messageid = message_handle_phpunit_redirection($eventdata, $table, $tabledata)) { - return $messageid; - } - - // Fetch enabled processors. - $processors = get_message_processors(true); - - // Preset variables. - $processorlist = []; - // Fill in the array of processors to be used based on default and user preferences. - foreach ($processors as $processor) { - // Skip adding processors for internal user, if processor doesn't support sending message to internal user. - if (!$usertoisrealuser && !$processor->object->can_send_to_any_users()) { - continue; - } - - // First find out permissions. - $defaultlockedpreference = $processor->name . '_provider_' . $preferencebase . '_locked'; - $locked = false; - if (isset($defaultpreferences->{$defaultlockedpreference})) { - $locked = $defaultpreferences->{$defaultlockedpreference}; - } else { - // MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't. - // exist in the message_provider table (thus there is no default settings for them). - $preferrormsg = "Could not load preference $defaultlockedpreference. Make sure the component and name you supplied - to message_send() are valid."; - throw new \coding_exception($preferrormsg); - } - - $preferencename = 'message_provider_'.$preferencebase.'_enabled'; - $forced = false; - if ($locked && isset($defaultpreferences->{$preferencename})) { - $userpreference = $defaultpreferences->{$preferencename}; - $forced = in_array($processor->name, explode(',', $userpreference)); - } - - // Find out if user has configured this output. - // Some processors cannot function without settings from the user. - $userisconfigured = $processor->object->is_user_configured($eventdata->userto); - - // DEBUG: notify if we are forcing unconfigured output. - if ($forced && !$userisconfigured) { - debugging( - 'Attempt to force message delivery to user who has "'.$processor->name.'" output unconfigured', DEBUG_NORMAL); - } - - // Populate the list of processors we will be using. - if ($forced && $userisconfigured) { - // An admin is forcing users to use this message processor. Use this processor unconditionally. - $processorlist[] = $processor->name; - } else if (!$forced && !$locked && $userisconfigured && !$eventdata->userto->emailstop) { - // User has not disabled notifications. - // See if user set any notification preferences, otherwise use site default ones. - if ($userpreference = get_user_preferences($preferencename, null, $eventdata->userto)) { - if (in_array($processor->name, explode(',', $userpreference))) { - $processorlist[] = $processor->name; - } - } else if (isset($defaultpreferences->{$preferencename})) { - if (in_array($processor->name, explode(',', $defaultpreferences->{$preferencename}))) { - $processorlist[] = $processor->name; - } - } - } - } - - // Store unread message just in case we get a fatal error any time later. - $tabledata->id = $DB->insert_record($table, $tabledata); - $eventdata->savedmessageid = $tabledata->id; - - // Let the manager do the sending or buffering when db transaction in progress. - try { - // PULSE MODIFY - Send the message using pulse notification manager instead of core/mesage/manager. - return \pulseaction_notification\manager::send_message($eventdata, $tabledata, $processorlist); - } catch (\moodle_exception $exception) { - return false; - } - } - -} diff --git a/actions/notification/classes/local/entities/notification.php b/actions/notification/classes/local/entities/notification.php deleted file mode 100644 index c053f37..0000000 --- a/actions/notification/classes/local/entities/notification.php +++ /dev/null @@ -1,371 +0,0 @@ -. - -/** - * Pulse notification entities for report builder. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -namespace pulseaction_notification\local\entities; - -use core_reportbuilder\local\entities\base; -use core_reportbuilder\local\report\{column, filter}; -use core_reportbuilder\local\filters\{date, number, select, text}; -use core_reportbuilder\local\helpers\format; -use html_writer; -use pulseaction_notification\notification as pulsenotification; -use lang_string; - -/** - * Pulse notification entity base for report source. - */ -class notification extends base { - - /** - * Database tables that this entity uses and their default aliases - * - * @return array - */ - protected function get_default_table_aliases(): array { - - return [ - 'user' => 'plnu', - 'context' => 'plnctx', - 'course' => 'plnc', - 'pulseaction_notification_sch' => 'plnsch', - 'pulse_autoinstances' => 'plni', - 'pulse_autotemplates' => 'plnt', - 'pulse_autotemplates_ins' => 'plnti', - 'pulseaction_notification_ins' => 'plani', - 'pulseaction_notification' => 'plan', - 'cohort_members' => 'chtm', - 'cohort' => 'cht', - ]; - } - - /** - * The default title for this entity - * - * @return lang_string - */ - protected function get_default_entity_title(): lang_string { - return new lang_string('notificationreport', 'pulseaction_notification'); - } - - /** - * Initialise the notification datasource columns and filter, conditions. - * - * @return base - */ - public function initialise(): base { - - $columns = $this->get_all_columns(); - foreach ($columns as $column) { - $this->add_column($column); - } - - list($filters, $conditions) = $this->get_all_filters(); - foreach ($filters as $filter) { - $this->add_filter($filter); - } - - foreach ($conditions as $condition) { - $this->add_condition($condition); - } - - return $this; - } - - /** - * List of columns available for this notfication datasource. - * - * @return array - */ - protected function get_all_columns(): array { - - $notificationschalias = $this->get_table_alias('pulseaction_notification_sch'); - $templatesalias = $this->get_table_alias('pulse_autotemplates'); - $templatesinsalias = $this->get_table_alias('pulse_autotemplates_ins'); - - $instancealias = $this->get_table_alias('pulse_autoinstances'); - $notificationalias = $this->get_table_alias('pulseaction_notification'); - - $notificationinsalias = $this->get_table_alias('pulseaction_notification_ins'); - - // Title of instance. - $columns[] = (new column( - 'title', - new lang_string('institle', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$templatesinsalias}.title", 'institle') - ->add_callback(static function ($value, $row): string { - $val = $row->institle ?: ''; - return format_string($val); - }); - - // Reference of instance. - $columns[] = (new column( - 'insreference', - new lang_string('insreference', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$templatesinsalias}.insreference") - ->add_callback(static function ($value, $row): string { - $val = $row->insreference ?: ''; - return format_string($val); - }); - - // Title of templates. - $columns[] = (new column( - 'templatetitle', - new lang_string('templatetitle', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$templatesalias}.title", 'templatetitle') - ->add_callback(static function ($value, $row): string { - $val = $row->templatetitle ?: ''; - return format_string($val); - }); - - // Reference of templates. - $columns[] = (new column( - 'tempreference', - new lang_string('tempreference', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$templatesalias}.reference", 'tempreference') - ->add_callback(static function ($value, $row): string { - $val = $row->tempreference ?: ''; - return format_string($val); - }); - - // Time the schedule is created. - $columns[] = (new column( - 'timecreated', - new lang_string('timecreated', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$notificationschalias}.timecreated") - ->add_callback(static function ($value, $row): string { - return userdate($value); - }); - - // Schedule time to send notification. - $columns[] = (new column( - 'scheduletime', - new lang_string('scheduledtime', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$notificationschalias}.scheduletime") - ->add_callback(static function ($value, $row): string { - return userdate($value); - }); - - // Instance id. - $columns[] = (new column( - 'instanceid', - new lang_string('instanceid', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$notificationschalias}.instanceid"); - - // Message type field. - $columns[] = (new column( - 'messagetype', - new lang_string('messagetype', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$templatesalias}.title", 'templatetitle') - ->add_field("{$templatesinsalias}.title", 'institle') - ->add_callback(function($val, $row) { - $val = $row->institle ?: $row->templatetitle; - return format_string($val); - }); - - // Subject field. - $columns[] = (new column( - 'subject', - new lang_string('subject', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$notificationinsalias}.subject", "instancesubject") - ->add_field("{$notificationalias}.subject", "templatesubject") - ->add_field("{$templatesinsalias}.instanceid") - ->add_field("{$notificationschalias}.userid") - ->add_callback([pulsenotification::class, 'get_schedule_subject']); - - // Status of the schedule. - $columns[] = (new column( - 'status', - new lang_string('status', 'pulseaction_notification'), - $this->get_entity_name() - )) - ->set_is_sortable(true) - ->add_field("{$notificationschalias}.status") - ->add_field("{$instancealias}.status", "instancestatus") - ->add_callback([pulsenotification::class, 'get_schedule_status']); - - return $columns; - } - - /** - * Defined filters for the notification entities. - * - * @return array - */ - protected function get_all_filters(): array { - global $DB; - - $notificationschalias = $this->get_table_alias('pulseaction_notification_sch'); - $templatesalias = $this->get_table_alias('pulse_autotemplates'); - $templatesinsalias = $this->get_table_alias('pulse_autotemplates_ins'); - - $instancealias = $this->get_table_alias('pulse_autoinstances'); - $notificationinsalias = $this->get_table_alias('pulseaction_notification_ins'); - - $cohortmembersalias = $this->get_table_alias('cohort_members'); - $cohortalias = $this->get_table_alias('cohort'); - - $useralias = $this->get_table_alias('user'); - - // Automation instance id. - $conditions[] = (new filter( - number::class, - 'instanceid', - new lang_string('instanceid', 'pulseaction_notification'), - $this->get_entity_name(), - "{$instancealias}.id" - )); - - // Automation instance. - $filters[] = (new filter( - text::class, - 'automationinstance', - new lang_string('automationinstance', 'pulseaction_notification'), - $this->get_entity_name(), - "{$templatesinsalias}.title" - )); - - // Automation template. - $filters[] = (new filter( - text::class, - 'automationtemplate', - new lang_string('automationtemplate', 'pulseaction_notification'), - $this->get_entity_name(), - "{$templatesalias}.title" - )); - - // Status of the schedule. - $filters[] = (new filter( - select::class, - 'status', - new lang_string('status', 'pulseaction_notification'), - $this->get_entity_name(), - "{$notificationschalias}.status", - ))->set_options([ - 1 => get_string('onhold', 'pulseaction_notification'), - 2 => get_string('queued', 'pulseaction_notification'), - 3 => get_string('sent', 'pulseaction_notification'), - 0 => get_string('failed', 'pulseaction_notification'), - ]); - - // Scheduled time date filter. - $filters[] = (new filter( - date::class, - 'timecreated', - new lang_string('schedulecreatedtime', 'pulseaction_notification'), - $this->get_entity_name(), - "{$notificationschalias}.timecreated" - )); - - // Filter by the schedule nextrun time. - $filters[] = (new filter( - date::class, - 'scheduletime', - new lang_string('scheduledtime', 'pulseaction_notification'), - $this->get_entity_name(), - "{$notificationschalias}.scheduletime" - )); - - // Cohort based filters. - $options = $DB->get_records_menu('cohort', [], '', 'id, name'); - $filters[] = (new filter( - select::class, - 'cohort', - new lang_string('cohort', 'cohort'), - $this->get_entity_name(), - "{$cohortalias}.id" - ))->add_join(" - JOIN {user} {$useralias} ON {$useralias}.id = {$notificationschalias}.userid - JOIN {cohort_members} {$cohortmembersalias} on {$cohortmembersalias}.userid = {$useralias}.id - JOIN {cohort} {$cohortalias} on {$cohortalias}.id = {$cohortmembersalias}.cohortid - ")->set_options($options); - - // Conditions. - $options = $DB->get_records_menu('cohort', [], '', 'id, name'); - $conditions[] = (new filter( - select::class, - 'cohort', - new lang_string('cohort', 'cohort'), - $this->get_entity_name(), - "{$cohortalias}.id" - ))->add_join(" - JOIN {user} {$useralias} ON {$useralias}.id = {$notificationschalias}.userid - JOIN {cohort_members} {$cohortmembersalias} on {$cohortmembersalias}.userid = {$useralias}.id - JOIN {cohort} {$cohortalias} on {$cohortalias}.id = {$cohortmembersalias}.cohortid - ")->set_options($options); - - return [$filters, $conditions]; - } - - /** - * Schedule join sql. - * - * @return string - */ - public function schedulejoin() { - - $notificationschalias = $this->get_table_alias('pulseaction_notification_sch'); - - $autoinstancesalias = $this->get_table_alias('pulse_autoinstances'); - $autotemplatesalias = $this->get_table_alias('pulse_autotemplates'); - $autotemplatesinsalias = $this->get_table_alias('pulse_autotemplates_ins'); - $notificationinsalias = $this->get_table_alias('pulseaction_notification_ins'); - $notificationalias = $this->get_table_alias('pulseaction_notification'); - - return " - JOIN {pulse_autoinstances} {$autoinstancesalias} ON {$autoinstancesalias}.id = {$notificationschalias}.instanceid - JOIN {pulse_autotemplates} {$autotemplatesalias} ON {$autotemplatesalias}.id = {$autoinstancesalias}.templateid - JOIN {pulse_autotemplates_ins} {$autotemplatesinsalias} - ON {$autotemplatesinsalias}.instanceid = {$autoinstancesalias}.id - JOIN {pulseaction_notification_ins} {$notificationinsalias} - ON {$notificationinsalias}.instanceid = {$notificationschalias}.instanceid - JOIN {pulseaction_notification} {$notificationalias} - ON {$notificationalias}.templateid = {$autoinstancesalias}.templateid"; - } -} diff --git a/actions/notification/classes/manager.php b/actions/notification/classes/manager.php deleted file mode 100644 index d66ce77..0000000 --- a/actions/notification/classes/manager.php +++ /dev/null @@ -1,205 +0,0 @@ -. - -/** - * Notification pulse action - Message manager. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace pulseaction_notification; - -use core\message\message; - -/** - * Helps to send messages from available message processesors, extends the moodle core\message\manager method. - */ -class manager extends \core\message\manager { - - /** - * Do the message sending - The method to use the custom call_processor method. - * - * NOTE: to be used from message_send() only. - * - * @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/} - * @author Petr Skoda - * - * @param \core\message\message $eventdata fully prepared event data for processors - * @param \stdClass $savemessage the message saved in 'message' table - * @param array $processorlist list of processors for target user - * @return int $messageid the id from 'messages' (false is not returned) - */ - public static function send_message(message $eventdata, \stdClass $savemessage, array $processorlist) { - global $CFG; - - require_once($CFG->dirroot.'/message/lib.php'); // This is most probably already included from messagelib.php file. - - if (empty($processorlist)) { - // Trigger event for sending a message or notification - we need to do this before marking as read! - self::trigger_message_events($eventdata, $savemessage); - - if ($eventdata->notification) { - // If they have deselected all processors and it's a notification mark it read. The user doesn't want to be - // bothered. - $savemessage->timeread = null; - \core_message\api::mark_notification_as_read($savemessage); - } else if (empty($CFG->messaging)) { - // If it's a message and messaging is disabled mark it read. - \core_message\api::mark_message_as_read($eventdata->userto->id, $savemessage); - } - - return $savemessage->id; - } - - // Let the manager do the sending or buffering when db transaction in progress. - return self::send_message_to_processors($eventdata, $savemessage, $processorlist); - } - - /** - * Send message to message processors - Inherit the method to use the custom call_processor method. - * - * @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/} - * @author Petr Skoda - * - * @param \stdClass|\core\message\message $eventdata - * @param \stdClass $savemessage - * @param array $processorlist - * @throws \moodle_exception - * @return int $messageid - */ - protected static function send_message_to_processors($eventdata, \stdClass $savemessage, array - $processorlist) { - global $CFG, $DB; - - // We cannot communicate with external systems in DB transactions, - // buffer the messages if necessary. - if ($DB->is_transaction_started()) { - // We need to clone all objects so that devs may not modify it from outside later. - $eventdata = clone($eventdata); - $eventdata->userto = clone($eventdata->userto); - $eventdata->userfrom = clone($eventdata->userfrom); - - // Conserve some memory the same was as $USER setup does. - unset($eventdata->userto->description); - unset($eventdata->userfrom->description); - - self::$buffer[] = [$eventdata, $savemessage, $processorlist]; - return $savemessage->id; - } - - // Send the message to processors. - if (!self::call_processors($eventdata, $processorlist)) { - throw new \moodle_exception("Message was not sent."); - } - - // Trigger event for sending a message or notification - we need to do this before marking as read! - self::trigger_message_events($eventdata, $savemessage); - - if (!$eventdata->notification && empty($CFG->messaging)) { - // If it's a message and messaging is disabled mark it read. - \core_message\api::mark_message_as_read($eventdata->userto->id, $savemessage); - } - - return $savemessage->id; - } - - /** - * For each processor, call it's send_message() method - * - This method is modified version of core\message\manager call_processors. - * - Modifed to use our custom email message procesessor instead of default message_output_email method. - * - By updating the email processor pulse includes the CC and Bcc emails. - * - * @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/} - * @author Petr Skoda - * - * @param message $eventdata the message object. - * @param array $processorlist the list of processors for a single user. - * @return bool false if error calling message processor - */ - protected static function call_processors(message $eventdata, array $processorlist) { - // Allow plugins to change the message/notification data before sending it. - $pluginsfunction = get_plugins_with_function('pre_processor_message_send'); - $sendmsgsuccessful = true; - foreach ($processorlist as $procname) { - // Let new messaging class add custom content based on the processor. - $proceventdata = ($eventdata instanceof message) ? $eventdata->get_eventobject_for_processor($procname) : $eventdata; - - if ($pluginsfunction) { - foreach ($pluginsfunction as $plugintype => $plugins) { - foreach ($plugins as $pluginfunction) { - $pluginfunction($procname, $proceventdata); - } - } - } - - $stdproc = new \stdClass(); - $stdproc->name = $procname; - - // Call the pulse email process instead of message_email_output. - $processor = ($procname == 'email') - ? self::get_processed_processor_object($stdproc) : \core_message\api::get_processed_processor_object($stdproc); - if (!$processor->object->send_message($proceventdata)) { - debugging('Error calling message processor ' . $procname); - $sendmsgsuccessful = false; - } - } - return $sendmsgsuccessful; - } - - /** - * Modified version of \core_message\api::get_processed_processor_object. - * - Fetch the pulseaction_notification_email processor. This helps to use pulse custom email processor. - * - * Given a processor object, loads information about it's settings and configurations. - * This is not a public api, instead use {@see \core_message\api::get_message_processor()} - * or {@see \get_message_processors()} - * - * @copyright 2016 Mark Nelson - * - * @param \stdClass $processor processor object - * @return \stdClass processed processor object - * @since Moodle 3.2 - */ - public static function get_processed_processor_object(\stdClass $processor) { - global $CFG; - - $processorfile = $CFG->dirroot. '/mod/pulse/actions/notification/pulseaction_notification_email.php'; - if (is_readable($processorfile)) { - include_once($processorfile); - $processclass = 'pulseaction_notification_email'; - if (class_exists($processclass)) { - $pclass = new $processclass(); - $processor->object = $pclass; - $processor->configured = 0; - if ($pclass->is_system_configured()) { - $processor->configured = 1; - } - $processor->hassettings = 0; - if (is_readable($CFG->dirroot.'/message/output/'.$processor->name.'/settings.php')) { - $processor->hassettings = 1; - } - $processor->available = 1; - } else { - throw new \moodle_exception('errorcallingprocessor', 'message'); - } - } else { - $processor->available = 0; - } - return $processor; - } -} diff --git a/actions/notification/classes/notification.php b/actions/notification/classes/notification.php deleted file mode 100644 index 1a5d9be..0000000 --- a/actions/notification/classes/notification.php +++ /dev/null @@ -1,1100 +0,0 @@ -. - -/** - * Notification pulse action - Create and Manage notifications. - * - * This Controller create a schedule for users, verify their availability based on conditions. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace pulseaction_notification; - -use book; -use DateTime; -use stdClass; -use moodle_url; -use html_writer; -use pulseaction_notification\task\notify_users; -use mod_pulse\automation\helper; -use mod_pulse\automation\instances; -use mod_pulse\helper as pulsehelper; -use mod_pulse\plugininfo\pulseaction; -use tool_dataprivacy\form\context_instance; - -/** - * Notification helper, Manage user schedules CRUD. - */ -class notification { - - /** - * Represents a course teacher as type of notification sender. - * @var int - */ - const SENDERCOURSETEACHER = 1; - - /** - * Represents a group teacher as type of notification sender. - * @var int - */ - const SENDERGROUPTEACHER = 2; - - /** - * Represents a tenent role as type of notification sender. - * @var int - */ - const SENDERTENANTROLE = 3; - - /** - * Represents a custom email as type of notification sender. - * @var int - */ - const SENDERCUSTOM = 4; - - /** - * Represents a notification interval is once. - * @var int - */ - const INTERVALONCE = 1; // Once. - - /** - * Represents a notification interval is Daily. - * @var int - */ - const INTERVALDAILY = 2; // Daily. - - /** - * Represents a notification interval is weekly. - * @var int - */ - const INTERVALWEEKLY = 3; // Weekly. - - /** - * Represents a notification interval is monthly. - * @var int - */ - const INTERVALMONTHLY = 4; // Montly. - - /** - * Represents there is no delay to send the notification. - * @var int - */ - const DELAYNONE = 0; - - /** - * Represents a delay before send the notifications. - * @var int - */ - const DELAYBEFORE = 1; - - /** - * Represents a delay after some time to send the notifications. - * @var int - */ - const DELAYAFTER = 2; - - /** - * Represents a length of the dynamic content is teaser. - * @var int - */ - const LENGTH_TEASER = 1; - - /** - * Represents the dynamic content included the link to access the desired module. - * @var int - */ - const LENGTH_LINKED = 2; - - /** - * Represents there is not links included with dynamic content. - * @var int - */ - const LENGTH_NOTLINKED = 3; - - /** - * Represents the content of the dynamic module is only for placeholder. - * @var int - */ - const DYNAMIC_PLACEHOLDER = 0; - - /** - * Represents the description of the dynamic module is included in the notification. - * @var int - */ - const DYNAMIC_DESCRIPTION = 1; - - /** - * Represents the content of the dynamic module is included in the notification. - * @var int - */ - const DYNAMIC_CONTENT = 2; - - /** - * Represents the notification schedule status is failed. - * @var int - */ - const STATUS_FAILED = 0; - - /** - * Represents the notification schedule status is disabled. - * @var int - */ - const STATUS_DISABLED = 1; - - /** - * Represents the notification schedule status is queued. - * @var int - */ - const STATUS_QUEUED = 2; - - /** - * Represents the notification schedule status is sent. - * @var int - */ - const STATUS_SENT = 3; - - /** - * Represents the user completed the suppress module. - * @var int - */ - const SUPPRESSREACHED = 1; - - /** - * The record of the notification instance with templates and general conditions. - * - * @var stdclass - */ - protected $instancedata; - - /** - * The merged notification data based on instance overrides. - * - * @var stdclass - */ - protected $notificationdata; - - /** - * The ID of the action notification table. - * @var int - */ - protected $notificationid; // Notification table id. - - /** - * Create the instance of the notification controller. - * - * @param int $notificationid Notification instance record id (pulseaction_notification_ins) NOT autoinstanceid. - * @return notification - */ - public static function instance($notificationid) { - static $instance; - - if (!$instance || ($instance && $instance->notificationid != $notificationid)) { - $instance = new self($notificationid); - } - - return $instance; - } - - /** - * Contructor for this notification controller. - * - * @param int $notificationid Notification table id. - */ - protected function __construct(int $notificationid) { - $this->notificationid = $notificationid; - } - - /** - * Create the notification instance and set the data to this class. - * - * @return void - */ - protected function create_instance_data() { - global $DB; - - if (empty($this->notificationid)) { - return []; - } - - $notification = $DB->get_record('pulseaction_notification_ins', ['id' => $this->notificationid]); - - if (empty($notification)) { - throw new \moodle_exception('notificationinstancenotfound', 'pulse'); - } - $instance = instances::create($notification->instanceid); - $autoinstance = $instance->get_instance_data(); - - $notificationdata = $autoinstance->actions['notification']; - - unset($autoinstance->actions['notification']); // Remove actions. - - $this->set_notification_data($notificationdata, $autoinstance); - } - - /** - * Set the notification data to global. Decode and do other structure updates for the data before setup. - * - * @param stdclass $notificationdata Contains notification data. - * @param stdclass $instancedata Contains other than actions. - * @return void - */ - public function set_notification_data($notificationdata, $instancedata) { - // Set the notification data. - $notificationdata = (object) $notificationdata; - $this->notificationdata = $this->update_data_structure($notificationdata); - - // Set the instance data. - $instancedata = (object) $instancedata; - // Instance not contains course then include course. - if (!isset($instancedata->course)) { - $instancedata->course = get_course($instancedata->courseid); - } - $this->instancedata = $instancedata; - } - - /** - * Decode the encoded json values to array, to further uses. - * - * @param stdclass $actiondata - * @return stdclass $actiondata Updated action data. - */ - public function update_data_structure($actiondata) { - - $actiondata->recipients = is_array($actiondata->recipients) - ? $actiondata->recipients : json_decode($actiondata->recipients); - - $actiondata->bcc = is_array($actiondata->bcc) ? $actiondata->bcc : json_decode($actiondata->bcc); - $actiondata->cc = is_array($actiondata->cc) ? $actiondata->cc : json_decode($actiondata->cc); - - $actiondata->notifyinterval = is_array($actiondata->notifyinterval) - ? $actiondata->notifyinterval : json_decode($actiondata->notifyinterval, true); - - return $actiondata; - } - - /** - * Generate the data set for the user to create schedule for this instance. - * - * @param int $userid ID of the user to create schedule. - * @return array $record Record to insert into schdeule. - */ - protected function generate_schedule_record(int $userid) { - - $record = [ - 'instanceid' => $this->notificationdata->instanceid, - 'userid' => $userid, - 'type' => $this->notificationdata->notifyinterval['interval'], - 'status' => self::STATUS_QUEUED, - 'timecreated' => time(), - 'timemodified' => time(), - ]; - return $record; - } - - /** - * Insert the schedule to database, verify if the schedule is already in queue then override the schedule with given record. - * - * @param stdclass $data - * @param bool $newschedule - * @return int Inserted schedule ID. - */ - protected function insert_schedule($data, $newschedule=false) { - global $DB; - - $sql = 'SELECT * FROM {pulseaction_notification_sch} - WHERE instanceid = :instanceid AND userid = :userid AND (status = :disabledstatus OR status = :queued)'; - - if ($record = $DB->get_record_sql($sql, [ - 'instanceid' => $data['instanceid'], 'userid' => $data['userid'], 'disabledstatus' => self::STATUS_DISABLED, - 'queued' => self::STATUS_QUEUED, - ])) { - - $data['id'] = $record->id; - // Update the status to enable for notify. - $DB->update_record('pulseaction_notification_sch', $data); - - return $record->id; - } - - // Dont create new schedule for already notified users until is not new schedule. - // It prevents creating new record for user during the update of instance interval. - if (!$newschedule && $DB->record_exists('pulseaction_notification_sch', [ - 'instanceid' => $data['instanceid'], 'userid' => $data['userid'], 'status' => self::STATUS_SENT, - ])) { - return false; - } - - return $DB->insert_record('pulseaction_notification_sch', $data); - } - - /** - * Disable the queued schdule of the given user. - * - * @param int $userid - * @return void - */ - protected function disable_user_schedule($userid) { - global $DB; - - $sql = "SELECT * FROM {pulseaction_notification_sch} - WHERE instanceid = :instanceid AND userid = :userid AND (status = :disabledstatus OR status = :queued)"; - - $params = [ - 'instanceid' => $this->notificationdata->instanceid, 'userid' => $userid, 'disabledstatus' => self::STATUS_DISABLED, - 'queued' => self::STATUS_QUEUED, - ]; - - if ($record = $DB->get_record_sql($sql, $params)) { - $DB->set_field('pulseaction_notification_sch', 'status', self::STATUS_DISABLED, ['id' => $record->id]); - } - } - - /** - * Remove the queued and disabled schedules of this user. - * - * @param int $userid - * @return void - */ - public function remove_user_schedules($userid) { - global $DB; - - $sql = "SELECT * FROM {pulseaction_notification_sch} - WHERE instanceid = :instanceid AND userid = :userid AND (status = :disabledstatus OR status = :queued)"; - - $params = [ - 'instanceid' => $this->notificationdata->instanceid, 'userid' => $userid, 'disabledstatus' => self::STATUS_DISABLED, - 'queued' => self::STATUS_QUEUED, - ]; - - if ($record = $DB->get_record_sql($sql, $params)) { - $DB->delete_records('pulseaction_notification_sch', ['id' => $record->id]); - } - } - - /** - * Get the current schedule created for the user related to specific instance. - * - * @param stdclass $data Data with instance id and user id. - * @return stdclass|null Record of the current schedule. - */ - protected function get_schedule($data) { - global $DB; - - if ($record = $DB->get_record('pulseaction_notification_sch', [ - 'instanceid' => $data->instanceid, 'userid' => $data->userid, - ])) { - return $record; - } - - return false; - } - - /** - * Find the sent time of the last schedule to the user for the specific instance. - * - * @param int $userid - * @return int|null Time of the last schedule notified to the user for the specific instance - */ - protected function find_last_notifiedtime($userid) { - global $DB; - - $id = $this->notificationdata->instanceid; - - // Get last notified schedule for this instance to the user. - $condition = ['instanceid' => $id, 'userid' => $userid, 'status' => self::STATUS_SENT]; - $records = $DB->get_records('pulseaction_notification_sch', $condition, 'id DESC', '*', 0, 1); - - return !empty($records) ? current($records)->notifiedtime : ''; - } - - /** - * Find a count of the schedules sent to the user for the current notification instance. - * - * @param int $userid ID of the user to fetch the counts - * @return int|null Count of the schedules sent to the user - */ - protected function find_notify_count($userid) { - global $DB; - - $id = $this->notificationdata->instanceid; - - // Get last notified schedule for this instance to the user. - $condition = ['instanceid' => $id, 'userid' => $userid, 'status' => self::STATUS_SENT]; - $records = $DB->get_records('pulseaction_notification_sch', $condition, 'id DESC', '*', 0, 1); - - return !empty($records) ? current($records)->notifycount : ''; - } - - /** - * Verify the user is already notified for this instance. It verify the lastrun is empty for the user record. - * - * Note: Use this method to verify the instance with interval once. - * - * @param int $userid - * @return bool - */ - protected function is_user_notified(int $userid) { - global $DB; - - $id = $this->notificationdata->instanceid; - $condition = ['instanceid' => $id, 'userid' => $userid, 'status' => self::STATUS_SENT]; - if ($record = $DB->get_record('pulseaction_notification_sch', $condition)) { - return $record->notifiedtime != null ? true : false; - } - return false; - } - - /** - * Remove the schdeduled notifications for this instance. - * - * @param int $status - * @return void - */ - protected function remove_schedules($status=self::STATUS_SENT) { - global $DB; - - $DB->delete_records('pulseaction_notification_sch', ['instanceid' => $this->instancedata->id, 'status' => $status]); - } - - /** - * Disable the queued schdule for all users. - * - * @return void - */ - protected function disable_schedules() { - global $DB; - - if (!empty($this->notificationdata)) { - $this->create_instance_data(); - } - - $params = [ - 'instanceid' => $this->notificationdata->instanceid, - 'status' => self::STATUS_QUEUED, - ]; - - // Disable the queued schedules for this instance. - $DB->set_field('pulseaction_notification_sch', 'status', self::STATUS_DISABLED, $params); - - } - - /** - * Removes the current queued schedules and recreate the schedule for all the qualified users. - * - * @return void - */ - public function recreate_schedule_forinstance() { - // Remove the current queued schedules. - $this->create_instance_data(); - - $this->remove_schedules(self::STATUS_QUEUED); - // Create the schedules for all users. - $this->create_schedule_forinstance(); - } - - /** - * Create schdule for the instance. - * - * @param bool $newenrolment Is the schedule for new enrolments. - * @return void - */ - public function create_schedule_forinstance($newenrolment=false) { - // Generate the notification instance data. - if (empty($this->instancedata)) { - $this->create_instance_data(); - } - - // Confirm the instance is not disabled. - if (!$this->instancedata->status) { - $this->disable_schedules(); - return false; - } - - // Course context. - $context = \context_course::instance($this->instancedata->courseid); - // Roles to receive the notifications. - $roles = $this->notificationdata->recipients; - if (empty($roles)) { - // No roles are defined to recieve notifications. Remove the schedules for this instance. - $this->remove_schedules(); - return true; // No roles are defined to recieve notifications. Break the schedule creation. - } - // Get the users for this receipents roles. - $users = $this->get_users_withroles($roles, $context); - foreach ($users as $userid => $user) { - $suppressreached = notify_users::is_suppress_reached( - $this->notificationdata, $user->id, $this->instancedata->course, null); - if ($suppressreached) { - continue; - } - $this->create_schedule_foruser($user->id, null, 0, null, $newenrolment); - } - - return true; - } - - /** - * Verfiy the current instance configured any conditions. - * - * @return bool if configured any conditions return true otherwise returns flase. - */ - protected function verfiy_instance_contains_condition() { - - if (!isset($this->instancedata->condition)) { - return false; - } - - // Verify the instance contains any enabled conditions. - foreach ($this->instancedata->condition as $condition => $values) { - if ($values['status']) { - return true; - } - } - - return false; - } - - /** - * Create schedule for the user. - * - * @param int $userid - * @param string $lastrun - * @param integer $notifycount - * @param int $expectedruntime Timestamp of the time to run. - * @param bool $isnewuser - * @param bool $newschedule - * @return int ID of the created schedule. - */ - public function create_schedule_foruser($userid, $lastrun='', $notifycount=0, $expectedruntime=null, - $isnewuser=false, $newschedule=false) { - - if (empty($this->instancedata)) { - $this->create_instance_data(); - } - - // Instance should be configured with any of conditions. Otherwise stop creating instance (PLS-637). - // Verify the user passed the instance condition. - if (!$this->verfiy_instance_contains_condition() || !instances::create($this->notificationdata->instanceid) - ->find_user_completion_conditions($this->instancedata->condition, $this->instancedata, $userid, $isnewuser)) { - // Remove the user condition. - $this->disable_user_schedule($userid); - return true; - } - - $notifycount = $notifycount ?: $this->find_notify_count($userid); - // Verify the Limit is reached, if 0 then its unlimited. - if ($this->notificationdata->notifylimit > 0 && ($notifycount >= $this->notificationdata->notifylimit)) { - return false; - } - - // Notification interval is once per user, it already notified to the user. break the trigger here. - if ($this->notificationdata->notifyinterval['interval'] == self::INTERVALONCE && $this->is_user_notified($userid)) { - return true; - } - - $lastrun = $lastrun ?: $this->find_last_notifiedtime($userid); - - // Generate the schedule record. - $data = $this->generate_schedule_record($userid); - - $data['notifycount'] = $notifycount; - // ...# Find the next run. - $nextrun = $this->generate_the_scheduletime($userid, $lastrun, $expectedruntime); - // Include the next run to schedule. - $data['scheduletime'] = $nextrun; - // Insert the new schedule or update schedule. - $scheduleid = $this->insert_schedule($data, $newschedule); - - return $scheduleid; - } - - /** - * Generate the schedule time for this notification. - * - * @param int $userid - * @param int $lastrun - * @param int $expectedruntime - * @return int - */ - protected function generate_the_scheduletime($userid, $lastrun=null, $expectedruntime=null) { - global $DB; - - $data = $this->notificationdata; - $data->userid = $userid; - - $now = new DateTime('now', \core_date::get_server_timezone_object()); - - if ($expectedruntime) { - $expectedruntime = $now->setTimestamp($expectedruntime); - } - - $nextrun = $expectedruntime ?: $now; - if (!empty($lastrun)) { - $lastrun = ($lastrun instanceof DateTime) - ?: (new DateTime('now', \core_date::get_server_timezone_object()))->setTimestamp($lastrun); - $nextrun = $lastrun; - } - - $interval = $data->notifyinterval['interval']; - - switch ($interval) { - - case self::INTERVALDAILY: - $time = $data->notifyinterval['time']; - $nextrun->modify('+1 day'); // ...TODO: Change this to Dateinterval(). - $timeex = explode(':', $time); - $nextrun->setTime(...$timeex); - break; - - case self::INTERVALWEEKLY: - $day = $data->notifyinterval['weekday']; - $time = $data->notifyinterval['time']; - $nextrun->modify("Next ".$day); - $timeex = explode(':', $time); - $nextrun->setTime(...$timeex); - break; - - case self::INTERVALMONTHLY: - $monthdate = $data->notifyinterval['monthdate']; - if ($monthdate != 31) { // If the date is set as 31 then use the month end. - $nextrun->modify('first day of next month'); - $date = $data->notifyinterval['monthdate'] - ? $data->notifyinterval['monthdate'] - 1 : $data->notifyinterval['monthdate']; - $nextrun->modify("+$date day"); - } else { - $nextrun->modify('last day of next month'); - } - - $time = $data->notifyinterval['time'] ?? '0:00'; - $timeex = explode(':', $time); - $nextrun->setTime(...$timeex); - break; - - case self::INTERVALONCE: - $nextrun = $expectedruntime ?: $now; - break; - } - - // Add limit of available. - if ($data->notifydelay == self::DELAYAFTER) { - $delay = $data->delayduration; - $nextrun->modify("+ $delay seconds"); - } else if ($data->notifydelay == self::DELAYBEFORE) { - $delay = $data->delayduration; - - if ($expectedruntime) { - // SEssion condition only send the expected runtime. - // Reduce the delay directly from the expected runtime. - $nextrun->modify("- $delay seconds"); - - } else if (method_exists('\pulsecondition_session\conditionform', 'get_session_time')) { - // Confirm any f2f module added in condition. - $sessionstarttime = \pulsecondition_session\conditionform::get_session_time($data, $this->instancedata); - - if (!empty($sessionstarttime)) { - $nextrun->setTimestamp($sessionstarttime); - $nextrun->modify("- $delay seconds"); - } - } - } - - return $nextrun->getTimestamp(); - } - - /** - * Get the users assigned in the roles. - * - * @param array $roles Role ids to fetch - * @param \context $context - * @param int $childuserid - * @return array List of the users. - */ - protected function get_users_withroles(array $roles, $context, $childuserid=null) { - global $DB; - - // ...TODO: Cache the role users. - if (empty($roles)) { - return []; - } - - list($insql, $inparams) = $DB->get_in_or_equal($roles, SQL_PARAMS_NAMED, 'rle'); - - // ...TODO: Define user fields, never get entire fields. - $rolesql = "SELECT ra.id as assignid, u.*, ra.roleid FROM {role_assignments} ra - JOIN {user} u ON u.id = ra.userid - JOIN {role} r ON ra.roleid = r.id - LEFT JOIN {role_names} rn ON (rn.contextid = :ctxid AND rn.roleid = r.id) "; - - // Fetch the parent users related to the child user. - $childcontext = ''; - if ($childuserid) { - $rolesql .= " JOIN {context} uctx ON uctx.instanceid=:childuserid AND contextlevel=" . CONTEXT_USER . " "; - $childcontext = " OR ra.contextid = uctx.id "; - $inparams['childuserid'] = $childuserid; - } - - $rolesql .= " WHERE (ra.contextid = :ctxid2 $childcontext) AND ra.roleid $insql ORDER BY u.id"; - - $params = ['ctxid' => $context->id, 'ctxid2' => $context->id] + $inparams; - - $users = $DB->get_records_sql($rolesql, $params); - - return $users; - } - - /** - * Build the notification content. - * - * @param stdClass|null $cm Course module - * @param \context $context - * @param array $overrides - * - * @return string Notification content. - */ - public function build_notification_content(?stdClass $cm=null, $context=null, $overrides=[]) { - global $CFG, $DB; - - $syscontext = \context_system::instance(); - - $headercontent = $this->notificationdata->headercontent; - $staticcontent = $this->notificationdata->staticcontent; - $footercontent = $this->notificationdata->footercontent; - - // Rewrite the plugin url for files in editors. - foreach (['headercontent', 'staticcontent', 'footercontent'] as $editor) { - - $content = $$editor; - - $field = 'pulsenotification_'.$editor; - $prefix = (isset($overrides[$editor]) || isset($overrides[$field]) || isset($overrides[$field.'_editor'])) - ? '_instance' : ''; - $id = (isset($overrides[$editor]) || isset($overrides[$field]) || isset($overrides[$field.'_editor'])) - ? $this->notificationdata->instanceid : $this->instancedata->templateid; - - $$editor = file_rewrite_pluginfile_urls( - $content, 'pluginfile.php', $syscontext->id, - 'mod_pulse', $field.$prefix, $id - ); - } - - // Include the dynamic contents. - $dynamiccontent = $this->notificationdata->dynamiccontent; - if ($dynamiccontent) { - if ($cm == null) { - $module = get_coursemodule_from_id('', $dynamiccontent); - $cm = (object) [ - 'instance' => $module->instance, - 'modname' => $module->modname, - 'id' => $module->id, - ]; - } - - $modcontext = \context_module::instance($dynamiccontent); - - $staticcontent .= self::generate_dynamic_content( - $this->notificationdata->contenttype, - $this->notificationdata->contentlength, - $this->notificationdata->chapterid ?? 0, - $modcontext, - $cm - ); // Concat the dynamic content after static content. - } - - $finalcontent = $headercontent . $staticcontent . $footercontent; - - return format_text($finalcontent, FORMAT_HTML, ['noclean' => true, 'overflowdiv' => true]); - } - - /** - * Gernerate the content based on dynamic module to attach with the notification content. - * - * @param string $contenttype - * @param int $contentlength - * @param int $chapterid - * @param \context $context - * @param stdclass $cm - * - * @return string - */ - public static function generate_dynamic_content($contenttype, $contentlength, $chapterid, $context, $cm) { - global $CFG, $DB; - - // Include module libarary files. - require_once($CFG->dirroot.'/lib/modinfolib.php'); - require_once($CFG->dirroot.'/mod/book/lib.php'); - require_once($CFG->libdir.'/filelib.php'); - - // Content type is placholder, no need to include the content. - if ($contenttype == self::DYNAMIC_PLACEHOLDER) { - return ''; - } - - if ($contenttype == self::DYNAMIC_CONTENT && in_array($cm->modname, ['book', 'page'])) { - - if ($cm->modname == 'book') { - $chapter = $DB->get_record('book_chapters', ['id' => $chapterid, 'bookid' => $cm->instance]); - $chaptertext = \file_rewrite_pluginfile_urls( - $chapter->content, 'pluginfile.php', $context->id, 'mod_book', 'chapter', $chapter->id); - - $content = format_text($chaptertext, $chapter->contentformat, ['noclean' => true, 'overflowdiv' => true]); - $link = new moodle_url('/mod/book/view.php', ['id' => $cm->id, 'chapterid' => $chapterid]); - } else if ($cm->modname == 'page') { - $page = $DB->get_record('page', ['id' => $cm->instance], '*', MUST_EXIST); - - $content = file_rewrite_pluginfile_urls( - $page->content, 'pluginfile.php', $context->id, 'mod_page', 'content', $page->revision); - $link = new moodle_url('/mod/page/view.php', ['id' => $cm->id]); - } - - } else { - // ...TODO: Need to cache module intro content. - $activity = $DB->get_record("$cm->modname", ['id' => $cm->instance]); - $content = format_module_intro($cm->modname, $activity, $cm->id, true); - $link = new moodle_url("/mod/$cm->modname/view.php", ['id' => $cm->id]); - } - - // Verify the contnet length. - switch ($contentlength) { - - case self::LENGTH_TEASER: - preg_match('/

(.*?)<\/p>/s', $content, $match); - $content = $match[0] ?? $content; - - $content .= html_writer::link($link, get_string('readmore', 'pulseaction_notification')); - break; - - case self::LENGTH_LINKED: - $content .= html_writer::link($link, get_string('readmore', 'pulseaction_notification')); - break; - } - - return $content; - } - - /** - * Generate the details of the notification to send. - * - * @param stdclass $moddata - * @param stdclass $user - * @param \context $context - * @param array $notificationoverrides - * @return stdclass Basic details to send notification. - */ - public function generate_notification_details($moddata, $user, $context, $notificationoverrides=[]) { - global $USER; - - // Find the cc and bcc users for this schedule. - $roles = array_merge($this->notificationdata->cc, $this->notificationdata->bcc); - - // Get the users for this bcc and cc roles. - $roleusers = $this->get_users_withroles($roles, $context, $user->id); - - // Filter the cc users for this instance. - $cc = $this->notificationdata->cc; - $ccusers = array_filter($roleusers, function($value) use ($cc) { - return in_array($value->roleid, $cc); - }); - - // Filter the bcc users for this instance. - $bcc = $this->notificationdata->bcc; - $bccusers = array_filter($roleusers, function($value) use ($bcc) { - return in_array($value->roleid, $bcc); - }); - - // Set the recepient as session user for format content. - $olduser = $USER; - \core\session\manager::set_user($user); - - // Use the current user language to filter content. - if ($user->lang != current_language()) { - $oldforcelang = force_current_language($user->lang); - } - - $result = [ - 'recepient' => (object) $user, - 'cc' => array_map(fn($user) => [$user->email, fullname($user)], $ccusers), - 'bcc' => array_map(fn($user) => [$user->email, fullname($user)], $bccusers), - 'subject' => format_string($this->notificationdata->subject), - 'content' => $this->build_notification_content($moddata, $context, $notificationoverrides), - ]; - - // After format the message and subject return back to previous lang. - if (isset($oldforcelang)) { - force_current_language($oldforcelang); - unset($oldforcelang); - } - // Return to normal current session user. - \core\session\manager::set_user($olduser); - - return (object) $result; - } - - /** - * Get the tenant role sender user. - * - * @param stdclass $scheduledata - * @return stdclass - */ - public function get_tenantrole_sender($scheduledata) { - // ...TODO: Tenant based sender fetch goes here. - return (object) []; - } - - /** - * Find the sender users for the course context, fetched the teachers based on group assignment. - * - * @param \context $coursecontext - * @param int $groupid - * @return stdclass - */ - protected static function get_sender_users($coursecontext, $groupid) { - - $withcapability = 'pulseaction/notification:sender'; - $sender = get_enrolled_users( - $coursecontext, - $withcapability, - $groupid, - 'u.*', - null, - 0, - 1, - true - ); - - return !empty($sender) ? current($sender) : []; - } - - /** - * Load book chapters. - * - * @param int $cmid Course module id. - * @return array - */ - public static function load_book_chapters(int $cmid) { - global $DB; - $mod = get_coursemodule_from_id('', $cmid); - - $list = ''; - if ($mod->modname == 'book') { - $chapters = $DB->get_records('book_chapters', ['bookid' => $mod->instance]); - return $chapters; - } - return $list; - } - - /** - * Get the status of the schedule. - * - * @param int $value - * @param stdclass $row - * @return string - */ - public static function get_schedule_status($value, $row) { - - if ($value == self::STATUS_DISABLED) { - return get_string('onhold', 'pulseaction_notification'); - } else if ($value == self::STATUS_QUEUED) { - if (!$row->instancestatus) { - return get_string('onhold', 'pulseaction_notification'); - } - return get_string('queued', 'pulseaction_notification'); - } else if ($value == self::STATUS_SENT) { - return get_string('sent', 'pulseaction_notification'); - } else { - return get_string('failed', 'pulseaction_notification'); - } - } - - /** - * Get the schedule subject to display in the reports. - * - * @param string $value Subject - * @param stdclass $row - * @return string - */ - public static function get_schedule_subject($value, $row) { - global $DB; - - $value = $row->instancesubject ?: $row->templatesubject; // Use templates subject if instance subject doesn't overrides. - $sender = \core_user::get_support_user(); - $courseid = $DB->get_field('pulse_autoinstances', 'courseid', ['id' => $row->instanceid]); - $user = (object) \core_user::get_user($row->userid); - $course = get_course($courseid ?? SITEID); - - list($subject, $messagehtml) = \mod_pulse\helper::update_emailvars('', $value, $course, $user, null, $sender); - - return $subject . html_writer::link('javascript:void(0);', '', [ - 'class' => 'pulse-automation-info-block', - 'data-target' => 'view-content', - 'data-instanceid' => $row->instanceid, - 'data-userid' => $row->userid, - ]); - } - - /** - * Get the list of modules data for the placholders. includes the metadata fields. - * - * @param array $modules List of modules. - * @return array - */ - public static function get_modules_data($modules) { - global $DB, $CFG; - - if (file_exists($CFG->dirroot.'/local/metadata/lib.php')) { - require_once($CFG->dirroot.'/local/metadata/lib.php'); - } - - $list = []; - foreach ($modules as $modname => $instances) { - - $tablename = $DB->get_prefix().$modname; - list($insql, $inparams) = $DB->get_in_or_equal($instances, SQL_PARAMS_NAMED, 'md'); - - $sql = "SELECT md.*, cm.id as cmid FROM $tablename md - JOIN {modules} m ON m.name = '$modname' - JOIN {course_modules} cm ON cm.instance = md.id AND cm.module = m.id - WHERE md.id $insql"; - - $records = $DB->get_records_sql($sql, $inparams); - - foreach ($records as $modid => $mod) { - $mod->type = $modname; - - if (isset($list[$modname][$modid])) { - continue; - } - - if (function_exists('local_metadata_load_data')) { - $newmod = (object) ['id' => $mod->cmid]; - local_metadata_load_data($newmod, CONTEXT_MODULE); - unset($newmod->cmid); - foreach ($newmod as $name => $value) { - $key = str_replace('local_metadata_field_', 'metadata', $name); - $key = strtolower($key); - $mod->$key = $value; - } - } - - $list[$modname][$modid] = $mod; - } - } - - return $list; - } -} diff --git a/actions/notification/classes/reportbuilder/datasource/notification.php b/actions/notification/classes/reportbuilder/datasource/notification.php deleted file mode 100644 index c893775..0000000 --- a/actions/notification/classes/reportbuilder/datasource/notification.php +++ /dev/null @@ -1,137 +0,0 @@ -. - -/** - * Pulse notification datasource for the schedules. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace pulseaction_notification\reportbuilder\datasource; - -use core_reportbuilder\datasource; -use core_reportbuilder\local\entities\course; -use core_reportbuilder\local\entities\user; -use core_reportbuilder\local\filters\select; - -/** - * Notification datasource definition for the list of schedules. - */ -class notification extends datasource { - - /** - * Return user friendly name of the datasource - * - * @return string - */ - public static function get_name(): string { - return get_string('formtab', 'pulseaction_notification'); - } - - /** - * Initialise report - */ - protected function initialise(): void { - global $PAGE; - - $notificationentity = new \pulseaction_notification\local\entities\notification(); - $notificationalias = $notificationentity->get_table_alias('pulse_autoinstances'); - - $notificationschalias = $notificationentity->get_table_alias('pulseaction_notification_sch'); - $this->set_main_table('pulseaction_notification_sch', $notificationschalias); - $this->add_entity($notificationentity); - - // Force the join to be added so that course fields can be added first. - $this->add_join($notificationentity->schedulejoin()); - - // Add core user join. - $userentity = new user(); - $useralias = $userentity->get_table_alias('user'); - $userjoin = "JOIN {user} {$useralias} ON {$useralias}.id = {$notificationschalias}.userid"; - $this->add_entity($userentity->add_join($userjoin)); - - $coursentity = new course(); - $coursealias = $coursentity->get_table_alias('course'); - $coursejoin = "JOIN {course} {$coursealias} ON {$coursealias}.id = {$notificationalias}.courseid"; - $this->add_entity($coursentity->add_join($coursejoin)); - - if ($instance = optional_param('instanceid', null, PARAM_INT)) { - $this->add_base_condition_simple("{$notificationschalias}.instanceid", $instance); - } - - // Support for 4.2. - if (method_exists($this, 'add_all_from_entities')) { - $this->add_all_from_entities(); - } else { - // Add all the entities used in notification datasource. moodle 4.0 support. - $this->add_columns_from_entity($notificationentity->get_entity_name()); - $this->add_filters_from_entity($notificationentity->get_entity_name()); - $this->add_conditions_from_entity($notificationentity->get_entity_name()); - - $this->add_columns_from_entity($userentity->get_entity_name()); - $this->add_filters_from_entity($userentity->get_entity_name()); - $this->add_conditions_from_entity($userentity->get_entity_name()); - - $this->add_columns_from_entity($coursentity->get_entity_name()); - $this->add_filters_from_entity($coursentity->get_entity_name()); - $this->add_conditions_from_entity($coursentity->get_entity_name()); - } - - // Init the script to show the notification content in the modal. - $params = ['contextid' => \context_system::instance()->id]; - $PAGE->requires->js_call_amd('pulseaction_notification/chaptersource', 'reportModal', $params); - } - - /** - * Return the columns that will be added to the report once is created - * - * @return string[] - */ - public function get_default_columns(): array { - - return [ - 'course:fullname', - 'notification:messagetype', - 'notification:subject', - 'user:fullname', - 'notification:timecreated', - 'notification:scheduletime', - 'notification:status', - ]; - } - - /** - * Return the filters that will be added to the report once is created - * - * @return array - */ - public function get_default_filters(): array { - return []; - } - - /** - * Return the conditions that will be added to the report once is created - * - * @return array - */ - public function get_default_conditions(): array { - - return []; - } - -} diff --git a/actions/notification/classes/schedule.php b/actions/notification/classes/schedule.php deleted file mode 100644 index 6135aaa..0000000 --- a/actions/notification/classes/schedule.php +++ /dev/null @@ -1,575 +0,0 @@ -. - -/** - * Notification pulse action - Send the scheduled notifications. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace pulseaction_notification; - -use mod_pulse\automation\helper; -use mod_pulse\automation\instances; -use mod_pulse\helper as pulsehelper; -use stdClass; - -/** - * Notification pulse action - Send the scheduled notifications. - */ -class schedule { - - /** - * The user object of support user. - * @var stdclass - */ - protected $supportuser; - - /** - * The automation instance data. - * @var stdclass - */ - protected $instancedata; - - /** - * The record of notification schedule. - * @var stdclass - */ - protected $schedulerecord; - - /** - * The record of entire notifcation schedule row. - * @var stdclass - */ - protected $schedule; - - /** - * The course object. - * @var stdclass - */ - protected $course; - - /** - * The user data object. - * @var stdclass - */ - protected $user; - - /** - * Notification record. - * - * @var notification - */ - protected $notification; - - /** - * List of overrides in the notification action. - * - * @var array - */ - protected $notificationoverrides; - - /** - * Notification data processed with overrides. - * - * @var stdclass - */ - protected $notificationdata; - - /** - * Course context. - * - * @var \context_course - */ - protected $coursecontext; // Context_module. - - /** - * List of conditions enabled in this instance. - * - * @var array - */ - protected $conditions; - - /** - * Schedule constructor. - */ - public function __construct() { - // Support user. - $this->supportuser = \core_user::get_support_user(); - - } - - /** - * Create a instance of this schedule controller. - * - * @return self - */ - public static function instance() { - - $self = new self(); - return $self; - } - - /** - * Sends scheduled notifications to users. - * - * @param int|null $userid (Optional) The ID of the user. If provided, notifications will be sent only to this user. - * - * @return void - */ - public function send_scheduled_notification($userid=null) { - global $DB, $CFG; - - require_once($CFG->dirroot.'/mod/pulse/automation/automationlib.php'); - require_once($CFG->dirroot.'/user/profile/lib.php'); - require_once($CFG->dirroot.'/lib/moodlelib.php'); - - // Genreate the SQL to fetch the list of schedules to send notification. - // It fetch the list based on the limit. - $schedules = $this->get_scheduled_records($userid); - // Get the modules list for the dynamiccontent will be used in this schedule. - // Reduce the query to fetch same modules again. - $modules = $this->get_modules_dynamic_content($schedules); - - foreach ($schedules as $sid => $schedule) { - - // Separete the values from the record and create each as class level variable object. - $this->build_schedule_values($schedule); - - // Verify the notification instance limit of notification is reached for this user. - if (isset($schedule->notifiycount) && $schedule->notifiycount >= $this->notificationdata->notifylimit) { - continue; // Limit reached break this continue to next user. - } - - $cmdata = (object) [ - 'modname' => $schedule->md_name, // Module name Book or page. - 'instance' => $schedule->cm_instance, - 'id' => $schedule->cm_id, - ]; - // Generate the details to send the notification, it contains user, cc, bcc and schedule data. - $detail = $this->notification->generate_notification_details( - $cmdata, $this->user, $this->coursecontext, $this->notificationoverrides); - - $sender = $this->find_sender_user(); - - // Add bcc and CC to sender user custom headers. - $cc = $detail->cc ?? []; - $bcc = $detail->bcc ?? []; - - // Prepare the module data. based on dynamic content and includ the session data. - $mod = $this->prepare_moduledata_placeholders($modules, $cmdata); - - // Check the session condition are set for this notification. if its added then load the session data for placeholders. - $sessionin = in_array('session', (array) $this->instancedata->template->triggerconditions); - $sessionin = ($this->schedulerecord->con_isoverridden == 1) ? $this->schedulerecord->con_status : $sessionin; - if ($sessionin && $this->schedulerecord->con_additional) { - $sessionaddional = json_decode($this->schedulerecord->con_additional); - $sessionmodules = !empty($sessionaddional->modules) ? $sessionaddional->modules : []; - $sessionconditiondata = (object) ['modules' => $sessionmodules]; - $this->include_session_data($mod, $sessionconditiondata, $this->user->id); - } - - // Include the conditions vars for placeholder replace. - $plugins = \mod_pulse\plugininfo\pulsecondition::instance()->get_plugins_base(); - $conditionvars = []; - foreach ($plugins as $component => $pluginbase) { - $vars = $pluginbase->update_email_customvars($this->user->id, $this->instancedata, $this->schedulerecord); - $conditionvars += $vars ?: []; - } - - // Update the email placeholders. - list($subject, $messagehtml) = pulsehelper::update_emailvars( - $detail->content, $detail->subject, $this->course, $this->user, $mod, $sender, array_filter($conditionvars)); - - // Plain message. - $messageplain = html_to_text($messagehtml); - - // Courseid is needed in the message api. - $pulse = (object) ['course' => $this->course->id]; - // ...TODO: NOTE using notification API takes 16 queries. Direct email_to_user method will take totally 9 queries. - // Send the notification to user. - $messagesend = \pulseaction_notification\helper::messagetouser( - $detail->recepient, $subject, $messageplain, $messagehtml, $pulse, $sender, $cc, $bcc - ); - - if ($messagesend) { - // Update the current time as lastrun. - // Update the lastrun and increase the limit. - $notifiedtime = time(); - - $notifycount = $this->schedule->notifycount ? $this->schedule->notifycount + 1 : 1; - $update = [ - 'id' => $this->schedule->id, - 'notifycount' => $notifycount, - 'status' => notification::STATUS_SENT, - 'notifiedtime' => $notifiedtime, - ]; - - // Generate a next runtime. Only if user has limit to receive notifications. otherwise made the nextrun null. - // Update the schedule. - $DB->update_record('pulseaction_notification_sch', $update); - - if ($notifycount < $this->notificationdata->notifylimit - && $this->notificationdata->notifyinterval['interval'] != notification::INTERVALONCE) { - $newschedule = true; - $this->notification->create_schedule_foruser( - $this->schedule->userid, $notifiedtime, $notifycount, null, null, $newschedule); - } - } - } - } - - /** - * Fetch the queued schedules for the user. - * - * @param int|null $userid (Optional) The ID of the user. If provided, schedules are fetched and inti the notification sent. - * - * @return void - */ - protected function get_scheduled_records($userid=null) { - global $DB; - - $select[] = 'ns.id AS id'; // Set the schdule id as unique column. - - // Get columns not increase table queries. - // ...TODO: Fetch only used columns. Fetching all the fields in a query will make double the time of query result. - $tables = [ - 'ns' => $DB->get_columns('pulseaction_notification_sch'), - 'ai' => $DB->get_columns('pulse_autoinstances'), - 'pat' => $DB->get_columns('pulse_autotemplates'), - 'pati' => $DB->get_columns('pulse_autotemplates_ins'), - 'ni' => $DB->get_columns('pulseaction_notification_ins'), - 'an' => $DB->get_columns('pulseaction_notification'), - 'con' => array_fill_keys(["status", "additional", "isoverridden"], ""), - 'ue' => $DB->get_columns('user'), - 'c' => $DB->get_columns('course'), - 'ctx' => $DB->get_columns('context'), - 'cm' => array_fill_keys(["id", "course", "module", "instance"], ""), // Make the values as keys. - 'md' => array_fill_keys(['name'], ""), - ]; - - foreach ($tables as $prefix => $table) { - $columns = array_keys($table); - // Columns. - array_walk($columns, function(&$value, $key, $prefix) { - $value = "$prefix.$value AS ".$prefix."_$value"; - }, $prefix); - - $select = array_merge($select, $columns); - } - - // Number of notification to send in this que. - $limit = get_config('pulse', 'schedulecount') ?: 100; - - // Trigger the schedules for sepecied users. - $userwhere = $userid ? ' AND ns.userid =:userid ' : ''; - $userparam = $userid ? ['userid' => $userid] : []; - - $conditionleftjoins = ''; - $plugins = \mod_pulse\plugininfo\pulsecondition::instance()->get_plugins_base(); - foreach ($plugins as $component => $pluginbase) { - list($fields, $join) = $pluginbase->schedule_override_join(); - if (empty($fields)) { - continue; - } - $conditionleftjoins .= $join; - $select[] = $fields; - } - - // Final list of select columns, convert to sql mode. - $select = implode(', ', $select); - - // Fetch the schedule which is status as 1 and nextrun not empty and not greater than now. - $sql = "SELECT $select FROM {pulseaction_notification_sch} ns - JOIN {pulse_autoinstances} ai ON ai.id = ns.instanceid - JOIN {pulse_autotemplates} pat ON pat.id = ai.templateid - JOIN {pulse_autotemplates_ins} pati ON pati.instanceid = ai.id - JOIN {pulseaction_notification_ins} ni ON ni.instanceid = ns.instanceid - JOIN {pulseaction_notification} an ON an.templateid = ai.templateid - JOIN {user} ue ON ue.id = ns.userid - JOIN {course} c ON c.id = ai.courseid - JOIN {context} ctx ON ctx.instanceid = c.id AND ctx.contextlevel = 50 - LEFT JOIN {pulse_condition_overrides} con ON con.instanceid = pati.instanceid AND con.triggercondition = 'session' - LEFT JOIN {course_modules} cm ON cm.id = ni.dynamiccontent - $conditionleftjoins - LEFT JOIN {modules} md ON md.id = cm.module - JOIN ( - SELECT DISTINCT eu1_u.id, ej1_e.courseid, COUNT(ej1_ue.enrolid) AS activeenrolment - FROM {user} eu1_u - JOIN {user_enrolments} ej1_ue ON ej1_ue.userid = eu1_u.id - JOIN {enrol} ej1_e ON (ej1_e.id = ej1_ue.enrolid) - WHERE 1 = 1 AND ej1_ue.status = 0 - AND (ej1_ue.timestart = 0 OR ej1_ue.timestart <= :timestart) - AND (ej1_ue.timeend = 0 OR ej1_ue.timeend > :timeend) - GROUP BY eu1_u.id, ej1_e.courseid - ) active_enrols ON active_enrols.id = ue.id AND active_enrols.courseid = c.id - WHERE ns.status = :status AND ai.status <> 0 - AND active_enrols.activeenrolment <> 0 - AND c.visible = 1 - AND c.startdate <= :startdate AND (c.enddate = 0 OR c.enddate >= :enddate) - AND ue.deleted = 0 AND ue.suspended = 0 - AND ns.suppressreached = 0 AND ns.scheduletime <= :current_timestamp $userwhere ORDER BY ns.timecreated ASC"; - - $params = [ - 'status' => notification::STATUS_QUEUED, - 'current_timestamp' => time(), - 'timestart' => time(), 'timeend' => time(), - 'startdate' => time(), 'enddate' => time(), - ] + $userparam; - - $schedules = $DB->get_records_sql($sql, $params, 0, $limit); - - return $schedules; - } - - /** - * Retrieves dynamic content modules for a list of schedules. - * - * @param array $schedules An array of schedules containing information about modules. - * Each schedule should have 'md_name' (module name) and 'cm_instance' (module instance) properties. - * - * @return array An array of dynamic modules data. - */ - protected function get_modules_dynamic_content($schedules) { - // Get the dynamic modules list of all schedules. - $dynamicmodules = []; - foreach ($schedules as $key => $schedule) { - if (!isset($schedule->md_name) || empty($schedule->md_name)) { - continue; - } - $dynamicmodules[$schedule->md_name][] = $schedule->cm_instance; - } - - return notification::get_modules_data($dynamicmodules); - } - - /** - * Builds the values for a given schedule. - * - * @param object $schedule The schedule object containing information about the automation instance. - * - * @return void - */ - protected function build_schedule_values($schedule) { - - $this->schedulerecord = $schedule; - - // Prepare templates instance data. - $templatedata = helper::filter_record_byprefix($schedule, 'pat'); - $templateinsdata = helper::filter_record_byprefix($schedule, 'pati'); - $templateinsdata = (object) helper::merge_instance_overrides($templateinsdata, $templatedata); - $templateinsdata->triggerconditions = json_decode($templateinsdata->triggerconditions, true); - - // Prepare the instance data. - $instancedata = (object) helper::filter_record_byprefix($schedule, 'ai'); - // Merge the template data to instance. - $instancedata->template = $templateinsdata; - unset($templateinsdata->id); - $instancedata = (object) array_merge((array) $instancedata, (array) $templateinsdata); - $this->instancedata = $instancedata; // Auomtaion instance data. - - if (isset($this->conditions[$instancedata->id])) { - // Include the condition for this instance if already created for this cron use it. - $this->instancedata->condition = $this->conditions[$instancedata->id]; - } else { - $condition = (new instances($instancedata->id))->include_conditions_data($this->instancedata); - $this->conditions[$instancedata->id] = $condition; - $this->instancedata->condition = $condition; - } - - // Schedule data. - $this->schedule = (object) helper::filter_record_byprefix($schedule, 'ns'); - // Course data. - $this->course = (object) helper::filter_record_byprefix($schedule, 'c'); - // User data. - $this->user = (object) helper::filter_record_byprefix($schedule, 'ue'); - // Course context data. - $context = (object) helper::filter_record_byprefix($schedule, 'ctx'); - // Conver the context data to moodle context. - $this->coursecontext = \mod_pulse_context_course::create_instance_fromrecord($context); - // Filter the notification data by its prefix. - $notificationrecord = helper::filter_record_byprefix($schedule, 'an'); - // Filter the notification instance data by its prefix. - $notificationinstancerecord = helper::filter_record_byprefix($schedule, 'ni'); - - // Merge the notification overrides data and its notification data. - $this->notificationdata = (object) helper::merge_instance_overrides($notificationinstancerecord, $notificationrecord); - - // Filter the notification instance overrided values list. - $this->notificationoverrides = array_filter((array) $notificationinstancerecord, function($value) { - return $value !== null; - }); - - // Create notification instance. - // Set the notification instance data merge with notification and instance data. - $this->notification = notification::instance($notificationrecord['id']); - $this->notification->set_notification_data($this->notificationdata, $this->instancedata); - - } - - /** - * Finds the sender user for this schedule. - * - * @return mixed Returns either a string with the custom sender email, or an object representing the sender user. - */ - protected function find_sender_user() { - global $CFG; - - // Find the sender for this schedule. - if ($this->notificationdata->sender == notification::SENDERCUSTOM) { - // Find the user from moodle for this custom email. - $sender = \core_user::get_user_by_email($this->notificationdata->senderemail); - // Use the custom sender email as the support user email. - $senderemail = $this->notificationdata->senderemail; - - // SEnder is not found and is a custom email. then create dummy user data with custom email. - if (empty($sender)) { - $replyto = $senderemail; - $expsender = explode('@', $senderemail); - - $sender = (object) [ - 'id' => $CFG->siteguest, // Send the notification as guest user. - 'email' => $senderemail, - 'from' => $replyto, - 'firstname' => $expsender[0] ?? $senderemail, // Use the first part of the email as firstname of the user. - 'lastname' => '', - 'firstnamephonetic' => '', - 'lastnamephonetic' => '', - 'middlename' => '', - 'alternatename' => '', - 'maildisplay' => \core_user::MAILDISPLAY_EVERYONE, - ]; - } - - } else if ($this->notificationdata->sender == notification::SENDERTENANTROLE) { - $sender = $this->notification->get_tenantrole_sender($this->schedulerecord); - } else { - // Get user groups is sender is configured as group teacher. - $groups = ($this->notificationdata->sender == notification::SENDERGROUPTEACHER) - ? groups_get_user_groups($this->course->id, $this->schedule->userid) : 0; - - $groupids = 0; - if (!empty($groups)) { - $firstgroup = current($groups); - $groupids = current($firstgroup); - } - // Get the course teacher if group teacher not available it will fallback to course teacher automatically. - $sender = (object) $this->get_sender_users($groupids); - - if (empty((array) $sender)) { - // Sender not found then use the support user. - $sender = $this->supportuser; - } - } - - return $sender; - } - - /** - * Retrieves the sender user(s) for a given group ID. - * - * @param int|array $groupid The ID of the group or an array of group IDs. - * - * @return stdClass|array Returns an object representing the sender user if found, or an empty array if not. - */ - protected function get_sender_users($groupid) { - - $groupid = is_array($groupid) ? current($groupid) : $groupid; - - $withcapability = 'pulseaction/notification:sender'; - $sender = get_enrolled_users( - $this->coursecontext, - $withcapability, - $groupid, - 'u.*', - null, - 0, - 1, - true - ); - - return !empty($sender) ? current($sender) : []; - } - - /** - * Prepares module data to use as placeholders in notifications. - * - * @param array $modules An array of dynamic content modules organized by module name and instance. - * @param object $cmdata An object containing information about the module (modname, instance, id). - * - * @return stdClass Returns an object representing the module data to be used as placeholders. - */ - public function prepare_moduledata_placeholders($modules, $cmdata) { - global $CFG; - - // Prepare the module data to use as placeholders. - $mod = new \stdclass; - - // Find the module data if dynamic content is configured. - if ($modules && $this->notificationdata->dynamiccontent) { - $mod = (object) $modules[$cmdata->modname][$cmdata->instance] ?? []; - } - - return $mod; - } - - /** - * Includes session data in the module object. - * - * @param stdClass $mod The module object to which session data will be added. - * @param stdClass $sessiondata An object containing session data. - * @param int $userid The ID of the user. - * - * @return stdClass The modified module object with session data included. - */ - public function include_session_data(&$mod, $sessiondata, $userid) { - global $CFG; - - require_once($CFG->dirroot.'/mod/facetoface/lib.php'); - - $modules = $sessiondata->modules; - $sessions = \pulsecondition_session\conditionform::get_session_data($modules, $userid); - if (empty($sessions)) { - $mod->session = new stdClass(); - } else { - $finalsessiondata = new \stdclass(); - $session = current($sessions); - $finalsessiondata->discountcode = $session->discountcode; - $finalsessiondata->details = format_text($session->details); - $finalsessiondata->capacity = $session->capacity; - $finalsessiondata->normalcost = format_cost($session->normalcost); - $finalsessiondata->discountcost = format_cost($session->discountcost); - $finalsessiondata->link = $CFG->wwwroot . "/mod/facetoface/signup.php?s=$session->id"; - - $formatedtime = facetoface_format_session_times($session->timestart, $session->timefinish, null); - $finalsessiondata = (object) array_merge((array) $finalsessiondata, (array) $formatedtime); - // Fetch the sessions custom fields. - $customfields = facetoface_get_session_customfields(); - $finalsessiondata->customfield = new \stdclass(); - // Include the session custom fields. - foreach ($customfields as $field) { - $finalsessiondata->customfield->{$field->shortname} = facetoface_get_customfield_value( - $field, $session->sessionid, 'session'); - } - - $mod->session = $finalsessiondata; - } - - return $mod; - } -} diff --git a/actions/notification/classes/task/notify_users.php b/actions/notification/classes/task/notify_users.php deleted file mode 100644 index f33c07e..0000000 --- a/actions/notification/classes/task/notify_users.php +++ /dev/null @@ -1,183 +0,0 @@ -. - -/** - * Scheduled cron task to send pulse. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace pulseaction_notification\task; - -use pulseaction_notification\schedule; -use pulseaction_notification\notification; -use tool_dataprivacy\form\context_instance; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot.'/mod/pulse/lib.php'); - -/** - * Send notification to users - scheduled task execution observer. - */ -class notify_users extends \core\task\scheduled_task { - - /** - * Return the task's name as shown in admin screens. - * - * @return string - */ - public function get_name() { - return get_string('notifyusers', 'pulseaction_notification'); - } - - /** - * Cron execution to send the available pulses. - * - * @return void - */ - public function execute() { - schedule::instance()->send_scheduled_notification(); - } - - /** - * Module completion event observer. - * Find the notification which configured with this activity and disable the schedules for this user. - * - * @param stdclass $eventdata - * @return void - */ - public static function module_completed($eventdata) { - global $DB; - - // Event data. - $data = $eventdata->get_data(); - - $cmid = $data['contextinstanceid']; - $userid = $data['userid']; - - // Get the info for the context. - list($context, $course, $cm) = get_context_info_array($data['contextid']); - - // Course completion info. - $completion = new \completion_info($course); - - // Get all the notification instance configures the suppress with this activity. - $notifications = self::get_suppress_notifications($cmid); - - foreach ($notifications as $notification) { - // Update suppress reached for all queued shedules. - self::is_suppress_reached($notification, $userid, $course, $completion); - } - } - - /** - * Find the scheduled notification instance supress conditions are reached for the user. - * - * @param object $notification List of notification to verify the suppress. - * @param int $userid User ID to verify for. - * @param stdclass $course Instance Course record. - * @param \completion_info $completion Instance course completion info. - * @return bool True if the user is reached the suppress conditions for the instance. Otherwise False. - */ - public static function is_suppress_reached($notification, $userid, $course, $completion=null) { - global $DB; - - $completion = $completion ?: new \completion_info($course); - - // Get the notification suppres module ids. - $suppress = $notification->suppress && is_string($notification->suppress) - ? json_decode($notification->suppress) : $notification->suppress; - - if (!empty($suppress)) { - $result = []; - // Find the completion status for all this suppress modules. - foreach ($suppress as $cmid) { - if (method_exists($completion, 'get_completion_data')) { - $modulecompletion = $completion->get_completion_data($cmid, $userid, []); - } else { - $cminfo = get_coursemodule_from_id('', $cmid); - $modulecompletion = (array) $completion->get_data($cminfo, false, $userid); - } - if (isset($modulecompletion['completionstate']) == COMPLETION_COMPLETE) { - $result[] = true; - } - } - - // If suppress operator set as all, check all the configures modules are completed. - if ($notification->suppressoperator == \mod_pulse\automation\action_base::OPERATOR_ALL) { - // Remove the schedule only if all the activites are completed. - if (count($result) == count($suppress)) { - $remove = true; - } - - } else { - // If any one of the activity is completed then remove the schedule from the user. - if (count($result) >= 1) { - $remove = true; - } - } - - // Update the flag to user schedules as suppress reached, it prevents the update of the schedule on notification. - if (isset($remove) && $remove) { - $remove = false; // Reset for the next notification test. - - $sql = "SELECT * FROM {pulseaction_notification_sch} - WHERE instanceid = :instanceid AND userid = :userid - AND (status = :disabledstatus OR status = :queued)"; - - $params = [ - 'instanceid' => $notification->instanceid, 'userid' => $userid, - 'disabledstatus' => notification::STATUS_DISABLED, 'queued' => notification::STATUS_QUEUED, - ]; - - if ($records = $DB->get_records_sql($sql, $params)) { - foreach ($records as $record) { - $DB->set_field('pulseaction_notification_sch', 'suppressreached', notification::SUPPRESSREACHED, - ['id' => $record->id] - ); - } - } - - return notification::SUPPRESSREACHED; - } - - } - return false; - - } - - /** - * Retrieves notifications with suppression value containing a specific ID. - * - * @param int $id The ID to search for within the suppression values. - * - * @return array An array of notification records matching the suppression criteria. - */ - public static function get_suppress_notifications($id) { - global $DB; - - $like = $DB->sql_like('suppress', ':value'); - $sql = "SELECT * FROM {pulseaction_notification_ins} WHERE $like"; - $params = ['value' => '%"'.$id.'"%']; - - $records = $DB->get_records_sql($sql, $params); - - return $records; - } -} diff --git a/actions/notification/db/access.php b/actions/notification/db/access.php deleted file mode 100644 index 5316ff1..0000000 --- a/actions/notification/db/access.php +++ /dev/null @@ -1,47 +0,0 @@ -. - -/** - * Define plugin capabilities. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -$capabilities = [ - // Capability to recieve notifications. - 'pulseaction/notification:receivenotification' => [ - 'captype' => 'read', - 'contextlevel' => CONTEXT_MODULE, - 'archetypes' => [ - 'student' => CAP_ALLOW, - 'guest' => CAP_ALLOW, - ], - ], - - 'pulseaction/notification:sender' => [ - 'captype' => 'read', - 'contextlevel' => CONTEXT_MODULE, - 'archetypes' => [ - 'editingteacher' => CAP_ALLOW, - 'teacher' => CAP_ALLOW, - ], - ], - -]; diff --git a/actions/notification/db/events.php b/actions/notification/db/events.php deleted file mode 100644 index 79c7779..0000000 --- a/actions/notification/db/events.php +++ /dev/null @@ -1,31 +0,0 @@ -. - -/** - * Pulseaction notification - Define event observers. - * - * @package pulseaction_notification - * @copyright 2021, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -defined('MOODLE_INTERNAL') || die(); - -$observers = [ - [ - 'eventname' => 'core\event\course_module_completion_updated', - 'callback' => '\pulseaction_notification\task\notify_users::module_completed', - ], -]; diff --git a/actions/notification/db/install.xml b/actions/notification/db/install.xml deleted file mode 100644 index b509983..0000000 --- a/actions/notification/db/install.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - -
-
-
diff --git a/actions/notification/db/services.php b/actions/notification/db/services.php deleted file mode 100644 index f855e86..0000000 --- a/actions/notification/db/services.php +++ /dev/null @@ -1,37 +0,0 @@ -. - -/** - * Pulse action notification - external services. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die; - -$functions = [ - - 'pulseaction_notification_get_chapters' => [ - 'classname' => 'pulseaction_notification\external', - 'methodname' => 'get_chapters', - 'description' => 'Get list of chapters for the selected book', - 'type' => 'write', - 'ajax' => true, - 'loginrequired' => true, - ], -]; diff --git a/actions/notification/db/tasks.php b/actions/notification/db/tasks.php deleted file mode 100644 index 4340914..0000000 --- a/actions/notification/db/tasks.php +++ /dev/null @@ -1,37 +0,0 @@ -. - -/** - * Pulse action notification - List of scheduled tasks to send pulses in background. - * - * @package pulseaction_notification - * @copyright 2021, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die('No direct access !'); - -$tasks = [ - [ - 'classname' => 'pulseaction_notification\task\notify_users', - 'blocking' => 0, - 'minute' => '*', - 'hour' => '*', - 'day' => '*', - 'month' => '*', - 'dayofweek' => '*', - ], -]; diff --git a/actions/notification/db/upgrade.php b/actions/notification/db/upgrade.php deleted file mode 100644 index 38e9682..0000000 --- a/actions/notification/db/upgrade.php +++ /dev/null @@ -1,71 +0,0 @@ -. - -/** - * Pulse notification action upgrade steps. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -/** - * Pulse notification action upgrade steps. - * - * @param mixed $oldversion Previous version. - * @return void - */ -function xmldb_pulseaction_notification_upgrade($oldversion) { - global $CFG, $DB; - - // Inital plugin release - v1.0. - - $dbman = $DB->get_manager(); - - // Auto templates instance. - $instable = new xmldb_table('pulseaction_notification'); - $timemodified = new xmldb_field('timemodified', XMLDB_TYPE_INTEGER, '11', null, null, null, null); - // Verify field exists. - if ($dbman->field_exists($instable, $timemodified)) { - // Change the field. - $dbman->change_field_precision($instable, $timemodified); - } - - // Update the templates table timemodified. - $temptable = new xmldb_table('pulseaction_notification_ins'); - if ($dbman->field_exists($temptable, $timemodified)) { - // Change the field. - $dbman->change_field_precision($temptable, $timemodified); - } - - // Update the type of dynamic content. - $instable = new xmldb_table('pulseaction_notification'); - $dynamiccontent = new xmldb_field('dynamiccontent', XMLDB_TYPE_INTEGER, '11', null, null, null, null); - // Verify field exists. - if ($dbman->field_exists($instable, $dynamiccontent)) { - // Change the field. - $dbman->change_field_precision($instable, $dynamiccontent); - } - - // Update the templates table dynamiccontent. - $temptable = new xmldb_table('pulseaction_notification_ins'); - if ($dbman->field_exists($temptable, $dynamiccontent)) { - // Change the field. - $dbman->change_field_precision($temptable, $dynamiccontent); - } - - return true; -} diff --git a/actions/notification/lang/en/pulseaction_notification.php b/actions/notification/lang/en/pulseaction_notification.php deleted file mode 100644 index 6a2f4f3..0000000 --- a/actions/notification/lang/en/pulseaction_notification.php +++ /dev/null @@ -1,153 +0,0 @@ -. - -/** - * Notification pulse action - Language strings defined. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -$string['pluginname'] = 'Pulse notifications'; - -// ...Capabilities. -$string['notification:receivenotification'] = 'Recevie notifications from pulse'; -$string['notification:sender'] = 'Sender of the automation notification'; - - -$string['courseteacher'] = 'Course teacher'; -$string['groupteacher'] = 'Group teacher'; -$string['tenantrole'] = 'Tenant role'; -$string['custom'] = 'Custom'; -$string['senderemail'] = 'Sender email'; -$string['once'] = 'Once'; -$string['daily'] = 'Daily'; -$string['weekly'] = 'Weekly'; -$string['monthly'] = 'Monthly'; -$string['monday'] = 'Monday'; -$string['tuesday'] = 'Tuesday'; -$string['wednesday'] = 'Wednesday'; -$string['thursday'] = 'Thursday'; -$string['friday'] = 'Friday'; -$string['saturday'] = 'Saturday'; -$string['sunday'] = 'Sunday'; -$string['none'] = 'None'; -$string['before'] = 'Before'; -$string['after'] = 'After'; - -$string['suppressnotification'] = 'Suppress notification'; -$string['dynamicplacholder'] = 'Placeholder'; -$string['dynamicdescription'] = 'Description'; -$string['dynamiccontent'] = 'Content'; - -$string['teaser'] = 'Teaser'; -$string['full_linked'] = 'Full linked'; -$string['full_not_linked'] = 'Full not linked'; - -// ...Form tab. -$string['formtab'] = 'Notification'; -$string['notifyusers'] = 'Send notification'; - -// ... Report builder. -$string['notificationreport'] = 'Pulse Schedules'; -$string['timecreated'] = 'Time created'; -$string['nextrun'] = 'Datetime to send notification'; -$string['status'] = 'Status'; -$string['subject'] = 'Subject'; -$string['messagetype'] = 'Message type'; -// ...Status of the schedule. -$string['queued'] = 'Queued'; -$string['sent'] = 'sent'; -$string['failed'] = 'Failed'; -$string['onhold'] = 'On hold'; -$string['schedulecreatedtime'] = 'Schedule created time'; -$string['scheduledtime'] = 'Scheduled time'; -$string['cohort'] = 'Cohort'; -$string['readmore'] = 'Read more'; -$string['instanceid'] = 'Instance ID'; - -// Help texts. -// Sender. -$string['sender'] = 'Sender'; -$string['sender_help'] = 'Choose the sender of the notification from the following options:
Course Teacher: The notification will be sent from the course teacher (the first one assigned if there are several). If the user is not in any group, it falls back to the site support contact. Note that this is determined by capability, not by an actual role.
Group Teacher: The notification will be sent from the non-editing teacher who is a member of the same group as the user (the first one assigned if there are several). If there\'s no non-editing teacher in the group, it falls back to the course teacher. Note that this is determined by capability, not by an actual role.
Tenant Role (Workplace Feature): The notification will be sent from the user assigned to the specified role in the tenant (the first one assigned if there are several). If there\'s no user with the selected role, it falls back to the site support contact. Note that this is determined by capability, not by an actual role.
Custom: If selected, an additional setting for "Sender Email" will be displayed where you can enter a specific email address to be used as the sender.'; -// Interval. -$string['interval'] = 'Interval'; -$string['interval_help'] = 'Choose the interval for sending notifications:
Once: Send the notification only one time.
Daily: Send the notification every day at the time selected below.
Weekly: Send the notification every week on the day of the week and time of below.
Monthly: Send the notification every month on the day of the month and time of below.'; -// Delay. -$string['delay'] = 'Delay'; -$string['delay_help'] = 'Choose the delay option for sending notifications: -
None: Send notifications immediately upon the condition being met, considering the schedule limitations (e.g., weekday or time of day).
Before: Send the notification a specified number of days/hours before the condition is met. Note that this is only possible for timed events, e.g., appointment sessions.
After: Send the notification a specified number of days/hours after the condition is met. This is possible for all conditions.'; -// Delay duration. -$string['delayduraion'] = 'Delay duraion'; -$string['delayduraion_help'] = 'Please enter the duration time for the delay in sending the notification. This duration should be specified in terms of days or hours, depending on the selected delay option.'; -// Limit. -$string['limit'] = 'Limit of the notifications'; -$string['limit_help'] = 'Enter a number to limit the total number of notifications sent.
Note:Enter "0" for no limit. This is only relevant if the schedule is not set to "Once".'; -// Recipients. -$string['recipients'] = 'Recipients'; -$string['recipients_help'] = 'Select one or more roles that have the capability to receive notifications. By default, it\'s set for all graded roles, including students. Users selected here will be used in the query to determine who gets notifications.'; -// CC recipients. -$string['ccrecipients'] = 'Cc '; -$string['ccrecipients_help'] = 'Select course context and user context roles that will receive the notification as a CC (Carbon Copy) to the main recipient. Course context roles determine users by enrolment in the course and membership of a group, while user context roles determine users by their relation to the recipient (assigned role in user).'; -// BCC recipients. -$string['bccrecipients'] = 'Bcc '; -$string['bccrecipients_help'] = 'Select course context and user context roles that will receive the notification as a BCC (Blind Carbon Copy) to the main recipient. Course context roles determine users by enrolment in the course and membership of a group, while user context roles determine users by their relation to the recipient (assigned role in user).'; -// Subject. -$string['subject'] = 'Subject'; -$string['subject_help'] = 'Enter the subject for the notification.'; -// Header content. -$string['headercontent'] = 'Header content'; -$string['headercontent_help'] = 'Enter the first part of the body for the notification. This field supports filters and placeholders.'; -// Static content. -$string['staticcontent'] = 'Static content'; -$string['staticcontent_help'] = 'Enter the second part of the body for the notification. This field supports filters and placeholders.'; -// Footer content. -$string['footercontent'] = 'Footer content'; -$string['footercontent_help'] = 'Enter the last part of the body for the notification. This field supports filters and placeholders.'; -// Preview. -$string['preview'] = 'Preview'; -$string['preview_help'] = 'Click this button to open a modal window that displays the notification, allowing you to select an example user to determine the content of the notification.'; -// Suppress module. -$string['suppressmodule'] = 'Suppress module'; -$string['suppressmodule_help'] = 'Choose one or more activities that, when completed, will suppress the notification from being sent. You can select the operand below to determine how these activities affect notification.'; -// Suppress operator. -$string['suppressoperator'] = 'Suppress operator'; -$string['suppressoperator_help'] = 'Choose the operand that determines how the selected activities completion affects the notification:
Any: If any of the selected activities above are completed, the notification shall not be sent.
All: If all of the selected activities above are completed, the notification shall not be sent.'; -// Dynamic content. -$string['dynamiccontent'] = 'Dynamic content'; -$string['dynamiccontent_help'] = 'Select an activity within the course to add content below the static content. This is only available in the automation instance within the course.'; -// Content type. -$string['contenttype'] = 'Content type'; -$string['contenttype_help'] = 'Choose the type of content to be added below the static content:
Description: If selected, the description of the selected activity shall be added to the body of the notification.
Content: If selected, the content of the selected activity shall be added to the body of the notification. Note that this should support specific mod types like Page and Book with the ability to select specific chapters.'; -// Content length. -$string['contentlength'] = 'Content length'; -$string['contentlength_help'] = ' Choose the content length to include in the notification:
Teaser: If selected, only the first paragraph shall be used, with a "Read More" link added after it.
Full, Linked: If selected, the entire content shall be used, with a link to the content after it.
Full, Not Linked: If selected, the entire content shall be used without a link to the content after it.'; -// Chapters. -$string['chapters'] = 'Chapters'; -$string['chapters_help'] = 'Provides support to select specific chapters from a Book activity.'; -// ...Course instance warning messages. -$string['coursehidden'] = 'Course is hidden from the students. Please enable the visibility of the course to send notifications.'; -$string['noactiveusers'] = 'Course doesn\'t contain any active enrolments. Please enroll users in the course.'; -$string['coursenotstarted'] = 'Course has not started. Please set the course start date to a date in the past.'; -$string['courseenddatereached'] = 'Course has ended. Please set the course end date to a date in the future or remove the end date.'; -// ...Reports filter string. -$string['automationinstance'] = 'Automation instance'; -$string['automationtemplate'] = 'Automation template'; -$string['insreference'] = 'Instance reference'; -$string['tempreference'] = 'Template reference'; -$string['institle'] = 'Instance title'; -$string['templatetitle'] = 'Template title'; diff --git a/actions/notification/lib.php b/actions/notification/lib.php deleted file mode 100644 index b0f4597..0000000 --- a/actions/notification/lib.php +++ /dev/null @@ -1,187 +0,0 @@ -. - -/** - * Notification pulse action - Library file contains commonly used functions. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use pulseaction_notification\notification; -use pulseaction_notification\schedule; - -/** - * Returns list of fileareas used in the pulsepro reminder contents. - * - * @return array list of filearea to support pluginfile. - */ -function pulseaction_notification_extend_pulse_filearea() : array { - - return [ - 'pulsenotification_headercontent', - 'pulsenotification_staticcontent', - 'pulsenotification_footercontent', - 'pulsenotification_headercontent_instance', - 'pulsenotification_staticcontent_instance', - 'pulsenotification_footercontent_instance', - ]; -} - -/** - * Updates chapters for a notification output fragment. - * - * @param array $args An associative array of arguments. - * - 'context' (object) The context object. - * - 'mod' (int) The Course Module ID (optional). - * - * @return mixed Returns the loaded book chapters or null if 'mod' is not set in the arguments. - */ -function pulseaction_notification_output_fragment_update_chapters($args) { - $context = $args['context']; - - if (isset($args['mod'])) { - $cmid = $args['mod']; - return pulseaction_notification\notification::load_book_chapters($cmid); - } -} - -/** - * Preview of the instance for sepecific user. - * - * @param array $args - * @return string - */ -function pulseaction_notification_output_fragment_preview_instance_content($args) { - global $OUTPUT; - - $context = $args['context']; - if (isset($args['instanceid'])) { - - $insobj = new \mod_pulse\automation\instances($args['instanceid']); - $formdata = (object) $insobj->get_instance_data(); - - $notificationid = $formdata->actions['notification']['id']; - $notificationobj = pulseaction_notification\notification::instance($notificationid); - - $notificationobj->set_notification_data($formdata->actions['notification'], $formdata); - $content = $notificationobj->build_notification_content(null, null, $formdata->override); - - $sender = core_user::get_support_user(); - $users = get_enrolled_users(\context_course::instance($formdata->courseid)); - $user = (object) ($args['userid'] != null ? core_user::get_user($args['userid']) : current($users)); - - $course = get_course($formdata->courseid ?? SITEID); - - $mod = new stdclass; - // ...TODO: Inlcude the vars update from condition plugins. - if ($formdata->actions['notification']['dynamiccontent']) { - // Prepare the module data. based on dynamic content and includ the session data. - $modname = $formdata->actions['notification']['mod']->modname; - $dynamicmodules[$modname][] = $formdata->actions['notification']['mod']->instance; - $modules = notification::get_modules_data($dynamicmodules); - $mod = current($modules[$modname]); - } - // Check the session condition are set for this notification. if its added then load the session data for placeholders. - $sessionincondition = in_array('session', (array) array_keys($formdata->condition)); - if ($sessionincondition && $formdata->condition['session']['status']) { - $sessionconditiondata = (object) ['modules' => $formdata->condition['session']['modules']]; - schedule::instance()->include_session_data($mod, $sessionconditiondata, $user->id); - } - - list($subject, $messagehtml) = mod_pulse\helper::update_emailvars($content, '', $course, $user, $mod, $sender); - $selector = ""; - - $data = ['message' => $messagehtml, 'usersselector' => $selector]; - - return $OUTPUT->render_from_template('pulseaction_notification/preview', ['data' => $data]); - } -} - -/** - * Preview the content of the instance. - * - * @param [type] $args - * @return void - */ -function pulseaction_notification_output_fragment_preview_content($args) { - global $OUTPUT; - - if (isset($args['contentheader'])) { - - parse_str($args['formdata'], $formdata); - $courseid = $formdata['courseid'] ?? SITEID; - $course = get_course($courseid); - $coursecontext = context_course::instance($courseid); - - // Get the enrolled users for this course. - $users = get_enrolled_users($coursecontext); - $list = []; - foreach ($users as $userid => $user) { - $list[$userid] = fullname($user); - } - $sender = core_user::get_support_user(); - - $user = (object) ($args['userid'] != null ? core_user::get_user($args['userid']) : current($users)); - - $dynamiccontent = ''; - $mod = new stdclass; - if (isset($args['contentdynamic']) && !empty($args['contentdynamic'])) { - - $module = get_coursemodule_from_id('', $args['contentdynamic']); - $moddata = (object) [ - 'instance' => $module->instance, - 'modname' => $module->modname, - 'id' => $module->id, - ]; - $context = \context_module::instance($module->id); - // Generate dynamic content for the instance. - $dynamiccontent = notification::generate_dynamic_content( - $args['contenttype'], - $args['contentlength'], - $args['chapterid'], - $context, - $moddata - ); - // ...TODO: Inlcude the vars update from condition plugins. - if ($args['contentdynamic']) { - // Prepare the module data. based on dynamic content and includ the session data. - $modname = $module->modname; - $dynamicmodules[$modname][] = $module->instance; - $modules = notification::get_modules_data($dynamicmodules); - $mod = current($modules[$modname]); - } - - // Check the session condition are set for this notification. if its added then load the session data for placeholders. - if (isset($formdata['condition']['session']['status']) && $formdata['condition']['session']['status']) { - $sessionconditiondata = (object) ['modules' => $formdata['condition']['session']['modules']]; - schedule::instance()->include_session_data($mod, $sessionconditiondata, $user->id); - } - } - - $content = $args['contentheader'] . $args['contentstatic'] . $dynamiccontent . $args['contentfooter']; - - // Update the placeholders with course and user data. - list($subject, $messagehtml) = mod_pulse\helper::update_emailvars($content, '', $course, $user, $mod, $sender); - // User selector. - $selector = html_writer::select($list, 'userselector', $user->id); - - $data = ['message' => $messagehtml, 'usersselector' => $selector]; - - return $OUTPUT->render_from_template('pulseaction_notification/preview', ['data' => $data]); - } -} diff --git a/actions/notification/pulseaction_notification_email.php b/actions/notification/pulseaction_notification_email.php deleted file mode 100644 index 2184b9e..0000000 --- a/actions/notification/pulseaction_notification_email.php +++ /dev/null @@ -1,548 +0,0 @@ -. - -/** - * Contains the definiton of the email message processors (sends messages to users via email) - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot.'/message/output/email/message_output_email.php'); - -/** - * The email message processor for pulse notification actions. Extends the core message_output_email processor. - */ -class pulseaction_notification_email extends message_output_email { - - /** - * Processes the message (sends by email). - * - Modified the core message_output_email plugin method to send CC and BCC mails. - * - * @copyright 2008 Luis Rodrigues and Martin Dougiamas - * @param object $eventdata the event data submitted by the message sender plus $eventdata->savedmessageid - */ - public function send_message($eventdata) { - global $CFG, $DB; - - // Skip any messaging suspended and deleted users. - if ($eventdata->userto->auth === 'nologin' || $eventdata->userto->suspended || $eventdata->userto->deleted) { - return true; - } - - // The user the email is going to. - $recipient = null; - - // Check if the recipient has a different email address specified in their messaging preferences Vs their user profile. - $emailmessagingpreference = get_user_preferences('message_processor_email_email', null, $eventdata->userto); - $emailmessagingpreference = clean_param($emailmessagingpreference, PARAM_EMAIL); - - // If the recipient has set an email address in their preferences use that instead of the one in their profile, - // But only if overriding the notification email address is allowed. - if (!empty($emailmessagingpreference) && !empty($CFG->messagingallowemailoverride)) { - // Clone to avoid altering the actual user object. - $recipient = clone($eventdata->userto); - $recipient->email = $emailmessagingpreference; - } else { - $recipient = $eventdata->userto; - } - - // Check if we have attachments to send. - $attachment = ''; - $attachname = ''; - if (!empty($CFG->allowattachments) && !empty($eventdata->attachment)) { - if (empty($eventdata->attachname)) { - // Attachment needs a file name. - debugging('Attachments should have a file name. No attachments have been sent.', DEBUG_DEVELOPER); - } else if (!($eventdata->attachment instanceof stored_file)) { - // Attachment should be of a type stored_file. - debugging('Attachments should be of type stored_file. No attachments have been sent.', DEBUG_DEVELOPER); - } else { - // Copy attachment file to a temporary directory and get the file path. - $attachment = $eventdata->attachment->copy_content_to_temp(); - - // Get attachment file name. - $attachname = clean_filename($eventdata->attachname); - } - } - - // Configure mail replies - this is used for incoming mail replies. - $replyto = ''; - $replytoname = ''; - if (isset($eventdata->replyto)) { - $replyto = $eventdata->replyto; - if (isset($eventdata->replytoname)) { - $replytoname = $eventdata->replytoname; - } - } - - // Pulse - section to add bcc and cc users. - $ccaddr = []; - $bccaddr = []; - $customdata = json_decode($eventdata->customdata); - if (isset($eventdata->customdata) && isset($customdata->cc)) { - $ccaddr = $customdata->cc; - $bccaddr = $customdata->bcc; - } - - // We email messages from private conversations straight away, but for group we add them to a table to be sent later. - $emailuser = true; - if (!$eventdata->notification) { - if ($eventdata->conversationtype == \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP) { - $emailuser = false; - } - } - - if ($emailuser) { - $result = $this->email_to_user($recipient, $eventdata->userfrom, $eventdata->subject, $eventdata->fullmessage, - $eventdata->fullmessagehtml, $attachment, $attachname, true, $replyto, $replytoname, 79, $ccaddr, $bccaddr); - } else { - $messagetosend = new stdClass(); - $messagetosend->useridfrom = $eventdata->userfrom->id; - $messagetosend->useridto = $recipient->id; - $messagetosend->conversationid = $eventdata->convid; - $messagetosend->messageid = $eventdata->savedmessageid; - $result = $DB->insert_record('message_email_messages', $messagetosend, false); - } - - // Remove an attachment file if any. - if (!empty($attachment) && file_exists($attachment)) { - unlink($attachment); - } - - return $result; - } - - /** - * Send an email to a specified user - Modified the core moodlelib function to send CC and BCC mails. - * - * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com - * - * @param stdClass $user A {@see $USER} object - * @param stdClass $from A {@see $USER} object - * @param string $subject plain text subject line of the email - * @param string $messagetext plain text version of the message - * @param string $messagehtml complete html version of the message (optional) - * @param string $attachment a file on the filesystem, either relative to $CFG->dataroot or a full path to a file in one of - * the following directories: $CFG->cachedir, $CFG->dataroot, $CFG->dirroot, $CFG->localcachedir, $CFG->tempdir - * @param string $attachname the name of the file (extension indicates MIME) - * @param bool $usetrueaddress determines whether $from email address should - * be sent out. Will be overruled by user profile setting for maildisplay - * @param string $replyto Email address to reply to - * @param string $replytoname Name of reply to recipient - * @param int $wordwrapwidth custom word wrap width, default 79 - * @param array $cc Cc mails list. - * @param array $bcc Bcc mails list. - * - * @return bool Returns true if mail was sent OK and false if there was an error. - */ - public function email_to_user($user, $from, $subject, $messagetext, $messagehtml = '', $attachment = '', $attachname = '', - $usetrueaddress = true, $replyto = '', $replytoname = '', $wordwrapwidth = 79, $cc=[], $bcc=[]) { - - global $CFG, $PAGE, $SITE; - - if (empty($user) || empty($user->id)) { - debugging('Can not send email to null user', DEBUG_DEVELOPER); - return false; - } - - if (empty($user->email)) { - debugging('Can not send email to user without email: '.$user->id, DEBUG_DEVELOPER); - return false; - } - - if (!empty($user->deleted)) { - debugging('Can not send email to deleted user: '.$user->id, DEBUG_DEVELOPER); - return false; - } - - if (defined('BEHAT_SITE_RUNNING')) { - // Fake email sending in behat. - return true; - } - - if (!empty($CFG->noemailever)) { - // Hidden setting for development sites, set in config.php if needed. - debugging('Not sending email due to $CFG->noemailever config setting', DEBUG_NORMAL); - return true; - } - - if (email_should_be_diverted($user->email)) { - $subject = "[DIVERTED {$user->email}] $subject"; - $user = clone($user); - $user->email = $CFG->divertallemailsto; - } - - // Skip mail to suspended users. - if ((isset($user->auth) && $user->auth == 'nologin') || (isset($user->suspended) && $user->suspended)) { - return true; - } - - if (!validate_email($user->email)) { - // We can not send emails to invalid addresses - it might create security issue or confuse the mailer. - debugging("email_to_user: User $user->id (".fullname($user).") email ($user->email) is invalid! Not sending."); - return false; - } - - if (over_bounce_threshold($user)) { - debugging("email_to_user: User $user->id (".fullname($user).") is over bounce threshold! Not sending."); - return false; - } - - // TLD .invalid is specifically reserved for invalid domain names. - // For More information, see {@link http://tools.ietf.org/html/rfc2606#section-2}. - if (substr($user->email, -8) == '.invalid') { - debugging("email_to_user: User $user->id (".fullname($user).") email domain ($user->email) is invalid! Not sending."); - return true; // This is not an error. - } - - // If the user is a remote mnet user, parse the email text for URL to the - // wwwroot and modify the url to direct the user's browser to login at their - // home site (identity provider - idp) before hitting the link itself. - if (is_mnet_remote_user($user)) { - require_once($CFG->dirroot.'/mnet/lib.php'); - - $jumpurl = mnet_get_idp_jump_url($user); - $callback = partial('mnet_sso_apply_indirection', $jumpurl); - - $messagetext = preg_replace_callback("%($CFG->wwwroot[^[:space:]]*)%", - $callback, - $messagetext); - $messagehtml = preg_replace_callback("%href=[\"'`]($CFG->wwwroot[\w_:\?=#&@/;.~-]*)[\"'`]%", - $callback, - $messagehtml); - } - $mail = get_mailer(); - - if (!empty($mail->SMTPDebug)) { - echo '

' . "\n";
-        }
-
-        $temprecipients = [];
-        $tempreplyto = [];
-
-        // Make sure that we fall back onto some reasonable no-reply address.
-        $noreplyaddressdefault = 'noreply@' . get_host_from_url($CFG->wwwroot);
-        $noreplyaddress = empty($CFG->noreplyaddress) ? $noreplyaddressdefault : $CFG->noreplyaddress;
-
-        if (!validate_email($noreplyaddress)) {
-            debugging('email_to_user: Invalid noreply-email '.s($noreplyaddress));
-            $noreplyaddress = $noreplyaddressdefault;
-        }
-
-        // Make up an email address for handling bounces.
-        if (!empty($CFG->handlebounces)) {
-            $modargs = 'B'.base64_encode(pack('V', $user->id)).substr(md5($user->email), 0, 16);
-            $mail->Sender = generate_email_processing_address(0, $modargs);
-        } else {
-            $mail->Sender = $noreplyaddress;
-        }
-
-        // Make sure that the explicit replyto is valid, fall back to the implicit one.
-        if (!empty($replyto) && !validate_email($replyto)) {
-            debugging('email_to_user: Invalid replyto-email '.s($replyto));
-            $replyto = $noreplyaddress;
-        }
-
-        if (is_string($from)) { // So we can pass whatever we want if there is need.
-            $mail->From     = $noreplyaddress;
-            $mail->FromName = $from;
-            // Check if using the true address is true, and the email is in the list of allowed domains for sending email,
-            // and that the senders email setting is either displayed to everyone, or display to only other users that are enrolled
-            // in a course with the sender.
-        } else if ($usetrueaddress && can_send_from_real_email_address($from, $user)) {
-            if (!validate_email($from->email)) {
-                debugging('email_to_user: Invalid from-email '.s($from->email).' - not sending');
-                // Better not to use $noreplyaddress in this case.
-                return false;
-            }
-            $mail->From = $from->email;
-            $fromdetails = new stdClass();
-            $fromdetails->name = fullname($from);
-            $fromdetails->url = preg_replace('#^https?://#', '', $CFG->wwwroot);
-            $fromdetails->siteshortname = format_string($SITE->shortname);
-            $fromstring = $fromdetails->name;
-            if ($CFG->emailfromvia == EMAIL_VIA_ALWAYS) {
-                $fromstring = get_string('emailvia', 'core', $fromdetails);
-            }
-            $mail->FromName = $fromstring;
-            if (empty($replyto)) {
-                $tempreplyto[] = [$from->email, fullname($from)];
-            }
-        } else {
-            $mail->From = $noreplyaddress;
-            $fromdetails = new stdClass();
-            $fromdetails->name = fullname($from);
-            $fromdetails->url = preg_replace('#^https?://#', '', $CFG->wwwroot);
-            $fromdetails->siteshortname = format_string($SITE->shortname);
-            $fromstring = $fromdetails->name;
-            if ($CFG->emailfromvia != EMAIL_VIA_NEVER) {
-                $fromstring = get_string('emailvia', 'core', $fromdetails);
-            }
-            $mail->FromName = $fromstring;
-            if (empty($replyto)) {
-                $tempreplyto[] = [$noreplyaddress, get_string('noreplyname')];
-            }
-        }
-
-        if (!empty($replyto)) {
-            $tempreplyto[] = [$replyto, $replytoname];
-        }
-
-        $temprecipients[] = [$user->email, fullname($user)];
-
-        // Set word wrap.
-        $mail->WordWrap = $wordwrapwidth;
-
-        if (!empty($from->customheaders)) {
-            // Add custom headers.
-            if (is_array($from->customheaders)) {
-                foreach ($from->customheaders as $customheader) {
-                    $mail->addCustomHeader($customheader);
-                }
-            } else {
-                $mail->addCustomHeader($from->customheaders);
-            }
-        }
-
-        // If the X-PHP-Originating-Script email header is on then also add an additional
-        // header with details of where exactly in moodle the email was triggered from,
-        // either a call to message_send() or to email_to_user().
-        if (ini_get('mail.add_x_header')) {
-            // @codingStandardsIgnoreStart
-            $stack = debug_backtrace(false);
-            // @codingStandardsIgnoreEnd
-            $origin = $stack[0];
-
-            foreach ($stack as $depth => $call) {
-                if ($call['function'] == 'message_send') {
-                    $origin = $call;
-                }
-            }
-
-            $originheader = $CFG->wwwroot . ' => ' . gethostname() . ':'
-                . str_replace($CFG->dirroot . '/', '', $origin['file']) . ':' . $origin['line'];
-            $mail->addCustomHeader('X-Moodle-Originating-Script: ' . $originheader);
-        }
-
-        if (!empty($CFG->emailheaders)) {
-            $headers = array_map('trim', explode("\n", $CFG->emailheaders));
-            foreach ($headers as $header) {
-                if (!empty($header)) {
-                    $mail->addCustomHeader($header);
-                }
-            }
-        }
-
-        if (!empty($from->priority)) {
-            $mail->Priority = $from->priority;
-        }
-
-        $renderer = $PAGE->get_renderer('core');
-        $context = [
-            'sitefullname' => $SITE->fullname,
-            'siteshortname' => $SITE->shortname,
-            'sitewwwroot' => $CFG->wwwroot,
-            'subject' => $subject,
-            'prefix' => $CFG->emailsubjectprefix,
-            'to' => $user->email,
-            'toname' => fullname($user),
-            'from' => $mail->From,
-            'fromname' => $mail->FromName,
-        ];
-        if (!empty($tempreplyto[0])) {
-            $context['replyto'] = $tempreplyto[0][0];
-            $context['replytoname'] = $tempreplyto[0][1];
-        }
-        if ($user->id > 0) {
-            $context['touserid'] = $user->id;
-            $context['tousername'] = $user->username;
-        }
-
-        if (!empty($user->mailformat) && $user->mailformat == 1) {
-            // Only process html templates if the user preferences allow html email.
-
-            if (!$messagehtml) {
-                // If no html has been given, BUT there is an html wrapping template then
-                // auto convert the text to html and then wrap it.
-                $messagehtml = trim(text_to_html($messagetext));
-            }
-            $context['body'] = $messagehtml;
-            $messagehtml = $renderer->render_from_template('core/email_html', $context);
-        }
-
-        $context['body'] = html_to_text(nl2br($messagetext));
-        $mail->Subject = $renderer->render_from_template('core/email_subject', $context);
-        $mail->FromName = $renderer->render_from_template('core/email_fromname', $context);
-        $messagetext = $renderer->render_from_template('core/email_text', $context);
-
-        // Autogenerate a MessageID if it's missing.
-        if (empty($mail->MessageID)) {
-            $mail->MessageID = generate_email_messageid();
-        }
-
-        if ($messagehtml && !empty($user->mailformat) && $user->mailformat == 1) {
-            // Don't ever send HTML to users who don't want it.
-            $mail->isHTML(true);
-            $mail->Encoding = 'quoted-printable';
-            $mail->Body    = $messagehtml;
-            $mail->AltBody = "\n$messagetext\n";
-        } else {
-            $mail->IsHTML(false);
-            $mail->Body = "\n$messagetext\n";
-        }
-
-        if ($attachment && $attachname) {
-            if (preg_match( "~\\.\\.~" , $attachment )) {
-                // Security check for ".." in dir path.
-                $supportuser = core_user::get_support_user();
-                $temprecipients[] = [$supportuser->email, fullname($supportuser, true)];
-                $mail->addStringAttachment(
-                    'Error in attachment.  User attempted to attach a filename with a unsafe name.',
-                    'error.txt', '8bit', 'text/plain');
-            } else {
-                require_once($CFG->libdir.'/filelib.php');
-                $mimetype = mimeinfo('type', $attachname);
-
-                // Before doing the comparison, make sure that the paths are correct (Windows uses slashes in the other direction).
-                // The absolute (real) path is also fetched to ensure that comparisons to allowed paths are compared equally.
-                $attachpath = str_replace('\\', '/', realpath($attachment));
-
-                // Build an array of all filepaths from which attachments can be added (normalised slashes, absolute/real path).
-                $allowedpaths = array_map(function(string $path): string {
-                    return str_replace('\\', '/', realpath($path));
-                }, [
-                    $CFG->cachedir,
-                    $CFG->dataroot,
-                    $CFG->dirroot,
-                    $CFG->localcachedir,
-                    $CFG->tempdir,
-                    $CFG->localrequestdir,
-                ]);
-
-                // Set addpath to true.
-                $addpath = true;
-
-                // Check if attachment includes one of the allowed paths.
-                foreach (array_filter($allowedpaths) as $allowedpath) {
-                    // Set addpath to false if the attachment includes one of the allowed paths.
-                    if (strpos($attachpath, $allowedpath) === 0) {
-                        $addpath = false;
-                        break;
-                    }
-                }
-
-                // If the attachment is a full path to a file in the multiple allowed paths, use it as is,
-                // otherwise assume it is a relative path from the dataroot (for backwards compatibility reasons).
-                if ($addpath == true) {
-                    $attachment = $CFG->dataroot . '/' . $attachment;
-                }
-
-                $mail->addAttachment($attachment, $attachname, 'base64', $mimetype);
-            }
-        }
-
-        // Check if the email should be sent in an other charset then the default UTF-8.
-        if ((!empty($CFG->sitemailcharset) || !empty($CFG->allowusermailcharset))) {
-
-            // Use the defined site mail charset or eventually the one preferred by the recipient.
-            $charset = $CFG->sitemailcharset;
-            if (!empty($CFG->allowusermailcharset)) {
-                if ($useremailcharset = get_user_preferences('mailcharset', '0', $user->id)) {
-                    $charset = $useremailcharset;
-                }
-            }
-
-            // Convert all the necessary strings if the charset is supported.
-            $charsets = get_list_of_charsets();
-            unset($charsets['UTF-8']);
-            if (in_array($charset, $charsets)) {
-                $mail->CharSet  = $charset;
-                $mail->FromName = core_text::convert($mail->FromName, 'utf-8', strtolower($charset));
-                $mail->Subject  = core_text::convert($mail->Subject, 'utf-8', strtolower($charset));
-                $mail->Body     = core_text::convert($mail->Body, 'utf-8', strtolower($charset));
-                $mail->AltBody  = core_text::convert($mail->AltBody, 'utf-8', strtolower($charset));
-
-                foreach ($temprecipients as $key => $values) {
-                    $temprecipients[$key][1] = core_text::convert($values[1], 'utf-8', strtolower($charset));
-                }
-                foreach ($tempreplyto as $key => $values) {
-                    $tempreplyto[$key][1] = core_text::convert($values[1], 'utf-8', strtolower($charset));
-                }
-            }
-        }
-
-        foreach ($temprecipients as $values) {
-            $mail->addAddress($values[0], $values[1]);
-        }
-        foreach ($tempreplyto as $values) {
-            $mail->addReplyTo($values[0], $values[1]);
-        }
-
-        // Custom method to add cc and bcc.
-        foreach ($cc as $values) {
-            $mail->addCC($values[0], $values[1]);
-        }
-        foreach ($bcc as $values) {
-            $mail->addBCC($values[0], $values[1]);
-        }
-
-        if (!empty($CFG->emaildkimselector)) {
-            $domain = substr(strrchr($mail->From, "@"), 1);
-            $pempath = "{$CFG->dataroot}/dkim/{$domain}/{$CFG->emaildkimselector}.private";
-            if (file_exists($pempath)) {
-                $mail->DKIM_domain      = $domain;
-                $mail->DKIM_private     = $pempath;
-                $mail->DKIM_selector    = $CFG->emaildkimselector;
-                $mail->DKIM_identity    = $mail->From;
-            } else {
-                debugging("Email DKIM selector chosen due to {$mail->From} but no certificate found at $pempath", DEBUG_DEVELOPER);
-            }
-        }
-
-        if ($mail->send()) {
-            set_send_count($user);
-            if (!empty($mail->SMTPDebug)) {
-                echo '
'; - } - return true; - } else { - // Trigger event for failing to send email. - $event = \core\event\email_failed::create([ - 'context' => context_system::instance(), - 'userid' => $from->id, - 'relateduserid' => $user->id, - 'other' => [ - 'subject' => $subject, - 'message' => $messagetext, - 'errorinfo' => $mail->ErrorInfo, - ], - ]); - $event->trigger(); - if (CLI_SCRIPT) { - mtrace('Error: lib/moodlelib.php email_to_user(): '.$mail->ErrorInfo); - } - if (!empty($mail->SMTPDebug)) { - echo ''; - } - return false; - } - } - -} diff --git a/actions/notification/templates/preview.mustache b/actions/notification/templates/preview.mustache deleted file mode 100644 index 7370760..0000000 --- a/actions/notification/templates/preview.mustache +++ /dev/null @@ -1,17 +0,0 @@ -
-
- {{#data.usersselector}} -
-
- {{#str}} users, core {{/str}} -
-
- {{{data.usersselector}}} -
-
- {{/data.usersselector}} -
-
- {{{data.message}}} -
-
diff --git a/actions/notification/tests/behat/behat_pulseaction_notification.php b/actions/notification/tests/behat/behat_pulseaction_notification.php deleted file mode 100644 index edcfac2..0000000 --- a/actions/notification/tests/behat/behat_pulseaction_notification.php +++ /dev/null @@ -1,136 +0,0 @@ -. - -/** - * Behat pulse action notification - related steps definitions. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. - -require_once(__DIR__ . '/../../../../../../lib/behat/behat_base.php'); - -use Behat\Gherkin\Node\TableNode as TableNode, - Behat\Mink\Exception\ExpectationException as ExpectationException, - Behat\Mink\Exception\DriverException as DriverException, - Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException; -use PhpOffice\PhpSpreadsheet\Worksheet\Table; - -/** - * Pulse notification automation - related steps definitions. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class behat_pulseaction_notification extends behat_base { - - /** - * Fills a automation template condition form with field/value data. - * - * @Given /^I create pulse notification template "([^"]*)" "([^"]*)" to these values:$/ - * @param string $title - * @param string $reference - * @param TableNode $notificationdata - */ - public function i_create_pulsenotification_template_with_general($title, $reference, $notificationdata) { - - $this->execute("behat_general::i_click_on", ["Create new template", "button"]); - $this->execute('behat_forms::i_set_the_field_to', ["Title", $title]); - $this->execute('behat_forms::i_set_the_field_to', ["Reference", $reference]); - $this->execute("behat_general::i_click_on_in_the", ["Condition", "link", "#automation-tabs", "css_element"]); - $this->execute('behat_forms::i_set_the_field_to', ["Triggers", "User enrolment"]); - $this->execute("behat_general::i_click_on_in_the", ["Notification", "link", "#automation-tabs", "css_element"]); - $this->execute('behat_forms::i_set_the_following_fields_to_these_values', [$notificationdata]); - $this->execute("behat_general::i_click_on", ["Save changes", "button"]); - } - - /** - * Fills a automation template condition form with field/value data. - * - * @Given /^I create pulse notification instance "([^"]*)" "([^"]*)" to these values:$/ - * @param string $title - * @param string $reference - * @param TableNode $notificationdata - */ - public function i_create_pulsenotification_instance_with_general($title, $reference, TableNode $notificationdata) { - - $this->execute('behat_forms::i_open_the_autocomplete_suggestions_list', []); - $this->execute('behat_forms::i_click_on_item_in_the_autocomplete_list', [$title]); - $this->execute("behat_general::i_click_on", ["Add automation instance", "button"]); - $this->execute('behat_forms::i_set_the_field_to', ["Reference", $reference]); - $this->execute("behat_general::i_click_on_in_the", ["Notification", "link", "#automation-tabs", "css_element"]); - $this->execute('behat_forms::i_set_the_following_fields_to_these_values', [$notificationdata]); - $this->execute("behat_general::i_click_on", ["Save changes", "button"]); - } - - /** - * Create a new an automation instance and fills the form with field/value data. - * - * @Given /^I initiate new automation instance of template "([^"]*)" to these values:$/ - * @param String $title - * @param TableNode $generaldata - */ - public function i_set_pulsenotification_instance_with_general($title, TableNode $generaldata) { - - $this->execute('behat_forms::i_open_the_autocomplete_suggestions_list', []); - $this->execute('behat_forms::i_click_on_item_in_the_autocomplete_list', [$title]); - $this->execute("behat_general::i_click_on", ["Add automation instance", "button"]); - $this->execute('behat_forms::i_set_the_following_fields_to_these_values', [$generaldata]); - } - - /** - * Fills the previously initiated instance notification actions form with these values. - * - * @Given /^I set previous automation instance notification to these values:$/ - * @throws ElementNotFoundException Thrown by behat_base::find - * @param TableNode $notificationdata - */ - public function i_set_previous_instance_notification($notificationdata) { - $this->execute("behat_general::i_click_on_in_the", ["Notification", "link", "#automation-tabs", "css_element"]); - $this->execute('behat_forms::i_set_the_following_fields_to_these_values', [$notificationdata]); - } - - /** - * Create a new automation template, Fills a automation template form with field/value data. - * - * @Given /^I initiate new automation template to these values:$/ - * @throws ElementNotFoundException Thrown by behat_base::find - * @param TableNode $generaldata - */ - public function i_set_pulsenotification_template_with_general($generaldata) { - $this->execute('behat_pulse::i_navigate_to_automation_templates'); - $this->execute("behat_general::i_click_on", ["Create new template", "button"]); - $this->execute('behat_forms::i_set_the_following_fields_to_these_values', [$generaldata]); - } - - /** - * Fills the previously initiated templates notification actions form with these values. - * - * @Given /^I set previous automation template notification to these values:$/ - * @throws ElementNotFoundException Thrown by behat_base::find - * @param TableNode $notificationdata - */ - public function i_set_previous_template_notification($notificationdata) { - - $this->execute("behat_general::i_click_on_in_the", ["Notification", "link", "#automation-tabs", "css_element"]); - $this->execute('behat_forms::i_set_the_following_fields_to_these_values', [$notificationdata]); - - } -} diff --git a/actions/notification/tests/behat/pulse_notification_template.feature b/actions/notification/tests/behat/pulse_notification_template.feature deleted file mode 100644 index e2a1d12..0000000 --- a/actions/notification/tests/behat/pulse_notification_template.feature +++ /dev/null @@ -1,171 +0,0 @@ -@mod @mod_pulse @mod_pulse_automation @mod_pulse_automation_template @pulseactions @pulseaction_notification_template -Feature: Configuring the pulseaction_notification plugin on the "Automation template" page, applying different configurations to the notification - In order to use the features - As admin - I need to be able to configure the pulse automation template - - Background: - Given the following "categories" exist: - | name | category | idnumber | - | Cat 1 | 0 | CAT1 | - And the following "course" exist: - | fullname | shortname | category | - | Course 1 | C1 | 0 | - And the following "activities" exist: - | activity | name | intro | course | idnumber | - | book | Test book name | Test book description | C1 | book1 | - And the following "users" exist: - | username | firstname | lastname | email | - | student1 | student | User 1 | student1@test.com | - | teacher1 | teacher | User 1 | teacher1@test.com | - And the following "course enrolments" exist: - | user | course | role | - | student1 | C1 | student | - | teacher1 | C1 | teacher | - And the following "permission overrides" exist: - | capability | permission | role | contextlevel | reference | - | pulseaction/notification:receivenotification | Allow | teacher | System | | - - @javascript - Scenario: Create notification template and instance - Given I log in as "admin" - And I navigate to automation templates - And I create pulse notification template "WELCOME MESSAGE" "WELCOMEMESSAGE_" to these values: - | Sender | Course teacher | - | Recipients | Student | - | Subject | Welcome to {Site_FullName} | - | Header content | Hi {User_firstname} {User_lastname},
Welcome to learning portal of {Site_FullName} | - | Footer content | Copyright @ 2023 {Site_FullName} | - Then I should see "Automation templates" - And I should see "WELCOME MESSAGE" in the "pulse_automation_template" "table" - And I navigate to course "Course 1" automation instances - And I create pulse notification instance "WELCOME MESSAGE" "COURSE_1" to these values: - | Recipients | Student | - And I should see "WELCOMEMESSAGE_COURSE_1" in the "pulse_automation_template" "table" - And I click on ".action-report" "css_element" in the "WELCOMEMESSAGE_COURSE_1" "table_row" - And I switch to a second window - Then ".reportbuilder-report" "css_element" should exist - And the following should exist in the "reportbuilder-table" table: - | Full name | Subject | Status | - | student User 1 | Welcome to Acceptance test site | Queued | - - @javascript - Scenario: Override notification template - Given I log in as "admin" - And I navigate to automation templates - And I create pulse notification template "WELCOME MESSAGE" "WELCOMEMESSAGE_" to these values: - | Sender | Course teacher | - | Recipients | Student | - | Subject | Welcome to {Site_FullName} | - | Header content | Hi {User_firstname} {User_lastname},
Welcome to learning portal of {Site_FullName} | - | Footer content | Copyright @ 2023 {Site_FullName} | - Then I should see "Automation templates" - And I should see "WELCOME MESSAGE" in the "pulse_automation_template" "table" - And I navigate to course "Course 1" automation instances - And I create pulse notification instance "WELCOME MESSAGE" "COURSE_1" to these values: - | override[pulsenotification_subject] | 1 | - | Subject | Welcome to learning portal {Site_FullName} | - And I should see "WELCOMEMESSAGE_COURSE_1" in the "pulse_automation_template" "table" - And I click on ".action-report" "css_element" in the "WELCOMEMESSAGE_COURSE_1" "table_row" - And I switch to a second window - Then ".reportbuilder-report" "css_element" should exist - And the following should exist in the "reportbuilder-table" table: - | Full name | Subject | Status | - | student User 1 | Welcome to learning portal Acceptance test site | Queued | - - @javascript - Scenario: Preview the notification content - Given I log in as "admin" - And I initiate new automation template to these values: - | Title | WELCOME MESSAGE | - | Reference | WELCOMEMESSAGE_ | - And I set previous automation template notification to these values: - | Sender | Course teacher | - | Recipients | Student | - | Subject | Welcome to {Site_FullName} | - | Header content | Hi {User_firstname} {User_lastname},
Welcome to {Site_FullName} | - | Footer content |

Copyright @ 2023 {Site_FullName}

| - And I press "Preview" - And I should see "Preview" in the ".modal-header .modal-title" "css_element" - Then I set the field "userselector" to "student User 1" - And I should see "Hi student User 1," in the ".modal .modal-body" "css_element" - And I should see "Welcome to Acceptance test site" in the ".modal .modal-body" "css_element" - And I should see "Copyright @ 2023 Acceptance test site" in the ".modal .modal-body" "css_element" - Then I click on ".close" "css_element" in the ".modal-header" "css_element" - And I press "Save changes" - Then I should see "Automation templates" - And I should see "WELCOME MESSAGE" in the "pulse_automation_template" "table" - And I navigate to course "Course 1" automation instances - And I initiate new automation instance of template "WELCOME MESSAGE" to these values: - | Reference | COURSE_1 | - And I set previous automation template notification to these values: - | Recipients | Student | - And I press "Preview" - And I should see "Preview" in the ".modal-header .modal-title" "css_element" - Then I set the field "userselector" to "student User 1" - And I should see "Hi student User 1," in the ".modal .modal-body" "css_element" - And I should see "Welcome to Acceptance test site" in the ".modal .modal-body" "css_element" - And I should see "Copyright @ 2023 Acceptance test site" in the ".modal .modal-body" "css_element" - Then I click on ".close" "css_element" in the ".modal-header" "css_element" - Then I set the field "Dynamic content" to "Test book name" - And I set the field "Content type" to "Description" - And I press "Preview" - And I should see "Test book description" in the ".modal-body .no-overflow" "css_element" - - @javascript - Scenario: Set delayed notification - Given I log in as "admin" - And I navigate to automation templates - And I create pulse notification template "WELCOME MESSAGE" "WELCOMEMESSAGE_" to these values: - | Sender | Course teacher | - | Recipients | Student | - | Subject | Welcome to {Site_FullName} | - | Header content | Hi {User_firstname} {User_lastname},
Welcome to learning portal of {Site_FullName} | - | Footer content | Copyright @ 2023 {Site_FullName} | - | Delay | After | - | pulsenotification_delayduration[number] | 10 | - Then I should see "Automation templates" - And I should see "WELCOME MESSAGE" in the "pulse_automation_template" "table" - And I navigate to course "Course 1" automation instances - And I create pulse notification instance "WELCOME MESSAGE" "COURSE_1" to these values: - | override[pulsenotification_subject] | 1 | - | Subject | Welcome to learning portal {Site_FullName} | - And I should see "WELCOMEMESSAGE_COURSE_1" in the "pulse_automation_template" "table" - And I click on ".action-report" "css_element" in the "WELCOMEMESSAGE_COURSE_1" "table_row" - And I switch to a second window - Then ".reportbuilder-report" "css_element" should exist - And the following should exist in the "reportbuilder-table" table: - | Full name | Subject | Status | Time created | Scheduled time | - | student User 1 | Welcome to learning portal Acceptance test site | Queued | ##now##%A, %d %B %Y, %I:%M %p## | ##+10 minutes##%A, %d %B %Y, %I:%M %p## | - - @javascript - Scenario Outline: Pulse action Notification: Send notification for different role users - Given I log in as "admin" - And I navigate to automation templates - And I create pulse notification template "WELCOME MESSAGE" "WELCOMEMESSAGE_" to these values: - | Sender | Custom | - | Sender email | test@test.com | - | Recipients | | - | Subject | Welcome to {Site_FullName} | - | Header content | Hi {User_firstname} {User_lastname},
Welcome to learning portal of {Site_FullName} | - Then I should see "Automation templates" - And I should see "WELCOME MESSAGE" in the "pulse_automation_template" "table" - And I navigate to course "Course 1" automation instances - # Demo content. - And I create pulse notification instance "WELCOME MESSAGE" "COURSE_1" to these values: - | Recipients | Student | - And I should see "WELCOMEMESSAGE_COURSE_1" in the "pulse_automation_template" "table" - And I click on ".action-report" "css_element" in the "WELCOMEMESSAGE_COURSE_1" "table_row" - And I switch to a second window - Then ".reportbuilder-report" "css_element" should exist - And the following exist in the "reportbuilder-table" table: - | Full name | Status | - | student User 1 | Queued | - And the following exist in the "reportbuilder-table" table: - | Full name | Status | - | teacher User 1 | Queued | - - Examples: - | recipient | studentshouldorshouldnot | teachershouldorshouldnot | - | Student | should | should not | - | Teacher | should not | should | diff --git a/actions/notification/version.php b/actions/notification/version.php deleted file mode 100644 index 39bf176..0000000 --- a/actions/notification/version.php +++ /dev/null @@ -1,28 +0,0 @@ -. - -/** - * Notification pulse action - Component and version details. - * - * @package pulseaction_notification - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -$plugin->component = "pulseaction_notification"; -$plugin->version = 2023080410; diff --git a/amd/build/automation.min.js b/amd/build/automation.min.js deleted file mode 100644 index bca0798..0000000 --- a/amd/build/automation.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("mod_pulse/automation",["jquery","core/modal_factory","core/templates","core/str","core/notification"],(function($,Modal,Template,Str,notification){const moveOverRidePosition=function(){var group="checkboxgroupautomation";return null===document.querySelectorAll("input[type=checkbox]."+group)||(document.querySelectorAll("input[type=checkbox]."+group).forEach((overElement=>{var id=overElement.id;id=id.replace("id_override_","");var element=document.querySelector("div#fitem_id_"+id);if(null===element&&null===(element=document.querySelector("div#fgroup_id_"+id)))return!0;var parent=overElement.parentNode;parent.innerHTML+='';var nodeToMove=document.createElement("div");return nodeToMove.classList.add("custom-control","custom-switch"),nodeToMove.append(parent),element.querySelector(".felement").append(nodeToMove),!0})),function(){if(null===document.querySelectorAll("input[type=checkbox].checkboxgroupautomation")||null===document.querySelectorAll('[data-fieldtype="autocomplete"], [data-fieldtype="tags"]'))return!0;var capinput=document.querySelector('input[name="hasinstanceeditcapability"]'),hasCapability=capinput&&capinput.value||null===capinput;document.querySelectorAll('[data-fieldtype="autocomplete"], [data-fieldtype="tags"]').forEach((element=>{if(null===element)return!0;var observer=new MutationObserver((function(mutations){mutations.forEach((mutation=>{var target=mutation.target;!hasCapability&&target.querySelector('input[data-fieldtype="autocomplete"]')&&(target.querySelector('input[data-fieldtype="autocomplete"]').disabled=!0);var overrideElement=target.querySelector(".custom-switch");null!==overrideElement&&(overrideElement.parentNode.append(overrideElement),observer.disconnect())}))}));return observer.observe(element,{attributes:!0,childList:!0,subtree:!0}),!0}))}()),!0};return{init:function(){null!==document.forms["pulse-automation-template"]&&(document.forms["pulse-automation-template"].onsubmit=e=>{var invalidElement=e.target.querySelector(".is-invalid");if(null===invalidElement)return!0;var hrefSelector='[href="#'+invalidElement.parentNode.parentNode.parentNode.id+'"]';return document.querySelector(hrefSelector).click(),!0}),function(){var templateReference=document.querySelector("#pulse-template-reference"),instanceReference=document.querySelector("#fitem_id_insreference .felement");templateReference&&instanceReference&&(templateReference.classList.remove("hide"),instanceReference.prepend(templateReference));const trigger=document.querySelectorAll('[data-target="overridemodal"]');null!==trigger&&trigger.forEach((elem=>{elem.nextSibling.querySelector(".felement").append(elem),elem.addEventListener("click",(function(e){e.preventDefault();var data=e.target.dataset,instance=document.querySelector("[name=overinstance_"+data.element+"]");if(null!==instance){var overrides=JSON.parse(instance.value);overrides.map((value=>(value.url=M.cfg.wwwroot+"/mod/pulse/automation/instances/edit.php?instanceid="+value.id+"&sesskey="+M.cfg.sesskey,value))),Modal.create({title:Str.get_string("instanceoverrides","pulse"),body:Template.render("mod_pulse/overrides",{instances:overrides})}).then((modal=>(modal.show(),!0))).catch(notification.exception)}}))}))}(),moveOverRidePosition(),null!==document.forms["pulse-automation-template"]&&(document.forms["pulse-automation-template"].onsubmit=()=>document.querySelector('[name="title"]').removeAttribute("disabled"))},instanceMenuLink:function(){(navMenu=>{if(null!==navMenu){var menu=navMenu.querySelector("a.automation-templates");null!==menu&&((menu=menu.parentNode).dataset.forceintomoremenu=!1,menu.querySelector("a").classList.remove("dropdown-item"),menu.querySelector("a").classList.add("nav-link"),menu.parentNode.removeChild(menu),navMenu.insertBefore(menu,navMenu.children[1]),window.dispatchEvent(new Event("resize")))}})(document.querySelector(".secondary-navigation ul.more-nav"))}}})); - -//# sourceMappingURL=automation.min.js.map \ No newline at end of file diff --git a/amd/build/automation.min.js.map b/amd/build/automation.min.js.map deleted file mode 100644 index c53fc9f..0000000 --- a/amd/build/automation.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"automation.min.js","sources":["../src/automation.js"],"sourcesContent":["define(\"mod_pulse/automation\", ['jquery', 'core/modal_factory', 'core/templates', 'core/str', \"core/notification\"],\n function($, Modal, Template, Str, notification) {\n\n const moveOutMoreMenu = (navMenu) => {\n\n if (navMenu === null) {\n return;\n }\n\n var menu = navMenu.querySelector('a.automation-templates');\n\n if (menu === null) {\n return;\n }\n\n menu = menu.parentNode;\n menu.dataset.forceintomoremenu = false;\n menu.querySelector('a').classList.remove('dropdown-item');\n menu.querySelector('a').classList.add('nav-link');\n menu.parentNode.removeChild(menu);\n\n // Insert the stored menus before the more menu.\n navMenu.insertBefore(menu, navMenu.children[1]);\n window.dispatchEvent(new Event('resize')); // Dispatch the resize event to create more menu.\n };\n\n const returnToFailedTab = () => {\n\n if (document.forms['pulse-automation-template'] === null) {\n return false;\n }\n\n document.forms['pulse-automation-template'].onsubmit = (e) => {\n var form = e.target;\n var invalidElement = form.querySelector('.is-invalid');\n if (invalidElement === null) {\n return true;\n }\n\n var tabid = invalidElement.parentNode.parentNode.parentNode.id;\n var hrefSelector = '[href=\"#' + tabid + '\"]';\n\n document.querySelector(hrefSelector).click();\n\n return true;\n };\n\n return true;\n };\n\n // No need.\n const updateAutoCompletionPositions = function() {\n var group = \"checkboxgroupautomation\";\n\n if (document.querySelectorAll('input[type=checkbox].' + group)\n === null || document.querySelectorAll('[data-fieldtype=\"autocomplete\"], [data-fieldtype=\"tags\"]') === null) {\n return true;\n }\n\n\n var capinput = document.querySelector('input[name=\"hasinstanceeditcapability\"]');\n var hasCapability = (capinput && capinput.value) || capinput === null;\n\n // Observer the autocomplete.\n document.querySelectorAll('[data-fieldtype=\"autocomplete\"], [data-fieldtype=\"tags\"]').forEach((element) => {\n\n if (element === null) {\n return true;\n }\n\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach((mutation) => {\n var target = mutation.target;\n\n // Not has capability to edit instance, then hide the autocomplete input text.\n if (!hasCapability && target.querySelector('input[data-fieldtype=\"autocomplete\"]')) {\n target.querySelector('input[data-fieldtype=\"autocomplete\"]').disabled = true;\n }\n\n var overrideElement = target.querySelector('.custom-switch');\n if (overrideElement === null) {\n return;\n }\n overrideElement.parentNode.append(overrideElement);\n observer.disconnect();\n });\n });\n observer.observe(element, {attributes: true, childList: true, subtree: true});\n return true;\n });\n\n return true;\n };\n\n const moveOverRidePosition = function() {\n\n var group = \"checkboxgroupautomation\";\n\n if (document.querySelectorAll('input[type=checkbox].' + group) === null) {\n return true;\n }\n\n document.querySelectorAll('input[type=checkbox].' + group).forEach((overElement) => {\n var id = overElement.id;\n id = id.replace('id_override_', '');\n var element = document.querySelector('div#fitem_id_' + id);\n if (element === null) {\n element = document.querySelector('div#fgroup_id_' + id);\n if (element === null) {\n return true;\n }\n }\n var parent = overElement.parentNode;\n parent.innerHTML += '';\n var nodeToMove = document.createElement('div');\n nodeToMove.classList.add('custom-control', 'custom-switch');\n nodeToMove.append(parent);\n element.querySelector(\".felement\").append(nodeToMove);\n return true;\n });\n // Move the override button for autocompletion fields after the autocomplete nodes are created.\n updateAutoCompletionPositions();\n\n return true;\n };\n\n /**\n * Create a modal to display the list of instances which is overriden the template setting.\n *\n * @returns {void}\n */\n const overrideModal = function() {\n\n // Add the template reference as prefix of the instance reference.\n var templateReference = document.querySelector('#pulse-template-reference');\n var instanceReference = document.querySelector('#fitem_id_insreference .felement');\n if (templateReference && instanceReference) {\n templateReference.classList.remove('hide');\n instanceReference.prepend(templateReference);\n }\n\n const trigger = document.querySelectorAll('[data-target=\"overridemodal\"]');\n\n if (trigger === null) {\n return;\n }\n\n trigger.forEach((elem) => {\n\n elem.nextSibling.querySelector('.felement').append(elem);\n\n elem.addEventListener('click', function(e) {\n e.preventDefault();\n var element = e.target;\n var data = element.dataset;\n var instance = document.querySelector('[name=overinstance_' + data.element + ']');\n if (instance !== null) {\n var overrides = JSON.parse(instance.value);\n overrides.map((value) => {\n var path = '/mod/pulse/automation/instances/edit.php?instanceid=';\n value.url = M.cfg.wwwroot + path + value.id + '&sesskey=' + M.cfg.sesskey;\n return value;\n });\n Modal.create({\n title: Str.get_string('instanceoverrides', 'pulse'),\n body: Template.render('mod_pulse/overrides', {instances: overrides})\n }).then((modal) => {\n modal.show();\n return true;\n }).catch(notification.exception);\n }\n });\n });\n };\n\n const enableTitleOnSubmit = function() {\n if (document.forms['pulse-automation-template'] === null) {\n return;\n }\n document.forms['pulse-automation-template'].onsubmit =\n () => document.querySelector('[name=\"title\"]').removeAttribute(\"disabled\");\n };\n\n return {\n\n init: function() {\n returnToFailedTab();\n overrideModal();\n moveOverRidePosition();\n enableTitleOnSubmit();\n },\n\n instanceMenuLink: function() {\n var primaryNav = document.querySelector('.secondary-navigation ul.more-nav');\n moveOutMoreMenu(primaryNav);\n },\n\n };\n});\n"],"names":["define","$","Modal","Template","Str","notification","moveOverRidePosition","group","document","querySelectorAll","forEach","overElement","id","replace","element","querySelector","parent","parentNode","innerHTML","nodeToMove","createElement","classList","add","append","capinput","hasCapability","value","observer","MutationObserver","mutations","mutation","target","disabled","overrideElement","disconnect","observe","attributes","childList","subtree","updateAutoCompletionPositions","init","forms","onsubmit","e","invalidElement","hrefSelector","click","templateReference","instanceReference","remove","prepend","trigger","elem","nextSibling","addEventListener","preventDefault","data","dataset","instance","overrides","JSON","parse","map","url","M","cfg","wwwroot","sesskey","create","title","get_string","body","render","instances","then","modal","show","catch","exception","overrideModal","removeAttribute","instanceMenuLink","navMenu","menu","forceintomoremenu","removeChild","insertBefore","children","window","dispatchEvent","Event","moveOutMoreMenu"],"mappings":"AAAAA,8BAA+B,CAAC,SAAU,qBAAsB,iBAAkB,WAAY,sBAC1F,SAASC,EAAGC,MAAOC,SAAUC,IAAKC,oBA6F5BC,qBAAuB,eAErBC,MAAQ,iCAEuD,OAA/DC,SAASC,iBAAiB,wBAA0BF,SAIxDC,SAASC,iBAAiB,wBAA0BF,OAAOG,SAASC,kBAC5DC,GAAKD,YAAYC,GACrBA,GAAKA,GAAGC,QAAQ,eAAgB,QAC5BC,QAAUN,SAASO,cAAc,gBAAkBH,OACvC,OAAZE,SAEgB,QADhBA,QAAUN,SAASO,cAAc,iBAAmBH,YAEzC,MAGXI,OAASL,YAAYM,WACzBD,OAAOE,WAAa,iDAChBC,WAAaX,SAASY,cAAc,cACxCD,WAAWE,UAAUC,IAAI,iBAAkB,iBAC3CH,WAAWI,OAAOP,QAClBF,QAAQC,cAAc,aAAaQ,OAAOJ,aACnC,KAnEuB,cAI1B,OADJX,SAASC,iBAAiB,iDAC4E,OAA1FD,SAASC,iBAAiB,mEAC/B,MAIPe,SAAWhB,SAASO,cAAc,2CAClCU,cAAiBD,UAAYA,SAASE,OAAuB,OAAbF,SAGpDhB,SAASC,iBAAiB,4DAA4DC,SAASI,aAE3E,OAAZA,eACO,MAGPa,SAAW,IAAIC,kBAAiB,SAASC,WACzCA,UAAUnB,SAASoB,eACXC,OAASD,SAASC,QAGjBN,eAAiBM,OAAOhB,cAAc,0CACvCgB,OAAOhB,cAAc,wCAAwCiB,UAAW,OAGxEC,gBAAkBF,OAAOhB,cAAc,kBACnB,OAApBkB,kBAGJA,gBAAgBhB,WAAWM,OAAOU,iBAClCN,SAASO,2BAGjBP,SAASQ,QAAQrB,QAAS,CAACsB,YAAY,EAAMC,WAAW,EAAMC,SAAS,KAChE,KAiCXC,KAtBW,SAoFR,CAEHC,KAAM,WA7J8C,OAAhDhC,SAASiC,MAAM,+BAInBjC,SAASiC,MAAM,6BAA6BC,SAAYC,QAEhDC,eADOD,EAAEZ,OACahB,cAAc,kBACjB,OAAnB6B,sBACO,MAIPC,aAAe,WADPD,eAAe3B,WAAWA,WAAWA,WAAWL,GACpB,YAExCJ,SAASO,cAAc8B,cAAcC,SAE9B,IAuFO,eAGdC,kBAAoBvC,SAASO,cAAc,6BAC3CiC,kBAAoBxC,SAASO,cAAc,oCAC3CgC,mBAAqBC,oBACrBD,kBAAkB1B,UAAU4B,OAAO,QACnCD,kBAAkBE,QAAQH,0BAGxBI,QAAU3C,SAASC,iBAAiB,iCAE1B,OAAZ0C,SAIJA,QAAQzC,SAAS0C,OAEbA,KAAKC,YAAYtC,cAAc,aAAaQ,OAAO6B,MAEnDA,KAAKE,iBAAiB,SAAS,SAASX,GACpCA,EAAEY,qBAEEC,KADUb,EAAEZ,OACG0B,QACfC,SAAWlD,SAASO,cAAc,sBAAwByC,KAAK1C,QAAU,QAC5D,OAAb4C,SAAmB,KACfC,UAAYC,KAAKC,MAAMH,SAAShC,OACpCiC,UAAUG,KAAKpC,QAEXA,MAAMqC,IAAMC,EAAEC,IAAIC,QADP,uDACwBxC,MAAMd,GAAK,YAAcoD,EAAEC,IAAIE,QAC3DzC,SAEXxB,MAAMkE,OAAO,CACTC,MAAOjE,IAAIkE,WAAW,oBAAqB,SAC3CC,KAAMpE,SAASqE,OAAO,sBAAuB,CAACC,UAAWd,cAC1De,MAAMC,QACLA,MAAMC,QACC,KACRC,MAAMxE,aAAayE,kBAkB9BC,GACAzE,uBAZgD,OAAhDE,SAASiC,MAAM,+BAGnBjC,SAASiC,MAAM,6BAA6BC,SACxC,IAAMlC,SAASO,cAAc,kBAAkBiE,gBAAgB,cAYnEC,iBAAkB,WA7LGC,CAAAA,aAEL,OAAZA,aAIAC,KAAOD,QAAQnE,cAAc,0BAEpB,OAAToE,QAIJA,KAAOA,KAAKlE,YACPwC,QAAQ2B,mBAAoB,EACjCD,KAAKpE,cAAc,KAAKM,UAAU4B,OAAO,iBACzCkC,KAAKpE,cAAc,KAAKM,UAAUC,IAAI,YACtC6D,KAAKlE,WAAWoE,YAAYF,MAG5BD,QAAQI,aAAaH,KAAMD,QAAQK,SAAS,IAC5CC,OAAOC,cAAc,IAAIC,MAAM,cA2K3BC,CADiBnF,SAASO,cAAc"} \ No newline at end of file diff --git a/amd/build/bulkaction.min.js b/amd/build/bulkaction.min.js deleted file mode 100644 index 6428e9c..0000000 --- a/amd/build/bulkaction.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Contain the bulk action for the instance management. - * - * @module mod_pulse/bulkaction - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -define("mod_pulse/bulkaction",["jquery","core/fragment","core/modal_factory","core/modal_events","core/notification","core/str","core/ajax","core/templates"],(function($,Fragment,ModalFactory,ModalEvents,notification,String,Ajax,Templates){const Selectors_table="#manage-instance-table",Selectors_selectAll="#manage-instance-tab .selectall-pulse-bulkaction",Selectors_deselectAll="#manage-instance-tab .deselectall-pulse-bulkaction",Selectors_selectwithoutinsbtn="#manage-instance-tab #selectwithoutins-btn",Selectors_CheckBoxes="input[name='bc[]']",Selectors_DeleteBtn="#manage-instance-tab #bulkdelete-btn",Selectors_AddBtn="#manage-instance-tab #bulkadd-btn",Selectors_EnableBtn="#manage-instance-tab #bulkenable-btn",Selectors_DisableBtn="#manage-instance-tab #bulkdisable-btn",Selectors_CheckedBoxes="input[name='bc[]']:checked",Selectors_checkboxgroups="#manage-instance-tab .bulkaction-group",Selectors_tabUL="#automation-tabs",Selectors_tabPane="#pulsetemplates-tab-content .tab-pane",Selectors_tabContent="#pulsetemplates-tab-content",Selectors_manageInstanceTab="#manage-instance-tab",Selectors_templateForm=".mform#pulse-automation-template",Selectors_activeNav="#automation-tabs .nav-link.active";function bulkaction(){const select=document.querySelector(Selectors_selectAll),deselect=document.querySelector(Selectors_deselectAll),checkboxelm=()=>document.querySelector(Selectors_table).querySelectorAll(Selectors_CheckBoxes),selectwithoutins=document.querySelector(Selectors_selectwithoutinsbtn),checkboxgroup=document.querySelector(Selectors_checkboxgroups);select.addEventListener("click",(()=>{checkboxelm().forEach((checkbox=>{checkbox.checked=!0,checkboxgroup.classList.remove("hide")}))})),deselect.addEventListener("click",(()=>{checkboxelm().forEach((checkbox=>{checkbox.checked=!1,checkboxgroup.classList.add("hide")}))})),selectwithoutins.addEventListener("click",(()=>{checkboxelm().forEach((checkbox=>{checkbox.classList.contains("emptyinstance")&&(checkbox.checked=!0,checkboxgroup.classList.remove("hide"))}))})),document.addEventListener("change",(function(e){e.target.matches(Selectors_CheckBoxes)&&(Array.from(checkboxelm()).some((function(checkbox){return checkbox.checked}))?checkboxgroup.classList.remove("hide"):checkboxgroup.classList.add("hide"))}))}function getcourseids(){var courseids=[];return document.querySelectorAll(Selectors_CheckedBoxes).forEach((checkedbox=>{courseids.push(checkedbox.value)})),courseids}function getInstanceModal(courseids,params,action){var args={templateid:params,courseids:courseids,action:action};ModalFactory.create({type:ModalFactory.types.SAVE_CANCEL,title:String.get_string("confirmation","pulse"),body:String.get_string("confirm"+action+"instance","pulse"),large:!1}).then((function(modal){return modal.setButtonText("save",String.get_string("yes")),modal.getRoot().on(ModalEvents.save,(e=>{e.preventDefault(),function(params){Ajax.call([{methodname:"mod_pulse_manage_instances",args:params,done:function(response){window.location.reload(),response.message&¬ification.addNotification({message:response.message,type:"success"})}}])}(args),function(params){var table=document.querySelector(Selectors_table);Fragment.loadFragment("mod_pulse","get_manageinstance_table",1,params).done(((html,js)=>{Templates.replaceNode(table,html,js)}))}(args),modal.getRoot().find("form").submit(),modal.hide()})),modal.getRoot().on(ModalEvents.hidden,(function(){modal.destroy()})),modal.show(),modal})).catch(notification.exception)}return{init:function(params){bulkaction(),function(params){const deletebtn=document.querySelector(Selectors_DeleteBtn),addbtn=document.querySelector(Selectors_AddBtn),disableBtn=document.querySelector(Selectors_DisableBtn),enableBtn=document.querySelector(Selectors_EnableBtn);deletebtn.addEventListener("click",(function(){getInstanceModal(getcourseids(),params,"delete")})),addbtn.addEventListener("click",(function(){getInstanceModal(getcourseids(),params,"add")})),disableBtn.addEventListener("click",(function(){getInstanceModal(getcourseids(),params,"disable")})),enableBtn.addEventListener("click",(function(){getInstanceModal(getcourseids(),params,"enable")})),document.querySelector(Selectors_tabUL).addEventListener("click",(function(e){templateInstanceFilter(e)}));const templateInstanceFilter=e=>{document.querySelectorAll(Selectors_tabPane).forEach((e=>{e.classList.remove("active"),e.classList.remove("show")}));var href=null!==e&&e.target.matches("#automation-tabs .nav-link")?e.target.getAttribute("href"):activeTabHref();document.querySelector(Selectors_tabContent+" "+href).classList.add("active"),document.querySelector(Selectors_tabContent+" "+href).classList.add("show"),document.querySelector(Selectors_templateForm).style.display=href==Selectors_manageInstanceTab?"none":"block"},activeTabHref=()=>!document.querySelector(Selectors_activeNav)||document.querySelector(Selectors_activeNav).getAttribute("href");templateInstanceFilter(null)}(params)}}})); - -//# sourceMappingURL=bulkaction.min.js.map \ No newline at end of file diff --git a/amd/build/bulkaction.min.js.map b/amd/build/bulkaction.min.js.map deleted file mode 100644 index 2e1b8c3..0000000 --- a/amd/build/bulkaction.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bulkaction.min.js","sources":["../src/bulkaction.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Contain the bulk action for the instance management.\n *\n * @module mod_pulse/bulkaction\n * @copyright 2023, bdecent gmbh bdecent.de\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\"mod_pulse/bulkaction\", [\"jquery\", \"core/fragment\", \"core/modal_factory\",\n\"core/modal_events\", \"core/notification\", \"core/str\", \"core/ajax\", \"core/templates\"],\n (function($, Fragment, ModalFactory, ModalEvents, notification, String, Ajax, Templates) {\n\n /**\n * Instance management page selectors.\n */\n const Selectors = {\n table: '#manage-instance-table',\n selectAll: '#manage-instance-tab .selectall-pulse-bulkaction',\n deselectAll: '#manage-instance-tab .deselectall-pulse-bulkaction',\n selectwithoutinsbtn: '#manage-instance-tab #selectwithoutins-btn',\n CheckBoxes: \"input[name='bc[]']\",\n DeleteBtn: '#manage-instance-tab #bulkdelete-btn',\n AddBtn: '#manage-instance-tab #bulkadd-btn',\n EnableBtn: '#manage-instance-tab #bulkenable-btn',\n DisableBtn: '#manage-instance-tab #bulkdisable-btn',\n CheckedBoxes: \"input[name='bc[]']:checked\",\n checkboxgroups: '#manage-instance-tab .bulkaction-group',\n tabUL: '#automation-tabs',\n tabPane: '#pulsetemplates-tab-content .tab-pane',\n tabContent: '#pulsetemplates-tab-content',\n manageInstanceTab: '#manage-instance-tab',\n templateForm: '.mform#pulse-automation-template',\n activeNav: '#automation-tabs .nav-link.active',\n };\n\n /**\n * Init the bulk select actions in the instance management tab.\n */\n function bulkaction() {\n const select = document.querySelector(Selectors.selectAll);\n const deselect = document.querySelector(Selectors.deselectAll);\n const tableRoot = () => document.querySelector(Selectors.table);\n const checkboxelm = () => tableRoot().querySelectorAll(Selectors.CheckBoxes);\n const selectwithoutins = document.querySelector(Selectors.selectwithoutinsbtn);\n const checkboxgroup = document.querySelector(Selectors.checkboxgroups);\n\n // Whent click the select all button on the instance management tab.\n select.addEventListener('click', () => {\n checkboxelm().forEach(checkbox => {\n checkbox.checked = true;\n checkboxgroup.classList.remove('hide');\n });\n });\n\n // Whent click the de-select all button on the instance management tab.\n deselect.addEventListener('click', () => {\n checkboxelm().forEach(checkbox => {\n checkbox.checked = false;\n checkboxgroup.classList.add('hide');\n });\n });\n\n // Whent click the select all without instances button on the instance management tab.\n selectwithoutins.addEventListener('click', () => {\n checkboxelm().forEach(checkbox => {\n if (checkbox.classList.contains(\"emptyinstance\")) {\n checkbox.checked = true;\n checkboxgroup.classList.remove('hide');\n }\n });\n });\n\n // Add event listener to checkboxes.\n document.addEventListener('change', function(e) {\n\n if (e.target.matches(Selectors.CheckBoxes)) {\n // Check if at least one checkbox is checked.\n var atLeastOneChecked = Array.from(checkboxelm()).some(function(checkbox) {\n return checkbox.checked;\n });\n\n // Toggle visibility of bulk edit action based on checkbox status.\n if (atLeastOneChecked) {\n checkboxgroup.classList.remove('hide');\n } else {\n checkboxgroup.classList.add('hide');\n }\n }\n });\n }\n\n /**\n * Return the selected check boxes coursed ids for the instance management.\n *\n * @returns array $courseids course Ids\n */\n function getcourseids() {\n var courseids = [];\n var checkedboxes = document.querySelectorAll(Selectors.CheckedBoxes);\n checkedboxes.forEach(checkedbox => {\n courseids.push(checkedbox.value);\n });\n return courseids;\n }\n\n /**\n * Manage the automation instances confirmation and bulk action in modal.\n *\n * @param {int} params\n */\n function manageInstances(params) {\n const deletebtn = document.querySelector(Selectors.DeleteBtn);\n const addbtn = document.querySelector(Selectors.AddBtn);\n const disableBtn = document.querySelector(Selectors.DisableBtn);\n const enableBtn = document.querySelector(Selectors.EnableBtn);\n\n // Click the delete instance bulk action button.\n deletebtn.addEventListener('click', function() {\n var courseids = getcourseids();\n getInstanceModal(courseids, params, 'delete');\n });\n\n // Click the add instance bulk action button.\n addbtn.addEventListener('click', function() {\n var courseids = getcourseids();\n getInstanceModal(courseids, params, 'add');\n });\n\n // Click the disable instance bulk action button.\n disableBtn.addEventListener('click', function() {\n var courseids = getcourseids();\n getInstanceModal(courseids, params, 'disable');\n });\n\n // Click the enable instance bulk action button.\n enableBtn.addEventListener('click', function() {\n var courseids = getcourseids();\n getInstanceModal(courseids, params, 'enable');\n });\n\n // Show/hide the instance manage tab of template.\n // Moved the tab outside the form, default tab handlers not works. Used custom method to show hide.\n document.querySelector(Selectors.tabUL).addEventListener('click', function(e) {\n templateInstanceFilter(e);\n });\n\n const templateInstanceFilter = (e) => {\n\n document.querySelectorAll(Selectors.tabPane).forEach((e) => {\n e.classList.remove('active');\n e.classList.remove('show');\n });\n // Remove the active.\n var href = (e === null || !e.target.matches('#automation-tabs .nav-link')) ? activeTabHref() :\n e.target.getAttribute('href');\n\n document.querySelector(Selectors.tabContent + ' ' + href).classList.add('active');\n document.querySelector(Selectors.tabContent + ' ' + href).classList.add('show');\n\n // Hide the form.\n if (href == Selectors.manageInstanceTab) {\n document.querySelector(Selectors.templateForm).style.display = 'none';\n } else {\n document.querySelector(Selectors.templateForm).style.display = 'block';\n }\n };\n\n // Find the active ul.\n const activeTabHref = () => {\n return !document.querySelector(Selectors.activeNav)\n || document.querySelector(Selectors.activeNav).getAttribute('href');\n };\n\n templateInstanceFilter(null);\n }\n\n /**\n * Get the instance management confirmation modal.\n *\n * @param {array} courseids Course Ids\n * @param {int} params Templated ID\n * @param {string} action Bulk action name\n */\n function getInstanceModal(courseids, params, action) {\n var args = {templateid: params, courseids: courseids, action: action};\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: String.get_string('confirmation', 'pulse'),\n body: String.get_string('confirm' + action + 'instance', 'pulse'),\n large: false\n })\n .then(function(modal) {\n\n modal.setButtonText('save', String.get_string('yes'));\n\n modal.getRoot().on(ModalEvents.save, e => {\n e.preventDefault();\n submitformdata(args);\n loadInstancetable(args);\n modal.getRoot().find('form').submit();\n modal.hide();\n });\n\n modal.getRoot().on(ModalEvents.hidden, function() {\n modal.destroy();\n });\n modal.show();\n return modal;\n }).catch(notification.exception);\n }\n\n /**\n * Submit and recieve the message form the modal confirmation on the instance management.\n *\n * @param {string} params\n */\n function submitformdata(params) {\n Ajax.call([{\n methodname: 'mod_pulse_manage_instances',\n args: params,\n done: function(response) {\n window.location.reload();\n if (response.message) {\n notification.addNotification({\n message: response.message,\n type: \"success\"\n });\n }\n }\n }]);\n }\n\n /**\n * Load the current manage instance table to replace the table root.\n *\n * @param {string} params\n */\n function loadInstancetable(params) {\n var table = document.querySelector(Selectors.table);\n Fragment.loadFragment('mod_pulse', 'get_manageinstance_table', 1, params).done((html, js) => {\n Templates.replaceNode(table, html, js);\n });\n }\n\n return {\n init: function(params) {\n bulkaction();\n manageInstances(params);\n },\n };\n\n }));\n"],"names":["define","$","Fragment","ModalFactory","ModalEvents","notification","String","Ajax","Templates","Selectors","bulkaction","select","document","querySelector","deselect","checkboxelm","querySelectorAll","selectwithoutins","checkboxgroup","addEventListener","forEach","checkbox","checked","classList","remove","add","contains","e","target","matches","Array","from","some","getcourseids","courseids","checkedbox","push","value","getInstanceModal","params","action","args","templateid","create","type","types","SAVE_CANCEL","title","get_string","body","large","then","modal","setButtonText","getRoot","on","save","preventDefault","call","methodname","done","response","window","location","reload","message","addNotification","submitformdata","table","loadFragment","html","js","replaceNode","loadInstancetable","find","submit","hide","hidden","destroy","show","catch","exception","init","deletebtn","addbtn","disableBtn","enableBtn","templateInstanceFilter","href","getAttribute","activeTabHref","style","display","manageInstances"],"mappings":";;;;;;;AAuBAA,8BAA+B,CAAC,SAAU,gBAAiB,qBAC3D,oBAAqB,oBAAqB,WAAY,YAAa,mBAC9D,SAASC,EAAGC,SAAUC,aAAcC,YAAaC,aAAcC,OAAQC,KAAMC,iBAKpEC,gBACK,yBADLA,oBAES,mDAFTA,sBAGW,qDAHXA,8BAImB,6CAJnBA,qBAKU,qBALVA,oBAMS,wCANTA,iBAOM,qCAPNA,oBAQS,wCARTA,qBASU,yCATVA,uBAUY,6BAVZA,yBAWc,yCAXdA,gBAYK,mBAZLA,kBAaO,wCAbPA,qBAcU,8BAdVA,4BAeiB,uBAfjBA,uBAgBY,mCAhBZA,oBAiBS,6CAMNC,mBACCC,OAASC,SAASC,cAAcJ,qBAChCK,SAAWF,SAASC,cAAcJ,uBAElCM,YAAc,IADIH,SAASC,cAAcJ,iBACTO,iBAAiBP,sBACjDQ,iBAAmBL,SAASC,cAAcJ,+BAC1CS,cAAgBN,SAASC,cAAcJ,0BAG7CE,OAAOQ,iBAAiB,SAAS,KAC7BJ,cAAcK,SAAQC,WAClBA,SAASC,SAAU,EACnBJ,cAAcK,UAAUC,OAAO,cAKvCV,SAASK,iBAAiB,SAAS,KAC/BJ,cAAcK,SAAQC,WAClBA,SAASC,SAAU,EACnBJ,cAAcK,UAAUE,IAAI,cAKpCR,iBAAiBE,iBAAiB,SAAS,KACvCJ,cAAcK,SAAQC,WACdA,SAASE,UAAUG,SAAS,mBAC5BL,SAASC,SAAU,EACnBJ,cAAcK,UAAUC,OAAO,eAM3CZ,SAASO,iBAAiB,UAAU,SAASQ,GAErCA,EAAEC,OAAOC,QAAQpB,wBAEOqB,MAAMC,KAAKhB,eAAeiB,MAAK,SAASX,iBACrDA,SAASC,WAKhBJ,cAAcK,UAAUC,OAAO,QAE/BN,cAAcK,UAAUE,IAAI,qBAWnCQ,mBACDC,UAAY,UACGtB,SAASI,iBAAiBP,wBAChCW,SAAQe,aACjBD,UAAUE,KAAKD,WAAWE,UAEvBH,mBAiFFI,iBAAiBJ,UAAWK,OAAQC,YACrCC,KAAO,CAACC,WAAYH,OAAQL,UAAWA,UAAWM,OAAQA,QAC9DrC,aAAawC,OAAO,CAChBC,KAAMzC,aAAa0C,MAAMC,YACzBC,MAAOzC,OAAO0C,WAAW,eAAgB,SACzCC,KAAM3C,OAAO0C,WAAW,UAAYR,OAAS,WAAY,SACzDU,OAAO,IAENC,MAAK,SAASC,cAEXA,MAAMC,cAAc,OAAQ/C,OAAO0C,WAAW,QAE9CI,MAAME,UAAUC,GAAGnD,YAAYoD,MAAM7B,IACjCA,EAAE8B,0BAoBMlB,QACpBhC,KAAKmD,KAAK,CAAC,CACPC,WAAY,6BACZlB,KAAMF,OACNqB,KAAM,SAASC,UACXC,OAAOC,SAASC,SACZH,SAASI,SACT5D,aAAa6D,gBAAgB,CACzBD,QAASJ,SAASI,QAClBrB,KAAM,gBA5BVuB,CAAe1B,eAwCJF,YACnB6B,MAAQxD,SAASC,cAAcJ,iBACnCP,SAASmE,aAAa,YAAa,2BAA4B,EAAG9B,QAAQqB,MAAK,CAACU,KAAMC,MAClF/D,UAAUgE,YAAYJ,MAAOE,KAAMC,OA1C3BE,CAAkBhC,MAClBW,MAAME,UAAUoB,KAAK,QAAQC,SAC7BvB,MAAMwB,UAGVxB,MAAME,UAAUC,GAAGnD,YAAYyE,QAAQ,WACnCzB,MAAM0B,aAEV1B,MAAM2B,OACC3B,SACR4B,MAAM3E,aAAa4E,iBAoCvB,CACHC,KAAM,SAAS3C,QACX7B,sBAxIiB6B,cACf4C,UAAYvE,SAASC,cAAcJ,qBACnC2E,OAASxE,SAASC,cAAcJ,kBAChC4E,WAAazE,SAASC,cAAcJ,sBACpC6E,UAAY1E,SAASC,cAAcJ,qBAGzC0E,UAAUhE,iBAAiB,SAAS,WAEhCmB,iBADgBL,eACYM,OAAQ,aAIxC6C,OAAOjE,iBAAiB,SAAS,WAE7BmB,iBADgBL,eACYM,OAAQ,UAIxC8C,WAAWlE,iBAAiB,SAAS,WAEjCmB,iBADgBL,eACYM,OAAQ,cAIxC+C,UAAUnE,iBAAiB,SAAS,WAEhCmB,iBADgBL,eACYM,OAAQ,aAKxC3B,SAASC,cAAcJ,iBAAiBU,iBAAiB,SAAS,SAASQ,GACvE4D,uBAAuB5D,YAGrB4D,uBAA0B5D,IAE5Bf,SAASI,iBAAiBP,mBAAmBW,SAASO,IAClDA,EAAEJ,UAAUC,OAAO,UACnBG,EAAEJ,UAAUC,OAAO,eAGnBgE,KAAc,OAAN7D,GAAeA,EAAEC,OAAOC,QAAQ,8BACxCF,EAAEC,OAAO6D,aAAa,QADmDC,gBAG7E9E,SAASC,cAAcJ,qBAAuB,IAAM+E,MAAMjE,UAAUE,IAAI,UACxEb,SAASC,cAAcJ,qBAAuB,IAAM+E,MAAMjE,UAAUE,IAAI,QAIpEb,SAASC,cAAcJ,wBAAwBkF,MAAMC,QADrDJ,MAAQ/E,4BACuD,OAEA,SAKjEiF,cAAgB,KACV9E,SAASC,cAAcJ,sBACxBG,SAASC,cAAcJ,qBAAqBgF,aAAa,QAGpEF,uBAAuB,MA0EnBM,CAAgBtD"} \ No newline at end of file diff --git a/amd/src/automation.js b/amd/src/automation.js deleted file mode 100644 index 0d4a674..0000000 --- a/amd/src/automation.js +++ /dev/null @@ -1,199 +0,0 @@ -define("mod_pulse/automation", ['jquery', 'core/modal_factory', 'core/templates', 'core/str', "core/notification"], - function($, Modal, Template, Str, notification) { - - const moveOutMoreMenu = (navMenu) => { - - if (navMenu === null) { - return; - } - - var menu = navMenu.querySelector('a.automation-templates'); - - if (menu === null) { - return; - } - - menu = menu.parentNode; - menu.dataset.forceintomoremenu = false; - menu.querySelector('a').classList.remove('dropdown-item'); - menu.querySelector('a').classList.add('nav-link'); - menu.parentNode.removeChild(menu); - - // Insert the stored menus before the more menu. - navMenu.insertBefore(menu, navMenu.children[1]); - window.dispatchEvent(new Event('resize')); // Dispatch the resize event to create more menu. - }; - - const returnToFailedTab = () => { - - if (document.forms['pulse-automation-template'] === null) { - return false; - } - - document.forms['pulse-automation-template'].onsubmit = (e) => { - var form = e.target; - var invalidElement = form.querySelector('.is-invalid'); - if (invalidElement === null) { - return true; - } - - var tabid = invalidElement.parentNode.parentNode.parentNode.id; - var hrefSelector = '[href="#' + tabid + '"]'; - - document.querySelector(hrefSelector).click(); - - return true; - }; - - return true; - }; - - // No need. - const updateAutoCompletionPositions = function() { - var group = "checkboxgroupautomation"; - - if (document.querySelectorAll('input[type=checkbox].' + group) - === null || document.querySelectorAll('[data-fieldtype="autocomplete"], [data-fieldtype="tags"]') === null) { - return true; - } - - - var capinput = document.querySelector('input[name="hasinstanceeditcapability"]'); - var hasCapability = (capinput && capinput.value) || capinput === null; - - // Observer the autocomplete. - document.querySelectorAll('[data-fieldtype="autocomplete"], [data-fieldtype="tags"]').forEach((element) => { - - if (element === null) { - return true; - } - - var observer = new MutationObserver(function(mutations) { - mutations.forEach((mutation) => { - var target = mutation.target; - - // Not has capability to edit instance, then hide the autocomplete input text. - if (!hasCapability && target.querySelector('input[data-fieldtype="autocomplete"]')) { - target.querySelector('input[data-fieldtype="autocomplete"]').disabled = true; - } - - var overrideElement = target.querySelector('.custom-switch'); - if (overrideElement === null) { - return; - } - overrideElement.parentNode.append(overrideElement); - observer.disconnect(); - }); - }); - observer.observe(element, {attributes: true, childList: true, subtree: true}); - return true; - }); - - return true; - }; - - const moveOverRidePosition = function() { - - var group = "checkboxgroupautomation"; - - if (document.querySelectorAll('input[type=checkbox].' + group) === null) { - return true; - } - - document.querySelectorAll('input[type=checkbox].' + group).forEach((overElement) => { - var id = overElement.id; - id = id.replace('id_override_', ''); - var element = document.querySelector('div#fitem_id_' + id); - if (element === null) { - element = document.querySelector('div#fgroup_id_' + id); - if (element === null) { - return true; - } - } - var parent = overElement.parentNode; - parent.innerHTML += ''; - var nodeToMove = document.createElement('div'); - nodeToMove.classList.add('custom-control', 'custom-switch'); - nodeToMove.append(parent); - element.querySelector(".felement").append(nodeToMove); - return true; - }); - // Move the override button for autocompletion fields after the autocomplete nodes are created. - updateAutoCompletionPositions(); - - return true; - }; - - /** - * Create a modal to display the list of instances which is overriden the template setting. - * - * @returns {void} - */ - const overrideModal = function() { - - // Add the template reference as prefix of the instance reference. - var templateReference = document.querySelector('#pulse-template-reference'); - var instanceReference = document.querySelector('#fitem_id_insreference .felement'); - if (templateReference && instanceReference) { - templateReference.classList.remove('hide'); - instanceReference.prepend(templateReference); - } - - const trigger = document.querySelectorAll('[data-target="overridemodal"]'); - - if (trigger === null) { - return; - } - - trigger.forEach((elem) => { - - elem.nextSibling.querySelector('.felement').append(elem); - - elem.addEventListener('click', function(e) { - e.preventDefault(); - var element = e.target; - var data = element.dataset; - var instance = document.querySelector('[name=overinstance_' + data.element + ']'); - if (instance !== null) { - var overrides = JSON.parse(instance.value); - overrides.map((value) => { - var path = '/mod/pulse/automation/instances/edit.php?instanceid='; - value.url = M.cfg.wwwroot + path + value.id + '&sesskey=' + M.cfg.sesskey; - return value; - }); - Modal.create({ - title: Str.get_string('instanceoverrides', 'pulse'), - body: Template.render('mod_pulse/overrides', {instances: overrides}) - }).then((modal) => { - modal.show(); - return true; - }).catch(notification.exception); - } - }); - }); - }; - - const enableTitleOnSubmit = function() { - if (document.forms['pulse-automation-template'] === null) { - return; - } - document.forms['pulse-automation-template'].onsubmit = - () => document.querySelector('[name="title"]').removeAttribute("disabled"); - }; - - return { - - init: function() { - returnToFailedTab(); - overrideModal(); - moveOverRidePosition(); - enableTitleOnSubmit(); - }, - - instanceMenuLink: function() { - var primaryNav = document.querySelector('.secondary-navigation ul.more-nav'); - moveOutMoreMenu(primaryNav); - }, - - }; -}); diff --git a/amd/src/bulkaction.js b/amd/src/bulkaction.js deleted file mode 100644 index 49baf53..0000000 --- a/amd/src/bulkaction.js +++ /dev/null @@ -1,267 +0,0 @@ -// 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 . - -/** - * Contain the bulk action for the instance management. - * - * @module mod_pulse/bulkaction - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -define("mod_pulse/bulkaction", ["jquery", "core/fragment", "core/modal_factory", -"core/modal_events", "core/notification", "core/str", "core/ajax", "core/templates"], - (function($, Fragment, ModalFactory, ModalEvents, notification, String, Ajax, Templates) { - - /** - * Instance management page selectors. - */ - const Selectors = { - table: '#manage-instance-table', - selectAll: '#manage-instance-tab .selectall-pulse-bulkaction', - deselectAll: '#manage-instance-tab .deselectall-pulse-bulkaction', - selectwithoutinsbtn: '#manage-instance-tab #selectwithoutins-btn', - CheckBoxes: "input[name='bc[]']", - DeleteBtn: '#manage-instance-tab #bulkdelete-btn', - AddBtn: '#manage-instance-tab #bulkadd-btn', - EnableBtn: '#manage-instance-tab #bulkenable-btn', - DisableBtn: '#manage-instance-tab #bulkdisable-btn', - CheckedBoxes: "input[name='bc[]']:checked", - checkboxgroups: '#manage-instance-tab .bulkaction-group', - tabUL: '#automation-tabs', - tabPane: '#pulsetemplates-tab-content .tab-pane', - tabContent: '#pulsetemplates-tab-content', - manageInstanceTab: '#manage-instance-tab', - templateForm: '.mform#pulse-automation-template', - activeNav: '#automation-tabs .nav-link.active', - }; - - /** - * Init the bulk select actions in the instance management tab. - */ - function bulkaction() { - const select = document.querySelector(Selectors.selectAll); - const deselect = document.querySelector(Selectors.deselectAll); - const tableRoot = () => document.querySelector(Selectors.table); - const checkboxelm = () => tableRoot().querySelectorAll(Selectors.CheckBoxes); - const selectwithoutins = document.querySelector(Selectors.selectwithoutinsbtn); - const checkboxgroup = document.querySelector(Selectors.checkboxgroups); - - // Whent click the select all button on the instance management tab. - select.addEventListener('click', () => { - checkboxelm().forEach(checkbox => { - checkbox.checked = true; - checkboxgroup.classList.remove('hide'); - }); - }); - - // Whent click the de-select all button on the instance management tab. - deselect.addEventListener('click', () => { - checkboxelm().forEach(checkbox => { - checkbox.checked = false; - checkboxgroup.classList.add('hide'); - }); - }); - - // Whent click the select all without instances button on the instance management tab. - selectwithoutins.addEventListener('click', () => { - checkboxelm().forEach(checkbox => { - if (checkbox.classList.contains("emptyinstance")) { - checkbox.checked = true; - checkboxgroup.classList.remove('hide'); - } - }); - }); - - // Add event listener to checkboxes. - document.addEventListener('change', function(e) { - - if (e.target.matches(Selectors.CheckBoxes)) { - // Check if at least one checkbox is checked. - var atLeastOneChecked = Array.from(checkboxelm()).some(function(checkbox) { - return checkbox.checked; - }); - - // Toggle visibility of bulk edit action based on checkbox status. - if (atLeastOneChecked) { - checkboxgroup.classList.remove('hide'); - } else { - checkboxgroup.classList.add('hide'); - } - } - }); - } - - /** - * Return the selected check boxes coursed ids for the instance management. - * - * @returns array $courseids course Ids - */ - function getcourseids() { - var courseids = []; - var checkedboxes = document.querySelectorAll(Selectors.CheckedBoxes); - checkedboxes.forEach(checkedbox => { - courseids.push(checkedbox.value); - }); - return courseids; - } - - /** - * Manage the automation instances confirmation and bulk action in modal. - * - * @param {int} params - */ - function manageInstances(params) { - const deletebtn = document.querySelector(Selectors.DeleteBtn); - const addbtn = document.querySelector(Selectors.AddBtn); - const disableBtn = document.querySelector(Selectors.DisableBtn); - const enableBtn = document.querySelector(Selectors.EnableBtn); - - // Click the delete instance bulk action button. - deletebtn.addEventListener('click', function() { - var courseids = getcourseids(); - getInstanceModal(courseids, params, 'delete'); - }); - - // Click the add instance bulk action button. - addbtn.addEventListener('click', function() { - var courseids = getcourseids(); - getInstanceModal(courseids, params, 'add'); - }); - - // Click the disable instance bulk action button. - disableBtn.addEventListener('click', function() { - var courseids = getcourseids(); - getInstanceModal(courseids, params, 'disable'); - }); - - // Click the enable instance bulk action button. - enableBtn.addEventListener('click', function() { - var courseids = getcourseids(); - getInstanceModal(courseids, params, 'enable'); - }); - - // Show/hide the instance manage tab of template. - // Moved the tab outside the form, default tab handlers not works. Used custom method to show hide. - document.querySelector(Selectors.tabUL).addEventListener('click', function(e) { - templateInstanceFilter(e); - }); - - const templateInstanceFilter = (e) => { - - document.querySelectorAll(Selectors.tabPane).forEach((e) => { - e.classList.remove('active'); - e.classList.remove('show'); - }); - // Remove the active. - var href = (e === null || !e.target.matches('#automation-tabs .nav-link')) ? activeTabHref() : - e.target.getAttribute('href'); - - document.querySelector(Selectors.tabContent + ' ' + href).classList.add('active'); - document.querySelector(Selectors.tabContent + ' ' + href).classList.add('show'); - - // Hide the form. - if (href == Selectors.manageInstanceTab) { - document.querySelector(Selectors.templateForm).style.display = 'none'; - } else { - document.querySelector(Selectors.templateForm).style.display = 'block'; - } - }; - - // Find the active ul. - const activeTabHref = () => { - return !document.querySelector(Selectors.activeNav) - || document.querySelector(Selectors.activeNav).getAttribute('href'); - }; - - templateInstanceFilter(null); - } - - /** - * Get the instance management confirmation modal. - * - * @param {array} courseids Course Ids - * @param {int} params Templated ID - * @param {string} action Bulk action name - */ - function getInstanceModal(courseids, params, action) { - var args = {templateid: params, courseids: courseids, action: action}; - ModalFactory.create({ - type: ModalFactory.types.SAVE_CANCEL, - title: String.get_string('confirmation', 'pulse'), - body: String.get_string('confirm' + action + 'instance', 'pulse'), - large: false - }) - .then(function(modal) { - - modal.setButtonText('save', String.get_string('yes')); - - modal.getRoot().on(ModalEvents.save, e => { - e.preventDefault(); - submitformdata(args); - loadInstancetable(args); - modal.getRoot().find('form').submit(); - modal.hide(); - }); - - modal.getRoot().on(ModalEvents.hidden, function() { - modal.destroy(); - }); - modal.show(); - return modal; - }).catch(notification.exception); - } - - /** - * Submit and recieve the message form the modal confirmation on the instance management. - * - * @param {string} params - */ - function submitformdata(params) { - Ajax.call([{ - methodname: 'mod_pulse_manage_instances', - args: params, - done: function(response) { - window.location.reload(); - if (response.message) { - notification.addNotification({ - message: response.message, - type: "success" - }); - } - } - }]); - } - - /** - * Load the current manage instance table to replace the table root. - * - * @param {string} params - */ - function loadInstancetable(params) { - var table = document.querySelector(Selectors.table); - Fragment.loadFragment('mod_pulse', 'get_manageinstance_table', 1, params).done((html, js) => { - Templates.replaceNode(table, html, js); - }); - } - - return { - init: function(params) { - bulkaction(); - manageInstances(params); - }, - }; - - })); diff --git a/automation/automationlib.php b/automation/automationlib.php deleted file mode 100644 index daeb328..0000000 --- a/automation/automationlib.php +++ /dev/null @@ -1,154 +0,0 @@ -. - -/** - * Notification pulse action - Automation lib. - * - * @package mod_pulse - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die('No direct access'); - -require_once($CFG->dirroot.'/lib/formslib.php'); - -/** - * Template add instance form. - */ -class template_addinstance_form extends \moodleform { - - /** - * Definition of the form elements. - * - * @return void - */ - public function definition() { - $mform =& $this->_form; - - $mform->updateAttributes(['class' => 'form-inline']); - - // Current course id. - $courseid = $this->_customdata['courseid'] ?? 0; - $mform->addElement('hidden', 'courseid', $courseid); - $mform->setType('courseid', PARAM_INT); - - // List of templates to create instance. - $templates = [0 => ''] + mod_pulse\automation\helper::get_templates_forinstance($courseid); - if (!empty($templates)) { - $mform->addElement('autocomplete', 'templateid', '', $templates); - } - - $this->add_action_buttons(true, get_string('addtemplatebtn', 'pulse')); - } - - /** - * validation - * - * @param mixed $data - * @param mixed $files - * @return array - */ - public function validation($data, $files) { - $errors = parent::validation($data, $files); - - if (empty($data['templateid'])) { - $errors['templateid'] = get_string('required'); - } - - return $errors; - - } -} - -/** - * Filter form for the templates table. - */ -class template_table_filter extends \moodleform { - - /** - * Filter form elements defined. - * - * @return void - */ - public function definition() { - $mform =& $this->_form; - - $mform->addElement('html', html_writer::tag('h3', get_string('filter'))); - $list = [0 => get_string('all')] + core_course_category::make_categories_list(); - $mform->addElement('autocomplete', 'category', get_string('category'), $list); - - $this->add_action_buttons(false, get_string('filter')); - } -} - -/** - * Course context class to create a context_course instance from record. - */ -class mod_pulse_context_course extends \context_course { - - /** - * Convert the record of context into course_context object. - * - * @param stdclass $data - * @return void - */ - public static function create_instance_fromrecord($data) { - return \context::create_instance_from_record($data); - } -} - -/** - * Filter form for the instance management table. - */ -class manage_instance_table_filter extends \moodleform { - - /** - * Filter form elements defined. - * - * @return void - */ - public function definition() { - global $DB; - $mform =& $this->_form; - - // Set the id of template. - $mform->addElement('hidden', 'id', $this->_customdata['id'] ?? 0); - $mform->setType('id', PARAM_INT); - - $mform->addElement('html', html_writer::tag('h3', get_string('filter'))); - $list = [0 => get_string('all')] + core_course_category::make_categories_list(); - $mform->addElement('autocomplete', 'category', get_string('category'), $list); - - $courses = $DB->get_records_sql('SELECT id, fullname FROM {course} WHERE id <> 1 AND visible != 0', []); - foreach ($courses as $id => $course) { - $courselist[$id] = $course->fullname; - } - $courselists = [0 => get_string('all')] + $courselist; - $mform->addElement('autocomplete', 'course', get_string('coursename', 'pulse'), $courselists); - - $mform->addElement('text', 'numberofinstance', get_string('numberofinstance', 'pulse')); - $mform->setType('numberofinstance', PARAM_ALPHANUM); // To use 0 for filter not used param_int. - $mform->setDefault('numberofinstace', ''); - - // Number of overrides. - $mform->addElement('text', 'numberofoverrides', get_string('numberofoverrides', 'pulse')); - $mform->setType('numberofoverrides', PARAM_ALPHANUM); // To use 0 for filter not used param_int. - $mform->setDefault('numberofoverrides', ''); - - $this->add_action_buttons(false, get_string('filter')); - } -} diff --git a/automation/instances/edit.php b/automation/instances/edit.php deleted file mode 100644 index e8fe498..0000000 --- a/automation/instances/edit.php +++ /dev/null @@ -1,170 +0,0 @@ -. - -/** - * Mod pulse - Edit template. - * - * @package mod_pulse - * @copyright 2023 bdecent GmbH - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use mod_pulse\automation\condition_base; -use mod_pulse\automation\helper; - -// Require config. -require(__DIR__.'/../../../../config.php'); - -require_login(); - -// Require admin library. -require_once($CFG->libdir.'/adminlib.php'); - -require_sesskey(); - -// Automation template ID to edit. -$templateid = optional_param('templateid', null, PARAM_INT); -$courseid = optional_param('courseid', null, PARAM_INT); -$instanceid = optional_param('instanceid', null, PARAM_INT); - -if ($courseid) { - $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST); -} else { - $instanceid = required_param('instanceid', PARAM_INT); -} - -// Create the page url. -$url = new moodle_url('/mod/pulse/automation/instances/edit.php', ['sesskey' => sesskey()]); - -// Include the instance id to the page url params. -if ($instanceid) { - if ($instance = $DB->get_record('pulse_autoinstances', ['id' => $instanceid], '*', MUST_EXIST)) { - $course = $DB->get_record('course', ['id' => $instance->courseid], '*', MUST_EXIST); - $templateid = $instance->templateid; - } - $url->param('instanceid', $instanceid); - -} -// Include the course id to the page url params if exists. -if (isset($course->id)) { - $url->param('courseid', $course->id); -} - -// Instance list page url for this course. -$overviewurl = new moodle_url('/mod/pulse/automation/instances/list.php', ['courseid' => $course->id, 'sesskey' => sesskey()]); - -if ($templateid) { - $url->params(['templateid' => $templateid]); - // Create this instance template object. - $tempalteobj = mod_pulse\automation\templates::create($templateid); - // Check this template is avialable for this course to create instances. - if (!$tempalteobj->is_available_forcourse($course->category)) { - // Not available to create instance. redirect to instance list. - redirect($overviewurl, get_string('errortemplatenotavailable', 'pulse')); - } - // Template reference to add as prefix. - $templatereference = $tempalteobj->get_template()->reference; -} - -// Page values. -$context = \context_course::instance($course->id); -// Verify the user capability. -require_capability('mod/pulse:addtemplateinstance', $context); - -// Setup page values. -$PAGE->set_url($url); -$PAGE->set_context($context); -$PAGE->set_course(get_course($course->id)); - -// Edit automation templates form. -$templatesform = new \mod_pulse\forms\automation_instance_form(null, [ - 'templateid' => $templateid, - 'courseid' => $course->id, - 'instanceid' => $instanceid, - 'templatereference' => $templatereference ?? '', -]); - -// Instance form submitted, handel the submitted data. -if ($formdata = $templatesform->get_data()) { - $result = mod_pulse\automation\instances::manage_instance($formdata); - // Redirect to instances list. - redirect($overviewurl); -} else if ($templatesform->is_cancelled()) { - // Form cancelled redirect to list page. - redirect($overviewurl); -} - -// Setup the tempalte data to the form, if the form id param available. -if ($instanceid !== null && $instanceid > 0) { - - if ($record = mod_pulse\automation\instances::create($instanceid)->get_instance_formdata()) { - if ($record['status'] == mod_pulse\automation\templates::STATUS_ORPHANED) { - \core\notification::error(get_string('templatesorphanederror', 'pulse')); - redirect($overviewurl); - } - // Set the template data to the templates edit form. - $templatesform->set_data($record); - } else { - // Direct the user to list page with error message, when the requested menu is not available. - \core\notification::error(get_string('templatesrecordmissing', 'pulse')); - redirect($overviewurl); - } - -} else { - // Instance not created and initiated the new instance from the template, - // Then fetch the template data with actions data and assign to the template form. - $courseid = required_param('courseid', PARAM_INT); - $templateid = required_param('templateid', PARAM_INT); - - if ($record = mod_pulse\automation\templates::create($templateid)->get_template()) { - // Attach the course id to the templates. - $record->courseid = $courseid; - // Convert the trigger conditions to separate element. - $conditions = $record->triggerconditions; - foreach ($conditions as $condition) { - $record->{'condition['.$condition.'][status]'} = ($condition == 'events') ? condition_base::FUTURE - : condition_base::ALL; - } - // Set the template data to the templates edit form. - $templatesform->set_data($record); - } else { - // Direct the user to list page with error message, when the requested template instance is not available. - \core\notification::error(get_string('templatesrecordmissing', 'pulse')); - redirect($overviewurl); - } - -} -// Template edit page heading. -$PAGE->set_heading(format_string($course->fullname)); -// PAGE breadcrumbs. -$PAGE->navbar->add(get_string('mycourses', 'core'), new moodle_url('/course/index.php')); -$PAGE->navbar->add(format_string($course->shortname), new moodle_url('/course/view.php', ['id' => $course->id])); -$PAGE->navbar->add(get_string('autotemplates', 'pulse'), $overviewurl); -$PAGE->navbar->add(get_string('autoinstances', 'pulse')); - -// Page content display started. -echo $OUTPUT->header(); - -// Template heading. -echo $OUTPUT->heading(get_string('editinstance', 'pulse')); - -// Course action warning messages. -echo helper::display_actions_course_warnings($course); -// Display the template form for create or edit. -echo $templatesform->display(); - -// Footer. -echo $OUTPUT->footer(); diff --git a/automation/instances/list.php b/automation/instances/list.php deleted file mode 100644 index 3634d38..0000000 --- a/automation/instances/list.php +++ /dev/null @@ -1,227 +0,0 @@ -. - -/** - * Mod pulse - List the available template and manage the template Create, Update, Delete actions, sort the order of templates. - * - * @package mod_pulse - * @copyright 2023 bdecent GmbH - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use mod_pulse\automation\helper; - -// Require config. -require(__DIR__.'/../../../../config.php'); -require_once($CFG->dirroot. '/mod/pulse/automation/automationlib.php'); - -require_login(); - -// Require admin library. -require_once($CFG->libdir.'/adminlib.php'); - -// Get parameters. -$action = optional_param('action', null, PARAM_ALPHAEXT); - -$courseid = optional_param('courseid', null, PARAM_INT); -$instanceid = optional_param('instanceid', null, PARAM_INT); - -// Find the courseid. -if ($courseid) { - $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST); -} else { - $instanceid = required_param('instanceid', PARAM_INT); - if ($instance = $DB->get_record('pulse_autoinstances', ['id' => $instanceid], '*', MUST_EXIST)) { - $course = $DB->get_record('course', ['id' => $instance->courseid], '*', MUST_EXIST); - $courseid = $course->id; - } -} - -if (!($course = $DB->get_record('course', ['id' => $courseid]))) { - throw new moodle_exception('coursenotfound', 'core_course'); -} - -// Page values. -$context = \context_course::instance($courseid); -// Verify the user capability. -require_capability('mod/pulse:addtemplateinstance', $context); - -// Prepare the page. -$PAGE->set_context($context); -$PAGE->set_url(new moodle_url('/mod/pulse/automation/instances/list.php', ['courseid' => $courseid])); -$PAGE->set_course($course); - -// Further prepare the page. -$PAGE->set_title(get_string('autoinstances', 'pulse')); - -// Add instance template form submitted. -$instanceaddform = new \template_addinstance_form(null, ['courseid' => $courseid]); -if ($data = $instanceaddform->get_data()) { - $url = (new moodle_url('/mod/pulse/automation/instances/edit.php', - ['courseid' => $courseid, 'templateid' => $data->templateid, 'sesskey' => sesskey()]))->out(false); - - redirect($url); -} - -// Process actions. -if ($action !== null && confirm_sesskey()) { - // Every action is based on a template, thus the template ID param has to exist. - $instanceid = required_param('instanceid', PARAM_INT); - - // Create template instance. Actions are performed in template instance. - $instance = mod_pulse\automation\instances::create($instanceid); - - $transaction = $DB->start_delegated_transaction(); - - // Perform the requested action. - switch ($action) { - // Triggered action is delete, then init the deletion of template. - case 'delete': - // Delete the template. - if ($instance->delete_instance()) { - // Notification to user for template deleted success. - \core\notification::success(get_string('templatedeleted', 'pulse')); - } - break; - case 'disable': - // Disable the template visibility. - $instance->update_status(false); - break; - - case 'enable': - // Disable the template visibility. - $instance->update_status(true); - break; - - case 'copy': - // Duplicate the instance. - $instance->duplicate(); - break; - - case 'report': - $redirecturl = $instance->get_report_url(); - break; - } - - // Allow to update the changes to database. - $transaction->allow_commit(); - - // Redirect to the same page to view the templates list. - redirect($redirecturl ?? $PAGE->url); -} - -$PAGE->add_body_class('mod-pulse-automation-table'); - -// Further prepare the page. -$PAGE->set_heading(format_string($course->fullname)); - -$PAGE->navbar->add(get_string('mycourses', 'core'), new moodle_url('/course/index.php')); -$PAGE->navbar->add(format_string($course->shortname), new moodle_url('/course/view.php', ['id' => $course->id])); -$PAGE->navbar->add(get_string('autotemplates', 'pulse'), new moodle_url('/mod/pulse/automation/instances/list.php')); - -// Build automation templates table. -$filterset = new mod_pulse\table\automation_instance_filterset; - -if ($templateid = optional_param('templateid', null, PARAM_INT)) { - $template = new \core_table\local\filter\integer_filter('templateid'); - $template->add_filter_value($templateid); - $filterset->add_filter($template); -} - -$table = new mod_pulse\table\auto_instances($context->id); -$table->define_baseurl($PAGE->url); -$table->set_filterset($filterset); - -// Start page output. -echo $OUTPUT->header(); -echo $OUTPUT->heading(get_string('automation', 'pulse')); - -// Show smart menus description. -echo get_string('autoinstance_desc', 'pulse'); - -// Prepare 'Create smart menu' button. -$createbutton = $OUTPUT->box_start(); -$createbutton .= mod_pulse\automation\helper::get_addtemplate_instance($instanceaddform, $courseid); -$createbutton .= $OUTPUT->box_end(); - -// If there aren't any smart menus yet. -$countmenus = $DB->count_records('pulse_autotemplates'); -if ($countmenus < 1) { - // Show the table, which, since it is empty, falls back to the - // "There aren't any smart menus created yet. Please create your first smart menu to get things going." notice. - $table->out(0, true); - - // And then show the button. - echo $createbutton; - - // Otherwise. -} else { - // Show the button. - echo $createbutton; - - // And then show the table. - $table->out(10, true); - - echo helper::get_instance_tablehelps(); - - $PAGE->requires->js_amd_inline('require(["jquery"], function($) { - var notes = document.querySelectorAll("[data-target=notes-collapse]"); - - if (notes !== null) { - - notes.forEach((note) => { - note.addEventListener("click", function(e) { - e.preventDefault(); - var target = e.target.closest("[data-target=notes-collapse]"); - var collapse = target.dataset.collapse; - var tbody = target.parentNode.parentNode.parentNode; - if (target.dataset.notes == "") { - return true; - } - - if (collapse == "1") { - // target.classList.add("show"); - var trNode = document.createElement("tr"); - trNode.id = "notes_"+target.dataset.instance; - trNode.innerHTML = ""+target.dataset.notes+""; - tbody.insertBefore(trNode, target.parentNode.parentNode.nextSibling); - target.dataset.collapse = 0; - } else { - var id = "#notes_"+target.dataset.instance; - document.querySelector(id).remove(); - target.dataset.collapse = 1; - } - target.childNodes[0].classList.toggle("fa-angle-right"); - target.childNodes[0].classList.toggle("fa-angle-down"); - }) - }); - } - - // Make the status toggle check and uncheck on click on status update toggle. - var form = document.querySelectorAll(".pulse-instance-status-switch"); - form.forEach((switche) => { - switche.addEventListener("click", function(e) { - var form = e.currentTarget.querySelector("input[type=checkbox]"); - form.click(); - }) - }); - - })'); - -} - -// Finish page output. -echo $OUTPUT->footer(); diff --git a/automation/templates/edit.php b/automation/templates/edit.php deleted file mode 100644 index e48a5ce..0000000 --- a/automation/templates/edit.php +++ /dev/null @@ -1,245 +0,0 @@ -. - -/** - * Mod pulse - Edit template. - * - * @package mod_pulse - * @copyright 2023 bdecent GmbH - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use mod_pulse\automation\helper; -use mod_pulse\automation\manage; - -// Require config. -require(__DIR__.'/../../../../config.php'); - -// Login check after config inlcusion. -require_login(); - -// Require admin library. -require_once($CFG->libdir.'/adminlib.php'); - -// User access checks. -require_sesskey(); - -// Verify the user capability. -$context = \context_system::instance(); -require_capability('mod/pulse:viewtemplateslist', $context); - -// Automation template ID to edit. -$id = optional_param('id', null, PARAM_INT); -$action = optional_param('action', false, PARAM_ALPHA); - -// Page values. -$url = new moodle_url('/mod/pulse/automation/templates/edit.php', ['id' => $id, 'sesskey' => sesskey()]); - -// Setup page values. -$PAGE->set_url($url); -$PAGE->set_context($context); - -$PAGE->add_body_class('mod-pulse-automation-filter'); - -if ($action !== null && confirm_sesskey()) { - // Every action is based on a template, thus the template ID param has to exist. - $templateid = optional_param('id', null, PARAM_INT); - $courseid = optional_param('courseid', null, PARAM_INT); - - // Instance management. - $manage = mod_pulse\automation\manage::create($templateid, $courseid); - - $redirectback = false; - $redirectmessage = false; - // Perform the requested action. - switch ($action) { - - case 'delete': - $redirectback = $manage->delete_course_instance(); - // Notification to user for instance deleted success. - $redirectmessage = \core\notification::success(get_string('templatedeleted', 'pulse')); - break; - - case 'add': - $redirectback = $manage->add_course_instance(); - // Show the inserted success notification. - $redirectmessage = \core\notification::success(get_string('templateinsertsuccess', 'pulse')); - break; - - case 'disable': - // Disable the instances visibility in the course. - $redirectback = $manage->update_instance_status(false); - break; - - case 'enable': - // Enable the instance visibility in the course. - $redirectback = $manage->update_instance_status(true); - break; - } - - if ($redirectback) { - if ($redirectmessage) { - redirect($PAGE->url, $redirectmessage, 5); - } else { - redirect($PAGE->url); - } - } - -} - -if (is_siteadmin()) { - // Extend the features of admin settings. - admin_externalpage_setup('pulseautomation'); - $PAGE->set_heading(get_string('autotemplates', 'pulse')); - $moduleurl = new moodle_url('/admin/category.php?category=modsettings'); - $PAGE->navbar->add(get_string('activity', 'core'), $moduleurl); -} else { - $moduleurl = new moodle_url('/user/profile.php'); - $PAGE->navbar->add(get_string('profile', 'core'), $moduleurl); -} - -// Build instance management table filter. -$filterset = new mod_pulse\table\manage_instance_filterset; - -$category = new \core_table\local\filter\integer_filter('category'); -if ($categoryid = optional_param('category', null, PARAM_INT)) { - $category->add_filter_value($categoryid); - $filtered = true; -} -$filterset->add_filter($category); - -$course = new \core_table\local\filter\integer_filter('course'); -if ($courseid = optional_param('course', null, PARAM_INT)) { - $course->add_filter_value($courseid); - $filtered = true; -} -$filterset->add_filter($course); - -// Number of instances filter for instance management table. -$instancenumber = new \core_table\local\filter\integer_filter('numberofinstance'); -$numberofinstance = optional_param('numberofinstance', null, PARAM_INT); -$submitted = data_submitted(); -// Verify the filter is empty or requested with 0. -if ($numberofinstance || (isset($submitted->numberofinstance) && $submitted->numberofinstance !== '')) { - $instancenumber->add_filter_value($numberofinstance); - $filtered = true; -} -$filterset->add_filter($instancenumber); - -// Number of overrides. -$overrides = new \core_table\local\filter\integer_filter('numberofoverrides'); -$numberofoverrides = optional_param('numberofoverrides', null, PARAM_INT); - -// Verify the filter is empty or requested with 0. -if ($numberofoverrides || (isset($submitted->numberofoverrides) && $submitted->numberofoverrides !== '')) { - $overrides->add_filter_value($numberofoverrides); - $filtered = true; -} -$filterset->add_filter($overrides); - -// PAGE breadcrumbs. -$PAGE->navbar->add(get_string('autotemplates', 'pulse'), new moodle_url('/mod/pulse/automation/templates/list.php')); -$PAGE->navbar->add(get_string('edit')); - -$manageinstancetable = new \mod_pulse\table\manage_instance($id); -$manageinstancetable->set_filterset($filterset); - -// Edit automation templates form. -$templatesform = new \mod_pulse\forms\automation_template_form(null, ['id' => $id]); - -// Template list url. -$overviewurl = new moodle_url('/mod/pulse/automation/templates/list.php'); - -// Handling the templates form submitted data. -if ($formdata = $templatesform->get_data()) { - - // Create and update the template. - $result = mod_pulse\automation\templates::manage_instance($formdata); - - // Redirect to templates list. - redirect($overviewurl); - -} else if ($templatesform->is_cancelled()) { - // Form cancelled, redirect to the templates list. - redirect($overviewurl); -} - -// Setup the tempalte data to the form, if the form id param available. -if ($id !== null && $id > 0) { - - // Fetch the data of the template and its conditions and actions. - if ($record = mod_pulse\automation\templates::create($id)->get_template()) { - // Set the template data to the templates edit form. - $templatesform->set_data($record); - } else { - // Direct the user to list page with error message, when the requested template is not available. - \core\notification::error(get_string('templatesrecordmissing', 'pulse')); - redirect($overviewurl); - } - -} else { - // Trigger the prepare file areas for the new template create. - $templatesform->set_data([]); -} -// Page content display started. -echo $OUTPUT->header(); - -// Templates heading. -echo $OUTPUT->heading(get_string('templatessettings', 'pulse')); - -$tabs = [ - ['name' => 'autotemplate-general', 'title' => get_string('tabgeneral', 'pulse'), 'active' => 'active'], - ['name' => 'pulse-condition-tab', 'title' => get_string('tabcondition', 'pulse')], -]; - -$context = \context_system::instance(); - -// Load all actions forms. -// Define the lang key "formtab" in the action component it automatically includes it. -foreach (helper::get_actions() as $key => $action) { - $tabs[] = ['name' => 'pulse-action-'.$key, 'title' => get_string('formtab', 'pulseaction_'.$key)]; -} - -// Instance management tab. -if ($templateid = $templatesform->get_customdata('id') && has_capability('mod/pulse:manageinstance', $context)) { - $tabs[] = ['name' => 'manage-instance-tab', 'title' => get_string('tabmanageinstance', 'pulse')]; -} - -echo $OUTPUT->render_from_template('mod_pulse/automation_tabs', ['tabs' => $tabs]); - -echo '
'; - -// Display the templates form for create or edit. -echo $templatesform->display(); - -if ($templateid = $templatesform->get_customdata('id') && has_capability('mod/pulse:manageinstance', $context)) { - // Template Manage instance. - echo $templatesform->load_template_manageinstance($manageinstancetable); -} - -echo html_writer::end_div(); - -$PAGE->requires->js_amd_inline("require(['jquery'], function() { - // Filter form display. - var filterIcon = document.querySelector('#pulse-manageinstance-filter'); - var filterForm = document.querySelector('#pulse-automation-filterform'); - if (filterIcon !== null) { - filterIcon.onclick = (e) => filterForm.classList.toggle('hide'); - } - })" -); -// Footer. -echo $OUTPUT->footer(); diff --git a/automation/templates/list.php b/automation/templates/list.php deleted file mode 100644 index a3732bd..0000000 --- a/automation/templates/list.php +++ /dev/null @@ -1,232 +0,0 @@ -. - -/** - * Mod pulse - List the available template and manage the template Create, Update, Delete actions, sort the order of templates. - * - * @package mod_pulse - * @copyright 2023 bdecent GmbH - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use mod_pulse\automation\helper; - -// Require config. -require(__DIR__.'/../../../../config.php'); - -// Login check after config inlcusion. -require_login(); - -// Require admin library. -require_once($CFG->libdir.'/adminlib.php'); - -// Get parameters. -$action = optional_param('action', null, PARAM_ALPHAEXT); -$templateid = optional_param('id', null, PARAM_INT); -$instance = optional_param('instance', false, PARAM_BOOL); - -// Page values. -$context = \context_system::instance(); - -if (is_siteadmin()) { - // Setup breadcrumps. - admin_externalpage_setup('pulseautomation'); -} - -// Verify the user capability. -require_capability('mod/pulse:viewtemplateslist', $context); - -// Prepare the page. -$PAGE->set_context($context); -$PAGE->set_url(new moodle_url('/mod/pulse/automation/templates/list.php')); - -// Process actions. -if ($action !== null && confirm_sesskey()) { - // Every action is based on a template, thus the template ID param has to exist. - $templateid = required_param('id', PARAM_INT); - - // Create template instance. Actions are performed in template instance. - $template = mod_pulse\automation\templates::create($templateid); - - $transaction = $DB->start_delegated_transaction(); - - // Perform the requested action. - switch ($action) { - // Triggered action is delete, then init the deletion of template. - case 'delete': - // Delete the template. - if ($template->delete_template()) { - // Notification to user for template deleted success. - \core\notification::success(get_string('templatedeleted', 'pulse')); - } - break; - case "hidemenu": - // Disable the template visibility. - $template->update_visible(false); - break; - case "showmenu": - // Enable the template visibility. - $template->update_visible(true); - break; - - case 'disable': - // Disable the template visibility. - $template->update_status(false, $instance); - break; - - case 'enable': - // Disable the template visibility. - $template->update_status(true, $instance); - break; - } - - // Allow to update the changes to database. - $transaction->allow_commit(); - - // Redirect to the same page to view the templates list. - redirect($PAGE->url); -} - -$PAGE->add_body_class('mod-pulse-automation-table'); -$PAGE->add_body_class('mod-pulse-automation-filter'); - - -// Further prepare the page. -$PAGE->set_heading(get_string('autotemplates', 'pulse')); - -// Add breadcrumbs for the non admin users, has the capability to view templates. -if (!is_siteadmin()) { - // PAGE breadcrumbs. - $PAGE->navbar->add(get_string('profile', 'core'), new moodle_url('/user/profile.php')); - $PAGE->navbar->add(get_string('autotemplates', 'pulse'), new moodle_url('/mod/pulse/automation/templates/list.php')); - $PAGE->navbar->add(get_string('list')); -} - -// Build automation templates table. -$filterset = new mod_pulse\table\automation_filterset; - -if ($categoryid = optional_param('category', null, PARAM_INT)) { - $category = new \core_table\local\filter\integer_filter('category'); - $category->add_filter_value($categoryid); - $filterset->add_filter($category); - $filtered = true; -} - -// Build automation templates table. -$table = new mod_pulse\table\auto_templates($context->id); -$table->define_baseurl($PAGE->url); -$table->set_filterset($filterset); - -// Start page output. -echo $OUTPUT->header(); -// Display the heading only for admin users. -if (is_siteadmin()) { - echo $OUTPUT->heading(get_string('autotemplates', 'pulse')); -} - -// Show templates description. -echo get_string('autotemplates_desc', 'pulse'); - -// Prepare 'Create template' button. -$createbutton = $OUTPUT->box_start(); -$createbutton .= mod_pulse\automation\helper::template_buttons($filtered ?? false); -$createbutton .= $OUTPUT->box_end(); - -// If there aren't any templates yet. -$countmenus = $DB->count_records('pulse_autotemplates'); -if ($countmenus < 1) { - // Show the table, which, since it is empty, falls back to the - // "There aren't any templates created yet. Please create your first template to get things going." notice. - $table->out(0, true); - - // And then show the button. - echo $createbutton; - - // Otherwise. -} else { - // Show the button. - echo $createbutton; - - // And then show the table. - $table->out(10, true); - - echo helper::get_templates_tablehelps(); -} - -$PAGE->requires->js_amd_inline(" -require(['jquery', 'core/modal_factory', 'core/str', 'mod_pulse/modal_preset', 'mod_pulse/events', 'mod_pulse/presetmodal'], - function($, ModalFactory, Str, ModalPreset, PresetEvents, PresetModal) { - - var form = document.querySelectorAll('.updatestatus-switch-form'); - form.forEach((switche) => { - switche.querySelector('.custom-switch').addEventListener('click', function(e) { - e.preventDefault(); - - var statusElem = e.target.parentNode.querySelector('input[name=action]'); - var instanceElem = e.target.parentNode.querySelector('input[name=instance]'); - - var form = e.target.closest('form'); - var checkbox = e.target.closest('.custom-control-input'); - - if (checkbox.checked) { - statusElem.value = 'enable'; - } else { - statusElem.value = 'disable'; - } - - if (typeof PresetModal.registerModalType == 'undefined') { - var modalFn = ModalFactory.create({ - type: ModalPreset.TYPE, - title: Str.get_string('updatetemplate', 'pulse'), - body: Str.get_string('templatestatusudpate', 'pulse'), - large: true - }); - } else { - var modalFn = PresetModal.create({ - title: Str.get_string('updatetemplate', 'pulse'), - body: Str.get_string('templatestatusudpate', 'pulse'), - large: true - }); - } - - modalFn.then(function(modal) { - - modal.setButtonText('customize', Str.get_string('updateinstance', 'pulse')); - modal.setButtonText('save', Str.get_string('updatetemplate', 'pulse')); - modal.show(); - - modal.getRoot().on(PresetEvents.customize, (e) => { - instanceElem.value = true; - form.submit(); - }); - - modal.getRoot().on(PresetEvents.save, (e) => { - instanceElem.value = false; - form.submit(); - }); - }) - }) - }); - - // Filter form display. - var filterIcon = document.querySelector('#pulse-automation-filter'); - var filterForm = document.querySelector('#pulse-automation-filterform'); - filterIcon.onclick = (e) => filterForm.classList.toggle('hide'); - -})"); - -// Finish page output. -echo $OUTPUT->footer(); diff --git a/backup/moodle2/backup_pulse_activity_task.class.php b/backup/moodle2/backup_pulse_activity_task.class.php index 676f836..48cb745 100644 --- a/backup/moodle2/backup_pulse_activity_task.class.php +++ b/backup/moodle2/backup_pulse_activity_task.class.php @@ -35,7 +35,7 @@ class backup_pulse_activity_task extends backup_activity_task { * No specific settings for this activity */ public function define_my_settings() { - // No particular settings for this activity. + // Pulse don't have any specified settings. } /** @@ -44,12 +44,6 @@ public function define_my_settings() { public function define_my_steps() { // Only single structure step. $this->add_step(new backup_pulse_activity_structure_step('pulse_structure', 'pulse.xml')); - - // Add the automation instances and templates backup only for course backup. - if ($this->plan->get_type() == \backup::TYPE_1COURSE) { - $this->add_step(new backup_pulse_course_structure_step('pulse_automation', 'pulseautomation.xml')); - } - } /** diff --git a/backup/moodle2/backup_pulse_stepslib.php b/backup/moodle2/backup_pulse_stepslib.php index 8791ff3..1406f66 100644 --- a/backup/moodle2/backup_pulse_stepslib.php +++ b/backup/moodle2/backup_pulse_stepslib.php @@ -22,18 +22,11 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); - -use mod_pulse\automation\action_base; -use mod_pulse\plugininfo\pulseaction; -use mod_pulse\plugininfo\pulsecondition; - /** * Define the complete pulse structure for backup, with file and id annotations. */ class backup_pulse_activity_structure_step extends backup_activity_structure_step { - /** * Define backup steps structure. */ @@ -93,73 +86,3 @@ protected function define_structure() { return $this->prepare_activity_structure($pulse); } } - -/** - * Define the complete pulse structure for backup, with file and id annotations. - */ -class backup_pulse_course_structure_step extends backup_activity_structure_step { - - /** - * Define the pulse course structure steps. - */ - protected function define_structure() { - - $automationinstance = new backup_nested_element('automationinstance'); - $instances = new backup_nested_element('pulse_autoinstances', ['id'], [ - 'templateid', 'courseid', 'status', 'timemodified', - ]); - - // Automation templates. - $automation = new backup_nested_element('automationtemplates'); - $templates = new backup_nested_element('pulse_autotemplates', ['id'], [ - 'title', 'reference', 'visible', 'notes', 'status', 'tags', 'tenants', - 'categories', 'triggerconditions', 'triggeroperator', 'timemodified', - ]); - - $automationtempinstance = new backup_nested_element('automationtemplateinstance'); - $tempinstances = new backup_nested_element('pulse_autotemplates_ins', ['id'], [ - 'instanceid', 'title', 'insreference', 'notes', 'tags', 'tenants', - 'categories', 'triggerconditions', 'triggeroperator', 'timemodified', - ]); - - $pulseconditionoverrides = new backup_nested_element('pulseconditionoverrides'); - $overrides = new backup_nested_element('pulse_condition_overrides', ['id'], [ - 'instanceid', 'triggercondition', 'status', 'upcomingtime', 'additional', 'isoverridden', - ]); - - // Automation template. - $instances->add_child($automation); - $automation->add_child($templates); - - // Automation template instance. - $instances->add_child($automationtempinstance); - $automationtempinstance->add_child($tempinstances); - - // Condition overrides. - $instances->add_child($pulseconditionoverrides); - $pulseconditionoverrides->add_child($overrides); - - // Pulse instance. - $instances->set_source_table('pulse_autoinstances', ['courseid' => backup::VAR_COURSEID]); - $tempinstances->set_source_table('pulse_autotemplates_ins', ['instanceid' => backup::VAR_PARENTID]); - $overrides->set_source_table('pulse_condition_overrides', ['instanceid' => backup::VAR_PARENTID]); - - // Pulse autotemplates. - $templates->set_source_sql(' - SELECT * - FROM {pulse_autotemplates} at - WHERE at.id IN ( - SELECT templateid - FROM {pulse_autoinstances} - WHERE courseid = :courseid - ) - ', ['courseid' => backup::VAR_COURSEID]); - - // Include the backup steps for actions and conditions. - $this->add_subplugin_structure('pulseaction', $instances, true); - $this->add_subplugin_structure('pulsecondition', $instances, true); - - // Return the root element (data), wrapped into standard activity structure. - return $this->prepare_activity_structure($instances); - } -} diff --git a/backup/moodle2/restore_pulse_activity_task.class.php b/backup/moodle2/restore_pulse_activity_task.class.php index f93daa7..0cd240f 100644 --- a/backup/moodle2/restore_pulse_activity_task.class.php +++ b/backup/moodle2/restore_pulse_activity_task.class.php @@ -42,14 +42,7 @@ protected function define_my_settings() { * Define restore structure steps to restore to database from pulse.xml. */ protected function define_my_steps() { - $this->add_step(new restore_pulse_activity_structure_step('pulse_structure', 'pulse.xml')); - - $fullpath = $this->get_taskbasepath(); - $fullpath = rtrim($fullpath, '/') . '/pulseautomation.xml'; - if (file_exists($fullpath)) { - $this->add_step(new restore_pulse_course_structure_step('pulse_automation', 'pulseautomation.xml')); - } } /** @@ -63,8 +56,6 @@ public static function define_decode_contents() { \mod_pulse\extendpro::pulse_extend_restore_content($contents); - restore_pulse_course_structure_step::decode_contents($contents); - return $contents; } @@ -75,5 +66,4 @@ public static function define_decode_contents() { public static function define_decode_rules() { return []; } - } diff --git a/backup/moodle2/restore_pulse_stepslib.php b/backup/moodle2/restore_pulse_stepslib.php index a66a057..136f750 100644 --- a/backup/moodle2/restore_pulse_stepslib.php +++ b/backup/moodle2/restore_pulse_stepslib.php @@ -22,8 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); - /** * Define all the restore steps that will be used by the restore_pulse_activity_task */ @@ -160,186 +158,3 @@ protected function after_execute() { $this->add_related_files('mod_pulse', 'pulse_content', null); } } - -/** - * Structure step to restore pulse course. - */ -class restore_pulse_course_structure_step extends restore_activity_structure_step { - - /** - * Restore steps structure definition. - */ - protected function define_structure() { - static $cache; - - $paths = []; - // Restore path. - - if (isset($cache[$this->get_restoreid()]) && $cache[$this->get_restoreid()]) { - return $paths; - } - - $instances = new restore_path_element('pulse_autoinstances', - '/activity/pulse_autoinstances'); - - $paths[] = $instances; - - $paths[] = new restore_path_element('pulse_autotemplates', - '/activity/pulse_autoinstances/automationtemplates/pulse_autotemplates'); - - $paths[] = new restore_path_element('pulse_autotemplates_ins', - '/activity/pulse_autoinstances/automationtemplateinstance/pulse_autotemplates_ins'); - $paths[] = new restore_path_element('pulse_condition_overrides', - '/activity/pulse_autoinstances/pulseconditionoverrides/pulse_condition_overrides'); - - $this->add_subplugin_structure('pulseaction', $instances); - - $cache[$this->get_restoreid()] = true; - - // Return the paths wrapped into standard activity structure. - return $paths; - } - - /** - * Process activity pulse restore. - * @param mixed $data restore pulse table data. - */ - protected function process_pulse_autoinstances($data) { - global $DB; - - $data = (object) $data; - $oldid = $data->id; - $data->courseid = $this->get_courseid(); - // All the status of the instance is disabled initialy. - $data->status = \mod_pulse\automation\instances::STATUS_DISABLE; - // Use the new template id. - $data->templateid = $this->get_mappingid('templateid', $data->templateid); - - // Insert instance into Database. - $newitemid = $DB->insert_record('pulse_autoinstances', $data); - - // Create a mapping for the instance. - $this->set_mapping('pulse_autoinstances', $oldid, $newitemid, true); - } - - /** - * Process pulse users records. - * - * @param object $data The data in object form - * @return void - */ - protected function process_pulse_autotemplates($data) { - global $DB; - - $data = (object) $data; - $oldtemplateid = $data->id; - - $record = $DB->get_record('pulse_autotemplates', ['reference' => $data->reference]); - if (empty($record)) { - $templateid = $DB->insert_record('pulse_autotemplates', $data); - } else { - $templateid = $record->id; - } - - // Update the parent instance templateid. - if ($DB->record_exists('pulse_autoinstances', ['id' => $this->get_new_parentid('pulse_autoinstances')])) { - $DB->set_field('pulse_autoinstances', 'templateid', $templateid, - ['id' => $this->get_new_parentid('pulse_autoinstances')], - ); - $categories = json_decode($record->categories, true); - - // In array of categories. - $category = get_course($this->get_courseid())->category; - - if (!empty($categories) && !in_array($category, $categories)) { - $status = \mod_pulse\automation\instances::STATUS_ORPHANED; - $DB->set_field('pulse_autoinstances', 'status', $status, ['id' => $this->get_new_parentid('pulse_autoinstances')]); - } - } - - $this->set_mapping('pulse_autotemplates', $oldtemplateid, $templateid); - - } - - /** - * Process pulse users records. - * - * @param object $data The data in object form - * @return void - */ - protected function process_pulse_autotemplates_ins($data) { - global $DB; - - $data = (object) $data; - - $data->instanceid = $this->get_mappingid('pulse_autoinstances', $data->instanceid); - - $DB->insert_record('pulse_autotemplates_ins', $data); - // No need to save this mapping as far as nothing depend on it - // (child paths, file areas nor links decoder). - } - - /** - * Process pulse pro features restore structures. - * Pro feature. - * @param mixed $data - * @return void - */ - protected function process_pulse_condition_overrides($data) { - global $DB; - $data = (object) $data; - - $data->instanceid = $this->get_mappingid('pulse_autoinstances', $data->instanceid); - - $DB->insert_record('pulse_condition_overrides', $data); - } - - /** - * Update the files of editors after restore execution. - * - * @return void - */ - protected function after_restore() { - global $DB; - - $instances = $DB->get_records('backup_ids_temp', [ - 'backupid' => $this->get_restoreid(), 'itemname' => 'pulse_autoinstances', - ]); - - foreach ($instances as $instance) { - $oldinstances = $DB->get_records('pulse_condition_overrides', ['instanceid' => $instance->newitemid]); - foreach ($oldinstances as $ins) { - $additional = $ins->additional ? json_decode($ins->additional, true) : []; - if (isset($additional['modules']) && $additional['modules']) { - $newcmid = $ins->triggercondition == 'session' - ? $this->get_mappingid('facetoface', $additional['modules'], $additional['modules']) - : $this->get_mappingid('course_module', $additional['modules'], $additional['modules']); - $additional['modules'] = $newcmid; - $additional = json_encode($additional); - $DB->set_field('pulse_condition_overrides', 'additional', $additional, ['id' => $ins->id]); - } - } - } - } - - /** - * Decode the pulse action restore contents. - * - * @param array $contents - * @return void - */ - public static function decode_contents(&$contents) { - // Get all the restore path elements, looking across all the subplugin dirs. - $subplugintype = 'pulseaction'; - $subpluginsdirs = core_component::get_plugin_list($subplugintype); - foreach ($subpluginsdirs as $name => $subpluginsdir) { - $classname = 'restore_' . $subplugintype . '_' . $name . '_subplugin'; - $restorefile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php'; - if (file_exists($restorefile)) { - require_once($restorefile); - $classname::decode_contents($contents); - } - } - } - -} diff --git a/classes/automation/action_base.php b/classes/automation/action_base.php deleted file mode 100644 index 5e0e887..0000000 --- a/classes/automation/action_base.php +++ /dev/null @@ -1,402 +0,0 @@ -. - -/** - * Notification pulse action - Automation actions plugins controller base. - * - * @package mod_pulse - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_pulse\automation; - -use backup_nested_element; -use moodle_exception; - -/** - * Notification pulse action - Automation actions base. - */ -abstract class action_base { - - /** - * Repersents the actions operator is any - * @var int - */ - const OPERATOR_ANY = 1; - - /** - * Repersents the actions operator is all - * @var int - */ - const OPERATOR_ALL = 2; - - /** - * The name of this action plugin. - * - * @var string - */ - protected $component; - - /** - * Returns the shortname of the configuration. - * - * @return string The shortname of the configuration. - */ - abstract public function config_shortname(); - - /** - * Triggers the action for a specific instance and user. - * - * @param object $instancedata The data for the instance. - * @param int $userid The ID of the user. - * - * @return void - */ - abstract public function trigger_action($instancedata, $userid); - - /** - * Deletes the action associated with a template. - * - * @param int $templateid The ID of the template. - * - * @return void - */ - abstract public function delete_template_action($templateid); - - /** - * Gets the data for a specific template. - * - * @param int $templateid The ID of the template. - * - * @return mixed The data for the template. - */ - abstract public function get_data_fortemplate($templateid); - - /** - * Gets the data for a specific instance. - * - * @param int $instanceid The ID of the instance. - * - * @return mixed The data for the instance. - */ - abstract public function get_data_forinstance($instanceid); - - /** - * Loads the global form. - * - * @param object $mform The Moodle form object. - * @param mixed $forminstance The form instance. - * - * @return void - */ - abstract public function load_global_form(&$mform, $forminstance); - - /** - * Sets the action plugins name. - * - * @param string $component The component to set. - * - * @return void - */ - public function set_component(string $component) { - $this->component = $component; - } - - /** - * Gets the name of the action component. - * - * @return string The component. - */ - public function get_component() { - return $this->component; - } - - /** - * Get the instance tablename for this action. - * - * @return string Tablename. - */ - public function get_instance_tablename() { - global $DB; - return $DB->get_prefix().'pulseaction_'.$this->component.'_ins'; - } - - /** - * Gets the table name for the action component. - * - * @return string The table name for the action component. - */ - public function get_tablename() { - global $DB; - return $DB->get_prefix().'pulseaction_'.$this->component; - } - - /** - * Performs actions after data has been defined in the form. - * - * @param object $mform The Moodle form object. - * @param \automation_templates_form $forminstance The form instance. - * - * @return void - */ - public function definition_after_data(&$mform, $forminstance) { - } - - /** - * Prepares file areas for the editor. - * - * @param mixed $data The default data of the form. - * @param \context $context The context object. - * - * @return bool False. - */ - public function prepare_editor_fileareas(&$data, \context $context) { - return false; - } - - /** - * Returns the default override elements. - * - * @return array An array of default override elements. - */ - public function default_override_elements() { - return []; - } - - /** - * Observe an event triggered from conditions. - * - * @param object $instancedata The data for the instance. - * @param string $method The method to trigger. - * @param mixed $eventdata The event data. - * - * @return bool True. - */ - public function trigger_action_event($instancedata, $method, $eventdata) { - return true; - } - - /** - * Handles actions when an instance is disabled. - * - * @param int $instanceid The ID of the instance. - * @param int $status The status of the instance. - * - * @return bool True. - */ - public function instance_disabled($instanceid, $status) { - return true; - } - - /** - * Loads the instance form by calling the global form loading method. - * - * @param object $mform The automation instance form object. - * @param mixed $forminstance - * - * @return void - */ - public function load_instance_form(&$mform, $forminstance) { - $this->load_global_form($mform, $forminstance); - } - - /** - * Deletes an action instances. - * - * @param int $instanceid The ID of the instance. - * - * @return bool True. - */ - public function delete_instance_action(int $instanceid) { - global $DB; - - if (!$this->component) { - throw new moodle_exception('componentnotset', 'pulse'); - } - - $instancetable = 'pulseaction_'.$this->component.'_ins'; - return $DB->delete_records($instancetable, ['instanceid' => $instanceid]); - } - - /** - * Filters action data by its configuration shortname. - * - * @param mixed $record The record to filter. - * - * @return object The filtered record. - */ - public function filter_action_data($record) { - - $shortname = $this->config_shortname(); - - $final = helper::filter_record_byprefix($record, $shortname); - - return (object) $final; - } - - /** - * Updates encoded data. - * - * @param mixed $data The data to update. - * - * @return mixed|null - */ - public function update_encode_data(&$data) { - return null; - } - - /** - * Includes data for a template. - * - * @param object $data The data object. - * - * @return bool True if successful, false otherwise. - */ - public function include_data_fortemplate(&$data) { - global $DB; - - // In moodle, the main table should be the name of the component. - // Therefore, generate the table name based on the component name. - $actiondata = $this->get_data_fortemplate($data->templateid); - - if (empty($actiondata)) { - return false; - } - - $this->update_encode_data($actiondata); - - if (!empty($actiondata)) { - $prefix = $this->config_shortname(); - $notificationkeys = array_keys($actiondata); - array_walk($notificationkeys, function(&$value) use ($prefix) { - $value = $prefix.'_'.$value; - }); - $data = (object) array_merge((array) $data, array_combine($notificationkeys, array_values($actiondata))); - } - } - - /** - * Includes actions instance data for an instance. - * - * @param stdClass $instance The instance object. - * @param bool $addprefix Flag to add prefix to keys. - * @return stdClass|array|bool Modified instance object or data without prefix. - */ - public function include_data_forinstance(&$instance, $addprefix=true) { - - // Retrieve action data for the template. - $actiondata = $this->get_data_fortemplate($instance->templateid); - - if (empty($actiondata)) { - return false; - } - // Set the prefix based on the component. - $prefix = $this->config_shortname(); - // Get data specific to the instance. - $actioninstancedata = $this->get_data_forinstance($instance->id); - - // ...TODO: Get editors dynamically. - // Define editors for special handling. - $editors = ['headercontent', 'footercontent', 'staticcontent']; - // Include the override data which is used in the form. - foreach ($actioninstancedata as $configname => $configvalue) { - // Count the overrides. - if ($configvalue !== null && array_key_exists($configname, (array) $actiondata) - && $configname != 'id' && $configname != 'dynamiccontent') { - $instance->overridecount = $instance->overridecount + 1; - } - - // Create a flag for override element to hide the switch. - if ($configvalue !== null) { - $configname = in_array($configname, $editors) ? $configname.'_editor' : $configname; - $instance->override[$prefix . "_" . $configname] = 1; - } - } - // Merge instance overrides with template data. - $actiondata = \mod_pulse\automation\helper::merge_instance_overrides($actioninstancedata, $actiondata); - // Update encoded data if necessary. - $this->update_encode_data($actiondata); - // Handle dynamic content if configured. - $actiondata['mod'] = ($actiondata['dynamiccontent']) ? get_coursemodule_from_id('', $actiondata['dynamiccontent']) : []; - - // Apply prefix to keys if needed. - if (!empty($actiondata) && $addprefix) { - $notificationkeys = array_keys($actiondata); - array_walk($notificationkeys, function(&$value) use ($prefix) { - $value = $prefix.'_'.$value; - }); - - // Update the keys with prefix. - $instance = (object) array_merge((array) $instance, array_combine($notificationkeys, array_values($actiondata))); - return $instance; - } - - // Return modified instance or data without prefix. - return $actiondata; - } - - /** - * Retrieves instances associated with a specific template. - * - * @param int $templateid The ID of the template. - * - * @return array An array of template instances or an empty array if none are found. - */ - protected function get_template_instances($templateid) { - global $DB; - - if ($instances = $DB->get_records('pulse_autoinstances', ['templateid' => $templateid]) ) { - return $instances; - } - - return []; - } - - /** - * Define the stutures of actions for the backup. - * - * @param object $instances - * @return void - */ - public function backup_define_structure(&$instances) { - global $DB; - - $templatecolumns = $DB->get_columns($this->get_tablename()); - $inscolumns = $DB->get_columns($this->get_instance_tablename()); - - // NOtification action template. - $action = new \backup_nested_element('action'.$this->component); - $actionfields = new \backup_nested_element($this->get_tablename(), ['id'], [ - array_keys($templatecolumns), - ]); - - $actionins = new \backup_nested_element('actionins'.$this->component); - $actioninsfields = new \backup_nested_element($this->get_instance_tablename(), ['id'], [ - array_keys($inscolumns), - ]); - - $instances->add_child($action); - $action->add_child($actionfields); - - $instances->add_child($actionins); - $actionins->add_child($actioninsfields); - - $actionfields->set_source_table($this->get_tablename(), ['instanceid' => \backup::VAR_PARENTID]); - $actioninsfields->set_source_table($this->get_tablename(), ['instanceid' => \backup::VAR_PARENTID]); - } - -} diff --git a/classes/automation/condition_base.php b/classes/automation/condition_base.php deleted file mode 100644 index c803179..0000000 --- a/classes/automation/condition_base.php +++ /dev/null @@ -1,306 +0,0 @@ -. - -/** - * Notification pulse action - Automation conditions base. - * - * @package mod_pulse - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_pulse\automation; - -/** - * Automation conditions base. - */ -abstract class condition_base { - - /** - * Repersents the conditions operator is any. - * @var int - */ - const OPERATOR_ANY = 1; - - /** - * Repersents the conditions operator is all. - * @var int - */ - const OPERATOR_ALL = 2; - - /** - * Repersents the condition status is disabled. - * @var int - */ - const DISABLED = 0; // Option. - - /** - * Repersents the condition status is all. - * @var int - */ - const ALL = 1; - - /** - * Repersents the condition status is future. - * @var int - */ - const FUTURE = 2; - - /** - * The name of this action plugin. - * - * @var string - */ - protected $component; - - /** - * Includes an condition based to the template condition options. - * - * @param mixed $option The action option. - */ - abstract public function include_condition(&$option); - - /** - * Loads the condition form for an instance. - * - * @param moodleform $mform The form to be loaded. - * @param stdClass $forminstance The instance object. - */ - abstract public function load_instance_form(&$mform, $forminstance); - - /** - * Sets the name of the conditions component. - * - * @param string $componentname The component name. - */ - public function set_component($componentname) { - $this->component = $componentname; - } - - /** - * Adds an upcoming element to the form. - * - * @param moodleform $mform The form object. - */ - public function upcoming_element(&$mform) { - - $mform->addElement('hidden', 'condition['.$this->component.'][upcomingtime]'); - $mform->setType('condition['.$this->component.'][upcomingtime]', PARAM_INT); - } - - /** - * Gets available options. - * - * @return array List of options. - */ - public function get_options() { - return [ - self::DISABLED => get_string('disable'), - self::ALL => get_string('all'), - self::FUTURE => get_string('upcoming', 'pulse'), - ]; - } - - /** - * Triggers an actions associated to the instance. - * - * @param int $instanceid The instance ID. - * @param int $userid The user ID. - * @param mixed $expectedtime The expected time for triggering. - * @param bool $newuser Is the trigger instance for new user. - */ - public function trigger_instance(int $instanceid, int $userid, $expectedtime=null, $newuser=false) { - - \mod_pulse\automation\instances::create($instanceid)->trigger_action($userid, $expectedtime, $newuser); - } - - /** - * Checks if a user has completed the conditions. - * - * @param mixed $notification The notification object. - * @param int $userid The user ID. - * - * @return bool True if user has completed, otherwise false. - */ - public function is_user_completed($notification, int $userid) { - return true; - } - - /** - * Status of the condition addon works based on the user enrolment. - * - * @return bool - */ - public function is_user_enrolment_based() { - return true; - } - - /** - * Processes the saving of an instance. - * - * @param int $instanceid The instance ID. - * @param array $data The data to be saved. - * - * @return bool True if the instance was saved successfully, otherwise false. - */ - public function process_instance_save($instanceid, $data) { - global $DB; - - // Remove empty values from data array. - $filter = array_filter($data); - // If 'status' is not set and there are no other non-empty values, stopped here. - if (!isset($data['status']) && empty($filter)) { - return true; - } - - // Get the 'status' or set it to an empty string if not present. - $status = $data['status'] ?? ''; - - // Future enrolment is disabled then make the upcoming time to null. - if ($status != self::FUTURE) { - $data['upcomingtime'] = ''; - } - // Future enrolments are enabled and its upcoming is empty set current time as upcoming. - // Conditions will affect for coming enrolments after this time. - if ($status == self::FUTURE && $data['upcomingtime'] == 0) { - $data['upcomingtime'] = time(); - } - // Prepare the record to be inserted or updated. - $record = [ - 'instanceid' => $instanceid, - 'triggercondition' => $this->component, - 'status' => $data['status'] ?? null, - 'upcomingtime' => $data['upcomingtime'] ?? null, - 'isoverridden' => (isset($data['status'])) ? true : false, - ]; - // Remove 'status' from data array. - unset($data['status']); - // Encode additional data as JSON. - $record['additional'] = json_encode($data); - - if ($this->component == 'events' && array_key_exists('event', $data)) { - $eventrecord = [ - 'instanceid' => $instanceid, - 'eventname' => stripslashes($data['event']), - 'notifyuser' => $data['notifyuser'], - ]; - if ($event = $DB->get_record('pulsecondition_events', ['instanceid' => $instanceid])) { - $eventrecord['id'] = $event->id; - // Update the record. - $DB->update_record('pulsecondition_events', $eventrecord); - } else { - // Insert the record. - $DB->insert_record('pulsecondition_events', $eventrecord); - } - } - - // Check if a record already exists for this instance and trigger condition. - if ($condition = $DB->get_record('pulse_condition_overrides', - ['instanceid' => $instanceid, 'triggercondition' => $this->component])) { - $record['id'] = $condition->id; - // Update the record. - $DB->update_record('pulse_condition_overrides', $record); - } else { - // Insert the record. - $DB->insert_record('pulse_condition_overrides', $record); - } - - return true; - } - - /** - * Includes data for an instance. - * - * @param object $instance The instance object. - * @param object $data The data object. - * @param bool $prefix Whether to add a prefix to keys or not. - * - * @return void - */ - public function include_data_forinstance(&$instance, $data, $prefix=true) { - // Decode additional data. - $additional = $data->additional ? json_decode($data->additional, true) : []; - - // Add overrides to instance. - foreach ($additional as $key => $value) { - // NEEDTOOVERVIEW: For the wrong count of overrides in the list, removed this additional. - $instance->override["condition_".$this->component."_".$key] = 1; - } - - // If data is overridden, set status override. - if ($data->isoverridden) { - $instance->condition[$this->component]['status'] = $data->status; - $instance->override["condition_".$this->component."_status"] = 1; - $count = isset($instance->overridecount) ? $instance->overridecount + 1 : 1; - $instance->overridecount = $count; - } - // If component condition is set, merge additional data and set 'upcomingtime'. - if (isset($instance->condition[$this->component]) - && $instance->condition[$this->component] != null && is_array($additional)) { - $instance->condition[$this->component] = array_merge($instance->condition[$this->component], $additional); - $instance->condition[$this->component]['upcomingtime'] = $data->upcomingtime ?: 0; - } - } - - /** - * List of placeholders rendered form the events. - * - * @return array - */ - public function get_email_placeholders() { - return []; - } - - /** - * Include the conditions var values for the placeholders. - * - * @param int $userid - * @param stdClass $instancedata - * @param stdClass $schedulerecord - * @return array - */ - public function update_email_customvars($userid, $instancedata, $schedulerecord) { - return []; - } - - /** - * Include the conditions join query with the sql used to fetch the schedule record. - * - * @return string - */ - public function schedule_override_join() { - return ''; - } - - /** - * Define the stutures of conditions for the backup. - * - * @param object $instances - * @return void - */ - public function backup_define_structure(&$instances) { - } - - /** - * Delete the records of condition for the custom instance. - * - * @param int $instanceid - * @return void - */ - public function delete_condition_instance(int $instanceid) { - return false; - } -} diff --git a/classes/automation/helper.php b/classes/automation/helper.php deleted file mode 100644 index e321e5a..0000000 --- a/classes/automation/helper.php +++ /dev/null @@ -1,660 +0,0 @@ -. - -/** - * Notification pulse action - Automation helper. - * - * @package mod_pulse - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_pulse\automation; - -use core_calendar\local\event\entities\event; -use html_writer; -use mod_pulse\plugininfo\pulseaction; -use moodle_url; -use single_button; - -/** - * Automation helper. - */ -class helper { - - /** - * Create the instance of the helper. - * - * @return helper - */ - public static function create() { - static $instance; - - if (!$instance) { - $instance = new self(); - } - - return $instance; - } - - /** - * Get templates for an instance. Retrieves templates for a given course, filtering based on categories, status, and visibility. - * - * @param int|null $courseid The ID of the course. Defaults to null. - * - * @return array Associative array of templates. - */ - public static function get_templates_forinstance($courseid=null) { - global $DB; - // Get course information. - $course = get_course($courseid); - // Generate the SQL LIKE condition for categories. - $like = $DB->sql_like('categories', ':value'); - // Construct the SQL query. - $sql = "SELECT * FROM {pulse_autotemplates} - WHERE (categories = '[]' OR categories = '' OR $like) AND visible = 1"; - $params = ['value' => '%"'.$course->category.'"%']; - // Retrieve records from the database. - $records = $DB->get_records_sql_menu($sql, $params); - // Format string values in the result. - array_walk($records, function(&$val) { - $val = format_string($val); - }); - - return $records; - } - - /** - * Merge instance overrides with template data. - * - * @param array $overridedata Array of overridden data. - * @param array $templatedata Array of template data. - * - * @return array Merged data. - */ - public static function merge_instance_overrides($overridedata, $templatedata) { - // Filter the empty values. - $filtered = array_filter((array) $overridedata, function($value) { - return $value !== null; - }); - // Merge the templatedata with filterdata. - $filtered = array_merge((array) $templatedata, $filtered); - - return $filtered; - } - - /** - * Filter the record data by keys with a specific prefix. - * - * @param array|object $record The record data to be filtered. - * @param string $prefix The prefix to filter keys by. - * - * @return array The filtered data with the prefix removed from keys. - */ - public static function filter_record_byprefix($record, $prefix) { - - // Filter the data based on the shortname. - $filtered = array_filter((array) $record, function($key) use ($prefix) { - return strpos($key, $prefix.'_') === 0; - }, ARRAY_FILTER_USE_KEY); - - // Remove the prefix from the keys. - $removedprefix = array_map(function($key) use ($prefix) { - return str_replace($prefix."_", '', $key); - }, array_keys($filtered)); - - // Combine the filtered values with prefix removed keys. - $final = array_combine(array_values($removedprefix), array_values($filtered)); - - return $final; - } - - /** - * Get a list of available actions plugins. - * - * @return array An array of available actions. - */ - public static function get_actions() { - return \mod_pulse\plugininfo\pulseaction::get_list(); - } - - /** - * Get a list of available conditions. - * - * @return array An array of available conditions. - */ - public static function get_conditions() { - return \mod_pulse\plugininfo\pulsecondition::get_list(); - } - - /** - * Prepare editor draft files for actions. - * - * @param array $data The data to be prepared. - * @param context $context The context. - */ - public static function prepare_editor_draftfiles(&$data, $context) { - - $actions = self::get_actions(); - foreach ($actions as $key => $action) { - $action->prepare_editor_fileareas($data, $context); - } - } - - /** - * Post-update editor draft files for actions. - * - * @param array $data The data to be updated. - * @param context $context The context. - */ - public static function postupdate_editor_draftfiles(&$data, $context) { - - $actions = self::get_actions(); - foreach ($actions as $key => $action) { - $action->postupdate_editor_fileareas($data, $context); - } - } - - /** - * Get instances associated with a course. - * - * @param int $courseid The ID of the course. - * - * @return array An array of instances associated with the course. - */ - public static function get_course_instances($courseid) { - global $DB; - - $list = $DB->get_records('pulse_autoinstances', ['courseid' => $courseid]); - - return $list; - } - - /** - * Insert the additional module fields data to the table. - * - * @param int $tablename - * @param int $instanceid - * @param array $options - * @return void - */ - public static function update_instance_option($tablename, int $instanceid, $options) { - global $DB; - - $records = []; - - foreach ($options as $name => $value) { - - if ($DB->record_exists($tablename, ['instanceid' => $instanceid, 'name' => $name])) { - $DB->set_field($tablename, 'value', $value, ['instanceid' => $instanceid, 'name' => $name]); - } else { - $record = new \stdClass; - $record->instanceid = $instanceid; - $record->name = $name; - $record->value = $value ?: ''; - $record->isoverridden = true; // Update overridden. - // Store to the list then insert at once after all the creations. - $records[$name] = $record; - } - } - - if (isset($records) && !empty($records)) { - $DB->insert_records($tablename, $records); - } - } - - /** - * Generate the button which is displayed on top of the templates table. Helps to create templates. - * - * @param bool $filtered Is the table result is filtered. - * @return string The HTML contents to display the create templates button. - */ - public static function template_buttons($filtered=false) { - global $OUTPUT, $DB, $CFG; - - require_once($CFG->dirroot. '/mod/pulse/automation/automationlib.php'); - - // Setup create template button on page. - $caption = get_string('templatecreatenew', 'pulse'); - $editurl = new moodle_url('/mod/pulse/automation/templates/edit.php', ['sesskey' => sesskey()]); - - // IN Moodle 4.2, primary button param depreceted. - $primary = defined('single_button::BUTTON_PRIMARY') ? single_button::BUTTON_PRIMARY : true; - $button = new single_button($editurl, $caption, 'get', $primary); - $button = $OUTPUT->render($button); - - // Filter form. - $button .= \html_writer::start_div('filter-form-container'); - $button .= \html_writer::link('javascript:void(0)', $OUTPUT->pix_icon('i/filter', 'Filter'), [ - 'id' => 'pulse-automation-filter', - 'class' => 'sort-autotemplates btn btn-primary ml-2 ' . ($filtered ? 'filtered' : ''), - ]); - $filter = new \template_table_filter(); - $button .= \html_writer::tag('div', $filter->render(), ['id' => 'pulse-automation-filterform', 'class' => 'hide']); - $button .= \html_writer::end_div(); - - // Sort button for the table. Sort y the reference. - $tdir = optional_param('tdir', null, PARAM_INT); - $tdir = ($tdir == SORT_ASC) ? SORT_DESC : SORT_ASC; - $dirimage = ($tdir == SORT_ASC) ? '' : $OUTPUT->pix_icon('t/sort_by', 'Sortby'); - - $manageurl = new moodle_url('/mod/pulse/automation/templates/list.php', [ - 'tsort' => 'reference', 'tdir' => $tdir, - ]); - $tempcount = $DB->count_records('pulse_autotemplates'); - if (!empty($tempcount)) { - $button .= \html_writer::link($manageurl->out(false), $dirimage.get_string('sort'), [ - 'class' => 'sort-autotemplates btn btn-primary ml-2', - ]); - } - - return $button; - } - - /** - * Create instance from templates. - * - * @param moodleform $form - * @param int $courseid - * @return string Form with templates list and manage templates button. - */ - public static function get_addtemplate_instance($form, $courseid) { - global $OUTPUT, $CFG, $PAGE; - - require_once($CFG->dirroot. '/mod/pulse/automation/automationlib.php'); - - // Form to add automation template as instance for the course. - $html = \html_writer::start_tag('div', ['class' => 'template-add-form']); - $templates = self::get_templates_forinstance($courseid); - if (!empty($templates)) { - $html .= $form->render(); - } - - // Button to access the manage the automation templates list. - // Verify the user capability. - $context = $PAGE->context; - if (has_capability('mod/pulse:viewtemplateslist', $context)) { - $manageurl = new moodle_url('/mod/pulse/automation/templates/list.php'); - $html .= \html_writer::link($manageurl->out(true), - get_string('managetemplate', 'pulse'), ['class' => 'btn btn-primary', 'target' => '_blank']); - } - - $tdir = optional_param('tdir', null, PARAM_INT); - $tdir = ($tdir == SORT_ASC) ? SORT_DESC : SORT_ASC; - $dirimage = ($tdir == SORT_ASC) ? '' : $OUTPUT->pix_icon('t/sort_by', 'Sortby'); - - $manageurl = new moodle_url('/mod/pulse/automation/instances/list.php', [ - 'courseid' => $courseid, 'tsort' => 'idnumber', 'tdir' => $tdir, - ]); - - if (!empty($templates)) { - $html .= \html_writer::link($manageurl->out(false), - $dirimage.get_string('sort'), ['class' => 'sort-autotemplates btn btn-primary ml-2']); - } - $html .= \html_writer::end_tag('div'); - - return $html; - } - - /** - * Templates table helps content. - * - * @return string - */ - public static function get_templates_tablehelps() { - global $OUTPUT; - - $actions = \mod_pulse\plugininfo\pulseaction::instance()->get_plugins_base(); - array_walk($actions, function(&$value) { - $classname = 'pulseaction_'.$value->get_component(); - $result['badge'] = \html_writer::tag('span', - get_string('formtab', 'pulseaction_'.$value->get_component()), ['class' => 'badge badge-primary '.$classname]); - $result['icon'] = \html_writer::span($value->get_action_icon(), 'action', ['class' => 'action-icon '.$classname]); - $value = $result; - }); - - $templatehelp = [ - 'help1' => implode(',', array_column($actions, 'icon')), - 'help2' => get_string('automation_title', 'pulse'), - 'help3' => implode(',', array_column($actions, 'badge')), - 'help4' => '
'.get_string('automation_reference', 'pulse').'
', - 'help5' => $OUTPUT->pix_icon('t/edit', \get_string('edit')), - 'help6' => $OUTPUT->pix_icon('t/hide', \get_string('hide')), - 'help7' => \html_writer::tag('div', ' - ', ['class' => "custom-control custom-switch"]), - 'help8' => \html_writer::tag('label', "33 (1)", ['class' => 'overrides badge badge-secondary pl-10']), - ]; - - $table = new \html_table(); - $table->id = 'plugins-control-panel'; - $table->head = ['', '']; - - foreach ($templatehelp as $help => $result) { - $row = new \html_table_row([$result, get_string('automationtemplate_'.$help, 'pulse')]); - $table->data[] = $row; - } - - $html = \html_writer::tag('h3', get_string('instruction', 'pulse')); - $html .= \html_writer::table($table); - return \html_writer::tag('div', $html, ['class' => 'automation-instruction']); - } - - /** - * Get instance table instructions helps. - * - * @return void - */ - public static function get_instance_tablehelps() { - global $OUTPUT; - - $actions = \mod_pulse\plugininfo\pulseaction::instance()->get_plugins_base(); - array_walk($actions, function(&$value) { - $classname = 'pulseaction_'.$value->get_component(); - $result['badge'] = \html_writer::tag('span', - get_string('formtab', 'pulseaction_'.$value->get_component()), ['class' => 'badge badge-primary ' . $classname]); - $result['icon'] = \html_writer::span($value->get_action_icon(), 'action', ['class' => 'action-icon ' . $classname]); - $value = $result; - }); - - $templatehelp = [ - 'help1' => implode(',', array_column($actions, 'icon')), - 'help2' => get_string('automationinstance_title', 'pulse'), - 'help3' => implode(',', array_column($actions, 'badge')), - 'help4' => '
'.get_string('automationinstance_reference', 'pulse').'
', - 'help5' => $OUTPUT->pix_icon('t/edit', \get_string('edit')), - 'help6' => $OUTPUT->pix_icon('t/copy', \get_string('copy')), - 'help7' => $OUTPUT->pix_icon('i/calendar', \get_string('instancereport', 'pulse')), - 'help8' => \html_writer::tag('div', ' - ', ['class' => "custom-control custom-switch"]), - 'help9' => $OUTPUT->pix_icon('t/delete', \get_string('delete')), - - ]; - - $table = new \html_table(); - $table->id = 'plugins-control-panel'; - $table->head = ['', '']; - - foreach ($templatehelp as $help => $result) { - $row = new \html_table_row([$result, get_string('automationinstance_'.$help, 'pulse')]); - $table->data[] = $row; - } - - $html = \html_writer::tag('h3', get_string('instruction', 'pulse')); - $html .= \html_writer::table($table); - return \html_writer::tag('div', $html, ['class' => 'automation-instruction']); - } - - /** - * Display the warning messages of actions for this course, If this instance is not ready to preform the actions. - * - * @param stdclass $course - * @return void - */ - public static function display_actions_course_warnings($course) { - $actions = self::get_actions(); - - $messages = []; - foreach ($actions as $component => $action) { - if (method_exists($action, 'display_instance_warnings')) { - $messages += $action->display_instance_warnings($course); - } - } - - $ul = \html_writer::tag('ul', implode('', array_map(fn($val) => '
  • ' . $val . '
  • ', array_filter($messages)))); - echo \html_writer::div($ul, 'pulse-warnings text-warning'); - } - - /** - * Find the time management tool installed and enabled in the learningtools. - * - * @return bool result of the time management plugin availability. - */ - public function timemanagement_installed() { - global $DB, $CFG; - $tools = \core_plugin_manager::instance()->get_subplugins_of_plugin('local_learningtools'); - if (in_array('ltool_timemanagement', array_keys($tools))) { - $status = $DB->get_field('local_learningtools_products', 'status', ['shortname' => 'timemanagement']); - if ($status) { - require_once($CFG->dirroot.'/local/learningtools/ltool/timemanagement/lib.php'); - } - return ($status) ? true : false; - } - return false; - } - - /** - * Get course time mananagment details user current course progress and due modules course. - * - * @param string $var - * @param \stdClass $course - * @param int $userid - * @param \context $context - * @param \stdClass $mod - * @return string - */ - public function timemanagement_details(string $var, \stdClass $course, int $userid, $context=null, $mod=null): string { - global $CFG, $DB, $PAGE; - - require_once($CFG->dirroot.'/enrol/locallib.php'); - - // Upcoming event dates. - if ($var == 'eventdates') { - // Calender lib inclusion. - require_once($CFG->dirroot.'/calendar/lib.php'); - - $calendar = \calendar_information::create(time(), $course->id, null); - list($data, $template) = calendar_get_view($calendar, 'upcoming_mini'); - $final = isset($data->events) ? array_map(function($event) { - $link = \html_writer::link($event->viewurl, $event->popupname); - $link .= '
    '.$event->formattedtime; - return \html_writer::tag('li', $link); - } , $data->events) : []; - - return $final ? \html_writer::tag('ul', implode('', $final)) : ''; - } - - // Other than eventdates all are need learning tools time management installed. - if (!$this->timemanagement_installed()) { - return ''; - } - - $context = $context ?? context_course::instance($course->id); - // Find the course due date. only if the timemanagement installed. - if ($var == 'coursedue' && function_exists('ltool_timemanagement_cal_course_duedate')) { - // Get user enrolment info in course. - $usercourseenrollinfo = ltool_timemanagement_get_course_user_enrollment($course->id, $userid); - $coursedatesinfo = $DB->get_record('ltool_timemanagement_course', ['course' => $course->id]); - // Check course set in time management. - if ($coursedatesinfo) { - $duedate = ltool_timemanagement_cal_course_duedate($coursedatesinfo, $usercourseenrollinfo[0]['timestart']); - return $duedate ? userdate($duedate) : ''; - } - return ''; - } - - if ($mod && $var == 'activityduedate' && function_exists('ltool_timemanagement_get_course_user_enrollment')) { - $userenrolments = ltool_timemanagement_get_course_user_enrollment($course->id, $userid); - $record = $DB->get_record('ltool_timemanagement_modules', ['cmid' => $mod->cmid ?? 0]); - if ($record) { - $dates = ltool_timemanagement_cal_coursemodule_managedates($record, $userenrolments[0]['timestart']); - return isset($dates['duedate']) ? userdate($dates['duedate']) : ''; - } - return ''; - } - - if ($mod && $var == 'upcomingmods' && function_exists('ltool_timemanagement_get_course_user_enrollment')) { - $mods = $this->ltool_timemanagement_get_upcoming_activities($course->id, $userid); - return implode(', ', array_map( - fn($mod) => \html_writer::link((new \moodle_url("/mod/$mod->modname/view.php", ['id' => $mod->id]))->out(false), - format_string($mod->name)), - array_values($mods))); - } - - return ''; - } - - /** - * Get upcoming activities list from time management learning tool. - * - * @param int $courseid - * @param int $userid - * @return string - */ - protected function ltool_timemanagement_get_upcoming_activities($courseid, $userid) { - global $DB; - $upcomingmods = []; - $modinfo = get_fast_modinfo($courseid); - $userenrolments = ltool_timemanagement_get_course_user_enrollment($courseid, $userid); - if (!empty($modinfo->sections) && !empty($userenrolments)) { - foreach ($modinfo->sections as $modnumbers) { - if (!empty($modnumbers)) { - foreach ($modnumbers as $modnumber) { - $mod = $modinfo->cms[$modnumber]; - if ($DB->record_exists('course_modules', ['id' => $mod->id, 'deletioninprogress' => 0]) - && !empty($mod) && $mod->uservisible) { - $record = $DB->get_record('ltool_timemanagement_modules', ['cmid' => $mod->id]); - if (!empty($record)) { - $timestarted = $userenrolments[0]['timestart']; - list('startdate' => $startdate, - 'duedate' => $duedate) = ltool_timemanagement_cal_coursemodule_managedates($record, $timestarted); - if ($startdate > time()) { - $upcomingmods[$modnumber] = $mod; - } - } - } - } - } - } - } - return $upcomingmods; - } - - /** - * Bulk edit action buttons for the instance management page. - * - * @param bool $filtered - * @return string $button. - */ - public static function bulkaction_buttons($filtered = true) { - global $OUTPUT; - - $button = ''; - $button .= \html_writer::start_div('box py-3 generalbox'); - - // Select all bulk action button. - $button .= \html_writer::link('javascript:void(0);', get_string('selectall', 'pulse'), [ - 'id' => 'selectall-btn', - 'class' => 'selectall-pulse-bulkaction btn btn-secondary ml-2', - ]); - - // De-select all bulk action button. - $button .= \html_writer::link('javascript:void(0);', get_string('deselectall', 'pulse'), [ - 'id' => 'deselectall-btn', - 'class' => 'deselectall-pulse-bulkaction btn btn-secondary ml-2', - ]); - - // Select without instance bulk action button. - $button .= \html_writer::link('javascript:void(0);', get_string('selectwithoutins', 'pulse'), [ - 'id' => 'selectwithoutins-btn', - 'class' => 'selectwithoutins-pulse-bulkaction btn btn-secondary ml-2', - ]); - - $button .= self::get_filter_button($filtered ?? false); - - $button .= \html_writer::end_div(); - - return $button; - } - - /** - * Return the bulk action buttons on the instance management tab. - * - * @return string $button - */ - public static function bulkreaction_buttons() { - global $OUTPUT; - - $button = ''; - $button .= html_writer::start_div('box py-3 generalbox', ['id' => 'bulk-action']); - $button .= html_writer::start_div('bulkaction-group hide'); - $button .= html_writer::span(get_string('withselect', 'pulse'), '', ['id' => 'withselect']); - - $button .= \html_writer::link('javascript:void(0);', - get_string('bulkaction:deleteinstance', 'pulse'). $OUTPUT->pix_icon('t/delete', \get_string('delete')), [ - 'id' => 'bulkdelete-btn', - 'class' => 'bulkdeleteinstance-btn btn btn-secondary ml-2', - ]); - - $button .= \html_writer::link('javascript:void(0);', get_string('bulkaction:addinstance', 'pulse'). - $OUTPUT->pix_icon('t/add', \get_string('addinstance', 'pulse')), [ - 'id' => 'bulkadd-btn', - 'class' => 'bulkaddinstance-btn btn btn-secondary ml-2', - ]); - - $button .= \html_writer::link('javascript:void(0);', - get_string('bulkaction:disableinstance', 'pulse'). - \html_writer::tag('div', ' - ', ['class' => "custom-control custom-switch"]), [ - 'id' => 'bulkdisable-btn', - 'class' => 'bulkdisableinstance-btn btn btn-secondary ml-2', - ]); - - $button .= \html_writer::link('javascript:void(0);', get_string('bulkaction:enableinstance', 'pulse'). - \html_writer::tag('div', ' - ', ['class' => "custom-control custom-switch"]), [ - 'id' => 'bulkenable-btn', - 'class' => 'bulkenableinstance-btn btn btn-secondary ml-2', - ]); - - $button .= \html_writer::end_div(); - $button .= \html_writer::end_div(); - return $button; - } - - /** - * Return the manage instance filter button html. - * - * @param bool $filtered - * @return string $button Filter button html - */ - public static function get_filter_button($filtered) { - global $CFG, $OUTPUT; - - require_once($CFG->dirroot. '/mod/pulse/automation/automationlib.php'); - - // Filter button. - $button = ''; - $button .= \html_writer::start_div('filter-form-container'); - $button .= \html_writer::link('javascript:void(0);', $OUTPUT->pix_icon('i/filter', 'Filter'), [ - 'id' => 'pulse-manageinstance-filter', - 'class' => 'sort-autotemplates btn btn-secondary ml-2 filtered' . ($filtered ? 'filtered' : ''), - ]); - - $templateid = optional_param('id', 0, PARAM_INT) ?? 0; - - $url = new moodle_url('/mod/pulse/automation/templates/edit.php', ['id' => $templateid, 'sesskey' => sesskey()]); - - $filter = new \manage_instance_table_filter( - $url->out(false).'#manage-instance-tab', ['id' => $templateid, 'numberofinstance' => null]); - - $button .= \html_writer::tag('div', $filter->render(), - ['id' => 'pulse-automation-filterform', 'class' => 'hideinstance hide']); - - $button .= \html_writer::end_div(); - - return $button; - } -} diff --git a/classes/automation/instances.php b/classes/automation/instances.php deleted file mode 100644 index 118c755..0000000 --- a/classes/automation/instances.php +++ /dev/null @@ -1,718 +0,0 @@ -. - -/** - * Notification pulse action - Automation instances. - * - * This controller handles the manitence of automation instance create, edit and delete. - * - * @package mod_pulse - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -namespace mod_pulse\automation; - -use availability_completion\condition; -use mod_pulse\automation\action_base; -use moodle_url; -use core_reportbuilder\local\helpers\report as reporthelper; -use course_enrolment_manager; -use moodle_exception; - -/** - * Automation Instance controller, handles instance data management. - */ -class instances extends templates { - - /** - * The ID of the automation instance. - * - * @var int - */ - protected $instanceid; - - /** - * The processed data of the automation instance. - * - * @var stdclass - */ - protected $instance; - - /** - * List of pulse action plugins - * - * @var stdclass - */ - protected $actions; - - /** - * Constructor for the class. - * - * @param int $instanceid The ID of the instance. - * - * @throws moodle_exception If the instance does not exist. - */ - public function __construct($instanceid) { - // ...TODO Check istance exists. throw exception if not available. - $this->instanceid = $instanceid; - $this->set_instance_actions(); - } - - /** - * Create a new instance of the class. - * - * @param int $instanceid The ID of the instance. - * - * @return self An instance of this class. - */ - public static function create($instanceid) { - $instance = new self($instanceid); - return $instance; - } - - /** - * Get instance data. Contains the conditions, and actions data related to this instance. - * Those data's are generated based on overrides. - * - * @return stdclass - */ - public function get_instance_data() { - - // Fetch the instance record to find the template id. - $instance = $this->get_instance_record(); - - if (empty($instance)) { - throw new moodle_exception('instancedatanotgenerated', 'pulse'); - } - // Include the template data with overrides merged. - $instance->template = \mod_pulse\automation\templates::create($instance->templateid)->get_data_forinstance($instance); - // Include the actions plugins data. - $instance->actions = $this->include_actions_data($instance, false); - // Include all the conditions data. - $this->include_conditions_data($instance); - - $this->instance = $instance; - // Include the course information to instance data. - $this->instance->course = get_course($instance->courseid); - - return $this->instance; - } - - /** - * Get the record for the current instance. - * - * @return stdClass|false The instance record or false if not found. - */ - protected function get_instance_record() { - global $DB; - - return $DB->get_record('pulse_autoinstances', ['id' => $this->instanceid]); - } - - /** - * Get the instance formdata. Contains the override info, these data is set to the form. - * - * Actions data are included with its config prefix. - * - * @return array - */ - public function get_instance_formdata() { - - // Fetch the instance record to find the template id. - $instance = $this->get_instance_record(); - - if (empty($instance)) { - throw new moodle_exception('instancedatanotgenerated', 'pulse'); - } - - // Fetch record of template instance. - \mod_pulse\automation\templates::create($instance->templateid)->get_data_forinstance($instance); - - $this->include_actions_data($instance); - - $this->include_conditions_data($instance); - - return ((array) $instance); - } - - /** - * Include conditions data for the given instance. - * - * @param stdClass $instance The instance object. - * - * @return array The array of conditions data. - */ - public function include_conditions_data(&$instance) { - global $DB; - - $overrides = $DB->get_records('pulse_condition_overrides', ['instanceid' => $instance->id]); - $conditions = \mod_pulse\plugininfo\pulsecondition::get_list(); - - // Define empty list of conditions. - if (!isset($instance->condition)) { - $instance->condition = []; - } - - // Include template conditions. - - $triggerconditions = $instance->template->triggerconditions ?? $instance->triggerconditions; - array_map(function($value) use ($instance, $conditions) { - $instance->condition[$value] = ['status' => 1]; - }, $triggerconditions); - - // Override the instance conditions to template conditions. - foreach ($overrides as $condition) { - if (isset($conditions[$condition->triggercondition])) { - $triggercon = $condition->triggercondition; - $conditiondata[$triggercon] = $conditions[$triggercon]->include_data_forinstance($instance, $condition); - } - } - - return $conditiondata ?? []; - } - - /** - * Include actions data for the given instance. - * - * @param stdClass $instance The instance object. - * @param bool $prefix Whether to include prefixes. - * - * @return array The array of actions data. - */ - public function include_actions_data(&$instance, $prefix=true) { - - // Fetch the list of enabled action plugins. - $actionplugins = $this->actions; - foreach ($actionplugins as $name => $plugin) { - // Include all the actions data for this template. - $actiondata[$name] = $plugin->include_data_forinstance($instance, $prefix); - } - - return $actiondata; - } - - /** - * Get the course ID associated with this instance. - * - * @return int The course ID. - */ - public function get_courseid() { - global $DB; - return $DB->get_field('pulse_autoinstances', 'courseid', ['instance' => $this->instanceid]); - } - - /** - * Set the instance actions based on enable status. - * ...TODO: Fetch the actions based on the enable status for instances in future. - */ - public function set_instance_actions() { - // ...TODO: Fetch the actions based on the enable status for instances in future. - $this->actions = \mod_pulse\plugininfo\pulseaction::get_list(); - } - - /** - * Updates the "visible" field of the current menu and deletes it from the cache. - * - * @param bool $status The new value for the "status" field. - * @param bool $instance - * @return bool True if the update was successful, false otherwise. - */ - public function update_status(bool $status, bool $instance = false) { - - // Verify the instance is available for the course category. - if ($instance) { - $instancedata = $this->get_instance_data(); - $templatedata = $instancedata->template; - if (empty($templatedata->categories) || in_array($instancedata->course->category, $templatedata->categories)) { - $status = $instancedata->status == self::STATUS_ORPHANED ? self::STATUS_ENABLE : $instancedata->status; - } else { - $status = self::STATUS_ORPHANED; - } - } - - $result = $this->update_field('status', $status, ['id' => $this->instanceid]); - - foreach ($this->actions as $component => $action) { - $action->instance_status_updated($this->get_instance_data(), $status); - } - - return $result; - } - - /** - * Updates a field of the current menu with the given key and value. - * - * @param string $key The key of the field to update. - * @param mixed $value The new value of the field. - * @return bool|int Returns true on success, or false on failure. it also deletes the current menu from cache. - */ - public function update_field($key, $value) { - global $DB; - - $result = $DB->set_field('pulse_autoinstances', $key, $value, ['id' => $this->instanceid]); - - return $result; - } - - /** - * Delete the current menu and all its associated items from the database. - * - * @return bool True if the deletion is successful, false otherwise. - */ - public function delete_instance() { - global $DB; - - if ($DB->delete_records('pulse_autoinstances', ['id' => $this->instanceid])) { - // Delete all of its instance data from templates. - $DB->delete_records('pulse_autotemplates_ins', ['instanceid' => $this->instanceid]); - // Delete all its actions data for this instance. - $this->delete_actions_instances($this->instanceid); - // Delete all its conditions data. - $this->delete_condition_instances($this->instanceid); - - return true; - } - return false; - } - - /** - * Create a duplicate of this instance. Fetch the formatdata for this instance and remove the ids. - * Then send to the manage-instance method it will create new instance. - * - * @return void - */ - public function duplicate() { - - $instance = (object) $this->get_instance_formdata(); - $instance->id = 0; - $instance->instanceid = 0; - - foreach (helper::get_actions() as $action) { - $config = $action->config_shortname(); - if (isset($instance->{$config.'_id'})) { - $instance->{$config.'_id'} = 0; - } - } - - $context = \context_system::instance(); - helper::prepare_editor_draftfiles($instance, $context); - - // Create the item. - self::manage_instance($instance); - } - - /** - * Get the URL to view the notification report. - * - * @return moodle_url The URL to the report. - */ - public function get_report_url() { - - $reportid = self::get_reportid(); - $url = new moodle_url('/reportbuilder/view.php', ['id' => $reportid, 'instanceid' => $this->instanceid]); - return $url; - } - - /** - * Get the id of the instance report builder. - * - * @return int - */ - public static function get_reportid() { - global $DB; - - $data = [ - 'source' => 'pulseaction_notification\reportbuilder\datasource\notification', - 'component' => 'pulseaction_notification', - ]; - - if ($report = $DB->get_record('reportbuilder_report', $data)) { - return $report->id; - } else { - $data['name'] = get_string('automationreportname', 'pulse'); - $instance = reporthelper::create_report((object) $data, (bool) 1); - $reportid = $instance->get('id'); - } - - return $reportid; - } - - /** - * Delete all the available actions linked with this template. - * - * Find the lis of actions and get linked template instance based template id and delete those actions. - * - * @param int $instanceid - * @return void - */ - public function delete_actions_instances($instanceid) { - global $DB; - // Fetch the list of enabled action plugins. - $actionplugins = \mod_pulse\plugininfo\pulseaction::get_list(); - foreach ($actionplugins as $name => $plugin) { - // Delete the instance data related to the action. - $plugin->delete_instance_action($instanceid); - } - } - - /** - * Delete all the available actions linked with this template. - * - * Find the lis of actions and get linked template instance based template id and delete those actions. - * - * @param int $instanceid - * @return void - */ - public function delete_condition_instances($instanceid) { - // Fetch the list of enabled action plugins. - $actionplugins = \mod_pulse\plugininfo\pulsecondition::get_list(); - foreach ($actionplugins as $name => $plugin) { - $plugin->delete_condition_instance($instanceid); - } - - // Delete the condition plugins overrides. - \mod_pulse\plugininfo\pulsecondition::delete_condition_instance_overrides($instanceid); - } - - /** - * Trigger the action for a user. - * - * @param int $userid The ID of the user. - * @param int|null $runtime The runtime of the action. - * @param bool $newuser Whether this is a new user. - */ - public function trigger_action($userid, $runtime=null, $newuser=false) { - global $DB; - - // Check the trigger conditions are ok. - $instancedata = (object) $this->get_instance_formdata(); - - foreach ($this->actions as $name => $plugin) { - // Send the trigger conditions are statified, then initate the instances based. - $plugin->trigger_action($instancedata, $userid, $runtime, $newuser); - } - } - - /** - * Trigger an action event. - * - * @param string $method The method to trigger. - * @param mixed $eventdata The event data. - */ - public function trigger_action_event($method, $eventdata) { - global $DB; - - // Check the trigger conditions are ok. - $instancedata = (object) $this->get_instance_data(); - - foreach ($this->actions as $name => $plugin) { - // Send the trigger conditions are statified, then initate the instances based. - $plugin->trigger_action_event($instancedata, $method, $eventdata); - } - - } - - /** - * Find the user completion conditions. - * - * @param stdclass $conditions - * @param stdclass $instancedata - * @param int $userid - * @param bool $isnewuser - * @return void - */ - public function find_user_completion_conditions($conditions, $instancedata, $userid, $isnewuser=false) { - global $CFG; - - require_once($CFG->dirroot.'/lib/completionlib.php'); - // Get course completion info instance. - - $course = $instancedata->course ?? get_course($instancedata->courseid); - - $completion = new \completion_info($course); - - // Trigger condition operator method, require the user to complete all the conditions or any of one is fine. - $count = ($instancedata->triggeroperator == action_base::OPERATOR_ALL); - $enabled = $result = 0; - - foreach ($conditions as $component => $option) { - // Get the condition plugin instance. - $condition = \mod_pulse\plugininfo\pulsecondition::instance()->get_plugin($component); - // Status of the condition, some conditions have additional values. - $status = (is_array($option)) ? $option['status'] : $option; - // No need to check the condition if condition is set as future enrolment and the user is old user. - if ($status <= 0 ) { - continue; - } - - $enabled++; // Increase enabled condition count. - - // Condition is only configured to verify the future enrolment. - if ($status == condition_base::FUTURE && !$isnewuser) { - $userenroltime = $this->get_user_enrolment_createtime($userid, $instancedata->course); - // User enrolled before the condition is set as upcoming. then not need to verify the condition. - // User is passed this condition by default. - if ($condition->is_user_enrolment_based() && ($userenroltime < $option['upcomingtime'])) { - continue; - } - } - - // Verify the user is completed the condition. - if ($condition->is_user_completed($instancedata, $userid, $completion)) { - - $result++; // Increase completed condition count for this user. - // Instance only configured to complete any one of the conditions. - if (!$count) { - return true; // Break the loop, found one completed condition. - } - } - } - - // If one or more conditions are enabled then the result of completion condition should be same as enabled. - return ($enabled == $result) ? true : false; - } - - /** - * Get the creation time of a user's enrolment in a course. - * - * @param int $userid The ID of the user. - * @param stdClass $course The course object. - * @return int|false The enrolment creation time or false if not found. - */ - public function get_user_enrolment_createtime($userid, $course) { - global $PAGE, $CFG; - - require_once($CFG->dirroot.'/enrol/locallib.php'); - - static $context; - static $courseid; - - if ($context == null || $course != $course->id) { - $context = \context_course::instance($course->id); - $courseid = $course->id; - } - $enrolments = (new course_enrolment_manager($PAGE, $course))->get_user_enrolments($userid); - - if (!empty($enrolments)) { - $enrolmenttime = current($enrolments)->timecreated; - return $enrolmenttime; - } - - return false; - } - - /** - * Insert or update the menu instance to DB. Convert the multiple options select elements to json. - * setup menu order after insert. - * - * Delete the current menu cache after updated the menu. - * - * @param stdclass $formdata - * @return bool - */ - public static function manage_instance($formdata) { - global $DB; - - $record = $formdata; - - // Filter the overridden enabled form elements names as a list. - $override = $record->override; - - if ($override = $record->override) { - - array_walk($override, function($value, $key) use (&$override) { - $length = strlen('_editor'); - if (substr_compare($key, '_editor', -$length) === 0) { // Find elements Ends with _editor. - $key = str_replace('_editor', '', $key); - $override[$key] = $value; - } - - // Update the interval key to notify. - // ...TODO: Update the method to notification action. - // ...TODO: create hook to update the elements or add override element for groups. - }); - - $overridenkeys = array_filter($override, function($value) { - return $value ? true : false; - }); - - // Fetch the list of keys need to remove override values - not overrides. - $removeoverridenkeys = array_filter($override, function($value) { - return $value ? false : true; - }); - - if (isset($formdata->instanceid)) { - self::remove_override_values($removeoverridenkeys, $formdata->instanceid, $formdata->templateid); - } - } - - // Start the database transcation. - $transaction = $DB->start_delegated_transaction(); - - // Fetch the related template data. - $templatedata = parent::create($formdata->templateid)->get_formdata(); - - // Course. - $course = $DB->get_record('course', ['id' => $formdata->courseid], '*', IGNORE_MISSING); - if (!$course) { - return false; - } - // Instance data to store in autoinstance table. - $instancedata = (object) [ - 'templateid' => $formdata->templateid, - 'courseid' => $formdata->courseid, - 'status' => $formdata->status ?? $templatedata->status, - ]; - - // Check the isntance is already created. if created update the record otherwise create new instance. - $instancedata->timemodified = time(); - if (isset($formdata->instanceid) && $DB->record_exists('pulse_autoinstances', ['id' => $formdata->instanceid])) { - - if (!empty($templatedata->categories) || in_array($course->category, $templatedata->categories)) { - $instancedata->status = $instancedata->status == self::STATUS_ORPHANED ? self::STATUS_ENABLE : - $instancedata->status; - } else { - $instancedata->status = self::STATUS_ORPHANED; - } - - $instancedata->id = $formdata->instanceid; - // Update the template. - $DB->update_record('pulse_autoinstances', $instancedata); - // Show the edited success notification. - \core\notification::success(get_string('templateupdatesuccess', 'pulse')); - - $instanceid = $instancedata->id; - - } else { - $instanceid = $DB->insert_record('pulse_autoinstances', $instancedata); - // Show the inserted success notification. - \core\notification::success(get_string('templateinsertsuccess', 'pulse')); - } - - // Store the tags. - if (isset($overridenkeys['tags']) && !empty($overridenkeys['tags'])) { - $tagoptions = self::get_tag_instance_options(); - $context = \context_system::instance(); - if (!empty($record->tags)) { - \core_tag_tag::set_item_tags( - $tagoptions['component'], $tagoptions['itemtype'], $instanceid, $context, $record->tags); - } - } - // Store the templates, conditions and actions data. Find the overridden elements. - $conditions = helper::filter_record_byprefix($override, 'condition'); - foreach ($conditions as $key => $status) { - $component = explode('_', $key)[0]; - if (!isset($record->condition[$component])) { - continue; - } - $conditionname = "pulsecondition_".$component."\conditionform"; - $condition = new $conditionname(); - $condition->set_component($component); - - $condition->process_instance_save($instanceid, $record->condition[$component]); - } - - // Fetch the value of the overridden settings. - $overriddenelements = array_intersect_key((array) $record, $overridenkeys); - - // ...Store the templates overrides. - // Fetch the auto templates fields. - $templatefields = $DB->get_columns('pulse_autotemplates_ins'); - $fields = array_keys($templatefields); - $preventfields = ['id', 'triggerconditions', 'timemodified']; - - // Clear unused fields from list. - $fields = array_diff_key(array_flip($fields), array_flip($preventfields)); - $templatedata = array_intersect_key((array) $overriddenelements, $fields); - - // Convert the elements array into json. - array_walk($templatedata, function(&$value) { - if (is_array($value)) { - $value = json_encode($value); - } - }); - - $tablename = 'pulse_autotemplates_ins'; // Template instance tablename to update. - // Update the instance overridden data related to template. - $templatedata['timemodified'] = time(); - \mod_pulse\automation\templates::update_instance_data($instanceid, $templatedata); - - // ...Send the data to action plugins for perform the data store. - $context = \context_course::instance($record->courseid); - // Find list of actions. - $actionplugins = \mod_pulse\plugininfo\pulseaction::get_list(); - - // Added the item id for file editors. - $overriddenelements['instanceid'] = $instanceid; - $overriddenelements['courseid'] = $record->courseid; - $overriddenelements['templateid'] = $formdata->templateid; - - foreach ($actionplugins as $component => $pluginbase) { - $pluginbase->postupdate_editor_fileareas($overriddenelements, $context); - $pluginbase->process_instance_save($instanceid, $overriddenelements); - } - - // Allow to update the DB changes to Database. - $transaction->allow_commit(); - - return $instanceid; - } - - /** - * Remove the values of previously overrides values, those values are removed now. - * - * @param array $fields - * @param int $instanceid - * @return void - */ - protected static function remove_override_values($fields, $instanceid) { - global $DB; - - if (!empty($fields)) { - // Remove the conditions overrides. - $conditions = helper::filter_record_byprefix($fields, 'condition'); - foreach ($conditions as $key => $status) { - $component = explode('_', $key)[0]; - $DB->set_field('pulse_condition_overrides', 'isoverridden', null, - ['instanceid' => $instanceid, 'triggercondition' => $component]); - } - - // Remove template fields data. - $templatefields = $DB->get_columns('pulse_autotemplates'); - $tempfields = array_keys($templatefields); - $preventfields = ['id', 'triggerconditions', 'timemodified']; - // Clear unused fields from list. - $tempfields = array_diff_key(array_flip($tempfields), array_flip($preventfields)); - $templatedata = array_intersect_key((array) $fields, $tempfields); - $templatedata = array_fill_keys(array_keys($templatedata), null); - $templatedata['id'] = $DB->get_field('pulse_autotemplates_ins', 'id', ['instanceid' => $instanceid]); - $DB->update_record('pulse_autotemplates_ins', $templatedata); - - // Remove the actions overrides. - $actions = helper::get_actions(); - foreach ($actions as $component => $pluginbase) { - // Filter the current action data from the templates data by its shortname. - $actiondata = $pluginbase->filter_action_data((array) $fields); - $actiondata = (object) array_fill_keys(array_keys((array) $actiondata), null); - if (empty((array) $actiondata)) { - continue; - } - $actiondata->id = $DB->get_field('pulseaction_'.$component.'_ins', 'id', ['instanceid' => $instanceid]); - $DB->update_record('pulseaction_'.$component.'_ins', $actiondata); - } - } - } -} diff --git a/classes/automation/manage.php b/classes/automation/manage.php deleted file mode 100644 index c1e6a34..0000000 --- a/classes/automation/manage.php +++ /dev/null @@ -1,226 +0,0 @@ -. - -/** - * Notification pulse action - Automation helper. - * - * @package mod_pulse - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_pulse\automation; - -use core_reportbuilder\external\reports\retrieve; -use html_writer; -use moodle_exception; -use stdClass; - -/** - * Instance management class. - */ -class manage { - - /** - * Template ID - * - * @var int - */ - public $templateid; - - /** - * Course ID - * - * @var int - */ - public $courseid; - - /** - * Get the template id and course id. - * - * @param int $templateid Template ID - * @param int $courseid Course ID - */ - public function __construct($templateid, $courseid) { - global $DB; - $this->templateid = $templateid; - $this->courseid = $courseid; - } - - /** - * Create an instance of the template. - * - * @param int $templateid The ID of the template - * @param int $courseid The ID of the course - * @return self The instance of the template. - */ - public static function create($templateid, $courseid) { - // ...TODO: template exist checks goes here. - return new self($templateid, $courseid); - } - - /** - * Delete all the auto template instance in the database table. - * - * @return bool - */ - public function delete_auto_instances() { - global $DB; - - $instances = $this->get_course_instances_record(); - if (!empty($instances)) { - foreach ($instances as $instanceid => $instance) { - instances::create($instanceid)->delete_instance(); - } - return true; - } - return false; - } - - /** - * Delete the all instance in this course use the templateid. - * - * @return bool True if the deletion is successful, false otherwise - */ - public function delete_course_instance() { - - if ($this->delete_auto_instances()) { - return true; - } - return false; - } - - /** - * Get the template record data. - */ - public function get_template_formdata() { - global $DB; - if ($template = $DB->get_record('pulse_autotemplates', ['id' => $this->templateid], '*', MUST_EXIST)) { - return $template; - } - } - - /** - * Retrieves instances associated with this template. - * - * @return array An array of template instances or an empty array if none are found. - */ - public function get_course_instances_record() { - global $DB; - - if ($instances = $DB->get_records('pulse_autoinstances', - ['templateid' => $this->templateid, 'courseid' => $this->courseid])) { - return $instances; - } - - return []; - } - - /** - * Added the automation instance for this course. - */ - public function add_course_instance() { - global $DB; - - $transaction = $DB->start_delegated_transaction(); - - // Fetch the related template data. - $templatedata = $this->get_template_formdata(); // Fetch the template data from DB. - - // Instance data to store in autoinstance table. - $instancedata = (object) [ - 'templateid' => $templatedata->id, - 'courseid' => $this->courseid, - 'status' => $templatedata->status, - 'timemodified' => time(), - ]; - - if ($instanceid = $DB->insert_record('pulse_autoinstances', $instancedata)) { - - $course = get_course($this->courseid); - - // Default override elements and values for the instance management. - $overriddenelements = [ - 'insreference' => shorten_text(strip_tags($course->shortname), 30), - 'pulsenotification_suppress' => [], - 'pulsenotification_suppressoperator' => 2, - 'pulsenotification_dynamiccontent' => 0, - 'pulsenotification_chapterid' => '', - ]; - $templatefields = $DB->get_columns('pulse_autotemplates_ins'); - $fields = array_keys($templatefields); - $preventfields = ['id', 'triggerconditions', 'timemodified']; - - // Clear unused fields from list. - $fields = array_diff_key(array_flip($fields), array_flip($preventfields)); - - $templatedata = array_intersect_key((array) $overriddenelements, $fields); - - // Convert the elements array into json. - array_walk($templatedata, function(&$value) { - if (is_array($value)) { - $value = json_encode($value); - } - }); - - $tablename = 'pulse_autotemplates_ins'; // Template instance tablename to update. - // Update the instance overridden data related to template. - $templatedata['timemodified'] = time(); - \mod_pulse\automation\templates::update_instance_data($instanceid, $templatedata); - - // ...Send the data to action plugins for perform the data store. - $context = \context_course::instance($this->courseid); - - // Find list of actions. - $actionplugins = \mod_pulse\plugininfo\pulseaction::get_list(); - - // Added the item id for file editors. - $overriddenelements['instanceid'] = $instanceid; - $overriddenelements['courseid'] = $this->courseid; - $overriddenelements['templateid'] = $this->templateid; - - foreach ($actionplugins as $component => $pluginbase) { - $pluginbase->postupdate_editor_fileareas($overriddenelements, $context); - $pluginbase->process_instance_save($instanceid, $overriddenelements); - } - - } - - // Allow to update the DB changes to Database. - $transaction->allow_commit(); - - return true; - - } - - /** - * Updates the "visible" field of the current menu and deletes it from the cache. - * - * @param bool $status The new value for the "status" field. - * @param bool $instance - * @return bool True if the update was successful, false otherwise. - */ - public function update_instance_status(bool $status, bool $instance=false) { - global $DB; - - $instances = $this->get_course_instances_record(); - foreach ($instances as $instanceid => $instance) { - instances::create($instanceid)->update_status($status); - } - return true; - } - -} diff --git a/classes/automation/templates.php b/classes/automation/templates.php deleted file mode 100644 index 1226524..0000000 --- a/classes/automation/templates.php +++ /dev/null @@ -1,531 +0,0 @@ -. - -/** - * Notification pulse action - Manage automation templates. - * - * @package mod_pulse - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -namespace mod_pulse\automation; - -use context_system; -use context_module; -use core_tag_tag; - -/** - * Manage automation templates. - */ -class templates { - - /** - * Repersents the template visibility is shown. - * @var int - */ - const VISIBILITY_SHOW = 1; - - /** - * Repersents the template visibility is hidden. - * @var int - */ - const VISIBILITY_HIDDEN = 0; - - /** - * Repersents the template status is enabled. - * @var int - */ - const STATUS_ENABLE = 1; - - /** - * Repersents the template status is disabled. - * @var int - */ - const STATUS_DISABLE = 0; - - /** - * Repersents the status of the instance is orphaned. - * @var int - */ - const STATUS_ORPHANED = -1; - - /** - * ID of the automation template. - * - * @var int - */ - protected $templateid; // Template id. - - /** - * The record of the templates - * - * @var stdclass - */ - protected $template; - - /** - * Constructor for initializing a template. - * - * @param int $templateid The ID of the template. - */ - protected function __construct($templateid) { - $this->templateid = $templateid; - $this->template = $this->get_template_record(); - } - - /** - * Get the template associated with this instance. - * - * @return stdClass The template object. - */ - public function get_template() { - return $this->template; - } - - /** - * Create an instance of the template. - * - * @param int $templateid The ID of the template. - * @return self The instance of the template. - */ - public static function create($templateid) { - // TODO: template exist checks goes here. - return new self($templateid); - } - - /** - * Find the template is available to add in the course. - * - * @param int $categoryid - * @return bool - */ - public function is_available_forcourse(int $categoryid): bool { - // Fetch template record. - $formdata = $this->template; - // Check the template is visible. - if (!$formdata->visible) { - return false; - } - // Verify template has category restriction, if contains check the course category is included in the categories list. - $categories = (array) $formdata->categories; - if ($categories && !in_array($categoryid, $categories)) { - return false; - } - // Template is visible. - return true; - } - - /** - * Retrieve the template record from the database. - * - * @return stdClass The template record. - */ - protected function get_template_record() { - - $data = $this->get_formdata(); // Fetch the template data from DB. - - // Convert the json values to array. - $data->tenants = json_decode($data->tenants); - $data->categories = json_decode($data->categories); - $data->triggerconditions = json_decode($data->triggerconditions); - - // Get the tags for the template. - $tagoptions = self::get_tag_options(); - $data->tags = core_tag_tag::get_item_tags_array($tagoptions['component'], $tagoptions['itemtype'], $data->id); - - $data->templateid = $data->id; - // Include all available actions data for this current template. - $this->include_actions_data($data); - - return $data; - } - - /** - * Get form data for a template. - * - * @return stdClass The template data. - */ - public function get_formdata() { - global $DB; - - if ($autotemplate = $DB->get_record('pulse_autotemplates', ['id' => $this->templateid], '*', MUST_EXIST)) { - return $autotemplate; - } - } - - /** - * Get data for a specific instance. - * - * @param stdClass $instance The instance object. - * @return stdClass The instance data. - */ - public function get_data_forinstance(&$instance) { - global $DB; - - $autotemplateins = $DB->get_record('pulse_autotemplates_ins', ['instanceid' => $instance->id], '*', MUST_EXIST); - $autotemplate = $this->get_formdata(); - - $instance->overridecount = 0; // Count the overrides for this instance. - $overridedata = array_filter((array) $autotemplateins); - foreach ($overridedata as $key => $value) { - if (!in_array($key, ['id', 'timemodified', 'instanceid', 'insreference'])) { - $instance->override[$key] = 1; - $instance->overridecount += 1; - } - } - - // Merge the override data intop of template data. - $data = (object) \mod_pulse\automation\helper::merge_instance_overrides($autotemplateins, $autotemplate); - - // Convert the json values to array. - $data->status = $instance->status; // Use the instance status as final instance status. - $data->tenants = json_decode($data->tenants); - $data->categories = json_decode($data->categories); - $data->triggerconditions = json_decode($data->triggerconditions); - $data->tags = json_decode($data->tags); - - $data->templateid = $autotemplate->id; - unset($data->id); // Remove the template id. - - $instance = (object) array_merge((array) $instance, (array) $data); - - return $data; - } - - /** - * Get raw data for templates. NOT RECOMMENDED - * - * @return stdClass The raw template data. - */ - public function get_templates_rawdata() { - global $DB; - - $actions = \mod_pulse\plugininfo\pulseaction::get_list(); - - $select = ['te.id as templateid']; - $join = []; - $i = 0; - foreach ($actions as $action) { - $i++; - $tablename = $action->get_tablename(); - if (!$tablename) { - continue; - } - $sht = 'ac'.$i; - $join[] = " JOIN $tablename AS $sht ON $sht.templateid=te.id "; - $select[] = "$sht.id as $sht, $sht.*"; - } - $select[] = 'te.*'; - $select = implode(', ', $select); - $join = implode(' ', $join); - - $sql = "SELECT $select FROM {pulse_autotemplates} te"; - $sql .= $join; - $sql .= " WHERE te.id=:templateid"; - - return $DB->get_record_sql($sql, ['templateid' => $this->templateid]); - } - - /** - * List of instanced created using this template - * - * @return void - */ - public function get_instances() { - global $DB; - - $instances = $DB->get_records('pulse_autoinstances', ['templateid' => $this->templateid]); - $overrides = []; - $overinstances = []; - if (!empty($instances)) { - foreach ($instances as $instanceid => $instance) { - $insobj = new \mod_pulse\automation\instances($instance->id); - $formdata = (object) $insobj->get_instance_formdata(); - - // Verify the override element is available. - if (!property_exists($formdata, 'override')) { - continue; - } - - foreach ($formdata->override as $key => $value) { - if (isset($overrides[$key])) { - $overrides[$key] += 1; - $overinstances[$key][] = ['id' => $instance->id, 'name' => format_string($formdata->title)]; - } else { - $overrides[$key] = 1; - $overinstances[$key] = [['id' => $instance->id, 'name' => format_string($formdata->title)]]; - } - } - } - } - - return [$overrides, $overinstances]; - } - - /** - * Retrieves instances associated with this template. - * - * @return array An array of template instances or an empty array if none are found. - */ - public function get_instances_record() { - global $DB; - - if ($instances = $DB->get_records('pulse_autoinstances', ['templateid' => $this->templateid]) ) { - return $instances; - } - - return []; - } - - /** - * Include actions data. - * - * @param stdclass $data - * @return void - */ - public function include_actions_data(&$data) { - - // Fetch the list of enabled action plugins. - $actionplugins = \mod_pulse\plugininfo\pulseaction::get_list(); - foreach ($actionplugins as $name => $plugin) { - // Include all the actions data for this template. - $plugin->include_data_fortemplate($data); - } - } - - /** - * Delete all the available actions linked with this template. - * - * Find the lis of actions and get linked template instance based template id and delete those actions. - * - * @param int $templateid - * @return void - */ - public function delete_template_actions($templateid) { - global $DB; - // Fetch the list of enabled action plugins. - $actionplugins = \mod_pulse\plugininfo\pulseaction::get_list(); - foreach ($actionplugins as $name => $plugin) { - $plugin->delete_template_action($templateid); - } - } - - /** - * Updates the "visible" field of the current menu and deletes it from the cache. - * - * @param bool $visible The new value for the "visible" field. - * @return bool True if the update was successful, false otherwise. - */ - public function update_visible(bool $visible) { - - return $this->update_field('visible', $visible, ['id' => $this->templateid]); - } - - /** - * Updates the "visible" field of the current menu and deletes it from the cache. - * - * @param bool $status The new value for the "status" field. - * @param bool $instance - * @return bool True if the update was successful, false otherwise. - */ - public function update_status(bool $status, bool $instance=false) { - global $DB; - - if ($instance) { - foreach ($this->get_instances_record() as $instanceid => $instance) { - instances::create($instanceid)->update_status($status); - } - } - - return $this->update_field('status', $status, ['id' => $this->templateid]); - } - - /** - * Updates a field of the current menu with the given key and value. - * - * @param string $key The key of the field to update. - * @param mixed $value The new value of the field. - * @return bool|int Returns true on success, or false on failure. it also deletes the current menu from cache. - */ - public function update_field($key, $value) { - global $DB; - - $result = $DB->set_field('pulse_autotemplates', $key, $value, ['id' => $this->templateid]); - - return $result; - } - - /** - * Delete the current menu and all its associated items from the database. - * - * @return bool True if the deletion is successful, false otherwise. - */ - public function delete_template() { - global $DB; - if ($DB->delete_records('pulse_autotemplates', ['id' => $this->templateid])) { - // Delete all its actions. - $this->delete_template_actions($this->templateid); - - return true; - } - return false; - } - - /** - * Get options for tagging templates. - * - * @return array An associative array with 'itemtype' and 'component'. - */ - public static function get_tag_options() { - return [ - 'itemtype' => 'pulse_autotemplates', - 'component' => 'mod_pulse', - ]; - } - - /** - * Get options for tagging template instances. - * - * @return array An associative array with 'itemtype' and 'component'. - */ - public static function get_tag_instance_options() { - return [ - 'itemtype' => 'pulse_autotemplates_ins', - 'component' => 'mod_pulse', - ]; - } - - - /** - * Insert or update the menu instance to DB. Convert the multiple options select elements to json. - * setup menu order after insert. - * - * Delete the current menu cache after updated the menu. - * - * @param stdclass $formdata - * @return bool - */ - public static function manage_instance($formdata) { - global $DB; - - $record = clone $formdata; - - // Encode the multiple value elements into json to store. - foreach ($record as $key => $value) { - if (is_array($value)) { - $record->$key = json_encode($value); - } - } - - $transaction = $DB->start_delegated_transaction(); - - // Create template record. - $record->reference = shorten_text(strip_tags($record->reference), 30); - $record->timemodified = time(); - if (isset($formdata->id) && $DB->record_exists('pulse_autotemplates', ['id' => $formdata->id])) { - $templateid = $formdata->id; - - // Update the template. - $DB->update_record('pulse_autotemplates', $record); - foreach (self::create($templateid)->get_instances_record() as $instanceid => $instance) { - instances::create($instanceid)->update_status($formdata->status, true); - } - - // Show the edited success notification. - \core\notification::success(get_string('templateupdatesuccess', 'pulse')); - } else { - $templateid = $DB->insert_record('pulse_autotemplates', $record); - // Show the inserted success notification. - \core\notification::success(get_string('templateinsertsuccess', 'pulse')); - } - - // Update template tags. - $tagoptions = self::get_tag_options(); - $context = context_system::instance(); - - if (!empty($formdata->tags)) { - \core_tag_tag::set_item_tags( - $tagoptions['component'], $tagoptions['itemtype'], $templateid, $context, $formdata->tags); - } - - // Store actions data. - // Send the data to action plugins for perform the data store. - // Find list of actions. - $actionplugins = new \mod_pulse\plugininfo\pulseaction(); - $plugins = $actionplugins->get_plugins_base(); - - foreach ($plugins as $component => $pluginbase) { - $formdata->templateid = $templateid; - $pluginbase->process_save($formdata, $component); - } - // Allow to update the DB changes to Database. - $transaction->allow_commit(); - - return $templateid; - } - - /** - * Update instance data. - * - * @param int $instanceid - * @param array $options - * @return void - */ - public static function update_instance_data($instanceid, $options) { - global $DB; - - if (isset($options['reference'])) { - $options['reference'] = shorten_text(strip_tags($options['reference']), 30); - } - - if ($record = $DB->get_record('pulse_autotemplates_ins', ['instanceid' => $instanceid])) { - $diff = array_diff_key((array) $record, $options); - $removeoverrides = array_combine(array_keys($diff), array_fill(0, count($diff), null)); - $removeoverrides['id'] = $record->id; - $removeoverrides['instanceid'] = $record->instanceid; - $removeoverrides = array_merge($removeoverrides, $options); - - return $DB->update_record('pulse_autotemplates_ins', $removeoverrides); - } else { - $options['instanceid'] = $instanceid; - return $DB->insert_record('pulse_autotemplates_ins', $options); - } - - return false; - } - - /** - * Get records for a list of templates. - * - * @param array $templates An array of template IDs. - * @return array An associative array of template records. - */ - public static function get_templates_record($templates) { - global $DB; - - if (empty($templates)) { - return true; - } - // Generate SQL for IN clause and prepare parameters. - list($insql, $inparams) = $DB->get_in_or_equal($templates, SQL_PARAMS_NAMED, 'ins'); - $sql = "SELECT * FROM {pulse_autotemplates} te WHERE te.id $insql"; - // Fetch template records. - $tempoverride = $DB->get_records_sql($sql, $inparams); - - return $tempoverride; - } - -} diff --git a/classes/eventobserver.php b/classes/eventobserver.php index 8b86522..dbd9b23 100644 --- a/classes/eventobserver.php +++ b/classes/eventobserver.php @@ -78,44 +78,7 @@ public static function user_enrolment_deleted($event) { $DB->delete_records_select('pulse_users', $select, $inparams); } - self::trigger_action_event('user_enrolment_deleted', $event); - return true; } - /** - * User enrolment trigger actions. - * - * @param [type] $event - * @return void - */ - public static function user_enrolment_created($event) { - $userid = $event->relateduserid; // Unenrolled user id. - $courseid = $event->courseid; - - $list = \mod_pulse\automation\helper::get_course_instances($courseid); - if (!empty($list)) { - foreach ($list as $instanceid => $instance) { - \mod_pulse\automation\instances::create($instanceid)->trigger_action($userid, null, true); - } - } - } - - /** - * Trigger an action event for all instances in a course. - * - * @param string $method The method to trigger. - * @param stdClass $event The event object. - */ - public static function trigger_action_event($method, $event) { - - $courseid = $event->courseid; - - $list = \mod_pulse\automation\helper::get_course_instances($courseid); - if (!empty($list)) { - foreach ($list as $instanceid => $instance) { - \mod_pulse\automation\instances::create($instanceid)->trigger_action_event($method, $event); - } - } - } } diff --git a/classes/external.php b/classes/external.php index efb2052..c39d9af 100644 --- a/classes/external.php +++ b/classes/external.php @@ -85,102 +85,4 @@ public static function apply_presets(int $contextid, string $formdata, $pagepara public static function apply_presets_returns() { return new \external_value(PARAM_RAW, 'Count of Page user notes'); } - - /** - * Bulk manage instances on the instance managemente table. - * - * @return array bulk actions parameters. - */ - public static function manage_instances_parameters() { - - return new \external_function_parameters( - [ - 'templateid' => new \external_value(PARAM_INT, 'The automation template id'), - 'courseids' => new \external_multiple_structure(new \external_value(PARAM_INT, 'course ID', - VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of Course IDs', VALUE_DEFAULT, []), - 'action' => new \external_value(PARAM_TEXT, 'The bulk action'), - ] - ); - } - - /** - * Manage the instance on the selected courses use the bulk action in the instance management table. - * - * @param int $templateid Automation template ID. - * @param array $courseids Course ID. - * @param string $action Action name - * - * @return array $message - */ - public static function manage_instances(int $templateid, array $courseids, string $action) { - global $DB; - - $vaildparams = self::validate_parameters(self::manage_instances_parameters(), - ['templateid' => $templateid, 'courseids' => $courseids, 'action' => $action]); - - $message = ''; - switch ($vaildparams['action']) { - - case 'delete': - if (!empty($vaildparams['courseids'])) { - foreach ($vaildparams['courseids'] as $courseid) { - $management = \mod_pulse\automation\manage::create($templateid, $courseid); - if ($management->delete_course_instance()) { - $message = get_string('templatedeleted', 'pulse'); - } - } - } - break; - - case 'add': - if (!empty($vaildparams['courseids'])) { - foreach ($vaildparams['courseids'] as $courseid) { - $management = \mod_pulse\automation\manage::create($templateid, $courseid); - if ($management->add_course_instance()) { - $message = get_string('templateinsertsuccess', 'pulse'); - } - } - } - break; - - case 'disable': - if (!empty($vaildparams['courseids'])) { - foreach ($vaildparams['courseids'] as $courseid) { - $management = \mod_pulse\automation\manage::create($templateid, $courseid); - if ($management->update_instance_status(false)) { - $message = get_string('templatedisablesuccess', 'pulse'); - } - } - } - break; - - case 'enable': - if (!empty($vaildparams['courseids'])) { - foreach ($vaildparams['courseids'] as $courseid) { - $management = \mod_pulse\automation\manage::create($templateid, $courseid); - if ($management->update_instance_status(true)) { - $message = get_string('templateenablesuccess', 'pulse'); - } - } - } - break; - } - return [ - 'message' => $message, - ]; - } - - /** - * Retuns the redirect message for manage instances. - * - * @return array message. - */ - public static function manage_instances_returns() { - return new \external_single_structure( - [ - 'message' => new \external_value(PARAM_TEXT, 'Return status message'), - ] - ); - } - } diff --git a/classes/forms/automation_instance_form.php b/classes/forms/automation_instance_form.php deleted file mode 100644 index 8da04cf..0000000 --- a/classes/forms/automation_instance_form.php +++ /dev/null @@ -1,331 +0,0 @@ -. - -/** - * Automation instance form for the pulse 2.0. - * - * @package mod_pulse - * @copyright 2023, bdecent gmbh bdecent.de - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_pulse\forms; - -defined('MOODLE_INTERNAL') || die('No direct access!'); - -require_once($CFG->dirroot.'/lib/formslib.php'); - -use html_writer; -use mod_pulse\automation\templates; -use mod_pulse\automation\helper; -/** - * Define the automation instance form. - */ -class automation_instance_form extends automation_template_form { - - /** - * Define the form elements inside the definition function. - * - * @return void - */ - public function definition() { - global $PAGE, $OUTPUT; - - $mform = $this->_form; // Get the form instance. - - // Set the id of template. - $mform->addElement('hidden', 'id', 0); - $mform->setType('id', PARAM_INT); - - $mform->updateAttributes(['id' => 'pulse-automation-template' ]); - $tabs = [ - ['name' => 'autotemplate-general', 'title' => get_string('tabgeneral', 'pulse'), 'active' => 'active'], - ['name' => 'pulse-condition-tab', 'title' => get_string('tabcondition', 'pulse')], - ]; - - $context = \context_system::instance(); - - // Load all actions forms. - // Define the lang key "formtab" in the action component it automatically includes it. - foreach (helper::get_actions() as $key => $action) { - $tabs[] = ['name' => 'pulse-action-'.$key, 'title' => get_string('formtab', 'pulseaction_'.$key)]; - } - - // Instance management tab. - if ($templateid = $this->get_customdata('id') && has_capability('mod/pulse:manageinstance', $context)) { - $tabs[] = ['name' => 'manage-instance-tab', 'title' => get_string('tabmanageinstance', 'pulse')]; - } - - $tab = $OUTPUT->render_from_template('mod_pulse/automation_tabs', ['tabs' => $tabs]); - $mform->addElement('html', $tab); - - // Pulse templates tab content. - $mform->addElement('html', '
    '); - - // Template options. - $this->load_template_options($mform); - - // Template conditions. - $this->load_template_conditions($mform); - - // Load template actions. - $this->load_template_actions($mform); - - if ($templateid = $this->get_customdata('id')) { - list($overcount, $overinstances) = templates::create($templateid)->get_instances(); - - foreach ($mform->_elements as $key => $element) { - - $elementname = $element->getName(); - if (!isset($overcount[$elementname])) { - continue; - } - $count = html_writer::tag('span', $overcount[$elementname].' '.get_string('overrides', 'pulse'), [ - 'data-target' => "overridemodal", - 'data-templateid' => $templateid, - 'data-element' => $elementname, - 'class' => 'override-count-element badge mt-1', - 'id' => "override_$elementname", - ]); - $overrideelement = $mform->createElement('html', $count); - $mform->insertElementBefore($overrideelement, $elementname); - - $mform->addElement('hidden', "overinstance_$elementname", json_encode($overinstances[$elementname])); - $mform->setType("overinstance_$elementname", PARAM_RAW); - } - } - - $mform->addElement('html', html_writer::end_div()); - - // Show the list of overriden content. - $PAGE->requires->js_call_amd('mod_pulse/automation', 'init'); - - // Add standard form buttons. - $this->add_action_buttons(true); - } - - /** - * After the instance form elements are defined, create its override options for all elemennts, include hidden instance data. - * Remove elements doesn't used in instances. - * - * @return void - */ - public function after_definition() { - global $PAGE, $CFG; - - $mform =& $this->_form; - - $mform->updateAttributes(['id' => 'pulse-automation-template' ]); - // Courseid. - $course = $this->_customdata['courseid'] ?? ''; - $mform->addElement('hidden', 'courseid', $course); - $mform->setType('courseid', PARAM_INT); - - $templateid = $this->_customdata['templateid'] ?? ''; - $mform->addElement('hidden', 'templateid', $templateid); - $mform->setType('templateid', PARAM_INT); - - $templateid = $this->_customdata['instanceid'] ?? ''; - $mform->addElement('hidden', 'instanceid', $templateid); - $mform->setType('instanceid', PARAM_INT); - - $mform->removeElement('visible'); - $mform->removeElement('categories'); - - // Get the list of elments add in this form. create override button for all elements expect the hidden elements. - $elements = $mform->_elements; - - // Add the Reference element. - $reference = $mform->createElement('text', 'insreference', get_string('reference', 'pulse'), ['size' => '50']); - $mform->insertElementBefore($reference, 'reference'); - $mform->setType('insreference', PARAM_ALPHANUMEXT); - $mform->addRule('insreference', get_string('required'), 'required', null, 'client'); - $mform->addHelpButton('insreference', 'reference', 'pulse'); - - $mform->removeElement('reference'); - - $templatereference = $this->get_customdata('templatereference'); - $input = html_writer::empty_tag('input', - ['class' => 'form-control', - 'type' => 'text', - 'value' => $templatereference, - 'disabled' => 'disabled', - ]); - $referenceprefix = $mform->createElement('html', html_writer::div($input, 'hide', ['id' => 'pulse-template-reference'])); - $mform->insertElementBefore($referenceprefix, 'insreference'); - - $this->load_default_override_elements(['insreference']); - - // Add element to disable instance override options. - $hasinstanceeditcapability = has_capability('mod/pulse:overridetemplateinstance', \context_course::instance($course)); - $mform->addElement('hidden', 'hasinstanceeditcapability', $hasinstanceeditcapability); - $mform->setType('hasinstanceeditcapability', PARAM_INT); - - if (!empty($elements)) { - // List of element type don't need to add the override option. - $dontoverride = ['html', 'header', 'hidden', 'button']; - - foreach ($elements as $element) { - - if (!in_array($element->getType(), $dontoverride) && $element->getName() !== 'buttonar') { - - if ($hasinstanceeditcapability) { - // Has capability add override element. - $this->add_override_element($element, $course); - } else { - $elementname = $element->getName(); - $orgelementname = $elementname; - $mform->disabledIf($orgelementname, 'hasinstanceeditcapability', 'neq', 1); - } - } - } - } - // Email placeholders. - $PAGE->requires->js_call_amd('mod_pulse/vars', 'init'); - $PAGE->requires->js_call_amd('mod_pulse/module', 'init', [$CFG->branch]); - } - - /** - * Add an override element to the form. - * - * @param mixed $element The form element. - * @param int $course The course ID - */ - protected function add_override_element($element, $course) { - - $mform =& $this->_form; - $elementname = $element->getName(); - $orgelementname = $elementname; - - if (stripos($elementname, "[") !== false) { - $name = str_replace("]", "", str_replace("[", "_", $elementname)); - $name = 'override[' . $name .']'; - } else { - $name = 'override[' . $elementname .']'; - } - - // Override element already exists, no need to create new one. - if (isset($mform->_elementIndex[$name])) { - return; - } - - $overrideelement = $mform->createElement('advcheckbox', $name, '', '', - ['group' => 'automation', 'class' => 'custom-control-input'], [0, 1]); - - // Insert the override checkbox before the element. - if (isset($mform->_elementIndex[$orgelementname]) && $mform->_elementIndex[$orgelementname] - && has_capability('mod/pulse:overridetemplateinstance', \context_course::instance($course))) { - $mform->insertElementBefore($overrideelement, $orgelementname); - } - - // Disable the form fields by default, only enable whens its enabled for overriddden. - $mform->disabledIf($orgelementname, $name, 'notchecked'); - } - - /** - * Includ the pulse conditions element to the instance form. - * - * @return void - */ - protected function load_template_conditions() { - - $mform =& $this->_form; - $mform->addElement('html', '
    '); - $mform->addElement('header', 'generalconditions', '

    '.get_string('general').'

    '); - // Operator element. - $operators = [ - \mod_pulse\automation\action_base::OPERATOR_ALL => get_string('all', 'pulse'), - \mod_pulse\automation\action_base::OPERATOR_ANY => get_string('any', 'pulse'), - ]; - $mform->addElement('select', 'triggeroperator', get_string('triggeroperator', 'pulse'), $operators); - $mform->addHelpButton('triggeroperator', 'triggeroperator', 'pulse'); - - $conditionplugins = new \mod_pulse\plugininfo\pulsecondition(); - $plugins = $conditionplugins->get_plugins_base(); - - foreach ($plugins as $name => $plugin) { - $mform->addElement('header', $name, get_string('pluginname', 'pulsecondition_'.$name)); - - $plugin->load_instance_form($mform, $this); - $plugin->upcoming_element($mform); - $mform->setExpanded($name); - } - $mform->addElement('html', ''); // E.o of actions triggere tab. - $mform->addElement('html', html_writer::end_div()); // E.o of actions triggere tab. - } - - /** - * Load instance form elments for pulse action plugins. - * - * @return void - */ - protected function load_template_actions() { - $mform =& $this->_form; - $actionplugins = new \mod_pulse\plugininfo\pulseaction(); - $plugins = $actionplugins->get_plugins_base(); - foreach ($plugins as $name => $plugin) { - // Define the form elements inside the definition function. - $mform->addElement('html', '