MediaWiki master
SpecialInterwiki.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Specials;
4
5use LogicException;
12use MediaWiki\Languages\LanguageNameUtils;
22
30 private Language $contLang;
31 private InterwikiLookup $interwikiLookup;
32 private LanguageNameUtils $languageNameUtils;
33 private UrlUtils $urlUtils;
34 private IConnectionProvider $dbProvider;
35
36 public function __construct(
37 Language $contLang,
38 InterwikiLookup $interwikiLookup,
39 LanguageNameUtils $languageNameUtils,
40 UrlUtils $urlUtils,
41 IConnectionProvider $dbProvider
42 ) {
43 parent::__construct( 'Interwiki' );
44
45 $this->contLang = $contLang;
46 $this->interwikiLookup = $interwikiLookup;
47 $this->languageNameUtils = $languageNameUtils;
48 $this->urlUtils = $urlUtils;
49 $this->dbProvider = $dbProvider;
50 }
51
53 public function doesWrites() {
54 return true;
55 }
56
63 public function getDescription() {
64 return $this->msg( $this->canModify() ? 'interwiki' : 'interwiki-title-norights' );
65 }
66
68 public function getSubpagesForPrefixSearch() {
69 // delete, edit both require the prefix parameter.
70 return [ 'add' ];
71 }
72
78 public function execute( $par ) {
79 $this->setHeaders();
80 $this->outputHeader();
81
82 $out = $this->getOutput();
83 $request = $this->getRequest();
84
85 $out->addModuleStyles( 'mediawiki.special.interwiki' );
86
87 $action = $par ?? $request->getRawVal( 'action' );
88
89 if ( in_array( $action, [ 'add', 'edit', 'delete' ] ) && $this->canModify( $out ) ) {
90 $this->showForm( $action );
91 } else {
92 $this->showList();
93 }
94 }
95
103 private function canModify( $out = false ) {
104 if ( !$this->getUser()->isAllowed( 'interwiki' ) ) {
105 // Check permissions
106 if ( $out ) {
107 throw new PermissionsError( 'interwiki' );
108 }
109
110 return false;
111 } elseif ( $this->getConfig()->get( MainConfigNames::InterwikiCache ) ) {
112 // Editing the interwiki cache is not supported
113 if ( $out ) {
114 $out->addWikiMsg( 'interwiki-cached' );
115 }
116
117 return false;
118 } else {
119 if ( $out ) {
120 $this->checkReadOnly();
121 }
122 }
123
124 return true;
125 }
126
130 protected function showForm( $action ) {
131 $formDescriptor = [];
132 $hiddenFields = [
133 'action' => $action,
134 ];
135
136 $status = Status::newGood();
137 $request = $this->getRequest();
138 $prefix = $request->getText( 'prefix' );
139
140 switch ( $action ) {
141 case 'add':
142 case 'edit':
143 $formDescriptor = [
144 'prefix' => [
145 'type' => 'text',
146 'label-message' => 'interwiki-prefix-label',
147 'name' => 'prefix',
148 'autofocus' => true,
149 ],
150
151 'local' => [
152 'type' => 'check',
153 'id' => 'mw-interwiki-local',
154 'label-message' => 'interwiki-local-label',
155 'name' => 'local',
156 ],
157
158 'trans' => [
159 'type' => 'check',
160 'id' => 'mw-interwiki-trans',
161 'label-message' => 'interwiki-trans-label',
162 'name' => 'trans',
163 ],
164
165 'url' => [
166 'type' => 'url',
167 'id' => 'mw-interwiki-url',
168 'label-message' => 'interwiki-url-label',
169 'maxlength' => 200,
170 'name' => 'url',
171 'size' => 60,
172 ],
173
174 'api' => [
175 'type' => 'url',
176 'id' => 'mw-interwiki-api',
177 'label-message' => 'interwiki-api-label',
178 'maxlength' => 200,
179 'name' => 'api',
180 'size' => 60,
181 ],
182
183 'reason' => [
184 'type' => 'text',
185 'id' => "mw-interwiki-{$action}reason",
186 'label-message' => 'interwiki_reasonfield',
187 'maxlength' => 200,
188 'name' => 'reason',
189 'size' => 60,
190 ],
191 ];
192
193 break;
194 case 'delete':
195 $formDescriptor = [
196 'prefix' => [
197 'type' => 'hidden',
198 'name' => 'prefix',
199 'default' => $prefix,
200 ],
201
202 'reason' => [
203 'type' => 'text',
204 'name' => 'reason',
205 'label-message' => 'interwiki_reasonfield',
206 ],
207 ];
208
209 break;
210 default:
211 throw new LogicException( "Unexpected action: {$action}" );
212 }
213
214 if ( $action === 'edit' || $action === 'delete' ) {
215 $dbr = $this->dbProvider->getReplicaDatabase();
216 $row = $dbr->newSelectQueryBuilder()
217 ->select( '*' )
218 ->from( 'interwiki' )
219 ->where( [ 'iw_prefix' => $prefix ] )
220 ->caller( __METHOD__ )->fetchRow();
221
222 if ( $action === 'edit' ) {
223 if ( !$row ) {
224 $status->fatal( 'interwiki_editerror', $prefix );
225 } else {
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;
233 }
234 } else { // $action === 'delete'
235 if ( !$row ) {
236 $status->fatal( 'interwiki_delfailed', $prefix );
237 }
238 }
239 }
240
241 if ( !$status->isOK() ) {
242 $formDescriptor = [];
243 }
244
245 $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
246 $htmlForm
247 ->addHiddenFields( $hiddenFields )
248 ->setSubmitCallback( $this->onSubmit( ... ) );
249
250 if ( $status->isOK() ) {
251 if ( $action === 'delete' ) {
252 $htmlForm->setSubmitDestructive();
253 }
254
255 $htmlForm->setSubmitTextMsg( $action !== 'add' ? $action : 'interwiki_addbutton' )
256 ->setPreHtml( $this->msg( $action !== 'delete' ? "interwiki_{$action}intro" :
257 'interwiki_deleting', $prefix )->escaped() )
258 ->show();
259 } else {
260 $htmlForm->suppressDefaultSubmit()
261 ->prepareForm()
262 ->displayForm( $status );
263 }
264
265 $this->getOutput()->addBacklinkSubtitle( $this->getPageTitle() );
266 }
267
272 private function onSubmit( array $data ) {
273 $status = Status::newGood();
274 $request = $this->getRequest();
275 $config = $this->getConfig();
276 $prefix = $data['prefix'];
277 $do = $request->getRawVal( 'action' );
278 // Show an error if the prefix is invalid (only when adding one).
279 // Invalid characters for a title should also be invalid for a prefix.
280 // Whitespace, ':', '&' and '=' are invalid, too.
281 // (Bug 30599).
282 $validPrefixChars = preg_replace( '/[ :&=]/', '', Title::legalChars() );
283 if ( $do === 'add' && preg_match( "/\s|[^$validPrefixChars]/", $prefix ) ) {
284 $status->fatal( 'interwiki-badprefix', htmlspecialchars( $prefix ) );
285 return $status;
286 }
287 $virtualDomainsMapping = $this->getConfig()->get( MainConfigNames::VirtualDomainsMapping ) ?? [];
288 // Disallow adding local interlanguage definitions if using global
289 if (
290 $do === 'add'
291 && $this->isLanguagePrefix( $prefix )
292 && isset( $virtualDomainsMapping['virtual-interwiki-interlanguage'] )
293 ) {
294 $status->fatal( 'interwiki-cannotaddlocallanguage', htmlspecialchars( $prefix ) );
295 return $status;
296 }
297 $reason = $data['reason'];
298 $selfTitle = $this->getPageTitle();
299 $dbw = $this->dbProvider->getPrimaryDatabase();
300 switch ( $do ) {
301 case 'delete':
302 $dbw->newDeleteQueryBuilder()
303 ->deleteFrom( 'interwiki' )
304 ->where( [ 'iw_prefix' => $prefix ] )
305 ->caller( __METHOD__ )->execute();
306
307 if ( $dbw->affectedRows() === 0 ) {
308 $status->fatal( 'interwiki_delfailed', $prefix );
309 } else {
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
316 ] );
317 $log->setPerformer( $this->getUser() );
318 $log->insert();
319 $this->interwikiLookup->invalidateCache( $prefix );
320 }
321 break;
323 case 'add':
324 $prefix = $this->contLang->lc( $prefix );
325 // Fall through
326 case 'edit':
327 // T374771: Trim the URL and API URLs to reduce confusion when
328 // the URLs are accidentally provided with extra whitespace
329 $theurl = trim( $data['url'] );
330 $api = trim( $data['api'] ?? '' );
331 $local = $data['local'] ? 1 : 0;
332 $trans = $data['trans'] ? 1 : 0;
333 $row = [
334 'iw_prefix' => $prefix,
335 'iw_url' => $theurl,
336 'iw_api' => $api,
337 'iw_wikiid' => '',
338 'iw_local' => $local,
339 'iw_trans' => $trans
340 ];
341
342 if ( $prefix === '' || $theurl === '' ) {
343 $status->fatal( 'interwiki-submit-empty' );
344 break;
345 }
346
347 // Simple URL validation: check that the protocol is one of
348 // the supported protocols for this wiki.
349 // (T32600)
350 if ( !$this->urlUtils->parse( $theurl ) ) {
351 $status->fatal( 'interwiki-submit-invalidurl' );
352 break;
353 }
354
355 if ( $do === 'add' ) {
356 $dbw->newInsertQueryBuilder()
357 ->insertInto( 'interwiki' )
358 ->row( $row )
359 ->ignore()
360 ->caller( __METHOD__ )->execute();
361 } else { // $do === 'edit'
362 $dbw->newUpdateQueryBuilder()
363 ->update( 'interwiki' )
364 ->set( $row )
365 ->where( [ 'iw_prefix' => $prefix ] )
366 ->ignore()
367 ->caller( __METHOD__ )->execute();
368 }
369
370 // used here: interwiki_addfailed, interwiki_added, interwiki_edited
371 if ( $dbw->affectedRows() === 0 ) {
372 $status->fatal( "interwiki_{$do}failed", $prefix );
373 } else {
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,
380 '5::url' => $theurl,
381 '6::trans' => $trans,
382 '7::local' => $local
383 ] );
384 $log->setPerformer( $this->getUser() );
385 $log->insert();
386 $this->interwikiLookup->invalidateCache( $prefix );
387 }
388 break;
389 default:
390 throw new LogicException( "Unexpected action: {$do}" );
391 }
392
393 return $status;
394 }
395
402 private function isLanguagePrefix( $prefix ) {
403 return $this->getConfig()->get( MainConfigNames::InterwikiMagic )
404 && $this->languageNameUtils->getLanguageName( $prefix );
405 }
406
407 protected function showList() {
408 $canModify = $this->canModify();
409
410 // Build lists
411 $iwPrefixes = $this->interwikiLookup->getAllPrefixes( null );
412 $iwGlobalPrefixes = [];
413 $iwGlobalLanguagePrefixes = [];
414 $virtualDomainsMapping = $this->getConfig()->get( MainConfigNames::VirtualDomainsMapping ) ?? [];
415 if ( isset( $virtualDomainsMapping['virtual-interwiki'] ) ) {
416 // Fetch list from global table
417 $dbrCentralDB = $this->dbProvider->getReplicaDatabase( 'virtual-interwiki' );
418 $res = $dbrCentralDB->newSelectQueryBuilder()
419 ->select( '*' )
420 ->from( 'interwiki' )
421 ->caller( __METHOD__ )->fetchResultSet();
422 $retval = [];
423 foreach ( $res as $row ) {
424 $row = (array)$row;
425 if ( !$this->isLanguagePrefix( $row['iw_prefix'] ) ) {
426 $retval[] = $row;
427 }
428 }
429 $iwGlobalPrefixes = $retval;
430 }
431
432 // Almost the same loop as above, but for global inter*language* links, whereas the above is for
433 // global inter*wiki* links
434 $usingGlobalLanguages = isset( $virtualDomainsMapping['virtual-interwiki-interlanguage'] );
435 if ( $usingGlobalLanguages ) {
436 // Fetch list from global table
437 $dbrCentralLangDB = $this->dbProvider->getReplicaDatabase( 'virtual-interwiki-interlanguage' );
438 $res = $dbrCentralLangDB->newSelectQueryBuilder()
439 ->select( '*' )
440 ->from( 'interwiki' )
441 ->caller( __METHOD__ )->fetchResultSet();
442 $retval2 = [];
443 foreach ( $res as $row ) {
444 $row = (array)$row;
445 // Note that the above DB query explicitly *excludes* interlang ones
446 // (which makes sense), whereas here we _only_ care about interlang ones!
447 if ( $this->isLanguagePrefix( $row['iw_prefix'] ) ) {
448 $retval2[] = $row;
449 }
450 }
451 $iwGlobalLanguagePrefixes = $retval2;
452 }
453
454 // Split out language links
455 $iwLocalPrefixes = [];
456 $iwLanguagePrefixes = [];
457 foreach ( $iwPrefixes as $iwPrefix ) {
458 if ( $this->isLanguagePrefix( $iwPrefix['iw_prefix'] ) ) {
459 $iwLanguagePrefixes[] = $iwPrefix;
460 } else {
461 $iwLocalPrefixes[] = $iwPrefix;
462 }
463 }
464
465 // If using global interlanguage links, just ditch the data coming from the
466 // local table and overwrite it with the global data
467 if ( $usingGlobalLanguages ) {
468 unset( $iwLanguagePrefixes );
469 $iwLanguagePrefixes = $iwGlobalLanguagePrefixes;
470 }
471
472 // Page intro content
473 $this->getOutput()->addWikiMsg( 'interwiki_intro' );
474
475 // Add 'view log' link
476 $logLink = $this->getLinkRenderer()->makeLink(
477 SpecialPage::getTitleFor( 'Log', 'interwiki' ),
478 $this->msg( 'interwiki-logtext' )->text()
479 );
480 $this->getOutput()->addHTML(
481 Html::rawElement( 'p', [ 'class' => 'mw-interwiki-log' ], $logLink )
482 );
483
484 // Add 'add' link
485 if ( $canModify ) {
486 if ( count( $iwGlobalPrefixes ) !== 0 ) {
487 if ( $usingGlobalLanguages ) {
488 $addtext = 'interwiki-addtext-local-nolang';
489 } else {
490 $addtext = 'interwiki-addtext-local';
491 }
492 } else {
493 if ( $usingGlobalLanguages ) {
494 $addtext = 'interwiki-addtext-nolang';
495 } else {
496 $addtext = 'interwiki_addtext';
497 }
498 }
499 $addtext = $this->msg( $addtext )->text();
500 $addlink = $this->getLinkRenderer()->makeKnownLink(
501 $this->getPageTitle( 'add' ), $addtext );
502 $this->getOutput()->addHTML(
503 Html::rawElement( 'p', [ 'class' => 'mw-interwiki-addlink' ], $addlink )
504 );
505 }
506
507 $this->getOutput()->addWikiMsg( 'interwiki-legend' );
508
509 if ( $iwPrefixes === [] && $iwGlobalPrefixes === [] ) {
510 // If the interwiki table(s) are empty, display an error message
511 $this->error( 'interwiki_error' );
512 return;
513 }
514
515 // Add the global table
516 if ( count( $iwGlobalPrefixes ) !== 0 ) {
517 $this->getOutput()->addHTML(
518 Html::rawElement(
519 'h2',
520 [ 'class' => 'interwikitable-global' ],
521 $this->msg( 'interwiki-global-links' )->parse()
522 )
523 );
524 $this->getOutput()->addWikiMsg( 'interwiki-global-description' );
525
526 // $canModify is false here because this is just a display of remote data
527 $this->makeTable( false, $iwGlobalPrefixes );
528 }
529
530 // Add the local table
531 if ( count( $iwLocalPrefixes ) !== 0 ) {
532 if ( count( $iwGlobalPrefixes ) !== 0 ) {
533 $this->getOutput()->addHTML(
534 Html::rawElement(
535 'h2',
536 [ 'class' => 'interwikitable-local' ],
537 $this->msg( 'interwiki-local-links' )->parse()
538 )
539 );
540 $this->getOutput()->addWikiMsg( 'interwiki-local-description' );
541 } else {
542 $this->getOutput()->addHTML(
543 Html::rawElement(
544 'h2',
545 [ 'class' => 'interwikitable-local' ],
546 $this->msg( 'interwiki-links' )->parse()
547 )
548 );
549 $this->getOutput()->addWikiMsg( 'interwiki-description' );
550 }
551 $this->makeTable( $canModify, $iwLocalPrefixes );
552 }
553
554 // Add the language table
555 if ( count( $iwLanguagePrefixes ) !== 0 ) {
556 if ( $usingGlobalLanguages ) {
557 $header = 'interwiki-global-language-links';
558 $description = 'interwiki-global-language-description';
559 } else {
560 $header = 'interwiki-language-links';
561 $description = 'interwiki-language-description';
562 }
563
564 $this->getOutput()->addHTML(
565 Html::rawElement(
566 'h2',
567 [ 'class' => 'interwikitable-language' ],
568 $this->msg( $header )->parse()
569 )
570 );
571 $this->getOutput()->addWikiMsg( $description );
572
573 // When using global interlanguage links, don't allow them to be modified
574 // except on the source wiki
575 $canModify = ( $usingGlobalLanguages ? false : $canModify );
576 $this->makeTable( $canModify, $iwLanguagePrefixes );
577 }
578 }
579
580 protected function makeTable( bool $canModify, array $iwPrefixes ) {
581 // Output the existing Interwiki prefixes table header
582 $out = Html::openElement(
583 'table',
584 [ 'class' => 'mw-interwikitable wikitable sortable' ]
585 ) . "\n";
586 $out .= Html::openElement( 'thead' ) .
587 Html::openElement( 'tr', [ 'class' => 'interwikitable-header' ] ) .
588 Html::element( 'th', [], $this->msg( 'interwiki_prefix' )->text() ) .
589 Html::element( 'th', [], $this->msg( 'interwiki_url' )->text() ) .
590 Html::element( 'th', [], $this->msg( 'interwiki_local' )->text() ) .
591 Html::element( 'th', [], $this->msg( 'interwiki_trans' )->text() ) .
592 ( $canModify ?
594 'th',
595 [ 'class' => 'unsortable' ],
596 $this->msg( 'interwiki_edit' )->text()
597 ) :
598 ''
599 );
600 $out .= Html::closeElement( 'tr' ) .
601 Html::closeElement( 'thead' ) . "\n" .
602 Html::openElement( 'tbody' );
603
604 $selfTitle = $this->getPageTitle();
605
606 // Output the existing Interwiki prefixes table rows
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'] );
611 $out .= Html::element(
612 'td',
613 [ 'class' => 'mw-interwikitable-url' ],
614 $iwPrefix['iw_url']
615 );
616 $attribs = [ 'class' => 'mw-interwikitable-local' ];
617 // Green background for cells with "yes".
618 if ( isset( $iwPrefix['iw_local'] ) && $iwPrefix['iw_local'] ) {
619 $attribs['class'] .= ' mw-interwikitable-local-yes';
620 }
621 // The messages interwiki_0 and interwiki_1 are used here.
622 $contents = isset( $iwPrefix['iw_local'] ) ?
623 $this->msg( 'interwiki_' . $iwPrefix['iw_local'] )->text() :
624 '-';
625 $out .= Html::element( 'td', $attribs, $contents );
626 $attribs = [ 'class' => 'mw-interwikitable-trans' ];
627 // Green background for cells with "yes".
628 if ( isset( $iwPrefix['iw_trans'] ) && $iwPrefix['iw_trans'] ) {
629 $attribs['class'] .= ' mw-interwikitable-trans-yes';
630 }
631 // The messages interwiki_0 and interwiki_1 are used here.
632 $contents = isset( $iwPrefix['iw_trans'] ) ?
633 $this->msg( 'interwiki_' . $iwPrefix['iw_trans'] )->text() :
634 '-';
635 $out .= Html::element( 'td', $attribs, $contents );
636
637 // Additional column when the interwiki table can be modified.
638 if ( $canModify ) {
639 $out .= Html::rawElement( 'td', [ 'class' => 'mw-interwikitable-modify' ],
640 $this->getLinkRenderer()->makeKnownLink(
641 $selfTitle,
642 $this->msg( 'edit' )->text(),
643 [],
644 [ 'action' => 'edit', 'prefix' => $iwPrefix['iw_prefix'] ]
645 ) .
646 $this->msg( 'comma-separator' )->escaped() .
647 $this->getLinkRenderer()->makeKnownLink(
648 $selfTitle,
649 $this->msg( 'delete' )->text(),
650 [],
651 [ 'action' => 'delete', 'prefix' => $iwPrefix['iw_prefix'] ]
652 )
653 );
654 }
655 $out .= Html::closeElement( 'tr' ) . "\n";
656 }
657 $out .= Html::closeElement( 'tbody' ) .
658 Html::closeElement( 'table' );
659
660 $this->getOutput()->addHTML( $out );
661 $this->getOutput()->addModuleStyles( 'jquery.tablesorter.styles' );
662 $this->getOutput()->addModules( 'jquery.tablesorter' );
663 }
664
668 protected function error( ...$args ) {
669 $this->getOutput()->wrapWikiMsg( "<p class='error'>$1</p>", $args );
670 }
671
673 protected function getGroupName() {
674 return 'wiki';
675 }
676}
Show an error when a user tries to do something they do not have the necessary permissions for.
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:195
This class is a collection of static functions that serve two purposes:
Definition Html.php:43
Base class for language-specific code.
Definition Language.php:70
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()
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
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...
Implements Special:Interwiki.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
makeTable(bool $canModify, array $iwPrefixes)
getSubpagesForPrefixSearch()
Return an array of subpages that this special page will accept for prefix searches....
getDescription()
Different description will be shown on Special:SpecialPage depending on whether the user can modify t...
execute( $par)
Show the special page.
doesWrites()
Indicates whether POST requests to this special page require write access to the wiki....
__construct(Language $contLang, InterwikiLookup $interwikiLookup, LanguageNameUtils $languageNameUtils, UrlUtils $urlUtils, IConnectionProvider $dbProvider)
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Represents a title within MediaWiki.
Definition Title.php:70
A service to expand, parse, and otherwise manipulate URLs.
Definition UrlUtils.php:16
Service interface for looking up Interwiki records.
Provide primary and replica IDatabase connections.
element(SerializerNode $parent, SerializerNode $node, $contents)