MediaWiki  master
Skin.php
Go to the documentation of this file.
1 <?php
25 use Wikimedia\WrappedString;
26 use Wikimedia\WrappedStringList;
27 
39 abstract class Skin extends ContextSource {
43  protected $skinname = null;
44 
45  protected $mRelevantTitle = null;
46  protected $mRelevantUser = null;
47 
52  public $stylename = null;
53 
58  public static function getSkinNames() {
59  $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
60  return $skinFactory->getSkinNames();
61  }
62 
70  public static function getAllowedSkins() {
71  global $wgSkipSkins;
72 
73  $allowedSkins = self::getSkinNames();
74 
75  // Internal skins not intended for general use
76  unset( $allowedSkins['fallback'] );
77  unset( $allowedSkins['apioutput'] );
78 
79  foreach ( $wgSkipSkins as $skip ) {
80  unset( $allowedSkins[$skip] );
81  }
82 
83  return $allowedSkins;
84  }
85 
95  public static function normalizeKey( $key ) {
97 
98  $skinNames = self::getSkinNames();
99 
100  // Make keys lowercase for case-insensitive matching.
101  $skinNames = array_change_key_case( $skinNames, CASE_LOWER );
102  $key = strtolower( $key );
103  $defaultSkin = strtolower( $wgDefaultSkin );
104  $fallbackSkin = strtolower( $wgFallbackSkin );
105 
106  if ( $key == '' || $key == 'default' ) {
107  // Don't return the default immediately;
108  // in a misconfiguration we need to fall back.
109  $key = $defaultSkin;
110  }
111 
112  if ( isset( $skinNames[$key] ) ) {
113  return $key;
114  }
115 
116  // Older versions of the software used a numeric setting
117  // in the user preferences.
118  $fallback = [
119  0 => $defaultSkin,
120  2 => 'cologneblue'
121  ];
122 
123  if ( isset( $fallback[$key] ) ) {
124  $key = $fallback[$key];
125  }
126 
127  if ( isset( $skinNames[$key] ) ) {
128  return $key;
129  } elseif ( isset( $skinNames[$defaultSkin] ) ) {
130  return $defaultSkin;
131  } else {
132  return $fallbackSkin;
133  }
134  }
135 
140  public function __construct( $skinname = null ) {
141  if ( is_string( $skinname ) ) {
142  $this->skinname = $skinname;
143  }
144  }
145 
149  public function getSkinName() {
150  return $this->skinname;
151  }
152 
156  public function initPage( OutputPage $out ) {
157  $this->preloadExistence();
158  }
159 
170  public function getDefaultModules() {
171  $out = $this->getOutput();
172  $user = $this->getUser();
173 
174  // Modules declared in the $modules literal are loaded
175  // for ALL users, on ALL pages, in ALL skins.
176  // Keep this list as small as possible!
177  $modules = [
178  // The 'styles' key sets render-blocking style modules
179  // Unlike other keys in $modules, this is an associative array
180  // where each key is its own group pointing to a list of modules
181  'styles' => [
182  'core' => [],
183  'content' => [],
184  'syndicate' => [],
185  ],
186  'core' => [
187  'site',
188  'mediawiki.page.startup',
189  ],
190  // modules that enhance the content in some way
191  'content' => [
192  'mediawiki.page.ready',
193  ],
194  // modules relating to search functionality
195  'search' => [],
196  // modules relating to functionality relating to watching an article
197  'watch' => [],
198  // modules which relate to the current users preferences
199  'user' => [],
200  // modules relating to RSS/Atom Feeds
201  'syndicate' => [],
202  ];
203 
204  // Preload jquery.tablesorter for mediawiki.page.ready
205  if ( strpos( $out->getHTML(), 'sortable' ) !== false ) {
206  $modules['content'][] = 'jquery.tablesorter';
207  $modules['styles']['content'][] = 'jquery.tablesorter.styles';
208  }
209 
210  // Preload jquery.makeCollapsible for mediawiki.page.ready
211  if ( strpos( $out->getHTML(), 'mw-collapsible' ) !== false ) {
212  $modules['content'][] = 'jquery.makeCollapsible';
213  $modules['styles']['content'][] = 'jquery.makeCollapsible.styles';
214  }
215 
216  // Deprecated since 1.26: Unconditional loading of mediawiki.ui.button
217  // on every page is deprecated. Express a dependency instead.
218  if ( strpos( $out->getHTML(), 'mw-ui-button' ) !== false ) {
219  $modules['styles']['content'][] = 'mediawiki.ui.button';
220  }
221 
222  if ( $out->isTOCEnabled() ) {
223  $modules['content'][] = 'mediawiki.toc';
224  $modules['styles']['content'][] = 'mediawiki.toc.styles';
225  }
226 
227  $prefMgr = MediaWikiServices::getInstance()->getPermissionManager();
228  if ( $user->isLoggedIn()
229  && $prefMgr->userHasAllRights( $user, 'writeapi', 'viewmywatchlist', 'editmywatchlist' )
230  && $this->getRelevantTitle()->canExist()
231  ) {
232  $modules['watch'][] = 'mediawiki.page.watch.ajax';
233  }
234 
235  if ( $user->getBoolOption( 'editsectiononrightclick' )
236  || ( $out->isArticle() && $user->getOption( 'editondblclick' ) )
237  ) {
238  $modules['user'][] = 'mediawiki.misc-authed-pref';
239  }
240 
241  if ( $out->isSyndicated() ) {
242  $modules['styles']['syndicate'][] = 'mediawiki.feedlink';
243  }
244 
245  return $modules;
246  }
247 
251  protected function preloadExistence() {
252  $titles = [];
253 
254  // User/talk link
255  $user = $this->getUser();
256  if ( $user->isLoggedIn() ) {
257  $titles[] = $user->getUserPage();
258  $titles[] = $user->getTalkPage();
259  }
260 
261  // Check, if the page can hold some kind of content, otherwise do nothing
262  $title = $this->getRelevantTitle();
263  if ( $title->canExist() && $title->canHaveTalkPage() ) {
264  $namespaceInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
265  if ( $title->isTalkPage() ) {
266  $titles[] = $namespaceInfo->getSubjectPage( $title );
267  } else {
268  $titles[] = $namespaceInfo->getTalkPage( $title );
269  }
270  }
271 
272  // Footer links (used by SkinTemplate::prepareQuickTemplate)
273  if ( $this->getConfig()->get( 'FooterLinkCacheExpiry' ) <= 0 ) {
274  $titles = array_merge(
275  $titles,
276  array_filter( [
277  $this->footerLinkTitle( 'privacy', 'privacypage' ),
278  $this->footerLinkTitle( 'aboutsite', 'aboutpage' ),
279  $this->footerLinkTitle( 'disclaimers', 'disclaimerpage' ),
280  ] )
281  );
282  }
283 
284  Hooks::run( 'SkinPreloadExistence', [ &$titles, $this ] );
285 
286  if ( $titles ) {
287  $lb = new LinkBatch( $titles );
288  $lb->setCaller( __METHOD__ );
289  $lb->execute();
290  }
291  }
292 
299  public function getRevisionId() {
300  return $this->getOutput()->getRevisionId();
301  }
302 
309  public function isRevisionCurrent() {
310  return $this->getOutput()->isRevisionCurrent();
311  }
312 
318  public function setRelevantTitle( $t ) {
319  $this->mRelevantTitle = $t;
320  }
321 
332  public function getRelevantTitle() {
333  return $this->mRelevantTitle ?? $this->getTitle();
334  }
335 
341  public function setRelevantUser( $u ) {
342  $this->mRelevantUser = $u;
343  }
344 
353  public function getRelevantUser() {
354  if ( isset( $this->mRelevantUser ) ) {
355  return $this->mRelevantUser;
356  }
357  $title = $this->getRelevantTitle();
358  if ( $title->hasSubjectNamespace( NS_USER ) ) {
359  $rootUser = $title->getRootText();
360  if ( User::isIP( $rootUser ) ) {
361  $this->mRelevantUser = User::newFromName( $rootUser, false );
362  } else {
363  $user = User::newFromName( $rootUser, false );
364 
365  if ( $user ) {
366  $user->load( User::READ_NORMAL );
367 
368  if ( $user->isLoggedIn() ) {
369  $this->mRelevantUser = $user;
370  }
371  }
372  }
373  return $this->mRelevantUser;
374  }
375  return null;
376  }
377 
381  abstract public function outputPage();
382 
388  public static function makeVariablesScript( $data, $nonce = null ) {
389  if ( $data ) {
392  $nonce
393  );
394  }
395  return '';
396  }
397 
404  public static function getDynamicStylesheetQuery() {
405  wfDeprecated( __METHOD__, '1.32' );
406  return [
407  'action' => 'raw',
408  'ctype' => 'text/css',
409  ];
410  }
411 
418  public function setupSkinUserCss( OutputPage $out ) {
419  // Stub.
420  }
421 
427  public function getPageClasses( $title ) {
428  $numeric = 'ns-' . $title->getNamespace();
429  $user = $this->getUser();
430 
431  if ( $title->isSpecialPage() ) {
432  $type = 'ns-special';
433  // T25315: provide a class based on the canonical special page name without subpages
434  list( $canonicalName ) = MediaWikiServices::getInstance()->getSpecialPageFactory()->
435  resolveAlias( $title->getDBkey() );
436  if ( $canonicalName ) {
437  $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" );
438  } else {
439  $type .= ' mw-invalidspecialpage';
440  }
441  } else {
442  if ( $title->isTalkPage() ) {
443  $type = 'ns-talk';
444  } else {
445  $type = 'ns-subject';
446  }
447  // T208315: add HTML class when the user can edit the page
448  if ( MediaWikiServices::getInstance()->getPermissionManager()
449  ->quickUserCan( 'edit', $user, $title )
450  ) {
451  $type .= ' mw-editable';
452  }
453  }
454 
455  $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() );
456  $root = Sanitizer::escapeClass( 'rootpage-' . $title->getRootTitle()->getPrefixedText() );
457 
458  return "$numeric $type $name $root";
459  }
460 
465  public function getHtmlElementAttributes() {
466  $lang = $this->getLanguage();
467  return [
468  'lang' => $lang->getHtmlCode(),
469  'dir' => $lang->getDir(),
470  'class' => 'client-nojs',
471  ];
472  }
473 
481  public function addToBodyAttributes( $out, &$bodyAttrs ) {
482  // does nothing by default
483  }
484 
490  protected function getLogo() {
491  return ResourceLoaderSkinModule::getAvailableLogos( $this->getConfig() )[ '1x' ];
492  }
493 
503  public function shouldPreloadLogo() {
504  return false;
505  }
506 
510  public function getCategoryLinks() {
511  $out = $this->getOutput();
512  $allCats = $out->getCategoryLinks();
513  $title = $this->getTitle();
514  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
515 
516  if ( $allCats === [] ) {
517  return '';
518  }
519 
520  $embed = "<li>";
521  $pop = "</li>";
522 
523  $s = '';
524  $colon = $this->msg( 'colon-separator' )->escaped();
525 
526  if ( !empty( $allCats['normal'] ) ) {
527  $t = $embed . implode( $pop . $embed, $allCats['normal'] ) . $pop;
528 
529  $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) );
530  $linkPage = $this->msg( 'pagecategorieslink' )->inContentLanguage()->text();
531  $pageCategoriesLinkTitle = Title::newFromText( $linkPage );
532  if ( $pageCategoriesLinkTitle ) {
533  $link = $linkRenderer->makeLink( $pageCategoriesLinkTitle, $msg->text() );
534  } else {
535  $link = $msg->escaped();
536  }
537  $s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' .
538  $link . $colon . '<ul>' . $t . '</ul></div>';
539  }
540 
541  # Hidden categories
542  if ( isset( $allCats['hidden'] ) ) {
543  if ( $this->getUser()->getBoolOption( 'showhiddencats' ) ) {
544  $class = ' mw-hidden-cats-user-shown';
545  } elseif ( $title->inNamespace( NS_CATEGORY ) ) {
546  $class = ' mw-hidden-cats-ns-shown';
547  } else {
548  $class = ' mw-hidden-cats-hidden';
549  }
550 
551  $s .= "<div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks$class\">" .
552  $this->msg( 'hidden-categories' )->numParams( count( $allCats['hidden'] ) )->escaped() .
553  $colon . '<ul>' . $embed . implode( $pop . $embed, $allCats['hidden'] ) . $pop . '</ul>' .
554  '</div>';
555  }
556 
557  # optional 'dmoz-like' category browser. Will be shown under the list
558  # of categories an article belong to
559  if ( $this->getConfig()->get( 'UseCategoryBrowser' ) ) {
560  $s .= '<br /><hr />';
561 
562  # get a big array of the parents tree
563  $parenttree = $title->getParentCategoryTree();
564  # Skin object passed by reference cause it can not be
565  # accessed under the method subfunction drawCategoryBrowser
566  $tempout = explode( "\n", $this->drawCategoryBrowser( $parenttree ) );
567  # Clean out bogus first entry and sort them
568  unset( $tempout[0] );
569  asort( $tempout );
570  # Output one per line
571  $s .= implode( "<br />\n", $tempout );
572  }
573 
574  return $s;
575  }
576 
582  protected function drawCategoryBrowser( $tree ) {
583  $return = '';
584  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
585 
586  foreach ( $tree as $element => $parent ) {
587  if ( empty( $parent ) ) {
588  # element start a new list
589  $return .= "\n";
590  } else {
591  # grab the others elements
592  $return .= $this->drawCategoryBrowser( $parent ) . ' &gt; ';
593  }
594 
595  # add our current element to the list
596  $eltitle = Title::newFromText( $element );
597  $return .= $linkRenderer->makeLink( $eltitle, $eltitle->getText() );
598  }
599 
600  return $return;
601  }
602 
606  public function getCategories() {
607  $catlinks = $this->getCategoryLinks();
608  // Check what we're showing
609  $allCats = $this->getOutput()->getCategoryLinks();
610  $showHidden = $this->getUser()->getBoolOption( 'showhiddencats' ) ||
611  $this->getTitle()->inNamespace( NS_CATEGORY );
612 
613  $classes = [ 'catlinks' ];
614  if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) {
615  $classes[] = 'catlinks-allhidden';
616  }
617 
618  return Html::rawElement(
619  'div',
620  [ 'id' => 'catlinks', 'class' => $classes, 'data-mw' => 'interface' ],
621  $catlinks
622  );
623  }
624 
639  protected function afterContentHook() {
640  $data = '';
641 
642  if ( Hooks::run( 'SkinAfterContent', [ &$data, $this ] ) ) {
643  // adding just some spaces shouldn't toggle the output
644  // of the whole <div/>, so we use trim() here
645  if ( trim( $data ) != '' ) {
646  // Doing this here instead of in the skins to
647  // ensure that the div has the same ID in all
648  // skins
649  $data = "<div id='mw-data-after-content'>\n" .
650  "\t$data\n" .
651  "</div>\n";
652  }
653  } else {
654  wfDebug( "Hook SkinAfterContent changed output processing.\n" );
655  }
656 
657  return $data;
658  }
659 
665  protected function generateDebugHTML() {
666  return MWDebug::getHTMLDebugLog();
667  }
668 
674  public function bottomScripts() {
675  // TODO and the suckage continues. This function is really just a wrapper around
676  // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned
677  // up at some point
678  $chunks = [ $this->getOutput()->getBottomScripts() ];
679 
680  // Keep the hook appendage separate to preserve WrappedString objects.
681  // This enables BaseTemplate::getTrail() to merge them where possible.
682  $extraHtml = '';
683  Hooks::run( 'SkinAfterBottomScripts', [ $this, &$extraHtml ] );
684  if ( $extraHtml !== '' ) {
685  $chunks[] = $extraHtml;
686  }
687  return WrappedString::join( "\n", $chunks );
688  }
689 
696  public function printSource() {
697  $title = $this->getTitle();
698  $oldid = $this->getOutput()->getRevisionId();
699  if ( $oldid ) {
700  $canonicalUrl = $title->getCanonicalURL( 'oldid=' . $oldid );
701  $url = htmlspecialchars( wfExpandIRI( $canonicalUrl ) );
702  } else {
703  // oldid not available for non existing pages
704  $url = htmlspecialchars( wfExpandIRI( $title->getCanonicalURL() ) );
705  }
706 
707  return $this->msg( 'retrievedfrom' )
708  ->rawParams( '<a dir="ltr" href="' . $url . '">' . $url . '</a>' )
709  ->parse();
710  }
711 
715  public function getUndeleteLink() {
716  $action = $this->getRequest()->getVal( 'action', 'view' );
717  $title = $this->getTitle();
718  $user = $this->getUser();
719  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
720  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
721 
722  if ( ( !$title->exists() || $action == 'history' ) &&
723  $permissionManager->quickUserCan( 'deletedhistory', $user, $title )
724  ) {
725  $n = $title->isDeleted();
726 
727  if ( $n ) {
728  if ( $permissionManager->quickUserCan( 'undelete', $user, $title ) ) {
729  $msg = 'thisisdeleted';
730  } else {
731  $msg = 'viewdeleted';
732  }
733 
734  $subtitle = $this->msg( $msg )->rawParams(
735  $linkRenderer->makeKnownLink(
736  SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedDBkey() ),
737  $this->msg( 'restorelink' )->numParams( $n )->text() )
738  )->escaped();
739 
740  // Allow extensions to add more links
741  $links = [];
742  Hooks::run( 'UndeletePageToolLinks', [ $this->getContext(), $linkRenderer, &$links ] );
743 
744  if ( $links ) {
745  $subtitle .= ''
746  . $this->msg( 'word-separator' )->escaped()
747  . $this->msg( 'parentheses' )
748  ->rawParams( $this->getLanguage()->pipeList( $links ) )
749  ->escaped();
750  }
751  return Html::rawElement( 'div', [ 'class' => 'mw-undelete-subtitle' ], $subtitle );
752  }
753  }
754 
755  return '';
756  }
757 
762  public function subPageSubtitle( $out = null ) {
763  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
764  $out = $out ?? $this->getOutput();
765  $title = $out->getTitle();
766  $subpages = '';
767 
768  if ( !Hooks::run( 'SkinSubPageSubtitle', [ &$subpages, $this, $out ] ) ) {
769  return $subpages;
770  }
771 
772  if (
773  $out->isArticle() && MediaWikiServices::getInstance()->getNamespaceInfo()->
774  hasSubpages( $title->getNamespace() )
775  ) {
776  $ptext = $title->getPrefixedText();
777  if ( strpos( $ptext, '/' ) !== false ) {
778  $links = explode( '/', $ptext );
779  array_pop( $links );
780  $c = 0;
781  $growinglink = '';
782  $display = '';
783  $lang = $this->getLanguage();
784 
785  foreach ( $links as $link ) {
786  $growinglink .= $link;
787  $display .= $link;
788  $linkObj = Title::newFromText( $growinglink );
789 
790  if ( is_object( $linkObj ) && $linkObj->isKnown() ) {
791  $getlink = $linkRenderer->makeKnownLink(
792  $linkObj, $display
793  );
794 
795  $c++;
796 
797  if ( $c > 1 ) {
798  $subpages .= $lang->getDirMarkEntity() . $this->msg( 'pipe-separator' )->escaped();
799  } else {
800  $subpages .= '&lt; ';
801  }
802 
803  $subpages .= $getlink;
804  $display = '';
805  } else {
806  $display .= '/';
807  }
808  $growinglink .= '/';
809  }
810  }
811  }
812 
813  return $subpages;
814  }
815 
819  protected function getSearchLink() {
821  return $searchPage->getLocalURL();
822  }
823 
828  public function getCopyright( $type = 'detect' ) {
829  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
830  if ( $type == 'detect' ) {
831  if ( !$this->getOutput()->isRevisionCurrent()
832  && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled()
833  ) {
834  $type = 'history';
835  } else {
836  $type = 'normal';
837  }
838  }
839 
840  if ( $type == 'history' ) {
841  $msg = 'history_copyright';
842  } else {
843  $msg = 'copyright';
844  }
845 
846  $config = $this->getConfig();
847 
848  if ( $config->get( 'RightsPage' ) ) {
849  $title = Title::newFromText( $config->get( 'RightsPage' ) );
850  $link = $linkRenderer->makeKnownLink(
851  $title, new HtmlArmor( $config->get( 'RightsText' ) )
852  );
853  } elseif ( $config->get( 'RightsUrl' ) ) {
854  $link = Linker::makeExternalLink( $config->get( 'RightsUrl' ), $config->get( 'RightsText' ) );
855  } elseif ( $config->get( 'RightsText' ) ) {
856  $link = $config->get( 'RightsText' );
857  } else {
858  # Give up now
859  return '';
860  }
861 
862  // Allow for site and per-namespace customization of copyright notice.
863  Hooks::run( 'SkinCopyrightFooter', [ $this->getTitle(), $type, &$msg, &$link ] );
864 
865  return $this->msg( $msg )->rawParams( $link )->text();
866  }
867 
871  protected function getCopyrightIcon() {
872  $out = '';
873  $config = $this->getConfig();
874 
875  $footerIcons = $config->get( 'FooterIcons' );
876  if ( $footerIcons['copyright']['copyright'] ) {
877  $out = $footerIcons['copyright']['copyright'];
878  } elseif ( $config->get( 'RightsIcon' ) ) {
879  $icon = htmlspecialchars( $config->get( 'RightsIcon' ) );
880  $url = $config->get( 'RightsUrl' );
881 
882  if ( $url ) {
883  $out .= '<a href="' . htmlspecialchars( $url ) . '">';
884  }
885 
886  $text = htmlspecialchars( $config->get( 'RightsText' ) );
887  $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />";
888 
889  if ( $url ) {
890  $out .= '</a>';
891  }
892  }
893 
894  return $out;
895  }
896 
901  protected function getPoweredBy() {
902  $resourceBasePath = $this->getConfig()->get( 'ResourceBasePath' );
903  $url1 = htmlspecialchars(
904  "$resourceBasePath/resources/assets/poweredby_mediawiki_88x31.png"
905  );
906  $url1_5 = htmlspecialchars(
907  "$resourceBasePath/resources/assets/poweredby_mediawiki_132x47.png"
908  );
909  $url2 = htmlspecialchars(
910  "$resourceBasePath/resources/assets/poweredby_mediawiki_176x62.png"
911  );
912  $text = '<a href="https://www.mediawiki.org/"><img src="' . $url1
913  . '" srcset="' . $url1_5 . ' 1.5x, ' . $url2 . ' 2x" '
914  . 'height="31" width="88" alt="Powered by MediaWiki" loading="lazy" /></a>';
915  Hooks::run( 'SkinGetPoweredBy', [ &$text, $this ] );
916  return $text;
917  }
918 
924  protected function lastModified() {
925  $timestamp = $this->getOutput()->getRevisionTimestamp();
926  $user = $this->getUser();
927  $language = $this->getLanguage();
928 
929  # No cached timestamp, load it from the database
930  if ( $timestamp === null ) {
931  $timestamp = MediaWikiServices::getInstance()
932  ->getRevisionLookup()
933  ->getTimestampFromId( $this->getOutput()->getRevisionId() );
934  }
935 
936  if ( $timestamp ) {
937  $d = $language->userDate( $timestamp, $user );
938  $t = $language->userTime( $timestamp, $user );
939  $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->parse();
940  } else {
941  $s = '';
942  }
943 
944  if ( MediaWikiServices::getInstance()->getDBLoadBalancer()->getLaggedReplicaMode() ) {
945  $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->parse() . '</strong>';
946  }
947 
948  return $s;
949  }
950 
955  public function logoText( $align = '' ) {
956  if ( $align != '' ) {
957  $a = " style='float: {$align};'";
958  } else {
959  $a = '';
960  }
961 
962  $mp = $this->msg( 'mainpage' )->escaped();
963  $mptitle = Title::newMainPage();
964  $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' );
965 
966  $logourl = $this->getLogo();
967  return "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>";
968  }
969 
978  public function makeFooterIcon( $icon, $withImage = 'withImage' ) {
979  if ( is_string( $icon ) ) {
980  $html = $icon;
981  } else { // Assuming array
982  $url = $icon["url"] ?? null;
983  unset( $icon["url"] );
984  if ( isset( $icon["src"] ) && $withImage === 'withImage' ) {
985  // do this the lazy way, just pass icon data as an attribute array
986  $html = Html::element( 'img', $icon );
987  } else {
988  $html = htmlspecialchars( $icon["alt"] );
989  }
990  if ( $url ) {
991  $html = Html::rawElement( 'a',
992  [ "href" => $url, "target" => $this->getConfig()->get( 'ExternalLinkTarget' ) ],
993  $html );
994  }
995  }
996  return $html;
997  }
998 
1003  public function mainPageLink() {
1004  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1005  $s = $linkRenderer->makeKnownLink(
1007  $this->msg( 'mainpage' )->text()
1008  );
1009 
1010  return $s;
1011  }
1012 
1019  public function footerLink( $desc, $page ) {
1020  $title = $this->footerLinkTitle( $desc, $page );
1021  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1022  if ( !$title ) {
1023  return '';
1024  }
1025 
1026  return $linkRenderer->makeKnownLink(
1027  $title,
1028  $this->msg( $desc )->text()
1029  );
1030  }
1031 
1037  private function footerLinkTitle( $desc, $page ) {
1038  // If the link description has been set to "-" in the default language,
1039  if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) {
1040  // then it is disabled, for all languages.
1041  return null;
1042  }
1043  // Otherwise, we display the link for the user, described in their
1044  // language (which may or may not be the same as the default language),
1045  // but we make the link target be the one site-wide page.
1046  $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() );
1047 
1048  return $title ?: null;
1049  }
1050 
1057  public function getSiteFooterLinks() {
1058  $callback = function () {
1059  return [
1060  'privacy' => $this->privacyLink(),
1061  'about' => $this->aboutLink(),
1062  'disclaimer' => $this->disclaimerLink()
1063  ];
1064  };
1065 
1066  $services = MediaWikiServices::getInstance();
1067  $msgCache = $services->getMessageCache();
1068  $wanCache = $services->getMainWANObjectCache();
1069  $config = $this->getConfig();
1070 
1071  return ( $config->get( 'FooterLinkCacheExpiry' ) > 0 )
1072  ? $wanCache->getWithSetCallback(
1073  $wanCache->makeKey( 'footer-links' ),
1074  $config->get( 'FooterLinkCacheExpiry' ),
1075  $callback,
1076  [
1077  'checkKeys' => [
1078  // Unless there is both no exact $code override nor an i18n definition
1079  // in the software, the only MediaWiki page to check is for $code.
1080  $msgCache->getCheckKey( $this->getLanguage()->getCode() )
1081  ],
1082  'lockTSE' => 30
1083  ]
1084  )
1085  : $callback();
1086  }
1087 
1092  public function privacyLink() {
1093  return $this->footerLink( 'privacy', 'privacypage' );
1094  }
1095 
1100  public function aboutLink() {
1101  return $this->footerLink( 'aboutsite', 'aboutpage' );
1102  }
1103 
1108  public function disclaimerLink() {
1109  return $this->footerLink( 'disclaimers', 'disclaimerpage' );
1110  }
1111 
1119  public function editUrlOptions() {
1120  $options = [ 'action' => 'edit' ];
1121  $out = $this->getOutput();
1122 
1123  if ( !$out->isRevisionCurrent() ) {
1124  $options['oldid'] = intval( $out->getRevisionId() );
1125  }
1126 
1127  return $options;
1128  }
1129 
1134  public function showEmailUser( $id ) {
1135  if ( $id instanceof User ) {
1136  $targetUser = $id;
1137  } else {
1138  $targetUser = User::newFromId( $id );
1139  }
1140 
1141  # The sending user must have a confirmed email address and the receiving
1142  # user must accept emails from the sender.
1143  return $this->getUser()->canSendEmail()
1144  && SpecialEmailUser::validateTarget( $targetUser, $this->getUser() ) === '';
1145  }
1146 
1158  public function getSkinStylePath( $name ) {
1159  if ( $this->stylename === null ) {
1160  $class = static::class;
1161  throw new MWException( "$class::\$stylename must be set to use getSkinStylePath()" );
1162  }
1163 
1164  return $this->getConfig()->get( 'StylePath' ) . "/{$this->stylename}/$name";
1165  }
1166 
1167  /* these are used extensively in SkinTemplate, but also some other places */
1168 
1173  public static function makeMainPageUrl( $urlaction = '' ) {
1175  self::checkTitle( $title, '' );
1176 
1177  return $title->getLinkURL( $urlaction );
1178  }
1179 
1191  public static function makeSpecialUrl( $name, $urlaction = '', $proto = null ) {
1193  if ( $proto === null ) {
1194  return $title->getLocalURL( $urlaction );
1195  } else {
1196  return $title->getFullURL( $urlaction, false, $proto );
1197  }
1198  }
1199 
1206  public static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) {
1207  $title = SpecialPage::getSafeTitleFor( $name, $subpage );
1208  return $title->getLocalURL( $urlaction );
1209  }
1210 
1217  public static function makeI18nUrl( $name, $urlaction = '' ) {
1218  wfDeprecated( __METHOD__, '1.35' );
1219  $title = Title::newFromText( wfMessage( $name )->inContentLanguage()->text() );
1220  self::checkTitle( $title, $name );
1221  return $title->getLocalURL( $urlaction );
1222  }
1223 
1229  public static function makeUrl( $name, $urlaction = '' ) {
1230  $title = Title::newFromText( $name );
1231  self::checkTitle( $title, $name );
1232 
1233  return $title->getLocalURL( $urlaction );
1234  }
1235 
1242  public static function makeInternalOrExternalUrl( $name ) {
1243  if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $name ) ) {
1244  return $name;
1245  } else {
1246  return self::makeUrl( $name );
1247  }
1248  }
1249 
1258  public static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) {
1259  wfDeprecated( __METHOD__, '1.35' );
1260  $title = Title::makeTitleSafe( $namespace, $name );
1261  self::checkTitle( $title, $name );
1262 
1263  return $title->getLocalURL( $urlaction );
1264  }
1265 
1272  protected static function makeUrlDetails( $name, $urlaction = '' ) {
1273  $title = Title::newFromText( $name );
1274  self::checkTitle( $title, $name );
1275 
1276  return [
1277  'href' => $title->getLocalURL( $urlaction ),
1278  'exists' => $title->isKnown(),
1279  ];
1280  }
1281 
1288  protected static function makeKnownUrlDetails( $name, $urlaction = '' ) {
1289  $title = Title::newFromText( $name );
1290  self::checkTitle( $title, $name );
1291 
1292  return [
1293  'href' => $title->getLocalURL( $urlaction ),
1294  'exists' => true
1295  ];
1296  }
1297 
1304  public static function checkTitle( &$title, $name ) {
1305  if ( !is_object( $title ) ) {
1306  $title = Title::newFromText( $name );
1307  if ( !is_object( $title ) ) {
1308  $title = Title::newFromText( '--error: link target missing--' );
1309  }
1310  }
1311  }
1312 
1321  public function mapInterwikiToLanguage( $code ) {
1322  $map = $this->getConfig()->get( 'InterlanguageLinkCodeMap' );
1323  return $map[ $code ] ?? $code;
1324  }
1325 
1334  public function getLanguages() {
1335  if ( $this->getConfig()->get( 'HideInterlanguageLinks' ) ) {
1336  return [];
1337  }
1338  $hookContainer = MediaWikiServices::getInstance()->getHookContainer();
1339 
1340  $userLang = $this->getLanguage();
1341  $languageLinks = [];
1342  $langNameUtils = MediaWikiServices::getInstance()->getLanguageNameUtils();
1343 
1344  foreach ( $this->getOutput()->getLanguageLinks() as $languageLinkText ) {
1345  $class = 'interlanguage-link interwiki-' . explode( ':', $languageLinkText, 2 )[0];
1346 
1347  $languageLinkTitle = Title::newFromText( $languageLinkText );
1348  if ( !$languageLinkTitle ) {
1349  continue;
1350  }
1351 
1352  $ilInterwikiCode = $this->mapInterwikiToLanguage( $languageLinkTitle->getInterwiki() );
1353 
1354  $ilLangName = $langNameUtils->getLanguageName( $ilInterwikiCode );
1355 
1356  if ( strval( $ilLangName ) === '' ) {
1357  $ilDisplayTextMsg = $this->msg( "interlanguage-link-$ilInterwikiCode" );
1358  if ( !$ilDisplayTextMsg->isDisabled() ) {
1359  // Use custom MW message for the display text
1360  $ilLangName = $ilDisplayTextMsg->text();
1361  } else {
1362  // Last resort: fallback to the language link target
1363  $ilLangName = $languageLinkText;
1364  }
1365  } else {
1366  // Use the language autonym as display text
1367  $ilLangName = $this->getLanguage()->ucfirst( $ilLangName );
1368  }
1369 
1370  // CLDR extension or similar is required to localize the language name;
1371  // otherwise we'll end up with the autonym again.
1372  $ilLangLocalName = $langNameUtils->getLanguageName(
1373  $ilInterwikiCode,
1374  $userLang->getCode()
1375  );
1376 
1377  $languageLinkTitleText = $languageLinkTitle->getText();
1378  if ( $ilLangLocalName === '' ) {
1379  $ilFriendlySiteName = $this->msg( "interlanguage-link-sitename-$ilInterwikiCode" );
1380  if ( !$ilFriendlySiteName->isDisabled() ) {
1381  if ( $languageLinkTitleText === '' ) {
1382  $ilTitle = $this->msg(
1383  'interlanguage-link-title-nonlangonly',
1384  $ilFriendlySiteName->text()
1385  )->text();
1386  } else {
1387  $ilTitle = $this->msg(
1388  'interlanguage-link-title-nonlang',
1389  $languageLinkTitleText,
1390  $ilFriendlySiteName->text()
1391  )->text();
1392  }
1393  } else {
1394  // we have nothing friendly to put in the title, so fall back to
1395  // displaying the interlanguage link itself in the title text
1396  // (similar to what is done in page content)
1397  $ilTitle = $languageLinkTitle->getInterwiki() .
1398  ":$languageLinkTitleText";
1399  }
1400  } elseif ( $languageLinkTitleText === '' ) {
1401  $ilTitle = $this->msg(
1402  'interlanguage-link-title-langonly',
1403  $ilLangLocalName
1404  )->text();
1405  } else {
1406  $ilTitle = $this->msg(
1407  'interlanguage-link-title',
1408  $languageLinkTitleText,
1409  $ilLangLocalName
1410  )->text();
1411  }
1412 
1413  $ilInterwikiCodeBCP47 = LanguageCode::bcp47( $ilInterwikiCode );
1414  $languageLink = [
1415  'href' => $languageLinkTitle->getFullURL(),
1416  'text' => $ilLangName,
1417  'title' => $ilTitle,
1418  'class' => $class,
1419  'link-class' => 'interlanguage-link-target',
1420  'lang' => $ilInterwikiCodeBCP47,
1421  'hreflang' => $ilInterwikiCodeBCP47,
1422  ];
1423  $hookContainer->run(
1424  'SkinTemplateGetLanguageLink',
1425  [ &$languageLink, $languageLinkTitle, $this->getTitle(), $this->getOutput() ],
1426  []
1427  );
1428  $languageLinks[] = $languageLink;
1429  }
1430 
1431  return $languageLinks;
1432  }
1433 
1440  protected function buildNavUrls() {
1441  $out = $this->getOutput();
1442  $request = $this->getRequest();
1443  $title = $this->getTitle();
1444  $thispage = $title->getPrefixedDBkey();
1445  $uploadNavigationUrl = $this->getConfig()->get( 'UploadNavigationUrl' );
1446 
1447  $nav_urls = [];
1448  $nav_urls['mainpage'] = [ 'href' => self::makeMainPageUrl() ];
1449  if ( $uploadNavigationUrl ) {
1450  $nav_urls['upload'] = [ 'href' => $uploadNavigationUrl ];
1451  } elseif ( UploadBase::isEnabled() && UploadBase::isAllowed( $this->getUser() ) === true ) {
1452  $nav_urls['upload'] = [ 'href' => self::makeSpecialUrl( 'Upload' ) ];
1453  } else {
1454  $nav_urls['upload'] = false;
1455  }
1456  $nav_urls['specialpages'] = [ 'href' => self::makeSpecialUrl( 'Specialpages' ) ];
1457 
1458  $nav_urls['print'] = false;
1459  $nav_urls['permalink'] = false;
1460  $nav_urls['info'] = false;
1461  $nav_urls['whatlinkshere'] = false;
1462  $nav_urls['recentchangeslinked'] = false;
1463  $nav_urls['contributions'] = false;
1464  $nav_urls['log'] = false;
1465  $nav_urls['blockip'] = false;
1466  $nav_urls['mute'] = false;
1467  $nav_urls['emailuser'] = false;
1468  $nav_urls['userrights'] = false;
1469 
1470  // A print stylesheet is attached to all pages, but nobody ever
1471  // figures that out. :) Add a link...
1472  if ( !$out->isPrintable() && ( $out->isArticle() || $title->isSpecialPage() ) ) {
1473  $nav_urls['print'] = [
1474  'text' => $this->msg( 'printableversion' )->text(),
1475  'href' => $title->getLocalURL(
1476  $request->appendQueryValue( 'printable', 'yes' ) )
1477  ];
1478  }
1479 
1480  if ( $out->isArticle() ) {
1481  // Also add a "permalink" while we're at it
1482  $revid = $out->getRevisionId();
1483  if ( $revid ) {
1484  $nav_urls['permalink'] = [
1485  'text' => $this->msg( 'permalink' )->text(),
1486  'href' => $title->getLocalURL( "oldid=$revid" )
1487  ];
1488  }
1489  }
1490 
1491  if ( $out->isArticleRelated() ) {
1492  $nav_urls['whatlinkshere'] = [
1493  'href' => SpecialPage::getTitleFor( 'Whatlinkshere', $thispage )->getLocalURL()
1494  ];
1495 
1496  $nav_urls['info'] = [
1497  'text' => $this->msg( 'pageinfo-toolboxlink' )->text(),
1498  'href' => $title->getLocalURL( "action=info" )
1499  ];
1500 
1501  if ( $title->exists() || $title->inNamespace( NS_CATEGORY ) ) {
1502  $nav_urls['recentchangeslinked'] = [
1503  'href' => SpecialPage::getTitleFor( 'Recentchangeslinked', $thispage )->getLocalURL()
1504  ];
1505  }
1506  }
1507 
1508  $user = $this->getRelevantUser();
1509  if ( $user ) {
1510  $rootUser = $user->getName();
1511 
1512  $nav_urls['contributions'] = [
1513  'text' => $this->msg( 'contributions', $rootUser )->text(),
1514  'href' => self::makeSpecialUrlSubpage( 'Contributions', $rootUser ),
1515  'tooltip-params' => [ $rootUser ],
1516  ];
1517 
1518  $nav_urls['log'] = [
1519  'href' => self::makeSpecialUrlSubpage( 'Log', $rootUser )
1520  ];
1521 
1522  if (
1523  MediaWikiServices::getInstance()
1525  ->userHasRight( $this->getUser(), 'block' )
1526  ) {
1527  $nav_urls['blockip'] = [
1528  'text' => $this->msg( 'blockip', $rootUser )->text(),
1529  'href' => self::makeSpecialUrlSubpage( 'Block', $rootUser )
1530  ];
1531  }
1532 
1533  if ( $this->showEmailUser( $user ) ) {
1534  $nav_urls['emailuser'] = [
1535  'text' => $this->msg( 'tool-link-emailuser', $rootUser )->text(),
1536  'href' => self::makeSpecialUrlSubpage( 'Emailuser', $rootUser ),
1537  'tooltip-params' => [ $rootUser ],
1538  ];
1539  }
1540 
1541  if ( !$user->isAnon() ) {
1542  if ( $this->getUser()->isRegistered() && $this->getConfig()->get( 'EnableSpecialMute' ) ) {
1543  $nav_urls['mute'] = [
1544  'text' => $this->msg( 'mute-preferences' )->text(),
1545  'href' => self::makeSpecialUrlSubpage( 'Mute', $rootUser )
1546  ];
1547  }
1548 
1549  $sur = new UserrightsPage;
1550  $sur->setContext( $this->getContext() );
1551  $canChange = $sur->userCanChangeRights( $user );
1552  $nav_urls['userrights'] = [
1553  'text' => $this->msg(
1554  $canChange ? 'tool-link-userrights' : 'tool-link-userrights-readonly',
1555  $rootUser
1556  )->text(),
1557  'href' => self::makeSpecialUrlSubpage( 'Userrights', $rootUser )
1558  ];
1559  }
1560  }
1561 
1562  return $nav_urls;
1563  }
1564 
1570  final protected function buildFeedUrls() {
1571  $feeds = [];
1572  $out = $this->getOutput();
1573  if ( $out->isSyndicated() ) {
1574  foreach ( $out->getSyndicationLinks() as $format => $link ) {
1575  $feeds[$format] = [
1576  // Messages: feed-atom, feed-rss
1577  'text' => $this->msg( "feed-$format" )->text(),
1578  'href' => $link
1579  ];
1580  }
1581  }
1582  return $feeds;
1583  }
1584 
1608  public function buildSidebar() {
1609  $services = MediaWikiServices::getInstance();
1610  $callback = function ( $old = null, &$ttl = null ) {
1611  $bar = [];
1612  $this->addToSidebar( $bar, 'sidebar' );
1613  Hooks::run( 'SkinBuildSidebar', [ $this, &$bar ] );
1614  $msgCache = MediaWikiServices::getInstance()->getMessageCache();
1615  if ( $msgCache->isDisabled() ) {
1616  $ttl = WANObjectCache::TTL_UNCACHEABLE; // bug T133069
1617  }
1618 
1619  return $bar;
1620  };
1621 
1622  $msgCache = $services->getMessageCache();
1623  $wanCache = $services->getMainWANObjectCache();
1624  $config = $this->getConfig();
1625  $languageCode = $this->getLanguage()->getCode();
1626 
1627  $sidebar = $config->get( 'EnableSidebarCache' )
1628  ? $wanCache->getWithSetCallback(
1629  $wanCache->makeKey( 'sidebar', $languageCode ),
1630  $config->get( 'SidebarCacheExpiry' ),
1631  $callback,
1632  [
1633  'checkKeys' => [
1634  // Unless there is both no exact $code override nor an i18n definition
1635  // in the software, the only MediaWiki page to check is for $code.
1636  $msgCache->getCheckKey( $languageCode )
1637  ],
1638  'lockTSE' => 30
1639  ]
1640  )
1641  : $callback();
1642 
1643  $sidebar['TOOLBOX'] = $this->makeToolbox(
1644  $this->buildNavUrls(),
1645  $this->buildFeedUrls()
1646  );
1647  $sidebar['LANGUAGES'] = $this->getLanguages();
1648  // Apply post-processing to the cached value
1649  Hooks::run( 'SidebarBeforeOutput', [ $this, &$sidebar ] );
1650 
1651  return $sidebar;
1652  }
1653 
1663  public function addToSidebar( &$bar, $message ) {
1664  $this->addToSidebarPlain( $bar, $this->msg( $message )->inContentLanguage()->plain() );
1665  }
1666 
1674  public function addToSidebarPlain( &$bar, $text ) {
1675  $lines = explode( "\n", $text );
1676 
1677  $heading = '';
1678  $config = $this->getConfig();
1679  $messageTitle = $config->get( 'EnableSidebarCache' )
1680  ? Title::newMainPage() : $this->getTitle();
1681  $messageCache = MediaWikiServices::getInstance()->getMessageCache();
1682 
1683  foreach ( $lines as $line ) {
1684  if ( strpos( $line, '*' ) !== 0 ) {
1685  continue;
1686  }
1687  $line = rtrim( $line, "\r" ); // for Windows compat
1688 
1689  if ( strpos( $line, '**' ) !== 0 ) {
1690  $heading = trim( $line, '* ' );
1691  if ( !array_key_exists( $heading, $bar ) ) {
1692  $bar[$heading] = [];
1693  }
1694  } else {
1695  $line = trim( $line, '* ' );
1696 
1697  if ( strpos( $line, '|' ) !== false ) { // sanity check
1698  $line = $messageCache->transform( $line, false, null, $messageTitle );
1699  $line = array_map( 'trim', explode( '|', $line, 2 ) );
1700  if ( count( $line ) !== 2 ) {
1701  // Second sanity check, could be hit by people doing
1702  // funky stuff with parserfuncs... (T35321)
1703  continue;
1704  }
1705 
1706  $extraAttribs = [];
1707 
1708  $msgLink = $this->msg( $line[0] )->title( $messageTitle )->inContentLanguage();
1709  if ( $msgLink->exists() ) {
1710  $link = $msgLink->text();
1711  if ( $link == '-' ) {
1712  continue;
1713  }
1714  } else {
1715  $link = $line[0];
1716  }
1717  $msgText = $this->msg( $line[1] )->title( $messageTitle );
1718  if ( $msgText->exists() ) {
1719  $text = $msgText->text();
1720  } else {
1721  $text = $line[1];
1722  }
1723 
1724  if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $link ) ) {
1725  $href = $link;
1726 
1727  // Parser::getExternalLinkAttribs won't work here because of the Namespace things
1728  if ( $config->get( 'NoFollowLinks' ) &&
1729  !wfMatchesDomainList( $href, $config->get( 'NoFollowDomainExceptions' ) )
1730  ) {
1731  $extraAttribs['rel'] = 'nofollow';
1732  }
1733 
1734  if ( $config->get( 'ExternalLinkTarget' ) ) {
1735  $extraAttribs['target'] = $config->get( 'ExternalLinkTarget' );
1736  }
1737  } else {
1738  $title = Title::newFromText( $link );
1739 
1740  if ( $title ) {
1741  $title = $title->fixSpecialName();
1742  $href = $title->getLinkURL();
1743  } else {
1744  $href = 'INVALID-TITLE';
1745  }
1746  }
1747 
1748  $bar[$heading][] = array_merge( [
1749  'text' => $text,
1750  'href' => $href,
1751  'id' => Sanitizer::escapeIdForAttribute( 'n-' . strtr( $line[1], ' ', '-' ) ),
1752  'active' => false,
1753  ], $extraAttribs );
1754  } else {
1755  continue;
1756  }
1757  }
1758  }
1759 
1760  return $bar;
1761  }
1762 
1768  public function getNewtalks() {
1769  $newMessagesAlert = '';
1770  $user = $this->getUser();
1771  $services = MediaWikiServices::getInstance();
1772  $linkRenderer = $services->getLinkRenderer();
1773  $userHasNewMessages = $services->getTalkPageNotificationManager()
1774  ->userHasNewMessages( $user );
1775  $timestamp = $services->getTalkPageNotificationManager()
1776  ->getLatestSeenMessageTimestamp( $user );
1777  $newtalks = !$userHasNewMessages ? [] : [
1778  [
1779  // TODO: Deprecate adding wiki and link to array and redesign GetNewMessagesAlert hook
1780  'wiki' => WikiMap::getCurrentWikiId(),
1781  'link' => $user->getTalkPage()->getLocalURL(),
1782  'rev' => $timestamp ? $services->getRevisionLookup()
1783  ->getRevisionByTimestamp( $user->getTalkPage(), $timestamp ) : null
1784  ]
1785  ];
1786  $out = $this->getOutput();
1787 
1788  // Allow extensions to disable or modify the new messages alert
1789  if ( !Hooks::run( 'GetNewMessagesAlert', [ &$newMessagesAlert, $newtalks , $user, $out ] ) ) {
1790  return '';
1791  }
1792  if ( $newMessagesAlert ) {
1793  return $newMessagesAlert;
1794  }
1795 
1796  if ( $newtalks !== [] ) {
1797  $uTalkTitle = $user->getTalkPage();
1798  $lastSeenRev = $newtalks[0]['rev'];
1799  $numAuthors = 0;
1800  if ( $lastSeenRev !== null ) {
1801  $plural = true; // Default if we have a last seen revision: if unknown, use plural
1802  $revStore = $services->getRevisionStore();
1803  $latestRev = $revStore->getRevisionByTitle(
1804  $uTalkTitle,
1805  0,
1806  RevisionLookup::READ_NORMAL
1807  );
1808  if ( $latestRev !== null ) {
1809  // Singular if only 1 unseen revision, plural if several unseen revisions.
1810  $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
1811  $numAuthors = $revStore->countAuthorsBetween(
1812  $uTalkTitle->getArticleID(),
1813  $lastSeenRev,
1814  $latestRev,
1815  null,
1816  10,
1817  'include_new'
1818  );
1819  }
1820  } else {
1821  // Singular if no revision -> diff link will show latest change only in any case
1822  $plural = false;
1823  }
1824  $plural = $plural ? 999 : 1;
1825  // 999 signifies "more than one revision". We don't know how many, and even if we did,
1826  // the number of revisions or authors is not necessarily the same as the number of
1827  // "messages".
1828  $newMessagesLink = $linkRenderer->makeKnownLink(
1829  $uTalkTitle,
1830  $this->msg( 'newmessageslinkplural' )->params( $plural )->text(),
1831  [],
1832  $uTalkTitle->isRedirect() ? [ 'redirect' => 'no' ] : []
1833  );
1834 
1835  $newMessagesDiffLink = $linkRenderer->makeKnownLink(
1836  $uTalkTitle,
1837  $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->text(),
1838  [],
1839  $lastSeenRev !== null
1840  ? [ 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' ]
1841  : [ 'diff' => 'cur' ]
1842  );
1843 
1844  if ( $numAuthors >= 1 && $numAuthors <= 10 ) {
1845  $newMessagesAlert = $this->msg(
1846  'youhavenewmessagesfromusers',
1847  $newMessagesLink,
1848  $newMessagesDiffLink
1849  )->numParams( $numAuthors, $plural );
1850  } else {
1851  // $numAuthors === 11 signifies "11 or more" ("more than 10")
1852  $newMessagesAlert = $this->msg(
1853  $numAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
1854  $newMessagesLink,
1855  $newMessagesDiffLink
1856  )->numParams( $plural );
1857  }
1858  $newMessagesAlert = $newMessagesAlert->text();
1859  // Disable CDN cache
1860  $out->setCdnMaxage( 0 );
1861  }
1862 
1863  return $newMessagesAlert;
1864  }
1865 
1873  private function getCachedNotice( $name ) {
1874  $config = $this->getConfig();
1875 
1876  if ( $name === 'default' ) {
1877  // special case
1878  $notice = $config->get( 'SiteNotice' );
1879  if ( empty( $notice ) ) {
1880  return false;
1881  }
1882  } else {
1883  $msg = $this->msg( $name )->inContentLanguage();
1884  if ( $msg->isBlank() ) {
1885  return '';
1886  } elseif ( $msg->isDisabled() ) {
1887  return false;
1888  }
1889  $notice = $msg->plain();
1890  }
1891 
1892  $services = MediaWikiServices::getInstance();
1893  $cache = $services->getMainWANObjectCache();
1894  $parsed = $cache->getWithSetCallback(
1895  // Use the extra hash appender to let eg SSL variants separately cache
1896  // Key is verified with md5 hash of unparsed wikitext
1897  $cache->makeKey( $name, $config->get( 'RenderHashAppend' ), md5( $notice ) ),
1898  // TTL in seconds
1899  600,
1900  function () use ( $notice ) {
1901  return $this->getOutput()->parseAsInterface( $notice );
1902  }
1903  );
1904 
1905  $contLang = $services->getContentLanguage();
1906  return Html::rawElement(
1907  'div',
1908  [
1909  'id' => 'localNotice',
1910  'lang' => $contLang->getHtmlCode(),
1911  'dir' => $contLang->getDir()
1912  ],
1913  $parsed
1914  );
1915  }
1916 
1922  public function getSiteNotice() {
1923  $siteNotice = '';
1924 
1925  if ( Hooks::run( 'SiteNoticeBefore', [ &$siteNotice, $this ] ) ) {
1926  if ( $this->getUser()->isLoggedIn() ) {
1927  $siteNotice = $this->getCachedNotice( 'sitenotice' );
1928  } else {
1929  $anonNotice = $this->getCachedNotice( 'anonnotice' );
1930  if ( $anonNotice === false ) {
1931  $siteNotice = $this->getCachedNotice( 'sitenotice' );
1932  } else {
1933  $siteNotice = $anonNotice;
1934  }
1935  }
1936  if ( $siteNotice === false ) {
1937  $siteNotice = $this->getCachedNotice( 'default' );
1938  }
1939  }
1940 
1941  Hooks::run( 'SiteNoticeAfter', [ &$siteNotice, $this ] );
1942  return $siteNotice;
1943  }
1944 
1958  public function doEditSectionLink( Title $nt, $section, $tooltip, Language $lang ) {
1959  // HTML generated here should probably have userlangattributes
1960  // added to it for LTR text on RTL pages
1961 
1962  $attribs = [];
1963  if ( $tooltip !== null ) {
1964  $attribs['title'] = $this->msg( 'editsectionhint' )->rawParams( $tooltip )
1965  ->inLanguage( $lang )->text();
1966  }
1967 
1968  $links = [
1969  'editsection' => [
1970  'text' => $this->msg( 'editsection' )->inLanguage( $lang )->text(),
1971  'targetTitle' => $nt,
1972  'attribs' => $attribs,
1973  'query' => [ 'action' => 'edit', 'section' => $section ]
1974  ]
1975  ];
1976 
1977  Hooks::run( 'SkinEditSectionLinks', [ $this, $nt, $section, $tooltip, &$links, $lang ] );
1978 
1979  $result = '<span class="mw-editsection"><span class="mw-editsection-bracket">[</span>';
1980 
1981  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
1982  $linksHtml = [];
1983  foreach ( $links as $k => $linkDetails ) {
1984  $linksHtml[] = $linkRenderer->makeKnownLink(
1985  $linkDetails['targetTitle'],
1986  $linkDetails['text'],
1987  $linkDetails['attribs'],
1988  $linkDetails['query']
1989  );
1990  }
1991 
1992  $result .= implode(
1993  '<span class="mw-editsection-divider">'
1994  . $this->msg( 'pipe-separator' )->inLanguage( $lang )->escaped()
1995  . '</span>',
1996  $linksHtml
1997  );
1998 
1999  $result .= '<span class="mw-editsection-bracket">]</span></span>';
2000  return $result;
2001  }
2002 
2012  public function makeToolbox( $navUrls, $feedUrls ) {
2013  $toolbox = [];
2014  if ( isset( $navUrls['whatlinkshere'] )
2015  && $navUrls['whatlinkshere']
2016  ) {
2017  $toolbox['whatlinkshere'] = $navUrls['whatlinkshere'];
2018  $toolbox['whatlinkshere']['id'] = 't-whatlinkshere';
2019  }
2020  if ( isset( $navUrls['recentchangeslinked'] )
2021  && $navUrls['recentchangeslinked']
2022  ) {
2023  $toolbox['recentchangeslinked'] = $navUrls['recentchangeslinked'];
2024  $toolbox['recentchangeslinked']['msg'] = 'recentchangeslinked-toolbox';
2025  $toolbox['recentchangeslinked']['id'] = 't-recentchangeslinked';
2026  $toolbox['recentchangeslinked']['rel'] = 'nofollow';
2027  }
2028  if ( isset( $feedUrls ) && $feedUrls ) {
2029  $toolbox['feeds']['id'] = 'feedlinks';
2030  $toolbox['feeds']['links'] = [];
2031  foreach ( $feedUrls as $key => $feed ) {
2032  $toolbox['feeds']['links'][$key] = $feed;
2033  $toolbox['feeds']['links'][$key]['id'] = "feed-$key";
2034  $toolbox['feeds']['links'][$key]['rel'] = 'alternate';
2035  $toolbox['feeds']['links'][$key]['type'] = "application/{$key}+xml";
2036  $toolbox['feeds']['links'][$key]['class'] = 'feedlink';
2037  }
2038  }
2039  foreach ( [ 'contributions', 'log', 'blockip', 'emailuser', 'mute',
2040  'userrights', 'upload', 'specialpages' ] as $special
2041  ) {
2042  if ( isset( $navUrls[$special] ) && $navUrls[$special] ) {
2043  $toolbox[$special] = $navUrls[$special];
2044  $toolbox[$special]['id'] = "t-$special";
2045  }
2046  }
2047  if ( isset( $navUrls['print'] ) && $navUrls['print'] ) {
2048  $toolbox['print'] = $navUrls['print'];
2049  $toolbox['print']['id'] = 't-print';
2050  $toolbox['print']['rel'] = 'alternate';
2051  $toolbox['print']['msg'] = 'printableversion';
2052  }
2053  if ( isset( $navUrls['permalink'] ) && $navUrls['permalink'] ) {
2054  $toolbox['permalink'] = $navUrls['permalink'];
2055  $toolbox['permalink']['id'] = 't-permalink';
2056  }
2057  if ( isset( $navUrls['info'] ) && $navUrls['info'] ) {
2058  $toolbox['info'] = $navUrls['info'];
2059  $toolbox['info']['id'] = 't-info';
2060  }
2061 
2062  return $toolbox;
2063  }
2064 
2081  final public function getIndicatorsHTML( $indicators ) {
2082  $out = "<div class=\"mw-indicators mw-body-content\">\n";
2083  foreach ( $indicators as $id => $content ) {
2084  $out .= Html::rawElement(
2085  'div',
2086  [
2087  'id' => Sanitizer::escapeIdForAttribute( "mw-indicator-$id" ),
2088  'class' => 'mw-indicator',
2089  ],
2090  $content
2091  ) . "\n";
2092  }
2093  $out .= "</div>\n";
2094  return $out;
2095  }
2096 
2109  final public function getPersonalToolsForMakeListItem( $urls ) {
2110  $personal_tools = [];
2111  foreach ( $urls as $key => $plink ) {
2112  # The class on a personal_urls item is meant to go on the <a> instead
2113  # of the <li> so we have to use a single item "links" array instead
2114  # of using most of the personal_url's keys directly.
2115  $ptool = [
2116  'links' => [
2117  [ 'single-id' => "pt-$key" ],
2118  ],
2119  'id' => "pt-$key",
2120  ];
2121  if ( isset( $plink['active'] ) ) {
2122  $ptool['active'] = $plink['active'];
2123  }
2124  foreach ( [
2125  'href',
2126  'class',
2127  'text',
2128  'dir',
2129  'data',
2130  'exists',
2131  'data-mw'
2132  ] as $k ) {
2133  if ( isset( $plink[$k] ) ) {
2134  $ptool['links'][0][$k] = $plink[$k];
2135  }
2136  }
2137  $personal_tools[$key] = $ptool;
2138  }
2139  return $personal_tools;
2140  }
2141 
2195  final public function makeLink( $key, $item, $options = [] ) {
2196  $text = $item['text'] ?? $this->msg( $item['msg'] ?? $key )->text();
2197 
2198  $html = htmlspecialchars( $text );
2199 
2200  if ( isset( $options['text-wrapper'] ) ) {
2201  $wrapper = $options['text-wrapper'];
2202  if ( isset( $wrapper['tag'] ) ) {
2203  $wrapper = [ $wrapper ];
2204  }
2205  while ( count( $wrapper ) > 0 ) {
2206  $element = array_pop( $wrapper );
2207  $html = Html::rawElement( $element['tag'], $element['attributes'] ?? null, $html );
2208  }
2209  }
2210 
2211  if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
2212  $attrs = $item;
2213  foreach ( [ 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary',
2214  'tooltip-params', 'exists' ] as $k ) {
2215  unset( $attrs[$k] );
2216  }
2217 
2218  if ( isset( $attrs['data'] ) ) {
2219  foreach ( $attrs['data'] as $key => $value ) {
2220  $attrs[ 'data-' . $key ] = $value;
2221  }
2222  unset( $attrs[ 'data' ] );
2223  }
2224 
2225  if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
2226  $item['single-id'] = $item['id'];
2227  }
2228 
2229  $tooltipParams = [];
2230  if ( isset( $item['tooltip-params'] ) ) {
2231  $tooltipParams = $item['tooltip-params'];
2232  }
2233 
2234  if ( isset( $item['single-id'] ) ) {
2235  $tooltipOption = isset( $item['exists'] ) && $item['exists'] === false ? 'nonexisting' : null;
2236 
2237  if ( isset( $item['tooltiponly'] ) && $item['tooltiponly'] ) {
2238  $title = Linker::titleAttrib( $item['single-id'], $tooltipOption, $tooltipParams );
2239  if ( $title !== false ) {
2240  $attrs['title'] = $title;
2241  }
2242  } else {
2244  $item['single-id'],
2245  $tooltipParams,
2246  $tooltipOption
2247  );
2248  if ( isset( $tip['title'] ) && $tip['title'] !== false ) {
2249  $attrs['title'] = $tip['title'];
2250  }
2251  if ( isset( $tip['accesskey'] ) && $tip['accesskey'] !== false ) {
2252  $attrs['accesskey'] = $tip['accesskey'];
2253  }
2254  }
2255  }
2256  if ( isset( $options['link-class'] ) ) {
2257  if ( isset( $attrs['class'] ) ) {
2258  $attrs['class'] .= " {$options['link-class']}";
2259  } else {
2260  $attrs['class'] = $options['link-class'];
2261  }
2262  }
2263  $html = Html::rawElement( isset( $attrs['href'] )
2264  ? 'a'
2265  : $options['link-fallback'], $attrs, $html );
2266  }
2267 
2268  return $html;
2269  }
2270 
2303  final public function makeListItem( $key, $item, $options = [] ) {
2304  // In case this is still set from SkinTemplate, we don't want it to appear in
2305  // the HTML output (normally removed in SkinTemplate::buildContentActionUrls())
2306  unset( $item['redundant'] );
2307 
2308  if ( isset( $item['links'] ) ) {
2309  $links = [];
2310  foreach ( $item['links'] as $linkKey => $link ) {
2311  $links[] = $this->makeLink( $linkKey, $link, $options );
2312  }
2313  $html = implode( ' ', $links );
2314  } else {
2315  $link = $item;
2316  // These keys are used by makeListItem and shouldn't be passed on to the link
2317  foreach ( [ 'id', 'class', 'active', 'tag', 'itemtitle' ] as $k ) {
2318  unset( $link[$k] );
2319  }
2320  if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
2321  // The id goes on the <li> not on the <a> for single links
2322  // but makeSidebarLink still needs to know what id to use when
2323  // generating tooltips and accesskeys.
2324  $link['single-id'] = $item['id'];
2325  }
2326  if ( isset( $link['link-class'] ) ) {
2327  // link-class should be set on the <a> itself,
2328  // so pass it in as 'class'
2329  $link['class'] = $link['link-class'];
2330  unset( $link['link-class'] );
2331  }
2332  $html = $this->makeLink( $key, $link, $options );
2333  }
2334 
2335  $attrs = [];
2336  foreach ( [ 'id', 'class' ] as $attr ) {
2337  if ( isset( $item[$attr] ) ) {
2338  $attrs[$attr] = $item[$attr];
2339  }
2340  }
2341  if ( isset( $item['active'] ) && $item['active'] ) {
2342  if ( !isset( $attrs['class'] ) ) {
2343  $attrs['class'] = '';
2344  }
2345  $attrs['class'] .= ' active';
2346  $attrs['class'] = trim( $attrs['class'] );
2347  }
2348  if ( isset( $item['itemtitle'] ) ) {
2349  $attrs['title'] = $item['itemtitle'];
2350  }
2351  return Html::rawElement( $options['tag'] ?? 'li', $attrs, $html );
2352  }
2353 
2360  final public function makeSearchInput( $attrs = [] ) {
2361  $realAttrs = [
2362  'type' => 'search',
2363  'name' => 'search',
2364  'placeholder' => $this->msg( 'searchsuggest-search' )->text(),
2365  ];
2366  $realAttrs = array_merge( $realAttrs, Linker::tooltipAndAccesskeyAttribs( 'search' ), $attrs );
2367  return Html::element( 'input', $realAttrs );
2368  }
2369 
2377  final public function makeSearchButton( $mode, $attrs = [] ) {
2378  switch ( $mode ) {
2379  case 'go':
2380  case 'fulltext':
2381  $realAttrs = [
2382  'type' => 'submit',
2383  'name' => $mode,
2384  'value' => $this->msg( $mode == 'go' ? 'searcharticle' : 'searchbutton' )->text(),
2385  ];
2386  $realAttrs = array_merge(
2387  $realAttrs,
2388  Linker::tooltipAndAccesskeyAttribs( "search-$mode" ),
2389  $attrs
2390  );
2391  return Html::element( 'input', $realAttrs );
2392  case 'image':
2393  $buttonAttrs = [
2394  'type' => 'submit',
2395  'name' => 'button',
2396  ];
2397  $buttonAttrs = array_merge(
2398  $buttonAttrs,
2399  Linker::tooltipAndAccesskeyAttribs( 'search-fulltext' ),
2400  $attrs
2401  );
2402  unset( $buttonAttrs['src'] );
2403  unset( $buttonAttrs['alt'] );
2404  unset( $buttonAttrs['width'] );
2405  unset( $buttonAttrs['height'] );
2406  $imgAttrs = [
2407  /* @phan-suppress-next-line PhanTypeInvalidDimOffset */
2408  'src' => $attrs['src'],
2409  'alt' => $attrs['alt'] ?? $this->msg( 'searchbutton' )->text(),
2410  'width' => $attrs['width'] ?? null,
2411  'height' => $attrs['height'] ?? null,
2412  ];
2413  return Html::rawElement( 'button', $buttonAttrs, Html::element( 'img', $imgAttrs ) );
2414  default:
2415  throw new MWException( 'Unknown mode passed to BaseTemplate::makeSearchButton' );
2416  }
2417  }
2418 }
Skin\shouldPreloadLogo
shouldPreloadLogo()
Whether the logo should be preloaded with an HTTP link header or not.
Definition: Skin.php:503
Skin\$skinname
string null $skinname
Definition: Skin.php:43
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:63
Skin\editUrlOptions
editUrlOptions()
Return URL options for the 'edit page' link.
Definition: Skin.php:1119
Skin\showEmailUser
showEmailUser( $id)
Definition: Skin.php:1134
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:557
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:332
Skin\makeUrlDetails
static makeUrlDetails( $name, $urlaction='')
these return an array with the 'href' and boolean 'exists'
Definition: Skin.php:1272
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:40
Skin\getSiteNotice
getSiteNotice()
Get the site notice.
Definition: Skin.php:1922
HtmlArmor
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:28
$searchPage
$searchPage
Definition: opensearch_desc.php:87
UploadBase\isAllowed
static isAllowed(UserIdentity $user)
Returns true if the user can use this upload module or else a string identifying the missing permissi...
Definition: UploadBase.php:149
ResourceLoader\makeConfigSetScript
static makeConfigSetScript(array $configuration)
Return JS code which will set the MediaWiki configuration array to the given value.
Definition: ResourceLoader.php:1661
Skin\buildFeedUrls
buildFeedUrls()
Build data structure representing syndication links.
Definition: Skin.php:1570
Skin\footerLinkTitle
footerLinkTitle( $desc, $page)
Definition: Skin.php:1037
LinkBatch
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:35
Skin\getPoweredBy
getPoweredBy()
Gets the powered by MediaWiki icon.
Definition: Skin.php:901
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:144
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
ResourceLoader\makeInlineScript
static makeInlineScript( $script, $nonce=null)
Make an HTML script that runs given JS code after startup and base modules.
Definition: ResourceLoader.php:1634
Sanitizer\escapeIdForAttribute
static escapeIdForAttribute( $id, $mode=self::ID_PRIMARY)
Given a section name or other user-generated or otherwise unsafe string, escapes it to be a valid HTM...
Definition: Sanitizer.php:1116
true
return true
Definition: router.php:90
Skin\makeVariablesScript
static makeVariablesScript( $data, $nonce=null)
Definition: Skin.php:388
$fallback
$fallback
Definition: MessagesAb.php:11
Skin\lastModified
lastModified()
Get the timestamp of the latest revision, formatted in user language.
Definition: Skin.php:924
Skin\getSiteFooterLinks
getSiteFooterLinks()
Gets the link to the wiki's privacy policy, about page, and disclaimer page.
Definition: Skin.php:1057
UploadBase\isEnabled
static isEnabled()
Returns true if uploads are enabled.
Definition: UploadBase.php:135
Skin\checkTitle
static checkTitle(&$title, $name)
make sure we have some title to operate on
Definition: Skin.php:1304
MWDebug\getHTMLDebugLog
static getHTMLDebugLog()
Generate debug log in HTML for displaying at the bottom of the main content area.
Definition: MWDebug.php:538
Skin\mapInterwikiToLanguage
mapInterwikiToLanguage( $code)
Allows correcting the language of interlanguage links which, mostly due to legacy reasons,...
Definition: Skin.php:1321
Skin\makeSpecialUrl
static makeSpecialUrl( $name, $urlaction='', $proto=null)
Make a URL for a Special Page using the given query and protocol.
Definition: Skin.php:1191
Skin\getCategoryLinks
getCategoryLinks()
Definition: Skin.php:510
Skin\aboutLink
aboutLink()
Gets the link to the wiki's about page.
Definition: Skin.php:1100
Skin\getDynamicStylesheetQuery
static getDynamicStylesheetQuery()
Get the query to generate a dynamic stylesheet.
Definition: Skin.php:404
Skin\makeSpecialUrlSubpage
static makeSpecialUrlSubpage( $name, $subpage, $urlaction='')
Definition: Skin.php:1206
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:533
Skin\addToBodyAttributes
addToBodyAttributes( $out, &$bodyAttrs)
This will be called by OutputPage::headElement when it is creating the "<body>" tag,...
Definition: Skin.php:481
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1198
$s
$s
Definition: mergeMessageFileList.php:185
SpecialPage\getTitleFor
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,...
Definition: SpecialPage.php:83
Sanitizer\escapeClass
static escapeClass( $class)
Given a value, escape it so that it can be used as a CSS class and return it.
Definition: Sanitizer.php:1260
ContextSource\getRequest
getRequest()
Definition: ContextSource.php:71
Title\newMainPage
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:657
Skin\$mRelevantUser
$mRelevantUser
Definition: Skin.php:46
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
ContextSource\getTitle
getTitle()
Definition: ContextSource.php:79
wfExpandIRI
wfExpandIRI( $url)
Take a URL, make sure it's expanded to fully qualified, and replace any encoded non-ASCII Unicode cha...
Definition: GlobalFunctions.php:863
WikiMap\getCurrentWikiId
static getCurrentWikiId()
Definition: WikiMap.php:303
Skin\makeI18nUrl
static makeI18nUrl( $name, $urlaction='')
Definition: Skin.php:1217
Skin\getHtmlElementAttributes
getHtmlElementAttributes()
Return values for <html> element.
Definition: Skin.php:465
SpecialEmailUser\validateTarget
static validateTarget( $target, User $sender)
Validate target User.
Definition: SpecialEmailUser.php:193
Skin\doEditSectionLink
doEditSectionLink(Title $nt, $section, $tooltip, Language $lang)
Create a section edit link.
Definition: Skin.php:1958
Skin\makeListItem
makeListItem( $key, $item, $options=[])
Generates a list item for a navigation, portlet, portal, sidebar...
Definition: Skin.php:2303
Revision\RevisionLookup
Service for looking up page revisions.
Definition: RevisionLookup.php:38
Skin\bottomScripts
bottomScripts()
This gets called shortly before the "</body>" tag.
Definition: Skin.php:674
Skin\mainPageLink
mainPageLink()
Gets the link to the wiki's main page.
Definition: Skin.php:1003
Linker\tooltipAndAccesskeyAttribs
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition: Linker.php:2296
Skin\getSkinNames
static getSkinNames()
Fetch the set of available skins.
Definition: Skin.php:58
Skin\afterContentHook
afterContentHook()
This runs a hook to allow extensions placing their stuff after content and article metadata (e....
Definition: Skin.php:639
ContextSource\getLanguage
getLanguage()
Definition: ContextSource.php:128
SpecialPage\getSafeTitleFor
static getSafeTitleFor( $name, $subpage=false)
Get a localised Title object for a page name with a possibly unvalidated subpage.
Definition: SpecialPage.php:112
NS_MAIN
const NS_MAIN
Definition: Defines.php:69
Skin\getCachedNotice
getCachedNotice( $name)
Get a cached notice.
Definition: Skin.php:1873
UserrightsPage
Special page to allow managing user group membership.
Definition: SpecialUserrights.php:31
MWException
MediaWiki exception.
Definition: MWException.php:26
Skin\buildSidebar
buildSidebar()
Build an array that represents the sidebar(s), the navigation bar among them.
Definition: Skin.php:1608
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1030
Skin\privacyLink
privacyLink()
Gets the link to the wiki's privacy policy page.
Definition: Skin.php:1092
Skin\normalizeKey
static normalizeKey( $key)
Normalize a skin preference value to a form that can be loaded.
Definition: Skin.php:95
Skin\footerLink
footerLink( $desc, $page)
Returns an HTML link for use in the footer.
Definition: Skin.php:1019
Skin\generateDebugHTML
generateDebugHTML()
Generate debug data HTML for displaying at the bottom of the main content area.
Definition: Skin.php:665
getPermissionManager
getPermissionManager()
$wgFallbackSkin
$wgFallbackSkin
Fallback skin used when the skin defined by $wgDefaultSkin can't be found.
Definition: DefaultSettings.php:3422
Skin\$stylename
string $stylename
Stylesheets set to use.
Definition: Skin.php:52
Skin\getCategories
getCategories()
Definition: Skin.php:606
Skin\setRelevantUser
setRelevantUser( $u)
Set the "relevant" user.
Definition: Skin.php:341
ContextSource\getOutput
getOutput()
Definition: ContextSource.php:112
ContextSource
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
Definition: ContextSource.php:29
$modules
$modules
Definition: HTMLFormElement.php:13
Skin\addToSidebar
addToSidebar(&$bar, $message)
Add content from a sidebar system message Currently only used for MediaWiki:Sidebar (but may be used ...
Definition: Skin.php:1663
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:946
Skin\isRevisionCurrent
isRevisionCurrent()
Whether the revision displayed is the latest revision of the page.
Definition: Skin.php:309
Skin\drawCategoryBrowser
drawCategoryBrowser( $tree)
Render the array as a series of links.
Definition: Skin.php:582
Skin\getLanguages
getLanguages()
Generates array of language links for the current page.
Definition: Skin.php:1334
Skin\makeNSUrl
static makeNSUrl( $name, $urlaction='', $namespace=NS_MAIN)
this can be passed the NS number as defined in Language.php
Definition: Skin.php:1258
$title
$title
Definition: testCompression.php:38
Skin\preloadExistence
preloadExistence()
Preload the existence of three commonly-requested pages in a single query.
Definition: Skin.php:251
Linker\makeExternalLink
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:845
$wgDefaultSkin
$wgDefaultSkin
Default skin, for new users and anonymous visitors.
Definition: DefaultSettings.php:3415
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:83
Skin\disclaimerLink
disclaimerLink()
Gets the link to the wiki's general disclaimers page.
Definition: Skin.php:1108
Skin\getLogo
getLogo()
URL to the default square logo (1x key) Please use ResourceLoaderSkinModule::getAvailableLogos.
Definition: Skin.php:490
$revStore
$revStore
Definition: testCompression.php:55
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:913
OutputPage
This is one of the Core classes and should be read at least once by any new developers.
Definition: OutputPage.php:46
Skin\makeToolbox
makeToolbox( $navUrls, $feedUrls)
Create an array of common toolbox items from the data in the quicktemplate stored by SkinTemplate.
Definition: Skin.php:2012
ResourceLoaderSkinModule\getAvailableLogos
static getAvailableLogos( $conf)
Return an array of all available logos that a skin may use.
Definition: ResourceLoaderSkinModule.php:319
Skin\$mRelevantTitle
$mRelevantTitle
Definition: Skin.php:45
$urls
$urls
Definition: opensearch_desc.php:82
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:168
wfUrlProtocols
wfUrlProtocols( $includeProtocolRelative=true)
Returns a regular expression of url protocols.
Definition: GlobalFunctions.php:719
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:621
Skin\getRelevantTitle
getRelevantTitle()
Return the "relevant" title.
Definition: Skin.php:332
$content
$content
Definition: router.php:76
Skin\getSkinStylePath
getSkinStylePath( $name)
Return a fully resolved style path URL to images or styles stored in the current skin's folder.
Definition: Skin.php:1158
Skin\getPageClasses
getPageClasses( $title)
TODO: document.
Definition: Skin.php:427
Skin\makeSearchInput
makeSearchInput( $attrs=[])
Definition: Skin.php:2360
Skin\getDefaultModules
getDefaultModules()
Defines the ResourceLoader modules that should be added to the skin It is recommended that skins wish...
Definition: Skin.php:170
Skin\getIndicatorsHTML
getIndicatorsHTML( $indicators)
Get the suggested HTML for page status indicators: icons (or short text snippets) usually displayed i...
Definition: Skin.php:2081
Skin\getSearchLink
getSearchLink()
Definition: Skin.php:819
$line
$line
Definition: mcc.php:119
Skin\getPersonalToolsForMakeListItem
getPersonalToolsForMakeListItem( $urls)
Create an array of personal tools items from the data in the quicktemplate stored by SkinTemplate.
Definition: Skin.php:2109
Skin\printSource
printSource()
Text with the permalink to the source page, usually shown on the footer of a printed page.
Definition: Skin.php:696
Skin\buildNavUrls
buildNavUrls()
Build array of common navigation links.
Definition: Skin.php:1440
Linker\titleAttrib
static titleAttrib( $name, $options=null, array $msgParams=[])
Given the id of an interface element, constructs the appropriate title attribute from the system mess...
Definition: Linker.php:2112
$lines
if(!file_exists( $CREDITS)) $lines
Definition: updateCredits.php:49
Skin\logoText
logoText( $align='')
Definition: Skin.php:955
Skin\getNewtalks
getNewtalks()
Gets new talk page messages for the current user and returns an appropriate alert message (or an empt...
Definition: Skin.php:1768
Title
Represents a title within MediaWiki.
Definition: Title.php:42
Skin\makeUrl
static makeUrl( $name, $urlaction='')
Definition: Skin.php:1229
wfMatchesDomainList
wfMatchesDomainList( $url, $domains)
Check whether a given URL has a domain that occurs in a given set of domains.
Definition: GlobalFunctions.php:879
$cache
$cache
Definition: mcc.php:33
LanguageCode\bcp47
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: LanguageCode.php:175
Skin\getCopyright
getCopyright( $type='detect')
Definition: Skin.php:828
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
SpecialPage\setContext
setContext( $context)
Sets the context this SpecialPage is executed in.
Definition: SpecialPage.php:689
Skin\getRelevantUser
getRelevantUser()
Return the "relevant" user.
Definition: Skin.php:353
Skin\subPageSubtitle
subPageSubtitle( $out=null)
Definition: Skin.php:762
NS_USER
const NS_USER
Definition: Defines.php:71
Skin\getUndeleteLink
getUndeleteLink()
Definition: Skin.php:715
Skin\makeKnownUrlDetails
static makeKnownUrlDetails( $name, $urlaction='')
Make URL details where the article exists (or at least it's convenient to think so)
Definition: Skin.php:1288
$wgSkipSkins
$wgSkipSkins
Specify the names of skins that should not be presented in the list of available skins in user prefer...
Definition: DefaultSettings.php:3434
Skin\setupSkinUserCss
setupSkinUserCss(OutputPage $out)
Hook point for adding style modules to OutputPage.
Definition: Skin.php:418
Skin\makeMainPageUrl
static makeMainPageUrl( $urlaction='')
Definition: Skin.php:1173
$t
$t
Definition: testCompression.php:74
Skin\getCopyrightIcon
getCopyrightIcon()
Definition: Skin.php:871
Skin\outputPage
outputPage()
Outputs the HTML generated by other functions.
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
Skin\getSkinName
getSkinName()
Definition: Skin.php:149
Skin\getAllowedSkins
static getAllowedSkins()
Fetch the list of user-selectable skins in regards to $wgSkipSkins.
Definition: Skin.php:70
Skin
The main skin class which provides methods and properties for all other skins.
Definition: Skin.php:39
Skin\initPage
initPage(OutputPage $out)
Definition: Skin.php:156
Skin\setRelevantTitle
setRelevantTitle( $t)
Set the "relevant" title.
Definition: Skin.php:318
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:54
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:133
Skin\makeSearchButton
makeSearchButton( $mode, $attrs=[])
Definition: Skin.php:2377
Skin\getRevisionId
getRevisionId()
Get the current revision ID.
Definition: Skin.php:299
Skin\addToSidebarPlain
addToSidebarPlain(&$bar, $text)
Add content from plain text.
Definition: Skin.php:1674
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:39
Skin\__construct
__construct( $skinname=null)
Definition: Skin.php:140
Skin\makeLink
makeLink( $key, $item, $options=[])
Makes a link, usually used by makeListItem to generate a link for an item in a list used in navigatio...
Definition: Skin.php:2195
Skin\makeInternalOrExternalUrl
static makeInternalOrExternalUrl( $name)
If url string starts with http, consider as external URL, else internal.
Definition: Skin.php:1242
Skin\makeFooterIcon
makeFooterIcon( $icon, $withImage='withImage')
Renders a $wgFooterIcons icon according to the method's arguments.
Definition: Skin.php:978
$type
$type
Definition: testCompression.php:52