MediaWiki  master
SkinTemplate.php
Go to the documentation of this file.
1 <?php
23 
38 class SkinTemplate extends Skin {
43  public $skinname = 'monobook';
44 
49  public $template = QuickTemplate::class;
50 
51  public $thispage;
52  public $titletxt;
53  public $userpage;
54  public $thisquery;
55  public $loggedin;
56  public $username;
58 
68  function setupTemplate( $classname ) {
69  return new $classname( $this->getConfig() );
70  }
71 
77  public function getLanguages() {
79  if ( $wgHideInterlanguageLinks ) {
80  return [];
81  }
82 
83  $userLang = $this->getLanguage();
84  $languageLinks = [];
85 
86  foreach ( $this->getOutput()->getLanguageLinks() as $languageLinkText ) {
87  $class = 'interlanguage-link interwiki-' . explode( ':', $languageLinkText, 2 )[0];
88 
89  $languageLinkTitle = Title::newFromText( $languageLinkText );
90  if ( $languageLinkTitle ) {
91  $ilInterwikiCode = $languageLinkTitle->getInterwiki();
92  $ilLangName = Language::fetchLanguageName( $ilInterwikiCode );
93 
94  if ( strval( $ilLangName ) === '' ) {
95  $ilDisplayTextMsg = wfMessage( "interlanguage-link-$ilInterwikiCode" );
96  if ( !$ilDisplayTextMsg->isDisabled() ) {
97  // Use custom MW message for the display text
98  $ilLangName = $ilDisplayTextMsg->text();
99  } else {
100  // Last resort: fallback to the language link target
101  $ilLangName = $languageLinkText;
102  }
103  } else {
104  // Use the language autonym as display text
105  $ilLangName = $this->formatLanguageName( $ilLangName );
106  }
107 
108  // CLDR extension or similar is required to localize the language name;
109  // otherwise we'll end up with the autonym again.
110  $ilLangLocalName = Language::fetchLanguageName(
111  $ilInterwikiCode,
112  $userLang->getCode()
113  );
114 
115  $languageLinkTitleText = $languageLinkTitle->getText();
116  if ( $ilLangLocalName === '' ) {
117  $ilFriendlySiteName = wfMessage( "interlanguage-link-sitename-$ilInterwikiCode" );
118  if ( !$ilFriendlySiteName->isDisabled() ) {
119  if ( $languageLinkTitleText === '' ) {
120  $ilTitle = wfMessage(
121  'interlanguage-link-title-nonlangonly',
122  $ilFriendlySiteName->text()
123  )->text();
124  } else {
125  $ilTitle = wfMessage(
126  'interlanguage-link-title-nonlang',
127  $languageLinkTitleText,
128  $ilFriendlySiteName->text()
129  )->text();
130  }
131  } else {
132  // we have nothing friendly to put in the title, so fall back to
133  // displaying the interlanguage link itself in the title text
134  // (similar to what is done in page content)
135  $ilTitle = $languageLinkTitle->getInterwiki() .
136  ":$languageLinkTitleText";
137  }
138  } elseif ( $languageLinkTitleText === '' ) {
139  $ilTitle = wfMessage(
140  'interlanguage-link-title-langonly',
141  $ilLangLocalName
142  )->text();
143  } else {
144  $ilTitle = wfMessage(
145  'interlanguage-link-title',
146  $languageLinkTitleText,
147  $ilLangLocalName
148  )->text();
149  }
150 
151  $ilInterwikiCodeBCP47 = LanguageCode::bcp47( $ilInterwikiCode );
152  $languageLink = [
153  'href' => $languageLinkTitle->getFullURL(),
154  'text' => $ilLangName,
155  'title' => $ilTitle,
156  'class' => $class,
157  'link-class' => 'interlanguage-link-target',
158  'lang' => $ilInterwikiCodeBCP47,
159  'hreflang' => $ilInterwikiCodeBCP47,
160  ];
161  Hooks::run(
162  'SkinTemplateGetLanguageLink',
163  [ &$languageLink, $languageLinkTitle, $this->getTitle(), $this->getOutput() ]
164  );
165  $languageLinks[] = $languageLink;
166  }
167  }
168 
169  return $languageLinks;
170  }
171 
175  protected function setupTemplateForOutput() {
176  $request = $this->getRequest();
177  $user = $this->getUser();
178  $title = $this->getTitle();
179 
180  $tpl = $this->setupTemplate( $this->template );
181 
182  $this->thispage = $title->getPrefixedDBkey();
183  $this->titletxt = $title->getPrefixedText();
184  $this->userpage = $user->getUserPage()->getPrefixedText();
185  $query = [];
186  if ( !$request->wasPosted() ) {
187  $query = $request->getValues();
188  unset( $query['title'] );
189  unset( $query['returnto'] );
190  unset( $query['returntoquery'] );
191  }
192  $this->thisquery = wfArrayToCgi( $query );
193  $this->loggedin = $user->isLoggedIn();
194  $this->username = $user->getName();
195 
196  if ( $this->loggedin ) {
197  $this->userpageUrlDetails = self::makeUrlDetails( $this->userpage );
198  } else {
199  # This won't be used in the standard skins, but we define it to preserve the interface
200  # To save time, we check for existence
201  $this->userpageUrlDetails = self::makeKnownUrlDetails( $this->userpage );
202  }
203 
204  return $tpl;
205  }
206 
210  function outputPage() {
211  Profiler::instance()->setAllowOutput();
212  $out = $this->getOutput();
213 
214  $this->initPage( $out );
215  $tpl = $this->prepareQuickTemplate();
216  // execute template
217  $res = $tpl->execute();
218 
219  // result may be an error
220  $this->printOrError( $res );
221  }
222 
230  protected function wrapHTML( $title, $html ) {
231  # An ID that includes the actual body text; without categories, contentSub, ...
232  $realBodyAttribs = [ 'id' => 'mw-content-text' ];
233 
234  # Add a mw-content-ltr/rtl class to be able to style based on text
235  # direction when the content is different from the UI language (only
236  # when viewing)
237  # Most information on special pages and file pages is in user language,
238  # rather than content language, so those will not get this
239  if ( Action::getActionName( $this ) === 'view' &&
240  ( !$title->inNamespaces( NS_SPECIAL, NS_FILE ) || $title->isRedirect() ) ) {
241  $pageLang = $title->getPageViewLanguage();
242  $realBodyAttribs['lang'] = $pageLang->getHtmlCode();
243  $realBodyAttribs['dir'] = $pageLang->getDir();
244  $realBodyAttribs['class'] = 'mw-content-' . $pageLang->getDir();
245  }
246 
247  return Html::rawElement( 'div', $realBodyAttribs, $html );
248  }
249 
256  protected function prepareQuickTemplate() {
261 
262  $title = $this->getTitle();
263  $request = $this->getRequest();
264  $out = $this->getOutput();
265  $tpl = $this->setupTemplateForOutput();
266 
267  $tpl->set( 'title', $out->getPageTitle() );
268  $tpl->set( 'pagetitle', $out->getHTMLTitle() );
269  $tpl->set( 'displaytitle', $out->mPageLinkTitle );
270 
271  $tpl->set( 'thispage', $this->thispage );
272  $tpl->set( 'titleprefixeddbkey', $this->thispage );
273  $tpl->set( 'titletext', $title->getText() );
274  $tpl->set( 'articleid', $title->getArticleID() );
275 
276  $tpl->set( 'isarticle', $out->isArticle() );
277 
278  $subpagestr = $this->subPageSubtitle();
279  if ( $subpagestr !== '' ) {
280  $subpagestr = '<span class="subpages">' . $subpagestr . '</span>';
281  }
282  $tpl->set( 'subtitle', $subpagestr . $out->getSubtitle() );
283 
284  $undelete = $this->getUndeleteLink();
285  if ( $undelete === '' ) {
286  $tpl->set( 'undelete', '' );
287  } else {
288  $tpl->set( 'undelete', '<span class="subpages">' . $undelete . '</span>' );
289  }
290 
291  $tpl->set( 'catlinks', $this->getCategories() );
292  if ( $out->isSyndicated() ) {
293  $feeds = [];
294  foreach ( $out->getSyndicationLinks() as $format => $link ) {
295  $feeds[$format] = [
296  // Messages: feed-atom, feed-rss
297  'text' => $this->msg( "feed-$format" )->text(),
298  'href' => $link
299  ];
300  }
301  $tpl->set( 'feeds', $feeds );
302  } else {
303  $tpl->set( 'feeds', false );
304  }
305 
306  $tpl->set( 'mimetype', $wgMimeType );
307  $tpl->set( 'charset', 'UTF-8' );
308  $tpl->set( 'wgScript', $wgScript );
309  $tpl->set( 'skinname', $this->skinname );
310  $tpl->set( 'skinclass', static::class );
311  $tpl->set( 'skin', $this );
312  $tpl->set( 'stylename', $this->stylename );
313  $tpl->set( 'printable', $out->isPrintable() );
314  $tpl->set( 'handheld', $request->getBool( 'handheld' ) );
315  $tpl->set( 'loggedin', $this->loggedin );
316  $tpl->set( 'notspecialpage', !$title->isSpecialPage() );
317  $tpl->set( 'searchaction', $this->getSearchLink() );
318  $tpl->set( 'searchtitle', SpecialPage::getTitleFor( 'Search' )->getPrefixedDBkey() );
319  $tpl->set( 'search', trim( $request->getVal( 'search' ) ) );
320  $tpl->set( 'stylepath', $wgStylePath );
321  $tpl->set( 'articlepath', $wgArticlePath );
322  $tpl->set( 'scriptpath', $wgScriptPath );
323  $tpl->set( 'serverurl', $wgServer );
324  $tpl->set( 'logopath', $wgLogo );
325  $tpl->set( 'sitename', $wgSitename );
326 
327  $userLang = $this->getLanguage();
328  $userLangCode = $userLang->getHtmlCode();
329  $userLangDir = $userLang->getDir();
330 
331  $tpl->set( 'lang', $userLangCode );
332  $tpl->set( 'dir', $userLangDir );
333  $tpl->set( 'rtl', $userLang->isRTL() );
334 
335  $tpl->set( 'capitalizeallnouns', $userLang->capitalizeAllNouns() ? ' capitalize-all-nouns' : '' );
336  $tpl->set( 'showjumplinks', true ); // showjumplinks preference has been removed
337  $tpl->set( 'username', $this->loggedin ? $this->username : null );
338  $tpl->set( 'userpage', $this->userpage );
339  $tpl->set( 'userpageurl', $this->userpageUrlDetails['href'] );
340  $tpl->set( 'userlang', $userLangCode );
341 
342  // Users can have their language set differently than the
343  // content of the wiki. For these users, tell the web browser
344  // that interface elements are in a different language.
345  $tpl->set( 'userlangattributes', '' );
346  $tpl->set( 'specialpageattributes', '' ); # obsolete
347  // Used by VectorBeta to insert HTML before content but after the
348  // heading for the page title. Defaults to empty string.
349  $tpl->set( 'prebodyhtml', '' );
350 
351  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
352  if (
353  $userLangCode !== $contLang->getHtmlCode() ||
354  $userLangDir !== $contLang->getDir()
355  ) {
356  $escUserlang = htmlspecialchars( $userLangCode );
357  $escUserdir = htmlspecialchars( $userLangDir );
358  // Attributes must be in double quotes because htmlspecialchars() doesn't
359  // escape single quotes
360  $attrs = " lang=\"$escUserlang\" dir=\"$escUserdir\"";
361  $tpl->set( 'userlangattributes', $attrs );
362  }
363 
364  $tpl->set( 'newtalk', $this->getNewtalks() );
365  $tpl->set( 'logo', $this->logoText() );
366 
367  $tpl->set( 'copyright', false );
368  // No longer used
369  $tpl->set( 'viewcount', false );
370  $tpl->set( 'lastmod', false );
371  $tpl->set( 'credits', false );
372  $tpl->set( 'numberofwatchingusers', false );
373  if ( $title->exists() ) {
374  if ( $out->isArticle() && $out->isRevisionCurrent() ) {
375  if ( $wgMaxCredits != 0 ) {
377  $action = Action::factory(
378  'credits', $this->getWikiPage(), $this->getContext() );
379  '@phan-var CreditsAction $action';
380  $tpl->set( 'credits',
381  $action->getCredits( $wgMaxCredits, $wgShowCreditsIfMax ) );
382  } else {
383  $tpl->set( 'lastmod', $this->lastModified() );
384  }
385  }
386  if ( $out->showsCopyright() ) {
387  $tpl->set( 'copyright', $this->getCopyright() );
388  }
389  }
390 
391  $tpl->set( 'copyrightico', $this->getCopyrightIcon() );
392  $tpl->set( 'poweredbyico', $this->getPoweredBy() );
393  $tpl->set( 'disclaimer', $this->disclaimerLink() );
394  $tpl->set( 'privacy', $this->privacyLink() );
395  $tpl->set( 'about', $this->aboutLink() );
396 
397  $tpl->set( 'footerlinks', [
398  'info' => [
399  'lastmod',
400  'numberofwatchingusers',
401  'credits',
402  'copyright',
403  ],
404  'places' => [
405  'privacy',
406  'about',
407  'disclaimer',
408  ],
409  ] );
410 
411  global $wgFooterIcons;
412  $tpl->set( 'footericons', $wgFooterIcons );
413  foreach ( $tpl->data['footericons'] as $footerIconsKey => &$footerIconsBlock ) {
414  if ( count( $footerIconsBlock ) > 0 ) {
415  foreach ( $footerIconsBlock as &$footerIcon ) {
416  if ( isset( $footerIcon['src'] ) ) {
417  if ( !isset( $footerIcon['width'] ) ) {
418  $footerIcon['width'] = 88;
419  }
420  if ( !isset( $footerIcon['height'] ) ) {
421  $footerIcon['height'] = 31;
422  }
423  }
424  }
425  } else {
426  unset( $tpl->data['footericons'][$footerIconsKey] );
427  }
428  }
429 
430  $tpl->set( 'indicators', $out->getIndicators() );
431 
432  $tpl->set( 'sitenotice', $this->getSiteNotice() );
433  $tpl->set( 'printfooter', $this->printSource() );
434  // Wrap the bodyText with #mw-content-text element
435  $out->mBodytext = $this->wrapHTML( $title, $out->mBodytext );
436  $tpl->set( 'bodytext', $out->mBodytext );
437 
438  $language_urls = $this->getLanguages();
439  if ( count( $language_urls ) ) {
440  $tpl->set( 'language_urls', $language_urls );
441  } else {
442  $tpl->set( 'language_urls', false );
443  }
444 
445  # Personal toolbar
446  $tpl->set( 'personal_urls', $this->buildPersonalUrls() );
447  $content_navigation = $this->buildContentNavigationUrls();
448  $content_actions = $this->buildContentActionUrls( $content_navigation );
449  $tpl->set( 'content_navigation', $content_navigation );
450  $tpl->set( 'content_actions', $content_actions );
451 
452  $tpl->set( 'sidebar', $this->buildSidebar() );
453  $tpl->set( 'nav_urls', $this->buildNavUrls() );
454 
455  // Do this last in case hooks above add bottom scripts
456  $tpl->set( 'bottomscripts', $this->bottomScripts() );
457 
458  // Set the head scripts near the end, in case the above actions resulted in added scripts
459  $tpl->set( 'headelement', $out->headElement( $this ) );
460 
461  $tpl->set( 'debug', '' );
462  $tpl->set( 'debughtml', $this->generateDebugHTML() );
463  $tpl->set( 'reporttime', wfReportTime( $out->getCSPNonce() ) );
464 
465  // Avoid PHP 7.1 warning of passing $this by reference
466  $skinTemplate = $this;
467  // original version by hansm
468  if ( !Hooks::run( 'SkinTemplateOutputPageBeforeExec', [ &$skinTemplate, &$tpl ] ) ) {
469  wfDebug( __METHOD__ . ": Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!\n" );
470  }
471 
472  // Set the bodytext to another key so that skins can just output it on its own
473  // and output printfooter and debughtml separately
474  $tpl->set( 'bodycontent', $tpl->data['bodytext'] );
475 
476  // Append printfooter and debughtml onto bodytext so that skins that
477  // were already using bodytext before they were split out don't suddenly
478  // start not outputting information.
479  $tpl->data['bodytext'] .= Html::rawElement(
480  'div',
481  [ 'class' => 'printfooter' ],
482  "\n{$tpl->data['printfooter']}"
483  ) . "\n";
484  $tpl->data['bodytext'] .= $tpl->data['debughtml'];
485 
486  // allow extensions adding stuff after the page content.
487  // See Skin::afterContentHook() for further documentation.
488  $tpl->set( 'dataAfterContent', $this->afterContentHook() );
489 
490  return $tpl;
491  }
492 
497  public function getPersonalToolsList() {
498  return $this->makePersonalToolsList();
499  }
500 
510  public function makePersonalToolsList( $personalTools = null, $options = [] ) {
511  $tpl = $this->setupTemplateForOutput();
512  $tpl->set( 'personal_urls', $this->buildPersonalUrls() );
513  $html = '';
514 
515  if ( $personalTools === null ) {
516  $personalTools = ( $tpl instanceof BaseTemplate )
517  ? $tpl->getPersonalTools()
518  : [];
519  }
520 
521  foreach ( $personalTools as $key => $item ) {
522  $html .= $tpl->makeListItem( $key, $item, $options );
523  }
524 
525  return $html;
526  }
527 
535  public function getStructuredPersonalTools() {
536  $tpl = $this->setupTemplateForOutput();
537  $tpl->set( 'personal_urls', $this->buildPersonalUrls() );
538 
539  return ( $tpl instanceof BaseTemplate ) ? $tpl->getPersonalTools() : [];
540  }
541 
550  function formatLanguageName( $name ) {
551  return $this->getLanguage()->ucfirst( $name );
552  }
553 
562  function printOrError( $str ) {
563  echo $str;
564  }
565 
575  function useCombinedLoginLink() {
578  }
579 
584  protected function buildPersonalUrls() {
585  $title = $this->getTitle();
586  $request = $this->getRequest();
587  $pageurl = $title->getLocalURL();
588  $authManager = AuthManager::singleton();
589  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
590 
591  /* set up the default links for the personal toolbar */
592  $personal_urls = [];
593 
594  # Due to T34276, if a user does not have read permissions,
595  # $this->getTitle() will just give Special:Badtitle, which is
596  # not especially useful as a returnto parameter. Use the title
597  # from the request instead, if there was one.
598  if ( $permissionManager->userHasRight( $this->getUser(), 'read' ) ) {
599  $page = $this->getTitle();
600  } else {
601  $page = Title::newFromText( $request->getVal( 'title', '' ) );
602  }
603  $page = $request->getVal( 'returnto', $page );
604  $returnto = [];
605  if ( strval( $page ) !== '' ) {
606  $returnto['returnto'] = $page;
607  $query = $request->getVal( 'returntoquery', $this->thisquery );
608  $paramsArray = wfCgiToArray( $query );
609  $query = wfArrayToCgi( $paramsArray );
610  if ( $query != '' ) {
611  $returnto['returntoquery'] = $query;
612  }
613  }
614 
615  if ( $this->loggedin ) {
616  $personal_urls['userpage'] = [
617  'text' => $this->username,
618  'href' => &$this->userpageUrlDetails['href'],
619  'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
620  'exists' => $this->userpageUrlDetails['exists'],
621  'active' => ( $this->userpageUrlDetails['href'] == $pageurl ),
622  'dir' => 'auto'
623  ];
624  $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
625  $personal_urls['mytalk'] = [
626  'text' => $this->msg( 'mytalk' )->text(),
627  'href' => &$usertalkUrlDetails['href'],
628  'class' => $usertalkUrlDetails['exists'] ? false : 'new',
629  'exists' => $usertalkUrlDetails['exists'],
630  'active' => ( $usertalkUrlDetails['href'] == $pageurl )
631  ];
632  $href = self::makeSpecialUrl( 'Preferences' );
633  $personal_urls['preferences'] = [
634  'text' => $this->msg( 'mypreferences' )->text(),
635  'href' => $href,
636  'active' => ( $href == $pageurl )
637  ];
638 
639  if ( $permissionManager->userHasRight( $this->getUser(), 'viewmywatchlist' ) ) {
640  $href = self::makeSpecialUrl( 'Watchlist' );
641  $personal_urls['watchlist'] = [
642  'text' => $this->msg( 'mywatchlist' )->text(),
643  'href' => $href,
644  'active' => ( $href == $pageurl )
645  ];
646  }
647 
648  # We need to do an explicit check for Special:Contributions, as we
649  # have to match both the title, and the target, which could come
650  # from request values (Special:Contributions?target=Jimbo_Wales)
651  # or be specified in "sub page" form
652  # (Special:Contributions/Jimbo_Wales). The plot
653  # thickens, because the Title object is altered for special pages,
654  # so it doesn't contain the original alias-with-subpage.
655  $origTitle = Title::newFromText( $request->getText( 'title' ) );
656  if ( $origTitle instanceof Title && $origTitle->isSpecialPage() ) {
657  list( $spName, $spPar ) =
658  MediaWikiServices::getInstance()->getSpecialPageFactory()->
659  resolveAlias( $origTitle->getText() );
660  $active = $spName == 'Contributions'
661  && ( ( $spPar && $spPar == $this->username )
662  || $request->getText( 'target' ) == $this->username );
663  } else {
664  $active = false;
665  }
666 
667  $href = self::makeSpecialUrlSubpage( 'Contributions', $this->username );
668  $personal_urls['mycontris'] = [
669  'text' => $this->msg( 'mycontris' )->text(),
670  'href' => $href,
671  'active' => $active
672  ];
673 
674  // if we can't set the user, we can't unset it either
675  if ( $request->getSession()->canSetUser() ) {
676  $personal_urls['logout'] = [
677  'text' => $this->msg( 'pt-userlogout' )->text(),
678  'href' => self::makeSpecialUrl( 'Userlogout',
679  // Note: userlogout link must always contain an & character, otherwise we might not be able
680  // to detect a buggy precaching proxy (T19790)
681  ( $title->isSpecial( 'Preferences' ) ? [] : $returnto ) ),
682  'active' => false
683  ];
684  }
685  } else {
686  $useCombinedLoginLink = $this->useCombinedLoginLink();
687  if ( !$authManager->canCreateAccounts() || !$authManager->canAuthenticateNow() ) {
688  // don't show combined login/signup link if one of those is actually not available
689  $useCombinedLoginLink = false;
690  }
691 
692  $loginlink = $permissionManager->userHasRight( $this->getUser(), 'createaccount' )
693  && $useCombinedLoginLink ? 'nav-login-createaccount' : 'pt-login';
694 
695  $login_url = [
696  'text' => $this->msg( $loginlink )->text(),
697  'href' => self::makeSpecialUrl( 'Userlogin', $returnto ),
698  'active' => $title->isSpecial( 'Userlogin' )
699  || $title->isSpecial( 'CreateAccount' ) && $useCombinedLoginLink,
700  ];
701  $createaccount_url = [
702  'text' => $this->msg( 'pt-createaccount' )->text(),
703  'href' => self::makeSpecialUrl( 'CreateAccount', $returnto ),
704  'active' => $title->isSpecial( 'CreateAccount' ),
705  ];
706 
707  // No need to show Talk and Contributions to anons if they can't contribute!
708  if ( $permissionManager->groupHasPermission( '*', 'edit' ) ) {
709  // Because of caching, we can't link directly to the IP talk and
710  // contributions pages. Instead we use the special page shortcuts
711  // (which work correctly regardless of caching). This means we can't
712  // determine whether these links are active or not, but since major
713  // skins (MonoBook, Vector) don't use this information, it's not a
714  // huge loss.
715  $personal_urls['anontalk'] = [
716  'text' => $this->msg( 'anontalk' )->text(),
717  'href' => self::makeSpecialUrlSubpage( 'Mytalk', false ),
718  'active' => false
719  ];
720  $personal_urls['anoncontribs'] = [
721  'text' => $this->msg( 'anoncontribs' )->text(),
722  'href' => self::makeSpecialUrlSubpage( 'Mycontributions', false ),
723  'active' => false
724  ];
725  }
726 
727  if (
728  $authManager->canCreateAccounts()
729  && $permissionManager->userHasRight( $this->getUser(), 'createaccount' )
730  && !$useCombinedLoginLink
731  ) {
732  $personal_urls['createaccount'] = $createaccount_url;
733  }
734 
735  if ( $authManager->canAuthenticateNow() ) {
736  $key = $permissionManager->groupHasPermission( '*', 'read' )
737  ? 'login'
738  : 'login-private';
739  $personal_urls[$key] = $login_url;
740  }
741  }
742 
743  Hooks::runWithoutAbort( 'PersonalUrls', [ &$personal_urls, &$title, $this ] );
744  return $personal_urls;
745  }
746 
758  function tabAction( $title, $message, $selected, $query = '', $checkEdit = false ) {
759  $classes = [];
760  if ( $selected ) {
761  $classes[] = 'selected';
762  }
763  $exists = true;
764  if ( $checkEdit && !$title->isKnown() ) {
765  $classes[] = 'new';
766  $exists = false;
767  if ( $query !== '' ) {
768  $query = 'action=edit&redlink=1&' . $query;
769  } else {
770  $query = 'action=edit&redlink=1';
771  }
772  }
773 
774  $services = MediaWikiServices::getInstance();
775  $linkClass = $services->getLinkRenderer()->getLinkClasses( $title );
776 
777  // wfMessageFallback will nicely accept $message as an array of fallbacks
778  // or just a single key
779  $msg = wfMessageFallback( $message )->setContext( $this->getContext() );
780  if ( is_array( $message ) ) {
781  // for hook compatibility just keep the last message name
782  $message = end( $message );
783  }
784  if ( $msg->exists() ) {
785  $text = $msg->text();
786  } else {
787  $text = $services->getContentLanguage()->getConverter()->
788  convertNamespace( $services->getNamespaceInfo()->
789  getSubject( $title->getNamespace() ) );
790  }
791 
792  // Avoid PHP 7.1 warning of passing $this by reference
793  $skinTemplate = $this;
794  $result = [];
795  if ( !Hooks::run( 'SkinTemplateTabAction', [ &$skinTemplate,
796  $title, $message, $selected, $checkEdit,
797  &$classes, &$query, &$text, &$result ] ) ) {
798  return $result;
799  }
800 
801  $result = [
802  'class' => implode( ' ', $classes ),
803  'text' => $text,
804  'href' => $title->getLocalURL( $query ),
805  'exists' => $exists,
806  'primary' => true ];
807  if ( $linkClass !== '' ) {
808  $result['link-class'] = $linkClass;
809  }
810 
811  return $result;
812  }
813 
814  function makeTalkUrlDetails( $name, $urlaction = '' ) {
815  $title = Title::newFromText( $name );
816  if ( !is_object( $title ) ) {
817  throw new MWException( __METHOD__ . " given invalid pagename $name" );
818  }
819  $title = $title->getTalkPage();
820  self::checkTitle( $title, $name );
821  return [
822  'href' => $title->getLocalURL( $urlaction ),
823  'exists' => $title->isKnown(),
824  ];
825  }
826 
833  function makeArticleUrlDetails( $name, $urlaction = '' ) {
834  $title = Title::newFromText( $name );
835  $title = $title->getSubjectPage();
836  self::checkTitle( $title, $name );
837  return [
838  'href' => $title->getLocalURL( $urlaction ),
839  'exists' => $title->exists(),
840  ];
841  }
842 
877  protected function buildContentNavigationUrls() {
879 
880  // Display tabs for the relevant title rather than always the title itself
881  $title = $this->getRelevantTitle();
882  $onPage = $title->equals( $this->getTitle() );
883 
884  $out = $this->getOutput();
885  $request = $this->getRequest();
886  $user = $this->getUser();
887  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
888 
889  $content_navigation = [
890  'namespaces' => [],
891  'views' => [],
892  'actions' => [],
893  'variants' => []
894  ];
895 
896  // parameters
897  $action = $request->getVal( 'action', 'view' );
898 
899  $userCanRead = $permissionManager->quickUserCan( 'read', $user, $title );
900 
901  // Avoid PHP 7.1 warning of passing $this by reference
902  $skinTemplate = $this;
903  $preventActiveTabs = false;
904  Hooks::run( 'SkinTemplatePreventOtherActiveTabs', [ &$skinTemplate, &$preventActiveTabs ] );
905 
906  // Checks if page is some kind of content
907  if ( $title->canExist() ) {
908  // Gets page objects for the related namespaces
909  $subjectPage = $title->getSubjectPage();
910  $talkPage = $title->getTalkPage();
911 
912  // Determines if this is a talk page
913  $isTalk = $title->isTalkPage();
914 
915  // Generates XML IDs from namespace names
916  $subjectId = $title->getNamespaceKey( '' );
917 
918  if ( $subjectId == 'main' ) {
919  $talkId = 'talk';
920  } else {
921  $talkId = "{$subjectId}_talk";
922  }
923 
924  $skname = $this->skinname;
925 
926  // Adds namespace links
927  $subjectMsg = [ "nstab-$subjectId" ];
928  if ( $subjectPage->isMainPage() ) {
929  array_unshift( $subjectMsg, 'mainpage-nstab' );
930  }
931  $content_navigation['namespaces'][$subjectId] = $this->tabAction(
932  $subjectPage, $subjectMsg, !$isTalk && !$preventActiveTabs, '', $userCanRead
933  );
934  $content_navigation['namespaces'][$subjectId]['context'] = 'subject';
935  $content_navigation['namespaces'][$talkId] = $this->tabAction(
936  $talkPage, [ "nstab-$talkId", 'talk' ], $isTalk && !$preventActiveTabs, '', $userCanRead
937  );
938  $content_navigation['namespaces'][$talkId]['context'] = 'talk';
939 
940  if ( $userCanRead ) {
941  // Adds "view" view link
942  if ( $title->isKnown() ) {
943  $content_navigation['views']['view'] = $this->tabAction(
944  $isTalk ? $talkPage : $subjectPage,
945  [ "$skname-view-view", 'view' ],
946  ( $onPage && ( $action == 'view' || $action == 'purge' ) ), '', true
947  );
948  // signal to hide this from simple content_actions
949  $content_navigation['views']['view']['redundant'] = true;
950  }
951 
952  $page = $this->canUseWikiPage() ? $this->getWikiPage() : false;
953  $isRemoteContent = $page && !$page->isLocal();
954 
955  // If it is a non-local file, show a link to the file in its own repository
956  // @todo abstract this for remote content that isn't a file
957  if ( $isRemoteContent ) {
958  $content_navigation['views']['view-foreign'] = [
959  'class' => '',
960  'text' => wfMessageFallback( "$skname-view-foreign", 'view-foreign' )->
961  setContext( $this->getContext() )->
962  params( $page->getWikiDisplayName() )->text(),
963  'href' => $page->getSourceURL(),
964  'primary' => false,
965  ];
966  }
967 
968  // Checks if user can edit the current page if it exists or create it otherwise
969  if ( $permissionManager->quickUserCan( 'edit', $user, $title ) &&
970  ( $title->exists() ||
971  $permissionManager->quickUserCan( 'create', $user, $title ) )
972  ) {
973  // Builds CSS class for talk page links
974  $isTalkClass = $isTalk ? ' istalk' : '';
975  // Whether the user is editing the page
976  $isEditing = $onPage && ( $action == 'edit' || $action == 'submit' );
977  // Whether to show the "Add a new section" tab
978  // Checks if this is a current rev of talk page and is not forced to be hidden
979  $showNewSection = !$out->forceHideNewSectionLink()
980  && ( ( $isTalk && $out->isRevisionCurrent() ) || $out->showNewSectionLink() );
981  $section = $request->getVal( 'section' );
982 
983  if ( $title->exists()
984  || ( $title->getNamespace() == NS_MEDIAWIKI
985  && $title->getDefaultMessageText() !== false
986  )
987  ) {
988  $msgKey = $isRemoteContent ? 'edit-local' : 'edit';
989  } else {
990  $msgKey = $isRemoteContent ? 'create-local' : 'create';
991  }
992  $content_navigation['views']['edit'] = [
993  'class' => ( $isEditing && ( $section !== 'new' || !$showNewSection )
994  ? 'selected'
995  : ''
996  ) . $isTalkClass,
997  'text' => wfMessageFallback( "$skname-view-$msgKey", $msgKey )
998  ->setContext( $this->getContext() )->text(),
999  'href' => $title->getLocalURL( $this->editUrlOptions() ),
1000  'primary' => !$isRemoteContent, // don't collapse this in vector
1001  ];
1002 
1003  // section link
1004  if ( $showNewSection ) {
1005  // Adds new section link
1006  // $content_navigation['actions']['addsection']
1007  $content_navigation['views']['addsection'] = [
1008  'class' => ( $isEditing && $section == 'new' ) ? 'selected' : false,
1009  'text' => wfMessageFallback( "$skname-action-addsection", 'addsection' )
1010  ->setContext( $this->getContext() )->text(),
1011  'href' => $title->getLocalURL( 'action=edit&section=new' )
1012  ];
1013  }
1014  // Checks if the page has some kind of viewable source content
1015  } elseif ( $title->hasSourceText() ) {
1016  // Adds view source view link
1017  $content_navigation['views']['viewsource'] = [
1018  'class' => ( $onPage && $action == 'edit' ) ? 'selected' : false,
1019  'text' => wfMessageFallback( "$skname-action-viewsource", 'viewsource' )
1020  ->setContext( $this->getContext() )->text(),
1021  'href' => $title->getLocalURL( $this->editUrlOptions() ),
1022  'primary' => true, // don't collapse this in vector
1023  ];
1024  }
1025 
1026  // Checks if the page exists
1027  if ( $title->exists() ) {
1028  // Adds history view link
1029  $content_navigation['views']['history'] = [
1030  'class' => ( $onPage && $action == 'history' ) ? 'selected' : false,
1031  'text' => wfMessageFallback( "$skname-view-history", 'history_short' )
1032  ->setContext( $this->getContext() )->text(),
1033  'href' => $title->getLocalURL( 'action=history' ),
1034  ];
1035 
1036  if ( $permissionManager->quickUserCan( 'delete', $user, $title ) ) {
1037  $content_navigation['actions']['delete'] = [
1038  'class' => ( $onPage && $action == 'delete' ) ? 'selected' : false,
1039  'text' => wfMessageFallback( "$skname-action-delete", 'delete' )
1040  ->setContext( $this->getContext() )->text(),
1041  'href' => $title->getLocalURL( 'action=delete' )
1042  ];
1043  }
1044 
1045  if ( $permissionManager->quickUserCan( 'move', $user, $title ) ) {
1046  $moveTitle = SpecialPage::getTitleFor( 'Movepage', $title->getPrefixedDBkey() );
1047  $content_navigation['actions']['move'] = [
1048  'class' => $this->getTitle()->isSpecial( 'Movepage' ) ? 'selected' : false,
1049  'text' => wfMessageFallback( "$skname-action-move", 'move' )
1050  ->setContext( $this->getContext() )->text(),
1051  'href' => $moveTitle->getLocalURL()
1052  ];
1053  }
1054  } else {
1055  // article doesn't exist or is deleted
1056  if ( $permissionManager->quickUserCan( 'deletedhistory', $user, $title ) ) {
1057  $n = $title->isDeleted();
1058  if ( $n ) {
1059  $undelTitle = SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedDBkey() );
1060  // If the user can't undelete but can view deleted
1061  // history show them a "View .. deleted" tab instead.
1062  $msgKey = $permissionManager->quickUserCan( 'undelete',
1063  $user, $title ) ? 'undelete' : 'viewdeleted';
1064  $content_navigation['actions']['undelete'] = [
1065  'class' => $this->getTitle()->isSpecial( 'Undelete' ) ? 'selected' : false,
1066  'text' => wfMessageFallback( "$skname-action-$msgKey", "{$msgKey}_short" )
1067  ->setContext( $this->getContext() )->numParams( $n )->text(),
1068  'href' => $undelTitle->getLocalURL()
1069  ];
1070  }
1071  }
1072  }
1073 
1074  if ( $permissionManager->quickUserCan( 'protect', $user, $title ) &&
1075  $title->getRestrictionTypes() &&
1076  $permissionManager->getNamespaceRestrictionLevels( $title->getNamespace(), $user ) !== [ '' ]
1077  ) {
1078  $mode = $title->isProtected() ? 'unprotect' : 'protect';
1079  $content_navigation['actions'][$mode] = [
1080  'class' => ( $onPage && $action == $mode ) ? 'selected' : false,
1081  'text' => wfMessageFallback( "$skname-action-$mode", $mode )
1082  ->setContext( $this->getContext() )->text(),
1083  'href' => $title->getLocalURL( "action=$mode" )
1084  ];
1085  }
1086 
1087  // Checks if the user is logged in
1088  if ( $this->loggedin && $permissionManager->userHasAllRights( $user,
1089  'viewmywatchlist', 'editmywatchlist' )
1090  ) {
1100  $mode = $user->isWatched( $title ) ? 'unwatch' : 'watch';
1101  $content_navigation['actions'][$mode] = [
1102  'class' => 'mw-watchlink ' . (
1103  $onPage && ( $action == 'watch' || $action == 'unwatch' ) ? 'selected' : ''
1104  ),
1105  // uses 'watch' or 'unwatch' message
1106  'text' => $this->msg( $mode )->text(),
1107  'href' => $title->getLocalURL( [ 'action' => $mode ] ),
1108  // Set a data-mw=interface attribute, which the mediawiki.page.ajax
1109  // module will look for to make sure it's a trusted link
1110  'data' => [
1111  'mw' => 'interface',
1112  ],
1113  ];
1114  }
1115  }
1116 
1117  // Avoid PHP 7.1 warning of passing $this by reference
1118  $skinTemplate = $this;
1120  'SkinTemplateNavigation',
1121  [ &$skinTemplate, &$content_navigation ]
1122  );
1123 
1124  if ( $userCanRead && !$wgDisableLangConversion ) {
1125  $pageLang = $title->getPageLanguage();
1126  // Checks that language conversion is enabled and variants exist
1127  // And if it is not in the special namespace
1128  if ( $pageLang->hasVariants() ) {
1129  // Gets list of language variants
1130  $variants = $pageLang->getVariants();
1131  // Gets preferred variant (note that user preference is
1132  // only possible for wiki content language variant)
1133  $preferred = $pageLang->getPreferredVariant();
1134  if ( Action::getActionName( $this ) === 'view' ) {
1135  $params = $request->getQueryValues();
1136  unset( $params['title'] );
1137  } else {
1138  $params = [];
1139  }
1140  // Loops over each variant
1141  foreach ( $variants as $code ) {
1142  // Gets variant name from language code
1143  $varname = $pageLang->getVariantname( $code );
1144  // Appends variant link
1145  $content_navigation['variants'][] = [
1146  'class' => ( $code == $preferred ) ? 'selected' : false,
1147  'text' => $varname,
1148  'href' => $title->getLocalURL( [ 'variant' => $code ] + $params ),
1149  'lang' => LanguageCode::bcp47( $code ),
1150  'hreflang' => LanguageCode::bcp47( $code ),
1151  ];
1152  }
1153  }
1154  }
1155  } else {
1156  // If it's not content, it's got to be a special page
1157  $content_navigation['namespaces']['special'] = [
1158  'class' => 'selected',
1159  'text' => $this->msg( 'nstab-special' )->text(),
1160  'href' => $request->getRequestURL(), // @see: T4457, T4510
1161  'context' => 'subject'
1162  ];
1163 
1164  // Avoid PHP 7.1 warning of passing $this by reference
1165  $skinTemplate = $this;
1166  Hooks::runWithoutAbort( 'SkinTemplateNavigation::SpecialPage',
1167  [ &$skinTemplate, &$content_navigation ] );
1168  }
1169 
1170  // Avoid PHP 7.1 warning of passing $this by reference
1171  $skinTemplate = $this;
1172  // Equiv to SkinTemplateContentActions
1173  Hooks::runWithoutAbort( 'SkinTemplateNavigation::Universal',
1174  [ &$skinTemplate, &$content_navigation ] );
1175 
1176  // Setup xml ids and tooltip info
1177  foreach ( $content_navigation as $section => &$links ) {
1178  foreach ( $links as $key => &$link ) {
1179  $xmlID = $key;
1180  if ( isset( $link['context'] ) && $link['context'] == 'subject' ) {
1181  $xmlID = 'ca-nstab-' . $xmlID;
1182  } elseif ( isset( $link['context'] ) && $link['context'] == 'talk' ) {
1183  $xmlID = 'ca-talk';
1184  $link['rel'] = 'discussion';
1185  } elseif ( $section == 'variants' ) {
1186  $xmlID = 'ca-varlang-' . $xmlID;
1187  } else {
1188  $xmlID = 'ca-' . $xmlID;
1189  }
1190  $link['id'] = $xmlID;
1191  }
1192  }
1193 
1194  # We don't want to give the watch tab an accesskey if the
1195  # page is being edited, because that conflicts with the
1196  # accesskey on the watch checkbox. We also don't want to
1197  # give the edit tab an accesskey, because that's fairly
1198  # superfluous and conflicts with an accesskey (Ctrl-E) often
1199  # used for editing in Safari.
1200  if ( in_array( $action, [ 'edit', 'submit' ] ) ) {
1201  if ( isset( $content_navigation['views']['edit'] ) ) {
1202  $content_navigation['views']['edit']['tooltiponly'] = true;
1203  }
1204  if ( isset( $content_navigation['actions']['watch'] ) ) {
1205  $content_navigation['actions']['watch']['tooltiponly'] = true;
1206  }
1207  if ( isset( $content_navigation['actions']['unwatch'] ) ) {
1208  $content_navigation['actions']['unwatch']['tooltiponly'] = true;
1209  }
1210  }
1211 
1212  return $content_navigation;
1213  }
1214 
1220  private function buildContentActionUrls( $content_navigation ) {
1221  // content_actions has been replaced with content_navigation for backwards
1222  // compatibility and also for skins that just want simple tabs content_actions
1223  // is now built by flattening the content_navigation arrays into one
1224 
1225  $content_actions = [];
1226 
1227  foreach ( $content_navigation as $links ) {
1228  foreach ( $links as $key => $value ) {
1229  if ( isset( $value['redundant'] ) && $value['redundant'] ) {
1230  // Redundant tabs are dropped from content_actions
1231  continue;
1232  }
1233 
1234  // content_actions used to have ids built using the "ca-$key" pattern
1235  // so the xmlID based id is much closer to the actual $key that we want
1236  // for that reason we'll just strip out the ca- if present and use
1237  // the latter potion of the "id" as the $key
1238  if ( isset( $value['id'] ) && substr( $value['id'], 0, 3 ) == 'ca-' ) {
1239  $key = substr( $value['id'], 3 );
1240  }
1241 
1242  if ( isset( $content_actions[$key] ) ) {
1243  wfDebug( __METHOD__ . ": Found a duplicate key for $key while flattening " .
1244  "content_navigation into content_actions.\n" );
1245  continue;
1246  }
1247 
1248  $content_actions[$key] = $value;
1249  }
1250  }
1251 
1252  return $content_actions;
1253  }
1254 
1259  protected function buildNavUrls() {
1260  global $wgUploadNavigationUrl;
1261 
1262  $out = $this->getOutput();
1263  $request = $this->getRequest();
1264 
1265  $nav_urls = [];
1266  $nav_urls['mainpage'] = [ 'href' => self::makeMainPageUrl() ];
1267  if ( $wgUploadNavigationUrl ) {
1268  $nav_urls['upload'] = [ 'href' => $wgUploadNavigationUrl ];
1269  } elseif ( UploadBase::isEnabled() && UploadBase::isAllowed( $this->getUser() ) === true ) {
1270  $nav_urls['upload'] = [ 'href' => self::makeSpecialUrl( 'Upload' ) ];
1271  } else {
1272  $nav_urls['upload'] = false;
1273  }
1274  $nav_urls['specialpages'] = [ 'href' => self::makeSpecialUrl( 'Specialpages' ) ];
1275 
1276  $nav_urls['print'] = false;
1277  $nav_urls['permalink'] = false;
1278  $nav_urls['info'] = false;
1279  $nav_urls['whatlinkshere'] = false;
1280  $nav_urls['recentchangeslinked'] = false;
1281  $nav_urls['contributions'] = false;
1282  $nav_urls['log'] = false;
1283  $nav_urls['blockip'] = false;
1284  $nav_urls['mute'] = false;
1285  $nav_urls['emailuser'] = false;
1286  $nav_urls['userrights'] = false;
1287 
1288  // A print stylesheet is attached to all pages, but nobody ever
1289  // figures that out. :) Add a link...
1290  if ( !$out->isPrintable() && ( $out->isArticle() || $this->getTitle()->isSpecialPage() ) ) {
1291  $nav_urls['print'] = [
1292  'text' => $this->msg( 'printableversion' )->text(),
1293  'href' => $this->getTitle()->getLocalURL(
1294  $request->appendQueryValue( 'printable', 'yes' ) )
1295  ];
1296  }
1297 
1298  if ( $out->isArticle() ) {
1299  // Also add a "permalink" while we're at it
1300  $revid = $this->getOutput()->getRevisionId();
1301  if ( $revid ) {
1302  $nav_urls['permalink'] = [
1303  'text' => $this->msg( 'permalink' )->text(),
1304  'href' => $this->getTitle()->getLocalURL( "oldid=$revid" )
1305  ];
1306  }
1307 
1308  // Avoid PHP 7.1 warning of passing $this by reference
1309  $skinTemplate = $this;
1310  // Use the copy of revision ID in case this undocumented, shady hook tries to mess with internals
1311  Hooks::run( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink',
1312  [ &$skinTemplate, &$nav_urls, &$revid, &$revid ] );
1313  }
1314 
1315  if ( $out->isArticleRelated() ) {
1316  $nav_urls['whatlinkshere'] = [
1317  'href' => SpecialPage::getTitleFor( 'Whatlinkshere', $this->thispage )->getLocalURL()
1318  ];
1319 
1320  $nav_urls['info'] = [
1321  'text' => $this->msg( 'pageinfo-toolboxlink' )->text(),
1322  'href' => $this->getTitle()->getLocalURL( "action=info" )
1323  ];
1324 
1325  if ( $this->getTitle()->exists() || $this->getTitle()->inNamespace( NS_CATEGORY ) ) {
1326  $nav_urls['recentchangeslinked'] = [
1327  'href' => SpecialPage::getTitleFor( 'Recentchangeslinked', $this->thispage )->getLocalURL()
1328  ];
1329  }
1330  }
1331 
1332  $user = $this->getRelevantUser();
1333  if ( $user ) {
1334  $rootUser = $user->getName();
1335 
1336  $nav_urls['contributions'] = [
1337  'text' => $this->msg( 'contributions', $rootUser )->text(),
1338  'href' => self::makeSpecialUrlSubpage( 'Contributions', $rootUser ),
1339  'tooltip-params' => [ $rootUser ],
1340  ];
1341 
1342  $nav_urls['log'] = [
1343  'href' => self::makeSpecialUrlSubpage( 'Log', $rootUser )
1344  ];
1345 
1346  if ( MediawikiServices::getInstance()
1348  ->userHasRight( $this->getUser(), 'block' )
1349  ) {
1350  $nav_urls['blockip'] = [
1351  'text' => $this->msg( 'blockip', $rootUser )->text(),
1352  'href' => self::makeSpecialUrlSubpage( 'Block', $rootUser )
1353  ];
1354  }
1355 
1356  if ( $this->showEmailUser( $user ) ) {
1357  $nav_urls['emailuser'] = [
1358  'text' => $this->msg( 'tool-link-emailuser', $rootUser )->text(),
1359  'href' => self::makeSpecialUrlSubpage( 'Emailuser', $rootUser ),
1360  'tooltip-params' => [ $rootUser ],
1361  ];
1362  }
1363 
1364  if ( !$user->isAnon() ) {
1365  if ( $this->getUser()->isRegistered() && $this->getConfig()->get( 'EnableSpecialMute' ) ) {
1366  $nav_urls['mute'] = [
1367  'text' => $this->msg( 'mute-preferences' )->text(),
1368  'href' => self::makeSpecialUrlSubpage( 'Mute', $rootUser )
1369  ];
1370  }
1371 
1372  $sur = new UserrightsPage;
1373  $sur->setContext( $this->getContext() );
1374  $canChange = $sur->userCanChangeRights( $user );
1375  $nav_urls['userrights'] = [
1376  'text' => $this->msg(
1377  $canChange ? 'tool-link-userrights' : 'tool-link-userrights-readonly',
1378  $rootUser
1379  )->text(),
1380  'href' => self::makeSpecialUrlSubpage( 'Userrights', $rootUser )
1381  ];
1382  }
1383  }
1384 
1385  return $nav_urls;
1386  }
1387 
1392  protected function getNameSpaceKey() {
1393  return $this->getTitle()->getNamespaceKey();
1394  }
1395 }
setContext(IContextSource $context)
makePersonalToolsList( $personalTools=null, $options=[])
Get the HTML for the personal tools list.
$wgShowCreditsIfMax
If there are more than $wgMaxCredits authors, show $wgMaxCredits of them.
showEmailUser( $id)
Definition: Skin.php:1124
makeArticleUrlDetails( $name, $urlaction='')
$wgFooterIcons
Abstract list of footer icons for skins in place of old copyrightico and poweredbyico code You can ad...
lastModified()
Get the timestamp of the latest revision, formatted in user language.
Definition: Skin.php:953
outputPage()
Initialize various variables and generate the template.
$wgScript
The URL path to index.php.
afterContentHook()
This runs a hook to allow extensions placing their stuff after content and article metadata (e...
Definition: Skin.php:651
getPoweredBy()
Gets the powered by MediaWiki icon.
Definition: Skin.php:930
getNewtalks()
Gets new talk page messages for the current user and returns an appropriate alert message (or an empt...
Definition: Skin.php:1473
getLanguages()
Generates array of language links for the current page.
$wgSitename
Name of the site.
wrapHTML( $title, $html)
Wrap the body text with language information and identifiable element.
buildSidebar()
Build an array that represents the sidebar(s), the navigation bar among them.
Definition: Skin.php:1320
$wgMimeType
The default Content-Type header.
static instance()
Singleton.
Definition: Profiler.php:63
privacyLink()
Gets the link to the wiki&#39;s privacy policy page.
Definition: Skin.php:1083
string $template
For QuickTemplate, the name of the subclass which will actually fill the template.
static factory( $action, Page $page, IContextSource $context=null)
Get an appropriate Action subclass for the given action.
Definition: Action.php:97
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
initPage(OutputPage $out)
Definition: Skin.php:165
editUrlOptions()
Return URL options for the &#39;edit page&#39; link.
Definition: Skin.php:1110
buildContentNavigationUrls()
a structured array of links usually used for the tabs in a skin
New base template for a skin&#39;s template extended from QuickTemplate this class features helper method...
const NS_SPECIAL
Definition: Defines.php:49
getCopyrightIcon()
Definition: Skin.php:900
$wgArticlePath
Definition: img_auth.php:47
getCategories()
Definition: Skin.php:616
getSiteNotice()
Get the site notice.
Definition: Skin.php:1616
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e.g.
canUseWikiPage()
Check whether a WikiPage object can be get with getWikiPage().
getPermissionManager()
$wgStylePath
The URL path of the skins directory.
wfReportTime( $nonce=null)
Returns a script tag that stores the amount of time it took MediaWiki to handle the request in millis...
setContext( $context)
Sets the context this SpecialPage is executed in.
getStructuredPersonalTools()
Get personal tools for the user.
setupTemplate( $classname)
Create the template engine object; we feed it a bunch of data and eventually it spits out some HTML...
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
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
useCombinedLoginLink()
Output a boolean indicating if buildPersonalUrls should output separate login and create account link...
Base class for template-based skins.
getRelevantTitle()
Return the "relevant" title.
Definition: Skin.php:349
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:774
disclaimerLink()
Gets the link to the wiki&#39;s general disclaimers page.
Definition: Skin.php:1099
wfMessageFallback(... $keys)
This function accepts multiple message keys and returns a message instance for the first message whic...
getContext()
Get the base IContextSource object.
getSearchLink()
Definition: Skin.php:833
const NS_CATEGORY
Definition: Defines.php:74
generateDebugHTML()
Generate debug data HTML for displaying at the bottom of the main content area.
Definition: Skin.php:677
static runWithoutAbort( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:231
getRelevantUser()
Return the "relevant" user.
Definition: Skin.php:370
static getActionName(IContextSource $context)
Get the action that will be executed, not necessarily the one passed passed through the "action" requ...
Definition: Action.php:123
aboutLink()
Gets the link to the wiki&#39;s about page.
Definition: Skin.php:1091
const NS_FILE
Definition: Defines.php:66
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:83
logoText( $align='')
Definition: Skin.php:981
$wgDisableLangConversion
Whether to enable language variant conversion.
const NS_MEDIAWIKI
Definition: Defines.php:68
static isEnabled()
Returns true if uploads are enabled.
Definition: UploadBase.php:135
static fetchLanguageName( $code, $inLanguage=self::AS_AUTONYMS, $include=self::ALL)
Definition: Language.php:828
buildContentActionUrls( $content_navigation)
an array of edit links by default used for the tabs
printSource()
Text with the permalink to the source page, usually shown on the footer of a printed page...
Definition: Skin.php:708
tabAction( $title, $message, $selected, $query='', $checkEdit=false)
Builds an array with tab definition.
$wgHideInterlanguageLinks
Hide interlanguage links from the sidebar.
buildPersonalUrls()
build array of urls for personal toolbar
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
$wgScriptPath
The path we should point to.
string $skinname
Name of our skin, it probably needs to be all lower case.
getNameSpaceKey()
Generate strings used for xml &#39;id&#39; names.
$wgMaxCredits
Set this to the number of authors that you want to be credited below an article text.
getCopyright( $type='detect')
Definition: Skin.php:851
prepareQuickTemplate()
initialize various variables and generate the template
$wgLogo
The URL path of the wiki logo.
Special page to allow managing user group membership.
formatLanguageName( $name)
Format language name for use in sidebar interlanguage links list.
getUndeleteLink()
Definition: Skin.php:726
$wgServer
URL of the server.
makeTalkUrlDetails( $name, $urlaction='')
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
$wgUseCombinedLoginLink
Login / create account link behavior when it&#39;s possible for anonymous users to create an account...
$wgUploadNavigationUrl
Point the upload navigation link to an external URL Useful if you want to use a shared repository by ...
getWikiPage()
Get the WikiPage object.
bottomScripts()
This gets called shortly before the "</body>" tag.
Definition: Skin.php:686
return true
Definition: router.php:92
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
printOrError( $str)
Output the string, or print error message if it&#39;s an error object of the appropriate type...
buildNavUrls()
build array of common navigation links
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:319
getPersonalToolsList()
Get the HTML for the p-personal list.