Moodle 5.0 qbank_bulkmove 升级指南:批量移动题目从「服务端表单渲染」迁移到「Modal + WebService」架构
教育后端前端【免费下载链接】moodleMoodle - the worlds open source learning platform项目地址https://gitcode.com/gh_mirrors/mo/moodle点击查看免费下载本文以仓库中 qbank_bulkmove 升级说明 为骨架系统梳理 Moodle 5.0 中「批量移动题目」功能的两次 API 废弃Deprecation及对应替代方案并结合helper、renderer、output/bulk_move、AMD 模态框模块与core_question\external\move_questionsWebService 的源码实现说明新旧两套架构的差异、迁移路径与测试验证方式。读完本文你将掌握 qbank_bulkmove 在 Moodle 5.0 后的正确调用姿势以及如何在不破坏既有功能的前提下完成第三方扩展的适配升级。一、升级概览一次针对批量移动题目的架构重构Moodle 5.0 对qbank_bulkmove插件做了两处标记为 Deprecated 的变更全部对应追踪条目MDL-71378废弃 API替代方案qbank_bulkmove\helper::get_displaydata由模态框modal与 WebService 取代参见qbank_bulkmove/modal_question_bank_bulkmove与core_question_external\move_questionsqbank_bulkmove\output\renderer::render_bulk_move_form由qbank_bulkmove\output\bulk_move取代从整体方向看这是一次前后端分离式重构旧实现由服务端 PHP 直接拼装「分类下拉 提交表单」并渲染整页模板新实现改为「浏览器端 Modal 模态框选择目标题库与分类 WebService 异步执行移动」的两段式交互。以下各节分别拆解旧实现、新实现与迁移要点。二、被废弃的旧实现get_displaydata与render_bulk_move_form2.1helper::get_displaydata拼装表单展示数据旧版的数据装配入口位于 classes/helper.php其职责是生成批量移动表单所需的三项数据#[\core\attribute\deprecated( replacement: replaced by a modal and webservice. See qbank_bulkmove/modal_question_bank_bulkmove and core_question_external\move_questions, since: 5.0, mdl: MDL-71378 )] public static function get_displaydata(array $addcontexts, \moodle_url $moveurl, \moodle_url $returnurl): array { \core\deprecation::emit_deprecation([self::class, __FUNCTION__]); $displaydata []; $displaydata [categorydropdown] \qbank_managecategories\helper::question_category_select_menu($addcontexts, false, 0, , -1, true); $displaydata [moveurl] $moveurl; $displaydata[returnurl] $returnurl; return $displaydata; }该方法接收三个参数并返回渲染数据数组参数类型说明$addcontextsarray参与渲染分类下拉菜单的上下文context列表$moveurl\moodle_url表单提交所指向的移动处理脚本 URL$returnurl\moodle_url取消操作时返回的 URL返回数组包含三个键categorydropdown由 qbank_managecategories\helper::question_category_select_menu 生成的下拉框 HTML、moveurl、returnurl。可以看出旧方案依赖插件qbank_managecategories在服务端直接产出分类下拉的完整 HTML数据与视图强耦合。2.2renderer::render_bulk_move_form服务端渲染整个表单视图层入口位于 classes/output/renderer.php#[\core\attribute\deprecated(qbank_bulkmove\output\bulk_move, since: 5.0, mdl: MDL-71378)] public function render_bulk_move_form($displaydata) { \core\deprecation::emit_deprecation([self::class, __FUNCTION__]); return $this-render_from_template(qbank_bulkmove/bulk_move, $displaydata); }它接收get_displaydata的返回结果直接渲染 templates/bulk_move.mustache 模板。该模板内含「题库bank下拉」「分类下拉」与「Move questions 提交按钮」三块区域其中分类选择复用了core_question/question_category_selector局部模板div classsearch-banks h5{{#str}}questionbank, question{{/str}}/h5 select classform-select bulk-move d-none idsearchbanks >public static function bulk_move_questions(string $movequestionselected, \stdClass $tocategory): void { global $DB, $CFG; require_once($CFG-libdir ./questionlib.php); if ($questionids explode(,, $movequestionselected)) { [$usql, $params] $DB-get_in_or_equal($questionids); $sql SELECT q.*, c.contextid FROM {question} q JOIN {question_versions} qv ON qv.questionid q.id JOIN {question_bank_entries} qbe ON qbe.id qv.questionbankentryid JOIN {question_categories} c ON c.id qbe.questioncategoryid WHERE q.id {$usql}; $questions $DB-get_records_sql($sql, $params); foreach ($questions as $question) { question_require_capability_on($question, move); } question_move_questions_to_category($questionids, $tocategory-id); } }它通过{question}、{question_versions}、{question_bank_entries}、{question_categories}四表联查为每个待移动题目调用question_require_capability_on($question, move)做逐题能力校验最终委托给核心库函数question_move_questions_to_category。同时helper 中的process_question_ids(array $rawquestions)源码 L87-L101负责把表单 post 中的qID形式的键解析为题目 ID 数组与逗号分隔串在新架构中仍可复用。四、新架构之二qbank_bulkmove\output\bulk_move输出类替代renderer::render_bulk_move_form的是 classes/output/bulk_move.php 中的bulk_move类实现\renderable与\templatable其类注释明确写道Output class to create a modal template with selects for question banks, question categories, and a move button.—— 即专门为模态框生成「题库选择、分类选择、移动按钮」三段式内容。4.1 构造与数据导出构造器接收两个 ID源码 L52-L55public function __construct(int $currentbankid, int $currentcategoryid) { $this-currentbankid $currentbankid; $this-currentcategoryid $currentcategoryid; }export_for_template()源码 L63-L108是整个新渲染链路的灵魂它按当前题库模块是否「发布共享题目」走两条数据路径共享题库plugin_supports(mod, $modname, FEATURE_PUBLISHES_QUESTIONS)为真调用question_bank_helper::get_activity_instances_with_shareable_questions(...)私有题库调用question_bank_helper::get_activity_instances_with_private_questions(...)并限定在当前课程incourseids: [$currentbankcm-course]。两者都以moodle/question:add为能力过滤条件并把当前题库置顶。随后用question_category_selector构建当前题库的分类选择器选中项格式为{当前分类ID},{当前contextID}并开启autocomplete用single_button生成一个默认disabled的 Move questions 按钮携带data-actionbulkmovesave返回bank、categories、save、contextid四项模板数据。分类与题库的候选集合都来自 question_bank_helper.php 的两个公开静态方法其底层get_bank_instances()负责构建 SQL 并支持按能力、课程、搜索词与数量上限过滤动态加载其余题库与分类正是依托这一层。4.2 Fragment 输出入口该类本身不直接渲染页面而是通过 lib.php 中的 Fragment 回调函数按需输出function qbank_bulkmove_output_fragment_bulk_move(array $args) { global $OUTPUT; $currentbankid clean_param($args[context]-instanceid, PARAM_INT); $currentcategoryid clean_param($args[categoryid], PARAM_INT); $qbankcatchooser new \qbank_bulkmove\output\bulk_move($currentbankid, $currentcategoryid); return $OUTPUT-render($qbankcatchooser); }它从 Fragment 参数中提取instanceid当前题库的 course module ID与categoryid实例化bulk_move并渲染——这就是 Modal 前端通过Fragment.loadFragment(qbank_bulkmove, bulk_move, ...)拉取弹窗内容的服务端端点。五、新架构之三AMD 模态框modal_question_bank_bulkmove前端核心是 amd/src/modal_question_bank_bulkmove.js 中的ModalQuestionBankBulkmove类继承core/modal的 Modal。5.1 初始化与触发init(contextId, categoryId)源码 L59-L72注册全局点击监听当点击「批量操作」下拉中的dropdown-item且其name move时创建模态框并传入当前 context/category。该 JS 的加载由批量操作类负责见下文第六节。initSelectedCategoryId()会优先解析当前 URL 中的filter参数从中取出category.values[0]作为初始分类否则使用传入的categoryId——这样既尊重用户在题库页的筛选状态又保证默认行为可预期。5.2 弹窗内容加载与交互display()源码 L117-L133通过Fragment.loadFragment(qbank_bulkmove, bulk_move, currentBankContextId, {categoryid})获取第四节的 Fragment 内容然后enhanceSelects()源码 L300-L324调用AutoComplete.enhance把「题库选择」下拉增强为自动补全框数据源为core_question/question_banks_datasource把「分类选择」下拉也增强为自动补全框registerEnhancedEventListeners()监听分类变化以刷新保存按钮状态监听题库切换时通过Fragment.loadFragment(core_question, category_selector, ...)动态替换分类选择器仅展示所选题库的分类并处理自动补全「搜索受限」的占位选项updateSaveButtonState()当目标分类存在且与当前分类不同时才启用「Move questions」按钮避免无效提交。5.3 确认页与最终提交点击保存按钮后displayConfirmMove()源码 L169-L193将模态框切换为确认视图标题变为confirm正文为confirmmoveAre you sure you want to move these questions?并从 bulk_move_footer.mustache 渲染「Confirm / Cancel」两个页脚按钮。moveQuestionsAfterConfirm()源码 L269-L293收集表格中所有勾选的题目选择器table#categoryquestions input[id^checkq]调用core_question/repository的moveQuestions(targetContextId, targetCategoryId, questionids, returnurl)——其底层即走第三节的core_question_external\move_questionsWebService——成功后把浏览器导航到返回 URL含新分类的 filter 参数。六、批量操作注册从功能入口看新旧衔接新旧架构在入口层面是无缝衔接的批量操作注册仍由 classes/bulk_move_action.php 与 classes/plugin_feature.php 负责它们不属于废弃范围。bulk_move_action extends \core_question\local\bank\bulk_action_base源码 L29-L75定义标题movetobulkactionMove to...、键move、能力要求moodle/question:moveall与moodle/question:add。其initialise_javascript()从题库页cat页面变量解析当前分类缺省时回退到question_get_default_category然后通过$PAGE-requires-js_call_amd(qbank_bulkmove/modal_question_bank_bulkmove, init, [contextid, categoryid])初始化模态框——这是连接第五节前端模块与题库页的挂载点plugin_feature::get_bulk_actions()源码 L37-L46在题目历史版本列表页is_listing_specific_versions()返回空数组、不提供批量移动其余页面返回[new bulk_move_action($qbank)]。插件字符串位于 lang/en/qbank_bulkmove.php包括bulkmoveheaderMove the selected questions to...、movequestions、confirmmove、warningYou must select a question bank before you can select a category.等且声明privacy:metadata为不存储任何个人数据。七、行为验证Behat 测试用例仓库提供了 tests/behat/bulk_move.feature 覆盖新架构的关键行为共 328 行典型场景包括插件开关联动在站点管理「Question bank plugins」中禁用/启用 Bulk move questions 后题库页「With selected」批量菜单中move操作随之消失/出现共享题库分类限定选择某个共享题库后自动补全的候选分类仅包含该题库自身的分类如选择了 Test quiz 的分类时Test questions 1、Test questions 6可见而其他题库的分类不可见验证了第五节updateCategorySelector的动态替换逻辑场景同时覆盖了跨课程题库Course 1/2/3、三个 qbank 活动与课程级分类的移动路径可以作为集成测试直接运行。八、第三方开发者迁移速查如果你的代码或自定义插件调用了被废弃的 API请按下表对照迁移旧 APIMoodle 5.0 起 Deprecated新 API / 新机制\qbank_bulkmove\helper::get_displaydata($addcontexts, $moveurl, $returnurl)使用core_question_external\move_questionsWebService参数newcontextid/newcategoryid/questionids/returnurl或直接复用\qbank_bulkmove\helper::bulk_move_questions()process_question_ids()\qbank_bulkmove\output\renderer::render_bulk_move_form($displaydata)实例化\qbank_bulkmove\output\bulk_move($currentbankid, $currentcategoryid)并渲染页面内嵌交互改用qbank_bulkmove/modal_question_bank_bulkmoveAMD 模块依赖整页表单提交的移动流程Fragment 动态加载qbank_bulkmove_output_fragment_bulk_moveAutoComplete自动补全 Modal 确认页两段式交互几点结论与提醒仓库内没有任何对旧 API 的活动调用残留——move_questions.php 调用的是未废弃的helper::bulk_move_questions旧方法仅保留声明并触发emit_deprecation告警从 version.php 可见该组件当前版本为2026042000、要求 Moodle 核心2026041000、成熟度MATURITY_STABLE两个被废弃方法的todo MDL-82413注释表明最终移除计划在Moodle 6.0请务必在此之前完成自有代码的迁移避免升级后出现白屏或功能缺失新架构对用户可见行为的影响移动操作从「整页表单」变为「弹窗选择 确认」且移动完成后 URL 的filter参数会自动切换到新分类便于在题库页直接看到移动结果。九、小结Moodle 5.0 对qbank_bulkmove的这次升级本质是把「批量移动题目」从服务端全量表单渲染重构为Fragment Modal WebService的现代交互链路bulk_move输出类负责服务端数据装配modal_question_bank_bulkmove负责弹窗与自动补全交互core_question_external\move_questions负责带权限校验的异步执行而helper::bulk_move_questions这一底层移动逻辑得以保留复用。对于升级维护者而言只需按第八节的对照表替换两处废弃调用即可平滑过渡到新架构并享受分类级联过滤、跨题库移动与更安全的逐题权限校验带来的体验与稳定性提升。赞分享教育后端前端【免费下载链接】moodleMoodle - the worlds open source learning platform项目地址https://gitcode.com/gh_mirrors/mo/moodle点击查看免费下载相关推荐Moodle enrol_guest 插件 5.0 升级指南enrol_guest_enrol_form 弃用与动态表单迁移Moodle enrol_guest 插件 5.0 升级指南enrol_guest_enrol_form 弃用与动态表单迁移 本文基于仓库中 enrol_gu教育后端前端Moodle tool_mfa 插件升级指南5.0/5.2 渲染器重构、因子管理表迁移与语言字符串弃用全解析Moodle tool_mfa 插件升级指南5.0/5.2 渲染器重构、因子管理表迁移与语言字符串弃用全解析 本文围绕 Moodle https://link教育后端前端PixiJS v8 迁移指南从 v7 平滑升级到新一代渲染架构PixiJS v8 迁移指南从 v7 平滑升级到新一代渲染架构 PixiJS v8 是自 v7 以来的一次重大架构升级引入了 WebGPU 渲染支持、异步初前端图形学上一篇Salvo框架中OpenAPI查询参数别名序列化问题解析下一篇Ruoyi-AI项目低代码平台功能演进与AI集成实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

SSM+JSP酒店客房预定管理系统源码:部署实战与避坑指南

SSM+JSP酒店客房预定管理系统源码:部署实战与避坑指南

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

2026/10/9 1:24:57 阅读更多 →
context-mode实践指南:构建AI辅助开发的多任务上下文工作流

context-mode实践指南:构建AI辅助开发的多任务上下文工作流

最近我在整理自己的开发环境时,把“context-mode”这套思路从里到外重新捋了一遍。很多人听到这个名字,第一反应是“这不就是上下文模式吗”,但实际用起来,真正能把上下文模式用好的人并不多。我理解的context-mode,不…

2026/10/9 1:24:57 阅读更多 →
校园局域网VLAN间路由与NAT实战配置指南

校园局域网VLAN间路由与NAT实战配置指南

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

2026/10/9 1:24:57 阅读更多 →

最新新闻

2026 Agent 开发实战手册:从架构、记忆到并发与安全的工程化落地

2026 Agent 开发实战手册:从架构、记忆到并发与安全的工程化落地

1. 这份报告到底在聊什么2026 年的 Agent 开发者调研报告,加上 Alibaba Cloud 的 AI Agent Handbook,这两个东西放在一起看,其实指向的是同一件事:Agent 开发这件事,已经从“能不能跑起来”进入到了“怎么跑得稳、跑得…

2026/10/9 1:58:18 阅读更多 →
EI_策略训练_Isaac sim

EI_策略训练_Isaac sim

Isaac Gym 本身不是动作采集(motion capture)框架,而是一个基于 GPU 加速的强化学习仿真训练平台。但它在利用动作数据(如 MoCap 录制的行走、跑跳等)来训练机器人运动控制策略方面具有显著优势,因此常被用…

2026/10/9 1:58:18 阅读更多 →
IL_动作捕捉和模仿学习

IL_动作捕捉和模仿学习

IL:Imitation Learning 实际机器人动作采集和策略训练中,完全可以将 CSV 文件(动作数据) 机器人模型 XML 文件(如 URDF、MJCF 或 MuJoCo XML)转换生成 .npz 文件,这是机器人模仿学习&#xff0…

2026/10/9 1:58:18 阅读更多 →
为什么团队应该从Cypress升级到e2e?3个真实场景分析

为什么团队应该从Cypress升级到e2e?3个真实场景分析

为什么团队应该从Cypress升级到e2e?3个真实场景分析 【免费下载链接】e2e Next generation e2e testing framework for web and mobile apps. 项目地址: https://gitcode.com/GitHub_Trending/e2e6/e2e e2e 是一个面向 Web 和移动应用的新一代端到端&#xf…

2026/10/9 1:58:18 阅读更多 →
研究生计算机网络实验3指南:抓包分析TCP状态机与重传机制

研究生计算机网络实验3指南:抓包分析TCP状态机与重传机制

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

2026/10/9 1:58:18 阅读更多 →
OpenPencil Vue SDK 中的 useI18n:读取本地化界面消息并构建自定义语言切换器

OpenPencil Vue SDK 中的 useI18n:读取本地化界面消息并构建自定义语言切换器

前端桌面应用AI 应用MCP 服务 【免费下载链接】open-pencil AI-native design editor. Open-source Figma alternative. 项目地址: https://gitcode.com/gh_mirrors/op/open-pencil 点击查看 免费下载 useI18n() 是 OpenPencil Vue SDK(open-pencil/vue…

2026/10/9 1:57:18 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* 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 13:34:55 阅读更多 →