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