0

0

MediaWiki扩展中获取页面编辑前后内容的教程

心靈之曲

心靈之曲

发布时间:2025-10-09 10:16:01

|

266人浏览过

|

来源于php中文网

原创

MediaWiki扩展中获取页面编辑前后内容的教程

在MediaWiki扩展的MultiContentSave钩子中,开发者需要获取页面在保存编辑前后的内容以进行比较或处理。本文将详细指导如何利用RenderedRevision对象获取新内容,并通过getParentId()和RevisionStore服务获取前一版本内容,从而实现对页面编辑前后内容的全面访问和处理。

理解 MultiContentSave 钩子

multicontentsave 钩子是mediawiki提供的一个关键扩展点,它在页面内容被保存(无论是新建、编辑还是回退)之后触发。这个钩子为开发者提供了在内容持久化到数据库后,对页面内容进行进一步处理、分析或记录的机会。

钩子的签名如下:

public static function onMultiContentSave(
    RenderedRevision $renderedRevision,
    UserIdentity $user,
    CommentStoreComment $summary,
    $flags,
    Status $hookStatus
)

其中,$renderedRevision 参数是核心,它包含了当前保存的修订版本的所有信息,包括新内容。

获取编辑后的新内容

获取页面编辑后的新内容相对直接。$renderedRevision 对象封装了当前保存的修订版本 (Revision)。我们可以通过它来访问新内容。

use MediaWiki\Revision\RenderedRevision;
use MediaWiki\Revision\RevisionRecord;
use Wikimedia\Content\SlotRecord;

class MyExtensionHooks {
    public static function onMultiContentSave(
        RenderedRevision $renderedRevision,
        UserIdentity $user,
        CommentStoreComment $summary,
        $flags,
        Status $hookStatus
    ) {
        // 获取当前修订版本对象
        $revision = $renderedRevision->getRevision();

        // 获取页面的 LinkTarget 对象(包含页面标题信息,非直接字符串)
        $title = $revision->getPageAsLinkTarget();

        // 获取编辑后的新内容
        // SlotRecord::MAIN 表示主内容槽
        // RevisionRecord::RAW 表示获取原始维基文本内容
        $new_content_object = $revision->getContent(SlotRecord::MAIN, RevisionRecord::RAW);
        $new_content_text = $new_content_object ? $new_content_object->getNativeData() : '';

        // $new_content_text 现在包含了编辑后的页面内容

        return true;
    }
}

上述代码中,$revision->getContent(SlotRecord::MAIN, RevisionRecord::RAW) 返回一个 Content 对象。通过调用 $new_content_object->getNativeData(),我们可以获取到其原始的字符串表示(通常是维基文本)。

获取编辑前的旧内容

获取编辑前的旧内容是本教程的核心挑战。MediaWiki的修订版本系统通过父子关系来跟踪历史。当前修订版本对象 ($revision) 包含了对其父版本(即编辑前的版本)的引用。

以下是获取旧内容的步骤:

  1. 获取父版本ID: 每个修订版本都有一个父版本ID,指向它所基于的上一版本。我们可以通过 $revision->getParentId() 方法获取这个ID。

    • 如果页面是首次创建,getParentId() 将返回 0,表示没有父版本。
    • 如果父版本未知或未定义,它可能返回 null。
  2. 通过ID加载前一个修订版本: 一旦我们有了父版本ID,就可以使用 MediaWiki\Revision\RevisionStore 服务来加载对应的修订版本对象。RevisionStore::getRevisionById() 方法可以根据ID检索修订版本。

    • 如果找不到对应的修订版本,此方法将返回 null。
  3. 从前一个修订版本中提取内容: 加载到前一个修订版本对象后,获取其内容的方式与获取新内容类似。

下面是获取旧内容的具体实现:

use MediaWiki\Revision\RenderedRevision;
use MediaWiki\WikiPage\Revision\RevisionStore; // 确保引入正确的 RevisionStore
use MediaWiki\Revision\RevisionRecord;
use Wikimedia\Content\SlotRecord;
use MediaWiki\Content\ContentHandler; // 用于将Content对象转换为文本

class MyExtensionHooks {
    public static function onMultiContentSave(
        RenderedRevision $renderedRevision,
        UserIdentity $user,
        CommentStoreComment $summary,
        $flags,
        Status $hookStatus
    ) {
        $revision = $renderedRevision->getRevision();
        $title = $revision->getPageAsLinkTarget(); // 获取页面的 LinkTarget 对象

        // 获取编辑后的新内容
        $new_content_object = $revision->getContent(SlotRecord::MAIN, RevisionRecord::RAW);
        $new_content_text = $new_content_object ? $new_content_object->getNativeData() : '';

        // --- 获取编辑前的旧内容 ---

        $old_content_text = ''; // 初始化旧内容为空字符串

        // 1. 获取父版本ID
        $parent_id = $revision->getParentId();

        // 2. 检查是否存在父版本(即不是首次创建)
        if ($parent_id !== 0 && $parent_id !== null) {
            // 3. 通过父版本ID加载前一个修订版本
            // RevisionStore 是一个服务,通常通过 MediaWiki\MediaWikiServices::getInstance()->getRevisionStore() 获取
            // 但在钩子环境中,可以直接访问静态方法 RevisionStore::getRevisionById()
            $previous_revision = RevisionStore::getRevisionById($parent_id);

            // 4. 检查是否成功加载到前一个修订版本
            if ($previous_revision) {
                // 5. 从前一个修订版本中提取内容
                // Revision::RAW 获取原始内容
                $old_content_object = $previous_revision->getContent(SlotRecord::MAIN, RevisionRecord::RAW);

                // 6. 将内容对象转换为文本
                // ContentHandler::getContentText() 是一个通用的转换方法
                $old_content_text = $old_content_object ? ContentHandler::getContentText($old_content_object) : '';
            }
        }

        // $old_content_text 现在包含了编辑前的页面内容

        // ... 在这里可以对 $new_content_text 和 $old_content_text 进行比较或处理

        return true;
    }
}

完整示例:比较编辑前后内容

将新旧内容获取逻辑整合在一起,可以方便地进行内容比较,例如实现一个简单的内容差异检测或审计功能。

剪映
剪映

一款全能易用的桌面端剪辑软件

下载
getRevision();
        $pageTitle = $currentRevision->getPageAsLinkTarget()->getText(); // 获取页面标题字符串

        // --- 获取编辑后的新内容 ---
        $newContentObject = $currentRevision->getContent(SlotRecord::MAIN, RevisionRecord::RAW);
        $newContentText = $newContentObject ? ContentHandler::getContentText($newContentObject) : '';

        // --- 获取编辑前的旧内容 ---
        $oldContentText = ''; // 默认旧内容为空

        $parentId = $currentRevision->getParentId();

        // 检查是否存在父版本 (即不是首次创建页面)
        if ($parentId !== 0 && $parentId !== null) {
            // 通过父版本ID加载前一个修订版本
            $previousRevision = RevisionStore::getRevisionById($parentId);

            if ($previousRevision) {
                // 从前一个修订版本中提取内容
                $oldContentObject = $previousRevision->getContent(SlotRecord::MAIN, RevisionRecord::RAW);
                $oldContentText = $oldContentObject ? ContentHandler::getContentText($oldContentObject) : '';
            } else {
                // 无法加载到前一个修订版本 (可能ID无效或已删除)
                wfDebugLog('ContentComparisonExtension', "Warning: Could not load previous revision with ID $parentId for page $pageTitle.");
            }
        } else {
            // 这是页面的首次创建,没有旧内容
            wfDebugLog('ContentComparisonExtension', "Page '$pageTitle' was created. No old content to compare.");
        }

        // --- 进行内容比较或进一步处理 ---
        if ($oldContentText !== $newContentText) {
            // 内容发生了变化
            wfDebugLog('ContentComparisonExtension', "Page '$pageTitle' content changed.");
            // 可以在此处执行差异分析、记录日志、发送通知等操作
            // 例如:
            // $diff = new \MediaWiki\Diff\TextDiffer();
            // $changes = $diff->getDiff($oldContentText, $newContentText);
            // ...
        } else {
            // 内容没有变化 (可能只是保存了空编辑或元数据编辑)
            wfDebugLog('ContentComparisonExtension', "Page '$pageTitle' content unchanged.");
        }

        return true; // 总是返回 true,除非你想阻止保存操作
    }
}

在 extension.json 中注册钩子:

{
    "name": "ContentComparisonExtension",
    "version": "1.0.0",
    "AutoloadClasses": {
        "ContentComparisonExtensionHooks": "ContentComparisonExtensionHooks.php"
    },
    "Hooks": {
        "MultiContentSave": [
            "ContentComparisonExtensionHooks::onMultiContentSave"
        ]
    },
    "manifest_version": 2
}

注意事项与最佳实践

  1. 错误处理与空值检查:

    • $revision->getParentId() 可能返回 0 或 null。在尝试加载前一个修订版本之前,务必进行检查。
    • RevisionStore::getRevisionById() 可能返回 null。在尝试访问 null 对象的属性之前,也应进行检查。
    • getContent() 方法返回的 Content 对象也可能为 null,因此在调用 getNativeData() 或 ContentHandler::getContentText() 之前进行检查。
  2. 性能考虑:

    • 频繁地加载旧版本内容可能会对大型Wiki的性能产生影响,尤其是在高流量或频繁编辑的页面上。如果只需要简单的标记或审计,可以考虑只存储哈希值进行比较,而不是加载完整内容。
  3. 内容格式:

    • RevisionRecord::RAW 用于获取原始的维基文本。如果页面内容是其他类型(例如JSON或自定义内容模型),可能需要使用不同的 SlotRecord 或 RevisionRecord 常量,并使用相应的 ContentHandler 方法进行处理。
    • SlotRecord::MAIN 用于获取页面的主要内容槽。MediaWiki 5.x 版本引入了多内容槽机制,如果你的扩展需要处理非主内容槽的内容,需要指定相应的 SlotRecord 常量。
  4. 钩子返回值:

    • MultiContentSave 钩子通常应返回 true,表示钩子已成功处理且不应阻止后续操作。如果返回 false,可能会中断MediaWiki的正常保存流程。

总结

通过本教程,我们详细学习了如何在MediaWiki扩展的MultiContentSave钩子中,有效地获取页面编辑前后的内容。关键在于利用RenderedRevision对象获取当前修订版本,并通过其getParentId()方法和RevisionStore服务来检索并访问前一个修订版本的内容。掌握这些技术,开发者可以构建出功能强大、能够深入分析和处理页面内容变化的MediaWiki扩展。在实际开发中,务必注意错误处理、性能优化以及内容格式的兼容性。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

400

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

528

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

306

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

69

2025.09.10

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

226

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

430

2024.03.01

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1428

2023.10.24

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

246

2023.08.03

虚拟号码教程汇总
虚拟号码教程汇总

本专题整合了虚拟号码接收验证码相关教程,阅读下面的文章了解更多详细操作。

25

2025.12.25

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 7.9万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 6.9万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号