MediaWiki master
IntroMessageBuilder.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\EditPage;
4
6use LogicException;
29use RepoGroup;
30use Skin;
31use SkinFactory;
34
43
44 use ParametersHelper;
45
46 // Parameters for getIntroMessages()
47 public const MORE_FRAMES = 1;
48 public const LESS_FRAMES = 2;
49
50 private Config $config;
51 private LinkRenderer $linkRenderer;
52 private PermissionManager $permManager;
53 private UserNameUtils $userNameUtils;
54 private TempUserCreator $tempUserCreator;
55 private UserFactory $userFactory;
56 private RestrictionStore $restrictionStore;
57 private DatabaseBlockStore $blockStore;
58 private ReadOnlyMode $readOnlyMode;
59 private SpecialPageFactory $specialPageFactory;
60 private RepoGroup $repoGroup;
61 private NamespaceInfo $namespaceInfo;
62 private SkinFactory $skinFactory;
63 private IConnectionProvider $dbProvider;
64 private UrlUtils $urlUtils;
65
66 public function __construct(
67 Config $config,
68 LinkRenderer $linkRenderer,
69 PermissionManager $permManager,
70 UserNameUtils $userNameUtils,
71 TempUserCreator $tempUserCreator,
72 UserFactory $userFactory,
73 RestrictionStore $restrictionStore,
74 DatabaseBlockStore $blockStore,
75 ReadOnlyMode $readOnlyMode,
76 SpecialPageFactory $specialPageFactory,
77 RepoGroup $repoGroup,
78 NamespaceInfo $namespaceInfo,
79 SkinFactory $skinFactory,
80 IConnectionProvider $dbProvider,
81 UrlUtils $urlUtils
82 ) {
83 $this->config = $config;
84 $this->linkRenderer = $linkRenderer;
85 $this->permManager = $permManager;
86 $this->userNameUtils = $userNameUtils;
87 $this->tempUserCreator = $tempUserCreator;
88 $this->userFactory = $userFactory;
89 $this->restrictionStore = $restrictionStore;
90 $this->blockStore = $blockStore;
91 $this->readOnlyMode = $readOnlyMode;
92 $this->specialPageFactory = $specialPageFactory;
93 $this->repoGroup = $repoGroup;
94 $this->namespaceInfo = $namespaceInfo;
95 $this->skinFactory = $skinFactory;
96 $this->dbProvider = $dbProvider;
97 $this->urlUtils = $urlUtils;
98 }
99
112 private function getLogExtract( $types = [], $page = '', $user = '', $param = [] ): string {
113 $outString = '';
114 LogEventsList::showLogExtract( $outString, $types, $page, $user, $param );
115 return $outString;
116 }
117
142 public function getIntroMessages(
143 int $frames,
144 array $skip,
145 MessageLocalizer $localizer,
146 ProperPageIdentity $page,
147 ?RevisionRecord $revRecord,
148 Authority $performer,
149 ?string $editIntro,
150 ?string $returnToQuery,
151 bool $preview,
152 ?string $section = null
153 ): array {
154 $title = Title::newFromPageIdentity( $page );
155 $messages = new IntroMessageList( $frames, $skip );
156
157 $this->addOldRevisionWarning( $messages, $localizer, $revRecord );
158
159 if ( !$preview ) {
160 $this->addCodeEditingIntro( $messages, $localizer, $title, $performer );
161 $this->addSharedRepoHint( $messages, $localizer, $page );
162 $this->addUserWarnings( $messages, $localizer, $title, $performer );
163 $this->addEditIntro( $messages, $localizer, $page, $performer, $editIntro, $section );
164 $this->addRecreateWarning( $messages, $localizer, $page );
165 }
166
167 $this->addTalkPageText( $messages, $localizer, $title );
168 $this->addEditNotices( $messages, $localizer, $title, $revRecord );
169
170 $this->addReadOnlyWarning( $messages, $localizer );
171 $this->addAnonEditWarning( $messages, $localizer, $title, $performer, $returnToQuery, $preview );
172 $this->addUserConfigPageInfo( $messages, $localizer, $title, $performer, $preview );
173 $this->addPageProtectionWarningHeaders( $messages, $localizer, $page );
174 $this->addHeaderCopyrightWarning( $messages, $localizer );
175
176 return $messages->getList();
177 }
178
182 private function addCodeEditingIntro(
183 IntroMessageList $messages,
184 MessageLocalizer $localizer,
185 Title $title,
186 Authority $performer
187 ): void {
188 $isUserJsConfig = $title->isUserJsConfigPage();
189 $namespace = $title->getNamespace();
190 $intro = '';
191
192 if (
193 $title->isUserConfigPage() &&
194 $title->isSubpageOf( Title::makeTitle( NS_USER, $performer->getUser()->getName() ) )
195 ) {
196 $isUserCssConfig = $title->isUserCssConfigPage();
197 $isUserJsonConfig = $title->isUserJsonConfigPage();
198 $isUserJsConfig = $title->isUserJsConfigPage();
199
200 if ( $isUserCssConfig ) {
201 $warning = 'usercssispublic';
202 } elseif ( $isUserJsonConfig ) {
203 $warning = 'userjsonispublic';
204 } else {
205 $warning = 'userjsispublic';
206 }
207
208 $warningText = $localizer->msg( $warning )->parse();
209 $intro .= $warningText ? Html::rawElement(
210 'div',
211 [ 'class' => 'mw-userconfigpublic' ],
212 $warningText
213 ) : '';
214
215 }
216 $codeMsg = $localizer->msg( 'editpage-code-message' );
217 $codeMessageText = $codeMsg->isDisabled() ? '' : $codeMsg->parseAsBlock();
218 $isJavaScript = $title->hasContentModel( CONTENT_MODEL_JAVASCRIPT );
219 $isCSS = $title->hasContentModel( CONTENT_MODEL_CSS );
220
221 if ( $namespace === NS_MEDIAWIKI ) {
222 $interfaceMsg = $localizer->msg( 'editinginterface' );
223 $interfaceMsgText = $interfaceMsg->parse();
224 # Show a warning if editing an interface message
225 $intro .= $interfaceMsgText ? Html::rawElement(
226 'div',
227 [ 'class' => 'mw-editinginterface' ],
228 $interfaceMsgText
229 ) : '';
230 # If this is a default message (but not css, json, or js),
231 # show a hint that it is translatable on translatewiki.net
232 if (
233 !$isCSS
234 && !$title->hasContentModel( CONTENT_MODEL_JSON )
235 && !$isJavaScript
236 ) {
237 $defaultMessageText = $title->getDefaultMessageText();
238 if ( $defaultMessageText !== false ) {
239 $translateInterfaceText = $localizer->msg( 'translateinterface' )->parse();
240 $intro .= $translateInterfaceText ? Html::rawElement(
241 'div',
242 [ 'class' => 'mw-translateinterface' ],
243 $translateInterfaceText
244 ) : '';
245 }
246 }
247 }
248
249 if ( $isUserJsConfig ) {
250 $userConfigDangerousMsg = $localizer->msg( 'userjsdangerous' )->parse();
251 $intro .= $userConfigDangerousMsg ? Html::rawElement(
252 'div',
253 [ 'class' => 'mw-userconfigdangerous' ],
254 $userConfigDangerousMsg
255 ) : '';
256 }
257
258 // If the wiki page contains JavaScript or CSS link add message specific to code.
259 if ( $isJavaScript || $isCSS ) {
260 $intro .= $codeMessageText;
261 }
262
263 $messages->addWithKey(
264 'code-editing-intro',
265 $intro,
266 // While semantically this is a warning, given the impact of editing these pages,
267 // it's best to deter users who don't understand what they are doing by
268 // acknowledging the danger here. This is a potentially destructive action
269 // so requires destructive coloring.
270 Html::errorBox( '$1' )
271 );
272 }
273
274 private function addSharedRepoHint(
275 IntroMessageList $messages,
276 MessageLocalizer $localizer,
277 ProperPageIdentity $page
278 ): void {
279 $namespace = $page->getNamespace();
280 if ( $namespace === NS_FILE ) {
281 # Show a hint to shared repo
282 $file = $this->repoGroup->findFile( $page );
283 if ( $file && !$file->isLocal() ) {
284 $descUrl = $file->getDescriptionUrl();
285 # there must be a description url to show a hint to shared repo
286 if ( $descUrl ) {
287 if ( !$page->exists() ) {
288 $messages->add(
289 $localizer->msg(
290 'sharedupload-desc-create',
291 $file->getRepo()->getDisplayName(),
292 $descUrl
293 ),
294 "<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>"
295 );
296 } else {
297 $messages->add(
298 $localizer->msg(
299 'sharedupload-desc-edit',
300 $file->getRepo()->getDisplayName(),
301 $descUrl
302 ),
303 "<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>"
304 );
305 }
306 }
307 }
308 }
309 }
310
311 private function addUserWarnings(
312 IntroMessageList $messages,
313 MessageLocalizer $localizer,
314 Title $title,
315 Authority $performer
316 ): void {
317 $namespace = $title->getNamespace();
318 # Show a warning message when someone creates/edits a user (talk) page but the user does not exist
319 # Show log extract when the user is currently blocked
320 if ( $namespace === NS_USER || $namespace === NS_USER_TALK ) {
321 $username = explode( '/', $title->getText(), 2 )[0];
322 // Allow IP users
323 $validation = UserRigorOptions::RIGOR_NONE;
324 $user = $this->userFactory->newFromName( $username, $validation );
325 $ip = $this->userNameUtils->isIP( $username );
326 $block = $this->blockStore->newFromTarget( $user, $user );
327
328 $userExists = ( $user && $user->isRegistered() );
329 if ( $userExists && $user->isHidden() && !$performer->isAllowed( 'hideuser' ) ) {
330 // If the user exists, but is hidden, and the viewer cannot see hidden
331 // users, pretend like they don't exist at all. See T120883
332 $userExists = false;
333 }
334
335 if ( !$userExists && !$ip ) {
336 $messages->addWithKey(
337 'userpage-userdoesnotexist',
338 // This wrapper frame, for whatever reason, is not optional
339 Html::warningBox(
340 $localizer->msg( 'userpage-userdoesnotexist', wfEscapeWikiText( $username ) )->parse(),
341 'mw-userpage-userdoesnotexist'
342 )
343 );
344 } elseif (
345 $block !== null &&
346 $block->getType() !== Block::TYPE_AUTO &&
347 (
348 $block->isSitewide() ||
349 $this->permManager->isBlockedFrom(
350 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable False positive
351 $user,
352 $title,
353 true
354 )
355 )
356 ) {
357 // Show log extract if the user is sitewide blocked or is partially
358 // blocked and not allowed to edit their user page or user talk page
359 $messages->addWithKey(
360 'blocked-notice-logextract',
361 $this->getLogExtract(
362 'block',
363 $this->namespaceInfo->getCanonicalName( NS_USER ) . ':' . $block->getTargetName(),
364 '',
365 [
366 'lim' => 1,
367 'showIfEmpty' => false,
368 'msgKey' => [
369 'blocked-notice-logextract',
370 $user->getName() # Support GENDER in notice
371 ],
372 ]
373 )
374 );
375 }
376 }
377 }
378
382 private function addEditIntro(
383 IntroMessageList $messages,
384 MessageLocalizer $localizer,
385 ProperPageIdentity $page,
386 Authority $performer,
387 ?string $editIntro,
388 ?string $section
389 ): void {
390 if ( ( $editIntro === null || $editIntro === '' ) && $section === 'new' ) {
391 // Custom edit intro for new sections
392 $editIntro = 'MediaWiki:addsection-editintro';
393 }
394 if ( $editIntro !== null && $editIntro !== '' ) {
395 $introTitle = Title::newFromText( $editIntro );
396
397 // (T334855) Use SpecialMyLanguage redirect so that nonexistent translated pages can
398 // fall back to the corresponding page in a suitable language
399 $introTitle = $this->getTargetTitleIfSpecialMyLanguage( $introTitle );
400
401 if ( $this->isPageExistingAndViewable( $introTitle, $performer ) ) {
402 $messages->addWithKey(
403 'editintro',
404 $localizer->msg( new RawMessage(
405 // Added using template syntax, to take <noinclude>'s into account.
406 '<div class="mw-editintro">{{:' . $introTitle->getFullText() . '}}</div>'
407 ) )
408 // Parse as content to enable language conversion (T353870)
409 ->inContentLanguage()
410 ->parse()
411 );
412 return;
413 }
414 }
415
416 if ( !$page->exists() ) {
417 $helpLink = $this->urlUtils->expand(
419 $localizer->msg( 'helppage' )->inContentLanguage()->text()
420 ),
422 );
423 if ( $helpLink === null ) {
424 throw new LogicException( 'Help link was invalid, this should be impossible' );
425 }
426 if ( $performer->getUser()->isRegistered() ) {
427 $messages->add(
428 $localizer->msg( 'newarticletext', $helpLink ),
429 // Suppress the external link icon, consider the help url an internal one
430 "<div class=\"mw-newarticletext plainlinks\">\n$1\n</div>"
431 );
432 } else {
433 $messages->add(
434 $localizer->msg( 'newarticletextanon', $helpLink ),
435 // Suppress the external link icon, consider the help url an internal one
436 "<div class=\"mw-newarticletextanon plainlinks\">\n$1\n</div>"
437 );
438 }
439 }
440 }
441
442 private function addRecreateWarning(
443 IntroMessageList $messages,
444 MessageLocalizer $localizer,
445 ProperPageIdentity $page
446 ): void {
447 # Give a notice if the user is editing a deleted/moved page...
448 if ( !$page->exists() ) {
449 $dbr = $this->dbProvider->getReplicaDatabase();
450
451 $messages->addWithKey(
452 'recreate-moveddeleted-warn',
453 $this->getLogExtract( [ 'delete', 'move', 'merge' ], $page, '', [
454 'lim' => 10,
455 'conds' => [ $dbr->expr( 'log_action', '!=', 'revision' ) ],
456 'showIfEmpty' => false,
457 'msgKey' => [ 'recreate-moveddeleted-warn' ],
458 ] )
459 );
460 }
461 }
462
463 private function addTalkPageText(
464 IntroMessageList $messages,
465 MessageLocalizer $localizer,
466 Title $title
467 ): void {
468 if ( $title->isTalkPage() ) {
469 $messages->add( $localizer->msg( 'talkpagetext' ) );
470 }
471 }
472
473 private function addEditNotices(
474 IntroMessageList $messages,
475 MessageLocalizer $localizer,
476 Title $title,
477 ?RevisionRecord $revRecord
478 ): void {
479 $editNotices = $title->getEditNotices( $revRecord ? $revRecord->getId() : 0 );
480 if ( count( $editNotices ) ) {
481 foreach ( $editNotices as $key => $html ) {
482 $messages->addWithKey( $key, $html );
483 }
484 } else {
485 $msg = $localizer->msg( 'editnotice-notext' );
486 if ( !$msg->isDisabled() ) {
487 $messages->addWithKey(
488 'editnotice-notext',
489 Html::rawElement(
490 'div',
491 [ 'class' => 'mw-editnotice-notext' ],
492 $msg->parseAsBlock()
493 )
494 );
495 }
496 }
497 }
498
499 private function addOldRevisionWarning(
500 IntroMessageList $messages,
501 MessageLocalizer $localizer,
502 ?RevisionRecord $revRecord
503 ): void {
504 if ( $revRecord && !$revRecord->isCurrent() ) {
505 // This wrapper frame is not optional (T337071)
506 $messages->addWithKey( 'editingold', Html::warningBox( $localizer->msg( 'editingold' )->parse() ) );
507 }
508 }
509
510 private function addReadOnlyWarning(
511 IntroMessageList $messages,
512 MessageLocalizer $localizer
513 ): void {
514 if ( $this->readOnlyMode->isReadOnly() ) {
515 $messages->add(
516 $localizer->msg( 'readonlywarning', $this->readOnlyMode->getReason() ),
517 "<div id=\"mw-read-only-warning\">\n$1\n</div>"
518 );
519 }
520 }
521
522 private function addAnonEditWarning(
523 IntroMessageList $messages,
524 MessageLocalizer $localizer,
525 Title $title,
526 Authority $performer,
527 ?string $returnToQuery,
528 bool $preview
529 ): void {
530 if ( !$performer->getUser()->isRegistered() ) {
531 $tempUserCreateActive = $this->tempUserCreator->shouldAutoCreate( $performer, 'edit' );
532 if ( !$preview ) {
533 $messages->addWithKey(
534 'anoneditwarning',
535 $localizer->msg(
536 $tempUserCreateActive ? 'autocreate-edit-warning' : 'anoneditwarning',
537 // Log-in link
538 SpecialPage::getTitleFor( 'Userlogin' )->getFullURL( [
539 'returnto' => $title->getPrefixedDBkey(),
540 'returntoquery' => $returnToQuery,
541 ] ),
542 // Sign-up link
543 SpecialPage::getTitleFor( 'CreateAccount' )->getFullURL( [
544 'returnto' => $title->getPrefixedDBkey(),
545 'returntoquery' => $returnToQuery,
546 ] )
547 )->parse(),
548 Html::warningBox( '$1', 'mw-anon-edit-warning' )
549 );
550 } else {
551 $messages->addWithKey(
552 'anoneditwarning',
553 $localizer->msg( $tempUserCreateActive ? 'autocreate-preview-warning' : 'anonpreviewwarning' )
554 ->parse(),
555 Html::warningBox( '$1', 'mw-anon-preview-warning' ) );
556 }
557 }
558 }
559
564 private function isWrongCaseUserConfigPage( Title $title ): bool {
565 if ( $title->isUserCssConfigPage() || $title->isUserJsConfigPage() ) {
566 $name = $title->getSkinFromConfigSubpage();
567 $skins = array_merge(
568 array_keys( $this->skinFactory->getInstalledSkins() ),
569 [ 'common' ]
570 );
571 return !in_array( $name, $skins, true )
572 && in_array( strtolower( $name ), $skins, true );
573 } else {
574 return false;
575 }
576 }
577
578 private function addUserConfigPageInfo(
579 IntroMessageList $messages,
580 MessageLocalizer $localizer,
581 Title $title,
582 Authority $performer,
583 bool $preview
584 ): void {
585 if ( $title->isUserConfigPage() ) {
586 # Check the skin exists
587 if ( $this->isWrongCaseUserConfigPage( $title ) ) {
588 $messages->add(
589 $localizer->msg( 'userinvalidconfigtitle', $title->getSkinFromConfigSubpage() ),
590 Html::errorBox( '$1', '', 'mw-userinvalidconfigtitle' )
591 );
592 }
593 if ( $title->isSubpageOf( Title::makeTitle( NS_USER, $performer->getUser()->getName() ) ) ) {
594 $isUserCssConfig = $title->isUserCssConfigPage();
595 $isUserJsonConfig = $title->isUserJsonConfigPage();
596 $isUserJsConfig = $title->isUserJsConfigPage();
597
598 if ( !$preview ) {
599 if ( $isUserCssConfig && $this->config->get( MainConfigNames::AllowUserCss ) ) {
600 $messages->add(
601 $localizer->msg( 'usercssyoucanpreview' ),
602 "<div id='mw-usercssyoucanpreview'>\n$1\n</div>"
603 );
604 } elseif ( $isUserJsonConfig /* No comparable 'AllowUserJson' */ ) {
605 $messages->add(
606 $localizer->msg( 'userjsonyoucanpreview' ),
607 "<div id='mw-userjsonyoucanpreview'>\n$1\n</div>"
608 );
609 } elseif ( $isUserJsConfig && $this->config->get( MainConfigNames::AllowUserJs ) ) {
610 $messages->add(
611 $localizer->msg( 'userjsyoucanpreview' ),
612 "<div id='mw-userjsyoucanpreview'>\n$1\n</div>"
613 );
614 }
615 }
616 }
617 }
618 }
619
620 private function addPageProtectionWarningHeaders(
621 IntroMessageList $messages,
622 MessageLocalizer $localizer,
623 ProperPageIdentity $page
624 ): void {
625 if ( $this->restrictionStore->isProtected( $page, 'edit' ) &&
626 $this->permManager->getNamespaceRestrictionLevels(
627 $page->getNamespace()
628 ) !== [ '' ]
629 ) {
630 # Is the title semi-protected?
631 if ( $this->restrictionStore->isSemiProtected( $page ) ) {
632 $noticeMsg = 'semiprotectedpagewarning';
633 } else {
634 # Then it must be protected based on static groups (regular)
635 $noticeMsg = 'protectedpagewarning';
636 }
637 $messages->addWithKey(
638 $noticeMsg,
639 $this->getLogExtract( 'protect', $page, '', [ 'lim' => 1, 'msgKey' => [ $noticeMsg ] ] )
640 );
641 }
642 if ( $this->restrictionStore->isCascadeProtected( $page ) ) {
643 # Is this page under cascading protection from some source pages?
644 $cascadeSources = $this->restrictionStore->getCascadeProtectionSources( $page )[0];
645 $htmlList = '';
646 # Explain, and list the titles responsible
647 foreach ( $cascadeSources as $source ) {
648 $htmlList .= Html::rawElement( 'li', [], $this->linkRenderer->makeLink( $source ) );
649 }
650 $messages->addWithKey(
651 'cascadeprotectedwarning',
652 $localizer->msg( 'cascadeprotectedwarning', count( $cascadeSources ) )->parse() .
653 ( $htmlList ? Html::rawElement( 'ul', [], $htmlList ) : '' ),
654 Html::warningBox( '$1', 'mw-cascadeprotectedwarning' )
655 );
656 }
657 if ( !$page->exists() && $this->restrictionStore->getRestrictions( $page, 'create' ) ) {
658 $messages->addWithKey(
659 'titleprotectedwarning',
660 $this->getLogExtract(
661 'protect', $page,
662 '',
663 [
664 'lim' => 1,
665 'showIfEmpty' => false,
666 'msgKey' => [ 'titleprotectedwarning' ],
667 'wrap' => "<div class=\"mw-titleprotectedwarning\">\n$1</div>"
668 ]
669 )
670 );
671 }
672 }
673
674 private function addHeaderCopyrightWarning(
675 IntroMessageList $messages,
676 MessageLocalizer $localizer
677 ): void {
678 $messages->add(
679 $localizer->msg( 'editpage-head-copy-warn' ),
680 "<div class='editpage-head-copywarn'>\n$1\n</div>"
681 );
682 }
683}
const NS_USER
Definition Defines.php:67
const CONTENT_MODEL_CSS
Definition Defines.php:230
const NS_FILE
Definition Defines.php:71
const PROTO_CURRENT
Definition Defines.php:215
const NS_MEDIAWIKI
Definition Defines.php:73
const CONTENT_MODEL_JSON
Definition Defines.php:232
const NS_USER_TALK
Definition Defines.php:68
const CONTENT_MODEL_JAVASCRIPT
Definition Defines.php:229
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:81
Provides the intro messages (edit notices and others) to be displayed before an edit form.
getIntroMessages(int $frames, array $skip, MessageLocalizer $localizer, ProperPageIdentity $page, ?RevisionRecord $revRecord, Authority $performer, ?string $editIntro, ?string $returnToQuery, bool $preview, ?string $section=null)
Return intro messages to be shown before an edit form.
__construct(Config $config, LinkRenderer $linkRenderer, PermissionManager $permManager, UserNameUtils $userNameUtils, TempUserCreator $tempUserCreator, UserFactory $userFactory, RestrictionStore $restrictionStore, DatabaseBlockStore $blockStore, ReadOnlyMode $readOnlyMode, SpecialPageFactory $specialPageFactory, RepoGroup $repoGroup, NamespaceInfo $namespaceInfo, SkinFactory $skinFactory, IConnectionProvider $dbProvider, UrlUtils $urlUtils)
Encapsulates a list of edit form intro messages (as HTML) with their identifiers.
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
Variant of the Message class.
Class that generates HTML for internal links.
A class containing constants representing the names of configuration variables.
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Page revision base class.
Factory for handling the special page list and generating SpecialPage objects.
Parent class for all special pages.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Represents a title within MediaWiki.
Definition Title.php:78
Service for temporary user creation.
Create User objects.
UserNameUtils service.
A service to expand, parse, and otherwise manipulate URLs.
Definition UrlUtils.php:16
Prioritized list of file repositories.
Definition RepoGroup.php:32
Factory class to create Skin objects.
The base class for all skins.
Definition Skin.php:64
static makeInternalOrExternalUrl( $name)
If url string starts with http, consider as external URL, else internal.
Definition Skin.php:1199
Determine whether a site is currently in read-only mode.
Represents a block that may prevent users from performing specific operations.
Definition Block.php:45
Interface for configuration instances.
Definition Config.php:32
Interface for a page that is (or could be, or used to be) an editable wiki page.
This interface represents the authority associated with the current execution context,...
Definition Authority.php:37
Shared interface for rigor levels when dealing with User methods.
Interface for localizing messages in MediaWiki.
msg( $key,... $params)
This is the method for getting translated interface messages.
Provide primary and replica IDatabase connections.
$source