MediaWiki  master
SkinTemplate.php
Go to the documentation of this file.
1 <?php
23 
40 class SkinTemplate extends Skin {
45  public $template;
46 
47  public $thispage;
48  public $titletxt;
49  public $userpage;
50  public $thisquery;
51  // TODO: Rename this to $isRegistered (but that's a breaking change)
52  public $loggedin;
53  public $username;
55 
64  protected function setupTemplate( $classname ) {
65  return new $classname( $this->getConfig() );
66  }
67 
71  protected function setupTemplateForOutput() {
72  $this->setupTemplateContext();
73  $template = $this->options['template'] ?? $this->template;
74  if ( !$template ) {
75  throw new RuntimeException(
76  'SkinTemplate skins must define a `template` either as a public'
77  . ' property of by passing in a`template` option to the constructor.'
78  );
79  }
80  $tpl = $this->setupTemplate( $template );
81  return $tpl;
82  }
83 
93  final protected function setupTemplateContext() {
94  $request = $this->getRequest();
95  $user = $this->getUser();
96  $title = $this->getTitle();
97 
98  $this->thispage = $title->getPrefixedDBkey();
99  $this->titletxt = $title->getPrefixedText();
100  $this->userpage = $user->getUserPage()->getPrefixedText();
101  $query = [];
102  if ( !$request->wasPosted() ) {
103  $query = $request->getValues();
104  unset( $query['title'] );
105  unset( $query['returnto'] );
106  unset( $query['returntoquery'] );
107  }
108  $this->thisquery = wfArrayToCgi( $query );
109  $this->loggedin = $user->isRegistered();
110  $this->username = $user->getName();
111 
112  if ( $this->loggedin ) {
113  $this->userpageUrlDetails = self::makeUrlDetails( $this->userpage );
114  } else {
115  # This won't be used in the standard skins, but we define it to preserve the interface
116  # To save time, we check for existence
117  $this->userpageUrlDetails = self::makeKnownUrlDetails( $this->userpage );
118  }
119  }
120 
129  public function generateHTML() {
130  $tpl = $this->prepareQuickTemplate();
131  // execute template
132  return $tpl->execute();
133  }
134 
139  public function outputPage() {
140  Profiler::instance()->setAllowOutput();
141  $out = $this->getOutput();
142 
143  $this->initPage( $out );
144  $out->addJsConfigVars( $this->getJsConfigVars() );
145 
146  // result may be an error
147  echo $this->generateHTML();
148  }
149 
157  protected function getJsConfigVars(): array {
158  return [];
159  }
160 
168  protected function wrapHTML( $title, $html ) {
169  # An ID that includes the actual body text; without categories, contentSub, ...
170  $realBodyAttribs = [
171  'id' => 'mw-content-text',
172  'class' => [
173  'mw-body-content',
174  ],
175  ];
176 
177  # Add a mw-content-ltr/rtl class to be able to style based on text
178  # direction when the content is different from the UI language (only
179  # when viewing)
180  # Most information on special pages and file pages is in user language,
181  # rather than content language, so those will not get this
182  if ( Action::getActionName( $this ) === 'view' &&
183  ( !$title->inNamespaces( NS_SPECIAL, NS_FILE ) || $title->isRedirect() ) ) {
184  $pageLang = $title->getPageViewLanguage();
185  $realBodyAttribs['lang'] = $pageLang->getHtmlCode();
186  $realBodyAttribs['dir'] = $pageLang->getDir();
187  $realBodyAttribs['class'][] = 'mw-content-' . $pageLang->getDir();
188  }
189 
190  return Html::rawElement( 'div', $realBodyAttribs, $html );
191  }
192 
198  final protected function prepareUserLanguageAttributes() {
199  $userLang = $this->getLanguage();
200  $userLangCode = $userLang->getHtmlCode();
201  $userLangDir = $userLang->getDir();
202  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
203  if (
204  $userLangCode !== $contLang->getHtmlCode() ||
205  $userLangDir !== $contLang->getDir()
206  ) {
207  $escUserlang = htmlspecialchars( $userLangCode );
208  $escUserdir = htmlspecialchars( $userLangDir );
209  // Attributes must be in double quotes because htmlspecialchars() doesn't
210  // escape single quotes
211  return " lang=\"$escUserlang\" dir=\"$escUserdir\"";
212  }
213  return '';
214  }
215 
221  protected function getFooterIcons() {
222  $config = $this->getConfig();
223 
224  $footericons = [];
225  foreach ( $config->get( 'FooterIcons' ) as $footerIconsKey => &$footerIconsBlock ) {
226  if ( count( $footerIconsBlock ) > 0 ) {
227  $footericons[$footerIconsKey] = [];
228  foreach ( $footerIconsBlock as &$footerIcon ) {
229  if ( isset( $footerIcon['src'] ) ) {
230  if ( !isset( $footerIcon['width'] ) ) {
231  $footerIcon['width'] = 88;
232  }
233  if ( !isset( $footerIcon['height'] ) ) {
234  $footerIcon['height'] = 31;
235  }
236  }
237 
238  // Only output icons which have an image.
239  // For historic reasons this mimics the `icononly` option
240  // for BaseTemplate::getFooterIcons.
241  // In some cases the icon may be an empty array.
242  // Filter these out. (See T269776)
243  if ( is_string( $footerIcon ) || isset( $footerIcon['src'] ) ) {
244  $footericons[$footerIconsKey][] = $footerIcon;
245  }
246  }
247 
248  // If no valid icons with images were added, unset the parent array
249  // Should also prevent empty arrays from when no copyright is set.
250  if ( !count( $footericons[$footerIconsKey] ) ) {
251  unset( $footericons[$footerIconsKey] );
252  }
253  }
254  }
255  return $footericons;
256  }
257 
263  final protected function prepareUndeleteLink() {
264  $undelete = $this->getUndeleteLink();
265  return $undelete === '' ? null : '<span class="subpages">' . $undelete . '</span>';
266  }
267 
274  protected function prepareQuickTemplate() {
275  $title = $this->getTitle();
276  $request = $this->getRequest();
277  $out = $this->getOutput();
278  $config = $this->getConfig();
279  $tpl = $this->setupTemplateForOutput();
280 
281  $tpl->set( 'title', $out->getPageTitle() );
282  $tpl->set( 'pagetitle', $out->getHTMLTitle() );
283  $tpl->set( 'displaytitle', $out->mPageLinkTitle );
284 
285  $tpl->set( 'thispage', $this->thispage );
286  $tpl->set( 'titleprefixeddbkey', $this->thispage );
287  $tpl->set( 'titletext', $title->getText() );
288  $tpl->set( 'articleid', $title->getArticleID() );
289 
290  $tpl->set( 'isarticle', $out->isArticle() );
291 
292  $tpl->set( 'subtitle', $this->prepareSubtitle() );
293  $tpl->set( 'undelete', $this->prepareUndeleteLink() );
294 
295  $tpl->set( 'catlinks', $this->getCategories() );
296  $feeds = $this->buildFeedUrls();
297  $tpl->set( 'feeds', count( $feeds ) ? $feeds : false );
298 
299  $tpl->set( 'mimetype', $config->get( 'MimeType' ) );
300  $tpl->set( 'charset', 'UTF-8' );
301  $tpl->set( 'wgScript', $config->get( 'Script' ) );
302  $tpl->set( 'skinname', $this->skinname );
303  $tpl->set( 'skinclass', static::class );
304  $tpl->set( 'skin', $this );
305  $tpl->set( 'stylename', $this->stylename );
306  $tpl->set( 'printable', $out->isPrintable() );
307  $tpl->set( 'handheld', $request->getBool( 'handheld' ) );
308  $tpl->set( 'loggedin', $this->loggedin );
309  $tpl->set( 'notspecialpage', !$title->isSpecialPage() );
310 
311  // Deprecated since 1.36
312  $searchLink = $this->getSearchPageTitle()->getLocalURL();
313  $tpl->set( 'searchaction', $searchLink );
314 
315  $tpl->set( 'searchtitle', $this->getSearchPageTitle()->getPrefixedDBkey() );
316  $tpl->set( 'search', trim( $request->getVal( 'search' ) ) );
317  $tpl->set( 'stylepath', $config->get( 'StylePath' ) );
318  $tpl->set( 'articlepath', $config->get( 'ArticlePath' ) );
319  $tpl->set( 'scriptpath', $config->get( 'ScriptPath' ) );
320  $tpl->set( 'serverurl', $config->get( 'Server' ) );
322  $tpl->set( 'logopath', $logos['1x'] );
323  $tpl->set( 'sitename', $config->get( 'Sitename' ) );
324 
325  $userLang = $this->getLanguage();
326  $userLangCode = $userLang->getHtmlCode();
327  $userLangDir = $userLang->getDir();
328 
329  $tpl->set( 'lang', $userLangCode );
330  $tpl->set( 'dir', $userLangDir );
331  $tpl->set( 'rtl', $userLang->isRTL() );
332 
333  $tpl->set( 'showjumplinks', true ); // showjumplinks preference has been removed
334  $tpl->set( 'username', $this->loggedin ? $this->username : null );
335  $tpl->set( 'userpage', $this->userpage );
336  $tpl->set( 'userpageurl', $this->userpageUrlDetails['href'] );
337  $tpl->set( 'userlang', $userLangCode );
338 
339  // Users can have their language set differently than the
340  // content of the wiki. For these users, tell the web browser
341  // that interface elements are in a different language.
342  $tpl->set( 'userlangattributes', $this->prepareUserLanguageAttributes() );
343  $tpl->set( 'specialpageattributes', '' ); # obsolete
344  // Used by VectorBeta to insert HTML before content but after the
345  // heading for the page title. Defaults to empty string.
346  $tpl->set( 'prebodyhtml', '' );
347 
348  $tpl->set( 'newtalk', $this->getNewtalks() );
349  $tpl->set( 'logo', $this->logoText() );
350 
351  $footerData = $this->getFooterLinks();
352  $tpl->set( 'copyright', $footerData['info']['copyright'] ?? false );
353  // No longer used
354  $tpl->set( 'viewcount', false );
355  $tpl->set( 'lastmod', $footerData['info']['lastmod'] ?? false );
356  $tpl->set( 'credits', $footerData['info']['credits'] ?? false );
357  $tpl->set( 'numberofwatchingusers', false );
358 
359  $tpl->set( 'copyrightico', $this->getCopyrightIcon() );
360  $tpl->set( 'poweredbyico', $this->getPoweredBy() );
361 
362  $tpl->set( 'disclaimer', $footerData['places']['disclaimer'] ?? false );
363  $tpl->set( 'privacy', $footerData['places']['privacy'] ?? false );
364  $tpl->set( 'about', $footerData['places']['about'] ?? false );
365 
366  // Flatten for compat with the 'footerlinks' key in QuickTemplate-based skins.
367  $flattenedfooterlinks = [];
368  foreach ( $footerData as $category => $links ) {
369  $flattenedfooterlinks[$category] = array_keys( $links );
370  foreach ( $links as $key => $value ) {
371  // For full support with BaseTemplate we also need to
372  // copy over the keys.
373  $tpl->set( $key, $value );
374  }
375  }
376  $tpl->set( 'footerlinks', $flattenedfooterlinks );
377  $tpl->set( 'footericons', $this->getFooterIcons() );
378 
379  $tpl->set( 'indicators', $out->getIndicators() );
380 
381  $tpl->set( 'sitenotice', $this->getSiteNotice() );
382  $tpl->set( 'printfooter', $this->printSource() );
383  // Wrap the bodyText with #mw-content-text element
384  $tpl->set( 'bodytext', $this->wrapHTML( $title, $out->getHTML() ) );
385 
386  $tpl->set( 'language_urls', $this->getLanguages() ?: false );
387 
388  $content_navigation = $this->buildContentNavigationUrls();
389  # Personal toolbar
390  $tpl->set( 'personal_urls', $this->makeSkinTemplatePersonalUrls( $content_navigation ) );
391  // The user-menu, notifications, and user-interface-preferences are new content navigation entries which aren't
392  // expected to be part of content_navigation or content_actions. Adding them in there breaks skins that do not
393  // expect it.
394  unset(
395  $content_navigation['user-menu'],
396  $content_navigation['notifications'],
397  $content_navigation['user-page'],
398  $content_navigation['user-interface-preferences']
399  );
400  $content_actions = $this->buildContentActionUrls( $content_navigation );
401  $tpl->set( 'content_navigation', $content_navigation );
402  $tpl->set( 'content_actions', $content_actions );
403 
404  $tpl->set( 'sidebar', $this->buildSidebar() );
405  $tpl->set( 'nav_urls', $this->buildNavUrls() );
406 
407  // Do this last in case hooks above add bottom scripts
408  $tpl->set( 'bottomscripts', $this->bottomScripts() );
409 
410  // Set the head scripts near the end, in case the above actions resulted in added scripts
411  $tpl->set( 'headelement', $out->headElement( $this ) );
412 
413  $tpl->set( 'debug', '' );
414  $tpl->set( 'debughtml', MWDebug::getHTMLDebugLog() );
415  $tpl->set( 'reporttime', wfReportTime( $out->getCSP()->getNonce() ) );
416 
417  // Set the bodytext to another key so that skins can just output it on its own
418  // and output printfooter and debughtml separately
419  $tpl->set( 'bodycontent', $tpl->data['bodytext'] );
420 
421  // Append printfooter and debughtml onto bodytext so that skins that
422  // were already using bodytext before they were split out don't suddenly
423  // start not outputting information.
424  $tpl->data['bodytext'] .= Html::rawElement(
425  'div',
426  [ 'class' => 'printfooter' ],
427  "\n{$tpl->data['printfooter']}"
428  ) . "\n";
429  $tpl->data['bodytext'] .= $tpl->data['debughtml'];
430 
431  // allow extensions adding stuff after the page content.
432  // See Skin::afterContentHook() for further documentation.
433  $tpl->set( 'dataAfterContent', $this->afterContentHook() );
434 
435  return $tpl;
436  }
437 
443  public function getPersonalToolsList() {
444  return $this->makePersonalToolsList();
445  }
446 
457  public function makePersonalToolsList( $personalTools = null, $options = [] ) {
458  $this->setupTemplateContext();
459  $html = '';
460 
461  if ( $personalTools === null ) {
462  $personalTools = $this->getPersonalToolsForMakeListItem(
463  $this->buildPersonalUrls()
464  );
465  }
466 
467  foreach ( $personalTools as $key => $item ) {
468  $html .= $this->makeListItem( $key, $item, $options );
469  }
470 
471  return $html;
472  }
473 
481  public function getStructuredPersonalTools() {
482  // buildPersonalUrls requires the template context.
483  $this->setupTemplateContext();
484  return $this->getPersonalToolsForMakeListItem(
485  $this->buildPersonalUrls()
486  );
487  }
488 
496  protected function buildPersonalUrls( bool $includeNotifications = true ) {
497  $title = $this->getTitle();
498  $request = $this->getRequest();
499  $pageurl = $title->getLocalURL();
500  $services = MediaWikiServices::getInstance();
501  $authManager = $services->getAuthManager();
502  $permissionManager = $services->getPermissionManager();
503  $returnto = $this->getReturnToParam();
504 
505  /* set up the default links for the personal toolbar */
506  $personal_urls = [];
507 
508  if ( $this->loggedin ) {
509  $personal_urls['userpage'] = $this->buildPersonalPageItem( 'pt-userpage' );
510 
511  // Merge notifications into the personal menu for older skins.
512  if ( $includeNotifications ) {
513  $contentNavigation = $this->buildContentNavigationUrls();
514 
515  $personal_urls += $contentNavigation['notifications'];
516  }
517 
518  $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
519  $personal_urls['mytalk'] = [
520  'text' => $this->msg( 'mytalk' )->text(),
521  'href' => &$usertalkUrlDetails['href'],
522  'class' => $usertalkUrlDetails['exists'] ? false : 'new',
523  'exists' => $usertalkUrlDetails['exists'],
524  'active' => ( $usertalkUrlDetails['href'] == $pageurl ),
525  'icon' => 'userTalk'
526  ];
527  $href = self::makeSpecialUrl( 'Preferences' );
528  $personal_urls['preferences'] = [
529  'text' => $this->msg( 'mypreferences' )->text(),
530  'href' => $href,
531  'active' => ( $href == $pageurl ),
532  'icon' => 'settings'
533  ];
534 
535  if ( $this->getAuthority()->isAllowed( 'viewmywatchlist' ) ) {
536  $href = self::makeSpecialUrl( 'Watchlist' );
537  $personal_urls['watchlist'] = [
538  'text' => $this->msg( 'mywatchlist' )->text(),
539  'href' => $href,
540  'active' => ( $href == $pageurl ),
541  'icon' => 'unStar'
542  ];
543  }
544 
545  # We need to do an explicit check for Special:Contributions, as we
546  # have to match both the title, and the target, which could come
547  # from request values (Special:Contributions?target=Jimbo_Wales)
548  # or be specified in "sub page" form
549  # (Special:Contributions/Jimbo_Wales). The plot
550  # thickens, because the Title object is altered for special pages,
551  # so it doesn't contain the original alias-with-subpage.
552  $origTitle = Title::newFromText( $request->getText( 'title' ) );
553  if ( $origTitle instanceof Title && $origTitle->isSpecialPage() ) {
554  list( $spName, $spPar ) =
555  MediaWikiServices::getInstance()->getSpecialPageFactory()->
556  resolveAlias( $origTitle->getText() );
557  $active = $spName == 'Contributions'
558  && ( ( $spPar && $spPar == $this->username )
559  || $request->getText( 'target' ) == $this->username );
560  } else {
561  $active = false;
562  }
563 
564  $href = self::makeSpecialUrlSubpage( 'Contributions', $this->username );
565  $personal_urls['mycontris'] = [
566  'text' => $this->msg( 'mycontris' )->text(),
567  'href' => $href,
568  'active' => $active,
569  'icon' => 'userContributions'
570  ];
571 
572  // if we can't set the user, we can't unset it either
573  if ( $request->getSession()->canSetUser() ) {
574  $personal_urls['logout'] = $this->buildLogoutLinkData();
575  }
576  } else {
577  $useCombinedLoginLink = $this->useCombinedLoginLink();
578  $login_url = $this->buildLoginData( $returnto, $useCombinedLoginLink );
579  $createaccount_url = $this->buildCreateAccountData( $returnto );
580 
581  // No need to show Talk and Contributions to anons if they can't contribute!
582  // TODO: easy way to get anon authority!
583  if ( $permissionManager->groupHasPermission( '*', 'edit' ) ) {
584  // Non interactive placeholder for anonymous users.
585  // It's unstyled by default (black color). Skin that
586  // needs it, can style it using the 'pt-anonuserpage' id.
587  // Skin that does not need it should unset it.
588  $personal_urls['anonuserpage'] = [
589  'text' => $this->msg( 'notloggedin' )->text(),
590  ];
591 
592  // Because of caching, we can't link directly to the IP talk and
593  // contributions pages. Instead we use the special page shortcuts
594  // (which work correctly regardless of caching). This means we can't
595  // determine whether these links are active or not, but since major
596  // skins (MonoBook, Vector) don't use this information, it's not a
597  // huge loss.
598  $personal_urls['anontalk'] = [
599  'text' => $this->msg( 'anontalk' )->text(),
600  'href' => self::makeSpecialUrlSubpage( 'Mytalk', false ),
601  'active' => false,
602  'icon' => 'userTalk'
603  ];
604  $personal_urls['anoncontribs'] = [
605  'text' => $this->msg( 'anoncontribs' )->text(),
606  'href' => self::makeSpecialUrlSubpage( 'Mycontributions', false ),
607  'active' => false,
608  'icon' => 'userContributions'
609  ];
610  }
611 
612  if (
613  $authManager->canCreateAccounts()
614  && $this->getAuthority()->isAllowed( 'createaccount' )
615  && !$useCombinedLoginLink
616  ) {
617  $personal_urls['createaccount'] = $createaccount_url;
618  }
619 
620  if ( $authManager->canAuthenticateNow() ) {
621  // TODO: easy way to get anon authority
622  $key = $permissionManager->groupHasPermission( '*', 'read' )
623  ? 'login'
624  : 'login-private';
625  $personal_urls[$key] = $login_url;
626  }
627  }
628 
629  $this->getHookRunner()->onPersonalUrls( $personal_urls, $title, $this );
630 
631  return $personal_urls;
632  }
633 
640  protected function getReturnToParam() {
641  $title = $this->getTitle();
642  $request = $this->getRequest();
643 
644  # Due to T34276, if a user does not have read permissions,
645  # $this->getTitle() will just give Special:Badtitle, which is
646  # not especially useful as a returnto parameter. Use the title
647  # from the request instead, if there was one.
648  if ( $this->getAuthority()->isAllowed( 'read' ) ) {
649  $page = $title;
650  } else {
651  $page = Title::newFromText( $request->getVal( 'title', '' ) );
652  }
653  $page = $request->getVal( 'returnto', $page );
654  $returnto = [];
655  if ( strval( $page ) !== '' ) {
656  $returnto['returnto'] = $page;
657  $query = $request->getVal( 'returntoquery', $this->thisquery );
658  $paramsArray = wfCgiToArray( $query );
659  $query = wfArrayToCgi( $paramsArray );
660  if ( $query != '' ) {
661  $returnto['returntoquery'] = $query;
662  }
663  }
664 
665  return $returnto;
666  }
667 
674  protected function useCombinedLoginLink() {
675  $services = MediaWikiServices::getInstance();
676  $authManager = $services->getAuthManager();
677  $useCombinedLoginLink = $this->getConfig()->get( 'UseCombinedLoginLink' );
678  if ( !$authManager->canCreateAccounts() || !$authManager->canAuthenticateNow() ) {
679  // don't show combined login/signup link if one of those is actually not available
680  $useCombinedLoginLink = false;
681  }
682 
683  return $useCombinedLoginLink;
684  }
685 
695  protected function buildLoginData( $returnto, $useCombinedLoginLink ) {
696  $title = $this->getTitle();
697 
698  $loginlink = $this->getAuthority()->isAllowed( 'createaccount' )
699  && $useCombinedLoginLink ? 'nav-login-createaccount' : 'pt-login';
700 
701  $login_url = [
702  'text' => $this->msg( $loginlink )->text(),
703  'href' => self::makeSpecialUrl( 'Userlogin', $returnto ),
704  'active' => $title->isSpecial( 'Userlogin' )
705  || $title->isSpecial( 'CreateAccount' ) && $useCombinedLoginLink,
706  'icon' => 'logIn'
707  ];
708 
709  return $login_url;
710  }
711 
721  final protected function buildLogoutLinkData() {
722  $title = $this->getTitle();
723  $returnto = $this->getReturnToParam();
724 
725  return [
726  'text' => $this->msg( 'pt-userlogout' )->text(),
727  'data-mw' => 'interface',
728  'href' => self::makeSpecialUrl( 'Userlogout',
729  // Note: userlogout link must always contain an & character, otherwise we might not be able
730  // to detect a buggy precaching proxy (T19790)
731  ( $title->isSpecial( 'Preferences' ) ? [] : $returnto ) ),
732  'active' => false,
733  'icon' => 'logOut'
734  ];
735  }
736 
744  protected function buildCreateAccountData( $returnto ) {
745  $title = $this->getTitle();
746 
747  $createaccount_url = [
748  'text' => $this->msg( 'pt-createaccount' )->text(),
749  'href' => self::makeSpecialUrl( 'CreateAccount', $returnto ),
750  'active' => $title->isSpecial( 'CreateAccount' ),
751  'icon' => 'userAvatar'
752  ];
753 
754  return $createaccount_url;
755  }
756 
762  protected function buildPersonalPageItem( $id = 'pt-userpage' ): array {
763  // Build the personal page link array.
764  return [
765  'text' => $this->username,
766  'id' => $id,
767  'href' => &$this->userpageUrlDetails['href'],
768  'link-class' => $this->userpageUrlDetails['exists'] ? [] : [ 'new' ],
769  'exists' => $this->userpageUrlDetails['exists'],
770  'active' => ( $this->userpageUrlDetails['href'] == $this->getTitle()->getLocalURL() ),
771  'icon' => 'userAvatar',
772  'dir' => 'auto'
773  ];
774  }
775 
789  public function tabAction( $title, $message, $selected, $query = '', $checkEdit = false ) {
790  $classes = [];
791  if ( $selected ) {
792  $classes[] = 'selected';
793  }
794  $exists = true;
795  if ( $checkEdit && !$title->isKnown() ) {
796  $classes[] = 'new';
797  $exists = false;
798  if ( $query !== '' ) {
799  $query = 'action=edit&redlink=1&' . $query;
800  } else {
801  $query = 'action=edit&redlink=1';
802  }
803  }
804 
805  $services = MediaWikiServices::getInstance();
806  $linkClass = $services->getLinkRenderer()->getLinkClasses( $title );
807 
808  if ( $message instanceof MessageSpecifier ) {
809  $msg = new Message( $message );
810  $message = $message->getKey();
811  } else {
812  // wfMessageFallback will nicely accept $message as an array of fallbacks
813  // or just a single key
814  $msg = wfMessageFallback( $message );
815  if ( is_array( $message ) ) {
816  // for hook compatibility just keep the last message name
817  $message = end( $message );
818  }
819  }
820  $msg->setContext( $this->getContext() );
821  if ( $msg->exists() ) {
822  $text = $msg->text();
823  } else {
824  $text = $services->getLanguageConverterFactory()
825  ->getLanguageConverter( $services->getContentLanguage() )
826  ->convertNamespace(
827  $services->getNamespaceInfo()
828  ->getSubject( $title->getNamespace() )
829  );
830  }
831 
832  $result = [
833  'class' => implode( ' ', $classes ),
834  'text' => $text,
835  'href' => $title->getLocalURL( $query ),
836  'exists' => $exists,
837  'primary' => true ];
838  if ( $linkClass !== '' ) {
839  $result['link-class'] = $linkClass;
840  }
841 
842  return $result;
843  }
844 
850  private function makeTalkUrlDetails( $name, $urlaction = '' ) {
851  $title = Title::newFromText( $name );
852  if ( !is_object( $title ) ) {
853  throw new MWException( __METHOD__ . " given invalid pagename $name" );
854  }
855  $title = $title->getTalkPage();
856  self::checkTitle( $title, $name );
857  return [
858  'href' => $title->getLocalURL( $urlaction ),
859  'exists' => $title->isKnown(),
860  ];
861  }
862 
872  private function getWatchLinkAttrs(
873  string $mode, Authority $performer, Title $title, ?string $action, bool $onPage
874  ): array {
875  $class = 'mw-watchlink ' . (
876  $onPage && ( $action == 'watch' || $action == 'unwatch' ) ? 'selected' : ''
877  );
878 
879  // Add class identifying the page is temporarily watched, if applicable.
880  if ( $this->getConfig()->get( 'WatchlistExpiry' ) &&
881  MediaWikiServices::getInstance()->getWatchlistManager()->isTempWatched( $performer, $title )
882  ) {
883  $class .= ' mw-watchlink-temp';
884  }
885 
886  return [
887  'class' => $class,
888  // uses 'watch' or 'unwatch' message
889  'text' => $this->msg( $mode )->text(),
890  'href' => $title->getLocalURL( [ 'action' => $mode ] ),
891  // Set a data-mw=interface attribute, which the mediawiki.page.ajax
892  // module will look for to make sure it's a trusted link
893  'data' => [
894  'mw' => 'interface',
895  ],
896  ];
897  }
898 
908  protected function runOnSkinTemplateNavigationHooks( SkinTemplate $skin, &$content_navigation ) {
909  $title = $this->getRelevantTitle();
910  if ( $title->canExist() ) {
911  $this->getHookRunner()->onSkinTemplateNavigation( $skin, $content_navigation );
912  } else {
913  $this->getHookRunner()->onSkinTemplateNavigation__SpecialPage(
914  $skin, $content_navigation );
915  }
916 
917  // Equiv to SkinTemplateContentActions, run
918  $this->getHookRunner()->onSkinTemplateNavigation__Universal(
919  $skin, $content_navigation );
920  }
921 
956  protected function buildContentNavigationUrls() {
957  // Display tabs for the relevant title rather than always the title itself
958  $title = $this->getRelevantTitle();
959  $onPage = $title->equals( $this->getTitle() );
960 
961  $out = $this->getOutput();
962  $request = $this->getRequest();
963  $performer = $this->getAuthority();
964  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
965 
966  $content_navigation = [
967  // Modern keys: Please ensure these get unset inside Skin::prepareQuickTemplate
968  'user-interface-preferences' => [],
969  'user-page' => $this->loggedin ? [
970  'userpage' => $this->buildPersonalPageItem( 'pt-userpage-2' )
971  ] : [],
972  'user-menu' => $this->buildPersonalUrls( false ),
973  'notifications' => [],
974  // Legacy keys
975  'namespaces' => [],
976  'views' => [],
977  'actions' => [],
978  'variants' => []
979  ];
980 
981  // parameters
982  $action = $request->getVal( 'action', 'view' );
983 
984  $userCanRead = $this->getAuthority()->probablyCan( 'read', $title );
985 
986  // Checks if page is some kind of content
987  if ( $title->canExist() ) {
988  // Gets page objects for the related namespaces
989  $subjectPage = $title->getSubjectPage();
990  $talkPage = $title->getTalkPage();
991 
992  // Determines if this is a talk page
993  $isTalk = $title->isTalkPage();
994 
995  // Generates XML IDs from namespace names
996  $subjectId = $title->getNamespaceKey( '' );
997 
998  if ( $subjectId == 'main' ) {
999  $talkId = 'talk';
1000  } else {
1001  $talkId = "{$subjectId}_talk";
1002  }
1003 
1004  $skname = $this->skinname;
1005 
1006  // Adds namespace links
1007  if ( $subjectId === 'user' ) {
1008  $subjectMsg = wfMessage( 'nstab-user', $subjectPage->getRootText() );
1009  } else {
1010  $subjectMsg = [ "nstab-$subjectId" ];
1011  }
1012  if ( $subjectPage->isMainPage() ) {
1013  array_unshift( $subjectMsg, 'mainpage-nstab' );
1014  }
1015 
1016  $content_navigation['namespaces'][$subjectId] = $this->tabAction(
1017  $subjectPage, $subjectMsg, !$isTalk, '', $userCanRead
1018  );
1019  $content_navigation['namespaces'][$subjectId]['context'] = 'subject';
1020  $content_navigation['namespaces'][$talkId] = $this->tabAction(
1021  $talkPage, [ "nstab-$talkId", 'talk' ], $isTalk, '', $userCanRead
1022  );
1023  $content_navigation['namespaces'][$talkId]['context'] = 'talk';
1024 
1025  if ( $userCanRead ) {
1026  // Adds "view" view link
1027  if ( $title->isKnown() ) {
1028  $content_navigation['views']['view'] = $this->tabAction(
1029  $isTalk ? $talkPage : $subjectPage,
1030  [ "$skname-view-view", 'view' ],
1031  ( $onPage && ( $action == 'view' || $action == 'purge' ) ), '', true
1032  );
1033  // signal to hide this from simple content_actions
1034  $content_navigation['views']['view']['redundant'] = true;
1035  }
1036 
1037  $page = $this->canUseWikiPage() ? $this->getWikiPage() : false;
1038  $isRemoteContent = $page && !$page->isLocal();
1039 
1040  // If it is a non-local file, show a link to the file in its own repository
1041  // @todo abstract this for remote content that isn't a file
1042  if ( $isRemoteContent ) {
1043  $content_navigation['views']['view-foreign'] = [
1044  'class' => '',
1045  'text' => wfMessageFallback( "$skname-view-foreign", 'view-foreign' )->
1046  setContext( $this->getContext() )->
1047  params( $page->getWikiDisplayName() )->text(),
1048  'href' => $page->getSourceURL(),
1049  'primary' => false,
1050  ];
1051  }
1052 
1053  // Checks if user can edit the current page if it exists or create it otherwise
1054  if ( $this->getAuthority()->probablyCan( 'edit', $title ) ) {
1055  // Builds CSS class for talk page links
1056  $isTalkClass = $isTalk ? ' istalk' : '';
1057  // Whether the user is editing the page
1058  $isEditing = $onPage && ( $action == 'edit' || $action == 'submit' );
1059  // Whether to show the "Add a new section" tab
1060  // Checks if this is a current rev of talk page and is not forced to be hidden
1061  $showNewSection = !$out->forceHideNewSectionLink()
1062  && ( ( $isTalk && $out->isRevisionCurrent() ) || $out->showNewSectionLink() );
1063  $section = $request->getVal( 'section' );
1064 
1065  if ( $title->exists()
1066  || ( $title->inNamespace( NS_MEDIAWIKI )
1067  && $title->getDefaultMessageText() !== false
1068  )
1069  ) {
1070  $msgKey = $isRemoteContent ? 'edit-local' : 'edit';
1071  } else {
1072  $msgKey = $isRemoteContent ? 'create-local' : 'create';
1073  }
1074  $content_navigation['views']['edit'] = [
1075  'class' => ( $isEditing && ( $section !== 'new' || !$showNewSection )
1076  ? 'selected'
1077  : ''
1078  ) . $isTalkClass,
1079  'text' => wfMessageFallback( "$skname-view-$msgKey", $msgKey )
1080  ->setContext( $this->getContext() )->text(),
1081  'href' => $title->getLocalURL( $this->editUrlOptions() ),
1082  'primary' => !$isRemoteContent, // don't collapse this in vector
1083  ];
1084 
1085  // section link
1086  if ( $showNewSection ) {
1087  // Adds new section link
1088  // $content_navigation['actions']['addsection']
1089  $content_navigation['views']['addsection'] = [
1090  'class' => ( $isEditing && $section == 'new' ) ? 'selected' : false,
1091  'text' => wfMessageFallback( "$skname-action-addsection", 'addsection' )
1092  ->setContext( $this->getContext() )->text(),
1093  'href' => $title->getLocalURL( 'action=edit&section=new' )
1094  ];
1095  }
1096  // Checks if the page has some kind of viewable source content
1097  } elseif ( $title->hasSourceText() ) {
1098  // Adds view source view link
1099  $content_navigation['views']['viewsource'] = [
1100  'class' => ( $onPage && $action == 'edit' ) ? 'selected' : false,
1101  'text' => wfMessageFallback( "$skname-action-viewsource", 'viewsource' )
1102  ->setContext( $this->getContext() )->text(),
1103  'href' => $title->getLocalURL( $this->editUrlOptions() ),
1104  'primary' => true, // don't collapse this in vector
1105  ];
1106  }
1107 
1108  // Checks if the page exists
1109  if ( $title->exists() ) {
1110  // Adds history view link
1111  $content_navigation['views']['history'] = [
1112  'class' => ( $onPage && $action == 'history' ) ? 'selected' : false,
1113  'text' => wfMessageFallback( "$skname-view-history", 'history_short' )
1114  ->setContext( $this->getContext() )->text(),
1115  'href' => $title->getLocalURL( 'action=history' ),
1116  ];
1117 
1118  if ( $this->getAuthority()->probablyCan( 'delete', $title ) ) {
1119  $content_navigation['actions']['delete'] = [
1120  'class' => ( $onPage && $action == 'delete' ) ? 'selected' : false,
1121  'text' => wfMessageFallback( "$skname-action-delete", 'delete' )
1122  ->setContext( $this->getContext() )->text(),
1123  'href' => $title->getLocalURL( 'action=delete' )
1124  ];
1125  }
1126 
1127  if ( $this->getAuthority()->probablyCan( 'move', $title ) ) {
1128  $moveTitle = SpecialPage::getTitleFor( 'Movepage', $title->getPrefixedDBkey() );
1129  $content_navigation['actions']['move'] = [
1130  'class' => $this->getTitle()->isSpecial( 'Movepage' ) ? 'selected' : false,
1131  'text' => wfMessageFallback( "$skname-action-move", 'move' )
1132  ->setContext( $this->getContext() )->text(),
1133  'href' => $moveTitle->getLocalURL()
1134  ];
1135  }
1136  } else {
1137  // article doesn't exist or is deleted
1138  if ( $this->getAuthority()->probablyCan( 'deletedhistory', $title ) ) {
1139  $n = $title->getDeletedEditsCount();
1140  if ( $n ) {
1141  $undelTitle = SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedDBkey() );
1142  // If the user can't undelete but can view deleted
1143  // history show them a "View .. deleted" tab instead.
1144  $msgKey = $this->getAuthority()->probablyCan( 'undelete', $title ) ?
1145  'undelete' : 'viewdeleted';
1146  $content_navigation['actions']['undelete'] = [
1147  'class' => $this->getTitle()->isSpecial( 'Undelete' ) ? 'selected' : false,
1148  'text' => wfMessageFallback( "$skname-action-$msgKey", "{$msgKey}_short" )
1149  ->setContext( $this->getContext() )->numParams( $n )->text(),
1150  'href' => $undelTitle->getLocalURL()
1151  ];
1152  }
1153  }
1154  }
1155 
1156  if ( $this->getAuthority()->probablyCan( 'protect', $title ) &&
1157  $title->getRestrictionTypes() &&
1158  $permissionManager->getNamespaceRestrictionLevels(
1159  $title->getNamespace(),
1160  $performer->getUser()
1161  ) !== [ '' ]
1162  ) {
1163  $mode = $title->isProtected() ? 'unprotect' : 'protect';
1164  $content_navigation['actions'][$mode] = [
1165  'class' => ( $onPage && $action == $mode ) ? 'selected' : false,
1166  'text' => wfMessageFallback( "$skname-action-$mode", $mode )
1167  ->setContext( $this->getContext() )->text(),
1168  'href' => $title->getLocalURL( "action=$mode" )
1169  ];
1170  }
1171 
1172  // Checks if the user is logged in
1173  if ( $this->loggedin && $this->getAuthority()
1174  ->isAllowedAll( 'viewmywatchlist', 'editmywatchlist' )
1175  ) {
1185  $mode = MediaWikiServices::getInstance()->getWatchlistManager()
1186  ->isWatched( $performer, $title ) ? 'unwatch' : 'watch';
1187 
1188  // Add the watch/unwatch link.
1189  $content_navigation['actions'][$mode] = $this->getWatchLinkAttrs(
1190  $mode,
1191  $performer,
1192  $title,
1193  $action,
1194  $onPage
1195  );
1196  }
1197  }
1198 
1199  // Add language variants
1200  $languageConverterFactory = MediaWikiServices::getInstance()->getLanguageConverterFactory();
1201 
1202  if ( $userCanRead && !$languageConverterFactory->isConversionDisabled() ) {
1203  $pageLang = $title->getPageLanguage();
1204  $converter = $languageConverterFactory
1205  ->getLanguageConverter( $pageLang );
1206  // Checks that language conversion is enabled and variants exist
1207  // And if it is not in the special namespace
1208  if ( $converter->hasVariants() ) {
1209  // Gets list of language variants
1210  $variants = $converter->getVariants();
1211  // Gets preferred variant (note that user preference is
1212  // only possible for wiki content language variant)
1213  $preferred = $converter->getPreferredVariant();
1214  if ( Action::getActionName( $this ) === 'view' ) {
1215  $params = $request->getQueryValues();
1216  unset( $params['title'] );
1217  } else {
1218  $params = [];
1219  }
1220  // Loops over each variant
1221  foreach ( $variants as $code ) {
1222  // Gets variant name from language code
1223  $varname = $pageLang->getVariantname( $code );
1224  // Appends variant link
1225  $content_navigation['variants'][] = [
1226  'class' => ( $code == $preferred ) ? 'selected' : false,
1227  'text' => $varname,
1228  'href' => $title->getLocalURL( [ 'variant' => $code ] + $params ),
1229  'lang' => LanguageCode::bcp47( $code ),
1230  'hreflang' => LanguageCode::bcp47( $code ),
1231  ];
1232  }
1233  }
1234  }
1235  } else {
1236  // If it's not content, and a request URL is set it's got to be a special page
1237  try {
1238  $url = $request->getRequestURL();
1239  } catch ( MWException $e ) {
1240  $url = false;
1241  }
1242  $content_navigation['namespaces']['special'] = [
1243  'class' => 'selected',
1244  'text' => $this->msg( 'nstab-special' )->text(),
1245  'href' => $url, // @see: T4457, T4510
1246  'context' => 'subject'
1247  ];
1248  }
1249  $this->runOnSkinTemplateNavigationHooks( $this, $content_navigation );
1250 
1251  // Setup xml ids and tooltip info
1252  foreach ( $content_navigation as $section => &$links ) {
1253  foreach ( $links as $key => &$link ) {
1254  // Allow links to set their own id for backwards compatibility reasons.
1255  if ( isset( $link['id'] ) ) {
1256  continue;
1257  }
1258  $xmlID = $key;
1259  if ( isset( $link['context'] ) && $link['context'] == 'subject' ) {
1260  $xmlID = 'ca-nstab-' . $xmlID;
1261  } elseif ( isset( $link['context'] ) && $link['context'] == 'talk' ) {
1262  $xmlID = 'ca-talk';
1263  $link['rel'] = 'discussion';
1264  } elseif ( $section == 'variants' ) {
1265  $xmlID = 'ca-varlang-' . $xmlID;
1266  } else {
1267  $xmlID = 'ca-' . $xmlID;
1268  }
1269  $link['id'] = $xmlID;
1270  }
1271  }
1272 
1273  # We don't want to give the watch tab an accesskey if the
1274  # page is being edited, because that conflicts with the
1275  # accesskey on the watch checkbox. We also don't want to
1276  # give the edit tab an accesskey, because that's fairly
1277  # superfluous and conflicts with an accesskey (Ctrl-E) often
1278  # used for editing in Safari.
1279  if ( in_array( $action, [ 'edit', 'submit' ] ) ) {
1280  if ( isset( $content_navigation['views']['edit'] ) ) {
1281  $content_navigation['views']['edit']['tooltiponly'] = true;
1282  }
1283  if ( isset( $content_navigation['actions']['watch'] ) ) {
1284  $content_navigation['actions']['watch']['tooltiponly'] = true;
1285  }
1286  if ( isset( $content_navigation['actions']['unwatch'] ) ) {
1287  $content_navigation['actions']['unwatch']['tooltiponly'] = true;
1288  }
1289  }
1290 
1291  return $content_navigation;
1292  }
1293 
1299  private function buildContentActionUrls( $content_navigation ) {
1300  // content_actions has been replaced with content_navigation for backwards
1301  // compatibility and also for skins that just want simple tabs content_actions
1302  // is now built by flattening the content_navigation arrays into one
1303 
1304  $content_actions = [];
1305 
1306  foreach ( $content_navigation as $navigation => $links ) {
1307  foreach ( $links as $key => $value ) {
1308  if ( isset( $value['redundant'] ) && $value['redundant'] ) {
1309  // Redundant tabs are dropped from content_actions
1310  continue;
1311  }
1312 
1313  // content_actions used to have ids built using the "ca-$key" pattern
1314  // so the xmlID based id is much closer to the actual $key that we want
1315  // for that reason we'll just strip out the ca- if present and use
1316  // the latter potion of the "id" as the $key
1317  if ( isset( $value['id'] ) && substr( $value['id'], 0, 3 ) == 'ca-' ) {
1318  $key = substr( $value['id'], 3 );
1319  }
1320 
1321  if ( isset( $content_actions[$key] ) ) {
1322  wfDebug( __METHOD__ . ": Found a duplicate key for $key while flattening " .
1323  "content_navigation into content_actions." );
1324  continue;
1325  }
1326 
1327  $content_actions[$key] = $value;
1328  }
1329  }
1330 
1331  return $content_actions;
1332  }
1333 
1339  protected function getNameSpaceKey() {
1340  return $this->getTitle()->getNamespaceKey();
1341  }
1342 
1351  final protected function injectLegacyMenusIntoPersonalTools(
1352  array $contentNavigation
1353  ): array {
1354  $userMenu = $contentNavigation['user-menu'] ?? [];
1355  // userpage is only defined for logged-in users, and wfArrayInsertAfter requires the
1356  // $after parameter to be a known key in the array.
1357  if ( isset( $contentNavigation['user-menu']['userpage'] ) && isset( $contentNavigation['notifications'] ) ) {
1358  $userMenu = wfArrayInsertAfter(
1359  $userMenu,
1360  $contentNavigation['notifications'],
1361  'userpage'
1362  );
1363  }
1364  if ( isset( $contentNavigation['user-interface-preferences'] ) ) {
1365  return array_merge(
1366  $contentNavigation['user-interface-preferences'],
1367  $userMenu
1368  );
1369  }
1370  return $userMenu;
1371  }
1372 
1382  array $contentNavigation
1383  ): array {
1384  if ( isset( $contentNavigation['user-menu'] ) ) {
1385  return $this->injectLegacyMenusIntoPersonalTools( $contentNavigation );
1386  }
1387  return [];
1388  }
1389 
1390 }
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:108
Skin\prepareSubtitle
prepareSubtitle()
Prepare the subtitle of the page for output in the skin if one has been set.
Definition: Skin.php:2561
SkinTemplate\makePersonalToolsList
makePersonalToolsList( $personalTools=null, $options=[])
Get the HTML for the personal tools list Please ensure setupTemplateContext is called before calling ...
Definition: SkinTemplate.php:457
Skin\$skinname
string null $skinname
Definition: Skin.php:55
wfArrayInsertAfter
wfArrayInsertAfter(array $array, array $insert, $after)
Insert array into another array after the specified KEY
Definition: GlobalFunctions.php:189
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:72
SkinTemplate\makeSkinTemplatePersonalUrls
makeSkinTemplatePersonalUrls(array $contentNavigation)
Build the personal urls array.
Definition: SkinTemplate.php:1381
SkinTemplate\useCombinedLoginLink
useCombinedLoginLink()
Returns if a combined login/signup link will be used.
Definition: SkinTemplate.php:674
Skin\editUrlOptions
editUrlOptions()
Return URL options for the 'edit page' link.
Definition: Skin.php:1211
SkinTemplate\setupTemplateForOutput
setupTemplateForOutput()
Definition: SkinTemplate.php:71
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:415
Skin\makeUrlDetails
static makeUrlDetails( $name, $urlaction='')
these return an array with the 'href' and boolean 'exists'
Definition: Skin.php:1341
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:47
Skin\getSiteNotice
getSiteNotice()
Definition: Skin.php:1987
wfMessageFallback
wfMessageFallback(... $keys)
This function accepts multiple message keys and returns a message instance for the first message whic...
Definition: GlobalFunctions.php:1205
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:72
Skin\buildFeedUrls
buildFeedUrls()
Build data structure representing syndication links.
Definition: Skin.php:1634
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:64
Skin\$options
array $options
Skin options passed into constructor.
Definition: Skin.php:60
SkinTemplate\buildContentNavigationUrls
buildContentNavigationUrls()
a structured array of links usually used for the tabs in a skin
Definition: SkinTemplate.php:956
Skin\getPoweredBy
getPoweredBy()
Gets the powered by MediaWiki icon.
Definition: Skin.php:963
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:69
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:186
Skin\getPersonalToolsForMakeListItem
getPersonalToolsForMakeListItem( $urls, $applyClassesToListItems=false)
Create an array of personal tools items from the data in the quicktemplate stored by SkinTemplate.
Definition: Skin.php:2192
true
return true
Definition: router.php:90
MessageSpecifier
Definition: MessageSpecifier.php:24
Skin\checkTitle
static checkTitle(&$title, $name)
make sure we have some title to operate on
Definition: Skin.php:1373
SkinTemplate\$userpageUrlDetails
$userpageUrlDetails
Definition: SkinTemplate.php:54
MWDebug\getHTMLDebugLog
static getHTMLDebugLog()
Generate debug log in HTML for displaying at the bottom of the main content area.
Definition: MWDebug.php:643
Skin\makeSpecialUrl
static makeSpecialUrl( $name, $urlaction='', $proto=null)
Make a URL for a Special Page using the given query and protocol.
Definition: Skin.php:1285
SkinTemplate\$titletxt
$titletxt
Definition: SkinTemplate.php:48
Skin\makeSpecialUrlSubpage
static makeSpecialUrlSubpage( $name, $subpage, $urlaction='')
Definition: Skin.php:1300
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1182
SkinTemplate\$thispage
$thispage
Definition: SkinTemplate.php:47
SkinTemplate\getReturnToParam
getReturnToParam()
Builds query params for the page to return to, used when building links.
Definition: SkinTemplate.php:640
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:107
ContextSource\canUseWikiPage
canUseWikiPage()
Check whether a WikiPage object can be get with getWikiPage().
Definition: ContextSource.php:103
ContextSource\getRequest
getRequest()
Definition: ContextSource.php:81
SkinTemplate\wrapHTML
wrapHTML( $title, $html)
Wrap the body text with language information and identifiable element.
Definition: SkinTemplate.php:168
ContextSource\getUser
getUser()
Definition: ContextSource.php:136
ContextSource\getTitle
getTitle()
Definition: ContextSource.php:90
Skin\makeListItem
makeListItem( $key, $item, $options=[])
Generates a list item for a navigation, portlet, portal, sidebar...
Definition: Skin.php:2411
Skin\bottomScripts
bottomScripts()
This gets called shortly before the "</body>" tag.
Definition: Skin.php:710
SkinTemplate\$userpage
$userpage
Definition: SkinTemplate.php:49
Skin\afterContentHook
afterContentHook()
This runs a hook to allow extensions placing their stuff after content and article metadata (e....
Definition: Skin.php:684
ContextSource\getLanguage
getLanguage()
Definition: ContextSource.php:153
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:53
SkinTemplate\getPersonalToolsList
getPersonalToolsList()
Get the HTML for the p-personal list.
Definition: SkinTemplate.php:443
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:1265
MWException
MediaWiki exception.
Definition: MWException.php:29
Skin\buildSidebar
buildSidebar()
Build an array that represents the sidebar(s), the navigation bar among them.
Definition: Skin.php:1673
SkinTemplate\getFooterIcons
getFooterIcons()
Get template representation of the footer.
Definition: SkinTemplate.php:221
SkinTemplate\tabAction
tabAction( $title, $message, $selected, $query='', $checkEdit=false)
Builds an array with tab definition.
Definition: SkinTemplate.php:789
Skin\getCategories
getCategories()
Definition: Skin.php:649
Skin\getSearchPageTitle
getSearchPageTitle()
Definition: Skin.php:2619
SkinTemplate\buildContentActionUrls
buildContentActionUrls( $content_navigation)
an array of edit links by default used for the tabs
Definition: SkinTemplate.php:1299
ContextSource\getOutput
getOutput()
Definition: ContextSource.php:126
ContextSource\getWikiPage
getWikiPage()
Get the WikiPage object.
Definition: ContextSource.php:117
SkinTemplate\$username
$username
Definition: SkinTemplate.php:53
wfCgiToArray
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
Definition: GlobalFunctions.php:375
Skin\getLanguages
getLanguages()
Generates array of language links for the current page.
Definition: Skin.php:1403
SkinTemplate\$loggedin
$loggedin
Definition: SkinTemplate.php:52
$title
$title
Definition: testCompression.php:38
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:894
ContextSource\setContext
setContext(IContextSource $context)
Definition: ContextSource.php:63
ResourceLoaderSkinModule\getAvailableLogos
static getAvailableLogos( $conf)
Return an array of all available logos that a skin may use.
Definition: ResourceLoaderSkinModule.php:529
SkinTemplate\buildPersonalUrls
buildPersonalUrls(bool $includeNotifications=true)
build array of urls for personal toolbar Please ensure setupTemplateContext is called before calling ...
Definition: SkinTemplate.php:496
SkinTemplate\getStructuredPersonalTools
getStructuredPersonalTools()
Get personal tools for the user.
Definition: SkinTemplate.php:481
SkinTemplate\$thisquery
$thisquery
Definition: SkinTemplate.php:50
SkinTemplate\buildCreateAccountData
buildCreateAccountData( $returnto)
Build "Create Account" link.
Definition: SkinTemplate.php:744
MediaWiki\Permissions\Authority
This interface represents the authority associated the current execution context, such as a web reque...
Definition: Authority.php:37
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:197
SkinTemplate\prepareQuickTemplate
prepareQuickTemplate()
initialize various variables and generate the template
Definition: SkinTemplate.php:274
SkinTemplate\setupTemplateContext
setupTemplateContext()
Setup class properties that are necessary prior to calling setupTemplateForOutput.
Definition: SkinTemplate.php:93
Skin\getRelevantTitle
getRelevantTitle()
Return the "relevant" title.
Definition: Skin.php:409
ContextSource\getAuthority
getAuthority()
Definition: ContextSource.php:144
SkinTemplate\$template
string $template
For QuickTemplate, the name of the subclass which will actually fill the template.
Definition: SkinTemplate.php:45
Skin\printSource
printSource()
Text with the permalink to the source page, usually shown on the footer of a printed page.
Definition: Skin.php:733
SkinTemplate\outputPage
outputPage()
Initialize various variables and generate the template @stable to override.
Definition: SkinTemplate.php:139
Skin\buildNavUrls
buildNavUrls()
Build array of common navigation links.
Definition: Skin.php:1509
SkinTemplate\makeTalkUrlDetails
makeTalkUrlDetails( $name, $urlaction='')
Definition: SkinTemplate.php:850
SkinTemplate\buildPersonalPageItem
buildPersonalPageItem( $id='pt-userpage')
Build a personal page link.
Definition: SkinTemplate.php:762
Skin\logoText
logoText( $align='')
Definition: Skin.php:1020
Skin\getNewtalks
getNewtalks()
Gets new talk page messages for the current user and returns an appropriate alert message (or an empt...
Definition: Skin.php:1833
SkinTemplate\runOnSkinTemplateNavigationHooks
runOnSkinTemplateNavigationHooks(SkinTemplate $skin, &$content_navigation)
Run hooks relating to navigation menu data.
Definition: SkinTemplate.php:908
SkinTemplate\getNameSpaceKey
getNameSpaceKey()
Generate strings used for xml 'id' names.
Definition: SkinTemplate.php:1339
Title
Represents a title within MediaWiki.
Definition: Title.php:49
SkinTemplate\prepareUserLanguageAttributes
prepareUserLanguageAttributes()
Prepare user language attribute links.
Definition: SkinTemplate.php:198
SkinTemplate\getJsConfigVars
getJsConfigVars()
Returns array of config variables that should be added only to this skin for use in JavaScript.
Definition: SkinTemplate.php:157
Title\isSpecialPage
isSpecialPage()
Returns true if this is a special page.
Definition: Title.php:1312
SkinTemplate\buildLogoutLinkData
buildLogoutLinkData()
Build data required for "Logout" link.
Definition: SkinTemplate.php:721
LanguageCode\bcp47
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: LanguageCode.php:175
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
Message
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition: Message.php:138
Skin\getUndeleteLink
getUndeleteLink()
Definition: Skin.php:752
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:1357
Skin\getFooterLinks
getFooterLinks()
Get template representation of the footer containing site footer links as well as standard footer lin...
Definition: Skin.php:2581
Skin\getCopyrightIcon
getCopyrightIcon()
Definition: Skin.php:930
SkinTemplate\prepareUndeleteLink
prepareUndeleteLink()
Prepare undelete link for output in page.
Definition: SkinTemplate.php:263
NS_FILE
const NS_FILE
Definition: Defines.php:70
Skin
The main skin class which provides methods and properties for all other skins.
Definition: Skin.php:44
SkinTemplate\getWatchLinkAttrs
getWatchLinkAttrs(string $mode, Authority $performer, Title $title, ?string $action, bool $onPage)
Get the attributes for the watch link.
Definition: SkinTemplate.php:872
Skin\initPage
initPage(OutputPage $out)
Definition: Skin.php:229
SkinTemplate
Base class for template-based skins.
Definition: SkinTemplate.php:40
SkinTemplate\injectLegacyMenusIntoPersonalTools
injectLegacyMenusIntoPersonalTools(array $contentNavigation)
Insert legacy menu items from content navigation into the personal toolbar.
Definition: SkinTemplate.php:1351
SkinTemplate\generateHTML
generateHTML()
Subclasses not wishing to use the QuickTemplate render method can rewrite this method,...
Definition: SkinTemplate.php:129
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:330
SkinTemplate\buildLoginData
buildLoginData( $returnto, $useCombinedLoginLink)
Build "Login" link.
Definition: SkinTemplate.php:695