37 parent::__construct(
'Interwiki' );
52 return $this->
msg( $this->canModify() ?
'interwiki' :
'interwiki-title-norights' );
73 $out->addModuleStyles(
'mediawiki.special.interwiki' );
75 $action = $par ?? $request->getRawVal(
'action' );
77 if ( in_array( $action, [
'add',
'edit',
'delete' ] ) && $this->canModify( $out ) ) {
91 private function canModify( $out =
false ) {
92 if ( !$this->
getUser()->isAllowed(
'interwiki' ) ) {
102 $out->addWikiMsg(
'interwiki-cached' );
119 $formDescriptor = [];
124 $status = Status::newGood();
126 $prefix = $request->getText(
'prefix' );
134 'label-message' =>
'interwiki-prefix-label',
141 'id' =>
'mw-interwiki-local',
142 'label-message' =>
'interwiki-local-label',
148 'id' =>
'mw-interwiki-trans',
149 'label-message' =>
'interwiki-trans-label',
155 'id' =>
'mw-interwiki-url',
156 'label-message' =>
'interwiki-url-label',
164 'id' =>
'mw-interwiki-api',
165 'label-message' =>
'interwiki-api-label',
173 'id' =>
"mw-interwiki-{$action}reason",
174 'label-message' =>
'interwiki_reasonfield',
187 'default' => $prefix,
193 'label-message' =>
'interwiki_reasonfield',
199 throw new LogicException(
"Unexpected action: {$action}" );
202 if ( $action ===
'edit' || $action ===
'delete' ) {
203 $dbr = $this->dbProvider->getReplicaDatabase();
204 $row = $dbr->newSelectQueryBuilder()
206 ->from(
'interwiki' )
207 ->where( [
'iw_prefix' => $prefix ] )
208 ->caller( __METHOD__ )->fetchRow();
210 if ( $action ===
'edit' ) {
212 $status->fatal(
'interwiki_editerror', $prefix );
214 $formDescriptor[
'prefix'][
'disabled'] =
true;
215 $formDescriptor[
'prefix'][
'default'] = $prefix;
216 $hiddenFields[
'prefix'] = $prefix;
217 $formDescriptor[
'url'][
'default'] = $row->iw_url;
218 $formDescriptor[
'api'][
'default'] = $row->iw_api;
219 $formDescriptor[
'trans'][
'default'] = $row->iw_trans;
220 $formDescriptor[
'local'][
'default'] = $row->iw_local;
224 $status->fatal(
'interwiki_delfailed', $prefix );
229 if ( !$status->isOK() ) {
230 $formDescriptor = [];
233 $htmlForm = HTMLForm::factory(
'ooui', $formDescriptor, $this->
getContext() );
235 ->addHiddenFields( $hiddenFields )
236 ->setSubmitCallback( $this->onSubmit( ... ) );
238 if ( $status->isOK() ) {
239 if ( $action ===
'delete' ) {
240 $htmlForm->setSubmitDestructive();
243 $htmlForm->setSubmitTextMsg( $action !==
'add' ? $action :
'interwiki_addbutton' )
244 ->setPreHtml( $this->
msg( $action !==
'delete' ?
"interwiki_{$action}intro" :
245 'interwiki_deleting', $prefix )->escaped() )
248 $htmlForm->suppressDefaultSubmit()
250 ->displayForm( $status );
260 private function onSubmit( array $data ) {
261 $status = Status::newGood();
264 $prefix = $data[
'prefix'];
265 $do = $request->getRawVal(
'action' );
270 $validPrefixChars = preg_replace(
'/[ :&=]/',
'', Title::legalChars() );
271 if ( $do ===
'add' && preg_match(
"/\s|[^$validPrefixChars]/", $prefix ) ) {
272 $status->fatal(
'interwiki-badprefix', htmlspecialchars( $prefix ) );
279 && $this->isLanguagePrefix( $prefix )
280 && isset( $virtualDomainsMapping[
'virtual-interwiki-interlanguage'] )
282 $status->fatal(
'interwiki-cannotaddlocallanguage', htmlspecialchars( $prefix ) );
285 $reason = $data[
'reason'];
287 $dbw = $this->dbProvider->getPrimaryDatabase();
290 $dbw->newDeleteQueryBuilder()
291 ->deleteFrom(
'interwiki' )
292 ->where( [
'iw_prefix' => $prefix ] )
293 ->caller( __METHOD__ )->execute();
295 if ( $dbw->affectedRows() === 0 ) {
296 $status->fatal(
'interwiki_delfailed', $prefix );
298 $this->
getOutput()->addWikiMsg(
'interwiki_deleted', $prefix );
299 $log =
new ManualLogEntry(
'interwiki',
'iw_delete' );
300 $log->setTarget( $selfTitle );
301 $log->setComment( $reason );
302 $log->setParameters( [
303 '4::prefix' => $prefix
305 $log->setPerformer( $this->
getUser() );
307 $this->interwikiLookup->invalidateCache( $prefix );
312 $prefix = $this->contLang->lc( $prefix );
317 $theurl = trim( $data[
'url'] );
318 $api = trim( $data[
'api'] ??
'' );
319 $local = $data[
'local'] ? 1 : 0;
320 $trans = $data[
'trans'] ? 1 : 0;
322 'iw_prefix' => $prefix,
326 'iw_local' => $local,
330 if ( $prefix ===
'' || $theurl ===
'' ) {
331 $status->fatal(
'interwiki-submit-empty' );
338 if ( !$this->urlUtils->parse( $theurl ) ) {
339 $status->fatal(
'interwiki-submit-invalidurl' );
343 if ( $do ===
'add' ) {
344 $dbw->newInsertQueryBuilder()
345 ->insertInto(
'interwiki' )
348 ->caller( __METHOD__ )->execute();
350 $dbw->newUpdateQueryBuilder()
351 ->update(
'interwiki' )
353 ->where( [
'iw_prefix' => $prefix ] )
355 ->caller( __METHOD__ )->execute();
359 if ( $dbw->affectedRows() === 0 ) {
360 $status->fatal(
"interwiki_{$do}failed", $prefix );
362 $this->
getOutput()->addWikiMsg(
"interwiki_{$do}ed", $prefix );
363 $log =
new ManualLogEntry(
'interwiki',
'iw_' . $do );
364 $log->setTarget( $selfTitle );
365 $log->setComment( $reason );
366 $log->setParameters( [
367 '4::prefix' => $prefix,
369 '6::trans' => $trans,
372 $log->setPerformer( $this->
getUser() );
374 $this->interwikiLookup->invalidateCache( $prefix );
378 throw new LogicException(
"Unexpected action: {$do}" );
390 private function isLanguagePrefix( $prefix ) {
392 && $this->languageNameUtils->getLanguageName( $prefix );
396 $canModify = $this->canModify();
399 $iwPrefixes = $this->interwikiLookup->getAllPrefixes(
null );
400 $iwGlobalPrefixes = [];
401 $iwGlobalLanguagePrefixes = [];
403 if ( isset( $virtualDomainsMapping[
'virtual-interwiki'] ) ) {
405 $dbrCentralDB = $this->dbProvider->getReplicaDatabase(
'virtual-interwiki' );
406 $res = $dbrCentralDB->newSelectQueryBuilder()
408 ->from(
'interwiki' )
409 ->caller( __METHOD__ )->fetchResultSet();
411 foreach ( $res as $row ) {
413 if ( !$this->isLanguagePrefix( $row[
'iw_prefix'] ) ) {
417 $iwGlobalPrefixes = $retval;
422 $usingGlobalLanguages = isset( $virtualDomainsMapping[
'virtual-interwiki-interlanguage'] );
423 if ( $usingGlobalLanguages ) {
425 $dbrCentralLangDB = $this->dbProvider->getReplicaDatabase(
'virtual-interwiki-interlanguage' );
426 $res = $dbrCentralLangDB->newSelectQueryBuilder()
428 ->from(
'interwiki' )
429 ->caller( __METHOD__ )->fetchResultSet();
431 foreach ( $res as $row ) {
435 if ( $this->isLanguagePrefix( $row[
'iw_prefix'] ) ) {
439 $iwGlobalLanguagePrefixes = $retval2;
443 $iwLocalPrefixes = [];
444 $iwLanguagePrefixes = [];
445 foreach ( $iwPrefixes as $iwPrefix ) {
446 if ( $this->isLanguagePrefix( $iwPrefix[
'iw_prefix'] ) ) {
447 $iwLanguagePrefixes[] = $iwPrefix;
449 $iwLocalPrefixes[] = $iwPrefix;
455 if ( $usingGlobalLanguages ) {
456 unset( $iwLanguagePrefixes );
457 $iwLanguagePrefixes = $iwGlobalLanguagePrefixes;
461 $this->
getOutput()->addWikiMsg(
'interwiki_intro' );
466 $this->
msg(
'interwiki-logtext' )->text()
469 Html::rawElement(
'p', [
'class' =>
'mw-interwiki-log' ], $logLink )
474 if ( count( $iwGlobalPrefixes ) !== 0 ) {
475 if ( $usingGlobalLanguages ) {
476 $addtext =
'interwiki-addtext-local-nolang';
478 $addtext =
'interwiki-addtext-local';
481 if ( $usingGlobalLanguages ) {
482 $addtext =
'interwiki-addtext-nolang';
484 $addtext =
'interwiki_addtext';
487 $addtext = $this->
msg( $addtext )->text();
491 Html::rawElement(
'p', [
'class' =>
'mw-interwiki-addlink' ], $addlink )
495 $this->
getOutput()->addWikiMsg(
'interwiki-legend' );
497 if ( $iwPrefixes === [] && $iwGlobalPrefixes === [] ) {
499 $this->
error(
'interwiki_error' );
504 if ( count( $iwGlobalPrefixes ) !== 0 ) {
508 [
'class' =>
'interwikitable-global' ],
509 $this->
msg(
'interwiki-global-links' )->parse()
512 $this->
getOutput()->addWikiMsg(
'interwiki-global-description' );
515 $this->
makeTable(
false, $iwGlobalPrefixes );
519 if ( count( $iwLocalPrefixes ) !== 0 ) {
520 if ( count( $iwGlobalPrefixes ) !== 0 ) {
524 [
'class' =>
'interwikitable-local' ],
525 $this->
msg(
'interwiki-local-links' )->parse()
528 $this->
getOutput()->addWikiMsg(
'interwiki-local-description' );
533 [
'class' =>
'interwikitable-local' ],
534 $this->
msg(
'interwiki-links' )->parse()
537 $this->
getOutput()->addWikiMsg(
'interwiki-description' );
539 $this->
makeTable( $canModify, $iwLocalPrefixes );
543 if ( count( $iwLanguagePrefixes ) !== 0 ) {
544 if ( $usingGlobalLanguages ) {
545 $header =
'interwiki-global-language-links';
546 $description =
'interwiki-global-language-description';
548 $header =
'interwiki-language-links';
549 $description =
'interwiki-language-description';
555 [
'class' =>
'interwikitable-language' ],
556 $this->
msg( $header )->parse()
559 $this->
getOutput()->addWikiMsg( $description );
563 $canModify = ( $usingGlobalLanguages ? false : $canModify );
564 $this->
makeTable( $canModify, $iwLanguagePrefixes );
568 protected function makeTable(
bool $canModify, array $iwPrefixes ) {
570 $out = Html::openElement(
572 [
'class' =>
'mw-interwikitable wikitable sortable' ]
574 $out .= Html::openElement(
'thead' ) .
575 Html::openElement(
'tr', [
'class' =>
'interwikitable-header' ] ) .
583 [
'class' =>
'unsortable' ],
584 $this->
msg(
'interwiki_edit' )->text()
588 $out .= Html::closeElement(
'tr' ) .
589 Html::closeElement(
'thead' ) .
"\n" .
590 Html::openElement(
'tbody' );
595 foreach ( $iwPrefixes as $iwPrefix ) {
596 $out .= Html::openElement(
'tr', [
'class' =>
'mw-interwikitable-row' ] );
597 $out .=
Html::element(
'td', [
'class' =>
'mw-interwikitable-prefix' ],
598 $iwPrefix[
'iw_prefix'] );
601 [
'class' =>
'mw-interwikitable-url' ],
604 $attribs = [
'class' =>
'mw-interwikitable-local' ];
606 if ( isset( $iwPrefix[
'iw_local'] ) && $iwPrefix[
'iw_local'] ) {
607 $attribs[
'class'] .=
' mw-interwikitable-local-yes';
610 $contents = isset( $iwPrefix[
'iw_local'] ) ?
611 $this->
msg(
'interwiki_' . $iwPrefix[
'iw_local'] )->text() :
614 $attribs = [
'class' =>
'mw-interwikitable-trans' ];
616 if ( isset( $iwPrefix[
'iw_trans'] ) && $iwPrefix[
'iw_trans'] ) {
617 $attribs[
'class'] .=
' mw-interwikitable-trans-yes';
620 $contents = isset( $iwPrefix[
'iw_trans'] ) ?
621 $this->
msg(
'interwiki_' . $iwPrefix[
'iw_trans'] )->text() :
627 $out .= Html::rawElement(
'td', [
'class' =>
'mw-interwikitable-modify' ],
630 $this->
msg(
'edit' )->text(),
632 [
'action' =>
'edit',
'prefix' => $iwPrefix[
'iw_prefix'] ]
634 $this->
msg(
'comma-separator' )->escaped() .
637 $this->
msg(
'delete' )->text(),
639 [
'action' =>
'delete',
'prefix' => $iwPrefix[
'iw_prefix'] ]
643 $out .= Html::closeElement(
'tr' ) .
"\n";
645 $out .= Html::closeElement(
'tbody' ) .
646 Html::closeElement(
'table' );
649 $this->
getOutput()->addModuleStyles(
'jquery.tablesorter.styles' );
650 $this->
getOutput()->addModules(
'jquery.tablesorter' );
656 protected function error( ...$args ) {
657 $this->
getOutput()->wrapWikiMsg(
"<p class='error'>$1</p>", $args );
Class for creating new log entries and inserting them into the database.
A class containing constants representing the names of configuration variables.
const InterwikiCache
Name constant for the InterwikiCache setting, for use with Config::get()
const InterwikiMagic
Name constant for the InterwikiMagic setting, for use with Config::get()
const VirtualDomainsMapping
Name constant for the VirtualDomainsMapping setting, for use with Config::get()
This is one of the Core classes and should be read at least once by any new developers.
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getUser()
Shortcut to get the User executing this instance.
getPageTitle( $subpage=false)
Get a self-referential title object.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages By default the message key is the canonical name of...