From e861febc6c07e5e113960a7c2dff564c686860b2 Mon Sep 17 00:00:00 2001 From: Max Larkin Date: Thu, 12 Sep 2024 15:48:49 +0100 Subject: [PATCH] MDL-78337 tool_brickfield: Validate registration in adhoc task for realtime feedback --- .../brickfield/amd/build/registration.min.js | 10 +++ .../amd/build/registration.min.js.map | 1 + admin/tool/brickfield/amd/src/registration.js | 70 ++++++++++++++++ .../external/check_toolkit_validation.php | 82 +++++++++++++++++++ .../tool/brickfield/classes/registration.php | 23 ++++++ .../classes/task/validate_registration.php | 34 ++++++++ admin/tool/brickfield/db/services.php | 47 +++++++++++ .../brickfield/lang/en/tool_brickfield.php | 2 +- admin/tool/brickfield/registration.php | 6 +- admin/tool/brickfield/version.php | 2 +- 10 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 admin/tool/brickfield/amd/build/registration.min.js create mode 100644 admin/tool/brickfield/amd/build/registration.min.js.map create mode 100644 admin/tool/brickfield/amd/src/registration.js create mode 100644 admin/tool/brickfield/classes/external/check_toolkit_validation.php create mode 100644 admin/tool/brickfield/classes/task/validate_registration.php create mode 100644 admin/tool/brickfield/db/services.php diff --git a/admin/tool/brickfield/amd/build/registration.min.js b/admin/tool/brickfield/amd/build/registration.min.js new file mode 100644 index 0000000000000..88e1936700f1d --- /dev/null +++ b/admin/tool/brickfield/amd/build/registration.min.js @@ -0,0 +1,10 @@ +define("tool_brickfield/registration",["exports","core/ajax","core/templates","core/log"],(function(_exports,_ajax,_templates,_log){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +/** + * Handles polling registration status and replaces the alert block with the new one. + * + * @module tool_brickfield/registration + * @copyright 2024 onward Brickfield Education Labs Ltd, https://www.brickfield.ie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function init(){_ajax.default.call([{methodname:"tool_brickfield_check_toolkit_validation",args:{},done:data=>{-1==data.status?setTimeout((()=>{init()}),2e3):replaceAlertBlock(data.message,data.level)},fail:err=>{_log.default.error(err)}}])},_ajax=_interopRequireDefault(_ajax),_templates=_interopRequireDefault(_templates),_log=_interopRequireDefault(_log);const replaceAlertBlock=(message,level)=>{_templates.default.render("core/notification",{message:message,issuccess:"success"==level,iswarning:"warning"==level,iserror:"error"==level,closebutton:!0}).then((html=>{const oldAlert=document.querySelector(".alert-block");let temp=document.createElement("div");temp.innerHTML=html;let newAlert=temp.firstChild;oldAlert.parentNode.insertBefore(newAlert,oldAlert),oldAlert.parentNode.removeChild(oldAlert)})).catch((err=>{_log.default.error(err)}))}})); + +//# sourceMappingURL=registration.min.js.map \ No newline at end of file diff --git a/admin/tool/brickfield/amd/build/registration.min.js.map b/admin/tool/brickfield/amd/build/registration.min.js.map new file mode 100644 index 0000000000000..1a0c47c24ee54 --- /dev/null +++ b/admin/tool/brickfield/amd/build/registration.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"registration.min.js","sources":["../src/registration.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 Ajax from 'core/ajax';\nimport Template from 'core/templates';\nimport Log from 'core/log';\n\n/**\n * Handles polling registration status and replaces the alert block with the new one.\n *\n * @module tool_brickfield/registration\n * @copyright 2024 onward Brickfield Education Labs Ltd, https://www.brickfield.ie\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport function init() {\n Ajax.call([{\n methodname: 'tool_brickfield_check_toolkit_validation',\n args: {},\n done: (data) => {\n if (data.status == -1) {\n // Status of -1 indicates the registration adhoc task is running.\n setTimeout(() => {\n init();\n }, 2000);\n } else {\n replaceAlertBlock(data.message, data.level);\n }\n },\n fail: (err) => {\n Log.error(err);\n }\n }]);\n}\n\n/**\n * Replaces the existing alert block with a new one.\n * @param {string} message The notification message\n * @param {string} level The notification level\n */\nconst replaceAlertBlock = (message, level) => {\n Template.render('core/notification', {\n message: message,\n issuccess: level == 'success',\n iswarning: level == 'warning',\n iserror: level == 'error',\n closebutton: true\n }).then((html) => {\n const oldAlert = document.querySelector('.alert-block');\n let temp = document.createElement('div');\n temp.innerHTML = html;\n let newAlert = temp.firstChild;\n oldAlert.parentNode.insertBefore(newAlert, oldAlert);\n oldAlert.parentNode.removeChild(oldAlert);\n return;\n }).catch((err) => {\n Log.error(err);\n });\n};\n"],"names":["init","call","methodname","args","done","data","status","setTimeout","replaceAlertBlock","message","level","fail","err","error","render","issuccess","iswarning","iserror","closebutton","then","html","oldAlert","document","querySelector","temp","createElement","innerHTML","newAlert","firstChild","parentNode","insertBefore","removeChild","catch"],"mappings":";;;;;;;oFA0BgBA,qBACPC,KAAK,CAAC,CACPC,WAAY,2CACZC,KAAM,GACNC,KAAOC,QACiB,GAAhBA,KAAKC,OAELC,YAAW,KACPP,SACD,KAEHQ,kBAAkBH,KAAKI,QAASJ,KAAKK,QAG7CC,KAAOC,mBACCC,MAAMD,oIAUhBJ,kBAAoB,CAACC,QAASC,4BACvBI,OAAO,oBAAqB,CACjCL,QAASA,QACTM,UAAoB,WAATL,MACXM,UAAoB,WAATN,MACXO,QAAkB,SAATP,MACTQ,aAAa,IACdC,MAAMC,aACCC,SAAWC,SAASC,cAAc,oBACpCC,KAAOF,SAASG,cAAc,OAClCD,KAAKE,UAAYN,SACbO,SAAWH,KAAKI,WACpBP,SAASQ,WAAWC,aAAaH,SAAUN,UAC3CA,SAASQ,WAAWE,YAAYV,aAEjCW,OAAOpB,mBACFC,MAAMD"} \ No newline at end of file diff --git a/admin/tool/brickfield/amd/src/registration.js b/admin/tool/brickfield/amd/src/registration.js new file mode 100644 index 0000000000000..93f3066ca0a4f --- /dev/null +++ b/admin/tool/brickfield/amd/src/registration.js @@ -0,0 +1,70 @@ +// 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 Ajax from 'core/ajax'; +import Template from 'core/templates'; +import Log from 'core/log'; + +/** + * Handles polling registration status and replaces the alert block with the new one. + * + * @module tool_brickfield/registration + * @copyright 2024 onward Brickfield Education Labs Ltd, https://www.brickfield.ie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +export function init() { + Ajax.call([{ + methodname: 'tool_brickfield_check_toolkit_validation', + args: {}, + done: (data) => { + if (data.status == -1) { + // Status of -1 indicates the registration adhoc task is running. + setTimeout(() => { + init(); + }, 2000); + } else { + replaceAlertBlock(data.message, data.level); + } + }, + fail: (err) => { + Log.error(err); + } + }]); +} + +/** + * Replaces the existing alert block with a new one. + * @param {string} message The notification message + * @param {string} level The notification level + */ +const replaceAlertBlock = (message, level) => { + Template.render('core/notification', { + message: message, + issuccess: level == 'success', + iswarning: level == 'warning', + iserror: level == 'error', + closebutton: true + }).then((html) => { + const oldAlert = document.querySelector('.alert-block'); + let temp = document.createElement('div'); + temp.innerHTML = html; + let newAlert = temp.firstChild; + oldAlert.parentNode.insertBefore(newAlert, oldAlert); + oldAlert.parentNode.removeChild(oldAlert); + return; + }).catch((err) => { + Log.error(err); + }); +}; diff --git a/admin/tool/brickfield/classes/external/check_toolkit_validation.php b/admin/tool/brickfield/classes/external/check_toolkit_validation.php new file mode 100644 index 0000000000000..09d3958a1e619 --- /dev/null +++ b/admin/tool/brickfield/classes/external/check_toolkit_validation.php @@ -0,0 +1,82 @@ +. + +namespace tool_brickfield\external; + +use core\task\manager as taskmanager; +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_value; +use tool_brickfield\registration; +use tool_brickfield\manager; + +/** + * Service to check the status of the toolkit registrtion. + * + * @package tool_brickfield + * @copyright 2024 onward Brickfield Education Labs Ltd, https://www.brickfield.ie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class check_toolkit_validation extends \core_external\external_api { + + /** + * Returns description of method parameters + * @return external_function_parameters + */ + public static function execute_parameters() { + return new external_function_parameters([]); + } + + /** + * Check results of the adhoc task to check validation. + * + * @return bool + */ + public static function execute() { + $tasks = taskmanager::get_adhoc_tasks('\tool_brickfield\task\validate_registration', false, true); + if (empty($tasks)) { + $status = get_config('tool_brickfield', registration::STATUS); + if ($status == registration::NOT_ENTERED || $status == registration::EXPIRED || $status == registration::INVALID) { + $message = get_string('inactive', manager::PLUGINNAME); + $level = 'error'; + } else if ($status == registration::PENDING) { + $message = get_string('notvalidated', manager::PLUGINNAME); + $level = 'warning'; + } else if ($status == registration::VALIDATED) { + $message = get_string('activated', manager::PLUGINNAME); + $level = 'success'; + } else if ($status == registration::ERROR) { + $message = get_string('validationerror', manager::PLUGINNAME); + $level = 'error'; + } + return ['status' => $status, 'message' => $message, 'level' => $level]; + } else { + return ['status' => -1]; // Indicate task is still in progress. + } + } + + /** + * Returns description of method result value + * @return external_description + */ + public static function execute_returns() { + return new external_single_structure([ + 'status' => new external_value(PARAM_INT, 'Registration validation status'), + 'message' => new external_value(PARAM_TEXT, 'Status message', VALUE_OPTIONAL), + 'level' => new external_value(PARAM_TEXT, 'Status level', VALUE_OPTIONAL), + ]); + } +} diff --git a/admin/tool/brickfield/classes/registration.php b/admin/tool/brickfield/classes/registration.php index 4b4ee63c1adfe..5134836dc224c 100644 --- a/admin/tool/brickfield/classes/registration.php +++ b/admin/tool/brickfield/classes/registration.php @@ -444,6 +444,29 @@ protected function get_status(): int { return (int)$status; } + /** + * Return the current registration status as a string. + * + * @return array The status, label, and notification level. + */ + public function get_status_display(): array { + $status = $this->get_status(); + if ($status == self::NOT_ENTERED || $status == self::EXPIRED || $status == self::INVALID) { + $message = get_string('inactive', manager::PLUGINNAME); + $level = 'error'; + } else if ($status == self::PENDING) { + $message = get_string('notvalidated', manager::PLUGINNAME); + $level = 'warning'; + } else if ($status == self::VALIDATED) { + $message = get_string('activated', manager::PLUGINNAME); + $level = 'success'; + } else if ($status == self::ERROR) { + $message = get_string('validationerror', manager::PLUGINNAME); + $level = 'error'; + } + return ['status' => $status, 'message' => $message, 'level' => $level]; + } + /** * Set the time of the last registration check. * @param int $time diff --git a/admin/tool/brickfield/classes/task/validate_registration.php b/admin/tool/brickfield/classes/task/validate_registration.php new file mode 100644 index 0000000000000..2c9d7ed360d20 --- /dev/null +++ b/admin/tool/brickfield/classes/task/validate_registration.php @@ -0,0 +1,34 @@ +. + +namespace tool_brickfield\task; +use tool_brickfield\registration; + +/** + * Adhoc task to validate registration details. + * + * @package tool_brickfield + * @copyright 2024 onward Brickfield Education Labs Ltd, https://www.brickfield.ie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class validate_registration extends \core\task\adhoc_task { + /** + * Execute the task + */ + public function execute() { + (new registration())->validate(); + } +} diff --git a/admin/tool/brickfield/db/services.php b/admin/tool/brickfield/db/services.php new file mode 100644 index 0000000000000..4c277384a45a6 --- /dev/null +++ b/admin/tool/brickfield/db/services.php @@ -0,0 +1,47 @@ +. + +/** + * External functions and service declaration for Accessibility toolkit + * + * Documentation: {@link https://moodledev.io/docs/apis/subsystems/external/description} + * + * @package tool_brickfield + * @category webservice + * @copyright 2024 onward Brickfield Education Labs Ltd, https://www.brickfield.ie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$functions = [ + 'tool_brickfield_check_toolkit_validation' => [ + 'classname' => 'tool_brickfield\external\check_toolkit_validation', + 'methodname' => 'execute', + 'classpath' => 'classes/external/check_toolkit_validation.php', + 'description' => 'Service to check the status of the toolkit registration.', + 'type' => 'read', + 'ajax' => true, + ], +]; + +$services = [ + 'Accessibility toolkit' => [ + 'functions' => ['tool_brickfield_check_toolkit_validation'], + 'restrictedusers' => 0, + 'enabled' => 1, + ], +]; diff --git a/admin/tool/brickfield/lang/en/tool_brickfield.php b/admin/tool/brickfield/lang/en/tool_brickfield.php index ed0e4f2741031..e43eda70b3012 100644 --- a/admin/tool/brickfield/lang/en/tool_brickfield.php +++ b/admin/tool/brickfield/lang/en/tool_brickfield.php @@ -97,7 +97,7 @@ $string['noerrorsfound'] = 'No common accessibility errors were found for your search parameters. Congratulations!'; $string['norecords'] = 'No relevant records were found for your search parameters.'; $string['notregistered'] = 'Your accessibility toolkit needs to be registered.'; -$string['notvalidated'] = 'Your accessibility toolkit is functional while being validated.'; +$string['notvalidated'] = 'Your accessibility toolkit is functional while being validated. {$a}'; $string['numinstances'] = 'Number of instances'; $string['pagedesc:checktype'] = '

In order to summarise and analyse the results of the various checks conducted, we group these checks into different content types. Hence, all image-related accessibility check results are in the "Image" content type group, all layout-related accessibility check results are in the "Layout" content type group, and so on.

Activities are included as either activities, resources or content areas relating to the courses themselves.

The content type chart page displays the error breakdown per content type group: Image, Layout, Link, Media, Table, and Text.

'; $string['pagedesc:pertarget'] = '

In order to summarise and analyse the check results per activity, we group these check results into the different activities detected.

Activities are included as either activities, resources, or other content areas relating to the courses themselves. Each activity with no detected errors is counted as passed, each activity with one or more detected errors is counted as failed. The ratio of passed to failed activities is then displayed.

The activity breakdown chart page displays the ratio of passed to failed instances in total, per activity, such as assignment, course, label etc.

'; diff --git a/admin/tool/brickfield/registration.php b/admin/tool/brickfield/registration.php index f37aa07d7fab0..2f59bd1875116 100644 --- a/admin/tool/brickfield/registration.php +++ b/admin/tool/brickfield/registration.php @@ -70,7 +70,11 @@ if ($registration->validation_error()) { echo $OUTPUT->notification(get_string('validationerror', manager::PLUGINNAME), 'error'); } else { - echo $OUTPUT->notification(get_string('notvalidated', manager::PLUGINNAME), 'warning'); + echo $OUTPUT->notification(get_string('notvalidated', manager::PLUGINNAME, $OUTPUT->render_from_template('core/loading', [])), 'warning'); + // Start validation task and update page with JS. + $PAGE->requires->js_call_amd('tool_brickfield/registration', 'init'); + $task = new \tool_brickfield\task\validate_registration(); + \core\task\manager::queue_adhoc_task($task, true); } } else { echo $OUTPUT->notification(get_string('activated', manager::PLUGINNAME), 'success'); diff --git a/admin/tool/brickfield/version.php b/admin/tool/brickfield/version.php index 1dfd0ed6cedfd..ee201c7e512a1 100644 --- a/admin/tool/brickfield/version.php +++ b/admin/tool/brickfield/version.php @@ -25,5 +25,5 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'tool_brickfield'; -$plugin->version = 2024042200; +$plugin->version = 2024042203; $plugin->requires = 2024041600;