MediaWiki  1.34.0
TimelessTemplate.php
Go to the documentation of this file.
1 <?php
8 
10  protected $pileOfTools;
11 
13  protected $sidebar;
14 
16  protected $otherProjects;
17 
19  protected $collectionPortlet;
20 
24  public function execute() {
25  $this->sidebar = $this->getSidebar();
26 
27  // WikiBase sidebar thing
28  if ( isset( $this->sidebar['wikibase-otherprojects'] ) ) {
29  $this->otherProjects = $this->sidebar['wikibase-otherprojects'];
30  unset( $this->sidebar['wikibase-otherprojects'] );
31  }
32  // Collection sidebar thing
33  if ( isset( $this->sidebar['coll-print_export'] ) ) {
34  $this->collectionPortlet = $this->sidebar['coll-print_export'];
35  unset( $this->sidebar['coll-print_export'] );
36  }
37 
38  $this->pileOfTools = $this->getPageTools();
39  $userLinks = $this->getUserLinks();
40 
41  // Open html, body elements, etc
42  $html = $this->get( 'headelement' );
43 
44  $html .= Html::openElement( 'div', [ 'id' => 'mw-wrapper', 'class' => $userLinks['class'] ] );
45 
46  $html .= Html::rawElement( 'div', [ 'id' => 'mw-header-container', 'class' => 'ts-container' ],
47  Html::rawElement( 'div', [ 'id' => 'mw-header', 'class' => 'ts-inner' ],
48  $userLinks['html'] .
49  $this->getLogo( 'p-logo-text', 'text' ) .
50  $this->getSearch()
51  ) .
52  $this->getClear()
53  );
54  $html .= $this->getHeaderHack();
55 
56  // For mobile
57  $html .= Html::element( 'div', [ 'id' => 'menus-cover' ] );
58 
59  $html .= Html::rawElement( 'div', [ 'id' => 'mw-content-container', 'class' => 'ts-container' ],
60  Html::rawElement( 'div', [ 'id' => 'mw-content-block', 'class' => 'ts-inner' ],
61  Html::rawElement( 'div', [ 'id' => 'mw-content-wrapper' ],
62  $this->getContentBlock() .
63  $this->getAfterContent()
64  ) .
65  Html::rawElement( 'div', [ 'id' => 'mw-site-navigation' ],
66  $this->getLogo( 'p-logo', 'image' ) .
67  $this->getMainNavigation() .
68  $this->getSidebarChunk(
69  'site-tools',
70  'timeless-sitetools',
71  $this->getPortlet(
72  'tb',
73  $this->pileOfTools['general'],
74  'timeless-sitetools'
75  )
76  )
77  ) .
78  Html::rawElement( 'div', [ 'id' => 'mw-related-navigation' ],
79  $this->getPageToolSidebar() .
80  $this->getInterwikiLinks() .
81  $this->getCategories()
82  ) .
83  $this->getClear()
84  )
85  );
86 
87  $html .= Html::rawElement( 'div', [ 'id' => 'mw-footer-container', 'class' => 'ts-container' ],
88  Html::rawElement( 'div', [ 'id' => 'mw-footer', 'class' => 'ts-inner' ],
89  $this->getFooter()
90  )
91  );
92 
93  $html .= Html::closeElement( 'div' );
94 
95  // BaseTemplate::printTrail() stuff (has no get version)
96  // Required for RL to run
97  $html .= MWDebug::getDebugHTML( $this->getSkin()->getContext() );
98  $html .= $this->get( 'bottomscripts' );
99  $html .= $this->get( 'reporttime' );
100 
101  $html .= Html::closeElement( 'body' );
102  $html .= Html::closeElement( 'html' );
103 
104  // The unholy echo
105  echo $html;
106  }
107 
114  protected function getContentBlock() {
115  $html = Html::rawElement(
116  'div',
117  [ 'id' => 'content', 'class' => 'mw-body', 'role' => 'main' ],
118  $this->getSiteNotices() .
119  $this->getIndicators() .
120  Html::rawElement(
121  'h1',
122  [
123  'id' => 'firstHeading',
124  'class' => 'firstHeading',
125  'lang' => $this->get( 'pageLanguage' )
126  ],
127  $this->get( 'title' )
128  ) .
129  Html::rawElement( 'div', [ 'id' => 'bodyContentOuter' ],
130  Html::rawElement( 'div', [ 'id' => 'siteSub' ], $this->getMsg( 'tagline' )->parse() ) .
131  Html::rawElement( 'div', [ 'id' => 'mw-page-header-links' ],
132  $this->getPortlet(
133  'namespaces',
134  $this->pileOfTools['namespaces'],
135  'timeless-namespaces',
136  [ 'extra-classes' => 'tools-inline' ]
137  ) .
138  $this->getPortlet(
139  'more',
140  $this->pileOfTools['more'],
141  'timeless-more',
142  [ 'extra-classes' => 'tools-inline' ]
143  ) .
144  $this->getVariants() .
145  $this->getPortlet(
146  'views',
147  $this->pileOfTools['page-primary'],
148  'timeless-pagetools',
149  [ 'extra-classes' => 'tools-inline' ]
150  )
151  ) .
152  $this->getClear() .
153  Html::rawElement( 'div', [ 'class' => 'mw-body-content', 'id' => 'bodyContent' ],
154  $this->getContentSub() .
155  $this->get( 'bodytext' ) .
156  $this->getClear()
157  )
158  )
159  );
160 
161  return Html::rawElement( 'div', [ 'id' => 'mw-content' ], $html );
162  }
163 
191  protected function getPortlet( $name, $content, $msg = null, $setOptions = [] ) {
192  // random stuff to override with any provided options
193  $options = array_merge( [
194  'role' => 'navigation',
195  // extra classes/ids
196  'id' => 'p-' . $name,
197  'class' => [ 'mw-portlet', 'emptyPortlet' => !$content ],
198  'extra-classes' => '',
199  'body-id' => null,
200  'body-class' => 'mw-portlet-body',
201  'body-extra-classes' => '',
202  // wrapper for individual list items
203  'text-wrapper' => [ 'tag' => 'span' ],
204  // option to stick arbitrary stuff at the beginning of the ul
205  'list-prepend' => ''
206  ], $setOptions );
207 
208  // Handle the different $msg possibilities
209  if ( $msg === null ) {
210  $msg = $name;
211  $msgParams = [];
212  } elseif ( is_array( $msg ) ) {
213  $msgString = array_shift( $msg );
214  $msgParams = $msg;
215  $msg = $msgString;
216  } else {
217  $msgParams = [];
218  }
219  $msgObj = $this->getMsg( $msg, $msgParams );
220  if ( $msgObj->exists() ) {
221  $msgString = $msgObj->parse();
222  } else {
223  $msgString = htmlspecialchars( $msg );
224  }
225 
226  $labelId = Sanitizer::escapeIdForAttribute( "p-$name-label" );
227 
228  if ( is_array( $content ) ) {
229  $contentText = Html::openElement( 'ul',
230  [ 'lang' => $this->get( 'userlang' ), 'dir' => $this->get( 'dir' ) ]
231  );
232  $contentText .= $options['list-prepend'];
233  foreach ( $content as $key => $item ) {
234  if ( is_array( $options['text-wrapper'] ) ) {
235  $contentText .= $this->makeListItem(
236  $key,
237  $item,
238  [ 'text-wrapper' => $options['text-wrapper'] ]
239  );
240  } else {
241  $contentText .= $this->makeListItem(
242  $key,
243  $item
244  );
245  }
246  }
247  $contentText .= Html::closeElement( 'ul' );
248  } else {
249  $contentText = $content;
250  }
251 
252  $divOptions = [
253  'role' => $options['role'],
254  'class' => $this->mergeClasses( $options['class'], $options['extra-classes'] ),
255  'id' => Sanitizer::escapeIdForAttribute( $options['id'] ),
256  'title' => Linker::titleAttrib( $options['id'] ),
257  'aria-labelledby' => $labelId
258  ];
259  $labelOptions = [
260  'id' => $labelId,
261  'lang' => $this->get( 'userlang' ),
262  'dir' => $this->get( 'dir' )
263  ];
264 
265  $bodyDivOptions = [
266  'class' => $this->mergeClasses( $options['body-class'], $options['body-extra-classes'] )
267  ];
268  if ( is_string( $options['body-id'] ) ) {
269  $bodyDivOptions['id'] = $options['body-id'];
270  }
271 
272  $html = Html::rawElement( 'div', $divOptions,
273  Html::rawElement( 'h3', $labelOptions, $msgString ) .
274  Html::rawElement( 'div', $bodyDivOptions,
275  $contentText .
276  $this->getAfterPortlet( $name )
277  )
278  );
279 
280  return $html;
281  }
282 
294  protected function mergeClasses( $class, $extraClasses ) {
295  if ( !is_array( $class ) ) {
296  $class = [ $class ];
297  }
298  if ( !is_array( $extraClasses ) ) {
299  $extraClasses = [ $extraClasses ];
300  }
301 
302  return array_merge( $class, $extraClasses );
303  }
304 
315  protected function getSidebarChunk( $id, $headerMessage, $content, $classes = [] ) {
316  $html = '';
317 
318  $html .= Html::rawElement(
319  'div',
320  [
321  'id' => Sanitizer::escapeId( $id ),
322  'class' => array_merge( [ 'sidebar-chunk' ], $classes )
323  ],
324  Html::rawElement( 'h2', [],
325  Html::element( 'span', [],
326  $this->getMsg( $headerMessage )->text()
327  )
328  ) .
329  Html::rawElement( 'div', [ 'class' => 'sidebar-inner' ], $content )
330  );
331 
332  return $html;
333  }
334 
343  protected function getLogo( $id = 'p-logo', $part = 'both' ) {
344  $html = '';
345  $language = $this->getSkin()->getLanguage();
346  $config = $this->getSkin()->getContext()->getConfig();
347 
348  $html .= Html::openElement(
349  'div',
350  [
351  'id' => Sanitizer::escapeId( $id ),
352  'class' => 'mw-portlet',
353  'role' => 'banner'
354  ]
355  );
356  if ( $part !== 'image' ) {
357  $wordmarkImage = $this->getLogoImage( $config->get( 'TimelessWordmark' ), true );
358 
359  $titleClass = '';
360  if ( !$wordmarkImage ) {
361  if ( $language->hasVariants() ) {
362  $siteTitle = $language->convert( $this->getMsg( 'timeless-sitetitle' )->escaped() );
363  } else {
364  $siteTitle = $this->getMsg( 'timeless-sitetitle' )->escaped();
365  }
366  // width is 11em; 13 characters will probably fit?
367  if ( mb_strlen( $siteTitle ) > 13 ) {
368  $titleClass = 'long';
369  }
370  } else {
371  $titleClass = 'wordmark';
372  }
373  $html .= Html::rawElement( 'a', [
374  'id' => 'p-banner',
375  'class' => [ 'mw-wiki-title', $titleClass ],
376  'href' => $this->data['nav_urls']['mainpage']['href']
377  ],
378  $wordmarkImage ?: $siteTitle
379  );
380 
381  }
382  if ( $part !== 'text' ) {
383  $logoImage = $this->getLogoImage( $config->get( 'TimelessLogo' ) );
384 
385  $html .= Html::rawElement(
386  'a',
387  array_merge(
388  [
389  'class' => [ 'mw-wiki-logo', !$logoImage ? 'fallback' : 'timeless-logo' ],
390  'href' => $this->data['nav_urls']['mainpage']['href']
391  ],
393  ),
394  $logoImage ?: ''
395  );
396  }
397  $html .= Html::closeElement( 'div' );
398 
399  return $html;
400  }
401 
407  protected function getSearch() {
408  $html = '';
409 
410  $html .= Html::openElement( 'div', [ 'class' => 'mw-portlet', 'id' => 'p-search' ] );
411 
412  $html .= Html::rawElement(
413  'h3',
414  [ 'lang' => $this->get( 'userlang' ), 'dir' => $this->get( 'dir' ) ],
415  Html::rawElement( 'label', [ 'for' => 'searchInput' ], $this->getMsg( 'search' )->escaped() )
416  );
417 
418  $html .= Html::rawElement( 'form', [ 'action' => $this->get( 'wgScript' ), 'id' => 'searchform' ],
419  Html::rawElement( 'div', [ 'id' => 'simpleSearch' ],
420  Html::rawElement( 'div', [ 'id' => 'searchInput-container' ],
421  $this->makeSearchInput( [
422  'id' => 'searchInput'
423  ] )
424  ) .
425  Html::hidden( 'title', $this->get( 'searchtitle' ) ) .
426  $this->makeSearchButton(
427  'fulltext',
428  [ 'id' => 'mw-searchButton', 'class' => 'searchButton mw-fallbackSearchButton' ]
429  ) .
430  $this->makeSearchButton(
431  'go',
432  [ 'id' => 'searchButton', 'class' => 'searchButton' ]
433  )
434  )
435  );
436 
437  $html .= Html::closeElement( 'div' );
438 
439  return $html;
440  }
441 
447  protected function getMainNavigation() {
448  $html = '';
449 
450  // Already hardcoded into header
451  $this->sidebar['SEARCH'] = false;
452  // Parsed as part of pageTools
453  $this->sidebar['TOOLBOX'] = false;
454  // Forcibly removed to separate chunk
455  $this->sidebar['LANGUAGES'] = false;
456 
457  foreach ( $this->sidebar as $name => $content ) {
458  if ( $content === false ) {
459  continue;
460  }
461  // Numeric strings gets an integer when set as key, cast back - T73639
462  $name = (string)$name;
463  $html .= $this->getPortlet( $name, $content['content'] );
464  }
465 
466  $html = $this->getSidebarChunk( 'site-navigation', 'navigation', $html );
467 
468  return $html;
469  }
470 
477  protected function getHeaderHack() {
478  $html = '';
479 
480  // These are almost exactly the same and this is stupid.
481  $html .= Html::rawElement( 'div', [ 'id' => 'mw-header-hack', 'class' => 'color-bar' ],
482  Html::rawElement( 'div', [ 'class' => 'color-middle-container' ],
483  Html::element( 'div', [ 'class' => 'color-middle' ] )
484  ) .
485  Html::element( 'div', [ 'class' => 'color-left' ] ) .
486  Html::element( 'div', [ 'class' => 'color-right' ] )
487  );
488  $html .= Html::rawElement( 'div', [ 'id' => 'mw-header-nav-hack' ],
489  Html::rawElement( 'div', [ 'class' => 'color-bar' ],
490  Html::rawElement( 'div', [ 'class' => 'color-middle-container' ],
491  Html::element( 'div', [ 'class' => 'color-middle' ] )
492  ) .
493  Html::element( 'div', [ 'class' => 'color-left' ] ) .
494  Html::element( 'div', [ 'class' => 'color-right' ] )
495  )
496  );
497 
498  return $html;
499  }
500 
506  protected function getPageToolSidebar() {
507  $pageTools = '';
508  $pageTools .= $this->getPortlet(
509  'cactions',
510  $this->pileOfTools['page-secondary'],
511  'timeless-pageactions'
512  );
513  $pageTools .= $this->getPortlet(
514  'userpagetools',
515  $this->pileOfTools['user'],
516  'timeless-userpagetools'
517  );
518  $pageTools .= $this->getPortlet(
519  'pagemisc',
520  $this->pileOfTools['page-tertiary'],
521  'timeless-pagemisc'
522  );
523  if ( isset( $this->collectionPortlet ) ) {
524  $pageTools .= $this->getPortlet(
525  'coll-print_export',
526  $this->collectionPortlet['content']
527  );
528  }
529 
530  return $this->getSidebarChunk( 'page-tools', 'timeless-pageactions', $pageTools );
531  }
532 
539  protected function getUserLinks() {
540  $user = $this->getSkin()->getUser();
541  $personalTools = $this->getPersonalTools();
542  // Preserve standard username label to allow customisation (T215822)
543  $userName = $personalTools['userpage']['links'][0]['text'] ?? $user->getName();
544 
545  $html = '';
546  $extraTools = [];
547 
548  // Remove Echo badges
549  if ( isset( $personalTools['notifications-alert'] ) ) {
550  $extraTools['notifications-alert'] = $personalTools['notifications-alert'];
551  unset( $personalTools['notifications-alert'] );
552  }
553  if ( isset( $personalTools['notifications-notice'] ) ) {
554  $extraTools['notifications-notice'] = $personalTools['notifications-notice'];
555  unset( $personalTools['notifications-notice'] );
556  }
557  $class = empty( $extraTools ) ? '' : 'extension-icons';
558 
559  // Re-label some messages
560  if ( isset( $personalTools['userpage'] ) ) {
561  $personalTools['userpage']['links'][0]['text'] = $this->getMsg( 'timeless-userpage' )->text();
562  }
563  if ( isset( $personalTools['mytalk'] ) ) {
564  $personalTools['mytalk']['links'][0]['text'] = $this->getMsg( 'timeless-talkpage' )->text();
565  }
566 
567  // Labels
568  if ( $user->isLoggedIn() ) {
569  $dropdownHeader = $userName;
570  $headerMsg = [ 'timeless-loggedinas', $userName ];
571  } else {
572  $dropdownHeader = $this->getMsg( 'timeless-anonymous' )->text();
573  $headerMsg = 'timeless-notloggedin';
574  }
575  $html .= Html::openElement( 'div', [ 'id' => 'user-tools' ] );
576 
577  $html .= Html::rawElement( 'div', [ 'id' => 'personal' ],
578  Html::rawElement( 'h2', [],
579  Html::element( 'span', [], $dropdownHeader )
580  ) .
581  Html::rawElement( 'div', [ 'id' => 'personal-inner', 'class' => 'dropdown' ],
582  $this->getPortlet( 'personal', $personalTools, $headerMsg )
583  )
584  );
585 
586  // Extra icon stuff (echo etc)
587  if ( !empty( $extraTools ) ) {
588  $iconList = '';
589  foreach ( $extraTools as $key => $item ) {
590  $iconList .= $this->makeListItem( $key, $item );
591  }
592 
593  $html .= Html::rawElement(
594  'div',
595  [ 'id' => 'personal-extra', 'class' => 'p-body' ],
596  Html::rawElement( 'ul', [], $iconList )
597  );
598  }
599 
600  $html .= Html::closeElement( 'div' );
601 
602  return [
603  'html' => $html,
604  'class' => $class
605  ];
606  }
607 
613  protected function getSiteNotices() {
614  $html = '';
615 
616  if ( $this->data['sitenotice'] ) {
617  $html .= Html::rawElement( 'div', [ 'id' => 'siteNotice' ], $this->get( 'sitenotice' ) );
618  }
619  if ( $this->data['newtalk'] ) {
620  $html .= Html::rawElement( 'div', [ 'class' => 'usermessage' ], $this->get( 'newtalk' ) );
621  }
622 
623  return $html;
624  }
625 
631  protected function getContentSub() {
632  $html = '';
633 
634  $html .= Html::openElement( 'div', [ 'id' => 'contentSub' ] );
635  if ( $this->data['subtitle'] ) {
636  $html .= $this->get( 'subtitle' );
637  }
638  if ( $this->data['undelete'] ) {
639  $html .= $this->get( 'undelete' );
640  }
641  $html .= Html::closeElement( 'div' );
642 
643  return $html;
644  }
645 
652  protected function getAfterContent() {
653  $html = '';
654 
655  if ( $this->data['catlinks'] || $this->data['dataAfterContent'] ) {
656  $html .= Html::openElement( 'div', [ 'id' => 'content-bottom-stuff' ] );
657  if ( $this->data['catlinks'] ) {
658  $html .= $this->get( 'catlinks' );
659  }
660  if ( $this->data['dataAfterContent'] ) {
661  $html .= $this->get( 'dataAfterContent' );
662  }
663  $html .= Html::closeElement( 'div' );
664  }
665 
666  return $html;
667  }
668 
678  protected function getPageTools() {
679  $title = $this->getSkin()->getTitle();
680  $namespace = $title->getNamespace();
681 
682  $sortedPileOfTools = [
683  'namespaces' => [],
684  'page-primary' => [],
685  'page-secondary' => [],
686  'user' => [],
687  'page-tertiary' => [],
688  'more' => [],
689  'general' => []
690  ];
691 
692  // Tools specific to the page
693  $pileOfEditTools = [];
694  foreach ( $this->data['content_navigation'] as $navKey => $navBlock ) {
695  // Just use namespaces items as they are
696  if ( $navKey == 'namespaces' ) {
697  if ( $namespace < 0 && count( $navBlock ) < 2 ) {
698  // Put special page ns_pages in the more pile so they're not so lonely
699  $sortedPileOfTools['page-tertiary'] = $navBlock;
700  } else {
701  $sortedPileOfTools['namespaces'] = $navBlock;
702  }
703  } elseif ( $navKey == 'variants' ) {
704  // wat
705  $sortedPileOfTools['variants'] = $navBlock;
706  } else {
707  $pileOfEditTools = array_merge( $pileOfEditTools, $navBlock );
708  }
709  }
710 
711  // Tools that may be general or page-related (typically the toolbox)
712  $pileOfTools = $this->getToolbox();
713  if ( $namespace >= 0 ) {
714  $pileOfTools['pagelog'] = [
715  'text' => $this->getMsg( 'timeless-pagelog' )->text(),
716  'href' => SpecialPage::getTitleFor( 'Log' )->getLocalURL(
717  [ 'page' => $title->getPrefixedText() ]
718  ),
719  'id' => 't-pagelog'
720  ];
721  }
722 
723  // Mobile toggles
724  $pileOfTools['more'] = [
725  'text' => $this->getMsg( 'timeless-more' )->text(),
726  'id' => 'ca-more',
727  'class' => 'dropdown-toggle'
728  ];
729  if ( $this->data['language_urls'] !== false || $sortedPileOfTools['variants']
730  || isset( $this->otherProjects ) ) {
731  $pileOfTools['languages'] = [
732  'text' => $this->getMsg( 'timeless-languages' )->escaped(),
733  'id' => 'ca-languages',
734  'class' => 'dropdown-toggle'
735  ];
736  }
737 
738  // This is really dumb, and you're an idiot for doing it this way.
739  // Obviously if you're not the idiot who did this, I don't mean you.
740  foreach ( $pileOfEditTools as $navKey => $navBlock ) {
741  $currentSet = null;
742 
743  if ( in_array( $navKey, [
744  'watch',
745  'unwatch'
746  ] ) ) {
747  $currentSet = 'namespaces';
748  } elseif ( in_array( $navKey, [
749  'edit',
750  'view',
751  'history',
752  'addsection',
753  'viewsource'
754  ] ) ) {
755  $currentSet = 'page-primary';
756  } elseif ( in_array( $navKey, [
757  'delete',
758  'rename',
759  'protect',
760  'unprotect',
761  'move'
762  ] ) ) {
763  $currentSet = 'page-secondary';
764  } else {
765  // Catch random extension ones?
766  $currentSet = 'page-primary';
767  }
768  $sortedPileOfTools[$currentSet][$navKey] = $navBlock;
769  }
770  foreach ( $pileOfTools as $navKey => $navBlock ) {
771  $currentSet = null;
772 
773  if ( $navKey === 'contributions' ) {
774  $currentSet = 'page-primary';
775  } elseif ( in_array( $navKey, [
776  'blockip',
777  'userrights',
778  'log',
779  'emailuser'
780 
781  ] ) ) {
782  $currentSet = 'user';
783  } elseif ( in_array( $navKey, [
784  'whatlinkshere',
785  'print',
786  'info',
787  'pagelog',
788  'recentchangeslinked',
789  'permalink',
790  'wikibase',
791  'cite'
792  ] ) ) {
793  $currentSet = 'page-tertiary';
794  } elseif ( in_array( $navKey, [
795  'more',
796  'languages'
797  ] ) ) {
798  $currentSet = 'more';
799  } else {
800  $currentSet = 'general';
801  }
802  $sortedPileOfTools[$currentSet][$navKey] = $navBlock;
803  }
804 
805  // Extra sorting for Extension:ProofreadPage namespace items
806  $tabs = [
807  'proofreadPagePrevLink',
808  // This is the order we want them in...
809  'proofreadPageScanLink',
810  'proofreadPageIndexLink',
811  'proofreadPageNextLink',
812  ];
813  foreach ( $tabs as $tab ) {
814  if ( isset( $sortedPileOfTools['namespaces'][$tab] ) ) {
815  $toMove = $sortedPileOfTools['namespaces'][$tab];
816  unset( $sortedPileOfTools['namespaces'][$tab] );
817 
818  // add a hover tooltip, mostly for the icons
819  $toMove['title'] = $toMove['text'];
820 
821  if ( $tab === 'proofreadPagePrevLink' ) {
822  // prev at start
823  $sortedPileOfTools['namespaces'] = array_merge(
824  [ $tab => $toMove ],
825  $sortedPileOfTools['namespaces']
826  );
827  } else {
828  // move others to end
829  $sortedPileOfTools['namespaces'][$tab] = $toMove;
830  }
831  }
832  }
833 
834  return $sortedPileOfTools;
835  }
836 
845  protected function getCategories() {
846  $skin = $this->getSkin();
847  $catList = '';
848  $html = '';
849 
850  $allCats = $skin->getOutput()->getCategoryLinks();
851  if ( !empty( $allCats ) ) {
852  if ( !empty( $allCats['normal'] ) ) {
853  $catHeader = 'categories';
854  $catList .= $this->getCatList(
855  $allCats['normal'],
856  'normal-catlinks',
857  'mw-normal-catlinks',
858  'categories'
859  );
860  } else {
861  $catHeader = 'hidden-categories';
862  }
863 
864  if ( isset( $allCats['hidden'] ) ) {
865  $hiddenCatClass = [ 'mw-hidden-catlinks' ];
866  if ( $skin->getUser()->getBoolOption( 'showhiddencats' ) ) {
867  $hiddenCatClass[] = 'mw-hidden-cats-user-shown';
868  } elseif ( $skin->getTitle()->getNamespace() == NS_CATEGORY ) {
869  $hiddenCatClass[] = 'mw-hidden-cats-ns-shown';
870  } else {
871  $hiddenCatClass[] = 'mw-hidden-cats-hidden';
872  }
873  $catList .= $this->getCatList(
874  $allCats['hidden'],
875  'hidden-catlinks',
876  $hiddenCatClass,
877  [ 'hidden-categories', count( $allCats['hidden'] ) ]
878  );
879  }
880  }
881 
882  if ( $catList !== '' ) {
883  $html = $this->getSidebarChunk( 'catlinks-sidebar', $catHeader, $catList );
884  }
885 
886  return $html;
887  }
888 
899  protected function getCatList( $list, $id, $class, $message ) {
900  $html = Html::openElement( 'div', [ 'id' => "sidebar-{$id}", 'class' => $class ] );
901 
902  $makeLinkItem = function ( $linkHtml ) {
903  return Html::rawElement( 'li', [], $linkHtml );
904  };
905 
906  $categoryItems = array_map( $makeLinkItem, $list );
907 
908  $categoriesHtml = Html::rawElement( 'ul',
909  [],
910  implode( '', $categoryItems )
911  );
912 
913  $html .= $this->getPortlet( $id, $categoriesHtml, $message );
914 
915  $html .= Html::closeElement( 'div' );
916 
917  return $html;
918  }
919 
927  protected function getVariants() {
928  $html = '';
929 
930  if ( $this->pileOfTools['variants'] ) {
931  $html .= $this->getPortlet(
932  'variants-desktop',
933  $this->pileOfTools['variants'],
934  'variants',
935  [ 'body-extra-classes' => 'dropdown' ]
936  );
937  }
938 
939  return $html;
940  }
941 
947  protected function getInterwikiLinks() {
948  $html = '';
949  $variants = '';
950  $otherprojects = '';
951  $languages = '';
952  $show = false;
953  $variantsOnly = false;
954 
955  if ( $this->pileOfTools['variants'] ) {
956  $variants = $this->getPortlet(
957  'variants',
958  $this->pileOfTools['variants']
959  );
960  $show = true;
961  $variantsOnly = true;
962  }
963  if ( $this->data['language_urls'] !== false ) {
964  $languages = $this->getPortlet(
965  'lang',
966  $this->data['language_urls'] ?: [],
967  'otherlanguages'
968  );
969  $show = true;
970  $variantsOnly = false;
971  }
972  // if using wikibase for 'in other projects'
973  if ( isset( $this->otherProjects ) ) {
974  $otherprojects = $this->getPortlet(
975  'wikibase-otherprojects',
976  $this->otherProjects['content']
977  );
978  $show = true;
979  $variantsOnly = false;
980  }
981 
982  if ( $show ) {
983  $html .= $this->getSidebarChunk(
984  'other-languages',
985  'timeless-projects',
986  $variants . $languages . $otherprojects,
987  $variantsOnly ? [ 'variants-only' ] : []
988  );
989  }
990 
991  return $html;
992  }
993 
1002  protected function getLogoImage( $logo, $doLarge = false ) {
1003  if ( $logo === null ) {
1004  // not set, fall back to generic methods
1005  return false;
1006  }
1007 
1008  // Generate $logoData from a file upload
1009  if ( is_string( $logo ) ) {
1010  $file = wfFindFile( $logo );
1011 
1012  if ( !$file || !$file->canRender() ) {
1013  // eeeeeh bail, scary
1014  return false;
1015  }
1016  $logoData = [];
1017 
1018  // Calculate intended sizes
1019  $width = $file->getWidth();
1020  $height = $file->getHeight();
1021  $bound = $width > $height ? $width : $height;
1022  $svg = File::normalizeExtension( $file->getExtension() ) === 'svg';
1023 
1024  // Mobile stuff is generally a lot more than just 2ppp. Let's go with 4x?
1025  // Currently we're just doing this for wordmarks, which shouldn't get that
1026  // big in practice, so this is probably safe enough. And no need to use
1027  // this for desktop logos, so fall back to 2x for 2x as default...
1028  $large = $doLarge ? 4 : 2;
1029 
1030  if ( $bound <= 165 ) {
1031  // It's a 1x image
1032  $logoData['width'] = $width;
1033  $logoData['height'] = $height;
1034 
1035  if ( $svg ) {
1036  $logoData['1x'] = $file->createThumb( $logoData['width'] );
1037  $logoData['1.5x'] = $file->createThumb( $logoData['width'] * 1.5 );
1038  $logoData['2x'] = $file->createThumb( $logoData['width'] * $large );
1039  } elseif ( $file->mustRender() ) {
1040  $logoData['1x'] = $file->createThumb( $logoData['width'] );
1041  } else {
1042  $logoData['1x'] = $file->getUrl();
1043  }
1044 
1045  } elseif ( $bound >= 230 && $bound <= 330 ) {
1046  // It's a 2x image
1047  $logoData['width'] = $width / 2;
1048  $logoData['height'] = $height / 2;
1049 
1050  $logoData['1x'] = $file->createThumb( $logoData['width'] );
1051  $logoData['1.5x'] = $file->createThumb( $logoData['width'] * 1.5 );
1052 
1053  if ( $svg || $file->mustRender() ) {
1054  $logoData['2x'] = $file->createThumb( $logoData['width'] * 2 );
1055  } else {
1056  $logoData['2x'] = $file->getUrl();
1057  }
1058  } else {
1059  // Okay, whatever, we get to pick something random
1060  // Yes I am aware this means they might have arbitrarily tall logos,
1061  // and you know what, let 'em, I don't care
1062  $logoData['width'] = 155;
1063  $logoData['height'] = File::scaleHeight( $width, $height, $logoData['width'] );
1064 
1065  $logoData['1x'] = $file->createThumb( $logoData['width'] );
1066  if ( $svg || $logoData['width'] * 1.5 <= $width ) {
1067  $logoData['1.5x'] = $file->createThumb( $logoData['width'] * 1.5 );
1068  }
1069  if ( $svg || $logoData['width'] * 2 <= $width ) {
1070  $logoData['2x'] = $file->createThumb( $logoData['width'] * $large );
1071  }
1072  }
1073  } elseif ( is_array( $logo ) ) {
1074  // manually set logo data for non-file-uploads
1075  $logoData = $logo;
1076  } else {
1077  // nope
1078  return false;
1079  }
1080 
1081  // Render the html output!
1082  $attribs = [
1083  'alt' => $this->getMsg( 'sitetitle' )->text(),
1084  // Should we care? It's just a logo...
1085  'decoding' => 'auto',
1086  'width' => $logoData['width'],
1087  'height' => $logoData['height'],
1088  ];
1089 
1090  if ( !isset( $logoData['1x'] ) && isset( $logoData['2x'] ) ) {
1091  // We'll allow it...
1092  $attribs['src'] = $logoData['2x'];
1093  } else {
1094  // Okay, we really do want a 1x otherwise. If this throws an error or
1095  // something because there's nothing here, GOOD.
1096  $attribs['src'] = $logoData['1x'];
1097 
1098  // Throw the rest in a srcset
1099  unset( $logoData['1x'], $logoData['width'], $logoData['height'] );
1100  $srcset = '';
1101  foreach ( $logoData as $res => $path ) {
1102  if ( $srcset != '' ) {
1103  $srcset .= ', ';
1104  }
1105  $srcset .= $path . ' ' . $res;
1106  }
1107 
1108  if ( $srcset !== '' ) {
1109  $attribs['srcset'] = $srcset;
1110  }
1111  }
1112 
1113  return Html::element( 'img', $attribs );
1114  }
1115 }
BaseTemplate\getPersonalTools
getPersonalTools()
Create an array of personal tools items from the data in the quicktemplate stored by SkinTemplate.
Definition: BaseTemplate.php:127
MWDebug\getDebugHTML
static getDebugHTML(IContextSource $context)
Returns the HTML to add to the page for the toolbar.
Definition: MWDebug.php:444
TimelessTemplate\getPortlet
getPortlet( $name, $content, $msg=null, $setOptions=[])
Generates a block of navigation links with a header This is some random fork of some random fork of w...
Definition: TimelessTemplate.php:191
TimelessTemplate\getSidebarChunk
getSidebarChunk( $id, $headerMessage, $content, $classes=[])
Sidebar chunk containing one or more portlets.
Definition: TimelessTemplate.php:315
BaseTemplate\makeSearchButton
makeSearchButton( $mode, $attrs=[])
Definition: BaseTemplate.php:531
$languages
switch( $options['output']) $languages
Definition: transstat.php:76
true
return true
Definition: router.php:92
QuickTemplate\$config
$config
Definition: QuickTemplate.php:35
BaseTemplate\getClear
getClear()
Get a div with the core visualClear class, for clearing floats.
Definition: BaseTemplate.php:706
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
TimelessTemplate\mergeClasses
mergeClasses( $class, $extraClasses)
Helper function for getPortlet.
Definition: TimelessTemplate.php:294
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
TimelessTemplate\getContentSub
getContentSub()
Links and information that may appear below the firstHeading.
Definition: TimelessTemplate.php:631
TimelessTemplate\getPageToolSidebar
getPageToolSidebar()
Page tools in sidebar.
Definition: TimelessTemplate.php:506
$res
$res
Definition: testCompression.php:52
TimelessTemplate\getLogoImage
getLogoImage( $logo, $doLarge=false)
Generate img-based logos for proper HiDPI support.
Definition: TimelessTemplate.php:1002
getContext
getContext()
QuickTemplate\getSkin
getSkin()
Get the Skin object related to this object.
Definition: QuickTemplate.php:166
Linker\tooltipAndAccesskeyAttribs
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition: Linker.php:2195
TimelessTemplate\getSearch
getSearch()
The search box at the top.
Definition: TimelessTemplate.php:407
TimelessTemplate
BaseTemplate class for the Timeless skin.
Definition: TimelessTemplate.php:7
BaseTemplate\getAfterPortlet
getAfterPortlet( $name)
Allows extensions to hook into known portlets and add stuff to them.
Definition: BaseTemplate.php:295
File\normalizeExtension
static normalizeExtension( $extension)
Normalize a file extension to the common form, making it lowercase and checking some synonyms,...
Definition: File.php:234
BaseTemplate\makeListItem
makeListItem( $key, $item, $options=[])
Generates a list item for a navigation, portlet, portal, sidebar...
Definition: BaseTemplate.php:470
TimelessTemplate\getContentBlock
getContentBlock()
Generate the page content block Broken out here due to the excessive indenting, or stuff.
Definition: TimelessTemplate.php:114
$title
$title
Definition: testCompression.php:34
BaseTemplate\getIndicators
getIndicators()
Get the suggested HTML for page status indicators: icons (or short text snippets) usually displayed i...
Definition: BaseTemplate.php:725
TimelessTemplate\getHeaderHack
getHeaderHack()
The colour bars Split this out so we don't have to look at it/can easily kill it later.
Definition: TimelessTemplate.php:477
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:74
TimelessTemplate\$sidebar
array $sidebar
Definition: TimelessTemplate.php:13
BaseTemplate\getFooter
getFooter( $iconStyle='icononly', $linkStyle='flat')
Renderer for getFooterIcons and getFooterLinks.
Definition: BaseTemplate.php:656
QuickTemplate\text
text( $str)
Definition: QuickTemplate.php:107
$content
$content
Definition: router.php:78
TimelessTemplate\getSiteNotices
getSiteNotices()
Notices that may appear above the firstHeading.
Definition: TimelessTemplate.php:613
BaseTemplate\makeSearchInput
makeSearchInput( $attrs=[])
Definition: BaseTemplate.php:521
TimelessTemplate\getLogo
getLogo( $id='p-logo', $part='both')
The logo and (optionally) site title.
Definition: TimelessTemplate.php:343
TimelessTemplate\getInterwikiLinks
getInterwikiLinks()
Interwiki links block.
Definition: TimelessTemplate.php:947
Linker\titleAttrib
static titleAttrib( $name, $options=null, array $msgParams=[])
Given the id of an interface element, constructs the appropriate title attribute from the system mess...
Definition: Linker.php:2026
wfFindFile
wfFindFile( $title, $options=[])
Find a file.
Definition: GlobalFunctions.php:2604
TimelessTemplate\getMainNavigation
getMainNavigation()
Left sidebar navigation, usually.
Definition: TimelessTemplate.php:447
TimelessTemplate\$otherProjects
array null $otherProjects
Definition: TimelessTemplate.php:16
File\scaleHeight
static scaleHeight( $srcWidth, $srcHeight, $dstWidth)
Calculate the height of a thumbnail using the source and destination width.
Definition: File.php:2014
BaseTemplate\getToolbox
getToolbox()
Create an array of common toolbox items from the data in the quicktemplate stored by SkinTemplate.
Definition: BaseTemplate.php:61
$path
$path
Definition: NoLocalSettings.php:25
BaseTemplate\getMsg
getMsg( $name,... $params)
Get a Message object with its context set.
Definition: BaseTemplate.php:38
TimelessTemplate\getPageTools
getPageTools()
Generate pile of all the tools.
Definition: TimelessTemplate.php:678
TimelessTemplate\getCatList
getCatList( $list, $id, $class, $message)
List of categories.
Definition: TimelessTemplate.php:899
TimelessTemplate\getAfterContent
getAfterContent()
The data after content, catlinks, and potential other stuff that may appear within the content block ...
Definition: TimelessTemplate.php:652
TimelessTemplate\$collectionPortlet
array null $collectionPortlet
Definition: TimelessTemplate.php:19
BaseTemplate
New base template for a skin's template extended from QuickTemplate this class features helper method...
Definition: BaseTemplate.php:29
TimelessTemplate\getUserLinks
getUserLinks()
Personal/user links portlet for header.
Definition: TimelessTemplate.php:539
TimelessTemplate\getVariants
getVariants()
Interlanguage links block, with variants if applicable Layout sort of assumes we're using ULS compact...
Definition: TimelessTemplate.php:927
TimelessTemplate\execute
execute()
Outputs the entire contents of the page.
Definition: TimelessTemplate.php:24
TimelessTemplate\$pileOfTools
array $pileOfTools
Definition: TimelessTemplate.php:10
BaseTemplate\getSidebar
getSidebar( $options=[])
Definition: BaseTemplate.php:152
TimelessTemplate\getCategories
getCategories()
Categories for the sidebar.
Definition: TimelessTemplate.php:845