12use MediaWiki\Languages\LanguageNameUtils;
32 private LanguageNameUtils $languageNameUtils;
39 LanguageNameUtils $languageNameUtils,
43 parent::__construct(
'Interwiki' );
45 $this->contLang = $contLang;
46 $this->interwikiLookup = $interwikiLookup;
47 $this->languageNameUtils = $languageNameUtils;
48 $this->urlUtils = $urlUtils;
49 $this->dbProvider = $dbProvider;
64 return $this->
msg( $this->canModify() ?
'interwiki' :
'interwiki-title-norights' );
85 $out->addModuleStyles(
'mediawiki.special.interwiki' );
87 $action = $par ?? $request->getRawVal(
'action' );
89 if ( in_array( $action, [
'add',
'edit',
'delete' ] ) && $this->canModify( $out ) ) {
103 private function canModify( $out =
false ) {
104 if ( !$this->
getUser()->isAllowed(
'interwiki' ) ) {
114 $out->addWikiMsg(
'interwiki-cached' );
131 $formDescriptor = [];
136 $status = Status::newGood();
138 $prefix = $request->getText(
'prefix' );
146 'label-message' =>
'interwiki-prefix-label',
153 'id' =>
'mw-interwiki-local',
154 'label-message' =>
'interwiki-local-label',
160 'id' =>
'mw-interwiki-trans',
161 'label-message' =>
'interwiki-trans-label',
167 'id' =>
'mw-interwiki-url',
168 'label-message' =>
'interwiki-url-label',
176 'id' =>
'mw-interwiki-api',
177 'label-message' =>
'interwiki-api-label',
185 'id' =>
"mw-interwiki-{$action}reason",
186 'label-message' =>
'interwiki_reasonfield',
199 'default' => $prefix,
205 'label-message' =>
'interwiki_reasonfield',
211 throw new LogicException(
"Unexpected action: {$action}" );
214 if ( $action ===
'edit' || $action ===
'delete' ) {
215 $dbr = $this->dbProvider->getReplicaDatabase();
216 $row = $dbr->newSelectQueryBuilder()
218 ->from(
'interwiki' )
219 ->where( [
'iw_prefix' => $prefix ] )
220 ->caller( __METHOD__ )->fetchRow();
222 if ( $action ===
'edit' ) {
224 $status->fatal(
'interwiki_editerror', $prefix );
226 $formDescriptor[
'prefix'][
'disabled'] =
true;
227 $formDescriptor[
'prefix'][
'default'] = $prefix;
228 $hiddenFields[
'prefix'] = $prefix;
229 $formDescriptor[
'url'][
'default'] = $row->iw_url;
230 $formDescriptor[
'api'][
'default'] = $row->iw_api;
231 $formDescriptor[
'trans'][
'default'] = $row->iw_trans;
232 $formDescriptor[
'local'][
'default'] = $row->iw_local;
236 $status->fatal(
'interwiki_delfailed', $prefix );
241 if ( !$status->isOK() ) {
242 $formDescriptor = [];
245 $htmlForm = HTMLForm::factory(
'ooui', $formDescriptor, $this->
getContext() );
247 ->addHiddenFields( $hiddenFields )
248 ->setSubmitCallback( $this->onSubmit( ... ) );
250 if ( $status->isOK() ) {
251 if ( $action ===
'delete' ) {
252 $htmlForm->setSubmitDestructive();
255 $htmlForm->setSubmitTextMsg( $action !==
'add' ? $action :
'interwiki_addbutton' )
256 ->setPreHtml( $this->
msg( $action !==
'delete' ?
"interwiki_{$action}intro" :
257 'interwiki_deleting', $prefix )->escaped() )
260 $htmlForm->suppressDefaultSubmit()
262 ->displayForm( $status );
272 private function onSubmit( array $data ) {
273 $status = Status::newGood();
276 $prefix = $data[
'prefix'];
277 $do = $request->getRawVal(
'action' );
282 $validPrefixChars = preg_replace(
'/[ :&=]/',
'', Title::legalChars() );
283 if ( $do ===
'add' && preg_match(
"/\s|[^$validPrefixChars]/", $prefix ) ) {
284 $status->fatal(
'interwiki-badprefix', htmlspecialchars( $prefix ) );
291 && $this->isLanguagePrefix( $prefix )
292 && isset( $virtualDomainsMapping[
'virtual-interwiki-interlanguage'] )
294 $status->fatal(
'interwiki-cannotaddlocallanguage', htmlspecialchars( $prefix ) );
297 $reason = $data[
'reason'];
299 $dbw = $this->dbProvider->getPrimaryDatabase();
302 $dbw->newDeleteQueryBuilder()
303 ->deleteFrom(
'interwiki' )
304 ->where( [
'iw_prefix' => $prefix ] )
305 ->caller( __METHOD__ )->execute();
307 if ( $dbw->affectedRows() === 0 ) {
308 $status->fatal(
'interwiki_delfailed', $prefix );
310 $this->
getOutput()->addWikiMsg(
'interwiki_deleted', $prefix );
311 $log =
new ManualLogEntry(
'interwiki',
'iw_delete' );
312 $log->setTarget( $selfTitle );
313 $log->setComment( $reason );
314 $log->setParameters( [
315 '4::prefix' => $prefix
317 $log->setPerformer( $this->
getUser() );
319 $this->interwikiLookup->invalidateCache( $prefix );
324 $prefix = $this->contLang->lc( $prefix );
329 $theurl = trim( $data[
'url'] );
330 $api = trim( $data[
'api'] ??
'' );
331 $local = $data[
'local'] ? 1 : 0;
332 $trans = $data[
'trans'] ? 1 : 0;
334 'iw_prefix' => $prefix,
338 'iw_local' => $local,
342 if ( $prefix ===
'' || $theurl ===
'' ) {
343 $status->fatal(
'interwiki-submit-empty' );
350 if ( !$this->urlUtils->parse( $theurl ) ) {
351 $status->fatal(
'interwiki-submit-invalidurl' );
355 if ( $do ===
'add' ) {
356 $dbw->newInsertQueryBuilder()
357 ->insertInto(
'interwiki' )
360 ->caller( __METHOD__ )->execute();
362 $dbw->newUpdateQueryBuilder()
363 ->update(
'interwiki' )
365 ->where( [
'iw_prefix' => $prefix ] )
367 ->caller( __METHOD__ )->execute();
371 if ( $dbw->affectedRows() === 0 ) {
372 $status->fatal(
"interwiki_{$do}failed", $prefix );
374 $this->
getOutput()->addWikiMsg(
"interwiki_{$do}ed", $prefix );
375 $log =
new ManualLogEntry(
'interwiki',
'iw_' . $do );
376 $log->setTarget( $selfTitle );
377 $log->setComment( $reason );
378 $log->setParameters( [
379 '4::prefix' => $prefix,
381 '6::trans' => $trans,
384 $log->setPerformer( $this->
getUser() );
386 $this->interwikiLookup->invalidateCache( $prefix );
390 throw new LogicException(
"Unexpected action: {$do}" );
402 private function isLanguagePrefix( $prefix ) {
404 && $this->languageNameUtils->getLanguageName( $prefix );
408 $canModify = $this->canModify();
411 $iwPrefixes = $this->interwikiLookup->getAllPrefixes(
null );
412 $iwGlobalPrefixes = [];
413 $iwGlobalLanguagePrefixes = [];
415 if ( isset( $virtualDomainsMapping[
'virtual-interwiki'] ) ) {
417 $dbrCentralDB = $this->dbProvider->getReplicaDatabase(
'virtual-interwiki' );
418 $res = $dbrCentralDB->newSelectQueryBuilder()
420 ->from(
'interwiki' )
421 ->caller( __METHOD__ )->fetchResultSet();
423 foreach ( $res as $row ) {
425 if ( !$this->isLanguagePrefix( $row[
'iw_prefix'] ) ) {
429 $iwGlobalPrefixes = $retval;
434 $usingGlobalLanguages = isset( $virtualDomainsMapping[
'virtual-interwiki-interlanguage'] );
435 if ( $usingGlobalLanguages ) {
437 $dbrCentralLangDB = $this->dbProvider->getReplicaDatabase(
'virtual-interwiki-interlanguage' );
438 $res = $dbrCentralLangDB->newSelectQueryBuilder()
440 ->from(
'interwiki' )
441 ->caller( __METHOD__ )->fetchResultSet();
443 foreach ( $res as $row ) {
447 if ( $this->isLanguagePrefix( $row[
'iw_prefix'] ) ) {
451 $iwGlobalLanguagePrefixes = $retval2;
455 $iwLocalPrefixes = [];
456 $iwLanguagePrefixes = [];
457 foreach ( $iwPrefixes as $iwPrefix ) {
458 if ( $this->isLanguagePrefix( $iwPrefix[
'iw_prefix'] ) ) {
459 $iwLanguagePrefixes[] = $iwPrefix;
461 $iwLocalPrefixes[] = $iwPrefix;
467 if ( $usingGlobalLanguages ) {
468 unset( $iwLanguagePrefixes );
469 $iwLanguagePrefixes = $iwGlobalLanguagePrefixes;
473 $this->
getOutput()->addWikiMsg(
'interwiki_intro' );
478 $this->
msg(
'interwiki-logtext' )->text()
481 Html::rawElement(
'p', [
'class' =>
'mw-interwiki-log' ], $logLink )
486 if ( count( $iwGlobalPrefixes ) !== 0 ) {
487 if ( $usingGlobalLanguages ) {
488 $addtext =
'interwiki-addtext-local-nolang';
490 $addtext =
'interwiki-addtext-local';
493 if ( $usingGlobalLanguages ) {
494 $addtext =
'interwiki-addtext-nolang';
496 $addtext =
'interwiki_addtext';
499 $addtext = $this->
msg( $addtext )->text();
503 Html::rawElement(
'p', [
'class' =>
'mw-interwiki-addlink' ], $addlink )
507 $this->
getOutput()->addWikiMsg(
'interwiki-legend' );
509 if ( $iwPrefixes === [] && $iwGlobalPrefixes === [] ) {
511 $this->
error(
'interwiki_error' );
516 if ( count( $iwGlobalPrefixes ) !== 0 ) {
520 [
'class' =>
'interwikitable-global' ],
521 $this->
msg(
'interwiki-global-links' )->parse()
524 $this->
getOutput()->addWikiMsg(
'interwiki-global-description' );
527 $this->
makeTable(
false, $iwGlobalPrefixes );
531 if ( count( $iwLocalPrefixes ) !== 0 ) {
532 if ( count( $iwGlobalPrefixes ) !== 0 ) {
536 [
'class' =>
'interwikitable-local' ],
537 $this->
msg(
'interwiki-local-links' )->parse()
540 $this->
getOutput()->addWikiMsg(
'interwiki-local-description' );
545 [
'class' =>
'interwikitable-local' ],
546 $this->
msg(
'interwiki-links' )->parse()
549 $this->
getOutput()->addWikiMsg(
'interwiki-description' );
551 $this->
makeTable( $canModify, $iwLocalPrefixes );
555 if ( count( $iwLanguagePrefixes ) !== 0 ) {
556 if ( $usingGlobalLanguages ) {
557 $header =
'interwiki-global-language-links';
558 $description =
'interwiki-global-language-description';
560 $header =
'interwiki-language-links';
561 $description =
'interwiki-language-description';
567 [
'class' =>
'interwikitable-language' ],
568 $this->
msg( $header )->parse()
571 $this->
getOutput()->addWikiMsg( $description );
575 $canModify = ( $usingGlobalLanguages ? false : $canModify );
576 $this->
makeTable( $canModify, $iwLanguagePrefixes );
580 protected function makeTable(
bool $canModify, array $iwPrefixes ) {
582 $out = Html::openElement(
584 [
'class' =>
'mw-interwikitable wikitable sortable' ]
586 $out .= Html::openElement(
'thead' ) .
587 Html::openElement(
'tr', [
'class' =>
'interwikitable-header' ] ) .
595 [
'class' =>
'unsortable' ],
596 $this->
msg(
'interwiki_edit' )->text()
600 $out .= Html::closeElement(
'tr' ) .
601 Html::closeElement(
'thead' ) .
"\n" .
602 Html::openElement(
'tbody' );
607 foreach ( $iwPrefixes as $iwPrefix ) {
608 $out .= Html::openElement(
'tr', [
'class' =>
'mw-interwikitable-row' ] );
609 $out .=
Html::element(
'td', [
'class' =>
'mw-interwikitable-prefix' ],
610 $iwPrefix[
'iw_prefix'] );
613 [
'class' =>
'mw-interwikitable-url' ],
616 $attribs = [
'class' =>
'mw-interwikitable-local' ];
618 if ( isset( $iwPrefix[
'iw_local'] ) && $iwPrefix[
'iw_local'] ) {
619 $attribs[
'class'] .=
' mw-interwikitable-local-yes';
622 $contents = isset( $iwPrefix[
'iw_local'] ) ?
623 $this->
msg(
'interwiki_' . $iwPrefix[
'iw_local'] )->text() :
626 $attribs = [
'class' =>
'mw-interwikitable-trans' ];
628 if ( isset( $iwPrefix[
'iw_trans'] ) && $iwPrefix[
'iw_trans'] ) {
629 $attribs[
'class'] .=
' mw-interwikitable-trans-yes';
632 $contents = isset( $iwPrefix[
'iw_trans'] ) ?
633 $this->
msg(
'interwiki_' . $iwPrefix[
'iw_trans'] )->text() :
639 $out .= Html::rawElement(
'td', [
'class' =>
'mw-interwikitable-modify' ],
642 $this->
msg(
'edit' )->text(),
644 [
'action' =>
'edit',
'prefix' => $iwPrefix[
'iw_prefix'] ]
646 $this->
msg(
'comma-separator' )->escaped() .
649 $this->
msg(
'delete' )->text(),
651 [
'action' =>
'delete',
'prefix' => $iwPrefix[
'iw_prefix'] ]
655 $out .= Html::closeElement(
'tr' ) .
"\n";
657 $out .= Html::closeElement(
'tbody' ) .
658 Html::closeElement(
'table' );
661 $this->
getOutput()->addModuleStyles(
'jquery.tablesorter.styles' );
662 $this->
getOutput()->addModules(
'jquery.tablesorter' );
668 protected function error( ...$args ) {
669 $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...