news 2026/10/7 1:52:03

Moodle enrol_guest 插件 5.0 升级指南:enrol_guest_enrol_form 弃用与动态表单迁移

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Moodle enrol_guest 插件 5.0 升级指南:enrol_guest_enrol_form 弃用与动态表单迁移
  • 教育
  • 后端
  • 前端

【免费下载链接】moodle

Moodle - the world's open source learning platform

项目地址:https://gitcode.com/gh_mirrors/mo/moodle
点击查看免费下载

本文基于仓库中 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 {

可以看到三个关键信息:

  1. 弃用属性:使用 PHP 8 属性#[\core\attribute\deprecated]声明,since: '5.0'指明自 Moodle 5.0 起弃用;
  2. 替代类:replacement明确指向新类enrol_guest\form\enrol_form;
  3. 弃用原因: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 的逻辑是:

  1. 在课程选课页面查找带有data-instance属性的提交按钮;
  2. 点击后不再跳转到独立表单页,而是弹出core_form/modalform模态框;
  3. 通过按钮上的data-form属性(值为enrol_guest\form\enrol_form::class)动态加载新表单类;
  4. 表单提交成功后根据返回 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,覆盖了访客访问及密码校验路径,迁移后这些行为由新表单类继续支撑。

六、第三方代码迁移指南

对于在自定义插件或本地代码中直接使用旧表单类的开发者,建议按以下步骤迁移:

  1. 替换类引用:将new enrol_guest_enrol_form(...)或表单类常量引用替换为enrol_guest\form\enrol_form;
  2. 改用动态表单调用方式:若你的场景是弹出模态框,可参照 amd/src/enrol_page.js 使用core_form/modalform,将formClass设为enrol_guest\form\enrol_form::class,并传入{id: 课程ID, instance: 选课实例ID}作为参数;
  3. 移除对旧类构造函数的直接依赖:旧类构造函数会调用\core\deprecation::emit_deprecation()发出运行时告警,保留旧调用会污染日志;
  4. 必要时依赖消息路由:若无法立即迁移,可在升级前通过事件日志确认哪些路径仍在使用旧类,逐一定位替换。

七、相关配置与版本信息

围绕访客选课表单,插件的可配置项定义在 settings.php,与表单验证逻辑直接相关:

配置项类型默认值对表单的影响
enrol_guest/requirepasswordcheckbox0启用后新建访客选课实例必须设置密码,表单密码必填
enrol_guest/usepasswordpolicycheckbox0启用后密码需满足站点密码策略,validation()会调用\core\authentication\password::check_policy()校验
enrol_guest/showhintcheckbox0密码错误时是否提示密码首位字符(passwordinvalidhint文案)
enrol_guest/defaultenrolcheckbox1新建课程时是否默认添加访客选课实例
enrol_guest/statusselect+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

项目地址:https://gitcode.com/gh_mirrors/mo/moodle
点击查看免费下载
上一篇:Gulp-file-include 项目常见问题解决方案
下一篇:Sub-Zero字幕搜索揭秘:如何从10大提供商中智能匹配最佳字幕

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 1:47:01

(3)MARK点的作用及设计

Mark点,又称为基准点或光学定位点,是PCB设计中用于贴片机定位的重要标记。它在PCB大批量生产中为装配过程的每个步骤提供了统一的可测量点,从而确保组件的精确放置。 PCB单板中添加MARK点,需添加3-4个mark点,若放置4个…

作者头像 李华
网站建设 2026/10/7 1:45:42

安规电容可靠性试验全流程:X/Y电容验证与失效判定

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:45:19

Java Web图书馆借阅管理系统设计与实现:从数据库到借还书全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:45:15

64M参数大模型MiniMind:从零训练ChatGPT级对话模型全流程

1. 一个 64M 的模型凭什么敢叫板 ChatGPT第一次看到 MiniMind 这个项目的时候,我的反应和大多数人一样:64M 参数的模型,连 GPT-2 的零头都不到,凭什么能像 ChatGPT 一样对话?要知道现在随便一个能打的开源模型都是 7B …

作者头像 李华