- 教育
- 后端
- 前端
【免费下载链接】moodle
Moodle - the world's open source learning platform
本文基于仓库中 enrol_guest 升级说明 展开。Moodle 5.0 将访客选课(guest access)插件中的
enrol_guest_enrol_form表单类正式标记为弃用(deprecated),并推荐改用命名空间化的动态表单类enrol_guest\form\enrol_form。读完本文,你将理解这次弃用的来龙去脉、新旧表单类在源码层面的实现差异,以及第三方插件和自定义代码需要遵循的迁移路径。
一、升级说明原文要点
仓库中public/enrol/guest/UPGRADING.md是 Moodle 标准化的升级备注文档,其中 5.0 版本段落的「Deprecated」部分记录了一条关键变更:
- 类
enrol_guest_enrol_form已被弃用,应改用enrol_guest\form\enrol_form; - 该变更对应 Moodle 官方追踪器问题单 MDL-84142。
这条备注虽短,但指向了一次实质性的架构调整:访客密码表单从「继承moodleform的传统 PHP 页面内表单」迁移为「基于core_form\dynamic_form的动态(模态框)表单」。下面结合仓库源码逐一印证。
二、被弃用的旧类:enrol_guest_enrol_form
旧表单类定义在 locallib.php 中,仓库源码对其标注了明确的弃用元数据:
#[\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 {可以看到三个关键信息:
- 弃用属性:使用 PHP 8 属性
#[\core\attribute\deprecated]声明,since: '5.0'指明自 Moodle 5.0 起弃用; - 替代类:
replacement明确指向新类enrol_guest\form\enrol_form; - 弃用原因:
Now a dynamic form is used——即改用动态表单机制。
此外,该类的构造函数在初始化时会主动发出弃用通知:
public function __construct($action=null, $customdata=null, $method='post', ...) { \core\deprecation::emit_deprecation([$this, __FUNCTION__]); parent::__construct($action, $customdata, $method, $target, $attributes, $editable, $ajaxformdata); }这意味着任何仍通过new enrol_guest_enrol_form(...)实例化该类的代码,在 Moodle 5.0 运行环境下都会收到运行时弃用告警,同时类头注释也以@deprecated since Moodle 5.0 - please use {@see enrol_guest\form\enrol_form}提示开发者。
从旧类实现看,它本质上是一个典型的moodleform页面表单:
- 通过
addElement('password', 'guestpassword', ...)渲染访客密码输入框; - 携带隐藏字段
id(课程 ID)与instance(选课实例 ID); - 在
validation()中比对用户输入与实例密码,并根据插件配置showhint决定返回「密码错误」还是「带首位字符提示的密码错误」提示文案。
三、替代新类:enrol_guest\form\enrol_form(动态表单)
新表单类位于 classes/form/enrol_form.php,命名空间为enrol_guest\form,继承core_form\dynamic_form,这与 Moodle 4.x 以来推广的动态表单(Dynamic forms)架构一致。
declare(strict_types=1); namespace enrol_guest\form; use core\context\course as context_course; use core_form\dynamic_form; ... class enrol_form extends dynamic_form {3.1 表单定义与验证逻辑
新类在definition()中定义了与旧类基本一致的表单字段(密码输入 + 隐藏的id、instance参数),并复用了完全相同的密码验证策略:
public function validation($data, $files) { $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; }该验证逻辑与旧类逐行一致,业务行为没有变化,变化的只是承载机制。
3.2 实例获取与访问控制
新类通过get_instance()从请求参数读取id(课程 ID)与instance(选课实例 ID),并调用enrol_get_instances($courseid, true)校验该访客选课实例确实存在且处于启用状态:
$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;同时,动态表单必须实现三个权限/路由钩子方法:
| 方法 | 职责 |
|---|---|
check_access_for_dynamic_submission() | 校验未选课用户是否至少能查看课程信息,否则抛出coursehidden异常 |
get_context_for_dynamic_submission() | 返回课程所属父级分类上下文(category context),用于未选课用户的权限判定 |
get_page_url_for_dynamic_submission() | 返回动态提交回跳地址/enrol/index.php?id={courseid}&instance={instanceid} |
3.3 提交处理
process_dynamic_submission()是动态表单的提交入口:调用插件主类的方法$enrol->mark_user_as_enrolled($instance, $this->get_data()->guestpassword)将用户标记为临时访客并写入$SESSION->wantsurl指定的原目标页面或课程首页 URL,供前端跳转使用。
四、前端流程:从整页表单到模态框
这次弃用的本质,是访客密码输入从「独立整页表单」迁移到了「课程选课页面上的模态框(Modal)」交互。相关证据在插件前端模块 amd/src/enrol_page.js:
export function initEnrol(instanceId) { ... 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 }, formClass: button.dataset.form, args: {id: button.dataset.id, instance: instanceId}, saveButtonText: getString('loginguest', 'moodle'), returnFocus: button, }); ... modalForm.show(); }); } }这段 JS 的逻辑是:
- 在课程选课页面查找带有
data-instance属性的提交按钮; - 点击后不再跳转到独立表单页,而是弹出
core_form/modalform模态框; - 通过按钮上的
data-form属性(值为enrol_guest\form\enrol_form::class)动态加载新表单类; - 表单提交成功后根据返回 URL 重定向到课程页面。
与之配合,插件主类 lib.php 的enrol_page_hook()负责在需要密码的访客选课实例上渲染这个触发按钮,并注册 JS 模块:
$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]);注意data-form已经直接指向了新表单类enrol_guest\form\enrol_form,说明插件核心路径在 5.0 已完全切换到动态表单,旧类仅保留以兼容第三方代码。
五、为什么要迁移:动态表单的优势
从旧类(继承moodleform)到新类(继承dynamic_form)的迁移并非形式主义,其收益体现在多个层面:
- 无需整页刷新:密码校验、错误提示均在模态框内通过 AJAX 完成,不打断用户在选课页面的操作流;
- 权限判定更精准:动态表单提供专门的访问检查钩子(
check_access_for_dynamic_submission),能够在提交前校验未选课用户是否有权看到课程; - 统一表单基础设施:Moodle 4.x 起
core_form\dynamic_form成为官方推荐的动态交互表单基类,新类遵循同一套命名空间规范(pluginname\form\classname); - 职责内聚:旧类存放在插件根目录的
locallib.php(全局命名空间),新类移入classes/form/并声明declare(strict_types=1),更符合 Moodle 插件类自动加载(autoload)与 PSR 风格组织。
从测试角度看,仓库的 guest/tests 目录包含external_test.php、validate_password_test.php与 Behat 场景 guest_access.feature,覆盖了访客访问及密码校验路径,迁移后这些行为由新表单类继续支撑。
六、第三方代码迁移指南
对于在自定义插件或本地代码中直接使用旧表单类的开发者,建议按以下步骤迁移:
- 替换类引用:将
new enrol_guest_enrol_form(...)或表单类常量引用替换为enrol_guest\form\enrol_form; - 改用动态表单调用方式:若你的场景是弹出模态框,可参照 amd/src/enrol_page.js 使用
core_form/modalform,将formClass设为enrol_guest\form\enrol_form::class,并传入{id: 课程ID, instance: 选课实例ID}作为参数; - 移除对旧类构造函数的直接依赖:旧类构造函数会调用
\core\deprecation::emit_deprecation()发出运行时告警,保留旧调用会污染日志; - 必要时依赖消息路由:若无法立即迁移,可在升级前通过事件日志确认哪些路径仍在使用旧类,逐一定位替换。
七、相关配置与版本信息
围绕访客选课表单,插件的可配置项定义在 settings.php,与表单验证逻辑直接相关:
| 配置项 | 类型 | 默认值 | 对表单的影响 |
|---|---|---|---|
enrol_guest/requirepassword | checkbox | 0 | 启用后新建访客选课实例必须设置密码,表单密码必填 |
enrol_guest/usepasswordpolicy | checkbox | 0 | 启用后密码需满足站点密码策略,validation()会调用\core\authentication\password::check_policy()校验 |
enrol_guest/showhint | checkbox | 0 | 密码错误时是否提示密码首位字符(passwordinvalidhint文案) |
enrol_guest/defaultenrol | checkbox | 1 | 新建课程时是否默认添加访客选课实例 |
enrol_guest/status | select+advanced | 禁用 | 默认实例的启用/禁用状态 |
插件版本定义见 version.php:当前版本为2026042000,要求 Moodle 版本不低于2026041000。在升级到 5.0 之后,核心代码路径已完全基于动态表单,旧的enrol_guest_enrol_form仅作为向后兼容的遗留类存在,后续大版本可能被彻底移除。
小结
public/enrol/guest/UPGRADING.md中 5.0 的这条弃用备注,背后是一次完整的表单架构现代化:enrol_guest_enrol_form(moodleform整页表单)被enrol_guest\form\enrol_form(dynamic_form模态框表单)取代。仓库源码明确印证了新旧两类的实现差异、前端 ModalForm 的接入方式以及插件配置的联动逻辑。第三方开发者应尽快将引用切换到新类,以获得无整页刷新的用户体验、更精确的权限校验以及与 Moodle 官方表单基础设施的一致性。
- 教育
- 后端
- 前端
【免费下载链接】moodle
Moodle - the world's open source learning platform
相关推荐
Moodle core_calendar 升级指南:5.0/5.2 弃用与移除 API 全解析及迁移实战
Moodle core_calendar 升级指南:5.0/5.2 弃用与移除 API 全解析及迁移实战 本文以 public/calendar/UPGRADI
教育后端前端Moodle 5.0 tool_brickfield 无障碍插件升级指南:find_system_areas 弃用与 Chat/Survey 支持移除
Moodle 5.0 tool_brickfield 无障碍插件升级指南:find_system_areas 弃用与 Chat/Survey 支持移除 导读 t
教育后端前端Moodle tool_mfa 插件升级指南:5.0/5.2 渲染器重构、因子管理表迁移与语言字符串弃用全解析
Moodle tool_mfa 插件升级指南:5.0/5.2 渲染器重构、因子管理表迁移与语言字符串弃用全解析 本文围绕 Moodle https://link
教育后端前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考