From 44cf5f4a587086663e385a8b39bbbbb11548a01c Mon Sep 17 00:00:00 2001 From: Henning Bostelmann Date: Wed, 14 Aug 2024 17:07:34 +0200 Subject: [PATCH] CONTRIB-8227 autocomplete boxes loaded via web service --- amd/build/studentid.min.js | 10 +++ amd/build/studentid.min.js.map | 1 + amd/src/studentid.js | 78 ++++++++++++++++++ classes/external.php | 118 ++++++++++++++++++++++++++++ db/services.php | 37 +++++++++ lib.php | 12 +++ slotforms.php | 14 ++-- templates/studentid.mustache | 52 ++++++++++++ tests/behat/behat_mod_scheduler.php | 6 +- version.php | 2 +- 10 files changed, 318 insertions(+), 12 deletions(-) create mode 100644 amd/build/studentid.min.js create mode 100644 amd/build/studentid.min.js.map create mode 100644 amd/src/studentid.js create mode 100644 classes/external.php create mode 100644 db/services.php create mode 100644 templates/studentid.mustache diff --git a/amd/build/studentid.min.js b/amd/build/studentid.min.js new file mode 100644 index 00000000..2f95f499 --- /dev/null +++ b/amd/build/studentid.min.js @@ -0,0 +1,10 @@ +/** + * Potential user selector module. + * + * @module mod_scheduler/studentid + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define("mod_scheduler/studentid",["jquery","core/ajax","core/templates"],(function($,Ajax,Templates){return{processResults:function(selector,results){var users=[];return $.each(results,(function(index,user){users.push({value:user.id,label:user._label})})),users},transport:function(selector,query,success,failure){let scheduler=$(selector).attr("scheduler")||null,groupids=$(selector).attr("groupids")||null;Ajax.call([{methodname:"mod_scheduler_studentid",args:{query:query,scheduler:scheduler,groupids:groupids}}])[0].then((function(results){var promises=[],i=0;return $.each(results,(function(index,user){promises.push(Templates.render("mod_scheduler/studentid",user))})),$.when.apply($.when,promises).then((function(){var args=arguments;$.each(results,(function(index,user){user._label=args[i],i++})),success(results)}))})).fail(failure)}}})); + +//# sourceMappingURL=studentid.min.js.map \ No newline at end of file diff --git a/amd/build/studentid.min.js.map b/amd/build/studentid.min.js.map new file mode 100644 index 00000000..515c5dde --- /dev/null +++ b/amd/build/studentid.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"studentid.min.js","sources":["../src/studentid.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Potential user selector module.\n *\n * @module mod_scheduler/studentid\n * @copyright 2022 University of Glasgow\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n\n return /** @alias module:mod_scheduler/studentid */ {\n\n processResults: function(selector, results) {\n var users = [];\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n\n let scheduler = $(selector).attr('scheduler') || null;\n let groupids = $(selector).attr('groupids') || null;\n promise = Ajax.call([{\n methodname: 'mod_scheduler_studentid',\n args: {\n query: query,\n scheduler: scheduler,\n groupids: groupids\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results, function(index, user) {\n promises.push(Templates.render('mod_scheduler/studentid', user));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results);\n return;\n });\n\n }).fail(failure);\n }\n\n };\n\n});"],"names":["define","$","Ajax","Templates","processResults","selector","results","users","each","index","user","push","value","id","label","_label","transport","query","success","failure","scheduler","attr","groupids","call","methodname","args","then","promises","i","render","when","apply","arguments","fail"],"mappings":";;;;;;;AAuBAA,iCAAO,CAAC,SAAU,YAAa,mBAAmB,SAASC,EAAGC,KAAMC,iBAEZ,CAEhDC,eAAgB,SAASC,SAAUC,aAC3BC,MAAQ,UACZN,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BH,MAAMI,KAAK,CACPC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,YAGbR,OAGXS,UAAW,SAASX,SAAUY,MAAOC,QAASC,aAGtCC,UAAYnB,EAAEI,UAAUgB,KAAK,cAAgB,KAC7CC,SAAWrB,EAAEI,UAAUgB,KAAK,aAAe,KACrCnB,KAAKqB,KAAK,CAAC,CACjBC,WAAY,0BACZC,KAAM,CACFR,MAAOA,MACPG,UAAWA,UACXE,SAAUA,aAIV,GAAGI,MAAK,SAASpB,aACjBqB,SAAW,GACXC,EAAI,SAGR3B,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BiB,SAAShB,KAAKR,UAAU0B,OAAO,0BAA2BnB,UAIvDT,EAAE6B,KAAKC,MAAM9B,EAAE6B,KAAMH,UAAUD,MAAK,eACnCD,KAAOO,UACX/B,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BA,KAAKK,OAASU,KAAKG,GACnBA,OAEJV,QAAQZ,eAIb2B,KAAKd"} \ No newline at end of file diff --git a/amd/src/studentid.js b/amd/src/studentid.js new file mode 100644 index 00000000..76d2dc27 --- /dev/null +++ b/amd/src/studentid.js @@ -0,0 +1,78 @@ +// 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 . + +/** + * Potential user selector module. + * + * @module mod_scheduler/studentid + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) { + + return /** @alias module:mod_scheduler/studentid */ { + + processResults: function(selector, results) { + var users = []; + $.each(results, function(index, user) { + users.push({ + value: user.id, + label: user._label + }); + }); + return users; + }, + + transport: function(selector, query, success, failure) { + var promise; + + let scheduler = $(selector).attr('scheduler') || null; + let groupids = $(selector).attr('groupids') || null; + promise = Ajax.call([{ + methodname: 'mod_scheduler_studentid', + args: { + query: query, + scheduler: scheduler, + groupids: groupids + } + }]); + + promise[0].then(function(results) { + var promises = [], + i = 0; + + // Render the label. + $.each(results, function(index, user) { + promises.push(Templates.render('mod_scheduler/studentid', user)); + }); + + // Apply the label to the results. + return $.when.apply($.when, promises).then(function() { + var args = arguments; + $.each(results, function(index, user) { + user._label = args[i]; + i++; + }); + success(results); + return; + }); + + }).fail(failure); + } + + }; + +}); \ No newline at end of file diff --git a/classes/external.php b/classes/external.php new file mode 100644 index 00000000..3c8ab315 --- /dev/null +++ b/classes/external.php @@ -0,0 +1,118 @@ +. + +/** + * This is the external API for this component. + * + * @package mod_scheduler + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_scheduler; + +defined('MOODLE_INTERNAL') || die(); + +require_once("$CFG->libdir/externallib.php"); + +use external_api; +use external_function_parameters; +use external_value; +use external_single_structure; +use external_multiple_structure; + +use \mod_scheduler\model\scheduler; + +/** + * This is the external API for this component. + * + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class external extends external_api { + + /** + * studentid parameters + * + * @return external_function_parameters + */ + public static function studentid_parameters() { + return new external_function_parameters([ + 'query' => new external_value(PARAM_TEXT, 'The search query', VALUE_REQUIRED), + 'scheduler' => new external_value(PARAM_INT, 'The scheduler id', VALUE_REQUIRED), + 'groupids' => new external_value(PARAM_INT, 'The group ids', VALUE_DEFAULT) + ]); + } + + /** + * Fetch the details of a user's data request. + * + * @since Moodle 3.5 + * @param string $query The search query. + * @param string $scheduler The scheduler id. + * @param string $groupids The group ids. + * @return array + * @throws required_capability_exception + * @throws dml_exception + * @throws invalid_parameter_exception + * @throws restricted_context_exception + */ + + public static function studentid($query, $scheduler, $groupids) { + $params = external_api::validate_parameters(self::studentid_parameters(), [ + 'query' => $query, + 'scheduler' => $scheduler, + 'groupids' => $groupids + ]); + $query = $params['query']; + $scheduler = $params['scheduler']; + $groupids = $params['groupids']; + + $scheduler = scheduler::load_by_id($scheduler); + $availablestudents = $scheduler->get_available_students(); + + $students = []; + $i = 0; + foreach ($availablestudents as $id => $student) { + $fullname = fullname($student); + + if (mb_stripos($fullname, $query) !== false) { + $students[] = ['id' => $id, 'fullname' => fullname($student)]; + $i++; + } + } + + return $students; + + } + + /** + * Parameter description for get_users(). + * + * @since Moodle 3.5 + * @return external_description + * @throws coding_exception + */ + public static function studentid_returns() { + return new external_multiple_structure( + new external_single_structure([ + 'id' => new external_value(PARAM_INT, 'User ID'), + 'fullname' => new external_value(PARAM_NOTAGS, 'User fullname') + ]) + ); + } + +} diff --git a/db/services.php b/db/services.php new file mode 100644 index 00000000..6faa22da --- /dev/null +++ b/db/services.php @@ -0,0 +1,37 @@ +. + +/** + * Mod Scheduler webservice definitions + * + * @package mod_scheduler + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$functions = array( + + 'mod_scheduler_studentid' => array( + 'classname' => 'mod_scheduler\external', + 'methodname' => 'studentid', + 'description' => 'Retrieve the list of potential studentids.', + 'type' => 'read', + 'ajax' => true, + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + ), +); diff --git a/lib.php b/lib.php index 907ef63b..14119c92 100644 --- a/lib.php +++ b/lib.php @@ -78,6 +78,18 @@ function scheduler_add_instance($data, $mform = null) { return $data->id; } +function scheduler_student_autocomplete_callback($value) { + global $OUTPUT; + + $fields = 'id ' . \core_user\fields::for_name()->get_sql()->selects; + $user = \core_user::get_user($value, $fields); + $useroptiondata = [ + 'fullname' => fullname($user) + ]; + + return $OUTPUT->render_from_template('mod_scheduler/studentid', $useroptiondata); +} + /** * Given an object containing all the necessary data, * (defined by the form in mod.html) this function diff --git a/slotforms.php b/slotforms.php index 4f8d8ad5..c03384cb 100644 --- a/slotforms.php +++ b/slotforms.php @@ -249,14 +249,12 @@ protected function definition() { $repeatarray[] = $mform->createElement('header', 'appointhead', get_string('appointmentno', 'scheduler', '{no}')); // Choose student. - $students = $this->scheduler->get_available_students($this->usergroups); - $studentchoices = array(); - if ($students) { - foreach ($students as $astudent) { - $studentchoices[$astudent->id] = fullname($astudent); - } - } - $grouparray[] = $mform->createElement('searchableselector', 'studentid', '', $studentchoices); + $options = [ + 'ajax' => 'mod_scheduler/studentid', + 'valuehtmlcallback' => 'scheduler_student_autocomplete_callback', + 'scheduler' => $this->scheduler->id + ]; + $grouparray[] = $mform->createElement('autocomplete', 'studentid', '', [], $options); $grouparray[] = $mform->createElement('hidden', 'appointid', 0); // Seen tickbox. diff --git a/templates/studentid.mustache b/templates/studentid.mustache new file mode 100644 index 00000000..42658cd0 --- /dev/null +++ b/templates/studentid.mustache @@ -0,0 +1,52 @@ +{{! + 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 . +}} +{{! + @template mod_scheduler/studentid + + Moodle template for the list of valid options in an autocomplate form element. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + * fullname string Users full name + * email string user email field + + Example context (json): + { + "fullname": "Admin User", + "extrafields": [ + { + "name": "email", + "value": "admin@example.com" + }, + { + "name": "phone1", + "value": "0123456789" + } + ] + } +}} + + {{fullname}} + {{#extrafields}} + {{{value}}} + {{/extrafields}} + diff --git a/tests/behat/behat_mod_scheduler.php b/tests/behat/behat_mod_scheduler.php index 5c55add5..ad7e7db0 100644 --- a/tests/behat/behat_mod_scheduler.php +++ b/tests/behat/behat_mod_scheduler.php @@ -170,8 +170,8 @@ public function i_click_on_item_in_the_nth_autocomplete_list($item, $listnumber) $downarrowtarget = "(//span[contains(@class,'form-autocomplete-downarrow')])[$listnumber]"; $this->execute('behat_general::i_click_on', [$downarrowtarget, 'xpath_element']); - $xpathtarget = "(//ul[@class='form-autocomplete-suggestions']//*[contains(concat('|', string(.), '|'),'|" . - $item . "|')])[$listnumber]"; - $this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']); + $xpathtarget = "(//descendant::ul[@class='form-autocomplete-suggestions'][$listnumber]//" + ."*[contains(concat('|', string(.), '|'),'|$item|')])"; + $this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']); } } diff --git a/version.php b/version.php index 274c96d0..4952256a 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ */ $plugin->component = 'mod_scheduler'; // Full name of the plugin (used for diagnostics). -$plugin->version = 2024080102; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2024080103; // The current module version (Date: YYYYMMDDXX). $plugin->release = '4.x dev'; // Human-friendly version name. $plugin->requires = 2023100905; // Requires Moodle 4.3. $plugin->maturity = MATURITY_ALPHA; // Development release - not for production use.