导读:本期聚焦于小伙伴创作的《MediaWiki扩展开发教程:如何获取页面编辑前后的内容与处理差异》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《MediaWiki扩展开发教程:如何获取页面编辑前后的内容与处理差异》有用,将其分享出去将是对创作者最好的鼓励。

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

在MediaWiki扩展开发中,捕获页面编辑前后的内容差异是一项常见且重要的需求。无论是构建审计日志系统、自动生成变更摘要,还是实现内容差异分析功能,掌握这一技术都至关重要。本教程将深入讲解如何在MediaWiki扩展中使用PHP获取页面编辑前与编辑后的内容,并提供完整的代码示例。

MediaWiki扩展开发教程:如何获取页面编辑前后的内容与处理差异

理解MediaWiki的编辑流程与钩子机制

MediaWiki的内容修改流程涉及多个阶段,从用户打开编辑界面到最终保存页面,每个阶段都提供了相应的钩子(Hook)供扩展开发者介入。要获取编辑前后的内容,我们需要关注以下核心钩子:

  • EditPage::showEditForm - 在显示编辑表单时触发,此时可以获取页面的当前版本内容(编辑前内容)。

  • PageContentSave - 在页面内容保存到数据库之前触发,此时可以获取即将保存的新内容(编辑后内容)。

  • PageContentSaveComplete - 在页面内容成功保存到数据库之后触发,此时可以获取已保存的新内容以及旧内容。

扩展的基础结构与注册

首先,我们需要创建一个基本的MediaWiki扩展。假设我们的扩展名为ContentDiffTracker,用于追踪页面编辑前后的内容变化。

创建扩展目录和文件:

<?php
// ContentDiffTracker.php - 扩展主文件

if ( !defined( 'MEDIAWIKI' ) ) {
    die( 'This file is a MediaWiki extension and cannot be accessed directly.' );
}

$wgExtensionCredits['other'][] = array(
    'path'           => __FILE__,
    'name'           => 'ContentDiffTracker',
    'author'         => 'Your Name',
    'url'            => 'https://www.ipipp.com',
    'description'    => 'Tracks content changes before and after page edits.',
    'version'        => '1.0.0',
);

// 注册钩子
$wgHooks['EditPage::showEditForm'][] = 'ContentDiffTracker::onShowEditForm';
$wgHooks['PageContentSave'][]         = 'ContentDiffTracker::onPageContentSave';

上述代码注册了两个核心钩子,接下来我们需要实现对应的处理方法。

获取编辑前的内容

当用户点击"编辑"标签打开编辑界面时,EditPage::showEditForm钩子被触发。此时我们可以获取页面的当前版本内容,即编辑前的内容。

<?php

class ContentDiffTracker {

    /**
     * 处理 EditPage::showEditForm 钩子
     * 在显示编辑表单时获取当前页面内容(编辑前内容)
     *
     * @param EditPage $editPage 编辑页面对象
     * @param OutputPage $output 输出页面对象
     */
    public static function onShowEditForm( $editPage, $output ) {
        // 获取页面对象
        $title = $editPage->getTitle();
        $wikiPage = WikiPage::factory( $title );

        // 获取当前页面的最新版本内容
        $content = $wikiPage->getContent();

        if ( $content instanceof TextContent ) {
            $currentText = $content->getText();
        } else {
            // 对于非文本内容(如JSON内容模型),根据需要处理
            $currentText = $content ? $content->serialize() : '';
        }

        // 将编辑前内容存储在静态变量中,以便后续钩子使用
        self::$preEditContent = $currentText;

        // 可以根据需要记录日志或进行其他处理
        wfDebugLog( 'ContentDiffTracker', 'Pre-edit content retrieved for page: ' . $title->getPrefixedText() );

        return true;
    }

    // 存储编辑前内容的静态变量
    public static $preEditContent = null;
}

获取编辑后的内容

当用户点击"保存页面"时,PageContentSave钩子在内容实际写入数据库之前被触发。此时我们可以获取即将保存的新内容,并与之前存储的编辑前内容进行对比。

<?php

class ContentDiffTracker {

    // 在 onShowEditForm 中已定义的静态变量
    public static $preEditContent = null;

    /**
     * 处理 PageContentSave 钩子
     * 在内容保存到数据库之前获取新内容(编辑后内容)
     *
     * @param WikiPage $wikiPage 页面对象
     * @param User $user 执行编辑的用户
     * @param Content $content 即将保存的新内容对象
     * @param string $summary 编辑摘要
     * @param bool $isMinor 是否为小编辑
     * @param bool $isWatch 是否监视此页面
     * @param int $section 编辑的章节编号
     * @param int $flags 标志位
     * @param Status $status 状态对象
     */
    public static function onPageContentSave( $wikiPage, $user, $content, $summary,
                                               $isMinor, $isWatch, $section, $flags, $status ) {
        // 获取页面标题
        $title = $wikiPage->getTitle();

        // 获取编辑后的内容
        if ( $content instanceof TextContent ) {
            $newText = $content->getText();
        } else {
            $newText = $content ? $content->serialize() : '';
        }

        // 获取之前存储的编辑前内容
        $oldText = self::$preEditContent;

        // 如果编辑前内容为空,则尝试从页面当前版本获取
        if ( $oldText === null ) {
            $currentContent = $wikiPage->getContent();
            if ( $currentContent instanceof TextContent ) {
                $oldText = $currentContent->getText();
            } else {
                $oldText = $currentContent ? $currentContent->serialize() : '';
            }
        }

        // 比较新旧内容
        if ( $oldText !== $newText ) {
            // 内容发生了变化
            self::logContentChange( $title, $user, $oldText, $newText );

            // 可以在这里执行差异分析、通知等操作
            self::processContentDiff( $oldText, $newText, $title );
        }

        return true;
    }

    /**
     * 记录内容变更
     */
    private static function logContentChange( $title, $user, $oldText, $newText ) {
        $logEntry = sprintf(
            'Page: %s | User: %s | Old length: %d | New length: %d',
            $title->getPrefixedText(),
            $user->getName(),
            strlen( $oldText ),
            strlen( $newText )
        );

        wfDebugLog( 'ContentDiffTracker', $logEntry );
    }

    /**
     * 处理内容差异(示例:计算简单的行数差异)
     */
    private static function processContentDiff( $oldText, $newText, $title ) {
        $oldLines = explode( "n", $oldText );
        $newLines = explode( "n", $newText );

        $addedLines = array_diff( $newLines, $oldLines );
        $removedLines = array_diff( $oldLines, $newLines );

        if ( !empty( $addedLines ) || !empty( $removedLines ) ) {
            $diffSummary = sprintf(
                'Lines added: %d | Lines removed: %d',
                count( $addedLines ),
                count( $removedLines )
            );

            wfDebugLog( 'ContentDiffTracker', $diffSummary . ' for page: ' . $title->getPrefixedText() );
        }
    }
}

使用PageContentSaveComplete钩子获取完整上下文

有时候我们需要在内容保存完成后再获取旧内容和新内容,此时可以使用PageContentSaveComplete钩子。这个钩子提供了编辑前后的内容对象,更加方便。

<?php

class ContentDiffTracker {

    /**
     * 处理 PageContentSaveComplete 钩子
     * 在内容成功保存到数据库后触发
     *
     * @param WikiPage $wikiPage 页面对象
     * @param User $user 执行编辑的用户
     * @param Content $content 保存后的新内容
     * @param string $summary 编辑摘要
     * @param bool $isMinor 是否为小编辑
     * @param bool $isWatch 是否监视此页面
     * @param int $section 编辑的章节编号
     * @param int $flags 标志位
     * @param Revision $revision 新版本对象
     * @param Status $status 状态对象
     * @param int $baseRevId 基础版本ID
     * @param int $undidRevId 被撤销的版本ID(如果有)
     */
    public static function onPageContentSaveComplete( $wikiPage, $user, $content, $summary,
                                                       $isMinor, $isWatch, $section, $flags,
                                                       $revision, $status, $baseRevId, $undidRevId ) {
        // 获取新内容
        if ( $content instanceof TextContent ) {
            $newText = $content->getText();
        } else {
            $newText = $content ? $content->serialize() : '';
        }

        // 获取旧内容:通过基础版本ID获取
        $oldContent = null;
        if ( $baseRevId ) {
            $oldRevision = Revision::newFromId( $baseRevId );
            if ( $oldRevision ) {
                $oldContent = $oldRevision->getContent();
            }
        }

        // 如果无法通过基础版本获取,则尝试获取前一个版本
        if ( !$oldContent ) {
            $oldRevision = $wikiPage->getRevision()->getPrevious();
            if ( $oldRevision ) {
                $oldContent = $oldRevision->getContent();
            }
        }

        $oldText = '';
        if ( $oldContent instanceof TextContent ) {
            $oldText = $oldContent->getText();
        } elseif ( $oldContent ) {
            $oldText = $oldContent->serialize();
        }

        // 新旧内容都已获取,可以进行差异分析
        if ( $oldText !== $newText ) {
            // 执行更详细的差异分析
            self::performDetailedDiff( $oldText, $newText, $wikiPage, $user );
        }

        return true;
    }

    /**
     * 执行详细的差异分析
     */
    private static function performDetailedDiff( $oldText, $newText, $wikiPage, $user ) {
        // 这里可以使用MediaWiki内置的Diff引擎
        $diff = new Diff( explode( "n", $oldText ), explode( "n", $newText ) );
        $diffFormatter = new TableDiffFormatter();
        $diffHtml = $diffFormatter->format( $diff );

        wfDebugLog( 'ContentDiffTracker', 'Detailed diff generated for page: ' .
            $wikiPage->getTitle()->getPrefixedText() );
    }
}

完整的扩展实现

以下是将上述所有功能整合到一个完整扩展中的代码:

<?php
// ContentDiffTracker.php - 完整扩展实现

if ( !defined( 'MEDIAWIKI' ) ) {
    die( 'This file is a MediaWiki extension and cannot be accessed directly.' );
}

$wgExtensionCredits['other'][] = array(
    'path'           => __FILE__,
    'name'           => 'ContentDiffTracker',
    'author'         => 'Your Name',
    'url'            => 'https://www.ipipp.com',
    'description'    => 'Tracks content changes before and after page edits.',
    'version'        => '1.0.0',
);

// 注册所有钩子
$wgHooks['EditPage::showEditForm'][]       = 'ContentDiffTracker::onShowEditForm';
$wgHooks['PageContentSave'][]               = 'ContentDiffTracker::onPageContentSave';
$wgHooks['PageContentSaveComplete'][]       = 'ContentDiffTracker::onPageContentSaveComplete';

class ContentDiffTracker {

    // 存储编辑前内容
    private static $preEditContent = null;
    private static $preEditTitle = null;

    /**
     * EditPage::showEditForm 钩子
     */
    public static function onShowEditForm( $editPage, $output ) {
        $title = $editPage->getTitle();
        $wikiPage = WikiPage::factory( $title );

        $content = $wikiPage->getContent();
        if ( $content instanceof TextContent ) {
            self::$preEditContent = $content->getText();
        } elseif ( $content ) {
            self::$preEditContent = $content->serialize();
        } else {
            self::$preEditContent = '';
        }

        self::$preEditTitle = $title;

        wfDebugLog( 'ContentDiffTracker', 'Pre-edit content captured for ' . $title->getPrefixedText() );
        return true;
    }

    /**
     * PageContentSave 钩子
     */
    public static function onPageContentSave( $wikiPage, $user, $content, $summary,
                                               $isMinor, $isWatch, $section, $flags, $status ) {
        $title = $wikiPage->getTitle();

        // 获取新内容
        if ( $content instanceof TextContent ) {
            $newText = $content->getText();
        } else {
            $newText = $content ? $content->serialize() : '';
        }

        // 获取旧内容
        $oldText = self::$preEditContent;
        if ( $oldText === null ) {
            $currentContent = $wikiPage->getContent();
            if ( $currentContent instanceof TextContent ) {
                $oldText = $currentContent->getText();
            } else {
                $oldText = $currentContent ? $currentContent->serialize() : '';
            }
        }

        // 检测内容变化
        if ( $oldText !== $newText ) {
            $addedLength = strlen( $newText ) - strlen( $oldText );
            wfDebugLog( 'ContentDiffTracker', sprintf(
                'SAVE - Page: %s | User: %s | Change: %+d bytes',
                $title->getPrefixedText(),
                $user->getName(),
                $addedLength
            ));
        }

        return true;
    }

    /**
     * PageContentSaveComplete 钩子
     */
    public static function onPageContentSaveComplete( $wikiPage, $user, $content, $summary,
                                                       $isMinor, $isWatch, $section, $flags,
                                                       $revision, $status, $baseRevId, $undidRevId ) {
        $title = $wikiPage->getTitle();

        // 获取新内容
        if ( $content instanceof TextContent ) {
            $newText = $content->getText();
        } else {
            $newText = $content ? $content->serialize() : '';
        }

        // 获取旧内容
        $oldText = '';
        if ( $baseRevId ) {
            $oldRevision = Revision::newFromId( $baseRevId );
            if ( $oldRevision ) {
                $oldContent = $oldRevision->getContent();
                if ( $oldContent instanceof TextContent ) {
                    $oldText = $oldContent->getText();
                } elseif ( $oldContent ) {
                    $oldText = $oldContent->serialize();
                }
            }
        }

        // 如果基础版本获取失败,尝试前一个版本
        if ( empty( $oldText ) ) {
            $oldRevision = $wikiPage->getRevision()->getPrevious();
            if ( $oldRevision ) {
                $oldContent = $oldRevision->getContent();
                if ( $oldContent instanceof TextContent ) {
                    $oldText = $oldContent->getText();
                } elseif ( $oldContent ) {
                    $oldText = $oldContent->serialize();
                }
            }
        }

        // 最终的内容差异处理
        if ( $oldText !== $newText ) {
            wfDebugLog( 'ContentDiffTracker', sprintf(
                'COMPLETE - Page: %s | User: %s | Old length: %d | New length: %d',
                $title->getPrefixedText(),
                $user->getName(),
                strlen( $oldText ),
                strlen( $newText )
            ));

            // 在此处添加自定义处理逻辑,例如:
            // - 将差异写入到外部数据库
            // - 发送通知给关注者
            // - 更新搜索结果索引
        }

        // 清理静态变量
        self::$preEditContent = null;
        self::$preEditTitle = null;

        return true;
    }
}

配置与启用扩展

完成代码编写后,按照以下步骤启用扩展:

  1. ContentDiffTracker.php文件放置在MediaWiki的extensions/ContentDiffTracker/目录下。

  2. LocalSettings.php中添加以下配置:

// 启用扩展
wfLoadExtension( 'ContentDiffTracker' );

// 可选:启用调试日志
$wgDebugLogGroups['ContentDiffTracker'] = '/path/to/your/logfile.log';

进阶应用场景

掌握了获取编辑前后内容的基础方法后,可以将其应用于更多实际场景:

  • 内容审核工作流:在PageContentSave钩子中检测敏感内容,阻止或标记可疑编辑。

  • 自动生成变更摘要:基于内容差异自动生成结构化的编辑摘要,提升协作效率。

  • 页面变动通知:当页面内容发生变化时,通过邮件或即时消息通知相关用户。

  • 内容版本统计:统计用户编辑的字符数变化,用于贡献度计算或游戏化机制。

  • 差异可视化:生成类似于维基百科的差异视图,在自定义页面中展示版本间变化。

例如,以下代码展示了如何在PageContentSave钩子中阻止包含特定关键词的编辑:

public static function onPageContentSave( $wikiPage, $user, $content, $summary,
                                           $isMinor, $isWatch, $section, $flags, $status ) {
    $newText = $content instanceof TextContent ? $content->getText() : '';

    // 黑名单关键词
    $blockedWords = array( 'spam', 'malware', 'inappropriate' );

    foreach ( $blockedWords as $word ) {
        if ( stripos( $newText, $word ) !== false ) {
            // 设置错误状态,阻止保存
            $status->fatal( 'contentdifftracker-blocked-word', $word );
            return false; // 返回 false 以终止保存操作
        }
    }

    return true;
}

总结

本教程详细讲解了在MediaWiki扩展开发中获取页面编辑前后内容的完整方法。通过合理利用EditPage::showEditFormPageContentSavePageContentSaveComplete这三个核心钩子,我们可以在编辑流程的不同阶段获取所需的内容数据,并在此基础上构建丰富的功能。

关键要点回顾:

  • 使用EditPage::showEditForm钩子获取编辑前的内容,通常存储在静态变量中供后续使用。

  • 使用PageContentSave钩子在保存前获取新内容,并可选择中断保存操作。

  • 使用PageContentSaveComplete钩子在保存完成后获取完整的上下文信息,包括新旧版本内容。

  • 对于非文本内容模型(如JSON、JavaScript等),需要使用serialize()方法获取文本表示。

  • 在生产环境中,请确保处理各种边缘情况,如页面创建(旧内容为空)、内容模型变更等。

掌握这些技术后,您将能够构建出功能强大且灵活的MediaWiki扩展,满足各种内容管理需求。如有更复杂的场景需求,建议查阅MediaWiki官方文档或社区扩展的实现代码以获取更多灵感。

MediaWiki扩展开发 钩子机制 PageContentSave EditPage 内容差异处理

免责声明:已尽一切努力确保本网站所含信息的准确性。网站部分内容来源于网络或由用户自行发表,内容观点不代表本站立场。本站是个人网站免费分享,内容仅供个人学习、研究或参考使用,如内容中引用了第三方作品,其版权归原作者所有。若内容触犯了您的权益,请联系我们进行处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。前端、网络、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握网站开发与运维所需的核心技术栈。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端逻辑,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。