From 58a1667f928ff8dda12876f75dbdf2d98f193b9f Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Tue, 18 Feb 2025 13:34:13 +0000 Subject: [PATCH] MDL-84142 enrol_guest: use new template for the self enrolment widget --- .upgradenotes/MDL-84142-2025021805284040.yml | 7 + enrol/guest/amd/build/enrol_page.min.js | 10 ++ enrol/guest/amd/build/enrol_page.min.js.map | 1 + enrol/guest/amd/src/enrol_page.js | 63 ++++++++ enrol/guest/classes/form/enrol_form.php | 150 +++++++++++++++++++ enrol/guest/lang/en/enrol_guest.php | 1 + enrol/guest/lib.php | 77 +++++----- enrol/guest/locallib.php | 34 ++++- enrol/guest/tests/behat/guest_access.feature | 4 +- 9 files changed, 303 insertions(+), 44 deletions(-) create mode 100644 .upgradenotes/MDL-84142-2025021805284040.yml create mode 100644 enrol/guest/amd/build/enrol_page.min.js create mode 100644 enrol/guest/amd/build/enrol_page.min.js.map create mode 100644 enrol/guest/amd/src/enrol_page.js create mode 100644 enrol/guest/classes/form/enrol_form.php diff --git a/.upgradenotes/MDL-84142-2025021805284040.yml b/.upgradenotes/MDL-84142-2025021805284040.yml new file mode 100644 index 00000000000..b994c8b8c05 --- /dev/null +++ b/.upgradenotes/MDL-84142-2025021805284040.yml @@ -0,0 +1,7 @@ +issueNumber: MDL-84142 +notes: + enrol_guest: + - message: >- + Class enrol_guest_enrol_form is deprecated, use + enrol_guest\form\enrol_form instead + type: deprecated diff --git a/enrol/guest/amd/build/enrol_page.min.js b/enrol/guest/amd/build/enrol_page.min.js new file mode 100644 index 00000000000..30c2454a44b --- /dev/null +++ b/enrol/guest/amd/build/enrol_page.min.js @@ -0,0 +1,10 @@ +define("enrol_guest/enrol_page",["exports","core_form/modalform","core/str","core/prefetch","core/url"],(function(_exports,_modalform,_str,_prefetch,_url){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +/** + * Functions for the enrol_guest plugin + * + * @module enrol_guest/enrol_page + * @copyright Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initEnrol=function(instanceId){(0,_prefetch.prefetchStrings)("moodle",["loginguest"]);const button=document.querySelector('button[type="submit"][data-instance="'+instanceId+'"]');button&&button.addEventListener("click",(e=>{e.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:button.dataset.title,large:!1},formClass:button.dataset.form,args:{id:button.dataset.id,instance:instanceId},saveButtonText:(0,_str.getString)("loginguest","moodle"),returnFocus:button});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{window.location.href=event.detail?event.detail:_url.default.relativeUrl("/course/view.php",{id:button.dataset.id})})),modalForm.show()}))},_modalform=_interopRequireDefault(_modalform),_url=_interopRequireDefault(_url)})); + +//# sourceMappingURL=enrol_page.min.js.map \ No newline at end of file diff --git a/enrol/guest/amd/build/enrol_page.min.js.map b/enrol/guest/amd/build/enrol_page.min.js.map new file mode 100644 index 00000000000..04289bba68f --- /dev/null +++ b/enrol/guest/amd/build/enrol_page.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"enrol_page.min.js","sources":["../src/enrol_page.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 * Functions for the enrol_guest plugin\n *\n * @module enrol_guest/enrol_page\n * @copyright Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport Url from 'core/url';\n\n/**\n * Initialise widget on the course enrolment page - clicking on the button should submit the form\n *\n * @param {Number} instanceId\n */\nexport function initEnrol(instanceId) {\n prefetchStrings('moodle', [\n 'loginguest',\n ]);\n\n const button = document.querySelector('button[type=\"submit\"][data-instance=\"' + instanceId + '\"]');\n if (button) {\n button.addEventListener('click', (e) => {\n e.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: button.dataset.title,\n large: false, // This is a very small form that does not need a large popup.\n },\n formClass: button.dataset.form,\n args: {id: button.dataset.id, instance: instanceId},\n saveButtonText: getString('loginguest', 'moodle'),\n returnFocus: button,\n });\n\n // Redirect to the course page when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n window.location.href = event.detail ? event.detail :\n Url.relativeUrl('/course/view.php', {id: button.dataset.id});\n });\n\n modalForm.show();\n });\n }\n}\n"],"names":["instanceId","button","document","querySelector","addEventListener","e","preventDefault","modalForm","ModalForm","modalConfig","title","dataset","large","formClass","form","args","id","instance","saveButtonText","returnFocus","events","FORM_SUBMITTED","event","window","location","href","detail","Url","relativeUrl","show"],"mappings":";;;;;;;yFAiC0BA,0CACN,SAAU,CACtB,qBAGEC,OAASC,SAASC,cAAc,wCAA0CH,WAAa,MACzFC,QACAA,OAAOG,iBAAiB,SAAUC,IAC9BA,EAAEC,uBACIC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,MAAOT,OAAOU,QAAQD,MACtBE,OAAO,GAEXC,UAAWZ,OAAOU,QAAQG,KAC1BC,KAAM,CAACC,GAAIf,OAAOU,QAAQK,GAAIC,SAAUjB,YACxCkB,gBAAgB,kBAAU,aAAc,UACxCC,YAAalB,SAIjBM,UAAUH,iBAAiBG,UAAUa,OAAOC,gBAAgBC,QACxDC,OAAOC,SAASC,KAAOH,MAAMI,OAASJ,MAAMI,OACxCC,aAAIC,YAAY,mBAAoB,CAACZ,GAAIf,OAAOU,QAAQK,QAGhET,UAAUsB"} \ No newline at end of file diff --git a/enrol/guest/amd/src/enrol_page.js b/enrol/guest/amd/src/enrol_page.js new file mode 100644 index 00000000000..b792d5089be --- /dev/null +++ b/enrol/guest/amd/src/enrol_page.js @@ -0,0 +1,63 @@ +// 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 . + +/** + * Functions for the enrol_guest plugin + * + * @module enrol_guest/enrol_page + * @copyright Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import ModalForm from 'core_form/modalform'; +import {getString} from 'core/str'; +import {prefetchStrings} from 'core/prefetch'; +import Url from 'core/url'; + +/** + * Initialise widget on the course enrolment page - clicking on the button should submit the form + * + * @param {Number} instanceId + */ +export function initEnrol(instanceId) { + prefetchStrings('moodle', [ + 'loginguest', + ]); + + const button = document.querySelector('button[type="submit"][data-instance="' + instanceId + '"]'); + if (button) { + button.addEventListener('click', (e) => { + e.preventDefault(); + const modalForm = new ModalForm({ + modalConfig: { + title: button.dataset.title, + large: false, // This is a very small form that does not need a large popup. + }, + formClass: button.dataset.form, + args: {id: button.dataset.id, instance: instanceId}, + saveButtonText: getString('loginguest', 'moodle'), + returnFocus: button, + }); + + // Redirect to the course page when the form is submitted. + modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => { + window.location.href = event.detail ? event.detail : + Url.relativeUrl('/course/view.php', {id: button.dataset.id}); + }); + + modalForm.show(); + }); + } +} diff --git a/enrol/guest/classes/form/enrol_form.php b/enrol/guest/classes/form/enrol_form.php new file mode 100644 index 00000000000..c3d43976c22 --- /dev/null +++ b/enrol/guest/classes/form/enrol_form.php @@ -0,0 +1,150 @@ +. + +declare(strict_types=1); + +namespace enrol_guest\form; + +use core\context\course as context_course; +use core_form\dynamic_form; +use core_text; +use moodle_url; + +/** + * Form for entering password for guest enrolment + * + * @package enrol_guest + * @copyright Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class enrol_form extends dynamic_form { + /** @var \stdClass */ + protected $instance; + + /** + * Returns the instance of the enrolment method + * + * @throws \moodle_exception + * @return \stdClass + */ + protected function get_instance(): \stdClass { + global $DB, $CFG; + require_once($CFG->dirroot . '/lib/enrollib.php'); + if ($this->instance === null) { + $courseid = $this->optional_param('id', 0, PARAM_INT); + $instanceid = $this->optional_param('instance', 0, PARAM_INT); + // We need enrol_get_instances() to validate that the enrolment method is enabled. + $instances = enrol_get_instances($courseid, true); + if (empty($instances[$instanceid]) || $instances[$instanceid]->enrol !== 'guest') { + throw new \moodle_exception('invalidenrolinstance', 'enrol'); + } + $this->instance = $instances[$instanceid] ?? null; + } + return $this->instance; + } + + #[\Override] + public function definition() { + $mform = $this->_form; + + $mform->addElement('password', 'guestpassword', get_string('password', 'enrol_guest')); + + $mform->addElement('hidden', 'id'); + $mform->setType('id', PARAM_INT); + + $mform->addElement('hidden', 'instance'); + $mform->setType('instance', PARAM_INT); + } + + #[\Override] + public function validation($data, $files) { + global $DB, $CFG; + + $errors = parent::validation($data, $files); + $instance = $this->get_instance(); + + if ($instance->password !== '') { + if ($data['guestpassword'] !== $instance->password) { + $plugin = enrol_get_plugin('guest'); + if ($plugin->get_config('showhint')) { + $hint = core_text::substr($instance->password, 0, 1); + $errors['guestpassword'] = get_string('passwordinvalidhint', 'enrol_guest', $hint); + } else { + $errors['guestpassword'] = get_string('passwordinvalid', 'enrol_guest'); + } + } + } + + return $errors; + } + + #[\Override] + protected function check_access_for_dynamic_submission(): void { + global $USER, $CFG; + $courseid = $this->get_instance()->courseid; + $course = get_course($courseid); + $context = context_course::instance($this->get_instance()->courseid); + if (!\core_course_category::can_view_course_info($course) && !is_enrolled($context, $USER, '', true)) { + throw new \moodle_exception('coursehidden', '', $CFG->wwwroot . '/'); + } + } + + #[\Override] + protected function get_context_for_dynamic_submission(): \context { + // This form is used for users who are not yet enrolled in the course and do not have access to the course. + // For the purpose of permission checks they must be able to access the course category for this course. + return context_course::instance($this->get_instance()->courseid)->get_parent_context(); + } + + #[\Override] + protected function get_page_url_for_dynamic_submission(): moodle_url { + $instance = $this->get_instance(); + return new moodle_url('/enrol/index.php', ['id' => $instance->courseid, 'instance' => $instance->id]); + } + + /** + * Process the form submission, used if form was submitted via AJAX + * + * Enrols the user in the course and returns the URL to redirect to + * + * @return string + */ + public function process_dynamic_submission() { + global $USER, $CFG, $SESSION; + + /** @var \enrol_guest_plugin $enrol */ + $enrol = enrol_get_plugin('guest'); + $instance = $this->get_instance(); + + $enrol->mark_user_as_enrolled($instance, $this->get_data()->guestpassword); + + // Go to the originally requested page. + if (!empty($SESSION->wantsurl)) { + $destination = $SESSION->wantsurl; + unset($SESSION->wantsurl); + } else { + require_once($CFG->dirroot . '/course/lib.php'); + $destination = course_get_url($instance->courseid); + } + return $destination; + } + + #[\Override] + public function set_data_for_dynamic_submission(): void { + $instance = $this->get_instance(); + $this->set_data(['id' => $instance->courseid, 'instance' => $instance->id]); + } +} diff --git a/enrol/guest/lang/en/enrol_guest.php b/enrol/guest/lang/en/enrol_guest.php index bed0a5a85f2..bdd3f930124 100644 --- a/enrol/guest/lang/en/enrol_guest.php +++ b/enrol/guest/lang/en/enrol_guest.php @@ -31,6 +31,7 @@ $string['password_help'] = 'A password allows guest access to the course to be r $string['passwordinvalid'] = 'Incorrect access password, please try again'; $string['passwordinvalidhint'] = 'That access password was incorrect, please try again
(Here\'s a hint - it starts with \'{$a}\')'; +$string['passwordrequired'] = 'A password will be required'; $string['pluginname'] = 'Guest access'; $string['pluginname_desc'] = 'Guest access plugin is only granting temporary access to courses, it is not actually enrolling users.'; $string['requirepassword'] = 'Require guest access password'; diff --git a/enrol/guest/lib.php b/enrol/guest/lib.php index 1af049f19d1..e32c8754dec 100644 --- a/enrol/guest/lib.php +++ b/enrol/guest/lib.php @@ -25,7 +25,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); +use core\output\single_button; +use core_enrol\output\enrol_page; /** * Class enrol_guest_plugin @@ -148,14 +149,28 @@ class enrol_guest_plugin extends enrol_plugin { } /** - * Creates course enrol form, checks if form submitted - * and enrols user if necessary. It can also redirect. + * Enrol a user using the guest enrolment method * * @param stdClass $instance - * @return string html text, usually a form in a text box + * @param string $guestpassword + * @return void */ + public function mark_user_as_enrolled(stdClass $instance, string $guestpassword): void { + global $USER, $CFG; + + // Add guest role. + $context = \core\context\course::instance($instance->courseid); + $USER->enrol_guest_passwords[$instance->id] = $guestpassword; + if (isset($USER->enrol['tempguest'][$instance->courseid])) { + remove_temp_course_roles($context); + } + load_temp_course_role($context, $CFG->guestroleid); + $USER->enrol['tempguest'][$instance->courseid] = ENROL_MAX_TIMESTAMP; + } + + #[\Override] public function enrol_page_hook(stdClass $instance) { - global $CFG, $OUTPUT, $SESSION, $USER; + global $CFG, $OUTPUT, $SESSION, $USER, $PAGE; if ($instance->password === '') { return null; @@ -166,37 +181,27 @@ class enrol_guest_plugin extends enrol_plugin { return null; } - require_once("$CFG->dirroot/enrol/guest/locallib.php"); - $form = new enrol_guest_enrol_form(NULL, $instance); - $instanceid = optional_param('instance', 0, PARAM_INT); - - if ($instance->id == $instanceid) { - if ($data = $form->get_data()) { - // add guest role - $context = context_course::instance($instance->courseid); - $USER->enrol_guest_passwords[$instance->id] = $data->guestpassword; // this is a hack, ideally we should not add stuff to $USER... - if (isset($USER->enrol['tempguest'][$instance->courseid])) { - remove_temp_course_roles($context); - } - load_temp_course_role($context, $CFG->guestroleid); - $USER->enrol['tempguest'][$instance->courseid] = ENROL_MAX_TIMESTAMP; - - // go to the originally requested page - if (!empty($SESSION->wantsurl)) { - $destination = $SESSION->wantsurl; - unset($SESSION->wantsurl); - } else { - $destination = "$CFG->wwwroot/course/view.php?id=$instance->courseid"; - } - redirect($destination); - } - } - - ob_start(); - $form->display(); - $output = ob_get_clean(); - - return $OUTPUT->box($output, 'generalbox'); + $title = $this->get_instance_name($instance); + $notification = new \core\output\notification(get_string('passwordrequired', 'enrol_guest'), 'info', false); + $notification->set_extra_classes(['mb-0']); + $button = new single_button( + $PAGE->url, + get_string('loginguest', 'moodle'), + 'get', + single_button::BUTTON_PRIMARY, + [ + 'data-id' => $instance->courseid, + 'data-instance' => $instance->id, + 'data-form' => enrol_guest\form\enrol_form::class, + 'data-title' => $title, + ]); + $PAGE->requires->js_call_amd('enrol_guest/enrol_page', 'initEnrol', [$instance->id]); + $enrolpage = new enrol_page( + instance: $instance, + header: $title, + body: $OUTPUT->render($notification), + buttons: [$button]); + return $OUTPUT->render($enrolpage); } /** diff --git a/enrol/guest/locallib.php b/enrol/guest/locallib.php index 934d47a7e70..65f5a247cd7 100644 --- a/enrol/guest/locallib.php +++ b/enrol/guest/locallib.php @@ -14,21 +14,41 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Guest access plugin implementation. - * - * @package enrol_guest - * @copyright 2010 Petr Skoda {@link http://skodak.org} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ defined('MOODLE_INTERNAL') || die(); require_once("$CFG->libdir/formslib.php"); +/** + * Guest access plugin implementation. + * + * @deprecated since Moodle 5.0 - please use {@see enrol_guest\form\enrol_form} + * + * @package enrol_guest + * @copyright 2010 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +#[\core\attribute\deprecated(replacement: enrol_guest\form\enrol_form::class, since: '5.0', reason: 'Now a dynamic form is used')] class enrol_guest_enrol_form extends moodleform { protected $instance; + /** + * Constructor + * + * @param mixed $action + * @param mixed $customdata + * @param string $method + * @param string $target + * @param mixed $attributes + * @param bool $editable + * @param array $ajaxformdata + */ + public function __construct($action=null, $customdata=null, $method='post', $target='', $attributes=null, $editable=true, + $ajaxformdata=null) { + \core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]); + parent::__construct($action, $customdata, $method, $target, $attributes, $editable, $ajaxformdata); + } + public function definition() { $mform = $this->_form; $instance = $this->_customdata; diff --git a/enrol/guest/tests/behat/guest_access.feature b/enrol/guest/tests/behat/guest_access.feature index ce8d696b198..f5f6232af04 100644 --- a/enrol/guest/tests/behat/guest_access.feature +++ b/enrol/guest/tests/behat/guest_access.feature @@ -30,6 +30,7 @@ Feature: Guest users can auto-enrol themself in courses where guest access is al When I am on the "Test forum name" "forum activity" page logged in as student1 Then I should not see "Subscribe to this forum" + @javascript Scenario: Allow guest access with password Given I click on "Edit" "link" in the "Guest access" "table_row" And I set the following fields to these values: @@ -38,7 +39,8 @@ Feature: Guest users can auto-enrol themself in courses where guest access is al And I press "Save changes" When I am on the "Course 1" course page logged in as student1 Then I should see "Guest access" + And I press "Access as a guest" And I set the following fields to these values: | Password | moodle_rules | - And I press "Submit" + And I click on "Access as a guest" "button" in the "Guest access" "dialogue" And I should see "Test forum name"