28 if ( isset( $this->sidebar[
'wikibase-otherprojects'] ) ) {
29 $this->otherProjects = $this->sidebar[
'wikibase-otherprojects'];
30 unset( $this->sidebar[
'wikibase-otherprojects'] );
33 if ( isset( $this->sidebar[
'coll-print_export'] ) ) {
34 $this->collectionPortlet = $this->sidebar[
'coll-print_export'];
35 unset( $this->sidebar[
'coll-print_export'] );
42 $html = $this->
get(
'headelement' );
44 $html .= Html::openElement(
'div', [
'id' =>
'mw-wrapper',
'class' => $userLinks[
'class'] ] );
46 $html .= Html::rawElement(
'div', [
'id' =>
'mw-header-container',
'class' =>
'ts-container' ],
47 Html::rawElement(
'div', [
'id' =>
'mw-header',
'class' =>
'ts-inner' ],
49 $this->
getLogo(
'p-logo-text',
'text' ) .
57 $html .= Html::element(
'div', [
'id' =>
'menus-cover' ] );
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' ],
65 Html::rawElement(
'div', [
'id' =>
'mw-site-navigation' ],
66 $this->
getLogo(
'p-logo',
'image' ) .
73 $this->pileOfTools[
'general'],
78 Html::rawElement(
'div', [
'id' =>
'mw-related-navigation' ],
87 $html .= Html::rawElement(
'div', [
'id' =>
'mw-footer-container',
'class' =>
'ts-container' ],
88 Html::rawElement(
'div', [
'id' =>
'mw-footer',
'class' =>
'ts-inner' ],
93 $html .= Html::closeElement(
'div' );
98 $html .= $this->
get(
'bottomscripts' );
99 $html .= $this->
get(
'reporttime' );
101 $html .= Html::closeElement(
'body' );
102 $html .= Html::closeElement(
'html' );
115 $html = Html::rawElement(
117 [
'id' =>
'content',
'class' =>
'mw-body',
'role' =>
'main' ],
123 'id' =>
'firstHeading',
124 'class' =>
'firstHeading',
125 'lang' => $this->
get(
'pageLanguage' )
127 $this->
get(
'title' )
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' ],
134 $this->pileOfTools[
'namespaces'],
135 'timeless-namespaces',
136 [
'extra-classes' =>
'tools-inline' ]
140 $this->pileOfTools[
'more'],
142 [
'extra-classes' =>
'tools-inline' ]
147 $this->pileOfTools[
'page-primary'],
148 'timeless-pagetools',
149 [
'extra-classes' =>
'tools-inline' ]
153 Html::rawElement(
'div', [
'class' =>
'mw-body-content',
'id' =>
'bodyContent' ],
155 $this->
get(
'bodytext' ) .
161 return Html::rawElement(
'div', [
'id' =>
'mw-content' ], $html );
193 $options = array_merge( [
194 'role' =>
'navigation',
196 'id' =>
'p-' . $name,
197 'class' => [
'mw-portlet',
'emptyPortlet' => !
$content ],
198 'extra-classes' =>
'',
200 'body-class' =>
'mw-portlet-body',
201 'body-extra-classes' =>
'',
203 'text-wrapper' => [
'tag' =>
'span' ],
209 if ( $msg ===
null ) {
212 } elseif ( is_array( $msg ) ) {
213 $msgString = array_shift( $msg );
219 $msgObj = $this->
getMsg( $msg, $msgParams );
220 if ( $msgObj->exists() ) {
221 $msgString = $msgObj->parse();
223 $msgString = htmlspecialchars( $msg );
226 $labelId = Sanitizer::escapeIdForAttribute(
"p-$name-label" );
229 $contentText = Html::openElement(
'ul',
230 [
'lang' => $this->
get(
'userlang' ),
'dir' => $this->
get(
'dir' ) ]
232 $contentText .= $options[
'list-prepend'];
233 foreach (
$content as $key => $item ) {
234 if ( is_array( $options[
'text-wrapper'] ) ) {
238 [
'text-wrapper' => $options[
'text-wrapper'] ]
247 $contentText .= Html::closeElement(
'ul' );
253 'role' => $options[
'role'],
254 'class' => $this->
mergeClasses( $options[
'class'], $options[
'extra-classes'] ),
255 'id' => Sanitizer::escapeIdForAttribute( $options[
'id'] ),
257 'aria-labelledby' => $labelId
261 'lang' => $this->
get(
'userlang' ),
262 'dir' => $this->
get(
'dir' )
266 'class' => $this->
mergeClasses( $options[
'body-class'], $options[
'body-extra-classes'] )
268 if ( is_string( $options[
'body-id'] ) ) {
269 $bodyDivOptions[
'id'] = $options[
'body-id'];
272 $html = Html::rawElement(
'div', $divOptions,
273 Html::rawElement(
'h3', $labelOptions, $msgString ) .
274 Html::rawElement(
'div', $bodyDivOptions,
295 if ( !is_array( $class ) ) {
298 if ( !is_array( $extraClasses ) ) {
299 $extraClasses = [ $extraClasses ];
302 return array_merge( $class, $extraClasses );
318 $html .= Html::rawElement(
321 'id' => Sanitizer::escapeId( $id ),
322 'class' => array_merge( [
'sidebar-chunk' ], $classes )
324 Html::rawElement(
'h2', [],
325 Html::element(
'span', [],
329 Html::rawElement(
'div', [
'class' =>
'sidebar-inner' ],
$content )
343 protected function getLogo( $id =
'p-logo', $part =
'both' ) {
345 $language = $this->
getSkin()->getLanguage();
348 $html .= Html::openElement(
351 'id' => Sanitizer::escapeId( $id ),
352 'class' =>
'mw-portlet',
356 if ( $part !==
'image' ) {
360 if ( !$wordmarkImage ) {
361 if ( $language->hasVariants() ) {
362 $siteTitle = $language->convert( $this->
getMsg(
'timeless-sitetitle' )->escaped() );
364 $siteTitle = $this->
getMsg(
'timeless-sitetitle' )->escaped();
367 if ( mb_strlen( $siteTitle ) > 13 ) {
368 $titleClass =
'long';
371 $titleClass =
'wordmark';
373 $html .= Html::rawElement(
'a', [
375 'class' => [
'mw-wiki-title', $titleClass ],
376 'href' => $this->data[
'nav_urls'][
'mainpage'][
'href']
378 $wordmarkImage ?: $siteTitle
382 if ( $part !==
'text' ) {
385 $html .= Html::rawElement(
389 'class' => [
'mw-wiki-logo', !$logoImage ?
'fallback' :
'timeless-logo' ],
390 'href' => $this->data[
'nav_urls'][
'mainpage'][
'href']
397 $html .= Html::closeElement(
'div' );
410 $html .= Html::openElement(
'div', [
'class' =>
'mw-portlet',
'id' =>
'p-search' ] );
412 $html .= Html::rawElement(
414 [
'lang' => $this->
get(
'userlang' ),
'dir' => $this->
get(
'dir' ) ],
415 Html::rawElement(
'label', [
'for' =>
'searchInput' ], $this->
getMsg(
'search' )->escaped() )
418 $html .= Html::rawElement(
'form', [
'action' => $this->
get(
'wgScript' ),
'id' =>
'searchform' ],
419 Html::rawElement(
'div', [
'id' =>
'simpleSearch' ],
420 Html::rawElement(
'div', [
'id' =>
'searchInput-container' ],
422 'id' =>
'searchInput'
425 Html::hidden(
'title', $this->
get(
'searchtitle' ) ) .
428 [
'id' =>
'mw-searchButton',
'class' =>
'searchButton mw-fallbackSearchButton' ]
432 [
'id' =>
'searchButton',
'class' =>
'searchButton' ]
437 $html .= Html::closeElement(
'div' );
451 $this->sidebar[
'SEARCH'] =
false;
453 $this->sidebar[
'TOOLBOX'] =
false;
455 $this->sidebar[
'LANGUAGES'] =
false;
457 foreach ( $this->sidebar as $name =>
$content ) {
462 $name = (string)$name;
466 $html = $this->
getSidebarChunk(
'site-navigation',
'navigation', $html );
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' ] )
485 Html::element(
'div', [
'class' =>
'color-left' ] ) .
486 Html::element(
'div', [
'class' =>
'color-right' ] )
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' ] )
493 Html::element(
'div', [
'class' =>
'color-left' ] ) .
494 Html::element(
'div', [
'class' =>
'color-right' ] )
510 $this->pileOfTools[
'page-secondary'],
511 'timeless-pageactions'
515 $this->pileOfTools[
'user'],
516 'timeless-userpagetools'
520 $this->pileOfTools[
'page-tertiary'],
523 if ( isset( $this->collectionPortlet ) ) {
526 $this->collectionPortlet[
'content']
530 return $this->
getSidebarChunk(
'page-tools',
'timeless-pageactions', $pageTools );
540 $user = $this->
getSkin()->getUser();
543 $userName = $personalTools[
'userpage'][
'links'][0][
'text'] ?? $user->getName();
549 if ( isset( $personalTools[
'notifications-alert'] ) ) {
550 $extraTools[
'notifications-alert'] = $personalTools[
'notifications-alert'];
551 unset( $personalTools[
'notifications-alert'] );
553 if ( isset( $personalTools[
'notifications-notice'] ) ) {
554 $extraTools[
'notifications-notice'] = $personalTools[
'notifications-notice'];
555 unset( $personalTools[
'notifications-notice'] );
557 $class = empty( $extraTools ) ?
'' :
'extension-icons';
560 if ( isset( $personalTools[
'userpage'] ) ) {
561 $personalTools[
'userpage'][
'links'][0][
'text'] = $this->
getMsg(
'timeless-userpage' )->text();
563 if ( isset( $personalTools[
'mytalk'] ) ) {
564 $personalTools[
'mytalk'][
'links'][0][
'text'] = $this->
getMsg(
'timeless-talkpage' )->text();
568 if ( $user->isLoggedIn() ) {
569 $dropdownHeader = $userName;
570 $headerMsg = [
'timeless-loggedinas', $userName ];
572 $dropdownHeader = $this->
getMsg(
'timeless-anonymous' )->text();
573 $headerMsg =
'timeless-notloggedin';
575 $html .= Html::openElement(
'div', [
'id' =>
'user-tools' ] );
577 $html .= Html::rawElement(
'div', [
'id' =>
'personal' ],
578 Html::rawElement(
'h2', [],
579 Html::element(
'span', [], $dropdownHeader )
581 Html::rawElement(
'div', [
'id' =>
'personal-inner',
'class' =>
'dropdown' ],
582 $this->
getPortlet(
'personal', $personalTools, $headerMsg )
587 if ( !empty( $extraTools ) ) {
589 foreach ( $extraTools as $key => $item ) {
593 $html .= Html::rawElement(
595 [
'id' =>
'personal-extra',
'class' =>
'p-body' ],
596 Html::rawElement(
'ul', [], $iconList )
600 $html .= Html::closeElement(
'div' );
616 if ( $this->data[
'sitenotice'] ) {
617 $html .= Html::rawElement(
'div', [
'id' =>
'siteNotice' ], $this->
get(
'sitenotice' ) );
619 if ( $this->data[
'newtalk'] ) {
620 $html .= Html::rawElement(
'div', [
'class' =>
'usermessage' ], $this->
get(
'newtalk' ) );
634 $html .= Html::openElement(
'div', [
'id' =>
'contentSub' ] );
635 if ( $this->data[
'subtitle'] ) {
636 $html .= $this->
get(
'subtitle' );
638 if ( $this->data[
'undelete'] ) {
639 $html .= $this->
get(
'undelete' );
641 $html .= Html::closeElement(
'div' );
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' );
660 if ( $this->data[
'dataAfterContent'] ) {
661 $html .= $this->
get(
'dataAfterContent' );
663 $html .= Html::closeElement(
'div' );
680 $namespace =
$title->getNamespace();
682 $sortedPileOfTools = [
684 'page-primary' => [],
685 'page-secondary' => [],
687 'page-tertiary' => [],
693 $pileOfEditTools = [];
694 foreach ( $this->data[
'content_navigation'] as $navKey => $navBlock ) {
696 if ( $navKey ==
'namespaces' ) {
697 if ( $namespace < 0 && count( $navBlock ) < 2 ) {
699 $sortedPileOfTools[
'page-tertiary'] = $navBlock;
701 $sortedPileOfTools[
'namespaces'] = $navBlock;
703 } elseif ( $navKey ==
'variants' ) {
705 $sortedPileOfTools[
'variants'] = $navBlock;
707 $pileOfEditTools = array_merge( $pileOfEditTools, $navBlock );
713 if ( $namespace >= 0 ) {
715 'text' => $this->
getMsg(
'timeless-pagelog' )->text(),
717 [
'page' =>
$title->getPrefixedText() ]
725 'text' => $this->
getMsg(
'timeless-more' )->text(),
727 'class' =>
'dropdown-toggle'
729 if ( $this->data[
'language_urls'] !==
false || $sortedPileOfTools[
'variants']
730 || isset( $this->otherProjects ) ) {
732 'text' => $this->
getMsg(
'timeless-languages' )->escaped(),
733 'id' =>
'ca-languages',
734 'class' =>
'dropdown-toggle'
740 foreach ( $pileOfEditTools as $navKey => $navBlock ) {
743 if ( in_array( $navKey, [
747 $currentSet =
'namespaces';
748 } elseif ( in_array( $navKey, [
755 $currentSet =
'page-primary';
756 } elseif ( in_array( $navKey, [
763 $currentSet =
'page-secondary';
766 $currentSet =
'page-primary';
768 $sortedPileOfTools[$currentSet][$navKey] = $navBlock;
773 if ( $navKey ===
'contributions' ) {
774 $currentSet =
'page-primary';
775 } elseif ( in_array( $navKey, [
782 $currentSet =
'user';
783 } elseif ( in_array( $navKey, [
788 'recentchangeslinked',
793 $currentSet =
'page-tertiary';
794 } elseif ( in_array( $navKey, [
798 $currentSet =
'more';
800 $currentSet =
'general';
802 $sortedPileOfTools[$currentSet][$navKey] = $navBlock;
807 'proofreadPagePrevLink',
809 'proofreadPageScanLink',
810 'proofreadPageIndexLink',
811 'proofreadPageNextLink',
813 foreach ( $tabs as $tab ) {
814 if ( isset( $sortedPileOfTools[
'namespaces'][$tab] ) ) {
815 $toMove = $sortedPileOfTools[
'namespaces'][$tab];
816 unset( $sortedPileOfTools[
'namespaces'][$tab] );
819 $toMove[
'title'] = $toMove[
'text'];
821 if ( $tab ===
'proofreadPagePrevLink' ) {
823 $sortedPileOfTools[
'namespaces'] = array_merge(
825 $sortedPileOfTools[
'namespaces']
829 $sortedPileOfTools[
'namespaces'][$tab] = $toMove;
834 return $sortedPileOfTools;
850 $allCats = $skin->getOutput()->getCategoryLinks();
851 if ( !empty( $allCats ) ) {
852 if ( !empty( $allCats[
'normal'] ) ) {
853 $catHeader =
'categories';
857 'mw-normal-catlinks',
861 $catHeader =
'hidden-categories';
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';
871 $hiddenCatClass[] =
'mw-hidden-cats-hidden';
877 [
'hidden-categories', count( $allCats[
'hidden'] ) ]
882 if ( $catList !==
'' ) {
883 $html = $this->
getSidebarChunk(
'catlinks-sidebar', $catHeader, $catList );
899 protected function getCatList( $list, $id, $class, $message ) {
900 $html = Html::openElement(
'div', [
'id' =>
"sidebar-{$id}",
'class' => $class ] );
902 $makeLinkItem =
function ( $linkHtml ) {
903 return Html::rawElement(
'li', [], $linkHtml );
906 $categoryItems = array_map( $makeLinkItem, $list );
908 $categoriesHtml = Html::rawElement(
'ul',
910 implode(
'', $categoryItems )
913 $html .= $this->
getPortlet( $id, $categoriesHtml, $message );
915 $html .= Html::closeElement(
'div' );
930 if ( $this->pileOfTools[
'variants'] ) {
933 $this->pileOfTools[
'variants'],
935 [
'body-extra-classes' =>
'dropdown' ]
953 $variantsOnly =
false;
955 if ( $this->pileOfTools[
'variants'] ) {
958 $this->pileOfTools[
'variants']
961 $variantsOnly =
true;
963 if ( $this->data[
'language_urls'] !==
false ) {
966 $this->data[
'language_urls'] ?: [],
970 $variantsOnly =
false;
973 if ( isset( $this->otherProjects ) ) {
975 'wikibase-otherprojects',
976 $this->otherProjects[
'content']
979 $variantsOnly =
false;
987 $variantsOnly ? [
'variants-only' ] : []
1003 if ( $logo ===
null ) {
1009 if ( is_string( $logo ) ) {
1019 $width =
$file->getWidth();
1020 $height =
$file->getHeight();
1021 $bound = $width > $height ? $width : $height;
1028 $large = $doLarge ? 4 : 2;
1030 if ( $bound <= 165 ) {
1032 $logoData[
'width'] = $width;
1033 $logoData[
'height'] = $height;
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'] );
1042 $logoData[
'1x'] =
$file->getUrl();
1045 } elseif ( $bound >= 230 && $bound <= 330 ) {
1047 $logoData[
'width'] = $width / 2;
1048 $logoData[
'height'] = $height / 2;
1050 $logoData[
'1x'] =
$file->createThumb( $logoData[
'width'] );
1051 $logoData[
'1.5x'] =
$file->createThumb( $logoData[
'width'] * 1.5 );
1053 if ( $svg ||
$file->mustRender() ) {
1054 $logoData[
'2x'] =
$file->createThumb( $logoData[
'width'] * 2 );
1056 $logoData[
'2x'] =
$file->getUrl();
1062 $logoData[
'width'] = 155;
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 );
1069 if ( $svg || $logoData[
'width'] * 2 <= $width ) {
1070 $logoData[
'2x'] =
$file->createThumb( $logoData[
'width'] * $large );
1073 } elseif ( is_array( $logo ) ) {
1083 'alt' => $this->
getMsg(
'sitetitle' )->text(),
1085 'decoding' =>
'auto',
1086 'width' => $logoData[
'width'],
1087 'height' => $logoData[
'height'],
1090 if ( !isset( $logoData[
'1x'] ) && isset( $logoData[
'2x'] ) ) {
1092 $attribs[
'src'] = $logoData[
'2x'];
1096 $attribs[
'src'] = $logoData[
'1x'];
1099 unset( $logoData[
'1x'], $logoData[
'width'], $logoData[
'height'] );
1101 foreach ( $logoData as
$res =>
$path ) {
1102 if ( $srcset !=
'' ) {
1108 if ( $srcset !==
'' ) {
1109 $attribs[
'srcset'] = $srcset;
1113 return Html::element(
'img', $attribs );