diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 228e62b..ce7f2b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,16 +9,13 @@ jobs: matrix: # Note: Workflow block has SQL that only works in Postgres. include: - - php: '7.4' - moodle-branch: 'master' - database: 'pgsql' - - php: '7.4' - moodle-branch: 'MOODLE_311_STABLE' - database: 'pgsql' + - { php: '8.2', moodle-branch: MOODLE_405_STABLE, database: pgsql } + - { php: '8.3', moodle-branch: MOODLE_500_STABLE, database: pgsql } + - { php: '8.3', moodle-branch: main, database: pgsql } services: postgres: - image: postgres + image: postgres:15 env: POSTGRES_USER: 'postgres' POSTGRES_HOST_AUTH_METHOD: 'trust' @@ -32,7 +29,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: plugin @@ -45,16 +42,17 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: mbstring, pgsql, mysqli + extensions: ${{ matrix.extensions }} + ini-values: max_input_vars=5000 + coverage: none - name: Deploy moodle-plugin-ci run: | - composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3 - # Add dirs to $PATH + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 echo $(cd ci/bin; pwd) >> $GITHUB_PATH echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH - # PHPUnit depends on en_AU.UTF-8 locale sudo locale-gen en_AU.UTF-8 + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - name: Install Moodle run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 @@ -67,8 +65,9 @@ jobs: run: moodle-plugin-ci phplint - name: phpcpd + continue-on-error: true if: ${{ always() }} - run: moodle-plugin-ci phpcpd || true + run: moodle-plugin-ci phpcpd - name: phpmd if: ${{ always() }} diff --git a/addworkflow.php b/addworkflow.php index def7286..049a79e 100644 --- a/addworkflow.php +++ b/addworkflow.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); diff --git a/ajax.php b/ajax.php deleted file mode 100644 index 58d357d..0000000 --- a/ajax.php +++ /dev/null @@ -1,108 +0,0 @@ -. - -/** - * Server-side script for all ajax request for workflow API - * - * @package block_workflow - * @copyright 2011 Lancaster University Network Services Limited - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -define('AJAX_SCRIPT', true); - -require_once(dirname(__FILE__) . '/../../config.php'); -require_once(dirname(__FILE__) . '/locallib.php'); -require_once($CFG->dirroot . '/lib/form/editor.php'); - -// Require the session key. -require_sesskey(); - -$action = required_param('action', PARAM_ACTION); -$stateid = required_param('stateid', PARAM_INT); - -$state = new block_workflow_step_state($stateid); -list($context, $course, $cm) = get_context_info_array($state->contextid); - -// Require login. -require_login($course, false, $cm); - -$PAGE->set_context($context); -$PAGE->set_url('/blocks/workflow/ajax.php', array('stateid' => $stateid, 'action' => $action)); - -// Send headers. -echo $OUTPUT->header(); - -$outcome = new stdClass; -$outcome->success = true; -$outcome->response = new stdClass; -$outcome->error = ''; - -if (!block_workflow_can_make_changes($state)) { - // Check that the user is allowed to work on this step. - throw new block_workflow_ajax_exception(get_string('notallowedtodothisstep', 'block_workflow')); -} - -switch ($action) { - case 'getcomment': - $outcome->response->comment = $state->comment; - break; - case 'savecomment': - $text = required_param('text', PARAM_CLEANHTML); - $format = required_param('format', PARAM_INT); - $state->update_comment($text, $format); - $outcome->response->blockcomments = shorten_text($text, BLOCK_WORKFLOW_MAX_COMMENT_LENGTH); - break; - case 'finishstep': - $text = required_param('text', PARAM_CLEANHTML); - $format = required_param('format', PARAM_INT); - $renderer = $PAGE->get_renderer('block_workflow'); - - // Retrieve the next step. - $newstate = $state->finish_step($text, $format); - $canview = ($newstate) ? has_capability('block/workflow:view', $newstate->context()) : false; - - if ($newstate && ($canview || block_workflow_can_make_changes($newstate))) { - // There is a next possible state, and the current user may view and/or work on it. - $outcome->response->blockcontent = $renderer->block_display($newstate, true); - $outcome->response->stateid = $newstate->id; - } else if ($newstate) { - // There is a new step, but this user can't view it, and can't work on it ... - $outcome->response->blockcontent = $renderer->block_display_step_complete_confirmation(); - } else { - // Last step has been reached, if permitted retrieve the list of workflows. - $workflows = new block_workflow_workflow(); - $previous = $workflows->load_context_workflows($state->contextid); - $canadd = has_capability('block/workflow:manage', $state->context()); - $appliesto = $state->step()->workflow()->appliesto; - $addableworkflows = block_workflow_workflow::available_workflows($appliesto); - $outcome->response->listworkflows = $canadd && $addableworkflows; - - // Display. - $outcome->response->blockcontent = $renderer->block_display_no_more_steps( - $state->contextid, $canadd, $addableworkflows, $previous); - } - break; - case 'toggletaskdone': - // Toggle the todo item (mark as done). - $todoid = required_param('todoid', PARAM_INT); - $outcome->response->iscompleted = $state->todo_toggle($todoid); - break; - default: - throw new block_workflow_ajax_exception('unknowajaxaction'); -} - -echo json_encode($outcome); diff --git a/amd/build/comments.min.js b/amd/build/comments.min.js new file mode 100644 index 0000000..23db647 --- /dev/null +++ b/amd/build/comments.min.js @@ -0,0 +1,11 @@ +define("block_workflow/comments",["exports","core/modal_factory","core/str","core/notification","block_workflow/todolist","core/fragment","core/templates","core/ajax","core/pending","core/loadingicon","core_form/events"],(function(_exports,_modal_factory,Str,_notification,_todolist,_fragment,_templates,_ajax,_pending,_loadingicon,FormEvents){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initComments=void 0,_modal_factory=_interopRequireDefault(_modal_factory),Str=_interopRequireWildcard(Str),_notification=_interopRequireDefault(_notification),_fragment=_interopRequireDefault(_fragment),_templates=_interopRequireDefault(_templates),_pending=_interopRequireDefault(_pending),FormEvents=_interopRequireWildcard(FormEvents); +/** + * JavaScript to handle comment. + * + * @module block_workflow/comments + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class Comment{constructor(options){var obj,key,value;value={BLOCKWORKFLOW:"block_workflow",BLOCKCOMMENTS:"block_workflow_comments",BLOCKCOMMBTN:"block_workflow_editcommentbutton",BLOCKFINISHBTN:"block_workflow_finishstepbutton",PANEL:"block-workflow-panel",CONTENT:"content",LIGHTBOX:"loading-lightbox",SUBMIT:"wfk-submit"},(key="CSS")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.editorid=options.editorid,this.editorname=options.editorname,this.stateid=options.stateid,this.contextid=options.contextid}async buildCommentModal(){return _modal_factory.default.create({type:_modal_factory.default.types.DEFAULT})}async initializer(){this.modal=await this.buildCommentModal(),this.modal.attachToDOM(),this.modal.hide(),this.attachEvents()}attachEvents(){const commentButton=document.querySelector("."+this.CSS.BLOCKCOMMBTN+" button, ."+this.CSS.BLOCKCOMMBTN+" input[type=submit]");commentButton&&(commentButton.onclick=event=>this.show(event,!1));const finishButton=document.querySelector("."+this.CSS.BLOCKFINISHBTN+" button, ."+this.CSS.BLOCKFINISHBTN+" input[type=submit]");finishButton&&(finishButton.onclick=event=>this.show(event,!0))}async show(event,finishStep){event.preventDefault();const[editCommentString,saveChangesString,finishString]=await Str.get_strings([{key:"editcomments",component:"block_workflow"},{key:"savechanges",component:"moodle"},{key:"finishstep",component:"block_workflow"}]);let inputLabel;finishStep?(this.modal.setTitle(finishString),inputLabel=finishString):(inputLabel=saveChangesString,this.modal.setTitle(editCommentString));const fragment=_fragment.default.loadFragment("block_workflow","commentform",this.contextid,{inputLabel:inputLabel}),pendingModalReady=new _pending.default("block_workflow/actions:show");fragment.then(function(html,js){this.modal.getBody()[0].innerHTML=html,_templates.default.runTemplateJS(js);const body=this.modal.getBody()[0];(0,_loadingicon.addIconToContainerRemoveOnCompletion)(this.modal.getBody()[0].querySelector("."+this.CSS.PANEL),pendingModalReady),this.modal.getRoot()[0].querySelector(".modal-dialog").style.cssText="width: fit-content; max-width: 1280px;",this.modal.getRoot()[0].querySelector(".modal-dialog").style.width="fit-content",this.modal.show(),body.querySelector(".".concat(this.CSS.PANEL," .col-md-3")).classList.add("hidden","d-none");const submitButton=body.querySelector('button[name="'.concat(this.CSS.SUBMIT,'"]'));submitButton.classList.add("ml-auto","mr-0"),submitButton.onclick=finishStep?this.finishStep.bind(this):this.save.bind(this);const editorId=this.editorid;this.getStepStateComment(this.stateid).then((function(result){const editor=document.getElementById(editorId+"editable");editor&&(editor.innerHTML=result.response),setTimeout((function(){window.tinyMCE&&window.tinyMCE.activeEditor&&(window.tinyMCE.activeEditor.setContent(result.response),window.tinyMCE.activeEditor.save())})),document.getElementById(editorId).value=result.response,pendingModalReady.resolve()})).catch(_notification.default.exception)}.bind(this))}getStepStateComment(stateid){return(0,_ajax.call)([{methodname:"block_workflow_get_step_state_comment",args:{stateid:stateid}}])[0]}finishStep(){const comment=document.getElementById(this.editorid).value,worklowBlock=document.querySelector("."+this.CSS.BLOCKWORKFLOW+" ."+this.CSS.CONTENT),modal=this.modal,commentCls=this;this.updateFinishStep(this.stateid,comment,document.getElementsByName(this.editorname+"[format]")[0].value).then(function(result){if(result.response){if(worklowBlock.innerHTML=result.response,result.stateid){commentCls.attachEvents();const todo=new _todolist.TodoList({stateid:result.stateid});commentCls.stateid=result.stateid,todo.initializer()}if(result.listworkflows){const selectId=worklowBlock.querySelector(".singleselect form select").getAttribute("id");document.getElementById(selectId).onchange=function(){void 0===this.selectedOptions[0].dataset.ignore&&this.closest("form").submit()}}}modal.hide(),this.resetAutoSaveData()}.bind(this)).catch(_notification.default.exception)}updateFinishStep(stateid,comment,format){return(0,_ajax.call)([{methodname:"block_workflow_finish_step",args:{stateid:this.stateid,text:comment,format:format}}])[0]}async save(){const comment=document.getElementById(this.editorid).value,commentsBlock=document.querySelector("."+this.CSS.BLOCKWORKFLOW+" ."+this.CSS.BLOCKCOMMENTS),noCommentString=await Str.get_string("nocomments","block_workflow"),modal=this.modal;this.updateStepStateComment(this.stateid,comment,document.getElementsByName(this.editorname+"[format]")[0].value).then(function(result){result.response?commentsBlock.innerHTML=result.response:commentsBlock.innerText=noCommentString,modal.hide(),this.resetAutoSaveData()}.bind(this)).catch(_notification.default.exception)}updateStepStateComment(stateid,comment,format){return(0,_ajax.call)([{methodname:"block_workflow_update_step_state_comment",args:{stateid:this.stateid,newcomment:comment,newcommentformat:format}}])[0]}resetAutoSaveData(){void 0!==window.tinyMCE&&window.tinyMCE.activeEditor&&FormEvents.notifyFormSubmittedByJavascript(window.tinyMCE.activeEditor.formElement)}}_exports.initComments=options=>{new Comment(options).initializer()}})); + +//# sourceMappingURL=comments.min.js.map \ No newline at end of file diff --git a/amd/build/comments.min.js.map b/amd/build/comments.min.js.map new file mode 100644 index 0000000..7e851db --- /dev/null +++ b/amd/build/comments.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"comments.min.js","sources":["../src/comments.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport ModalFactory from 'core/modal_factory';\nimport * as Str from 'core/str';\nimport Notification from 'core/notification';\nimport {TodoList} from 'block_workflow/todolist';\nimport Fragment from 'core/fragment';\nimport Templates from 'core/templates';\nimport {call as fetchMany} from 'core/ajax';\nimport Pending from 'core/pending';\nimport {addIconToContainerRemoveOnCompletion} from 'core/loadingicon';\nimport * as FormEvents from 'core_form/events';\n\n/**\n * JavaScript to handle comment.\n *\n * @module block_workflow/comments\n * @copyright 2023 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nclass Comment {\n\n CSS = {\n BLOCKWORKFLOW: 'block_workflow',\n BLOCKCOMMENTS: 'block_workflow_comments',\n BLOCKCOMMBTN: 'block_workflow_editcommentbutton',\n BLOCKFINISHBTN: 'block_workflow_finishstepbutton',\n PANEL: 'block-workflow-panel',\n CONTENT: 'content',\n LIGHTBOX: 'loading-lightbox',\n SUBMIT: 'wfk-submit',\n };\n\n constructor(options) {\n this.editorid = options.editorid;\n this.editorname = options.editorname;\n this.stateid = options.stateid;\n this.contextid = options.contextid;\n }\n\n /**\n * Method that creates the comment modal.\n *\n * @returns {Promise} The modal promise (modal's body will be rendered later).\n */\n async buildCommentModal() {\n return ModalFactory.create({type: ModalFactory.types.DEFAULT});\n }\n\n /**\n * Initial function.\n */\n async initializer() {\n // Setup and attach the modal to DOM.\n this.modal = await this.buildCommentModal();\n this.modal.attachToDOM();\n this.modal.hide();\n this.attachEvents();\n }\n\n /**\n * Attach events to buttons.\n */\n attachEvents() {\n // Attach event for comment button.\n const commentButton = document.querySelector('.' + this.CSS.BLOCKCOMMBTN + ' button, .' +\n this.CSS.BLOCKCOMMBTN + ' input[type=submit]');\n if (commentButton) {\n commentButton.onclick = event => this.show(event, false);\n }\n // Attach event for finish step button.\n const finishButton = document.querySelector('.' + this.CSS.BLOCKFINISHBTN + ' button, .' +\n this.CSS.BLOCKFINISHBTN + ' input[type=submit]');\n if (finishButton) {\n finishButton.onclick = event => this.show(event, true);\n }\n }\n\n /**\n * Show comment action.\n *\n * @param {Object} event Event object.\n * @param {Boolean} finishStep Finish step flag.\n */\n async show(event, finishStep) {\n event.preventDefault();\n const [editCommentString, saveChangesString, finishString] = await Str.get_strings([\n {key: 'editcomments', component: 'block_workflow'},\n {key: 'savechanges', component: 'moodle'},\n {key: 'finishstep', component: 'block_workflow'},\n ]);\n\n let inputLabel;\n\n if (finishStep) {\n this.modal.setTitle(finishString);\n inputLabel = finishString;\n } else {\n inputLabel = saveChangesString;\n this.modal.setTitle(editCommentString);\n }\n\n const fragment = Fragment.loadFragment('block_workflow', 'commentform', this.contextid, {inputLabel});\n const pendingModalReady = new Pending('block_workflow/actions:show');\n fragment.then(function(html, js) {\n this.modal.getBody()[0].innerHTML = html;\n Templates.runTemplateJS(js);\n const body = this.modal.getBody()[0];\n addIconToContainerRemoveOnCompletion(\n this.modal.getBody()[0].querySelector('.' + this.CSS.PANEL), pendingModalReady\n );\n this.modal.getRoot()[0].querySelector('.modal-dialog').style.cssText = 'width: fit-content; max-width: 1280px;';\n this.modal.getRoot()[0].querySelector('.modal-dialog').style.width = 'fit-content';\n this.modal.show();\n body.querySelector(`.${this.CSS.PANEL} .col-md-3`).classList.add('hidden', 'd-none');\n const submitButton = body.querySelector(`button[name=\"${this.CSS.SUBMIT}\"]`);\n // Change position of the button.\n submitButton.classList.add('ml-auto', 'mr-0');\n if (finishStep) {\n submitButton.onclick = this.finishStep.bind(this);\n } else {\n submitButton.onclick = this.save.bind(this);\n }\n const editorId = this.editorid;\n // Fetch the comment and update the form\n this.getStepStateComment(this.stateid).then(function(result) {\n const editor = document.getElementById(editorId + 'editable');\n // Atto specific.\n if (editor) {\n editor.innerHTML = result.response;\n }\n // Tiny specific.\n // To make sure Tiny MCE has already loaded.\n setTimeout(function() {\n if (window.tinyMCE && window.tinyMCE.activeEditor) {\n window.tinyMCE.activeEditor.setContent(result.response);\n window.tinyMCE.activeEditor.save();\n }\n });\n document.getElementById(editorId).value = result.response;\n pendingModalReady.resolve();\n }).catch(Notification.exception);\n }.bind(this));\n }\n\n /**\n * Get the current comment of the step_state.\n *\n * @param {Number} stateid\n */\n getStepStateComment(stateid) {\n return fetchMany([{\n methodname: 'block_workflow_get_step_state_comment',\n args: {\n stateid: stateid,\n },\n }])[0];\n }\n\n /**\n * Handle finish step.\n */\n finishStep() {\n const comment = document.getElementById(this.editorid).value;\n const worklowBlock = document.querySelector('.' + this.CSS.BLOCKWORKFLOW + ' .' + this.CSS.CONTENT);\n const modal = this.modal;\n const commentCls = this;\n this.updateFinishStep(this.stateid, comment, document.getElementsByName(this.editorname + '[format]')[0].value)\n .then(function(result) {\n if (result.response) {\n // Update content\n worklowBlock.innerHTML = result.response;\n if (result.stateid) {\n // Re-attach events to block buttons\n commentCls.attachEvents();\n // Reinit to-do events\n const todo = new TodoList({stateid: result.stateid});\n commentCls.stateid = result.stateid;\n // We are on the next step\n todo.initializer();\n }\n if (result.listworkflows) {\n // Last step, available workflows are listed\n const selectId = worklowBlock.querySelector('.singleselect form select').getAttribute('id');\n // Reinit single_select event\n // This is horrible, but the core JS we need is now inline in the template,\n // so we have to copy it.\n document.getElementById(selectId).onchange = function() {\n if (this.selectedOptions[0].dataset.ignore === undefined) {\n this.closest('form').submit();\n }\n };\n }\n }\n modal.hide();\n this.resetAutoSaveData();\n }.bind(this)).catch(Notification.exception);\n }\n\n /**\n * Finish the current step_state.\n *\n * @param {Number} stateid stateid id of the current step_state.\n * @param {String} comment new comment of the finish step.\n * @param {Number} format format of the editor.\n */\n updateFinishStep(stateid, comment, format) {\n return fetchMany([{\n methodname: 'block_workflow_finish_step',\n args: {\n stateid: this.stateid,\n text: comment,\n format: format\n },\n }])[0];\n }\n\n /**\n * Handle save action.\n */\n async save() {\n const comment = document.getElementById(this.editorid).value;\n const commentsBlock = document.querySelector('.' + this.CSS.BLOCKWORKFLOW + ' .' + this.CSS.BLOCKCOMMENTS);\n const noCommentString = await Str.get_string('nocomments', 'block_workflow');\n const modal = this.modal;\n this.updateStepStateComment(this.stateid, comment, document.getElementsByName(this.editorname + '[format]')[0].value)\n .then(function(result) {\n if (result.response) {\n commentsBlock.innerHTML = result.response;\n } else {\n commentsBlock.innerText = noCommentString;\n }\n modal.hide();\n this.resetAutoSaveData();\n }.bind(this)).catch(Notification.exception);\n }\n\n /**\n * Update the comment of the step_state.\n *\n * @param {Number} stateid id of the current step_state.\n * @param {String} comment new comment of\n * @param {Number} format format of the editor.\n */\n updateStepStateComment(stateid, comment, format) {\n return fetchMany([{\n methodname: 'block_workflow_update_step_state_comment',\n args: {\n stateid: this.stateid,\n newcomment: comment,\n newcommentformat: format\n },\n }])[0];\n }\n\n /**\n * Reset auto-save data.\n */\n resetAutoSaveData() {\n if (window.tinyMCE !== undefined && window.tinyMCE.activeEditor) {\n FormEvents.notifyFormSubmittedByJavascript(window.tinyMCE.activeEditor.formElement);\n }\n }\n}\n\n/**\n * Handle action with comments.\n *\n * @param {Object} options The comment settings.\n */\nexport const initComments = (options) => {\n const comment = new Comment(options);\n comment.initializer();\n};\n"],"names":["Comment","constructor","options","BLOCKWORKFLOW","BLOCKCOMMENTS","BLOCKCOMMBTN","BLOCKFINISHBTN","PANEL","CONTENT","LIGHTBOX","SUBMIT","editorid","editorname","stateid","contextid","ModalFactory","create","type","types","DEFAULT","modal","this","buildCommentModal","attachToDOM","hide","attachEvents","commentButton","document","querySelector","CSS","onclick","event","show","finishButton","finishStep","preventDefault","editCommentString","saveChangesString","finishString","Str","get_strings","key","component","inputLabel","setTitle","fragment","Fragment","loadFragment","pendingModalReady","Pending","then","html","js","getBody","innerHTML","runTemplateJS","body","getRoot","style","cssText","width","classList","add","submitButton","bind","save","editorId","getStepStateComment","result","editor","getElementById","response","setTimeout","window","tinyMCE","activeEditor","setContent","value","resolve","catch","Notification","exception","methodname","args","comment","worklowBlock","commentCls","updateFinishStep","getElementsByName","todo","TodoList","initializer","listworkflows","selectId","getAttribute","onchange","undefined","selectedOptions","dataset","ignore","closest","submit","resetAutoSaveData","format","text","commentsBlock","noCommentString","get_string","updateStepStateComment","innerText","newcomment","newcommentformat","FormEvents","notifyFormSubmittedByJavascript","formElement"],"mappings":";;;;;;;;MAiCMA,QAaFC,YAAYC,iCAXN,CACFC,cAAgB,iBAChBC,cAAgB,0BAChBC,aAAgB,mCAChBC,eAAgB,kCAChBC,MAAgB,uBAChBC,QAAgB,UAChBC,SAAgB,mBAChBC,OAAgB,iJAIXC,SAAWT,QAAQS,cACnBC,WAAaV,QAAQU,gBACrBC,QAAUX,QAAQW,aAClBC,UAAYZ,QAAQY,2CASlBC,uBAAaC,OAAO,CAACC,KAAMF,uBAAaG,MAAMC,mCAQhDC,YAAcC,KAAKC,yBACnBF,MAAMG,mBACNH,MAAMI,YACNC,eAMTA,qBAEUC,cAAgBC,SAASC,cAAc,IAAMP,KAAKQ,IAAIxB,aAAe,aACvEgB,KAAKQ,IAAIxB,aAAe,uBACxBqB,gBACAA,cAAcI,QAAUC,OAASV,KAAKW,KAAKD,OAAO,UAGhDE,aAAeN,SAASC,cAAc,IAAMP,KAAKQ,IAAIvB,eAAiB,aACxEe,KAAKQ,IAAIvB,eAAiB,uBAC1B2B,eACAA,aAAaH,QAAUC,OAASV,KAAKW,KAAKD,OAAO,eAU9CA,MAAOG,YACdH,MAAMI,uBACCC,kBAAmBC,kBAAmBC,oBAAsBC,IAAIC,YAAY,CAC/E,CAACC,IAAK,eAAgBC,UAAW,kBACjC,CAACD,IAAK,cAAeC,UAAW,UAChC,CAACD,IAAK,aAAcC,UAAW,wBAG/BC,WAEAT,iBACKd,MAAMwB,SAASN,cACpBK,WAAaL,eAEbK,WAAaN,uBACRjB,MAAMwB,SAASR,0BAGlBS,SAAWC,kBAASC,aAAa,iBAAkB,cAAe1B,KAAKP,UAAW,CAAC6B,WAAAA,aACnFK,kBAAoB,IAAIC,iBAAQ,+BACtCJ,SAASK,KAAK,SAASC,KAAMC,SACpBhC,MAAMiC,UAAU,GAAGC,UAAYH,wBAC1BI,cAAcH,UAClBI,KAAOnC,KAAKD,MAAMiC,UAAU,yDAE9BhC,KAAKD,MAAMiC,UAAU,GAAGzB,cAAc,IAAMP,KAAKQ,IAAItB,OAAQyC,wBAE5D5B,MAAMqC,UAAU,GAAG7B,cAAc,iBAAiB8B,MAAMC,QAAU,8CAClEvC,MAAMqC,UAAU,GAAG7B,cAAc,iBAAiB8B,MAAME,MAAQ,mBAChExC,MAAMY,OACXwB,KAAK5B,yBAAkBP,KAAKQ,IAAItB,qBAAmBsD,UAAUC,IAAI,SAAU,gBACrEC,aAAeP,KAAK5B,qCAA8BP,KAAKQ,IAAInB,cAEjEqD,aAAaF,UAAUC,IAAI,UAAW,QAElCC,aAAajC,QADbI,WACuBb,KAAKa,WAAW8B,KAAK3C,MAErBA,KAAK4C,KAAKD,KAAK3C,YAEpC6C,SAAW7C,KAAKV,cAEjBwD,oBAAoB9C,KAAKR,SAASqC,MAAK,SAASkB,cAC3CC,OAAS1C,SAAS2C,eAAeJ,SAAW,YAE9CG,SACAA,OAAOf,UAAYc,OAAOG,UAI9BC,YAAW,WACHC,OAAOC,SAAWD,OAAOC,QAAQC,eACjCF,OAAOC,QAAQC,aAAaC,WAAWR,OAAOG,UAC9CE,OAAOC,QAAQC,aAAaV,WAGpCtC,SAAS2C,eAAeJ,UAAUW,MAAQT,OAAOG,SACjDvB,kBAAkB8B,aACnBC,MAAMC,sBAAaC,YACxBjB,KAAK3C,OAQX8C,oBAAoBtD,gBACT,cAAU,CAAC,CACdqE,WAAY,wCACZC,KAAM,CACFtE,QAASA,YAEb,GAMRqB,mBACUkD,QAAUzD,SAAS2C,eAAejD,KAAKV,UAAUkE,MACjDQ,aAAe1D,SAASC,cAAc,IAAMP,KAAKQ,IAAI1B,cAAgB,KAAOkB,KAAKQ,IAAIrB,SACrFY,MAAQC,KAAKD,MACbkE,WAAajE,UACdkE,iBAAiBlE,KAAKR,QAASuE,QAASzD,SAAS6D,kBAAkBnE,KAAKT,WAAa,YAAY,GAAGiE,OACpG3B,KAAK,SAASkB,WACPA,OAAOG,SAAU,IAEjBc,aAAa/B,UAAYc,OAAOG,SAC5BH,OAAOvD,QAAS,CAEhByE,WAAW7D,qBAELgE,KAAO,IAAIC,mBAAS,CAAC7E,QAASuD,OAAOvD,UAC3CyE,WAAWzE,QAAUuD,OAAOvD,QAE5B4E,KAAKE,iBAELvB,OAAOwB,cAAe,OAEhBC,SAAWR,aAAazD,cAAc,6BAA6BkE,aAAa,MAItFnE,SAAS2C,eAAeuB,UAAUE,SAAW,gBACMC,IAA3C3E,KAAK4E,gBAAgB,GAAGC,QAAQC,aAC3BC,QAAQ,QAAQC,WAKrCjF,MAAMI,YACD8E,qBACPtC,KAAK3C,OAAO0D,MAAMC,sBAAaC,WAUzCM,iBAAiB1E,QAASuE,QAASmB,eACxB,cAAU,CAAC,CACdrB,WAAY,6BACZC,KAAM,CACFtE,QAASQ,KAAKR,QACd2F,KAAMpB,QACNmB,OAAQA,WAEZ,sBAOEnB,QAAUzD,SAAS2C,eAAejD,KAAKV,UAAUkE,MACjD4B,cAAgB9E,SAASC,cAAc,IAAMP,KAAKQ,IAAI1B,cAAgB,KAAOkB,KAAKQ,IAAIzB,eACtFsG,sBAAwBnE,IAAIoE,WAAW,aAAc,kBACrDvF,MAAQC,KAAKD,WACdwF,uBAAuBvF,KAAKR,QAASuE,QAASzD,SAAS6D,kBAAkBnE,KAAKT,WAAa,YAAY,GAAGiE,OAC1G3B,KAAK,SAASkB,QACPA,OAAOG,SACPkC,cAAcnD,UAAYc,OAAOG,SAEjCkC,cAAcI,UAAYH,gBAE9BtF,MAAMI,YACD8E,qBACPtC,KAAK3C,OAAO0D,MAAMC,sBAAaC,WAUzC2B,uBAAuB/F,QAASuE,QAASmB,eAC9B,cAAU,CAAC,CACdrB,WAAY,2CACZC,KAAM,CACFtE,QAASQ,KAAKR,QACdiG,WAAY1B,QACZ2B,iBAAkBR,WAEtB,GAMRD,yBAC2BN,IAAnBvB,OAAOC,SAAyBD,OAAOC,QAAQC,cAC/CqC,WAAWC,gCAAgCxC,OAAOC,QAAQC,aAAauC,oCAUtDhH,UACT,IAAIF,QAAQE,SACpByF"} \ No newline at end of file diff --git a/amd/build/todolist.min.js b/amd/build/todolist.min.js new file mode 100644 index 0000000..4145682 --- /dev/null +++ b/amd/build/todolist.min.js @@ -0,0 +1,10 @@ +define("block_workflow/todolist",["exports","core/notification","core/ajax"],(function(_exports,_notification,_ajax){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj} +/** + * JavaScript for the workflow to-do list. + * + * @module block_workflow/todolist + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initTodolist=_exports.TodoList=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};class TodoList{constructor(options){_defineProperty(this,"TODOLISTNAME","blocks_workflow_todolist"),_defineProperty(this,"STATEID","stateid"),_defineProperty(this,"CSS",{BLOCKTODOTASK:"block-workflow-todotask",BLOCKTODOID:"block-workflow-todoid"}),this.stateid=options.stateid}initializer(){document.querySelectorAll("a."+this.CSS.BLOCKTODOTASK).forEach((node=>{node.closest("li").onclick=event=>this.toggle(event,node),node.setAttribute("href","#")}))}toggle(event,node){event.preventDefault();const reg=new RegExp(this.CSS.BLOCKTODOID+"-(\\d{1,})"),check=!node.parentNode.classList.contains("completed");this.updateStepStateTaskState(this.stateid,node.getAttribute("id").match(reg)[1],check).then((function(result){result.error?_notification.default.exception(result.error):result.response?node.parentNode.classList.add("completed"):node.parentNode.classList.remove("completed")})).catch(_notification.default.exception)}updateStepStateTaskState(stateid,todoid,check){return(0,_ajax.call)([{methodname:"block_workflow_update_step_state_task_state",args:{stateid:stateid,todoid:todoid,check:check}}])[0]}}_exports.TodoList=TodoList;_exports.initTodolist=options=>{new TodoList(options).initializer()}})); + +//# sourceMappingURL=todolist.min.js.map \ No newline at end of file diff --git a/amd/build/todolist.min.js.map b/amd/build/todolist.min.js.map new file mode 100644 index 0000000..d228e3a --- /dev/null +++ b/amd/build/todolist.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"todolist.min.js","sources":["../src/todolist.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport Notification from 'core/notification';\nimport {call as fetchMany} from 'core/ajax';\n\n/**\n * JavaScript for the workflow to-do list.\n *\n * @module block_workflow/todolist\n * @copyright 2023 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport class TodoList {\n TODOLISTNAME = 'blocks_workflow_todolist';\n STATEID = 'stateid';\n CSS = {\n BLOCKTODOTASK: 'block-workflow-todotask',\n BLOCKTODOID: 'block-workflow-todoid'\n };\n\n constructor(options) {\n this.stateid = options.stateid;\n }\n\n /**\n * Initial function.\n */\n initializer() {\n // Take each of the workflow tasks, remove the anchor, and change it to\n // call our update function\n document.querySelectorAll('a.' + this.CSS.BLOCKTODOTASK).forEach(node => {\n node.closest('li').onclick = event => this.toggle(event, node);\n node.setAttribute('href', '#');\n });\n }\n\n /**\n * Toggle checkbox.\n *\n * @param {Object} event The event object.\n * @param {Object} node HTML node object.\n */\n toggle(event, node) {\n event.preventDefault();\n // Expression to fetch ID\n const reg = new RegExp(this.CSS.BLOCKTODOID + \"-(\\\\d{1,})\");\n // We don't have a real checkbox, it is just image with different class. So we will check the status base on class.\n // When we click to the link with completed class, meaning we want to uncheck so the status should be false.\n // When we click to the link without the completed class, meaning we want to check so the status should be true.\n const check = !node.parentNode.classList.contains('completed');\n this.updateStepStateTaskState(this.stateid, node.getAttribute('id').match(reg)[1], check)\n .then(function(result) {\n if (result.error) {\n Notification.exception(result.error);\n return;\n }\n if (result.response) {\n node.parentNode.classList.add('completed');\n } else {\n node.parentNode.classList.remove('completed');\n }\n }).catch(Notification.exception);\n }\n\n /**\n * Update step_state to to\n *\n * @param {Number} stateid id of the current step_state\n * @param {Number} todoid id of the current to do.\n * @param {Boolean} check whether the current to is has been checked/uncheck.\n */\n updateStepStateTaskState(stateid, todoid, check) {\n return fetchMany([{\n methodname: 'block_workflow_update_step_state_task_state',\n args: {\n stateid: stateid,\n todoid: todoid,\n check: check\n },\n }])[0];\n }\n}\n\n/**\n * Handle to-do list action.\n *\n * @param {Object} options The settings.\n */\nexport const initTodolist = (options) => {\n const todo = new TodoList(options);\n todo.initializer();\n};\n"],"names":["TodoList","constructor","options","BLOCKTODOTASK","BLOCKTODOID","stateid","initializer","document","querySelectorAll","this","CSS","forEach","node","closest","onclick","event","toggle","setAttribute","preventDefault","reg","RegExp","check","parentNode","classList","contains","updateStepStateTaskState","getAttribute","match","then","result","error","exception","response","add","remove","catch","Notification","todoid","methodname","args"],"mappings":";;;;;;;sLAyBaA,SAQTC,YAAYC,6CAPG,2DACL,sCACJ,CACFC,cAAe,0BACfC,YAAa,+BAIRC,QAAUH,QAAQG,QAM3BC,cAGIC,SAASC,iBAAiB,KAAOC,KAAKC,IAAIP,eAAeQ,SAAQC,OAC7DA,KAAKC,QAAQ,MAAMC,QAAUC,OAASN,KAAKO,OAAOD,MAAOH,MACzDA,KAAKK,aAAa,OAAQ,QAUlCD,OAAOD,MAAOH,MACVG,MAAMG,uBAEAC,IAAM,IAAIC,OAAOX,KAAKC,IAAIN,YAAc,cAIxCiB,OAAST,KAAKU,WAAWC,UAAUC,SAAS,kBAC7CC,yBAAyBhB,KAAKJ,QAASO,KAAKc,aAAa,MAAMC,MAAMR,KAAK,GAAIE,OAClFO,MAAK,SAASC,QACPA,OAAOC,4BACMC,UAAUF,OAAOC,OAG9BD,OAAOG,SACPpB,KAAKU,WAAWC,UAAUU,IAAI,aAE9BrB,KAAKU,WAAWC,UAAUW,OAAO,gBAEtCC,MAAMC,sBAAaL,WAU1BN,yBAAyBpB,QAASgC,OAAQhB,cAC/B,cAAU,CAAC,CACdiB,WAAY,8CACZC,KAAM,CACFlC,QAASA,QACTgC,OAAQA,OACRhB,MAAOA,UAEX,qDASiBnB,UACZ,IAAIF,SAASE,SACrBI"} \ No newline at end of file diff --git a/amd/build/userinfo.min.js b/amd/build/userinfo.min.js new file mode 100644 index 0000000..a1bd0aa --- /dev/null +++ b/amd/build/userinfo.min.js @@ -0,0 +1,10 @@ +define("block_workflow/userinfo",["exports","core/modal_factory"],(function(_exports,_modal_factory){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj} +/** + * User info functionality for a popup in workflow block. + * + * @module block_workflow/userinfo + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_factory=(obj=_modal_factory)&&obj.__esModule?obj:{default:obj};class Popup{constructor(){_defineProperty(this,"CSS",{USERINFOCLASS:".userinfoclass"}),_defineProperty(this,"PARAMS",{id:"id",HEADER:"header",BODY:"body",STEPNO:"stepno"})}initializer(){document.querySelectorAll(this.CSS.USERINFOCLASS).forEach((node=>{const header=node.getAttribute(this.PARAMS.HEADER),body=node.getAttribute(this.PARAMS.BODY);node.onclick=event=>this.displayDialog(event,header,body)}))}async displayDialog(e,header,body){e.preventDefault();const modal=await _modal_factory.default.create({title:header,body:body,large:!0});modal.attachToDOM(),modal.getRoot()[0].querySelector(".modal-dialog").style.width="fit-content",modal.show()}}_exports.init=()=>{(new Popup).initializer()}})); + +//# sourceMappingURL=userinfo.min.js.map \ No newline at end of file diff --git a/amd/build/userinfo.min.js.map b/amd/build/userinfo.min.js.map new file mode 100644 index 0000000..0641531 --- /dev/null +++ b/amd/build/userinfo.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userinfo.min.js","sources":["../src/userinfo.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport ModalFactory from 'core/modal_factory';\n\n/**\n * User info functionality for a popup in workflow block.\n *\n * @module block_workflow/userinfo\n * @copyright 2023 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nclass Popup {\n CSS = {\n USERINFOCLASS: '.userinfoclass'\n };\n\n PARAMS = {\n id: 'id',\n HEADER: 'header',\n BODY: 'body',\n STEPNO: 'stepno'\n };\n\n /**\n * Initial function.\n */\n initializer() {\n document.querySelectorAll(this.CSS.USERINFOCLASS).forEach(node => {\n const header = node.getAttribute(this.PARAMS.HEADER);\n const body = node.getAttribute(this.PARAMS.BODY);\n node.onclick = event => this.displayDialog(event, header, body);\n });\n }\n\n /**\n * Handle display modal.\n *\n * @param {Object} e The event object.\n * @param {String} header Title string.\n * @param {String} body Body data.\n */\n async displayDialog(e, header, body) {\n e.preventDefault();\n const modal = await ModalFactory.create({\n title: header,\n body: body,\n large: true,\n });\n modal.attachToDOM();\n modal.getRoot()[0].querySelector('.modal-dialog').style.width = 'fit-content';\n modal.show();\n }\n}\n\n/**\n * Handle userinfo action.\n */\nexport const init = () => {\n const popup = new Popup();\n popup.initializer();\n};\n"],"names":["Popup","USERINFOCLASS","id","HEADER","BODY","STEPNO","initializer","document","querySelectorAll","this","CSS","forEach","node","header","getAttribute","PARAMS","body","onclick","event","displayDialog","e","preventDefault","modal","ModalFactory","create","title","large","attachToDOM","getRoot","querySelector","style","width","show"],"mappings":";;;;;;;8JAwBMA,+CACI,CACFC,cAAe,iDAGV,CACLC,GAAI,KACJC,OAAQ,SACRC,KAAM,OACNC,OAAQ,WAMZC,cACIC,SAASC,iBAAiBC,KAAKC,IAAIT,eAAeU,SAAQC,aAChDC,OAASD,KAAKE,aAAaL,KAAKM,OAAOZ,QACvCa,KAAOJ,KAAKE,aAAaL,KAAKM,OAAOX,MAC3CQ,KAAKK,QAAUC,OAAST,KAAKU,cAAcD,MAAOL,OAAQG,6BAW9CI,EAAGP,OAAQG,MAC3BI,EAAEC,uBACIC,YAAcC,uBAAaC,OAAO,CACpCC,MAAOZ,OACPG,KAAMA,KACNU,OAAO,IAEXJ,MAAMK,cACNL,MAAMM,UAAU,GAAGC,cAAc,iBAAiBC,MAAMC,MAAQ,cAChET,MAAMU,sBAOM,MACF,IAAIhC,OACZM"} \ No newline at end of file diff --git a/amd/src/comments.js b/amd/src/comments.js new file mode 100644 index 0000000..9b45b98 --- /dev/null +++ b/amd/src/comments.js @@ -0,0 +1,287 @@ +// 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 . + +import ModalFactory from 'core/modal_factory'; +import * as Str from 'core/str'; +import Notification from 'core/notification'; +import {TodoList} from 'block_workflow/todolist'; +import Fragment from 'core/fragment'; +import Templates from 'core/templates'; +import {call as fetchMany} from 'core/ajax'; +import Pending from 'core/pending'; +import {addIconToContainerRemoveOnCompletion} from 'core/loadingicon'; +import * as FormEvents from 'core_form/events'; + +/** + * JavaScript to handle comment. + * + * @module block_workflow/comments + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class Comment { + + CSS = { + BLOCKWORKFLOW: 'block_workflow', + BLOCKCOMMENTS: 'block_workflow_comments', + BLOCKCOMMBTN: 'block_workflow_editcommentbutton', + BLOCKFINISHBTN: 'block_workflow_finishstepbutton', + PANEL: 'block-workflow-panel', + CONTENT: 'content', + LIGHTBOX: 'loading-lightbox', + SUBMIT: 'wfk-submit', + }; + + constructor(options) { + this.editorid = options.editorid; + this.editorname = options.editorname; + this.stateid = options.stateid; + this.contextid = options.contextid; + } + + /** + * Method that creates the comment modal. + * + * @returns {Promise} The modal promise (modal's body will be rendered later). + */ + async buildCommentModal() { + return ModalFactory.create({type: ModalFactory.types.DEFAULT}); + } + + /** + * Initial function. + */ + async initializer() { + // Setup and attach the modal to DOM. + this.modal = await this.buildCommentModal(); + this.modal.attachToDOM(); + this.modal.hide(); + this.attachEvents(); + } + + /** + * Attach events to buttons. + */ + attachEvents() { + // Attach event for comment button. + const commentButton = document.querySelector('.' + this.CSS.BLOCKCOMMBTN + ' button, .' + + this.CSS.BLOCKCOMMBTN + ' input[type=submit]'); + if (commentButton) { + commentButton.onclick = event => this.show(event, false); + } + // Attach event for finish step button. + const finishButton = document.querySelector('.' + this.CSS.BLOCKFINISHBTN + ' button, .' + + this.CSS.BLOCKFINISHBTN + ' input[type=submit]'); + if (finishButton) { + finishButton.onclick = event => this.show(event, true); + } + } + + /** + * Show comment action. + * + * @param {Object} event Event object. + * @param {Boolean} finishStep Finish step flag. + */ + async show(event, finishStep) { + event.preventDefault(); + const [editCommentString, saveChangesString, finishString] = await Str.get_strings([ + {key: 'editcomments', component: 'block_workflow'}, + {key: 'savechanges', component: 'moodle'}, + {key: 'finishstep', component: 'block_workflow'}, + ]); + + let inputLabel; + + if (finishStep) { + this.modal.setTitle(finishString); + inputLabel = finishString; + } else { + inputLabel = saveChangesString; + this.modal.setTitle(editCommentString); + } + + const fragment = Fragment.loadFragment('block_workflow', 'commentform', this.contextid, {inputLabel}); + const pendingModalReady = new Pending('block_workflow/actions:show'); + fragment.then(function(html, js) { + this.modal.getBody()[0].innerHTML = html; + Templates.runTemplateJS(js); + const body = this.modal.getBody()[0]; + addIconToContainerRemoveOnCompletion( + this.modal.getBody()[0].querySelector('.' + this.CSS.PANEL), pendingModalReady + ); + this.modal.getRoot()[0].querySelector('.modal-dialog').style.cssText = 'width: fit-content; max-width: 1280px;'; + this.modal.getRoot()[0].querySelector('.modal-dialog').style.width = 'fit-content'; + this.modal.show(); + body.querySelector(`.${this.CSS.PANEL} .col-md-3`).classList.add('hidden', 'd-none'); + const submitButton = body.querySelector(`button[name="${this.CSS.SUBMIT}"]`); + // Change position of the button. + submitButton.classList.add('ml-auto', 'mr-0'); + if (finishStep) { + submitButton.onclick = this.finishStep.bind(this); + } else { + submitButton.onclick = this.save.bind(this); + } + const editorId = this.editorid; + // Fetch the comment and update the form + this.getStepStateComment(this.stateid).then(function(result) { + const editor = document.getElementById(editorId + 'editable'); + // Atto specific. + if (editor) { + editor.innerHTML = result.response; + } + // Tiny specific. + // To make sure Tiny MCE has already loaded. + setTimeout(function() { + if (window.tinyMCE && window.tinyMCE.activeEditor) { + window.tinyMCE.activeEditor.setContent(result.response); + window.tinyMCE.activeEditor.save(); + } + }); + document.getElementById(editorId).value = result.response; + pendingModalReady.resolve(); + }).catch(Notification.exception); + }.bind(this)); + } + + /** + * Get the current comment of the step_state. + * + * @param {Number} stateid + */ + getStepStateComment(stateid) { + return fetchMany([{ + methodname: 'block_workflow_get_step_state_comment', + args: { + stateid: stateid, + }, + }])[0]; + } + + /** + * Handle finish step. + */ + finishStep() { + const comment = document.getElementById(this.editorid).value; + const worklowBlock = document.querySelector('.' + this.CSS.BLOCKWORKFLOW + ' .' + this.CSS.CONTENT); + const modal = this.modal; + const commentCls = this; + this.updateFinishStep(this.stateid, comment, document.getElementsByName(this.editorname + '[format]')[0].value) + .then(function(result) { + if (result.response) { + // Update content + worklowBlock.innerHTML = result.response; + if (result.stateid) { + // Re-attach events to block buttons + commentCls.attachEvents(); + // Reinit to-do events + const todo = new TodoList({stateid: result.stateid}); + commentCls.stateid = result.stateid; + // We are on the next step + todo.initializer(); + } + if (result.listworkflows) { + // Last step, available workflows are listed + const selectId = worklowBlock.querySelector('.singleselect form select').getAttribute('id'); + // Reinit single_select event + // This is horrible, but the core JS we need is now inline in the template, + // so we have to copy it. + document.getElementById(selectId).onchange = function() { + if (this.selectedOptions[0].dataset.ignore === undefined) { + this.closest('form').submit(); + } + }; + } + } + modal.hide(); + this.resetAutoSaveData(); + }.bind(this)).catch(Notification.exception); + } + + /** + * Finish the current step_state. + * + * @param {Number} stateid stateid id of the current step_state. + * @param {String} comment new comment of the finish step. + * @param {Number} format format of the editor. + */ + updateFinishStep(stateid, comment, format) { + return fetchMany([{ + methodname: 'block_workflow_finish_step', + args: { + stateid: this.stateid, + text: comment, + format: format + }, + }])[0]; + } + + /** + * Handle save action. + */ + async save() { + const comment = document.getElementById(this.editorid).value; + const commentsBlock = document.querySelector('.' + this.CSS.BLOCKWORKFLOW + ' .' + this.CSS.BLOCKCOMMENTS); + const noCommentString = await Str.get_string('nocomments', 'block_workflow'); + const modal = this.modal; + this.updateStepStateComment(this.stateid, comment, document.getElementsByName(this.editorname + '[format]')[0].value) + .then(function(result) { + if (result.response) { + commentsBlock.innerHTML = result.response; + } else { + commentsBlock.innerText = noCommentString; + } + modal.hide(); + this.resetAutoSaveData(); + }.bind(this)).catch(Notification.exception); + } + + /** + * Update the comment of the step_state. + * + * @param {Number} stateid id of the current step_state. + * @param {String} comment new comment of + * @param {Number} format format of the editor. + */ + updateStepStateComment(stateid, comment, format) { + return fetchMany([{ + methodname: 'block_workflow_update_step_state_comment', + args: { + stateid: this.stateid, + newcomment: comment, + newcommentformat: format + }, + }])[0]; + } + + /** + * Reset auto-save data. + */ + resetAutoSaveData() { + if (window.tinyMCE !== undefined && window.tinyMCE.activeEditor) { + FormEvents.notifyFormSubmittedByJavascript(window.tinyMCE.activeEditor.formElement); + } + } +} + +/** + * Handle action with comments. + * + * @param {Object} options The comment settings. + */ +export const initComments = (options) => { + const comment = new Comment(options); + comment.initializer(); +}; diff --git a/amd/src/todolist.js b/amd/src/todolist.js new file mode 100644 index 0000000..724ab82 --- /dev/null +++ b/amd/src/todolist.js @@ -0,0 +1,105 @@ +// 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 . + +import Notification from 'core/notification'; +import {call as fetchMany} from 'core/ajax'; + +/** + * JavaScript for the workflow to-do list. + * + * @module block_workflow/todolist + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +export class TodoList { + TODOLISTNAME = 'blocks_workflow_todolist'; + STATEID = 'stateid'; + CSS = { + BLOCKTODOTASK: 'block-workflow-todotask', + BLOCKTODOID: 'block-workflow-todoid' + }; + + constructor(options) { + this.stateid = options.stateid; + } + + /** + * Initial function. + */ + initializer() { + // Take each of the workflow tasks, remove the anchor, and change it to + // call our update function + document.querySelectorAll('a.' + this.CSS.BLOCKTODOTASK).forEach(node => { + node.closest('li').onclick = event => this.toggle(event, node); + node.setAttribute('href', '#'); + }); + } + + /** + * Toggle checkbox. + * + * @param {Object} event The event object. + * @param {Object} node HTML node object. + */ + toggle(event, node) { + event.preventDefault(); + // Expression to fetch ID + const reg = new RegExp(this.CSS.BLOCKTODOID + "-(\\d{1,})"); + // We don't have a real checkbox, it is just image with different class. So we will check the status base on class. + // When we click to the link with completed class, meaning we want to uncheck so the status should be false. + // When we click to the link without the completed class, meaning we want to check so the status should be true. + const check = !node.parentNode.classList.contains('completed'); + this.updateStepStateTaskState(this.stateid, node.getAttribute('id').match(reg)[1], check) + .then(function(result) { + if (result.error) { + Notification.exception(result.error); + return; + } + if (result.response) { + node.parentNode.classList.add('completed'); + } else { + node.parentNode.classList.remove('completed'); + } + }).catch(Notification.exception); + } + + /** + * Update step_state to to + * + * @param {Number} stateid id of the current step_state + * @param {Number} todoid id of the current to do. + * @param {Boolean} check whether the current to is has been checked/uncheck. + */ + updateStepStateTaskState(stateid, todoid, check) { + return fetchMany([{ + methodname: 'block_workflow_update_step_state_task_state', + args: { + stateid: stateid, + todoid: todoid, + check: check + }, + }])[0]; + } +} + +/** + * Handle to-do list action. + * + * @param {Object} options The settings. + */ +export const initTodolist = (options) => { + const todo = new TodoList(options); + todo.initializer(); +}; diff --git a/amd/src/userinfo.js b/amd/src/userinfo.js new file mode 100644 index 0000000..ad146c2 --- /dev/null +++ b/amd/src/userinfo.js @@ -0,0 +1,74 @@ +// 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 . + +import ModalFactory from 'core/modal_factory'; + +/** + * User info functionality for a popup in workflow block. + * + * @module block_workflow/userinfo + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class Popup { + CSS = { + USERINFOCLASS: '.userinfoclass' + }; + + PARAMS = { + id: 'id', + HEADER: 'header', + BODY: 'body', + STEPNO: 'stepno' + }; + + /** + * Initial function. + */ + initializer() { + document.querySelectorAll(this.CSS.USERINFOCLASS).forEach(node => { + const header = node.getAttribute(this.PARAMS.HEADER); + const body = node.getAttribute(this.PARAMS.BODY); + node.onclick = event => this.displayDialog(event, header, body); + }); + } + + /** + * Handle display modal. + * + * @param {Object} e The event object. + * @param {String} header Title string. + * @param {String} body Body data. + */ + async displayDialog(e, header, body) { + e.preventDefault(); + const modal = await ModalFactory.create({ + title: header, + body: body, + large: true, + }); + modal.attachToDOM(); + modal.getRoot()[0].querySelector('.modal-dialog').style.width = 'fit-content'; + modal.show(); + } +} + +/** + * Handle userinfo action. + */ +export const init = () => { + const popup = new Popup(); + popup.initializer(); +}; diff --git a/block_workflow.php b/block_workflow.php index 4627750..e1730bb 100644 --- a/block_workflow.php +++ b/block_workflow.php @@ -28,7 +28,15 @@ require_once($CFG->dirroot . '/lib/form/editor.php'); +/** + * Class block_workflow + */ class block_workflow extends block_base { + /** + * Initializes the block settings and properties. + * + * @return void + */ public function init() { global $CFG; $this->title = get_string('workflow', 'block_workflow'); @@ -81,16 +89,14 @@ public function get_content() { $this->page->requires->strings_for_js(['editcomments', 'nocomments', 'finishstep'], 'block_workflow'); $this->page->requires->strings_for_js(['savechanges'], 'moodle'); - // Initialise the YUI module. - $arguments = array( + $arguments = [ 'stateid' => $state->id, - 'editorid' => 'wkf-comment-editor', + 'editorid' => 'id_wkf-comment-editor', 'editorname' => 'comment_editor', - ); - $this->page->requires->yui_module('moodle-block_workflow-comments', 'M.blocks_workflow.init_comments', - [$arguments]); - $this->page->requires->yui_module('moodle-block_workflow-todolist', 'M.blocks_workflow.init_todolist', - [['stateid' => $state->id]]); + 'contextid' => $this->instance->parentcontextid, + ]; + $this->page->requires->js_call_amd('block_workflow/comments', 'initComments', [$arguments]); + $this->page->requires->js_call_amd('block_workflow/todolist', 'initTodolist', [['stateid' => $state->id]]); // Display the block for this state. $this->content->text = $renderer->block_display($state); @@ -99,7 +105,7 @@ public function get_content() { $previous = $workflows->load_context_workflows($this->instance->parentcontextid); $canadd = has_capability('block/workflow:manage', $this->context); - // If this is a module, retrieve it's name, otherwise try the pagelayout to confirm + // If this is a module, retrieve its name, otherwise try the pagelayout to confirm // that this is a course. if ($this->page->cm) { $appliesto = $this->page->cm->modname; @@ -132,7 +138,7 @@ public function instance_allow_multiple() { * @return array An array of the applicable formats for the block */ public function applicable_formats() { - return array('course' => true, 'mod' => true); + return ['course' => true, 'mod' => true]; } /** diff --git a/changes.md b/changes.md index 3e93c11..09619ca 100644 --- a/changes.md +++ b/changes.md @@ -1,5 +1,25 @@ # Change log for the Workflow block +## Changes in 2.4 +* This version works with Moodle 5.0. +* Automation test failures are fixed. +* Cherry-picked commits since february 2024 till now: + * Fix PHP8 deprecation errors (optional params before required ones) + * Behat: blocks/workflow (tt) + * blocks/workflow: Fix PHP 8.1 issues + * M4.2: fix uses of depreated cron_setup_user + * Workflow error when moving workflow on - and error reporting code is buggy + * YUI->AMD: Rewrite old JavaScript in block_workflow + * Behat: PHP8.2: fix block_/report_workflow failures + * Workflow block: convert use of ajax.php into moodle web services + * Workflow block: convert use of ajax.php into moodle web services + * Workflow/TinyEditor: incorrectly keeps the cached data when comment and finish step + * Moodle 4.5 merge - replace deprecated get_plugin_list + * Moodle 4.5 merge - fix miscellaneous Behat and PHPunit failures + * Theme: Technical debt - remove IE-specific rules + * Workflow: Enable logs to show whether a workflow email is sent +* Upgrade the CI to support Moodle 5.0 (PHP 8.3), and update the branch to support branch MOODLE_405_STABLE, and MOODLE_500_STABLE. + ## Changes in 2.3 * This version works with Moodle 4.0. diff --git a/classes/command.php b/classes/command.php index fd69eb8..d9c8ed1 100644 --- a/classes/command.php +++ b/classes/command.php @@ -104,7 +104,7 @@ public function get_validation_errors($args, $step, $state = null) { public static function role_exists($rolename) { global $DB; - $role = $DB->get_record('role', array('shortname' => strtolower($rolename))); + $role = $DB->get_record('role', ['shortname' => strtolower($rolename)]); return $role; } @@ -157,6 +157,14 @@ public function is_course($workflow) { return ($workflow->appliesto == 'course'); } + /** + * Checks if the next word matches the required word. + * + * @param string $requiredword The word that is expected. + * @param string $actualword The word that is being checked. + * @param mixed $data Additional data used for the check. + * @return bool + */ protected function check_next_word_is($requiredword, $actualword, $data) { if ($actualword != $requiredword) { $data->errors[] = get_string('invalidsyntaxmissingx', 'block_workflow', $requiredword); diff --git a/classes/command_assignrole.php b/classes/command_assignrole.php index 550c641..89b2d4f 100644 --- a/classes/command_assignrole.php +++ b/classes/command_assignrole.php @@ -50,7 +50,7 @@ class block_workflow_command_assignrole extends block_workflow_command { public function parse($args, $step, $state = null) { // We'll return the components in an object. $data = new stdClass(); - $data->errors = array(); + $data->errors = []; if ($state) { $data->context = $state->context(); @@ -72,8 +72,8 @@ public function parse($args, $step, $state = null) { } // Check whether the specified roles exist and fill the list of target users. - $data->roles = array(); - $data->users = array(); + $data->roles = []; + $data->users = []; // Check each role exists, and retrieve the data. foreach ($line as $role) { diff --git a/classes/command_email.php b/classes/command_email.php index aba7068..4ddf6be 100644 --- a/classes/command_email.php +++ b/classes/command_email.php @@ -43,12 +43,12 @@ class block_workflow_command_email extends block_workflow_command { */ public function parse_args($args) { $data = new stdClass(); - $data->errors = array(); + $data->errors = []; // Break down the line. It should be in the format: // email to rolea roleb rolen // with any number of role shortnames. - $line = preg_split('/[\s+]/', $args); + $line = preg_split('/[\s+]/', $args ?? ''); // Grab the email name. $data->emailname = array_shift($line); @@ -106,7 +106,7 @@ public function parse($args, $step, $state = null) { } // Check whether the specified roles exist and fill the list of target users. - $data->users = array(); + $data->users = []; foreach ($data->roles as $role) { $thisrole = parent::require_role_exists($role, $data->errors); if ($data->errors) { @@ -173,7 +173,7 @@ public function execute($args, block_workflow_step_state $state) { */ foreach ($email->users as $user) { $eventdata->userto = $user; - self::message_send($eventdata); + self::message_send($state, $eventdata); } } @@ -186,7 +186,7 @@ public function execute($args, block_workflow_step_state $state) { */ public function email($shortname, &$errors) { global $DB; - $email = $DB->get_record('block_workflow_emails', array('shortname' => $shortname)); + $email = $DB->get_record('block_workflow_emails', ['shortname' => $shortname]); if (!$email) { $errors[] = get_string('invalidemailemail', 'block_workflow', $shortname); return false; @@ -279,7 +279,7 @@ private function email_params($email, $state) { $string = str_replace('%%instructions%%', $instructions, $string); // Replace %%tasks%%. - $tasks = array(); + $tasks = []; foreach ($step->todos() as $todo) { $tasks[] = format_string($todo->task); } @@ -302,7 +302,7 @@ private function email_params($email, $state) { $format = FORMAT_HTML; } $string = str_replace('%%comment%%', format_text($comment, $format, - array('context' => $email->context)), $string); + ['context' => $email->context]), $string); // Re-assign the message. $email->email->message = $string; @@ -319,14 +319,13 @@ private function email_params($email, $state) { * * It is safe to call this function multiple times * - * @access public + * @param block_workflow_step_state $state The step-state with the data * @param object $eventdata The message to send - * @return void */ - public static function message_send($eventdata = null) { - global $DB, $SITE; + public static function message_send(block_workflow_step_state $state, $eventdata = null) { + global $DB; - static $mailqueue = array(); + static $mailqueue = []; if ($eventdata) { $mailqueue[] = clone $eventdata; @@ -336,8 +335,18 @@ public static function message_send($eventdata = null) { // Only try to send if we're not in a transaction. while ($eventdata = array_shift($mailqueue)) { // Send each message in the array. - if (!message_send($eventdata)) { - throw new workflow_command_failed_exception(get_string('emailfailed', 'block_workflow')); + try { + if (!message_send($eventdata)) { + throw new block_workflow_exception( + get_string('emailfailed', 'block_workflow', + ['email' => $eventdata->userto->email, 'subject' => $eventdata->subject])); + } else { + $event = \block_workflow\event\email_sent_status::create_from_step_state($state); + $event->trigger(); + } + } catch (Exception $e) { + $event = \block_workflow\event\email_sent_status::create_from_step_state($state, $e->getMessage()); + $event->trigger(); } } } diff --git a/classes/command_override.php b/classes/command_override.php index 48d1782..8cd4ccf 100644 --- a/classes/command_override.php +++ b/classes/command_override.php @@ -33,9 +33,18 @@ * */ class block_workflow_command_override extends block_workflow_command { + + /** + * Parses the provided arguments and processes the workflow step and state. + * + * @param array $args The arguments to be parsed. + * @param mixed $step The workflow step to be processed. + * @param mixed|null $state The current state of the workflow (optional). + * @return stdClass + */ public function parse($args, $step, $state = null) { $data = new stdClass(); - $data->errors = array(); + $data->errors = []; if ($state) { $data->context = $state->context(); @@ -118,6 +127,14 @@ public function parse($args, $step, $state = null) { return $data; } + + /** + * Executes the command with the given arguments and state. + * + * @param array $args The arguments to be used for execution. + * @param mixed $state The current state to be processed. + * @return void + */ public function execute($args, $state) { $data = $this->parse($args, $state->step(), $state); assign_capability($data->capability, $data->permission, $data->role->id, $data->contextid, true); diff --git a/classes/command_setactivitylinkedsetting.php b/classes/command_setactivitylinkedsetting.php index 1f808c3..0b18e3c 100644 --- a/classes/command_setactivitylinkedsetting.php +++ b/classes/command_setactivitylinkedsetting.php @@ -47,12 +47,20 @@ class block_workflow_command_setactivitylinkedsetting extends block_workflow_com /** @var string used to indicate that the script action is to set values. */ const CLEAR = 'clear'; + /** + * Parses the given arguments and processes the workflow step. + * + * @param array $args Arguments to be parsed. + * @param int $step The workflow step identifier. + * @param mixed|null $state Optional state information. + * @return stdClass The result of the parsing operation. + */ public function parse($args, $step, $state = null) { global $DB; $dbman = $DB->get_manager(); $data = new stdClass(); - $data->errors = array(); + $data->errors = []; $workflow = $step->workflow(); @@ -106,7 +114,7 @@ public function parse($args, $step, $state = null) { } if ($data->action == self::SET) { - $data->toset = array(); + $data->toset = []; while ($line) { $column = array_shift($line); if (!$dbman->field_exists($data->table, $column)) { @@ -126,17 +134,24 @@ public function parse($args, $step, $state = null) { return $data; } + /** + * Executes the command to set the activity linked setting. + * + * @param mixed $args The arguments required for execution. + * @param mixed $state The current state or context for the command. + * @return void + */ public function execute($args, $state) { global $DB; $data = $this->parse($args, $state->step(), $state); if ($data->action == self::CLEAR) { - $DB->delete_records($data->table, array($data->fkcolumn => $data->cm->instance)); + $DB->delete_records($data->table, [$data->fkcolumn => $data->cm->instance]); return; } - $existingrow = $DB->get_record($data->table, array($data->fkcolumn => $data->cm->instance)); + $existingrow = $DB->get_record($data->table, [$data->fkcolumn => $data->cm->instance]); if ($existingrow) { $row = new stdClass(); $row->id = $existingrow->id; diff --git a/classes/command_setactivitysetting.php b/classes/command_setactivitysetting.php index 5d5f98d..02e87fa 100644 --- a/classes/command_setactivitysetting.php +++ b/classes/command_setactivitysetting.php @@ -33,57 +33,14 @@ * */ class block_workflow_command_setactivitysetting extends block_workflow_command { - public function parse($args, $step, $state = null) { - global $DB; - $data = new stdClass(); - $data->errors = array(); - - $workflow = $step->workflow(); - - // Check that this step workflow relatees to an activity. - if (!parent::is_activity($workflow)) { - $data->errors[] = get_string('notanactivity', 'block_workflow', 'setactivityvisibility'); - return $data; - } - - if ($state) { - $data->cm = get_coursemodule_from_id($workflow->appliesto, $state->context()->instanceid); - } - - // We'll use the database_manager to check whether tables and fields exist. - $dbman = $DB->get_manager(); - - // Check that the $appliesto table exists. - $data->table = $workflow->appliesto; - if (!$dbman->table_exists($data->table)) { - $data->errors[] = get_string('invalidappliestotable', 'block_workflow', $workflow->appliesto); - return $data; - } - - // Break down the line. It should be in the format: - // column to value - // where column is a column in the activity settings table. - $line = preg_split('/[\s+]/', $args); - - // Get the column and check that it exists. - $data->column = array_shift($line); - if (!$dbman->field_exists($data->table, $data->column)) { - $data->errors[] = get_string('invalidactivitysettingcolumn', 'block_workflow', $data->column); - return $data; - } - - // Shift off the 'to' component. - $to = array_shift($line); - if ($to !== 'to') { - $data->errors[] = get_string('invalidsyntaxmissingto', 'block_workflow'); - return $data; - } - - // What we'll be setting it to. - $data->value = array_shift($line); - - return $data; - } + /** + * Parses the provided arguments and processes them based on the given step and state. + * + * @param array $args The arguments to be parsed. + * @param mixed $step The step to be processed. + * @param mixed|null $state Optional. The current state, if any. Defaults to null. + * @return void + */ public function execute($args, $state) { global $DB; @@ -93,7 +50,7 @@ public function execute($args, $state) { $column = $data->column; $record->$column = $data->value; - $existing = $DB->get_record($data->table, array('id' => $data->cm->instance)); + $existing = $DB->get_record($data->table, ['id' => $data->cm->instance]); $record->id = $existing->id; $DB->update_record($data->table, $record); } diff --git a/classes/command_setactivityvisibility.php b/classes/command_setactivityvisibility.php index c0b4ad9..8978d27 100644 --- a/classes/command_setactivityvisibility.php +++ b/classes/command_setactivityvisibility.php @@ -32,9 +32,17 @@ * */ class block_workflow_command_setactivityvisibility extends block_workflow_command { + /** + * Parses the provided arguments and processes them based on the given step and state. + * + * @param array $args The arguments to be parsed. + * @param object $step The step object associated with the workflow. + * @param object|null $state Optional. The current state object, if available. + * @return stdClass The result of the parsing process. + */ public function parse($args, $step, $state = null) { $data = new stdClass(); - $data->errors = array(); + $data->errors = []; // Check that this step workflow relatees to an activity. if (!parent::is_activity($step->workflow())) { @@ -61,6 +69,14 @@ public function parse($args, $step, $state = null) { return $data; } + + /** + * Executes the command to set activity visibility. + * + * @param array $args Arguments required for the command execution. + * @param mixed $state The current state or context in which the command is executed. + * @return void + */ public function execute($args, $state) { global $CFG; require_once($CFG->dirroot . '/course/lib.php'); diff --git a/classes/command_setcoursevisibility.php b/classes/command_setcoursevisibility.php index b8ab312..57dec8b 100644 --- a/classes/command_setcoursevisibility.php +++ b/classes/command_setcoursevisibility.php @@ -32,9 +32,18 @@ * */ class block_workflow_command_setcoursevisibility extends block_workflow_command { + + /** + * Parses the provided arguments to set the course visibility. + * + * @param string $args Arguments required for parsing the command. + * @param object $step The current step in the workflow process. + * @param ?object $state Optional state information for the workflow. + * @return stdClass The result of the parsing operation. + */ public function parse($args, $step, $state = null) { $data = new stdClass(); - $data->errors = array(); + $data->errors = []; // Check that this step workflow relatees to an activity. if (!parent::is_course($step->workflow())) { @@ -57,6 +66,13 @@ public function parse($args, $step, $state = null) { return $data; } + /** + * Executes the command to set the course visibility. + * + * @param string $args Arguments required for the command execution. + * @param object $state Current state or context for the command. + * @return void + */ public function execute($args, $state) { global $DB; $data = $this->parse($args, $state->step(), $state); diff --git a/classes/command_setgradeitemvisibility.php b/classes/command_setgradeitemvisibility.php index 5b5d9ec..ff96818 100644 --- a/classes/command_setgradeitemvisibility.php +++ b/classes/command_setgradeitemvisibility.php @@ -39,9 +39,17 @@ class block_workflow_command_setgradeitemvisibility extends block_workflow_comma /** @var int Constant for visible grade items */ const VISIBLE = 0; - public function parse($args, $step, $state = null) { + /** + * Parses the given arguments to set the visibility of a grade item. + * + * @param array $args Arguments required for the command. + * @param object $step The current workflow step. + * @param object|null $state Optional state information. + * @return stdClass The result of the parsing operation. + */ + public function parse($args, $step, $state = null): stdClass { $data = new stdClass(); - $data->errors = array(); + $data->errors = []; // Check that this step workflow relates to an activity. if (!parent::is_activity($step->workflow())) { @@ -76,6 +84,13 @@ public function parse($args, $step, $state = null) { return $data; } + /** + * Executes the command to set the visibility of a grade item. + * + * @param mixed $args Arguments required to execute the command. + * @param mixed $state Current state or context for the command execution. + * @return void + */ public function execute($args, $state) { global $CFG; require_once($CFG->dirroot . '/lib/grade/grade_item.php'); @@ -84,8 +99,8 @@ public function execute($args, $state) { return; } // Set grade_items visibility. - $gradeitems = grade_item::fetch_all(array('courseid' => $data->cm->course, 'itemtype' => 'mod', - 'itemmodule' => $data->cm->modname, 'iteminstance' => $data->cm->instance)); + $gradeitems = grade_item::fetch_all(['courseid' => $data->cm->course, 'itemtype' => 'mod', + 'itemmodule' => $data->cm->modname, 'iteminstance' => $data->cm->instance]); if ($gradeitems) { foreach ($gradeitems as $gradeitem) { if ($gradeitem->hidden === $data->visibility) { diff --git a/classes/email.php b/classes/email.php index 1b46ae7..35ed356 100644 --- a/classes/email.php +++ b/classes/email.php @@ -34,11 +34,22 @@ * @property-read string $subject The subject of the e-mail email */ class block_workflow_email { + + /** @var int id from the database. */ public $id; - public $message; + + /** @var string identifier used to refer to this message. */ public $shortname; + + /** @var string email subject. */ public $subject; + /** @var string text of the email. */ + public $message; + + /** @var int format of the message. One of the FORMAT_... constants. */ + public $messageformat; + /** * Constructor to obtain an e-mail template * @@ -60,13 +71,13 @@ public function __construct($emailid = null) { * @param stdClass $email Database record to overload into the * object instance * @return The instantiated block_workflow_email object - * @access private */ - private function _load($email) { - $this->id = $email->id; - $this->message = $email->message; - $this->shortname = $email->shortname; - $this->subject = $email->subject; + private function load($email) { + $this->id = $email->id; + $this->shortname = $email->shortname; + $this->subject = $email->subject; + $this->message = $email->message; + $this->messageformat = $email->messageformat; return $this; } @@ -76,13 +87,13 @@ private function _load($email) { * @return array The list of available settings */ public function expected_settings() { - return array( + return [ 'id', 'message', 'messageformat', 'shortname', - 'subject' - ); + 'subject', + ]; } /** @@ -94,11 +105,11 @@ public function expected_settings() { */ public function load_email_id($id) { global $DB; - $email = $DB->get_record('block_workflow_emails', array('id' => $id)); + $email = $DB->get_record('block_workflow_emails', ['id' => $id]); if (!$email) { throw new block_workflow_invalid_email_exception(get_string('invalidid', 'block_workflow')); } - return $this->_load($email); + return $this->load($email); } /** @@ -109,11 +120,11 @@ public function load_email_id($id) { */ public function load_email_shortname($shortname) { global $DB; - $email = $DB->get_record('block_workflow_emails', array('shortname' => $shortname)); + $email = $DB->get_record('block_workflow_emails', ['shortname' => $shortname]); if (!$email) { return false; } - return $this->_load($email); + return $this->load($email); } /** @@ -165,8 +176,8 @@ public static function load_emails() { FROM {block_workflow_emails} emails ORDER BY shortname ASC "; - $params = array('email1' => '%email%', 'email2' => '%email%', 'email3' => '%email%', - 'to1' => '%to%', 'to2' => '%to%', 'to3' => '%to%'); + $params = ['email1' => '%email%', 'email2' => '%email%', 'email3' => '%email%', + 'to1' => '%to%', 'to2' => '%to%', 'to3' => '%to%']; return $DB->get_records_sql($sql, $params); } @@ -188,7 +199,7 @@ public function create($email) { } // Check whether this shortname is already in use. - if ($DB->get_record('block_workflow_emails', array('shortname' => $email->shortname))) { + if ($DB->get_record('block_workflow_emails', ['shortname' => $email->shortname])) { $transaction->rollback(new block_workflow_invalid_email_exception('shortnameinuse', 'block_workflow')); } @@ -239,7 +250,7 @@ public function update($data) { // Check whether this shortname is already in use. if (isset($data->shortname) && - ($id = $DB->get_field('block_workflow_emails', 'id', array('shortname' => $data->shortname)))) { + ($id = $DB->get_field('block_workflow_emails', 'id', ['shortname' => $data->shortname]))) { if ($id != $data->id) { $transaction->rollback(new block_workflow_invalid_email_exception('shortnameinuse', 'block_workflow')); } @@ -301,7 +312,7 @@ public function delete() { // First check that we can delete this. $this->require_deletable(); - $DB->delete_records('block_workflow_emails', array('id' => $this->id)); + $DB->delete_records('block_workflow_emails', ['id' => $this->id]); } /** @@ -321,22 +332,22 @@ public function used_count() { $sql = "SELECT activescripts.onactivescript AS script FROM {block_workflow_steps} activescripts WHERE " . $DB->sql_like('activescripts.onactivescript', '?', false); - $activescripts = $DB->get_records_sql($sql, array('%email%' . $this->shortname . '%to%')); - $count += $this->_used_count($activescripts); + $activescripts = $DB->get_records_sql($sql, ['%email%' . $this->shortname . '%to%']); + $count += $this->handle_used_count($activescripts); // Count the uses in the extranotifyscripts. $sql = "SELECT extranotifyscripts.onextranotifyscript AS script FROM {block_workflow_steps} extranotifyscripts WHERE " . $DB->sql_like('extranotifyscripts.onextranotifyscript', '?', false); - $extranotifyscripts = $DB->get_records_sql($sql, array('%email%' . $this->shortname . '%to%')); - $count += $this->_used_count($extranotifyscripts); + $extranotifyscripts = $DB->get_records_sql($sql, ['%email%' . $this->shortname . '%to%']); + $count += $this->handle_used_count($extranotifyscripts); // Count the uses in the completescripts. $sql = "SELECT completescripts.oncompletescript AS script FROM {block_workflow_steps} completescripts WHERE " . $DB->sql_like('completescripts.oncompletescript', '?', false); - $completescripts = $DB->get_records_sql($sql, array('%email%' . $this->shortname . '%to%')); - $count += $this->_used_count($completescripts); + $completescripts = $DB->get_records_sql($sql, ['%email%' . $this->shortname . '%to%']); + $count += $this->handle_used_count($completescripts); // Return the tital usage count. return $count; @@ -348,7 +359,7 @@ public function used_count() { * @param array $scripts An array of stdClass objects with a script value * @return integer The number of times the template is in use */ - private function _used_count($scripts) { + private function handle_used_count($scripts) { // Keep track of the count. $count = 0; diff --git a/classes/event/email_sent_status.php b/classes/event/email_sent_status.php new file mode 100644 index 0000000..d0fec86 --- /dev/null +++ b/classes/event/email_sent_status.php @@ -0,0 +1,152 @@ +. + +namespace block_workflow\event; + +/** + * This event is triggered when we want to sent an email in workflow. + * + * @property-read array $other { + * Extra information about the event. + * + * - int stepid: The ID of the step which has its status changed. + * - int stepstateid: The ID of the this state-change. + * - int workflowid: The ID of the workflow this step is part of. + * - int timestamp: The timestamp of when the state-change occurred + * (same timestamp as written to the state_change table) + * - string stepname: The name of the step which has its status changed. + * - string workflowname: The name of the workflow this step is part of. + * - string error the error message when the email is not sent. + * + * @package block_workflow + * @copyright 2025 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Class representing a user in the system. + */ +class email_sent_status extends \core\event\base { + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->data['objecttable'] = 'block_workflow_steps'; + $this->data['crud'] = 'r'; + $this->data['edulevel'] = self::LEVEL_OTHER; + } + + /** + * Convenience factory-method to create an event from a step_state object. + * + * @param \block_workflow_step_state $stepstate The step-state with the data + * to create the event from. + * @param string|null $error the error message when the email is not sent. + * @return \core\event\base + */ + public static function create_from_step_state(\block_workflow_step_state $stepstate, ?string $error = null) { + return self::create([ + 'context' => $stepstate->context(), + 'objectid' => $stepstate->stepid, + 'other' => [ + 'stepid' => $stepstate->stepid, + 'stepstateid' => $stepstate->id, + 'workflowid' => $stepstate->step()->workflowid, + 'timestamp' => $stepstate->timemodified, + 'stepname' => $stepstate->step()->name, + 'workflowname' => $stepstate->step()->workflow()->name, + 'error' => $error, + ], + ]); + } + + /** + * Returns localised general event name. + * + * @return string + */ + public static function get_name() { + return get_string('eventworkflownotification', 'block_workflow'); + } + + /** + * Returns non-localised event description with id's for admin use only. + * (Note: These texts are not stored in the database, but read every + * time the log is shown, so they must be backwards compatible). + * + * @return string + */ + public function get_description() { + if (!$this->other['error']) { + return "The email was successfully sent to user with id '$this->userid' in " . + "'" . $this->other['stepname'] . "'" . " (id = " . $this->other['stepid'] . ")."; + } else { + return "The email to user with id '$this->userid' failed in " . + "'" . $this->other['stepname'] . "'" . " (id = " . $this->other['stepid'] . ")." . + ' Error: ' . $this->other['error']; + } + + } + + /** + * Returns a Moodle URL where the event can be observed afterwards. + * Can be null, if no valid location is present. + * + * @return null|\moodle_url + */ + public function get_url() { + return new \moodle_url('/blocks/workflow/overview.php', + ['contextid' => $this->contextid, 'workflowid' => $this->other['workflowid']]); + } + + /** + * Custom validation. + * + * Here we check that the extra custom fields for this events + * (described in the class phpdoc comment) were actually given as + * parameters to the event when it was triggered. + */ + protected function validate_data() { + parent::validate_data(); + + if (!isset($this->other['stepid'])) { + throw new \coding_exception('The \'stepid\' value must be set in \'other\' of the event.'); + } + + if (!isset($this->other['stepstateid'])) { + throw new \coding_exception('The \'stepstateid\' value must be set in \'other\' of the event.'); + } + + if (!isset($this->other['workflowid'])) { + throw new \coding_exception('The \'workflowid\' value must be set in \'other\' of the event.'); + } + + if (!isset($this->other['timestamp'])) { + throw new \coding_exception('The \'timestamp\' value must be set in \'other\' of the event.'); + } + + if (!isset($this->other['stepname'])) { + throw new \coding_exception('The \'stepname\' value must be set in \'other\' of the event.'); + } + + if (!isset($this->other['workflowname'])) { + throw new \coding_exception('The \'workflowname\' value must be set in \'other\' of the event.'); + } + } +} diff --git a/classes/event/step_aborted.php b/classes/event/step_aborted.php index 4cf06d5..1815dc4 100644 --- a/classes/event/step_aborted.php +++ b/classes/event/step_aborted.php @@ -41,6 +41,11 @@ */ class step_aborted extends \core\event\base { + /** + * Init method. + * + * @return void + */ protected function init() { $this->data['objecttable'] = 'block_workflow_steps'; $this->data['crud'] = 'u'; @@ -64,8 +69,8 @@ public static function create_from_step_state(\block_workflow_step_state $stepst 'workflowid' => $stepstate->step()->workflowid, 'timestamp' => $stepstate->timemodified, 'stepname' => $stepstate->step()->name, - 'workflowname' => $stepstate->step()->workflow()->name - ] + 'workflowname' => $stepstate->step()->workflow()->name, + ], ]); } diff --git a/classes/event/step_activated.php b/classes/event/step_activated.php index 9e5ead0..882a726 100644 --- a/classes/event/step_activated.php +++ b/classes/event/step_activated.php @@ -42,6 +42,11 @@ */ class step_activated extends \core\event\base { + /** + * Init method. + * + * @return void + */ protected function init() { $this->data['objecttable'] = 'block_workflow_steps'; $this->data['crud'] = 'u'; @@ -68,8 +73,8 @@ public static function create_from_step_state(\block_workflow_step_state $stepst 'timestamp' => $stepstate->timemodified, 'stepname' => $stepstate->step()->name, 'workflowname' => $stepstate->step()->workflow()->name, - 'initial' => $initial - ] + 'initial' => $initial, + ], ]); } diff --git a/classes/event/step_completed.php b/classes/event/step_completed.php index fe20973..9563216 100644 --- a/classes/event/step_completed.php +++ b/classes/event/step_completed.php @@ -41,6 +41,11 @@ */ class step_completed extends \core\event\base { + /** + * Init method. + * + * @return void + */ protected function init() { $this->data['objecttable'] = 'block_workflow_steps'; $this->data['crud'] = 'u'; @@ -63,8 +68,8 @@ public static function create_from_step_state(\block_workflow_step_state $stepst 'workflowid' => $stepstate->step()->workflowid, 'timestamp' => $stepstate->timemodified, 'stepname' => $stepstate->step()->name, - 'workflowname' => $stepstate->step()->workflow()->name - ] + 'workflowname' => $stepstate->step()->workflow()->name, + ], ]); } diff --git a/classes/event/step_extra_notification_processed.php b/classes/event/step_extra_notification_processed.php index dc9729e..305a804 100644 --- a/classes/event/step_extra_notification_processed.php +++ b/classes/event/step_extra_notification_processed.php @@ -43,6 +43,11 @@ */ class step_extra_notification_processed extends \core\event\base { + /** + * Init method. + * + * @return void + */ protected function init() { $this->data['objecttable'] = 'block_workflow_steps'; $this->data['crud'] = 'u'; @@ -65,8 +70,8 @@ public static function create_from_step_state(\block_workflow_step_state $stepst 'workflowid' => $stepstate->step()->workflowid, 'timestamp' => $stepstate->timemodified, 'stepname' => $stepstate->step()->name, - 'workflowname' => $stepstate->step()->workflow()->name - ] + 'workflowname' => $stepstate->step()->workflow()->name, + ], ]); } diff --git a/classes/event/todo_triggered.php b/classes/event/todo_triggered.php index 0876f7e..004bf32 100644 --- a/classes/event/todo_triggered.php +++ b/classes/event/todo_triggered.php @@ -43,6 +43,11 @@ */ class todo_triggered extends \core\event\base { + /** + * Init method. + * + * @return void + */ protected function init() { $this->data['objecttable'] = 'block_workflow_step_todos'; $this->data['crud'] = 'u'; @@ -70,8 +75,8 @@ public static function create_from_step_state(\block_workflow_step_state $stepst 'completed' => $completed, 'stepname' => $stepstate->step()->name, 'todoname' => $todo->task, - 'workflowname' => $stepstate->step()->workflow()->name - ] + 'workflowname' => $stepstate->step()->workflow()->name, + ], ]); } diff --git a/classes/external/external_api_base.php b/classes/external/external_api_base.php new file mode 100644 index 0000000..70ef352 --- /dev/null +++ b/classes/external/external_api_base.php @@ -0,0 +1,54 @@ +. + +namespace block_workflow\external; + +use core_external\external_api; +use core_external\external_function_parameters; +use block_workflow_step_state; +use block_workflow_ajax_exception; + +defined('MOODLE_INTERNAL') || die(); +global $CFG; +require_once($CFG->dirroot . '/blocks/workflow/locallib.php'); + +/** + * Overriding the api base class for common functions. + * + * @package block_workflow + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class external_api_base extends external_api { + + /** + * Handle security check when we call the webservice. + * + * @param array $params + * @param external_function_parameters $decription + * @return array [$state, $validatedparams] the current state and the validated params. + */ + public static function handle_security_check(array $params, external_function_parameters $decription): array { + $validatedparams = self::validate_parameters($decription, $params); + $state = new block_workflow_step_state($validatedparams['stateid']); + if (!block_workflow_can_make_changes($state)) { + throw new block_workflow_ajax_exception(get_string('notallowedtodothisstep', 'block_workflow')); + } + [$context, $course, $cm] = get_context_info_array($state->contextid); + self::validate_context($context); + return [$state, $validatedparams]; + } +} diff --git a/classes/external/finish_step.php b/classes/external/finish_step.php new file mode 100644 index 0000000..a5c5053 --- /dev/null +++ b/classes/external/finish_step.php @@ -0,0 +1,97 @@ +. + +namespace block_workflow\external; + +use core_external\external_function_parameters; +use core_external\external_value; +use core_external\external_single_structure; +use block_workflow_workflow; + +/** + * Finish step webservice. + * + * @package block_workflow + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class finish_step extends external_api_base { + + /** + * Returns description of method parameters + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'stateid' => new external_value(PARAM_INT, 'The ID of the step_state to load', VALUE_REQUIRED), + 'text' => new external_value(PARAM_RAW, 'The text of the new comment', VALUE_REQUIRED), + 'format' => new external_value(PARAM_INT, 'The format of the new comment', VALUE_REQUIRED), + ]); + } + + /** + * Finish the step. + * + * @param int $stateid the id of the current state + * @param string $text The text of the new comment + * @param int $format The format of the new comment + * @return array of The next state or false if there is none + */ + public static function execute(int $stateid, string $text, int $format): array { + global $PAGE; + [$state, $params] = self::handle_security_check(['stateid' => $stateid, 'text' => $text, 'format' => $format], + self::execute_parameters()); + $renderer = $PAGE->get_renderer('block_workflow'); + + // Retrieve the next step. + $newstate = $state->finish_step($params['text'], $params['format']); + $canview = ($newstate) ? has_capability('block/workflow:view', $newstate->context()) : false; + + if ($newstate && ($canview || block_workflow_can_make_changes($newstate))) { + // There is a next possible state, and the current user may view and/or work on it. + $result['response'] = $renderer->block_display($newstate, true); + $result['stateid'] = $newstate->id; + } else if ($newstate) { + // There is a new step, but this user can't view it, and can't work on it ... + $result['response'] = $renderer->block_display_step_complete_confirmation(); + } else { + // Last step has been reached, if permitted retrieve the list of workflows. + $workflows = new block_workflow_workflow(); + $previous = $workflows->load_context_workflows($state->contextid); + $canadd = has_capability('block/workflow:manage', $state->context()); + $appliesto = $state->step()->workflow()->appliesto; + $addableworkflows = block_workflow_workflow::available_workflows($appliesto); + $result['listworkflows'] = $canadd && $addableworkflows; + $result['response'] = $renderer->block_display_no_more_steps( + $state->contextid, $canadd, $addableworkflows, $previous); + } + return $result; + } + + /** + * Describe the return structure of the external service. + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'response' => new external_value(PARAM_RAW), + 'stateid' => new external_value(PARAM_INT, '', VALUE_OPTIONAL), + 'listworkflows' => new external_value(PARAM_BOOL, '', VALUE_OPTIONAL), + ]); + } +} diff --git a/classes/external/get_step_state_comment.php b/classes/external/get_step_state_comment.php new file mode 100644 index 0000000..f8216d3 --- /dev/null +++ b/classes/external/get_step_state_comment.php @@ -0,0 +1,67 @@ +. + +namespace block_workflow\external; + +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_value; +use core_external\util; + +/** + * Get step_state comment web service + * + * @package block_workflow + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class get_step_state_comment extends external_api_base { + + /** + * Returns description of method parameters + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'stateid' => new external_value(PARAM_INT, 'The ID of the step_state to load', VALUE_REQUIRED), + ]); + } + + /** + * Get comment from stateid + * + * @param int $stateid the id of the current state + * @return array of newly created comment. + */ + public static function execute(int $stateid): array { + // Now security checks. + [$state, $params] = self::handle_security_check(['stateid' => $stateid], self::execute_parameters()); + $result['response'] = util::format_text($state->comment, FORMAT_HTML, $state->contextid)[0]; + return $result; + } + + /** + * Describe the return structure of the external service. + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'response' => new external_value(PARAM_RAW), + ]); + } +} diff --git a/classes/external/update_step_state_comment.php b/classes/external/update_step_state_comment.php new file mode 100644 index 0000000..fcbf0ea --- /dev/null +++ b/classes/external/update_step_state_comment.php @@ -0,0 +1,71 @@ +. + +namespace block_workflow\external; + +use core_external\external_function_parameters; +use core_external\external_value; +use core_external\external_single_structure; + +/** + * Save/update the comment for the currently loaded step_state + * + * @package block_workflow + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class update_step_state_comment extends external_api_base { + + /** + * Returns description of method parameters + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'stateid' => new external_value(PARAM_INT, 'The ID of the step_state to load', VALUE_REQUIRED), + 'newcomment' => new external_value(PARAM_RAW, 'The text of the new comment', VALUE_REQUIRED), + 'newcommentformat' => new external_value(PARAM_INT, 'The format of the new comment', VALUE_REQUIRED), + ]); + } + + /** + * Update the comment for the currently loaded step_state + * + * @param int $stateid the id of the current state + * @param string $newcomment The text of the new comment + * @param int $newcommentformat The format of the new comment + * @return array of newly created comment + */ + public static function execute(int $stateid, string $newcomment, int $newcommentformat): array { + [$state, $params] = self::handle_security_check(['stateid' => $stateid, 'newcomment' => $newcomment, + 'newcommentformat' => $newcommentformat], self::execute_parameters()); + $state->update_comment($params['newcomment'], $params['newcommentformat']); + $result['response'] = shorten_text($newcomment, BLOCK_WORKFLOW_MAX_COMMENT_LENGTH); + return $result; + } + + /** + * Describe the return structure of the external service. + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'response' => new external_value(PARAM_RAW), + ]); + } +} diff --git a/classes/external/update_step_state_task_state.php b/classes/external/update_step_state_task_state.php new file mode 100644 index 0000000..d680c96 --- /dev/null +++ b/classes/external/update_step_state_task_state.php @@ -0,0 +1,69 @@ +. + +namespace block_workflow\external; + +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_value; + +/** + * Update step_state done webservice. + * + * @package block_workflow + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class update_step_state_task_state extends external_api_base { + + /** + * Returns description of method parameters + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'stateid' => new external_value(PARAM_INT, 'The ID of the step_state to load', VALUE_REQUIRED), + 'todoid' => new external_value(PARAM_INT, 'The ID of the task', VALUE_REQUIRED), + 'check' => new external_value(PARAM_BOOL, 'The todo being check/uncheck', VALUE_REQUIRED), + ]); + } + + /** + * Toggle the completed status of a task for a step state + * + * @param int $stateid the id of the current state + * @param int $todoid The ID of the task + * @return array of the new state of the task + */ + public static function execute(int $stateid, int $todoid, bool $check): array { + [$state, $params] = self::handle_security_check(['stateid' => $stateid, 'todoid' => $todoid, 'check' => $check], + self::execute_parameters()); + $result['response'] = $state->todo_toggle($params['todoid'], $params['check']); + return $result; + } + + /** + * Describe the return structure of the external service. + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'response' => new external_value(PARAM_BOOL), + ]); + } +} diff --git a/classes/local/forms/comment_form.php b/classes/local/forms/comment_form.php new file mode 100644 index 0000000..6cc7540 --- /dev/null +++ b/classes/local/forms/comment_form.php @@ -0,0 +1,47 @@ +. + +namespace block_workflow\local\forms; + +// Make sure this isn't being directly accessed. +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/formslib.php'); + +/** + * Block workflow comment form. + * + * @package block_workflow + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class comment_form extends \moodleform { + + #[\Override] + protected function definition() { + $mform = $this->_form; + $editoroptions = [ + 'maxfiles' => 0, + 'autosave' => false, + ]; + // Tiny need the pre-fix 'id_'. + $mform->addElement('editor', 'comment_editor', + get_string('commentlabel', 'block_workflow'), + ['id' => 'id_wkf-comment-editor'], $editoroptions); + $mform->setType('commentext', PARAM_RAW); + $mform->addElement("button", 'wfk-submit', $this->_customdata['inputLabel']); + } +} diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 54808aa..e79a7d1 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -24,11 +24,11 @@ namespace block_workflow\privacy; -use \core_privacy\local\request\approved_contextlist; -use \core_privacy\local\request\contextlist; -use \core_privacy\local\request\writer; -use \core_privacy\local\request\helper; -use \core_privacy\local\metadata\collection; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\contextlist; +use core_privacy\local\request\writer; +use core_privacy\local\request\helper; +use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_userlist; use core_privacy\local\request\userlist; @@ -52,7 +52,7 @@ class provider implements * @param collection $collection The initialised collection to add items to. * @return collection A listing of user data stored through this system. */ - public static function get_metadata(collection $collection) : collection { + public static function get_metadata(collection $collection): collection { // The 'block_workflow_state_changes' table stores the state change by user. $collection->add_database_table('block_workflow_state_changes', [ 'userid' => 'privacy:metadata:block_workflow_state_changes:userid', @@ -160,7 +160,7 @@ public static function export_user_data(approved_contextlist $contextlist) { ORDER BY step.id ASC"; $params = [ 'stepstatecontextid' => $context->id, - 'statechangesuserid' => $user->id + 'statechangesuserid' => $user->id, ]; $rs = $DB->get_recordset_sql($sql, $params); @@ -172,7 +172,7 @@ public static function export_user_data(approved_contextlist $contextlist) { 'description' => $rec->description, 'stepname' => $rec->stepname, 'userid' => self::you_or_somebody_else($rec->userid, $user), - 'newstate' => $rec->newstate + 'newstate' => $rec->newstate, ]; if (empty($contextdata->statechangedata)) { @@ -196,7 +196,7 @@ public static function export_user_data(approved_contextlist $contextlist) { $params = [ 'statescontextid' => $context->id, - 'doneuserid' => $user->id + 'doneuserid' => $user->id, ]; $rs = $DB->get_recordset_sql($sql, $params); @@ -206,7 +206,7 @@ public static function export_user_data(approved_contextlist $contextlist) { $donedata = [ 'stepname' => $rec->stepname, 'taskdone' => $rec->taskdone, - 'userid' => self::you_or_somebody_else($rec->userid, $user) + 'userid' => self::you_or_somebody_else($rec->userid, $user), ]; if (empty($contextdata->tododonedata)) { @@ -265,7 +265,7 @@ public static function delete_data_for_all_users_in_context(\context $context) { $params = [ 'statescontextid' => $context->id, - 'adminuserid' => get_admin()->id + 'adminuserid' => get_admin()->id, ]; // Keep all the data but anonymise with the admin user id. @@ -320,7 +320,7 @@ protected static function delete_user_data($context, $userid) { $params = [ 'statescontextid' => $context->id, 'userid' => $userid, - 'adminuserid' => get_admin()->id + 'adminuserid' => get_admin()->id, ]; // Delete block_workflow_todo_done owned by user. diff --git a/classes/step.php b/classes/step.php index 61b973a..6f9e8a8 100644 --- a/classes/step.php +++ b/classes/step.php @@ -45,27 +45,105 @@ * @property-read string $onextranotifyscript The script for processing which email notification should be sent. */ class block_workflow_step { + /** + * Number of days before the quiz starts. + */ const DAYS_BEFORE_QUIZ = -10; + + /** + * Number of days after the quiz ends. + */ const DAYS_AFTER_QUIZ = 10; + + /** + * Number of days before the course starts. + */ const DAYS_BEFORE_COURSE = -120; + + /** + * Number of days after the course ends. + */ const DAYS_AFTER_COURSE = 30; - private $step = null; - private $workflow = null; - private $todos = null; + /** + * @var mixed|null $step The current step of the workflow. Defaults to null. + */ + private $step; + + /** + * @var mixed|null $workflow The workflow associated with the step. Defaults to null. + */ + private $workflow; + + /** + * @var mixed|null $todos The list of tasks or actions to be completed in this step. Defaults to null. + */ + private $todos; + /** + * @var int $id The unique identifier for the step. + */ public $id; + + /** + * @var int $workflowid The ID of the workflow to which this step belongs. + */ public $workflowid; + + /** + * @var int $stepno The step number in the workflow sequence. + */ public $stepno; + + /** + * @var string $name The name of the step. + */ public $name; + + /** + * @var string $instructions The instructions for completing the step. + */ public $instructions; + + /** + * @var int $instructionsformat The format of the instructions (e.g., plain text, HTML). + */ public $instructionsformat; + + /** + * @var string|null $onactivescript The script to execute when the step becomes active. Defaults to null. + */ public $onactivescript; + + /** + * @var string|null $oncompletescript The script to execute when the step is completed. Defaults to null. + */ public $oncompletescript; + + /** + * @var bool $autofinish Whether the step should automatically finish. Defaults to false. + */ public $autofinish; + + /** + * @var int|null $autofinishoffset The time offset (in seconds) for automatically finishing the step. Defaults to null. + */ public $autofinishoffset; + + /** + * @var string|null $extranotify Additional notification settings for the step. Defaults to null. + */ public $extranotify; + + /** + * @var int|null $extranotifyoffset The time offset (in seconds) for sending extra notifications. Defaults to null. + */ public $extranotifyoffset; + + + /** + * @var mixed $onextranotifyscript Holds the extra notification script associated with the workflow step. + */ public $onextranotifyscript; /** @@ -89,9 +167,8 @@ public function __construct($stepid = null) { * @param stdClass $step Database record to overload into the * object instance * @return The instantiated block_workflow_step object - * @access private */ - private function _load($step) { + private function load($step) { $this->id = $step->id; $this->workflowid = $step->workflowid; $this->stepno = $step->stepno; @@ -115,7 +192,7 @@ private function _load($step) { */ public static function make($stepdata) { $step = new block_workflow_step(); - $step->_load($stepdata); + $step->load($stepdata); return $step; } @@ -125,7 +202,7 @@ public static function make($stepdata) { * @return array The list of available settings */ public function expected_settings() { - return array( + return [ 'id', 'workflowid', 'stepno', @@ -138,8 +215,8 @@ public function expected_settings() { 'autofinishoffset', 'extranotify', 'extranotifyoffset', - 'onextranotifyscript' - ); + 'onextranotifyscript', + ]; } /** @@ -151,11 +228,11 @@ public function expected_settings() { */ public function load_step($stepid) { global $DB; - $step = $DB->get_record('block_workflow_steps', array('id' => $stepid)); + $step = $DB->get_record('block_workflow_steps', ['id' => $stepid]); if (!$step) { throw new block_workflow_invalid_step_exception(get_string('noactiveworkflow', 'block_workflow')); } - return $this->_load($step); + return $this->load($step); } /** @@ -169,11 +246,11 @@ public function load_step($stepid) { public function load_workflow_stepno($workflowid, $stepno) { global $DB; $step = $DB->get_record('block_workflow_steps', - array('workflowid' => $workflowid, 'stepno' => $stepno)); + ['workflowid' => $workflowid, 'stepno' => $stepno]); if (!$step) { throw new block_workflow_invalid_step_exception(get_string('invalidworkflowstepno', 'block_workflow')); } - return $this->_load($step); + return $this->load($step); } /** @@ -266,7 +343,7 @@ public function create_step($step, $beforeafter = 0) { } else { // Retrieve the stepno from the final step for this workflow. $sql = 'SELECT stepno FROM {block_workflow_steps} WHERE workflowid = ? ORDER BY stepno DESC LIMIT 1'; - $step->stepno = $DB->get_field_sql($sql, array($step->workflowid)); + $step->stepno = $DB->get_field_sql($sql, [$step->workflowid]); if ($step->stepno) { // If there's already a step on this workflow, add to that step number. @@ -335,7 +412,6 @@ public function create_step($step, $beforeafter = 0) { * no workflowid is specified, the step is placed within the same * workflow as the source * @return The newly created block_workflow_step object - * @static */ public static function clone_step($srcid, $workflowid = null) { global $DB; @@ -400,7 +476,7 @@ public function delete() { $transaction = $DB->start_delegated_transaction(); // Retrieve a list of the step_states. - $states = $DB->get_records('block_workflow_step_states', array('stepid' => $this->id), null, 'id'); + $states = $DB->get_records('block_workflow_step_states', ['stepid' => $this->id], null, 'id'); $statelist = array_map(function ($a) { return $a->id; }, $states); @@ -412,7 +488,7 @@ public function delete() { $DB->delete_records_list('block_workflow_todo_done', 'stepstateid', $statelist); // Remove the states. - $DB->delete_records('block_workflow_step_states', array('stepid' => $this->id)); + $DB->delete_records('block_workflow_step_states', ['stepid' => $this->id]); // Update the atengobacktostep setting for the workflow if required. $workflow = $this->workflow(); @@ -422,7 +498,7 @@ public function delete() { } // Remove the step. - $DB->delete_records('block_workflow_steps', array('id' => $this->id)); + $DB->delete_records('block_workflow_steps', ['id' => $this->id]); // Now that the step has been removed, renumber the remaining step numbers. $workflow->renumber_steps(); @@ -547,7 +623,7 @@ public function set_workflow($workflowid) { public static function is_step_in_use($stepid) { global $DB; return $DB->count_records('block_workflow_step_states', - array('stepid' => $stepid, 'state' => BLOCK_WORKFLOW_STATE_ACTIVE)); + ['stepid' => $stepid, 'state' => BLOCK_WORKFLOW_STATE_ACTIVE]); } /** @@ -614,7 +690,7 @@ public function load_active_step($contextid) { LEFT JOIN {block_workflow_steps} steps ON steps.id = state.stepid WHERE state.contextid = ? AND state.state = ?'; - $step = $DB->get_record_sql($sql, array($contextid, BLOCK_WORKFLOW_STATE_ACTIVE)); + $step = $DB->get_record_sql($sql, [$contextid, BLOCK_WORKFLOW_STATE_ACTIVE]); if (!$step) { throw new block_workflow_not_assigned_exception(get_string('noactiveworkflow', 'block_workflow')); } @@ -635,11 +711,11 @@ public function load_active_step($contextid) { public static function parse_script($script) { // Our return place-holder. $return = new stdClass(); - $return->errors = array(); - $return->commands = array(); + $return->errors = []; + $return->commands = []; // Break the script into lines. - $lines = preg_split('~[\r\n]+~', $script, null, PREG_SPLIT_NO_EMPTY); + $lines = preg_split('~[\r\n]+~', $script, -1, PREG_SPLIT_NO_EMPTY); foreach ($lines as $line) { $c = new stdClass(); @@ -780,7 +856,7 @@ public function process_script(block_workflow_step_state $state, $script) { // This is a workaround for a limitation of the message_send system. // This must be called outside of a transaction. - block_workflow_command_email::message_send(); + block_workflow_command_email::message_send($state); } /** @@ -799,7 +875,7 @@ public function get_next_step() { // Determine the stepid of the next step. $stepid = $DB->get_field('block_workflow_steps', 'id', - array('workflowid' => $this->workflowid, 'stepno' => ($this->stepno + 1))); + ['workflowid' => $this->workflowid, 'stepno' => ($this->stepno + 1)]); if ($stepid) { // If there is another step, return that step object. @@ -914,7 +990,7 @@ public function roles($stepid = null) { WHERE d.stepid = ? ORDER BY r.shortname ASC'; - return role_fix_names($DB->get_records_sql($sql, array($stepid))); + return role_fix_names($DB->get_records_sql($sql, [$stepid])); } /** @@ -927,8 +1003,8 @@ public function roles($stepid = null) { */ public function toggle_role($roleid) { global $DB; - if ($DB->get_record('block_workflow_step_doers', array('stepid' => $this->id, 'roleid' => $roleid))) { - return $DB->delete_records('block_workflow_step_doers', array('roleid' => $roleid, 'stepid' => $this->id)); + if ($DB->get_record('block_workflow_step_doers', ['stepid' => $this->id, 'roleid' => $roleid])) { + return $DB->delete_records('block_workflow_step_doers', ['roleid' => $roleid, 'stepid' => $this->id]); } else { $role = new stdClass(); $role->stepid = $this->id; @@ -937,20 +1013,32 @@ public function toggle_role($roleid) { } } + /** + * Formats the instructions for the given context. + * + * @param context $context The context in which to format the instructions. + * @return string The formatted instructions. + */ public function format_instructions($context) { - $replaces = array(); + $replaces = []; if ($context->contextlevel == CONTEXT_MODULE) { $replaces['%%cmid%%'] = $context->instanceid; } $instructions = str_replace(array_keys($replaces), array_values($replaces), $this->instructions); return format_text($instructions, $this->instructionsformat, - array('noclean' => true, 'context' => $context)); + ['noclean' => true, 'context' => $context]); } + /** + * Returns the available autofinish options for the specified application context. + * + * @param mixed $appliesto The context or entity to which the autofinish options apply. + * @return array List of autofinish options. + */ public static function get_autofinish_options($appliesto) { global $CFG, $DB; - $options = array(); + $options = []; $options[''] = get_string('donotautomaticallyfinish', 'block_workflow'); if ($appliesto === 'course') { // The string is stored in the database in the following format. @@ -985,7 +1073,7 @@ public static function get_autofinish_options($appliesto) { $days = self::get_list_of_days(0, 0); } - return array($options, $days); + return [$options, $days]; } /** @@ -1001,7 +1089,7 @@ public static function get_autofinish_options($appliesto) { * @return object $days, array of strings */ private static function get_list_of_days($daysbefore, $dayafter) { - $days = array(); + $days = []; $secondsinday = 24 * 60 * 60; for ($count = $daysbefore; $count <= $dayafter; $count++) { if ($count < 0) { diff --git a/classes/step_state.php b/classes/step_state.php index 6b64651..56150b5 100644 --- a/classes/step_state.php +++ b/classes/step_state.php @@ -41,17 +41,53 @@ * @property-read int $commentformat The format of the comment field */ class block_workflow_step_state { + /** + * @var ?int $step The current step of the workflow. Null if not set. + */ private $step = null; + /** + * @var ?array $todos The list of tasks or actions associated with the step. Null if not set. + */ private $todos = null; + /** + * @var int $id The unique identifier for the step state. + */ public $id; + /** + * @var int $stepid The ID of the workflow step associated with this state. + */ public $stepid; + /** + * @var int $contextid The context ID where this workflow step state is applied. + */ public $contextid; + /** + * @var string $state The current state of the workflow step (e.g., 'inprogress', 'completed'). + */ public $state; + /** + * @var int $timemodified The timestamp of the last modification to this step state. + */ public $timemodified; + /** + * @var ?string $comment Optional comment associated with the step state. + */ public $comment; + /** + * @var int The format of the comment (e.g., plain text, HTML). + */ public $commentformat; + /** + * @var ?string occasionally, so that it can be used in messges, we need + * to store the comment from the previous step as well. (A bit hacky, but ...). + */ + public $previouscomment = null; + + /** @var ?int format of $previouscomment, if present. */ + public $previouscommentformat = null; + /** * Constructor to obtain a step_state * @@ -73,9 +109,8 @@ public function __construct($stateid = null) { * @param stdClass $state Database record to overload into the * object instance * @return The instantiated block_workflow_step_state object - * @access private */ - private function _load($state) { + private function load($state) { $this->id = $state->id; $this->stepid = $state->stepid; $this->contextid = $state->contextid; @@ -96,11 +131,11 @@ private function _load($state) { */ public function load_state($stateid) { global $DB; - $state = $DB->get_record('block_workflow_step_states', array('id' => $stateid)); + $state = $DB->get_record('block_workflow_step_states', ['id' => $stateid]); if (!$state) { throw new block_workflow_exception(get_string('invalidstate', 'block_workflow')); } - return $this->_load($state); + return $this->load($state); } /** @@ -111,12 +146,12 @@ public function load_state($stateid) { */ public function load_active_state($contextid) { global $DB; - $state = $DB->get_record('block_workflow_step_states', array( - 'contextid' => $contextid, 'state' => BLOCK_WORKFLOW_STATE_ACTIVE)); + $state = $DB->get_record('block_workflow_step_states', + ['contextid' => $contextid, 'state' => BLOCK_WORKFLOW_STATE_ACTIVE]); if (!$state) { return false; } - return $this->_load($state); + return $this->load($state); } /** @@ -145,12 +180,11 @@ public function require_active_state($contextid) { */ public function load_context_step($contextid, $stepid) { global $DB; - $state = $DB->get_record('block_workflow_step_states', array( - 'contextid' => $contextid, 'stepid' => $stepid)); + $state = $DB->get_record('block_workflow_step_states', ['contextid' => $contextid, 'stepid' => $stepid]); if (!$state) { throw new block_workflow_not_assigned_exception(get_string('invalidstate', 'block_workflow')); } - return $this->_load($state); + return $this->load($state); } /** @@ -233,7 +267,7 @@ public function change_status($newstatus) { switch ($this->state) { case BLOCK_WORKFLOW_STATE_ABORTED: case BLOCK_WORKFLOW_STATE_COMPLETED: - role_unassign_all(array('component' => 'block_workflow', 'itemid' => $this->id)); + role_unassign_all(['component' => 'block_workflow', 'itemid' => $this->id]); break; default: break; @@ -273,7 +307,7 @@ public function change_status($newstatus) { // This is a workaround for a limitation of the message_send system. // This must be called outside of a transaction. - block_workflow_command_email::message_send(); + block_workflow_command_email::message_send($this); // Return the updated step_state object. return $this->load_state($this->id); @@ -287,7 +321,7 @@ public function change_status($newstatus) { * @return mixed The next state or false if there is none */ public function finish_step($newcomment, $newcommentformat) { - global $DB, $USER; + global $DB; $transaction = $DB->start_delegated_transaction(); // Update the comment. @@ -316,7 +350,7 @@ public function finish_step($newcomment, $newcommentformat) { $nextstate = new block_workflow_step_state($newstate->id); } - $nextstate->previouscomment = $this->comment; // Hack alert! + $nextstate->previouscomment = $this->comment; $nextstate->previouscommentformat = $this->commentformat; $nextstate->change_status(BLOCK_WORKFLOW_STATE_ACTIVE); } @@ -325,7 +359,7 @@ public function finish_step($newcomment, $newcommentformat) { // This is a workaround for a limitation of the message_send system. // This must be called outside of a transaction. - block_workflow_command_email::message_send(); + block_workflow_command_email::message_send($nextstep ? $nextstate : $this); // Return the new state. if ($nextstep) { @@ -364,7 +398,7 @@ public function jump_to_step($contextid = null, $newstepid = null) { // This is a workaround for a limitation of the message_send system. // This must be called outside of a transaction. - block_workflow_command_email::message_send(); + block_workflow_command_email::message_send($state); return; } @@ -400,7 +434,7 @@ public function jump_to_step($contextid = null, $newstepid = null) { // This is a workaround for a limitation of the message_send system. // This must be called outside of a transaction. - block_workflow_command_email::message_send(); + block_workflow_command_email::message_send($nextstate); // Return a reference to the new state. return $nextstate; @@ -421,30 +455,39 @@ public function todos() { LEFT JOIN {block_workflow_todo_done} done ON done.steptodoid = todos.id AND done.stepstateid = ? WHERE todos.stepid = ? AND todos.obsolete = 0 ORDER BY todos.id'; - $this->todos = $DB->get_records_sql($sql, array($this->id, $this->stepid)); + $this->todos = $DB->get_records_sql($sql, [$this->id, $this->stepid]); } return $this->todos; } /** * Toggle the completed status of a task for a step state - * @param int $todoid The ID of the task + * + * @param int $todoid The ID of the task + * @param bool whether user check/uncheck the link. * @return boolean The new state of the task */ - public function todo_toggle($todoid) { + public function todo_toggle(int $todoid, bool $check): bool { global $DB, $USER; $transaction = $DB->start_delegated_transaction(); // Try and pick up the current task. - $todo = $DB->get_record('block_workflow_todo_done', array('stepstateid' => $this->id, 'steptodoid' => $todoid)); - + $todo = $DB->get_record('block_workflow_todo_done', ['stepstateid' => $this->id, 'steptodoid' => $todoid]); + // Has completed to do and user want to completed it. Do nothing. + if ($todo && $check) { + return true; + } + // Don't have to do and user want to uncheck it. + if (!$check && !$todo) { + return false; + } // Trigger an event for the toggled completed status of this to-do. $event = \block_workflow\event\todo_triggered::create_from_step_state($this, $todoid, !$todo); $event->trigger(); - if ($todo) { + if (!$check) { // Remove the current record. There is no past history at present. - $DB->delete_records('block_workflow_todo_done', array('id' => $todo->id)); + $DB->delete_records('block_workflow_todo_done', ['id' => $todo->id]); $transaction->allow_commit(); return false; } else { @@ -473,7 +516,7 @@ public static function state_changes($stateid) { INNER JOIN {user} u ON u.id = changes.userid WHERE changes.stepstateid = ? ORDER BY changes.timestamp DESC'; - return $DB->get_records_sql($sql, array($stateid)); + return $DB->get_records_sql($sql, [$stateid]); } /** @@ -492,7 +535,7 @@ public function get_all_users_and_their_roles($roles, $context) { list ($sortorder, $notused) = users_order_by_sql('u'); $roleinfo = role_get_names($context); - $rolenames = array(); + $rolenames = []; foreach ($roleinfo as $role) { $rolenames[$role->shortname] = $role->localname; } @@ -507,11 +550,11 @@ public function get_all_users_and_their_roles($roles, $context) { $userroles = $DB->get_recordset_sql($sql, array_merge($fieldssql->params, $params)); - $users = array(); + $users = []; foreach ($userroles as $userrole) { if (!array_key_exists($userrole->id, $users)) { $users[$userrole->id] = $userrole; - $users[$userrole->id]->roles = array($rolenames[$userrole->shortname]); + $users[$userrole->id]->roles = [$rolenames[$userrole->shortname]]; } else { $users[$userrole->id]->roles[] = $rolenames[$userrole->shortname]; } diff --git a/classes/task/finish_step_automatically.php b/classes/task/finish_step_automatically.php index 1ee5836..a467e51 100644 --- a/classes/task/finish_step_automatically.php +++ b/classes/task/finish_step_automatically.php @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace block_workflow\task; + /** * A scheduled task for workflow automatic step finisher. * @@ -21,9 +23,6 @@ * @copyright 2015 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - -namespace block_workflow\task; - class finish_step_automatically extends \core\task\scheduled_task { /** @@ -35,6 +34,11 @@ public function get_name() { return get_string('crontaskautostepfinisher', 'block_workflow'); } + /** + * Executes the task to finish a workflow step automatically. + * + * @return void + */ public function execute() { global $CFG, $DB; diff --git a/classes/task/send_extra_notification.php b/classes/task/send_extra_notification.php index c23cbc6..dbbc0df 100644 --- a/classes/task/send_extra_notification.php +++ b/classes/task/send_extra_notification.php @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace block_workflow\task; + /** * A scheduled task for workflow extra notify. * @@ -21,9 +23,6 @@ * @copyright 2015 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - -namespace block_workflow\task; - class send_extra_notification extends \core\task\scheduled_task { /** @@ -35,6 +34,11 @@ public function get_name() { return get_string('crontaskextranotify', 'block_workflow'); } + /** + * Execution function. + * + * @return void + */ public function execute() { global $CFG, $DB; diff --git a/classes/todo.php b/classes/todo.php index 180f0c4..657bc29 100644 --- a/classes/todo.php +++ b/classes/todo.php @@ -34,11 +34,25 @@ * @property-read int $obsolete The visibility of this workflow */ class block_workflow_todo { + /** + * @var int $id This is the unique identifier for the todo item. + */ public $id; + /** + * @var int $stepid The identifier of the associated step. + */ public $stepid; + /** + * @var string $task The description or name of the task. + */ public $task; + /** + * @var bool $obsolete Indicates whether the task is obsolete. + */ public $obsolete; - + /** + * @var object|null $step The associated step object, or null if not set. + */ private $step = null; /** @@ -62,9 +76,8 @@ public function __construct($id = null) { * @param stdClass $todo Database record to overload into the * object instance * @return The instantiated block_workflow_todo object - * @access private */ - private function _load($todo) { + private function load($todo) { $this->id = $todo->id; $this->stepid = $todo->stepid; $this->task = $todo->task; @@ -78,12 +91,12 @@ private function _load($todo) { * @return array The list of available settings */ public function expected_settings() { - return array( + return [ 'id', 'stepid', 'task', - 'obsolete' - ); + 'obsolete', + ]; } /** @@ -95,11 +108,11 @@ public function expected_settings() { */ public function load_by_id($id) { global $DB; - $todo = $DB->get_record('block_workflow_step_todos', array('id' => $id)); + $todo = $DB->get_record('block_workflow_step_todos', ['id' => $id]); if (!$todo) { throw new block_workflow_invalid_todo_exception(get_string('invalidtodo', 'block_workflow')); } - return $this->_load($todo); + return $this->load($todo); } /** @@ -206,10 +219,10 @@ public function delete_todo() { $transaction = $DB->start_delegated_transaction(); // First remove any todo_done records. - $DB->delete_records('block_workflow_todo_done', array('steptodoid' => $this->id)); + $DB->delete_records('block_workflow_todo_done', ['steptodoid' => $this->id]); // Then remove the actual todo. - $DB->delete_records('block_workflow_step_todos', array('id' => $this->id)); + $DB->delete_records('block_workflow_step_todos', ['id' => $this->id]); $transaction->allow_commit(); } @@ -226,7 +239,6 @@ public function delete_todo() { * no stepid is specified, the todo is placed within the same * step as the source * @return The newly created block_workflow_todo object - * @static */ public static function clone_todo($srcid, $stepid = null) { global $DB; diff --git a/classes/workflow.php b/classes/workflow.php index d3a4e67..b29050e 100644 --- a/classes/workflow.php +++ b/classes/workflow.php @@ -22,6 +22,10 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +defined('MOODLE_INTERNAL') || die(); + +require_once(dirname(__FILE__) . '/../locallib.php'); + /** * Workflow class * @@ -41,13 +45,28 @@ */ class block_workflow_workflow { + /** @var int Workflow ID */ public $id; + + /** @var string Short name of the workflow */ public $shortname; + + /** @var string Name of the workflow */ public $name; + + /** @var string Description of the workflow */ public $description; + + /** @var int Format of the description (e.g., FORMAT_HTML) */ public $descriptionformat; + + /** @var string Context or entity this workflow applies to */ public $appliesto; + + /** @var int|null Step to go back to at the end, or null if not applicable */ public $atendgobacktostep; + + /** @var int Indicates if the workflow is obsolete (1 = obsolete, 0 = active) */ public $obsolete; /** @@ -71,9 +90,8 @@ public function __construct($workflowid = null) { * @param stdClass $workflow Database record to overload into the * object instance * @return The instantiated block_workflow_workflow object - * @access private */ - private function _load($workflow) { + private function load($workflow) { $this->id = $workflow->id; $this->shortname = $workflow->shortname; $this->name = $workflow->name; @@ -91,15 +109,16 @@ private function _load($workflow) { * @return array The list of available settings */ public function expected_settings() { - return array('id', + return [ + 'id', 'shortname', 'name', 'description', 'descriptionformat', 'appliesto', 'atendgobacktostep', - 'obsolete' - ); + 'obsolete', + ]; } /** @@ -111,11 +130,11 @@ public function expected_settings() { */ public function load_workflow($workflowid) { global $DB; - $workflow = $DB->get_record('block_workflow_workflows', array('id' => $workflowid)); + $workflow = $DB->get_record('block_workflow_workflows', ['id' => $workflowid]); if (!$workflow) { throw new block_workflow_invalid_workflow_exception(get_string('invalidworkflow', 'block_workflow')); } - return $this->_load($workflow); + return $this->load($workflow); } /** @@ -127,11 +146,11 @@ public function load_workflow($workflowid) { */ public function load_workflow_from_shortname($shortname) { global $DB; - $workflow = $DB->get_record('block_workflow_workflows', array('shortname' => $shortname)); + $workflow = $DB->get_record('block_workflow_workflows', ['shortname' => $shortname]); if (!$workflow) { throw new block_workflow_invalid_workflow_exception(get_string('invalidworkflow', 'block_workflow')); } - return $this->_load($workflow); + return $this->load($workflow); } /** @@ -151,7 +170,7 @@ public function load_context_workflows($contextid) { WHERE states.contextid = ? GROUP BY workflows.id ORDER BY MAX(states.timemodified) DESC"; - $workflows = $DB->get_records_sql($sql, array($contextid)); + $workflows = $DB->get_records_sql($sql, [$contextid]); return $workflows; } @@ -176,13 +195,13 @@ public function create_workflow($workflow, $createstep = true, $makenamesunique } // Check whether this shortname is already in use. - if ($DB->get_record('block_workflow_workflows', array('shortname' => $workflow->shortname))) { + if ($DB->get_record('block_workflow_workflows', ['shortname' => $workflow->shortname])) { if ($makenamesunique) { // Create new name by adding a digit and incrementing it if // name already has digit at the end. $shortnameclean = preg_replace('/\d+$/', '', $workflow->shortname); $sql = 'SELECT shortname FROM {block_workflow_workflows} WHERE shortname LIKE ? ORDER BY shortname DESC LIMIT 1'; - $lastshortname = $DB->get_record_sql($sql, array($shortnameclean."%")); + $lastshortname = $DB->get_record_sql($sql, [$shortnameclean."%"]); if (preg_match('/\d+$/', $lastshortname->shortname)) { $workflow->shortname = $lastshortname->shortname; $workflow->shortname++; @@ -200,16 +219,21 @@ public function create_workflow($workflow, $createstep = true, $makenamesunique } // Check whether this name is already in use. - if ($DB->get_record('block_workflow_workflows', array('name' => $workflow->name))) { + if ($DB->get_record('block_workflow_workflows', ['name' => $workflow->name])) { if ($makenamesunique) { // Create new name by adding a digit and incrementing it if // name already has digit at the end. $nameclean = preg_replace('/\d+$/', '', $workflow->name); $sql = 'SELECT name FROM {block_workflow_workflows} WHERE name LIKE ? ORDER BY name DESC LIMIT 1'; - $lastname = $DB->get_record_sql($sql, array($nameclean."%")); + $lastname = $DB->get_record_sql($sql, [$nameclean."%"]); if (preg_match('/\d+$/', $lastname->name)) { $workflow->name = $lastname->name; - $workflow->name++; + // Safe increment of trailing number (preserves leading zeros). + // E.g: workflow01 becomes workflow02, workflow99 becomes workflow100. + $workflow->name = preg_replace_callback('/(\d+)$/', function ($m) { + $num = (int) $m[1] + 1; + return str_pad((string) $num, strlen($m[1]), '0', STR_PAD_LEFT); + }, $workflow->name); } else { $workflow->name .= '1'; } @@ -295,7 +319,6 @@ public function create_workflow($workflow, $createstep = true, $makenamesunique * @param Object $data An object containing any data to override * @return The newly created block_workflow_workflow object * @throws block_workflow_invalid_workflow_exception if the supplied shortname is already in use - * @static */ public static function clone_workflow($srcid, $data) { global $DB; @@ -321,7 +344,7 @@ public static function clone_workflow($srcid, $data) { $dst = (object) array_merge((array) $src, (array) $data); // Check whether this shortname is already in use. - if ($DB->get_record('block_workflow_workflows', array('shortname' => $dst->shortname))) { + if ($DB->get_record('block_workflow_workflows', ['shortname' => $dst->shortname])) { $transaction->rollback(new block_workflow_invalid_workflow_exception('shortnameinuse', 'block_workflow')); } @@ -371,32 +394,30 @@ public function delete() { $this->require_deletable(); // First remove any steps and their associated doers and todos. - $steps = $DB->get_records('block_workflow_step_states', array('id' => $this->id), null, 'id'); + $steps = $DB->get_records('block_workflow_step_states', ['id' => $this->id], null, 'id'); $steplist = array_map(function ($a) { return $a->id; }, $steps); $DB->delete_records_list('block_workflow_step_doers', 'stepid', $steplist); $DB->delete_records_list('block_workflow_step_todos', 'stepid', $steplist); - $DB->delete_records('block_workflow_steps', array('workflowid' => $this->id)); + $DB->delete_records('block_workflow_steps', ['workflowid' => $this->id]); // Finally, remove the workflow itself. - $DB->delete_records('block_workflow_workflows', array('id' => $this->id)); + $DB->delete_records('block_workflow_workflows', ['id' => $this->id]); $transaction->allow_commit(); } /** - * Return an array of available workflows + * Return an array of available workflows. * - * @param String for The context in which the workflow is for - * @return array of stdClass objects as returned by the database - * abstraction layer + * @param string $for type of context the workflows should apply to. + * @return stdClass[] of stdClass objects representing workflows. */ - public static function available_workflows($for) { + public static function available_workflows(string $for): array { global $DB; - $workflows = $DB->get_records('block_workflow_workflows', - array('appliesto' => $for, 'obsolete' => 0), 'name'); - return $workflows; + return $DB->get_records('block_workflow_workflows', + ['appliesto' => $for, 'obsolete' => 0], 'name'); } /** @@ -432,7 +453,7 @@ public function add_to_context($contextid) { // Check whether this workflow has been previously assigned to this context. $existingstate = $DB->get_record('block_workflow_step_states', - array('stepid' => $step->id, 'contextid' => $contextid)); + ['stepid' => $step->id, 'contextid' => $contextid]); if ($existingstate) { $state->id = $existingstate->id; $DB->update_record('block_workflow_step_states', $state); @@ -465,7 +486,7 @@ public function add_to_context($contextid) { // This is a workaround for a limitation of the message_send system. // This must be called outside of a transaction. - block_workflow_command_email::message_send(); + block_workflow_command_email::message_send($state); return $state; } @@ -515,7 +536,7 @@ public function remove_workflow($contextid) { $DB->delete_records_list('block_workflow_todo_done', 'stepstateid', $statelist); // Remove the states. - $DB->delete_records('block_workflow_step_states', array('contextid' => $contextid)); + $DB->delete_records('block_workflow_step_states', ['contextid' => $contextid]); // These are all of the required steps for removing a workflow from a context, so commit. $transaction->allow_commit(); @@ -539,7 +560,7 @@ public function atendgobacktostep($atendgobacktostep = null) { // Check that we've been given a valid step to loop back to. if ($atendgobacktostep && !$DB->get_record('block_workflow_steps', - array('workflowid' => $this->id, 'stepno' => $atendgobacktostep))) { + ['workflowid' => $this->id, 'stepno' => $atendgobacktostep])) { $transaction->rollback(new block_workflow_invalid_workflow_exception('invalidstepno', 'block_workflow')); } @@ -649,7 +670,7 @@ public static function in_use_by($id, $activeonly = false) { if ($activeonly) { $sql .= " AND st.state IN ('active')"; } - return $DB->count_records_sql($sql, array($id)); + return $DB->count_records_sql($sql, [$id]); } /** @@ -675,13 +696,13 @@ public function renumber_steps($from = null, $moveup = 0) { FROM {block_workflow_steps} WHERE workflowid = ? AND stepno > ? ORDER BY stepno ASC'; - $steps = $DB->get_records_sql($sql, array($this->id, $from)); + $steps = $DB->get_records_sql($sql, [$this->id, $from]); // Check whether the steps are incorrectly ordered in any way. $sql = 'SELECT COUNT(stepno) AS count, MAX(stepno) AS max, MIN(stepno) AS min FROM {block_workflow_steps} WHERE workflowid = ?'; - $checksteps = $DB->get_record_sql($sql, array($this->id)); + $checksteps = $DB->get_record_sql($sql, [$this->id]); if (($checksteps->count != $checksteps->max) || ($checksteps->min != 0)) { $topstep = $checksteps->max + 1; @@ -718,7 +739,7 @@ public function steps() { // Retrieve all of the steps for this workflowid, in order of their // ascending stepno. - $steps = $DB->get_records('block_workflow_steps', array('workflowid' => $this->id), 'stepno ASC'); + $steps = $DB->get_records('block_workflow_steps', ['workflowid' => $this->id], 'stepno ASC'); return $steps; } @@ -794,7 +815,7 @@ public function step_states($contextid) { ORDER BY steps.stepno"; - $steps = $DB->get_records_sql($sql, array('contextid' => $contextid, 'workflowid' => $this->id)); + $steps = $DB->get_records_sql($sql, ['contextid' => $contextid, 'workflowid' => $this->id]); return $steps; } @@ -817,7 +838,7 @@ public function update($data) { // Check whether this shortname is already in use. if (isset($data->shortname) && - ($id = $DB->get_field('block_workflow_workflows', 'id', array('shortname' => $data->shortname)))) { + ($id = $DB->get_field('block_workflow_workflows', 'id', ['shortname' => $data->shortname]))) { if ($id != $data->id) { $transaction->rollback(new block_workflow_invalid_workflow_exception('shortnameinuse', 'block_workflow')); } diff --git a/clone.php b/clone.php index 556dfc3..5aaeb82 100644 --- a/clone.php +++ b/clone.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once(dirname(__FILE__) . '/clone_form.php'); @@ -43,7 +44,7 @@ // Set page and return urls. $returnurl = new moodle_url('/blocks/workflow/manage.php'); -$PAGE->set_url('/blocks/workflow/clone.php', array('workflowid' => $workflowid)); +$PAGE->set_url('/blocks/workflow/clone.php', ['workflowid' => $workflowid]); // Page settings. $title = get_string('cloneworkflowname', 'block_workflow', $workflow->name); @@ -71,7 +72,7 @@ $workflow = block_workflow_workflow::clone_workflow($workflowid, $data); // Redirect to the newly created workflow. - redirect(new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $workflow->id))); + redirect(new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $workflow->id])); } // Set the clone workflow form defaults. @@ -82,7 +83,7 @@ $data->description = $workflow->description; $data->descriptionformat = $workflow->descriptionformat; $data->appliesto = block_workflow_appliesto($workflow->appliesto); -$data = file_prepare_standard_editor($data, 'description', array('noclean' => true)); +$data = file_prepare_standard_editor($data, 'description', ['noclean' => true]); $cloneform->set_data($data); diff --git a/clone_form.php b/clone_form.php index 5a20b01..f6d2ae2 100644 --- a/clone_form.php +++ b/clone_form.php @@ -28,20 +28,27 @@ require_once($CFG->libdir . '/formslib.php'); +/** + * Form class for cloning a workflow in Moodle. + * + * This class extends the moodleform base class. + */ class clone_workflow extends moodleform { + + #[\Override] protected function definition() { $mform = $this->_form; $mform->addElement('header', 'general', get_string('cloneworkflow', 'block_workflow')); // Shortname. - $mform->addElement('text', 'shortname', get_string('shortname', 'block_workflow'), array('size' => 80, 'maxlength' => 255)); + $mform->addElement('text', 'shortname', get_string('shortname', 'block_workflow'), ['size' => 80, 'maxlength' => 255]); $mform->setType('shortname', PARAM_TEXT); $mform->addRule('shortname', null, 'required', null, 'client'); $mform->addRule('shortname', null, 'maxlength', 255); // Name. - $mform->addElement('text', 'name', get_string('name', 'block_workflow'), array('size' => 80, 'maxlength' => 255)); + $mform->addElement('text', 'name', get_string('name', 'block_workflow'), ['size' => 80, 'maxlength' => 255]); $mform->setType('name', PARAM_TEXT); $mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', null, 'maxlength', 255); @@ -61,6 +68,7 @@ protected function definition() { $this->add_action_buttons(true, get_string('clone', 'block_workflow')); } + #[\Override] public function validation($data, $files) { $errors = parent::validation($data, $files); diff --git a/db/access.php b/db/access.php index 4adc429..c628fc3 100644 --- a/db/access.php +++ b/db/access.php @@ -24,59 +24,59 @@ defined('MOODLE_INTERNAL') || die(); -$capabilities = array( +$capabilities = [ - 'block/workflow:addinstance' => array( + 'block/workflow:addinstance' => [ 'riskbitmask' => RISK_SPAM | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_BLOCK, - 'archetypes' => array( + 'archetypes' => [ 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ), + 'manager' => CAP_ALLOW, + ], - 'clonepermissionsfrom' => 'moodle/site:manageblocks' - ), + 'clonepermissionsfrom' => 'moodle/site:manageblocks', + ], // Allows access to, and use of the workflow definition. // By default given to manager. - 'block/workflow:editdefinitions' => array( + 'block/workflow:editdefinitions' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, - 'archetypes' => array( + 'archetypes' => [ 'manager' => CAP_ALLOW, - ) - ), + ], + ], // Allows users to see the workflow block. // By default given to manager and editingteacher. - 'block/workflow:view' => array( + 'block/workflow:view' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, - 'archetypes' => array( + 'archetypes' => [ 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - ) - ), + ], + ], // Allows use of the 'Jump to step' button. // By default given to manager. - 'block/workflow:manage' => array( + 'block/workflow:manage' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, - 'archetypes' => array( + 'archetypes' => [ 'manager' => CAP_ALLOW, - ) - ), + ], + ], // Allows use of the 'Finish step' button. // By default given to manager. - 'block/workflow:dostep' => array( + 'block/workflow:dostep' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, - 'archetypes' => array( + 'archetypes' => [ 'manager' => CAP_ALLOW, - ) - ) -); + ], + ], +]; diff --git a/db/messages.php b/db/messages.php index 2e73bef..51b0fa2 100644 --- a/db/messages.php +++ b/db/messages.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$messageproviders = array ( +$messageproviders = [ // Notification message provider. - 'notification' => array (), -); + 'notification' => [], +]; diff --git a/db/services.php b/db/services.php new file mode 100644 index 0000000..f4e3163 --- /dev/null +++ b/db/services.php @@ -0,0 +1,56 @@ +. + +/** + * Workflow block external functions and service definitions. + * + * @package block_workflow + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +$functions = [ + 'block_workflow_get_step_state_comment' => [ + 'classname' => 'block_workflow\\external\\get_step_state_comment', + 'methodname' => 'execute', + 'description' => 'Get comment from the current state', + 'type' => 'read', + 'ajax' => true, + ], + 'block_workflow_update_step_state_comment' => [ + 'classname' => 'block_workflow\\external\\update_step_state_comment', + 'methodname' => 'execute', + 'description' => 'Create/update comment from the current state', + 'type' => 'write', + 'ajax' => true, + ], + 'block_workflow_finish_step' => [ + 'classname' => 'block_workflow\\external\\finish_step', + 'methodname' => 'execute', + 'description' => 'Mark a step as finished', + 'type' => 'write', + 'ajax' => true, + ], + 'block_workflow_update_step_state_task_state' => [ + 'classname' => 'block_workflow\\external\\update_step_state_task_state', + 'methodname' => 'execute', + 'description' => 'Update the completed status of a task for a step state', + 'type' => 'write', + 'ajax' => true, + ], +]; diff --git a/db/tasks.php b/db/tasks.php index 873e9f6..3aeee03 100644 --- a/db/tasks.php +++ b/db/tasks.php @@ -26,25 +26,25 @@ defined('MOODLE_INTERNAL') || die(); -$tasks = array( +$tasks = [ // Run once a day after 05:01 AM. - array( + [ 'classname' => 'block_workflow\task\send_extra_notification', 'blocking' => 0, 'minute' => '1', 'hour' => '5', 'day' => '*', 'month' => '*', - 'dayofweek' => '*' - ), + 'dayofweek' => '*', + ], // Run once a day after 01:01 AM. - array( + [ 'classname' => 'block_workflow\task\finish_step_automatically', 'blocking' => 0, 'minute' => '1', 'hour' => '1', 'day' => '*', 'month' => '*', - 'dayofweek' => '*' - ) -); + 'dayofweek' => '*', + ], +]; diff --git a/db/upgrade.php b/db/upgrade.php index de69d9f..87bb722 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -22,6 +22,16 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +/** + * Handles the upgrade process for the block_workflow plugin. + * + * This function is called automatically during the upgrade process + * when the version in version.php is higher than the currently installed version. + * It applies any necessary database schema changes or data migrations. + * + * @param int $oldversion The version number of the currently installed plugin. + * @return bool True on success, false on failure. + */ function xmldb_block_workflow_upgrade($oldversion) { global $DB, $CFG; @@ -67,7 +77,7 @@ function xmldb_block_workflow_upgrade($oldversion) { $dbman->add_field($table, $field); if ($CFG->texteditors !== 'textarea') { $rs = $DB->get_recordset('block_workflow_emails', - array('messageformat' => FORMAT_MOODLE), '', 'id,message,messageformat'); + ['messageformat' => FORMAT_MOODLE], '', 'id,message,messageformat'); foreach ($rs as $b) { $b->message = text_to_html($b->message, false, false, true); $b->messageformat = FORMAT_HTML; @@ -83,14 +93,14 @@ function xmldb_block_workflow_upgrade($oldversion) { // Replace 'course:startdate' with 'course;startdate'. if ($oldversion < 2013072200) { $sql = "UPDATE {block_workflow_steps} SET autofinish = :new WHERE autofinish = :old"; - $DB->execute($sql, array('new' => 'course;startdate', 'old' => 'course:startdate')); + $DB->execute($sql, ['new' => 'course;startdate', 'old' => 'course:startdate']); upgrade_block_savepoint(true, 2013072200, 'workflow'); } // Fix broken autofinish values. if ($oldversion < 2014030500) { $DB->set_field_select('block_workflow_steps', 'autofinish', null, - 'autofinish IN (?, ?)', array('', 'donotautomaticallyfinish')); + 'autofinish IN (?, ?)', ['', 'donotautomaticallyfinish']); upgrade_block_savepoint(true, 2014030500, 'workflow'); } @@ -129,7 +139,7 @@ function xmldb_block_workflow_upgrade($oldversion) { // Define key roleid (foreign) to be added to block_workflow_step_doers. $table = new xmldb_table('block_workflow_step_doers'); - $key = new xmldb_key('roleid', XMLDB_KEY_FOREIGN, array('roleid'), 'role', array('id')); + $key = new xmldb_key('roleid', XMLDB_KEY_FOREIGN, ['roleid'], 'role', ['id']); // Launch add key roleid. $dbman->add_key($table, $key); diff --git a/delete.php b/delete.php index 10ed06e..f2d3800 100644 --- a/delete.php +++ b/delete.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/adminlib.php'); @@ -44,11 +45,11 @@ // The confirmation strings. $confirmstr = get_string('deleteworkflowcheck', 'block_workflow', $workflow->name); -$confirmurl = new moodle_url('/blocks/workflow/delete.php', array('workflowid' => $workflowid, 'confirm' => 1)); +$confirmurl = new moodle_url('/blocks/workflow/delete.php', ['workflowid' => $workflowid, 'confirm' => 1]); $returnurl = new moodle_url('/blocks/workflow/manage.php'); // Set page url. -$PAGE->set_url('/blocks/workflow/delete.php', array('workflowid' => $workflowid)); +$PAGE->set_url('/blocks/workflow/delete.php', ['workflowid' => $workflowid]); // Set the heading and page title. $title = get_string('confirmworkflowdeletetitle', 'block_workflow', $workflow->shortname); diff --git a/deleteemail.php b/deleteemail.php index 5f6e8dc..f840fae 100644 --- a/deleteemail.php +++ b/deleteemail.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/adminlib.php'); @@ -44,11 +45,11 @@ // The confirmation strings. $confirmstr = get_string('deleteemailcheck', 'block_workflow', $email->shortname); -$confirmurl = new moodle_url('/blocks/workflow/deleteemail.php', array('emailid' => $emailid, 'confirm' => 1)); +$confirmurl = new moodle_url('/blocks/workflow/deleteemail.php', ['emailid' => $emailid, 'confirm' => 1]); $returnurl = new moodle_url('/blocks/workflow/manage.php'); // Set the page and return urls. -$PAGE->set_url('/blocks/workflow/deleteemail.php', array('emaild' => $emailid)); +$PAGE->set_url('/blocks/workflow/deleteemail.php', ['emaild' => $emailid]); // Set the heading and page title. $title = get_string('confirmemaildeletetitle', 'block_workflow', $email->shortname); diff --git a/deletestep.php b/deletestep.php index 7c2169a..7d2f628 100644 --- a/deletestep.php +++ b/deletestep.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/adminlib.php'); @@ -44,11 +45,11 @@ // The confirmation strings. $confirmstr = get_string('deletestepcheck', 'block_workflow', $step->name); -$confirmurl = new moodle_url('/blocks/workflow/deletestep.php', array('stepid' => $stepid, 'confirm' => 1)); -$returnurl = new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $step->workflowid)); +$confirmurl = new moodle_url('/blocks/workflow/deletestep.php', ['stepid' => $stepid, 'confirm' => 1]); +$returnurl = new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $step->workflowid]); // Set page url. -$PAGE->set_url('/blocks/workflow/deletestep.php', array('stepid' => $stepid)); +$PAGE->set_url('/blocks/workflow/deletestep.php', ['stepid' => $stepid]); // Set the heading and page title. $title = get_string('confirmstepdeletetitle', 'block_workflow', $step->name); diff --git a/deletetask.php b/deletetask.php index 9d9e096..075b9ba 100644 --- a/deletetask.php +++ b/deletetask.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/adminlib.php'); @@ -40,11 +41,11 @@ // Load the todo. $todo = new block_workflow_todo($id); -$returnurl = new moodle_url('/blocks/workflow/editstep.php', array('stepid' => $todo->stepid)); +$returnurl = new moodle_url('/blocks/workflow/editstep.php', ['stepid' => $todo->stepid]); $workflow = $todo->step()->workflow(); // Generate the confirmation message. -$strparams = array('stepname' => $todo->step()->name, 'taskname' => $todo->task); +$strparams = ['stepname' => $todo->step()->name, 'taskname' => $todo->task]; // Set the heading and page title. $title = get_string('deletetasktitle', 'block_workflow', $strparams); @@ -59,11 +60,11 @@ // Generate the confirmation button. $confirmurl = new moodle_url('/blocks/workflow/deletetask.php', - array('id' => $todo->id, 'confirm' => 1)); + ['id' => $todo->id, 'confirm' => 1]); $confirmbutton = new single_button($confirmurl, get_string('confirm'), 'post'); // Set page url. -$PAGE->set_url('/blocks/workflow/deletetask.php', array('id' => $id)); +$PAGE->set_url('/blocks/workflow/deletetask.php', ['id' => $id]); // If confirmatation has already been received, then process. if ($confirm) { diff --git a/edit_workflow_form.php b/edit_workflow_form.php index 6524309..1774c0a 100644 --- a/edit_workflow_form.php +++ b/edit_workflow_form.php @@ -14,20 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Workflow edit form - * - * @package block_workflow - * @copyright 2011 Lancaster University Network Services Limited - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/formslib.php'); +/** + * Form definition for editing a workflow in the Moodle block_workflow plugin. + * + * This class extends moodleform to provide fields and validation for editing workflows. + * + * @package block_workflow + * @copyright 2025 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class edit_workflow extends moodleform { + + #[\Override] protected function definition() { $mform = $this->_form; @@ -35,13 +38,13 @@ protected function definition() { // Workflow base data. $mform->addElement('text', 'shortname', get_string('shortname', 'block_workflow'), - array('size' => 80, 'maxlength' => 255)); + ['size' => 80, 'maxlength' => 255]); $mform->setType('shortname', PARAM_TEXT); $mform->addRule('shortname', null, 'required', null, 'client'); $mform->addRule('shortname', null, 'maxlength', 255); $mform->addElement('text', 'name', get_string('name', 'block_workflow'), - array('size' => 80, 'maxlength' => 255)); + ['size' => 80, 'maxlength' => 255]); $mform->setType('name', PARAM_TEXT); $mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', null, 'maxlength', 255); @@ -60,7 +63,7 @@ protected function definition() { } // When reaching the end of the workflow, go back to. - $steplist = array(); + $steplist = []; $steplist[null] = get_string('atendfinishworkflow', 'block_workflow'); $finalstep = null; foreach ($this->_customdata['steps'] as $step) { @@ -75,7 +78,7 @@ protected function definition() { } // The current status of this workflow. - $enabledoptions = array(); + $enabledoptions = []; $enabledoptions['0'] = get_string('enabled', 'block_workflow'); $enabledoptions['1'] = get_string('disabled', 'block_workflow'); $mform->addElement('select', 'obsolete', get_string('status', 'block_workflow'), $enabledoptions); @@ -87,6 +90,7 @@ protected function definition() { $this->add_action_buttons(); } + #[\Override] public function validation($data, $files) { $errors = parent::validation($data, $files); diff --git a/editcomment.php b/editcomment.php index cba251b..ca0227d 100644 --- a/editcomment.php +++ b/editcomment.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once(dirname(__FILE__) . '/editcomment_form.php'); @@ -47,12 +48,12 @@ block_workflow_can_make_changes($state); // Set the page URL. -$PAGE->set_url('/blocks/workflow/editcomment.php', array('stateid' => $stateid)); +$PAGE->set_url('/blocks/workflow/editcomment.php', ['stateid' => $stateid]); $PAGE->set_pagelayout('standard'); $PAGE->set_course($course); // Set the heading and page title. -$tparams = array('stepname' => $state->step()->name, 'contextname' => $context->get_context_name()); +$tparams = ['stepname' => $state->step()->name, 'contextname' => $context->get_context_name()]; $title = get_string('editingcommentfor', 'block_workflow', $tparams); $PAGE->set_heading($title); $PAGE->set_title($title); @@ -63,7 +64,7 @@ $PAGE->navbar->add($state->step()->name); // Moodle form to update the state comment. -$mform = new state_editcomment(null, array('state' => $state)); +$mform = new state_editcomment(null, ['state' => $state]); // Grab a returnurl which relates to the context. $returnurl = $context->get_url(); @@ -85,7 +86,7 @@ $data->workflowname = $state->step()->workflow()->name; $data->stepname = $state->step()->name; $data->instructions = $state->step()->instructions; -$data = file_prepare_standard_editor($data, 'comment', array()); +$data = file_prepare_standard_editor($data, 'comment', []); $mform->set_data($data); diff --git a/editcomment_form.php b/editcomment_form.php index 730db44..effc66d 100644 --- a/editcomment_form.php +++ b/editcomment_form.php @@ -14,6 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + +require_once(dirname(__FILE__) . '/locallib.php'); +require_once($CFG->libdir . '/formslib.php'); + /** * Form for comments. * @@ -21,13 +26,9 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - -defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); - -require_once(dirname(__FILE__) . '/locallib.php'); -require_once($CFG->libdir . '/formslib.php'); - class state_editcomment extends moodleform { + + #[\Override] protected function definition() { $mform = $this->_form; $state = $this->_customdata['state']; diff --git a/editemail.php b/editemail.php index 977c06b..d73eef4 100644 --- a/editemail.php +++ b/editemail.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once(dirname(__FILE__) . '/editemail_form.php'); @@ -86,13 +87,15 @@ } // Set the form defaults. - $email->emailid = $email->id; - $message = $email->message; - $email->message = array(); - $email->message['text'] = $message; - $email->message['format'] = FORMAT_HTML; - - $emailform->set_data($email); +$toform = new stdClass(); +$toform->emailid = $email->id; +$toform->shortname = $email->shortname; +$toform->subject = $email->subject; +$toform->message = [ + 'text' => $email->message, + 'format' => $email->messageformat, +]; +$emailform->set_data($toform); // Grab the renderer. $renderer = $PAGE->get_renderer('block_workflow'); diff --git a/editemail_form.php b/editemail_form.php index 58342b6..7f3cbd4 100644 --- a/editemail_form.php +++ b/editemail_form.php @@ -14,20 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Email edit form - * - * @package block_workflow - * @copyright 2011 Lancaster University Network Services Limited - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/formslib.php'); +/** + * Form definition for editing email settings in the workflow block. + * + * This class extends moodleform to provide a form for editing email-related + * configuration within the Moodle workflow block. + * + * @package block_workflow + * @copyright 2025 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class email_edit extends moodleform { + + #[\Override] protected function definition() { $mform = $this->_form; $mform->addElement('header', 'general', get_string('emailsettings', 'block_workflow')); @@ -56,6 +60,7 @@ protected function definition() { $this->add_action_buttons(); } + #[\Override] public function validation($data, $files) { $errors = parent::validation($data, $files); diff --git a/editsettings.php b/editsettings.php index 8924ae3..a04897e 100644 --- a/editsettings.php +++ b/editsettings.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once(dirname(__FILE__) . '/edit_workflow_form.php'); @@ -42,13 +43,13 @@ $workflow = new block_workflow_workflow(); // Attempt to the set page/return url initially. -$returnurl = new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $workflowid)); +$returnurl = new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $workflowid]); if ($workflowid) { // If we've been given an existing workflow. $workflow->load_workflow($workflowid); $title = get_string('editworkflow', 'block_workflow', $workflow->name); - $PAGE->set_url('/blocks/workflow/editsettings.php', array('workflowid' => $workflowid)); + $PAGE->set_url('/blocks/workflow/editsettings.php', ['workflowid' => $workflowid]); } else { // We're creating a new workflow. $title = get_string('createworkflow', 'block_workflow'); @@ -68,7 +69,7 @@ } // Moodle form to create/edit workflow. -$customdata = array('steps' => $workflow->steps(), 'is_deletable' => $workflow->is_deletable()); +$customdata = ['steps' => $workflow->steps(), 'is_deletable' => $workflow->is_deletable()]; $editform = new edit_workflow(null, $customdata); if ($editform->is_cancelled()) { @@ -108,7 +109,7 @@ } // Redirect to the editsteps page. - redirect(new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $workflow->id))); + redirect(new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $workflow->id])); } $data = new stdClass(); @@ -123,7 +124,7 @@ $data->obsolete = $workflow->obsolete; $data->appliesto = $workflow->appliesto; $data->atendgobacktostep = $workflow->atendgobacktostep; - $data = file_prepare_standard_editor($data, 'description', array('noclean' => true)); + $data = file_prepare_standard_editor($data, 'description', ['noclean' => true]); $editform->set_data($data); } diff --git a/editstep.php b/editstep.php index 3d51a73..43218b9 100644 --- a/editstep.php +++ b/editstep.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once(dirname(__FILE__) . '/editstep_form.php'); @@ -41,26 +42,26 @@ if ($stepid) { // If we've been given an existing workflow. $step = new block_workflow_step($stepid); - $returnurl = new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $step->workflowid)); + $returnurl = new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $step->workflowid]); $todos = $step->todos(false); $doers = $step->roles(); $roles = block_workflow_contextlevel_roles($step->workflow()->context()); $title = get_string('editstepname', 'block_workflow', $step->name); - $PAGE->set_url('/blocks/workflow/editstep.php', array('stepid' => $stepid)); + $PAGE->set_url('/blocks/workflow/editstep.php', ['stepid' => $stepid]); // Add the breadcrumbs. $PAGE->navbar->add($step->workflow()->name, $returnurl); $PAGE->navbar->add(get_string('editstep', 'block_workflow')); - $appliesto = $DB->get_field('block_workflow_workflows', 'appliesto', array('id' => $step->workflowid)); + $appliesto = $DB->get_field('block_workflow_workflows', 'appliesto', ['id' => $step->workflowid]); } else { // We're creating a new step. $workflowid = required_param('workflowid', PARAM_INT); $workflow = new block_workflow_workflow($workflowid); - $returnurl = new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $workflowid)); + $returnurl = new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $workflowid]); $title = get_string('createstepname', 'block_workflow', $workflow->name); $beforeafter = optional_param('beforeafter', 0, PARAM_INT); - $PAGE->set_url('/blocks/workflow/editstep.php', array('workflowid' => $workflowid)); + $PAGE->set_url('/blocks/workflow/editstep.php', ['workflowid' => $workflowid]); // Add the breadcrumbs. $PAGE->navbar->add($workflow->name, $returnurl); @@ -74,7 +75,7 @@ $PAGE->set_heading($title); // Moodle form to create/edit step. -$stepedit = new step_edit('editstep.php', array('appliesto' => $appliesto)); +$stepedit = new step_edit('editstep.php', ['appliesto' => $appliesto]); if ($stepedit->is_cancelled()) { // Form was cancelled. @@ -128,7 +129,7 @@ $data->extranotify = $step->extranotify; $data->extranotifyoffset = $step->extranotifyoffset; $data->onextranotifyscript = $step->onextranotifyscript; - $data = file_prepare_standard_editor($data, 'instructions', array('noclean' => true)); + $data = file_prepare_standard_editor($data, 'instructions', ['noclean' => true]); $stepedit->set_data($data); } else { // Otherwise, this is a new step belonging to $workflowid. diff --git a/editstep_form.php b/editstep_form.php index 5da154e..49d3952 100644 --- a/editstep_form.php +++ b/editstep_form.php @@ -14,21 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Form for editing steps - * - * @package block_workflow - * @copyright 2011 Lancaster University Network Services Limited - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/formslib.php'); +/** + * Form definition for editing a workflow step in the Moodle block_workflow plugin. + * + * This class extends the moodleform base class to provide fields and validation + * for creating or editing workflow steps within the workflow block. + * + * @package block_workflow + * @copyright 2025 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class step_edit extends moodleform { + #[\Override] protected function definition() { global $DB; $mform = $this->_form; @@ -47,7 +50,7 @@ protected function definition() { $mform->addRule('instructions_editor', null, 'required', null, 'client'); // Scripts. - $scriptoptions = array('cols' => 80, 'rows' => 8); + $scriptoptions = ['cols' => 80, 'rows' => 8]; // IDs. $mform->addElement('hidden', 'stepid'); @@ -92,7 +95,7 @@ protected function definition() { $options[''] = get_string('donotautomaticallyfinish', 'block_workflow'); $mform->addElement('textarea', 'oncompletescript', get_string('oncompletescript', 'block_workflow'), $scriptoptions); $mform->setType('oncompletescript', PARAM_RAW); - $autofinish = array(); + $autofinish = []; $autofinish[] = $mform->createElement('select', 'autofinishoffset', null, $days); $autofinish[] = $mform->createElement('select', 'autofinish', null, $options); $mform->addGroup($autofinish, null, get_string('automaticallyfinish', 'block_workflow'), ' ', true); @@ -101,7 +104,7 @@ protected function definition() { $mform->disabledIf('autofinishoffset', 'autofinish', 'eq', ''); // We disable the autofinish functionality for modules other than quiz or external quiz. - $autofinshallowed = array('course', 'quiz', 'externalquiz'); + $autofinshallowed = ['course', 'quiz', 'externalquiz']; if (!in_array($this->_customdata['appliesto'], $autofinshallowed)) { // We use disbaleIf function without dependency and conditions, because // other modules apart from 'quiz and 'externalquiz' do not support the functionality. @@ -112,6 +115,7 @@ protected function definition() { $this->add_action_buttons(); } + #[\Override] public function validation($data, $files) { $errors = parent::validation($data, $files); diff --git a/editsteps.php b/editsteps.php index ae4fe4a..97da607 100644 --- a/editsteps.php +++ b/editsteps.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/adminlib.php'); @@ -41,7 +42,7 @@ $workflow = new block_workflow_workflow($workflowid); // Set the returnurl as we'll use this in a few places. -$returnurl = new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $workflowid)); +$returnurl = new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $workflowid]); // Set various page settings. $title = get_string('editworkflow', 'block_workflow', $workflow->name); diff --git a/edittask.php b/edittask.php index 18b88a5..13447ef 100644 --- a/edittask.php +++ b/edittask.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once(dirname(__FILE__) . '/edittask_form.php'); @@ -40,15 +41,15 @@ if ($taskid) { // An existing task was specified. $task->load_by_id($taskid); - $returnurl = new moodle_url('/blocks/workflow/editstep.php', array('stepid' => $task->stepid)); + $returnurl = new moodle_url('/blocks/workflow/editstep.php', ['stepid' => $task->stepid]); $PAGE->set_title(get_string('edittask', 'block_workflow', $task->task)); - $PAGE->set_url('/blocks/workflow/edittask.php', array('taskid' => $taskid)); + $PAGE->set_url('/blocks/workflow/edittask.php', ['taskid' => $taskid]); $data = (object) $task; } else { // Creating a new task. We require the stepid. $stepid = required_param('stepid', PARAM_INT); $step = new block_workflow_step($stepid); - $returnurl = new moodle_url('/blocks/workflow/editstep.php', array('stepid' => $step->id)); + $returnurl = new moodle_url('/blocks/workflow/editstep.php', ['stepid' => $step->id]); $PAGE->set_title(get_string('createtask', 'block_workflow', $step->name)); $PAGE->set_url('/blocks/workflow/edittask.php'); $data = new stdClass(); diff --git a/edittask_form.php b/edittask_form.php index 454ddfa..7a705cf 100644 --- a/edittask_form.php +++ b/edittask_form.php @@ -26,7 +26,14 @@ require_once($CFG->libdir . '/formslib.php'); +/** + * Form definition for editing a workflow task in the Moodle block_workflow plugin. + * + * This class extends the moodleform base class to provide fields and validation + * for editing or creating workflow tasks within the workflow block. + */ class task_edit extends moodleform { + #[\Override] protected function definition() { $mform = $this->_form; $mform->addElement('text', 'task', get_string('task', 'block_workflow')); diff --git a/export.php b/export.php index 9664fa9..0ec8a53 100644 --- a/export.php +++ b/export.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/filelib.php'); @@ -58,7 +59,7 @@ // Create a list of the workflow's steps. // We need to store any used templates here. -$templatelist = array(); +$templatelist = []; foreach ($workflow->steps() as $step) { // Load the object as we'll need to call some of it's functions. $step = new block_workflow_step($step->id); @@ -90,7 +91,7 @@ // As we loop through each step, we need to check each script for use // of e-mail emails and retrieve those for later inclusion. - $commands = array(); + $commands = []; $script = block_workflow_step::parse_script($step->onactivescript); $commands = array_merge($commands, $script->commands); @@ -107,7 +108,7 @@ $d = $class->parse($c->arguments, $step); // And retrieve the template. - $errors = array(); + $errors = []; $t = $class->email($d->emailname, $errors); $templatelist[$d->emailname] = $t; } @@ -143,5 +144,5 @@ * @return string The checked and (potentially) modified text */ function check_output_text($raw) { - return htmlspecialchars($raw, ENT_NOQUOTES, 'UTF-8'); + return htmlspecialchars($raw ?? '', ENT_NOQUOTES, 'UTF-8'); } diff --git a/finishstep.php b/finishstep.php index 8a9c6ae..30b2206 100644 --- a/finishstep.php +++ b/finishstep.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once(dirname(__FILE__) . '/finishstep_form.php'); @@ -47,12 +48,12 @@ block_workflow_can_make_changes($state); // Set the page URL. -$PAGE->set_url('/blocks/workflow/finishstep.php', array('stateid' => $stateid)); +$PAGE->set_url('/blocks/workflow/finishstep.php', ['stateid' => $stateid]); $PAGE->set_pagelayout('standard'); $PAGE->set_course($course); // Set the heading and page title. -$tparams = array('stepname' => $state->step()->name, 'contextname' => $context->get_context_name()); +$tparams = ['stepname' => $state->step()->name, 'contextname' => $context->get_context_name()]; $title = get_string('finishstepfor', 'block_workflow', $tparams); $PAGE->set_heading($title); $PAGE->set_title($title); @@ -63,7 +64,7 @@ $PAGE->navbar->add($state->step()->name); // Moodle form to update the state comment. -$mform = new state_finishtask(null, array('state' => $state)); +$mform = new state_finishtask(null, ['state' => $state]); // Grab a returnurl which relates to the context. $returnurl = $context->get_url(); @@ -85,7 +86,7 @@ $data->workflowname = $state->step()->workflow()->name; $data->stepname = $state->step()->name; $data->instructions = $state->step()->instructions; -$data = file_prepare_standard_editor($data, 'comment', array()); +$data = file_prepare_standard_editor($data, 'comment', []); $mform->set_data($data); diff --git a/finishstep_form.php b/finishstep_form.php index d268e72..05df667 100644 --- a/finishstep_form.php +++ b/finishstep_form.php @@ -14,20 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Form for task finishing - * - * @package block_workflow - * @copyright 2011 Lancaster University Network Services Limited - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/formslib.php'); +/** + * Form class for finishing a workflow step in the Moodle block_workflow plugin. + * + * This class defines the form used to complete a workflow step. + * + * @package block_workflow + * @copyright 2025 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class state_finishtask extends moodleform { + + #[\Override] protected function definition() { $mform = $this->_form; $state = $this->_customdata['state']; diff --git a/import.php b/import.php index 719f77a..8af46b7 100644 --- a/import.php +++ b/import.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once(dirname(__FILE__) . '/import_form.php'); @@ -105,7 +106,7 @@ $workflow = $workflow->create_workflow($workflowx, false, true); // Check whether the steps are incorrectly ordered and sort them. - $steporder = array(); + $steporder = []; foreach ($xml->steps->step as $importedstep) { $attributes = $importedstep->attributes(); $stepno = (string)$attributes['no']; @@ -200,7 +201,7 @@ $transaction->allow_commit(); // Redirect. - redirect(new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $workflow->id)), + redirect(new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $workflow->id]), $extramessage . get_string('importsuccess', 'block_workflow'), 10); } diff --git a/import_form.php b/import_form.php index 3d7b425..98c7b67 100644 --- a/import_form.php +++ b/import_form.php @@ -27,14 +27,19 @@ require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/formslib.php'); +/** + * Class import_workflow + */ class import_workflow extends moodleform { + + #[\Override] protected function definition() { $mform = $this->_form; $mform->addElement('header', 'general', get_string('workflowimport', 'block_workflow')); $mform->addElement('filepicker', 'importfile', get_string('importfile', 'block_workflow'), - null, array('accepted_type' => '*.xml')); + null, ['accepted_type' => '*.xml']); $mform->addRule('importfile', null, 'required', null, 'client'); $this->add_action_buttons(true, get_string('importworkflow', 'block_workflow')); diff --git a/jumptostep.php b/jumptostep.php index b50e32e..42fedee 100644 --- a/jumptostep.php +++ b/jumptostep.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); @@ -55,12 +56,12 @@ $step = new block_workflow_step($stepid); // Set the page URL. -$PAGE->set_url('/blocks/workflow/jumptostep.php', array('contextid' => $contextid, 'stepid' => $stepid)); +$PAGE->set_url('/blocks/workflow/jumptostep.php', ['contextid' => $contextid, 'stepid' => $stepid]); $PAGE->set_pagelayout('standard'); $PAGE->set_course($course); // Set the heading and page title. -$tparams = array('stepname' => $step->name, 'contextname' => $context->get_context_name()); +$tparams = ['stepname' => $step->name, 'contextname' => $context->get_context_name()]; $title = get_string('jumptostepon', 'block_workflow', $tparams); $PAGE->set_heading($title); $PAGE->set_title($title); @@ -86,7 +87,7 @@ } // Generate the confirmation message. -$strparams = array(); +$strparams = []; $strparams['fromstep'] = $state->step()->name; $strparams['tostep'] = $step->name; $strparams['workflowon'] = $context->get_context_name(); @@ -97,7 +98,7 @@ // Generate the confirmation button. $confirmurl = new moodle_url('/blocks/workflow/jumptostep.php', - array('contextid' => $contextid, 'stepid' => $stepid, 'confirm' => 1)); + ['contextid' => $contextid, 'stepid' => $stepid, 'confirm' => 1]); $confirmbutton = new single_button($confirmurl, get_string('confirm'), 'post'); echo $OUTPUT->header(); diff --git a/lang/en/block_workflow.php b/lang/en/block_workflow.php index 26e7a8a..14c8603 100644 --- a/lang/en/block_workflow.php +++ b/lang/en/block_workflow.php @@ -23,13 +23,9 @@ */ // Core strings. -$string['workflow:addinstance'] = 'Add a new workflow block'; -$string['pluginname'] = 'Workflows'; -$string['workflow'] = 'Workflow'; - $string['activetasktitle'] = 'Currently active task'; -$string['addaworkflow'] = 'Add a workflow'; $string['addanotherworkflow'] = 'Add another workflow'; +$string['addaworkflow'] = 'Add a workflow'; $string['addemail'] = 'Add email template'; $string['addroletostep'] = 'Add role to step'; $string['addstep'] = 'Add an additional step to this workflow'; @@ -66,11 +62,11 @@ $string['confirmstepdeletetitle'] = 'Delete step \'{$a}\'?'; $string['confirmworkflowdeletetitle'] = 'Delete workflow \'{$a}\'?'; $string['contexthasactiveworkflow'] = 'This context already has an active workflow.'; +$string['coursestartdate'] = 'the course start date'; $string['coursestudentclose'] = 'the course is closed to students'; $string['coursestudentopen'] = 'the course is opened to students'; $string['coursetutorclose'] = 'the course is closed to tutors'; $string['coursetutoropen'] = 'the course is opened to tutors'; -$string['coursestartdate'] = 'the course start date'; $string['create'] = 'Create'; $string['createemail'] = 'Create new email template'; $string['createstep'] = 'Create step'; @@ -82,12 +78,12 @@ $string['crontaskautostepfinisher'] = 'Workflow step finisher'; $string['crontaskextranotify'] = 'Workflow step extra notify'; $string['currentlyinuseby'] = 'This workflow is currently in use by'; +$string['dayafter'] = '{$a} day after'; +$string['dayas'] = 'same day as'; +$string['daybefore'] = '{$a} day before'; $string['days'] = 'Days'; -$string['dayas'] = 'same day as'; -$string['dayafter'] = '{$a} day after'; $string['daysafter'] = '{$a} days after'; -$string['daybefore'] = '{$a} day before'; $string['daysbefore'] = '{$a} days before'; $string['defaultonactivescript'] = '# You may place a set of actions to complete when marking this step active here'; $string['defaultoncompletescript'] = '# You may place a set of actions to complete when marking this step finished here'; @@ -129,6 +125,7 @@ $string['editworkflow'] = 'Editing workflow \'{$a}\''; $string['editworkflowinstructions'] = 'Edit workflow instructions'; $string['emaildescription'] = 'E-mail templates may be used by the various scripts in a workflow step'; +$string['emailfailed'] = 'Failed send email \'{$a->subject}\' to {$a->email}'; $string['emailfrom'] = '{$a} workflow system'; $string['emaillist'] = 'Email email templates'; $string['emailmessage'] = 'Message'; @@ -161,14 +158,16 @@ $string['eventstepaborted'] = 'Step aborted'; $string['eventstepactivated'] = 'Step activated'; $string['eventstepcompleted'] = 'Step completed'; -$string['eventtodotriggered'] = 'Todo triggered'; $string['eventstepextranotification'] = 'Step extra notification processed'; +$string['eventtodotriggered'] = 'Todo triggered'; +$string['eventworkflownotification'] = 'Workflow notification'; $string['export'] = 'Export'; $string['exportworkflow'] = 'Export workflow'; $string['finish'] = 'Finish'; $string['finishstep'] = 'Finish step'; $string['finishstepautomatically'] = 'This step was automatically finished by workflow system at {$a}.'; + $string['finishstepfor'] = 'Finish step \'{$a->stepname}\' on {$a->contextname}'; $string['finishstepinstructions'] = 'You are about to mark this step as complete, and move to the next step in the workflow. A summary of the step is shown below -- you may wish to update the comment below.'; $string['format_html'] = 'html'; @@ -180,8 +179,11 @@ $string['hidetask'] = 'Disable task'; $string['importfile'] = 'File'; + $string['importsuccess'] = 'Importing was successful. You will be redirected to workflow editing page shortly.'; + $string['importworkflow'] = 'Import workflow'; + $string['instructions'] = 'Instructions'; $string['inuseby'] = 'It is currently in use in {$a} locations.'; $string['invalidactivitysettingcolumn'] = 'The column specified ({$a}) does not exist.'; @@ -223,6 +225,7 @@ $string['jumpstep'] = 'Jump to step'; $string['jumptostep'] = 'Jump to step'; $string['jumptostepcheck'] = 'Are you sure you wish to jump from step \'{$a->fromstep}\' to step \'{$a->tostep}\' for the workflow on {$a->workflowon}?'; + $string['jumptostepcommentaddition'] = '

[Note: the workflow just jumped from step \'{$a->fromstep}\'. This comment may seem out-of-context.]

{$a->comment}'; $string['jumptostepon'] = 'Jump to step \'{$a->stepname}\' on {$a->contextname}'; $string['jumptosteptitle'] = 'Jump to step \'{$a->tostep}\' for \'{$a->workflowon}\' confirmation'; @@ -231,7 +234,9 @@ $string['managedescription'] = 'On this page you can create end edit workflows and the email templates that they use.'; $string['manageemails'] = 'Manage email templates'; + $string['manageworkflows'] = 'Manage workflows'; + $string['messageprovider:notification'] = 'Workflow notifications and alerts'; $string['missingfield'] = 'The required field is missing: {$a}'; $string['movedown'] = 'Move down'; @@ -240,6 +245,7 @@ $string['name'] = 'Name'; $string['nameinuse'] = 'The name specified is already in use. Names must be unique'; $string['nameshortname'] = '{$a->name} ({$a->shortname})'; + $string['noactiveworkflow'] = 'There is currently no active workflow.'; $string['nocomment'] = 'No comment yet...'; $string['nocomments'] = 'No comments have been made about this step yet'; @@ -267,23 +273,21 @@ $string['overviewtitle'] = 'Overview of {$a->workflowname} workflow on {$a->contexttitle}'; $string['percentcomplete'] = '{$a}% complete'; +$string['pluginname'] = 'Workflows'; +$string['privacy:metadata'] = 'The Calendar block only displays existing calendar data.'; -// GDPR. $string['privacy:metadata:block_workflow_state_changes'] = 'Changes to states.'; -$string['privacy:metadata:block_workflow_state_changes:userid'] = 'The user who caused the state change.'; $string['privacy:metadata:block_workflow_state_changes:newstate'] = 'The state that this step changed to.'; - +$string['privacy:metadata:block_workflow_state_changes:userid'] = 'The user who caused the state change.'; $string['privacy:metadata:block_workflow_todo_done'] = 'Changes to each step in the workflow.'; -$string['privacy:metadata:block_workflow_todo_done:userid'] = 'The user who caused the state change.'; -$string['privacy:metadata:block_workflow_todo_done:steptodoid'] = 'The todo step that user changed to.'; -$string['privacy:metadata'] = 'The Calendar block only displays existing calendar data.'; -$string['privacy_you'] = 'You'; +$string['privacy:metadata:block_workflow_todo_done:steptodoid'] = 'The todo step that user changed to.'; +$string['privacy:metadata:block_workflow_todo_done:userid'] = 'The user who caused the state change.'; $string['privacy_somebodyelse'] = 'Somebody else'; -// End GDPR. +$string['privacy_you'] = 'You'; -$string['quizopendate'] = 'the quiz open date'; $string['quizclosedate'] = 'the quiz close date'; +$string['quizopendate'] = 'the quiz open date'; $string['remove'] = 'Remove'; $string['removerolefromstep'] = 'Remove role from step'; @@ -346,14 +350,16 @@ $string['vieweditemail'] = 'View/Edit email'; $string['vieweditworkflow'] = 'View/Edit workflow'; +$string['workflow'] = 'Workflow'; +$string['workflow:addinstance'] = 'Add a new workflow block'; $string['workflow:dostep'] = 'Permission to perform a step'; $string['workflow:editdefinitions'] = 'Permission to edit workflow details'; $string['workflow:manage'] = 'Permission to manage workflows'; $string['workflow:view'] = 'Permission to view workflow information'; $string['workflowactive'] = 'This workflow is currently enabled (disable it). '; -$string['workflowalreadyset'] = 'A workflow has already been set for this step. Steps cannot be reassigned to a different workflow'; $string['workflowalreadyassigned'] = 'A workflow is already assigned to this context. Only one workflow may be assigned to any one context at a time.'; +$string['workflowalreadyset'] = 'A workflow has already been set for this step. Steps cannot be reassigned to a different workflow'; $string['workflowimport'] = 'Workflow importing'; $string['workflowinformation'] = 'Workflow information'; $string['workflowlist'] = 'Workflows'; diff --git a/lib.php b/lib.php new file mode 100644 index 0000000..1924815 --- /dev/null +++ b/lib.php @@ -0,0 +1,39 @@ +. + +/** + * Library for block workflow + * + * All the core Moodle functions, neeeded to allow the module to work + * integrated in Moodle should be placed here. + * + * @package block_workflow + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use block_workflow\local\forms\comment_form; + +/** + * Function to get comment form fragment. + * + * @param array $args Arguments for form. + * @return string HTML content. + */ +function block_workflow_output_fragment_commentform(array $args): string { + $mform = new comment_form(null, ['inputLabel' => $args['inputLabel']]); + return html_writer::div($mform->render(), 'block-workflow-panel'); +} diff --git a/locallib.php b/locallib.php index c384e72..d0316c0 100644 --- a/locallib.php +++ b/locallib.php @@ -90,10 +90,10 @@ function block_workflow_load_workflows() { */ function block_workflow_appliesto_list() { // Applies to should contain courses ... - $return = array('course' => get_string('course')); + $return = ['course' => get_string('course')]; // ... and any installed modules. - $mods = get_plugin_list('mod'); + $mods = core_component::get_plugin_list('mod'); foreach ($mods as $name => $path) { $return[$name] = get_string('pluginname', 'mod_' . $name); } @@ -138,7 +138,7 @@ function block_workflow_contextlevel_roles($contextlevel) { WHERE cl.contextlevel = ? ORDER BY roles.sortorder ASC "; - return role_fix_names($DB->get_records_sql($sql, array($contextlevel))); + return role_fix_names($DB->get_records_sql($sql, [$contextlevel])); } /** @@ -152,7 +152,7 @@ function block_workflow_contextlevel_roles($contextlevel) { * @return array Containing a list of default properties */ function block_workflow_editor_options() { - $options = array(); + $options = []; // Disallow files. $options['maxfiles'] = 0; @@ -195,10 +195,10 @@ function block_workflow_editor_format($type) { * @return int The editor format */ function block_workflow_convert_editor_format($format) { - $knownformats = array( + $knownformats = [ get_string('format_html', 'block_workflow') => FORMAT_HTML, get_string('format_plain', 'block_workflow') => FORMAT_PLAIN, - ); + ]; if (isset($knownformats[$format])) { return $knownformats[$format]; } else { @@ -218,7 +218,7 @@ function block_workflow_convert_editor_format($format) { function block_workflow_can_make_changes($state) { global $USER; - static $canmakechanges = array(); + static $canmakechanges = []; $context = $state->context(); @@ -270,9 +270,9 @@ function block_workflow_get_active_steps_with_fields_not_null($stepoptions) { AND (ctx.contextlevel = :coursecotext OR ctx.contextlevel = :modulecontext) ORDER BY state.id ASC"; - $options = array('state' => BLOCK_WORKFLOW_STATE_ACTIVE, + $options = ['state' => BLOCK_WORKFLOW_STATE_ACTIVE, 'coursecotext' => CONTEXT_COURSE, - 'modulecontext' => CONTEXT_MODULE); + 'modulecontext' => CONTEXT_MODULE]; return $DB->get_records_sql($sql, $options); } @@ -296,15 +296,15 @@ function block_workflow_get_offset_time($courseshortname, $courseid, $moduleid, SELECT MIN($dbfield) FROM $table WHERE vle_course_short_name = ? - ", array($courseshortname)); + ", [$courseshortname]); $timestamp = 0; if ($date) { $timestamp = strtotime($date); } } else if ($dbtable === 'course') { - $timestamp = $DB->get_field('course', $dbfield, array('id' => $courseid)); + $timestamp = $DB->get_field('course', $dbfield, ['id' => $courseid]); } else { - $timestamp = $DB->get_field($dbtable, $dbfield, array('id' => $moduleid)); + $timestamp = $DB->get_field($dbtable, $dbfield, ['id' => $moduleid]); } if ($timestamp) { return $timestamp + $offset; @@ -318,7 +318,7 @@ function block_workflow_get_offset_time($courseshortname, $courseid, $moduleid, * @return void */ function block_workflow_send_extra_notification() { - $options = array('extranotify', 'extranotifyoffset', 'onextranotifyscript'); + $options = ['extranotify', 'extranotifyoffset', 'onextranotifyscript']; $activesteps = block_workflow_get_active_steps_with_fields_not_null($options); if (!$activesteps) { @@ -343,7 +343,7 @@ function block_workflow_send_extra_notification() { } // Cron setup user. - cron_setup_user(); + \core\cron::setup_user(); } } catch (Exception $e) { block_workflow_report_scheduled_task_error('send extra notifications', $e, $activestep); @@ -356,7 +356,7 @@ function block_workflow_send_extra_notification() { * @return void */ function block_workflow_autofinish_steps() { - $options = array('autofinish', 'autofinishoffset', null); + $options = ['autofinish', 'autofinishoffset', null]; $activesteps = block_workflow_get_active_steps_with_fields_not_null($options); if (!$activesteps) { @@ -379,7 +379,7 @@ function block_workflow_autofinish_steps() { $state->finish_step($newcomment, FORMAT_HTML); // Cron setup user. - cron_setup_user(); + \core\cron::setup_user(); } } catch (Exception $e) { block_workflow_report_scheduled_task_error( diff --git a/manage.php b/manage.php index addc713..7575cd5 100644 --- a/manage.php +++ b/manage.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/adminlib.php'); diff --git a/movestep.php b/movestep.php index 915ad16..08393ae 100644 --- a/movestep.php +++ b/movestep.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); @@ -39,10 +40,10 @@ $PAGE->set_context(context_system::instance()); $PAGE->set_pagelayout('standard'); -$PAGE->set_url('/blocks/workflow/movestep.php', array('stepid' => $stepid, 'direction' => $direction)); +$PAGE->set_url('/blocks/workflow/movestep.php', ['stepid' => $stepid, 'direction' => $direction]); // Grab the retrieve. -$returnurl = new moodle_url('/blocks/workflow/editsteps.php', array('workflowid' => $step->workflowid)); +$returnurl = new moodle_url('/blocks/workflow/editsteps.php', ['workflowid' => $step->workflowid]); // Work out what we'll be waspping with. $stepno = $step->stepno; diff --git a/overview.php b/overview.php index 4426e23..b7c428f 100644 --- a/overview.php +++ b/overview.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); @@ -44,14 +45,14 @@ require_capability('block/workflow:view', $PAGE->context); // Set the page URL. -$PAGE->set_url('/blocks/workflow/overview.php', array('contextid' => $contextid, 'workflowid' => $workflowid)); +$PAGE->set_url('/blocks/workflow/overview.php', ['contextid' => $contextid, 'workflowid' => $workflowid]); $PAGE->set_pagelayout('standard'); $PAGE->set_course($course); // Grab the workflow and states. $workflow = new block_workflow_workflow($workflowid); $stepstates = $workflow->step_states($contextid, $workflowid); -$tparams = array('contexttitle' => $context->get_context_name(), 'workflowname' => $workflow->name); +$tparams = ['contexttitle' => $context->get_context_name(), 'workflowname' => $workflow->name]; // Check that this workflow is assigned to this context. $statelist = array_filter($stepstates, function ($a) { diff --git a/removeworkflow.php b/removeworkflow.php index b07a9fb..992ce53 100644 --- a/removeworkflow.php +++ b/removeworkflow.php @@ -21,6 +21,7 @@ * @copyright 2011 Lancaster University Network Services Limited * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/locallib.php'); require_once($CFG->libdir . '/adminlib.php'); @@ -48,11 +49,11 @@ // Set various page options. $PAGE->set_pagelayout('standard'); $PAGE->set_course($course); -$PAGE->set_url('/blocks/workflow/removeworkflow.php', array('workflowid' => $workflowid, 'contextid' => $contextid)); +$PAGE->set_url('/blocks/workflow/removeworkflow.php', ['workflowid' => $workflowid, 'contextid' => $contextid]); // Grab the workflow. $workflow = new block_workflow_workflow($workflowid); -$tparams = array('workflowname' => $workflow->name, 'contexttitle' => $context->get_context_name()); +$tparams = ['workflowname' => $workflow->name, 'contexttitle' => $context->get_context_name()]; // Check that this workflow is assigned to this context. $stepstates = $workflow->step_states($contextid); @@ -77,9 +78,9 @@ // The confirmation strings. $confirmstr = get_string('removeworkflowcheck', 'block_workflow', $tparams); $confirmurl = new moodle_url('/blocks/workflow/removeworkflow.php', - array('workflowid' => $workflowid, 'contextid' => $contextid, 'confirm' => 1)); + ['workflowid' => $workflowid, 'contextid' => $contextid, 'confirm' => 1]); $returnurl = new moodle_url('/blocks/workflow/overview.php', - array('workflowid' => $workflowid, 'contextid' => $contextid)); + ['workflowid' => $workflowid, 'contextid' => $contextid]); if ($confirm) { // Confirm the session key to stop CSRF. diff --git a/renderer.php b/renderer.php index 7ab6409..3e55ff5 100644 --- a/renderer.php +++ b/renderer.php @@ -36,10 +36,11 @@ class block_workflow_renderer extends plugin_renderer_base { /** * Render the block for the specified state * - * @param object $state The block_workflow_step_state to render for - * @return string The rendered content + * @param object $state The block_workflow_step_state to render for + * @param bool $renderforajax are we rendering this for an ajx request? (Default false: display directly in the block.) + * @return string The rendered HTML content */ - public function block_display(block_workflow_step_state $state, $ajax = false) { + public function block_display(block_workflow_step_state $state, $renderforajax = false) { global $USER; $canmakechanges = block_workflow_can_make_changes($state); @@ -57,7 +58,7 @@ public function block_display(block_workflow_step_state $state, $ajax = false) { $output .= html_writer::tag('h3', get_string('tobecompletedby', 'block_workflow')); $who = ''; - $whoelse = array(); + $whoelse = []; // Got through the list. foreach ($roles as $role) { @@ -88,7 +89,7 @@ public function block_display(block_workflow_step_state $state, $ajax = false) { $output .= html_writer::tag('span', $who); $output .= $this->get_popup_button($roles, $context); - $this->page->requires->yui_module('moodle-block_workflow-userinfo', 'M.block_workflow.userinfo.init'); + $this->page->requires->js_call_amd('block_workflow/userinfo', 'init'); } // Instructions. @@ -98,9 +99,9 @@ public function block_display(block_workflow_step_state $state, $ajax = false) { // Comments. $output .= html_writer::tag('h3', get_string('comments', 'block_workflow')); - $commentsblock = html_writer::start_tag('div', array('class' => 'block_workflow_comments')); + $commentsblock = html_writer::start_tag('div', ['class' => 'block_workflow_comments']); $commenttext = shorten_text(format_text($state->comment, $state->commentformat, - array('context' => $state->context())), BLOCK_WORKFLOW_MAX_COMMENT_LENGTH); + ['context' => $state->context()]), BLOCK_WORKFLOW_MAX_COMMENT_LENGTH); if ($commenttext) { $commentsblock .= $commenttext; } else { @@ -112,7 +113,7 @@ public function block_display(block_workflow_step_state $state, $ajax = false) { // To-do list overview. if ($todos = $state->todos()) { $output .= html_writer::tag('h3', get_string('todolisttitle', 'block_workflow')); - $list = html_writer::start_tag('ul', array('class' => 'block_workflow_todolist')); + $list = html_writer::start_tag('ul', ['class' => 'block_workflow_todolist']); foreach ($state->todos() as $todo) { $list .= $this->block_display_todo_item($todo, $state->id, $canmakechanges); } @@ -123,40 +124,25 @@ public function block_display(block_workflow_step_state $state, $ajax = false) { if ($canmakechanges) { // Edit comments. $url = new moodle_url('/blocks/workflow/editcomment.php', - array('stateid' => $state->id)); + ['stateid' => $state->id]); $editbutton = new single_button($url, get_string('editcomments', 'block_workflow'), 'get'); $editbutton->class = 'singlebutton block_workflow_editcommentbutton'; $output .= html_writer::tag('div', $this->output->render($editbutton)); - if (!$ajax) { - // Output the contents of the edit comment dialogue, hidden. - // Prepare editor. - $editor = new MoodleQuickForm_editor('comment_editor', get_string('commentlabel', 'block_workflow'), - array('id' => 'wkf-comment-editor'), block_workflow_editor_options()); - $editor->setValue(array('text' => $state->comment)); - - $output .= '
-
-
' . - html_writer::label(get_string('commentlabel', 'block_workflow'), - 'wkf-comment-editor', false, array('class' => 'accesshide')) . - $editor->toHtml() . ' -
-
- -
-
-