MediaWiki  master
Skin.php
Go to the documentation of this file.
1 <?php
26 
38 abstract class Skin extends ContextSource {
42  protected $skinname = null;
43 
44  protected $mRelevantTitle = null;
45  protected $mRelevantUser = null;
46 
51  public $stylename = null;
52 
57  static function getSkinNames() {
58  return SkinFactory::getDefaultInstance()->getSkinNames();
59  }
60 
65  static function getSkinNameMessages() {
66  $messages = [];
67  foreach ( self::getSkinNames() as $skinKey => $skinName ) {
68  $messages[] = "skinname-$skinKey";
69  }
70  return $messages;
71  }
72 
80  public static function getAllowedSkins() {
81  global $wgSkipSkins;
82 
83  $allowedSkins = self::getSkinNames();
84 
85  foreach ( $wgSkipSkins as $skip ) {
86  unset( $allowedSkins[$skip] );
87  }
88 
89  return $allowedSkins;
90  }
91 
101  static function normalizeKey( $key ) {
103 
104  $skinNames = self::getSkinNames();
105 
106  // Make keys lowercase for case-insensitive matching.
107  $skinNames = array_change_key_case( $skinNames, CASE_LOWER );
108  $key = strtolower( $key );
109  $defaultSkin = strtolower( $wgDefaultSkin );
110  $fallbackSkin = strtolower( $wgFallbackSkin );
111 
112  if ( $key == '' || $key == 'default' ) {
113  // Don't return the default immediately;
114  // in a misconfiguration we need to fall back.
115  $key = $defaultSkin;
116  }
117 
118  if ( isset( $skinNames[$key] ) ) {
119  return $key;
120  }
121 
122  // Older versions of the software used a numeric setting
123  // in the user preferences.
124  $fallback = [
125  0 => $defaultSkin,
126  2 => 'cologneblue'
127  ];
128 
129  if ( isset( $fallback[$key] ) ) {
130  $key = $fallback[$key];
131  }
132 
133  if ( isset( $skinNames[$key] ) ) {
134  return $key;
135  } elseif ( isset( $skinNames[$defaultSkin] ) ) {
136  return $defaultSkin;
137  } else {
138  return $fallbackSkin;
139  }
140  }
141 
146  public function __construct( $skinname = null ) {
147  if ( is_string( $skinname ) ) {
148  $this->skinname = $skinname;
149  }
150  }
151 
155  public function getSkinName() {
156  return $this->skinname;
157  }
158 
162  public function initPage( OutputPage $out ) {
163  $this->preloadExistence();
164  }
165 
176  public function getDefaultModules() {
177  $out = $this->getOutput();
178  $config = $this->getConfig();
179  $user = $this->getUser();
180 
181  // Modules declared in the $modules literal are loaded
182  // for ALL users, on ALL pages, in ALL skins.
183  // Keep this list as small as possible!
184  $modules = [
185  'styles' => [
186  // The 'styles' key sets render-blocking style modules
187  // Unlike other keys in $modules, this is an associative array
188  // where each key is its own group pointing to a list of modules
189  'core' => [
190  'mediawiki.legacy.shared',
191  'mediawiki.legacy.commonPrint',
192  ],
193  'content' => [],
194  'syndicate' => [],
195  ],
196  'core' => [
197  'site',
198  'mediawiki.page.startup',
199  ],
200  // modules that enhance the content in some way
201  'content' => [
202  'mediawiki.page.ready',
203  ],
204  // modules relating to search functionality
205  'search' => [
206  'mediawiki.searchSuggest',
207  ],
208  // modules relating to functionality relating to watching an article
209  'watch' => [],
210  // modules which relate to the current users preferences
211  'user' => [],
212  // modules relating to RSS/Atom Feeds
213  'syndicate' => [],
214  ];
215 
216  // Preload jquery.tablesorter for mediawiki.page.ready
217  if ( strpos( $out->getHTML(), 'sortable' ) !== false ) {
218  $modules['content'][] = 'jquery.tablesorter';
219  }
220 
221  // Preload jquery.makeCollapsible for mediawiki.page.ready
222  if ( strpos( $out->getHTML(), 'mw-collapsible' ) !== false ) {
223  $modules['content'][] = 'jquery.makeCollapsible';
224  $modules['styles']['content'][] = 'jquery.makeCollapsible.styles';
225  }
226 
227  // Deprecated since 1.26: Unconditional loading of mediawiki.ui.button
228  // on every page is deprecated. Express a dependency instead.
229  if ( strpos( $out->getHTML(), 'mw-ui-button' ) !== false ) {
230  $modules['styles']['content'][] = 'mediawiki.ui.button';
231  }
232 
233  if ( $out->isTOCEnabled() ) {
234  $modules['content'][] = 'mediawiki.toc';
235  $modules['styles']['content'][] = 'mediawiki.toc.styles';
236  }
237 
238  // Add various resources if required
239  if ( $user->isLoggedIn()
240  && $user->isAllowedAll( 'writeapi', 'viewmywatchlist', 'editmywatchlist' )
241  && $this->getRelevantTitle()->canExist()
242  ) {
243  $modules['watch'][] = 'mediawiki.page.watch.ajax';
244  }
245 
246  if ( $user->getBoolOption( 'editsectiononrightclick' ) ) {
247  $modules['user'][] = 'mediawiki.action.view.rightClickEdit';
248  }
249 
250  // Crazy edit-on-double-click stuff
251  if ( $out->isArticle() && $user->getOption( 'editondblclick' ) ) {
252  $modules['user'][] = 'mediawiki.action.view.dblClickEdit';
253  }
254 
255  if ( $out->isSyndicated() ) {
256  $modules['styles']['syndicate'][] = 'mediawiki.feedlink';
257  }
258 
259  return $modules;
260  }
261 
265  protected function preloadExistence() {
266  $titles = [];
267 
268  // User/talk link
269  $user = $this->getUser();
270  if ( $user->isLoggedIn() ) {
271  $titles[] = $user->getUserPage();
272  $titles[] = $user->getTalkPage();
273  }
274 
275  // Check, if the page can hold some kind of content, otherwise do nothing
276  $title = $this->getRelevantTitle();
277  if ( $title->canExist() ) {
278  if ( $title->isTalkPage() ) {
279  $titles[] = $title->getSubjectPage();
280  } else {
281  $titles[] = $title->getTalkPage();
282  }
283  }
284 
285  // Footer links (used by SkinTemplate::prepareQuickTemplate)
286  foreach ( [
287  $this->footerLinkTitle( 'privacy', 'privacypage' ),
288  $this->footerLinkTitle( 'aboutsite', 'aboutpage' ),
289  $this->footerLinkTitle( 'disclaimers', 'disclaimerpage' ),
290  ] as $title ) {
291  if ( $title ) {
292  $titles[] = $title;
293  }
294  }
295 
296  Hooks::run( 'SkinPreloadExistence', [ &$titles, $this ] );
297 
298  if ( $titles ) {
299  $lb = new LinkBatch( $titles );
300  $lb->setCaller( __METHOD__ );
301  $lb->execute();
302  }
303  }
304 
310  public function getRevisionId() {
311  return $this->getOutput()->getRevisionId();
312  }
313 
319  public function isRevisionCurrent() {
320  $revID = $this->getRevisionId();
321  return $revID == 0 || $revID == $this->getTitle()->getLatestRevID();
322  }
323 
329  public function setRelevantTitle( $t ) {
330  $this->mRelevantTitle = $t;
331  }
332 
343  public function getRelevantTitle() {
344  return $this->mRelevantTitle ?? $this->getTitle();
345  }
346 
352  public function setRelevantUser( $u ) {
353  $this->mRelevantUser = $u;
354  }
355 
364  public function getRelevantUser() {
365  if ( isset( $this->mRelevantUser ) ) {
366  return $this->mRelevantUser;
367  }
368  $title = $this->getRelevantTitle();
369  if ( $title->hasSubjectNamespace( NS_USER ) ) {
370  $rootUser = $title->getRootText();
371  if ( User::isIP( $rootUser ) ) {
372  $this->mRelevantUser = User::newFromName( $rootUser, false );
373  } else {
374  $user = User::newFromName( $rootUser, false );
375 
376  if ( $user ) {
377  $user->load( User::READ_NORMAL );
378 
379  if ( $user->isLoggedIn() ) {
380  $this->mRelevantUser = $user;
381  }
382  }
383  }
384  return $this->mRelevantUser;
385  }
386  return null;
387  }
388 
393  abstract function outputPage( OutputPage $out = null );
394 
400  public static function makeVariablesScript( $data, $nonce = null ) {
401  if ( $data ) {
404  $nonce
405  );
406  }
407  return '';
408  }
409 
416  public static function getDynamicStylesheetQuery() {
417  return [
418  'action' => 'raw',
419  'ctype' => 'text/css',
420  ];
421  }
422 
429  public function setupSkinUserCss( OutputPage $out ) {
430  // Stub.
431  }
432 
438  function getPageClasses( $title ) {
439  $numeric = 'ns-' . $title->getNamespace();
440  $user = $this->getUser();
441 
442  if ( $title->isSpecialPage() ) {
443  $type = 'ns-special';
444  // T25315: provide a class based on the canonical special page name without subpages
445  list( $canonicalName ) = MediaWikiServices::getInstance()->getSpecialPageFactory()->
446  resolveAlias( $title->getDBkey() );
447  if ( $canonicalName ) {
448  $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" );
449  } else {
450  $type .= ' mw-invalidspecialpage';
451  }
452  } else {
453  if ( $title->isTalkPage() ) {
454  $type = 'ns-talk';
455  } else {
456  $type = 'ns-subject';
457  }
458  // T208315: add HTML class when the user can edit the page
459  if ( $title->quickUserCan( 'edit', $user ) ) {
460  $type .= ' mw-editable';
461  }
462  }
463 
464  $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() );
465  $root = Sanitizer::escapeClass( 'rootpage-' . $title->getRootTitle()->getPrefixedText() );
466 
467  return "$numeric $type $name $root";
468  }
469 
474  public function getHtmlElementAttributes() {
475  $lang = $this->getLanguage();
476  return [
477  'lang' => $lang->getHtmlCode(),
478  'dir' => $lang->getDir(),
479  'class' => 'client-nojs',
480  ];
481  }
482 
490  function addToBodyAttributes( $out, &$bodyAttrs ) {
491  // does nothing by default
492  }
493 
498  function getLogo() {
499  return $this->getConfig()->get( 'Logo' );
500  }
501 
511  public function shouldPreloadLogo() {
512  return false;
513  }
514 
518  function getCategoryLinks() {
519  $out = $this->getOutput();
520  $allCats = $out->getCategoryLinks();
521 
522  if ( $allCats === [] ) {
523  return '';
524  }
525 
526  $embed = "<li>";
527  $pop = "</li>";
528 
529  $s = '';
530  $colon = $this->msg( 'colon-separator' )->escaped();
531 
532  if ( !empty( $allCats['normal'] ) ) {
533  $t = $embed . implode( "{$pop}{$embed}", $allCats['normal'] ) . $pop;
534 
535  $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) )->escaped();
536  $linkPage = $this->msg( 'pagecategorieslink' )->inContentLanguage()->text();
537  $title = Title::newFromText( $linkPage );
538  $link = $title ? Linker::link( $title, $msg ) : $msg;
539  $s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' .
540  $link . $colon . '<ul>' . $t . '</ul>' . '</div>';
541  }
542 
543  # Hidden categories
544  if ( isset( $allCats['hidden'] ) ) {
545  if ( $this->getUser()->getBoolOption( 'showhiddencats' ) ) {
546  $class = ' mw-hidden-cats-user-shown';
547  } elseif ( $this->getTitle()->getNamespace() == NS_CATEGORY ) {
548  $class = ' mw-hidden-cats-ns-shown';
549  } else {
550  $class = ' mw-hidden-cats-hidden';
551  }
552 
553  $s .= "<div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks$class\">" .
554  $this->msg( 'hidden-categories' )->numParams( count( $allCats['hidden'] ) )->escaped() .
555  $colon . '<ul>' . $embed . implode( "{$pop}{$embed}", $allCats['hidden'] ) . $pop . '</ul>' .
556  '</div>';
557  }
558 
559  # optional 'dmoz-like' category browser. Will be shown under the list
560  # of categories an article belong to
561  if ( $this->getConfig()->get( 'UseCategoryBrowser' ) ) {
562  $s .= '<br /><hr />';
563 
564  # get a big array of the parents tree
565  $parenttree = $this->getTitle()->getParentCategoryTree();
566  # Skin object passed by reference cause it can not be
567  # accessed under the method subfunction drawCategoryBrowser
568  $tempout = explode( "\n", $this->drawCategoryBrowser( $parenttree ) );
569  # Clean out bogus first entry and sort them
570  unset( $tempout[0] );
571  asort( $tempout );
572  # Output one per line
573  $s .= implode( "<br />\n", $tempout );
574  }
575 
576  return $s;
577  }
578 
584  function drawCategoryBrowser( $tree ) {
585  $return = '';
586 
587  foreach ( $tree as $element => $parent ) {
588  if ( empty( $parent ) ) {
589  # element start a new list
590  $return .= "\n";
591  } else {
592  # grab the others elements
593  $return .= $this->drawCategoryBrowser( $parent ) . ' &gt; ';
594  }
595 
596  # add our current element to the list
597  $eltitle = Title::newFromText( $element );
598  $return .= Linker::link( $eltitle, htmlspecialchars( $eltitle->getText() ) );
599  }
600 
601  return $return;
602  }
603 
607  function getCategories() {
608  $out = $this->getOutput();
609  $catlinks = $this->getCategoryLinks();
610 
611  // Check what we're showing
612  $allCats = $out->getCategoryLinks();
613  $showHidden = $this->getUser()->getBoolOption( 'showhiddencats' ) ||
614  $this->getTitle()->getNamespace() == NS_CATEGORY;
615 
616  $classes = [ 'catlinks' ];
617  if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) {
618  $classes[] = 'catlinks-allhidden';
619  }
620 
621  return Html::rawElement(
622  'div',
623  [ 'id' => 'catlinks', 'class' => $classes, 'data-mw' => 'interface' ],
624  $catlinks
625  );
626  }
627 
642  protected function afterContentHook() {
643  $data = '';
644 
645  if ( Hooks::run( 'SkinAfterContent', [ &$data, $this ] ) ) {
646  // adding just some spaces shouldn't toggle the output
647  // of the whole <div/>, so we use trim() here
648  if ( trim( $data ) != '' ) {
649  // Doing this here instead of in the skins to
650  // ensure that the div has the same ID in all
651  // skins
652  $data = "<div id='mw-data-after-content'>\n" .
653  "\t$data\n" .
654  "</div>\n";
655  }
656  } else {
657  wfDebug( "Hook SkinAfterContent changed output processing.\n" );
658  }
659 
660  return $data;
661  }
662 
668  protected function generateDebugHTML() {
669  return MWDebug::getHTMLDebugLog();
670  }
671 
677  function bottomScripts() {
678  // TODO and the suckage continues. This function is really just a wrapper around
679  // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned
680  // up at some point
681  $chunks = [ $this->getOutput()->getBottomScripts() ];
682 
683  // Keep the hook appendage separate to preserve WrappedString objects.
684  // This enables BaseTemplate::getTrail() to merge them where possible.
685  $extraHtml = '';
686  Hooks::run( 'SkinAfterBottomScripts', [ $this, &$extraHtml ] );
687  if ( $extraHtml !== '' ) {
688  $chunks[] = $extraHtml;
689  }
690  return WrappedString::join( "\n", $chunks );
691  }
692 
699  function printSource() {
700  $oldid = $this->getRevisionId();
701  if ( $oldid ) {
702  $canonicalUrl = $this->getTitle()->getCanonicalURL( 'oldid=' . $oldid );
703  $url = htmlspecialchars( wfExpandIRI( $canonicalUrl ) );
704  } else {
705  // oldid not available for non existing pages
706  $url = htmlspecialchars( wfExpandIRI( $this->getTitle()->getCanonicalURL() ) );
707  }
708 
709  return $this->msg( 'retrievedfrom' )
710  ->rawParams( '<a dir="ltr" href="' . $url . '">' . $url . '</a>' )
711  ->parse();
712  }
713 
717  function getUndeleteLink() {
718  $action = $this->getRequest()->getVal( 'action', 'view' );
719  $title = $this->getTitle();
720 
721  if ( ( !$title->exists() || $action == 'history' ) &&
722  $title->quickUserCan( 'deletedhistory', $this->getUser() )
723  ) {
724  $n = $title->isDeleted();
725 
726  if ( $n ) {
727  if ( $this->getTitle()->quickUserCan( 'undelete', $this->getUser() ) ) {
728  $msg = 'thisisdeleted';
729  } else {
730  $msg = 'viewdeleted';
731  }
732 
733  return $this->msg( $msg )->rawParams(
735  SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ),
736  $this->msg( 'restorelink' )->numParams( $n )->escaped() )
737  )->escaped();
738  }
739  }
740 
741  return '';
742  }
743 
748  function subPageSubtitle( $out = null ) {
749  if ( $out === null ) {
750  $out = $this->getOutput();
751  }
752  $title = $out->getTitle();
753  $subpages = '';
754 
755  if ( !Hooks::run( 'SkinSubPageSubtitle', [ &$subpages, $this, $out ] ) ) {
756  return $subpages;
757  }
758 
759  if ( $out->isArticle() && MWNamespace::hasSubpages( $title->getNamespace() ) ) {
760  $ptext = $title->getPrefixedText();
761  if ( strpos( $ptext, '/' ) !== false ) {
762  $links = explode( '/', $ptext );
763  array_pop( $links );
764  $c = 0;
765  $growinglink = '';
766  $display = '';
767  $lang = $this->getLanguage();
768 
769  foreach ( $links as $link ) {
770  $growinglink .= $link;
771  $display .= $link;
772  $linkObj = Title::newFromText( $growinglink );
773 
774  if ( is_object( $linkObj ) && $linkObj->isKnown() ) {
775  $getlink = Linker::linkKnown(
776  $linkObj,
777  htmlspecialchars( $display )
778  );
779 
780  $c++;
781 
782  if ( $c > 1 ) {
783  $subpages .= $lang->getDirMarkEntity() . $this->msg( 'pipe-separator' )->escaped();
784  } else {
785  $subpages .= '&lt; ';
786  }
787 
788  $subpages .= $getlink;
789  $display = '';
790  } else {
791  $display .= '/';
792  }
793  $growinglink .= '/';
794  }
795  }
796  }
797 
798  return $subpages;
799  }
800 
804  function getSearchLink() {
806  return $searchPage->getLocalURL();
807  }
808 
812  function escapeSearchLink() {
813  return htmlspecialchars( $this->getSearchLink() );
814  }
815 
820  function getCopyright( $type = 'detect' ) {
821  if ( $type == 'detect' ) {
822  if ( !$this->isRevisionCurrent()
823  && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled()
824  ) {
825  $type = 'history';
826  } else {
827  $type = 'normal';
828  }
829  }
830 
831  if ( $type == 'history' ) {
832  $msg = 'history_copyright';
833  } else {
834  $msg = 'copyright';
835  }
836 
837  $config = $this->getConfig();
838 
839  if ( $config->get( 'RightsPage' ) ) {
840  $title = Title::newFromText( $config->get( 'RightsPage' ) );
841  $link = Linker::linkKnown( $title, $config->get( 'RightsText' ) );
842  } elseif ( $config->get( 'RightsUrl' ) ) {
843  $link = Linker::makeExternalLink( $config->get( 'RightsUrl' ), $config->get( 'RightsText' ) );
844  } elseif ( $config->get( 'RightsText' ) ) {
845  $link = $config->get( 'RightsText' );
846  } else {
847  # Give up now
848  return '';
849  }
850 
851  // Allow for site and per-namespace customization of copyright notice.
852  // @todo Remove deprecated $forContent param from hook handlers and then remove here.
853  $forContent = true;
854 
855  Hooks::run(
856  'SkinCopyrightFooter',
857  [ $this->getTitle(), $type, &$msg, &$link, &$forContent ]
858  );
859 
860  return $this->msg( $msg )->rawParams( $link )->text();
861  }
862 
866  function getCopyrightIcon() {
867  $out = '';
868  $config = $this->getConfig();
869 
870  $footerIcons = $config->get( 'FooterIcons' );
871  if ( $footerIcons['copyright']['copyright'] ) {
872  $out = $footerIcons['copyright']['copyright'];
873  } elseif ( $config->get( 'RightsIcon' ) ) {
874  $icon = htmlspecialchars( $config->get( 'RightsIcon' ) );
875  $url = $config->get( 'RightsUrl' );
876 
877  if ( $url ) {
878  $out .= '<a href="' . htmlspecialchars( $url ) . '">';
879  }
880 
881  $text = htmlspecialchars( $config->get( 'RightsText' ) );
882  $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />";
883 
884  if ( $url ) {
885  $out .= '</a>';
886  }
887  }
888 
889  return $out;
890  }
891 
896  function getPoweredBy() {
897  $resourceBasePath = $this->getConfig()->get( 'ResourceBasePath' );
898  $url1 = htmlspecialchars(
899  "$resourceBasePath/resources/assets/poweredby_mediawiki_88x31.png"
900  );
901  $url1_5 = htmlspecialchars(
902  "$resourceBasePath/resources/assets/poweredby_mediawiki_132x47.png"
903  );
904  $url2 = htmlspecialchars(
905  "$resourceBasePath/resources/assets/poweredby_mediawiki_176x62.png"
906  );
907  $text = '<a href="//www.mediawiki.org/"><img src="' . $url1
908  . '" srcset="' . $url1_5 . ' 1.5x, ' . $url2 . ' 2x" '
909  . 'height="31" width="88" alt="Powered by MediaWiki" /></a>';
910  Hooks::run( 'SkinGetPoweredBy', [ &$text, $this ] );
911  return $text;
912  }
913 
919  protected function lastModified() {
920  $timestamp = $this->getOutput()->getRevisionTimestamp();
921 
922  # No cached timestamp, load it from the database
923  if ( $timestamp === null ) {
924  $timestamp = Revision::getTimestampFromId( $this->getTitle(), $this->getRevisionId() );
925  }
926 
927  if ( $timestamp ) {
928  $d = $this->getLanguage()->userDate( $timestamp, $this->getUser() );
929  $t = $this->getLanguage()->userTime( $timestamp, $this->getUser() );
930  $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->parse();
931  } else {
932  $s = '';
933  }
934 
935  if ( MediaWikiServices::getInstance()->getDBLoadBalancer()->getLaggedReplicaMode() ) {
936  $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->parse() . '</strong>';
937  }
938 
939  return $s;
940  }
941 
946  function logoText( $align = '' ) {
947  if ( $align != '' ) {
948  $a = " style='float: {$align};'";
949  } else {
950  $a = '';
951  }
952 
953  $mp = $this->msg( 'mainpage' )->escaped();
954  $mptitle = Title::newMainPage();
955  $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' );
956 
957  $logourl = $this->getLogo();
958  $s = "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>";
959 
960  return $s;
961  }
962 
971  function makeFooterIcon( $icon, $withImage = 'withImage' ) {
972  if ( is_string( $icon ) ) {
973  $html = $icon;
974  } else { // Assuming array
975  $url = $icon["url"] ?? null;
976  unset( $icon["url"] );
977  if ( isset( $icon["src"] ) && $withImage === 'withImage' ) {
978  // do this the lazy way, just pass icon data as an attribute array
979  $html = Html::element( 'img', $icon );
980  } else {
981  $html = htmlspecialchars( $icon["alt"] );
982  }
983  if ( $url ) {
984  $html = Html::rawElement( 'a',
985  [ "href" => $url, "target" => $this->getConfig()->get( 'ExternalLinkTarget' ) ],
986  $html );
987  }
988  }
989  return $html;
990  }
991 
996  function mainPageLink() {
999  $this->msg( 'mainpage' )->escaped()
1000  );
1001 
1002  return $s;
1003  }
1004 
1011  public function footerLink( $desc, $page ) {
1012  $title = $this->footerLinkTitle( $desc, $page );
1013  if ( !$title ) {
1014  return '';
1015  }
1016 
1017  return Linker::linkKnown(
1018  $title,
1019  $this->msg( $desc )->escaped()
1020  );
1021  }
1022 
1028  private function footerLinkTitle( $desc, $page ) {
1029  // If the link description has been set to "-" in the default language,
1030  if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) {
1031  // then it is disabled, for all languages.
1032  return null;
1033  }
1034  // Otherwise, we display the link for the user, described in their
1035  // language (which may or may not be the same as the default language),
1036  // but we make the link target be the one site-wide page.
1037  $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() );
1038 
1039  return $title ?: null;
1040  }
1041 
1046  function privacyLink() {
1047  return $this->footerLink( 'privacy', 'privacypage' );
1048  }
1049 
1054  function aboutLink() {
1055  return $this->footerLink( 'aboutsite', 'aboutpage' );
1056  }
1057 
1062  function disclaimerLink() {
1063  return $this->footerLink( 'disclaimers', 'disclaimerpage' );
1064  }
1065 
1073  function editUrlOptions() {
1074  $options = [ 'action' => 'edit' ];
1075 
1076  if ( !$this->isRevisionCurrent() ) {
1077  $options['oldid'] = intval( $this->getRevisionId() );
1078  }
1079 
1080  return $options;
1081  }
1082 
1087  function showEmailUser( $id ) {
1088  if ( $id instanceof User ) {
1089  $targetUser = $id;
1090  } else {
1091  $targetUser = User::newFromId( $id );
1092  }
1093 
1094  # The sending user must have a confirmed email address and the receiving
1095  # user must accept emails from the sender.
1096  return $this->getUser()->canSendEmail()
1097  && SpecialEmailUser::validateTarget( $targetUser, $this->getUser() ) === '';
1098  }
1099 
1111  function getSkinStylePath( $name ) {
1112  if ( $this->stylename === null ) {
1113  $class = static::class;
1114  throw new MWException( "$class::\$stylename must be set to use getSkinStylePath()" );
1115  }
1116 
1117  return $this->getConfig()->get( 'StylePath' ) . "/{$this->stylename}/$name";
1118  }
1119 
1120  /* these are used extensively in SkinTemplate, but also some other places */
1121 
1126  static function makeMainPageUrl( $urlaction = '' ) {
1128  self::checkTitle( $title, '' );
1129 
1130  return $title->getLinkURL( $urlaction );
1131  }
1132 
1144  static function makeSpecialUrl( $name, $urlaction = '', $proto = null ) {
1146  if ( is_null( $proto ) ) {
1147  return $title->getLocalURL( $urlaction );
1148  } else {
1149  return $title->getFullURL( $urlaction, false, $proto );
1150  }
1151  }
1152 
1159  static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) {
1160  $title = SpecialPage::getSafeTitleFor( $name, $subpage );
1161  return $title->getLocalURL( $urlaction );
1162  }
1163 
1169  static function makeI18nUrl( $name, $urlaction = '' ) {
1170  $title = Title::newFromText( wfMessage( $name )->inContentLanguage()->text() );
1171  self::checkTitle( $title, $name );
1172  return $title->getLocalURL( $urlaction );
1173  }
1174 
1180  static function makeUrl( $name, $urlaction = '' ) {
1182  self::checkTitle( $title, $name );
1183 
1184  return $title->getLocalURL( $urlaction );
1185  }
1186 
1193  static function makeInternalOrExternalUrl( $name ) {
1194  if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $name ) ) {
1195  return $name;
1196  } else {
1197  return self::makeUrl( $name );
1198  }
1199  }
1200 
1208  static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) {
1209  $title = Title::makeTitleSafe( $namespace, $name );
1210  self::checkTitle( $title, $name );
1211 
1212  return $title->getLocalURL( $urlaction );
1213  }
1214 
1221  static function makeUrlDetails( $name, $urlaction = '' ) {
1223  self::checkTitle( $title, $name );
1224 
1225  return [
1226  'href' => $title->getLocalURL( $urlaction ),
1227  'exists' => $title->isKnown(),
1228  ];
1229  }
1230 
1237  static function makeKnownUrlDetails( $name, $urlaction = '' ) {
1239  self::checkTitle( $title, $name );
1240 
1241  return [
1242  'href' => $title->getLocalURL( $urlaction ),
1243  'exists' => true
1244  ];
1245  }
1246 
1253  static function checkTitle( &$title, $name ) {
1254  if ( !is_object( $title ) ) {
1256  if ( !is_object( $title ) ) {
1257  $title = Title::newFromText( '--error: link target missing--' );
1258  }
1259  }
1260  }
1261 
1283  public function buildSidebar() {
1284  $callback = function ( $old = null, &$ttl = null ) {
1285  $bar = [];
1286  $this->addToSidebar( $bar, 'sidebar' );
1287  Hooks::run( 'SkinBuildSidebar', [ $this, &$bar ] );
1288  if ( MessageCache::singleton()->isDisabled() ) {
1289  $ttl = WANObjectCache::TTL_UNCACHEABLE; // bug T133069
1290  }
1291 
1292  return $bar;
1293  };
1294 
1295  $msgCache = MessageCache::singleton();
1296  $wanCache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1297  $config = $this->getConfig();
1298 
1299  $sidebar = $config->get( 'EnableSidebarCache' )
1300  ? $wanCache->getWithSetCallback(
1301  $wanCache->makeKey( 'sidebar', $this->getLanguage()->getCode() ),
1302  $config->get( 'SidebarCacheExpiry' ),
1303  $callback,
1304  [
1305  'checkKeys' => [
1306  // Unless there is both no exact $code override nor an i18n definition
1307  // in the software, the only MediaWiki page to check is for $code.
1308  $msgCache->getCheckKey( $this->getLanguage()->getCode() )
1309  ],
1310  'lockTSE' => 30
1311  ]
1312  )
1313  : $callback();
1314 
1315  // Apply post-processing to the cached value
1316  Hooks::run( 'SidebarBeforeOutput', [ $this, &$sidebar ] );
1317 
1318  return $sidebar;
1319  }
1320 
1330  public function addToSidebar( &$bar, $message ) {
1331  $this->addToSidebarPlain( $bar, $this->msg( $message )->inContentLanguage()->plain() );
1332  }
1333 
1341  function addToSidebarPlain( &$bar, $text ) {
1342  $lines = explode( "\n", $text );
1343 
1344  $heading = '';
1345  $config = $this->getConfig();
1346  $messageTitle = $config->get( 'EnableSidebarCache' )
1347  ? Title::newMainPage() : $this->getTitle();
1348 
1349  foreach ( $lines as $line ) {
1350  if ( strpos( $line, '*' ) !== 0 ) {
1351  continue;
1352  }
1353  $line = rtrim( $line, "\r" ); // for Windows compat
1354 
1355  if ( strpos( $line, '**' ) !== 0 ) {
1356  $heading = trim( $line, '* ' );
1357  if ( !array_key_exists( $heading, $bar ) ) {
1358  $bar[$heading] = [];
1359  }
1360  } else {
1361  $line = trim( $line, '* ' );
1362 
1363  if ( strpos( $line, '|' ) !== false ) { // sanity check
1364  $line = MessageCache::singleton()->transform( $line, false, null, $messageTitle );
1365  $line = array_map( 'trim', explode( '|', $line, 2 ) );
1366  if ( count( $line ) !== 2 ) {
1367  // Second sanity check, could be hit by people doing
1368  // funky stuff with parserfuncs... (T35321)
1369  continue;
1370  }
1371 
1372  $extraAttribs = [];
1373 
1374  $msgLink = $this->msg( $line[0] )->title( $messageTitle )->inContentLanguage();
1375  if ( $msgLink->exists() ) {
1376  $link = $msgLink->text();
1377  if ( $link == '-' ) {
1378  continue;
1379  }
1380  } else {
1381  $link = $line[0];
1382  }
1383  $msgText = $this->msg( $line[1] )->title( $messageTitle );
1384  if ( $msgText->exists() ) {
1385  $text = $msgText->text();
1386  } else {
1387  $text = $line[1];
1388  }
1389 
1390  if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $link ) ) {
1391  $href = $link;
1392 
1393  // Parser::getExternalLinkAttribs won't work here because of the Namespace things
1394  if ( $config->get( 'NoFollowLinks' ) &&
1395  !wfMatchesDomainList( $href, $config->get( 'NoFollowDomainExceptions' ) )
1396  ) {
1397  $extraAttribs['rel'] = 'nofollow';
1398  }
1399 
1400  if ( $config->get( 'ExternalLinkTarget' ) ) {
1401  $extraAttribs['target'] = $config->get( 'ExternalLinkTarget' );
1402  }
1403  } else {
1405 
1406  if ( $title ) {
1407  $title = $title->fixSpecialName();
1408  $href = $title->getLinkURL();
1409  } else {
1410  $href = 'INVALID-TITLE';
1411  }
1412  }
1413 
1414  $bar[$heading][] = array_merge( [
1415  'text' => $text,
1416  'href' => $href,
1417  'id' => Sanitizer::escapeIdForAttribute( 'n-' . strtr( $line[1], ' ', '-' ) ),
1418  'active' => false,
1419  ], $extraAttribs );
1420  } else {
1421  continue;
1422  }
1423  }
1424  }
1425 
1426  return $bar;
1427  }
1428 
1434  function getNewtalks() {
1435  $newMessagesAlert = '';
1436  $user = $this->getUser();
1437  $newtalks = $user->getNewMessageLinks();
1438  $out = $this->getOutput();
1439 
1440  // Allow extensions to disable or modify the new messages alert
1441  if ( !Hooks::run( 'GetNewMessagesAlert', [ &$newMessagesAlert, $newtalks, $user, $out ] ) ) {
1442  return '';
1443  }
1444  if ( $newMessagesAlert ) {
1445  return $newMessagesAlert;
1446  }
1447 
1448  if ( count( $newtalks ) == 1 && WikiMap::isCurrentWikiId( $newtalks[0]['wiki'] ) ) {
1449  $uTalkTitle = $user->getTalkPage();
1450  $lastSeenRev = $newtalks[0]['rev'] ?? null;
1451  $nofAuthors = 0;
1452  if ( $lastSeenRev !== null ) {
1453  $plural = true; // Default if we have a last seen revision: if unknown, use plural
1454  $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL );
1455  if ( $latestRev !== null ) {
1456  // Singular if only 1 unseen revision, plural if several unseen revisions.
1457  $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
1458  $nofAuthors = $uTalkTitle->countAuthorsBetween(
1459  $lastSeenRev, $latestRev, 10, 'include_new' );
1460  }
1461  } else {
1462  // Singular if no revision -> diff link will show latest change only in any case
1463  $plural = false;
1464  }
1465  $plural = $plural ? 999 : 1;
1466  // 999 signifies "more than one revision". We don't know how many, and even if we did,
1467  // the number of revisions or authors is not necessarily the same as the number of
1468  // "messages".
1469  $newMessagesLink = Linker::linkKnown(
1470  $uTalkTitle,
1471  $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
1472  [],
1473  $uTalkTitle->isRedirect() ? [ 'redirect' => 'no' ] : []
1474  );
1475 
1476  $newMessagesDiffLink = Linker::linkKnown(
1477  $uTalkTitle,
1478  $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
1479  [],
1480  $lastSeenRev !== null
1481  ? [ 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' ]
1482  : [ 'diff' => 'cur' ]
1483  );
1484 
1485  if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
1486  $newMessagesAlert = $this->msg(
1487  'youhavenewmessagesfromusers',
1488  $newMessagesLink,
1489  $newMessagesDiffLink
1490  )->numParams( $nofAuthors, $plural );
1491  } else {
1492  // $nofAuthors === 11 signifies "11 or more" ("more than 10")
1493  $newMessagesAlert = $this->msg(
1494  $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
1495  $newMessagesLink,
1496  $newMessagesDiffLink
1497  )->numParams( $plural );
1498  }
1499  $newMessagesAlert = $newMessagesAlert->text();
1500  # Disable CDN cache
1501  $out->setCdnMaxage( 0 );
1502  } elseif ( count( $newtalks ) ) {
1503  $sep = $this->msg( 'newtalkseparator' )->escaped();
1504  $msgs = [];
1505 
1506  foreach ( $newtalks as $newtalk ) {
1507  $msgs[] = Xml::element(
1508  'a',
1509  [ 'href' => $newtalk['link'] ], $newtalk['wiki']
1510  );
1511  }
1512  $parts = implode( $sep, $msgs );
1513  $newMessagesAlert = $this->msg( 'youhavenewmessagesmulti' )->rawParams( $parts )->escaped();
1514  $out->setCdnMaxage( 0 );
1515  }
1516 
1517  return $newMessagesAlert;
1518  }
1519 
1527  private function getCachedNotice( $name ) {
1528  $needParse = false;
1529  $config = $this->getConfig();
1530 
1531  if ( $name === 'default' ) {
1532  // special case
1533  $notice = $config->get( 'SiteNotice' );
1534  if ( empty( $notice ) ) {
1535  return false;
1536  }
1537  } else {
1538  $msg = $this->msg( $name )->inContentLanguage();
1539  if ( $msg->isBlank() ) {
1540  return '';
1541  } elseif ( $msg->isDisabled() ) {
1542  return false;
1543  }
1544  $notice = $msg->plain();
1545  }
1546 
1547  $services = MediaWikiServices::getInstance();
1548  $cache = $services->getMainWANObjectCache();
1549  $parsed = $cache->getWithSetCallback(
1550  // Use the extra hash appender to let eg SSL variants separately cache
1551  // Key is verified with md5 hash of unparsed wikitext
1552  $cache->makeKey( $name, $config->get( 'RenderHashAppend' ), md5( $notice ) ),
1553  // TTL in seconds
1554  600,
1555  function () use ( $notice ) {
1556  return $this->getOutput()->parseAsInterface( $notice );
1557  }
1558  );
1559 
1560  $contLang = $services->getContentLanguage();
1561  return Html::rawElement(
1562  'div',
1563  [
1564  'id' => 'localNotice',
1565  'lang' => $contLang->getHtmlCode(),
1566  'dir' => $contLang->getDir()
1567  ],
1568  $parsed
1569  );
1570  }
1571 
1577  function getSiteNotice() {
1578  $siteNotice = '';
1579 
1580  if ( Hooks::run( 'SiteNoticeBefore', [ &$siteNotice, $this ] ) ) {
1581  if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) {
1582  $siteNotice = $this->getCachedNotice( 'sitenotice' );
1583  } else {
1584  $anonNotice = $this->getCachedNotice( 'anonnotice' );
1585  if ( $anonNotice === false ) {
1586  $siteNotice = $this->getCachedNotice( 'sitenotice' );
1587  } else {
1588  $siteNotice = $anonNotice;
1589  }
1590  }
1591  if ( $siteNotice === false ) {
1592  $siteNotice = $this->getCachedNotice( 'default' );
1593  }
1594  }
1595 
1596  Hooks::run( 'SiteNoticeAfter', [ &$siteNotice, $this ] );
1597  return $siteNotice;
1598  }
1599 
1613  public function doEditSectionLink( Title $nt, $section, $tooltip, Language $lang ) {
1614  // HTML generated here should probably have userlangattributes
1615  // added to it for LTR text on RTL pages
1616 
1617  $attribs = [];
1618  if ( !is_null( $tooltip ) ) {
1619  $attribs['title'] = $this->msg( 'editsectionhint' )->rawParams( $tooltip )
1620  ->inLanguage( $lang )->text();
1621  }
1622 
1623  $links = [
1624  'editsection' => [
1625  'text' => $this->msg( 'editsection' )->inLanguage( $lang )->escaped(),
1626  'targetTitle' => $nt,
1627  'attribs' => $attribs,
1628  'query' => [ 'action' => 'edit', 'section' => $section ],
1629  'options' => [ 'noclasses', 'known' ]
1630  ]
1631  ];
1632 
1633  Hooks::run( 'SkinEditSectionLinks', [ $this, $nt, $section, $tooltip, &$links, $lang ] );
1634 
1635  $result = '<span class="mw-editsection"><span class="mw-editsection-bracket">[</span>';
1636 
1637  $linksHtml = [];
1638  foreach ( $links as $k => $linkDetails ) {
1639  $linksHtml[] = Linker::link(
1640  $linkDetails['targetTitle'],
1641  $linkDetails['text'],
1642  $linkDetails['attribs'],
1643  $linkDetails['query'],
1644  $linkDetails['options']
1645  );
1646  }
1647 
1648  $result .= implode(
1649  '<span class="mw-editsection-divider">'
1650  . $this->msg( 'pipe-separator' )->inLanguage( $lang )->escaped()
1651  . '</span>',
1652  $linksHtml
1653  );
1654 
1655  $result .= '<span class="mw-editsection-bracket">]</span></span>';
1656  return $result;
1657  }
1658 
1659 }
showEmailUser( $id)
Definition: Skin.php:1087
static makeUrlDetails( $name, $urlaction='')
these return an array with the &#39;href&#39; and boolean &#39;exists&#39;
Definition: Skin.php:1221
static makeI18nUrl( $name, $urlaction='')
Definition: Skin.php:1169
makeFooterIcon( $icon, $withImage='withImage')
Renders a $wgFooterIcons icon according to the method&#39;s arguments.
Definition: Skin.php:971
wfUrlProtocols( $includeProtocolRelative=true)
Returns a regular expression of url protocols.
lastModified()
Get the timestamp of the latest revision, formatted in user language.
Definition: Skin.php:919
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
Definition: hooks.txt:1995
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
footerLinkTitle( $desc, $page)
Definition: Skin.php:1028
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:232
static normalizeKey( $key)
Normalize a skin preference value to a form that can be loaded.
Definition: Skin.php:101
either a plain
Definition: hooks.txt:2056
static getTimestampFromId( $title, $id, $flags=0)
Get rev_timestamp from rev_id, without loading the rest of the row.
Definition: Revision.php:1264
afterContentHook()
This runs a hook to allow extensions placing their stuff after content and article metadata (e...
Definition: Skin.php:642
setRelevantUser( $u)
Set the "relevant" user.
Definition: Skin.php:352
getPoweredBy()
Gets the powered by MediaWiki icon.
Definition: Skin.php:896
getNewtalks()
Gets new talk page messages for the current user and returns an appropriate alert message (or an empt...
Definition: Skin.php:1434
static validateTarget( $target, User $sender=null)
Validate target User.
const NS_MAIN
Definition: Defines.php:64
static makeSpecialUrl( $name, $urlaction='', $proto=null)
Make a URL for a Special Page using the given query and protocol.
Definition: Skin.php:1144
static newMainPage()
Create a new Title for the Main Page.
Definition: Title.php:597
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:966
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
buildSidebar()
Build an array that represents the sidebar(s), the navigation bar among them.
Definition: Skin.php:1283
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
privacyLink()
Gets the link to the wiki&#39;s privacy policy page.
Definition: Skin.php:1046
if(!isset( $args[0])) $lang
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
doEditSectionLink(Title $nt, $section, $tooltip, Language $lang)
Create a section edit link.
Definition: Skin.php:1613
initPage(OutputPage $out)
Definition: Skin.php:162
editUrlOptions()
Return URL options for the &#39;edit page&#39; link.
Definition: Skin.php:1073
const TTL_UNCACHEABLE
Idiom for getWithSetCallback() callbacks to avoid calling set()
getLogo()
URL to the logo.
Definition: Skin.php:498
static getSkinNames()
Fetch the set of available skins.
Definition: Skin.php:57
getCopyrightIcon()
Definition: Skin.php:866
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
static makeConfigSetScript(array $configuration)
Returns JS code which will set the MediaWiki configuration array to the given value.
msg( $key)
Get a Message object with context set Parameters are the same as wfMessage()
getCachedNotice( $name)
Get a cached notice.
Definition: Skin.php:1527
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that&#39;s attached to a given link target...
Definition: Revision.php:137
shouldPreloadLogo()
Whether the logo should be preloaded with an HTTP link header or not.
Definition: Skin.php:511
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second as well as the first line of the second redirect text
getCategories()
Definition: Skin.php:607
getSiteNotice()
Get the site notice.
Definition: Skin.php:1577
static getHTMLDebugLog()
Generate debug log in HTML for displaying at the bottom of the main content area. ...
Definition: MWDebug.php:455
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1993
getPageClasses( $title)
TODO: document.
Definition: Skin.php:438
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:3042
addToBodyAttributes( $out, &$bodyAttrs)
This will be called by OutputPage::headElement when it is creating the "<body>" tag, skins can override it if they have a need to add in any body attributes or classes of their own.
Definition: Skin.php:490
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:47
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:34
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:1995
preloadExistence()
Preload the existence of three commonly-requested pages in a single query.
Definition: Skin.php:265
footerLink( $desc, $page)
Returns an HTML link for use in the footer.
Definition: Skin.php:1011
$modules
wfMatchesDomainList( $url, $domains)
Check whether a given URL has a domain that occurs in a given set of domains.
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
$mRelevantTitle
Definition: Skin.php:44
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response after the basic globals have been set but before ordinary actions take place or wrap services the preferred way to define a new service is the $wgServiceWiringFiles array $services
Definition: hooks.txt:2220
static checkTitle(&$title, $name)
make sure we have some title to operate on
Definition: Skin.php:1253
the value of this variable comes from LanguageConverter indexed by page_id indexed by prefixed DB keys on which the links will be shown can modify can modify can modify this should be populated with an alert message to that effect $newtalks
Definition: hooks.txt:1689
mainPageLink()
Gets the link to the wiki&#39;s main page.
Definition: Skin.php:996
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:1289
drawCategoryBrowser( $tree)
Render the array as a series of links.
Definition: Skin.php:584
getRelevantTitle()
Return the "relevant" title.
Definition: Skin.php:343
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
subPageSubtitle( $out=null)
Definition: Skin.php:748
getCategoryLinks()
Definition: Skin.php:518
disclaimerLink()
Gets the link to the wiki&#39;s general disclaimers page.
Definition: Skin.php:1062
$cache
Definition: mcc.php:33
getSearchLink()
Definition: Skin.php:804
$mRelevantUser
Definition: Skin.php:45
const NS_CATEGORY
Definition: Defines.php:78
static makeVariablesScript( $data, $nonce=null)
Definition: Skin.php:400
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1995
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:835
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing & $attribs
Definition: hooks.txt:1995
generateDebugHTML()
Generate debug data HTML for displaying at the bottom of the main content area.
Definition: Skin.php:668
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
setRelevantTitle( $t)
Set the "relevant" title.
Definition: Skin.php:329
getHtmlElementAttributes()
Return values for <html> element.
Definition: Skin.php:474
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:935
static escapeClass( $class)
Given a value, escape it so that it can be used as a CSS class and return it.
Definition: Sanitizer.php:1418
getRelevantUser()
Return the "relevant" user.
Definition: Skin.php:364
aboutLink()
Gets the link to the wiki&#39;s about page.
Definition: Skin.php:1054
addToSidebarPlain(&$bar, $text)
Add content from plain text.
Definition: Skin.php:1341
addToSidebar(&$bar, $message)
Add content from a sidebar system message Currently only used for MediaWiki:Sidebar (but may be used ...
Definition: Skin.php:1330
outputPage(OutputPage $out=null)
Outputs the HTML generated by other functions.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don&#39;t need a full Title object...
Definition: SpecialPage.php:82
logoText( $align='')
Definition: Skin.php:946
static makeKnownUrlDetails( $name, $urlaction='')
Make URL details where the article exists (or at least it&#39;s convenient to think so) ...
Definition: Skin.php:1237
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
static makeInlineScript( $script, $nonce=null)
Returns an HTML script tag that runs given JS code after startup and base modules.
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template $section
Definition: hooks.txt:3042
static makeUrl( $name, $urlaction='')
Definition: Skin.php:1180
getDefaultModules()
Defines the ResourceLoader modules that should be added to the skin It is recommended that skins wish...
Definition: Skin.php:176
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:573
printSource()
Text with the permalink to the source page, usually shown on the footer of a printed page...
Definition: Skin.php:699
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:608
string $stylename
Stylesheets set to use.
Definition: Skin.php:51
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:780
$fallback
Definition: MessagesAb.php:11
$lines
Definition: router.php:61
static isCurrentWikiId( $wikiId)
Definition: WikiMap.php:320
linkcache txt The LinkCache class maintains a list of article titles and the information about whether or not the article exists in the database This is used to mark up links when displaying a page If the same link appears more than once on any page then it only has to be looked up once In most cases link lookups are done in batches with the LinkBatch class or the equivalent in so the link cache is mostly useful for short snippets of parsed and for links in the navigation areas of the skin The link cache was formerly used to track links used in a document for the purposes of updating the link tables This application is now deprecated To create a you can use the following $titles
Definition: linkcache.txt:17
$parent
Definition: pageupdater.txt:71
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
wfExpandIRI( $url)
Take a URL, make sure it&#39;s expanded to fully qualified, and replace any encoded non-ASCII Unicode cha...
isRevisionCurrent()
Whether the revision displayed is the latest revision of the page.
Definition: Skin.php:319
static hasSubpages( $index)
Does the namespace allow subpages?
static linkKnown( $target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to &#39;known&#39;.
Definition: Linker.php:146
$wgDefaultSkin
Default skin, for new users and anonymous visitors.
static makeNSUrl( $name, $urlaction='', $namespace=NS_MAIN)
this can be passed the NS number as defined in Language.php
Definition: Skin.php:1208
$wgSkipSkins
Specify the names of skins that should not be presented in the list of available skins in user prefer...
$line
Definition: cdb.php:59
static getSkinNameMessages()
Fetch the skinname messages for available skins.
Definition: Skin.php:65
getCopyright( $type='detect')
Definition: Skin.php:820
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:41
$wgFallbackSkin
Fallback skin used when the skin defined by $wgDefaultSkin can&#39;t be found.
getUndeleteLink()
Definition: Skin.php:717
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
$messages
static makeInternalOrExternalUrl( $name)
If url string starts with http, consider as external URL, else internal.
Definition: Skin.php:1193
getSkinStylePath( $name)
Return a fully resolved style path URL to images or styles stored in the current skin&#39;s folder...
Definition: Skin.php:1111
static getSafeTitleFor( $name, $subpage=false)
Get a localised Title object for a page name with a possibly unvalidated subpage. ...
static getDynamicStylesheetQuery()
Get the query to generate a dynamic stylesheet.
Definition: Skin.php:416
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:584
setupSkinUserCss(OutputPage $out)
Hook point for adding style modules to OutputPage.
Definition: Skin.php:429
static makeMainPageUrl( $urlaction='')
Definition: Skin.php:1126
$searchPage
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:84
getRevisionId()
Get the current revision ID.
Definition: Skin.php:310
escapeSearchLink()
Definition: Skin.php:812
getSkinName()
Definition: Skin.php:155
static getAllowedSkins()
Fetch the list of user-selectable skins in regards to $wgSkipSkins.
Definition: Skin.php:80
static getDefaultInstance()
Definition: SkinFactory.php:50
__construct( $skinname=null)
Definition: Skin.php:146
bottomScripts()
This gets called shortly before the "</body>" tag.
Definition: Skin.php:677
static singleton()
Get the signleton instance of this class.
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1486
static makeSpecialUrlSubpage( $name, $subpage, $urlaction='')
Definition: Skin.php:1159
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
string null $skinname
Definition: Skin.php:42
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:280