定制 MediaWiki ,维基百科背后的 wiki 引擎

红薯 发布于 2010/08/09 16:06
阅读 3K+
收藏 7

简介

MediaWiki 应用程序最为人所知之处就是它是 Wikipedia 背后的引擎。很多人都发现 MediaWiki 提供了一个可用的环境来在工作组、甚至整个组织以及在线社区之间共享信息。MediaWiki 让用户可以通过博客、wiki 以及文件来共享信息。它还允许使用一个标记云来保护所上载的文件,标记文件以便轻松定位,以及定位专家。

那么,如果想要引入一些不想手动更新的定制信息并插入到您的 wiki 页面,该怎么办呢? 又如果想要为特定的某种信息提供定制的输出格式化,又该怎样呢?这些情况正是 MediaWiki 的用武之地,因为您可以轻松地通过扩展的使用来添加这些特定于站点的特性。

让我们来看看您如何创建能够与不同的信息源协作的 MediaWiki 扩展,而同时又能通过熟悉的 wiki 页的用户界面来提供数据。

MediaWiki 扩展

扩展可以向用来撰写文章的 wiki markup 中添加新的标记,通过创建特殊页面添加新的报告和管理特性,通过格式化皮肤更改 wiki 的观感,甚至与外部的身份验证方法相集成(但是身份验证不会在本文中介绍)。

扩展用 PHP 编写并利用了 MediaWiki 的各种内部 hook、类和方法来使其作业有效完成。在您使用任何受支持的 Web 服务器以及自己偏爱的 PHP 开发环境开发和部署 MediaWiki 时,您将会用到本文中所列的如下工具:

  • Eclipse V3.5.2 — 支持大量编程语言和环境
  • PHP Development Tools (PDT) V2.2.0 — 一个面向 Eclipse 的 PHP 插件
  • MAMP Pro V1.9 — 一个面向 Mac OS X 的十分便利的包,用一个有用的 GUI 前端在一个隔离的环境中提供了 Apache、 MySQL 和 PHP。虽然 OS X 自带了安装好了的 Apache 和 PHP,但是我更愿意使用这个包,因为 OS X V10.6 Snow Leopard (V5.3.1) 内的 PHP 版本有一个明显的 bug,有碍于 MediaWiki 的正确运行。
  • MediaWiki V1.15.3 — MediaWiki 的当前稳定版本

在深入探究不同类型的扩展之前,让我们先来看看为大多数扩展使用的文件夹和文件布局。之后,是对皮肤扩展的高度概览以供您更改您的 MediaWiki 站点。接下来,将创建一个可生成管理性统计数据的特殊页扩展。最后,您将看到如何添加定制 XML 标记 markup 支持以供您在编写 wiki 页面时使用。

扩展剖析

MediaWiki 扩展安装于主 MediaWiki 路径的 extensions 目录。大多数现代的扩展都安装于其自己的目录并一般包含如下三个文件(extension 是扩展的名称):

  • extension/extension.php
  • extension/extension.body.php
  • extension/extension.i18n.php

第一个文件执行初始化以及设置任务。第二个文件是扩展的主体,是实现扩展的工作代码。最后一个文件包含国际化(i18n 是一个常见缩写)字符串。通过将您的扩展消息字符串提取到这个 i18n 文件,就可以为任何 MediaWiki 支持的本地环境提供扩展的本地化版本(假设您能够找到翻译文本的帮助)。

举个例子,假如我创建了一个名为 CHTimeStamp 的 Hello World 扩展(有关本例的源代码以及本文中其他示例的源代码,参见 下载)。只要有人在 wiki 页面上插入了 {{CHSTAMP}}CHTimeStamp 就会插入当前的日期/时间戳。它包含如下文件:

  • CHTimeStamp/CHTimeStamp.php
  • CHTimeStamp/CHTimeStamp.body.php
  • CHTimeStamp/CHTimeStamp.i18n.php


图 1. Eclipse 内的 CHTimeStamp 布局
Eclipse 内的 CHTimeStamp 文件结构的屏幕快照

CHTimeStamp 扩展会向 MediaWiki 的 markup 添加一个 {{CHSTAMP}} 变量。每当您将 {{CHSTAMP}} 放入一个页面,它都会被一个时间戳取代。这不难理解,对么?如果您有兴趣,可以查看源代码;我在此只给出一个大概,旨在为您介绍 MediaWiki 扩展的总体布局和约定。

我的 CHTimeStamp.php 注册这个国际化消息文件,告诉 wiki 引擎它能够在 CHTimeStamp.body.php 找到 CHTimeStamp 类并将此 CHTimeStamp::registerHooks 方法添加到扩展函数的数组。

在 CHTimeStamp.body.php 内,定义 CHTimeStamp 类。如果仔细看一下此代码,就会发现这完全由静态方法组成,所以它也可以在无需更改扩展行为的情况下作为一系列函数编写。CHTimeStampregisterHooks 方法注册这些静态方法来创建这个 {{CHSTAMP}} 变量以及处理使用此变量的那些页面。

最后,在 CHTimeStamp.i18n.php 内,我为扩展内惟一的静态字符串(它的描述)创建了译文。借助 Google Translate,CHTimeStamp 可支持法语、德语以及西班牙语的本地语言环境。但我希望这种自动翻译不会将我的英语翻译成难以理解(或不适当的)的东西!

创建或下载了扩展后,需要将它安装到 MediaWiki 内并激活它。

安装一个扩展

为您的 MediaWiki 站点准备好一个有趣且有用的扩展后,您肯定想要安装并启用它:

  1. 将此扩展复制并解压缩到 MediaWiki 的 extensions 目录。
  2. 编辑 MediaWiki 根目录内的 LocalSettings.php。使用您喜爱的文本编辑器,添加一些行来配置这个新扩展,然后使用 PHP 的 require_once() 语句激活它。

比如,为了安装 CHTimeStamp,我已经将它的 CHTimeStamp 目录复制到了 extensions 目录并将如下语句添加到了 LocalSettings.php:require_once( "$IP/extensions/CHTimeStamp/CHTimeStamp.php" );

通过访问 wiki 的 Special:Version 页面,可以检查扩展是否已经被成功加载。除了有关所运行的 MediaWiki 版本的信息外,这个 Special:Version 页面还会列出已成功加载的扩展。


图 2. Special:Version 页面显示 CHTimeStamp
MediaWiki 的 Version 页面的屏幕快照显示了 CHTimeStamp 扩展已经安装

定制观感

MediaWiki 利用了 PHP 的混合代码功能以及 HTML markup 来让您能够通过皮肤的使用控制您 wiki 的观感。除了主要的 PHP 代码之外,一个皮肤可以包含各种 CSS 文件以及支持的图像或 JavaScript。

一个皮肤通常包含两个 PHP 文件、一个内含其他支持文件的目录。比如,著名的默认皮肤 MonoBook 的组成如下:

  • MonoBook.php — 主要的 MonoBook 皮肤代码
  • MonoBook.deps.php — 对 PHP V5 的 APC opcode 缓存内的 bug 的修复
  • monobook/ — 支持的 CSS 和图片

皮肤的命名约定非常严格,要求必须是 SkinName.php、SkinName.deps.php,并且以 skinname(小写)作为这个支持文件夹的名称。

在这个 skinname 文件夹内有皮肤样式所需的 main.css。特定于浏览器的样式修复也位于此处,所以可以经常在这里看到 FF2Fixes.css、IE6Fixes.css、Opera6Fixes.css 等。

SkinName.php 将开始于某些有用的元数据。

清单 1. MediaWiki 皮肤元数据

				
/**
* [SkinName] skin
*
* @file
* @ingroup Skins
* @version [#].[#].[#]
* @author [name] ([URL] / [E-Mail])
* @license [URL] [name]
*/

可以用适合于您皮肤的内容代替上述代码中方括号中的内容。

接下来,需要创建 SkinTemplate 的子类并覆盖 initPage 方法来表明您皮肤的名称、样式和模板。请记住用您皮肤的名称替换 SkinNameskinname


清单 2. 扩展 SkinTemplate 来提供一个新皮肤

				
// inherit main code from SkinTemplate, set the CSS and template filter
class SkinSkinName extends SkinTemplate {
function initPage( OutputPage $out ) {
parent::initPage( $out );
$this->skinname = 'skinname';
$this->stylename = 'skinname';
$this->template = 'SkinNameTemplate';
}
}

您皮肤的主要工作将位于您的 QuickTemplate 子类中。


清单 3. 大多数的工作都是在这个模板中完成的

				
class SkinNameTemplate extends QuickTemplate {
...
/**
* Template filter callback for this skin.
* Takes an associative array of data set from a SkinTemplate-based
* class, and a wrapper for MediaWiki's localization database, and
* outputs a formatted page.
*/
public function execute() {
global $wgUser, $wgSitename;
$skin = $wgUser->getSkin();

// retrieve site name
$this->set( 'sitename', $wgSitename );

// suppress warnings to prevent notices about missing indexes
// in $this->data
wfSuppressWarnings();

/* compose XHTML output */

?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
...

QuickTemplate 子类的内部,通过覆盖这些方法,可以随心所欲地格式化和样式化类别列表和交叉引用等。这个类的 execute 方法将整个页面布局为一个 XHTML 文档,让您得以完全控制页面的组织和样式化。

在这里,您不能进行 XHTML 和 CSS 页面的布局和样式化,但可以先看一下您 MediaWiki 的 skins 文件夹内的示例并立即尝试。

添加特殊页

MediaWiki 内的特殊页是按需生成的,目的是做某些特定的并且对 wiki 可能有用的事情,比如让您编辑系统范围的消息文本、列出所安装的扩展或获得外部链接的列表。

除非特别指定,否则特殊页将对所有人可用并会出现在 Special:SpecialPages 特殊页列表内。此外,还可以设置特殊页以便使用 {{Special:YourPageName}} 语法就能将它包含在页面上。

与其他的扩展类似,特殊页也作为一个目录安装在 extensions 文件夹内。它们通常包含如下 4 个文件:

  • specialpage/specialpage.php — 扩展的设置文件
  • specialpage/specialpage.aliases.php — 特殊页名称的别名
  • specialpage/specialpage.body.php — 特殊页的主要代码
  • specialpage/specialpage.i18n.php — 特殊页的国际化字符串

例如,如果要创建一个名为 CHStats 的特殊页,那么它的布局将类似于图 3。


图 3. Eclipse 内的 CHStats
这个屏幕快照显示了 Eclipse 内的 CHStats 文件结构

CHStats.php 内的代码会为此扩展添加更多的功能,注册别名、主体和 i18n 文件并告诉这个 wiki 引擎在需要时自动加载 CHStats 类。

清单 4. 设置 CHStats 特殊页

<?php
# This is not a valid entry point to MediaWiki.
if( !defined( 'MEDIAWIKI' ) ) {
echo <<<EOT
To install CHStats, put the following line in LocalSettings.php:
require_once( "\$IP/extensions/CHStats/CHStats.php" );
EOT;
exit( 1 );
}

# Take credit for this extension.
$wgExtensionCredits['specialpage'][] = array(
'name' => 'CHStats',
'author' => 'Chris Herborth (chrish@pobox.com)',
'url' => 'http://www.pobox.com/~chrish/CHStats/',
'description' => 'A simple special page demonstration, showing some DB stats.',
'descriptionmsg' => 'chstats-desc',
'version' => '1.0.0',
);

$dir = dirname( __FILE__ ) . '/';

# Register the extension's main code/class.
$wgAutoloadClasses['CHStats'] = $dir . 'CHStats.body.php';

# Register our internationalization files.
$wgExtensionMessagesFiles['CHStats'] = $dir . 'CHStats.i18n.php';
$wgExtensionAliasesFiles['CHStats'] = $dir . 'CHStats.aliases.php';

# Let MediaWiki know about the new special page.
$wgSpecialPages['CHStats'] = 'CHStats';

?>

在 CHStats.body.php 内,创建一个新类 CHStats,这个新类扩展了 SpecialPage 类。在这个构造函数内,初始化父类,然后通过调用 wfLoadExtensionMessages 加载国际化消息。有关 SpecialPage 类构造函数(利用它可限制访问、隐藏页面等)的更多信息,请查阅特殊页开发指南页面。

覆盖 execute 方法是为了生成这个页面。

清单 5. 生成 CHStats 特殊页

				
# This is where the special page's output is created.
function execute( $par ) {
global $wgOut;

# Initialize the output page.
$this->setHeaders();

# Do stuff.
$wgOut->addWikiText( "Some stats about this '''Wiki''':" );

$db = wfGetDB( DB_SLAVE );
// SELECT ... FROM site_stats
$result = $db->select( 'site_stats',
array( 'ss_total_views', 'ss_total_edits',
'ss_total_pages', 'ss_users' ) );
$statList = array();
foreach( $result as $row ) {
$statList[] = '* Total page views: ' . $row->ss_total_views;
$statList[] = '* Total page edits: ' . $row->ss_total_edits;
$statList[] = '* Total # of users: ' . $row->ss_users;
}

$wgOut->addWikiText( implode( "\n", $statList ) );

$wgOut->addWikiText( "That's it." );
}

execute 方法内,$par 参数是子页面。例如,如果加载 Special:CHStats/foo$par 将会被设为 foo

首先,您使用 setHeaders 方法来设置页面 header,然后调用 $wgOut->addWikiText 来编写某些 markup 到输出流。您还能够使用 $wgOut->addHTML 来直接编写格式化了的 HTML ,但我在输出中使用了 wiki markup。有关的更多信息以及如何向特殊页正确添加 wiki markup 和/或 HTML,请查阅特殊页开发指南。

CHStats 页使用了 wfGetDB 函数来获得对数据库的一个引用(使用 DB_SLAVE 进行只读操作,使用 DB_MASTER 进行写操作)。然后从 site_stats 数据库选择几个字段并使用 wiki markup 将结果格式化为项目符号格式的列表。

此特殊页的输出将类似于图 4。


图 4. 实际运行中的 CHStats
这个屏幕快照显示了 CHStats 列出了 lf 统计信息,包括页面视图、页面编辑和全部用户

CHStats.i18n.php 内的国际化字符串包含一个数组,针对每个受支持语言(在本例中为英语、法语、德语和西班牙语)各有一项。每一项中都有一个数组,将字符串 IDs 映射到它们的本地文本(希望如此)。法语、德语和西班牙语的翻译是利用 Google Translate 完成的。

CHStats.aliases.php 具有一个类似的数组,包含了 CHStats 页面名称本身的本地化版本。这可让法国用户(比如)以 Spécial:StatsCH 访问此页面。

添加标记

扩展 MediaWiki 的另一个流行的方式是向 markup 添加对新 XML 标记的支持。这些标记可以基于标记的属性或内容生成不同的输出并且对于插入内联 HTML 甚至大块的格式化了的输出非常有用。

Tag 扩展安装于 extensions 文件夹内的其自己的目录,并使用在本文开始时给出的这三个文件约定。让我们来看由我虚构的一个名为 CHUser 的简单扩展:

  • CHUser/CHUser.php — 扩展设置
  • CHUser/CHUser.body.php — 主要的扩展代码
  • CHUser/CHUser.i18n.php — 国际化数据

CHUser.php 内进行的扩展设置类似于我们已经看到过的那些,只不过这里使用了 $wgHooks 数组来向 ParserFirstCallInit 列表添加此扩展的 init 方法。首次使用时,将会调用 CHUser::init

清单 6. 设置标记扩展

				
# Let MediaWiki know about the new tag.
$wgHooks['ParserFirstCallInit'][] = 'CHUser::init';

在 CHUser.body.php 内,init 方法注册了两个标记:<chuser><bz>(参见清单 7)。这个扩展可在一个扩展内提供两个不同的标记。如果愿意,可以轻松组合本文中讨论的所有扩展,这里并没有任何拆分开来的要求(除非您自己愿意)。

清单 7. 注册 <chuser><bz> 标记

				
public static function init( &$parser ) {
# Add our <chuser> tag handler, the continue.
$parser->setHook( 'chuser', 'CHUser::render' );
$parser->setHook( 'bz', 'CHUser::renderBugzilla' );
return true;
}

只要 wiki markup 引擎遇到一个 <chuser> 标记,它就会调用 CHUser::render 方法,而 <bz> 标记将会调用 CHUser::renderBugzilla

<chuser> 呈现方法选择指定的用户全名和电子邮件地址,并将其格式化成围绕用户全名(如果有的话)的 mailto: 链接的形式。从清单 8 可以看出大多数的逻辑只是为了处理在数据库中未出现用户全名或电子邮件地址的情况(比如我的 wiki 上的 Admin 帐户)。

清单 8. 处理 <chuser>UserName</chuser>

				
public static function render( $input, $args, $parser, $frame ) {
$user = mysql_escape_string( $input );

$db = wfGetDB( DB_SLAVE );
// SELECT ... FROM user
$result = $db->select( 'user',
array( 'user_real_name', 'user_email' ),
'user_name = \'' . $user . '\'' );
$mailtos = array();
foreach( $result as $row ) {
$thisUser = '<span class="user">';

if( $row->user_email ) {
$thisUser = $thisUser . '<a href="mailto:'
. htmlspecialchars( $row->user_email ) . '">';
}

if( $row->user_real_name ) {
$thisUser = $thisUser . htmlspecialchars( $row->user_real_name );
} else {
$thisUser = $thisUser . htmlspecialchars( $input );
}

if( $row->user_email ) {
$thisUser = $thisUser . '</a>';
}

$thisUser = $thisUser . '</span>';

$mailtos[] = $thisUser;
}

return implode( ", ", $mailtos );
}

清单 9 显示了 <bz> 标记是如何使用其 ID 属性链接到 MediaWiki Bugzilla 数据库内的一个指定的 bug 报告的。它展示了处理外部的数据资源是多么地容易。如果想要更为花哨一点,可以使用 Ajax 加载这个 bug 报告,而不是只链接到此报告,并进而显示报告的某些数据,而不是仅显示链接。

清单 9. 到 MediaWiki Bugzilla 的 <bz id="number" /> 链接

				
public static function renderBugzilla( $input, $args, $parser, $frame ) {
$retval = '';
if( $args['id'] ) {
$bzId = htmlspecialchars( $args['id'] );

$retval = '<a href="https://bugzilla.wikimedia.org/show_bug.cgi?id='
. $bzId . '">MediaWiki Bug #' . $bzId . '</a>';
} else {
$retval = '<span class="error">No bug ID specified</span>';
}

return $retval;
}

在图 5 中可以看到实际运行中的这两个标记。


图 5. 实际运行中的扩展
CHUser 扩展的屏幕快照显示了屏幕名称以及用户的真实名称

清单 10 向您显示了对于页面的该部分,这个 wiki markup 看起来是什么样子的。

清单 10. 这个 wiki markup 展示了这些扩展

				
== Extension testing ==

If this is working, we should see a timestamp: {{CHSTAMP}}

CHUser info:

* '''Admin''': <chuser>Admin</chuser>
* '''Chrish''': <chuser>Chrish</chuser>

<bz id="1024" />

正如从 CHUser 扩展中看到的,添加对定制 XML 标记的支持很简单,几乎可随意添加。所有的 PHP 特性和 MediaWiki 服务均可用,所以您可以从外部系统拉出(或向外部系统发送)数据,基于当前用户的凭证和权限更改您的行为或插入 JavaScript 以便直接在查看者的浏览器内运行任务。可能性很多,只受限于您特定的需要和需求。

结束语

在本文中,向您介绍了几个受 MediaWiki 支持的扩展技术,MediaWiki 是一种开源的 wiki 系统,类似于商业的 wiki 软件,比如 Lotus® Connections。

您看到了 MediaWiki 的扩展约定,作为编写 MediaWiki 扩展的一个简单介绍,我还向您展示了如何创建一个简单的 wiki 变量扩展。之后对 MediaWiki 的皮肤特性的概览可帮助您开始创建您自己的定制站点布局。

特殊页面常用来生成信息,不管信息是从数据库还是其他资源检索而来,在本文中就制作了一个特殊页来显示系统的某些统计信息。然后创建了几个定制 XML 标记,可包含在任何页面的 wiki markup 内。

下载本文源码

加载中
0
该用户已被和谐
该用户已被和谐

觉得dokuwiki 挺漂亮的。

返回顶部
顶部