21 use Wikimedia\WrappedString;
22 use Wikimedia\WrappedStringList;
38 public function getMsg( $name, ...$params ) {
39 return $this->
getSkin()->msg( $name, ...$params );
42 function msg( $str ) {
43 echo $this->
getMsg( $str )->escaped();
51 echo $this->
getMsg( $str )->parseAsBlock();
63 if ( isset( $this->data[
'nav_urls'][
'whatlinkshere'] )
64 && $this->data[
'nav_urls'][
'whatlinkshere']
66 $toolbox[
'whatlinkshere'] = $this->data[
'nav_urls'][
'whatlinkshere'];
67 $toolbox[
'whatlinkshere'][
'id'] =
't-whatlinkshere';
69 if ( isset( $this->data[
'nav_urls'][
'recentchangeslinked'] )
70 && $this->data[
'nav_urls'][
'recentchangeslinked']
72 $toolbox[
'recentchangeslinked'] = $this->data[
'nav_urls'][
'recentchangeslinked'];
73 $toolbox[
'recentchangeslinked'][
'msg'] =
'recentchangeslinked-toolbox';
74 $toolbox[
'recentchangeslinked'][
'id'] =
't-recentchangeslinked';
75 $toolbox[
'recentchangeslinked'][
'rel'] =
'nofollow';
77 if ( isset( $this->data[
'feeds'] ) && $this->data[
'feeds'] ) {
78 $toolbox[
'feeds'][
'id'] =
'feedlinks';
79 $toolbox[
'feeds'][
'links'] = [];
80 foreach ( $this->data[
'feeds'] as $key => $feed ) {
81 $toolbox[
'feeds'][
'links'][$key] = $feed;
82 $toolbox[
'feeds'][
'links'][$key][
'id'] =
"feed-$key";
83 $toolbox[
'feeds'][
'links'][$key][
'rel'] =
'alternate';
84 $toolbox[
'feeds'][
'links'][$key][
'type'] =
"application/{$key}+xml";
85 $toolbox[
'feeds'][
'links'][$key][
'class'] =
'feedlink';
88 foreach ( [
'contributions',
'log',
'blockip',
'emailuser',
'mute',
89 'userrights',
'upload',
'specialpages' ] as $special
91 if ( isset( $this->data[
'nav_urls'][$special] ) && $this->data[
'nav_urls'][$special] ) {
92 $toolbox[$special] = $this->data[
'nav_urls'][$special];
93 $toolbox[$special][
'id'] =
"t-$special";
96 if ( isset( $this->data[
'nav_urls'][
'print'] ) && $this->data[
'nav_urls'][
'print'] ) {
97 $toolbox[
'print'] = $this->data[
'nav_urls'][
'print'];
98 $toolbox[
'print'][
'id'] =
't-print';
99 $toolbox[
'print'][
'rel'] =
'alternate';
100 $toolbox[
'print'][
'msg'] =
'printableversion';
102 if ( isset( $this->data[
'nav_urls'][
'permalink'] ) && $this->data[
'nav_urls'][
'permalink'] ) {
103 $toolbox[
'permalink'] = $this->data[
'nav_urls'][
'permalink'];
104 $toolbox[
'permalink'][
'id'] =
't-permalink';
106 if ( isset( $this->data[
'nav_urls'][
'info'] ) && $this->data[
'nav_urls'][
'info'] ) {
107 $toolbox[
'info'] = $this->data[
'nav_urls'][
'info'];
108 $toolbox[
'info'][
'id'] =
't-info';
113 Hooks::run(
'BaseTemplateToolbox', [ &$template, &$toolbox ] );
128 $personal_tools = [];
129 foreach ( $this->
get(
'personal_urls' ) as $key => $plink ) {
130 # The class on a personal_urls item is meant to go on the <a> instead
131 # of the <li> so we have to use a single item "links" array instead
132 # of using most of the personal_url's keys directly.
135 [
'single-id' =>
"pt-$key" ],
139 if ( isset( $plink[
'active'] ) ) {
140 $ptool[
'active'] = $plink[
'active'];
142 foreach ( [
'href',
'class',
'text',
'dir',
'data',
'exists' ] as $k ) {
143 if ( isset( $plink[$k] ) ) {
144 $ptool[
'links'][0][$k] = $plink[$k];
147 $personal_tools[$key] = $ptool;
149 return $personal_tools;
154 $sidebar = $this->data[
'sidebar'];
155 if ( !isset( $sidebar[
'SEARCH'] ) ) {
156 $sidebar[
'SEARCH'] =
true;
158 if ( !isset( $sidebar[
'TOOLBOX'] ) ) {
159 $sidebar[
'TOOLBOX'] =
true;
161 if ( !isset( $sidebar[
'LANGUAGES'] ) ) {
162 $sidebar[
'LANGUAGES'] =
true;
165 if ( !isset( $options[
'search'] ) || $options[
'search'] !==
true ) {
166 unset( $sidebar[
'SEARCH'] );
168 if ( isset( $options[
'toolbox'] ) && $options[
'toolbox'] ===
false ) {
169 unset( $sidebar[
'TOOLBOX'] );
171 if ( isset( $options[
'languages'] ) && $options[
'languages'] ===
false ) {
172 unset( $sidebar[
'LANGUAGES'] );
176 foreach ( $sidebar as $boxName =>
$content ) {
180 switch ( $boxName ) {
185 'header' => $this->
getMsg(
'search' )->text(),
186 'generated' =>
false,
191 $msgObj = $this->
getMsg(
'toolbox' );
194 'header' => $msgObj->exists() ? $msgObj->text() :
'toolbox',
195 'generated' =>
false,
200 if ( $this->data[
'language_urls'] !==
false ) {
201 $msgObj = $this->
getMsg(
'otherlanguages' );
204 'header' => $msgObj->exists() ? $msgObj->text() :
'otherlanguages',
205 'generated' =>
false,
206 'content' => $this->data[
'language_urls'] ?: [],
211 $msgObj = $this->
getMsg( $boxName );
213 'id' =>
"p-$boxName",
214 'header' => $msgObj->exists() ? $msgObj->text() : $boxName,
223 $hookContents =
null;
224 if ( isset( $boxes[
'TOOLBOX'] ) ) {
230 Hooks::run(
'SkinTemplateToolboxEnd', [ &$template,
true ] );
231 $hookContents = ob_get_contents();
233 if ( !trim( $hookContents ) ) {
234 $hookContents =
null;
239 if ( isset( $options[
'htmlOnly'] ) && $options[
'htmlOnly'] ===
true ) {
240 foreach ( $boxes as $boxName => $box ) {
241 if ( is_array( $box[
'content'] ) ) {
243 foreach ( $box[
'content'] as $key => $val ) {
247 if ( $hookContents ) {
252 $boxes[$boxName][
'content'] =
$content;
255 } elseif ( $hookContents ) {
256 $boxes[
'TOOLBOXEND'] = [
257 'id' =>
'p-toolboxend',
258 'header' => $boxes[
'TOOLBOX'][
'header'],
259 'generated' =>
false,
260 'content' =>
"<ul>{$hookContents}</ul>",
264 foreach ( $boxes as $key => $box ) {
265 if ( $key ===
'TOOLBOXEND' ) {
268 $boxes2[$key] = $box;
269 if ( $key ===
'TOOLBOX' ) {
270 $boxes2[
'TOOLBOXEND'] = $boxes[
'TOOLBOXEND'];
301 $html = Html::rawElement(
303 [
'class' => [
'after-portlet',
'after-portlet-' . $name ] ],
364 $text = $item[
'text'] ??
wfMessage( $item[
'msg'] ?? $key )->text();
366 $html = htmlspecialchars( $text );
368 if ( isset( $options[
'text-wrapper'] ) ) {
369 $wrapper = $options[
'text-wrapper'];
370 if ( isset( $wrapper[
'tag'] ) ) {
371 $wrapper = [ $wrapper ];
373 while ( count( $wrapper ) > 0 ) {
374 $element = array_pop( $wrapper );
375 $html = Html::rawElement( $element[
'tag'], $element[
'attributes'] ??
null, $html );
379 if ( isset( $item[
'href'] ) || isset( $options[
'link-fallback'] ) ) {
381 foreach ( [
'single-id',
'text',
'msg',
'tooltiponly',
'context',
'primary',
382 'tooltip-params',
'exists' ] as $k ) {
386 if ( isset( $attrs[
'data'] ) ) {
387 foreach ( $attrs[
'data'] as $key => $value ) {
388 $attrs[
'data-' . $key ] = $value;
390 unset( $attrs[
'data' ] );
393 if ( isset( $item[
'id'] ) && !isset( $item[
'single-id'] ) ) {
394 $item[
'single-id'] = $item[
'id'];
398 if ( isset( $item[
'tooltip-params'] ) ) {
399 $tooltipParams = $item[
'tooltip-params'];
402 if ( isset( $item[
'single-id'] ) ) {
403 $tooltipOption = isset( $item[
'exists'] ) && $item[
'exists'] ===
false ?
'nonexisting' :
null;
405 if ( isset( $item[
'tooltiponly'] ) && $item[
'tooltiponly'] ) {
416 if ( isset( $tip[
'title'] ) && $tip[
'title'] !==
false ) {
417 $attrs[
'title'] = $tip[
'title'];
419 if ( isset( $tip[
'accesskey'] ) && $tip[
'accesskey'] !==
false ) {
420 $attrs[
'accesskey'] = $tip[
'accesskey'];
424 if ( isset( $options[
'link-class'] ) ) {
425 if ( isset( $attrs[
'class'] ) ) {
426 $attrs[
'class'] .=
" {$options['link-class']}";
428 $attrs[
'class'] = $options[
'link-class'];
431 $html = Html::rawElement( isset( $attrs[
'href'] )
433 : $options[
'link-fallback'], $attrs, $html );
473 unset( $item[
'redundant'] );
475 if ( isset( $item[
'links'] ) ) {
477 foreach ( $item[
'links'] as $linkKey => $link ) {
478 $links[] = $this->
makeLink( $linkKey, $link, $options );
480 $html = implode(
' ', $links );
484 foreach ( [
'id',
'class',
'active',
'tag',
'itemtitle' ] as $k ) {
487 if ( isset( $item[
'id'] ) && !isset( $item[
'single-id'] ) ) {
491 $link[
'single-id'] = $item[
'id'];
493 if ( isset( $link[
'link-class'] ) ) {
496 $link[
'class'] = $link[
'link-class'];
497 unset( $link[
'link-class'] );
499 $html = $this->
makeLink( $key, $link, $options );
503 foreach ( [
'id',
'class' ] as $attr ) {
504 if ( isset( $item[$attr] ) ) {
505 $attrs[$attr] = $item[$attr];
508 if ( isset( $item[
'active'] ) && $item[
'active'] ) {
509 if ( !isset( $attrs[
'class'] ) ) {
510 $attrs[
'class'] =
'';
512 $attrs[
'class'] .=
' active';
513 $attrs[
'class'] = trim( $attrs[
'class'] );
515 if ( isset( $item[
'itemtitle'] ) ) {
516 $attrs[
'title'] = $item[
'itemtitle'];
518 return Html::rawElement( $options[
'tag'] ??
'li', $attrs, $html );
525 'placeholder' =>
wfMessage(
'searchsuggest-search' )->text(),
528 return Html::element(
'input', $realAttrs );
538 'value' =>
wfMessage( $mode ==
'go' ?
'searcharticle' :
'searchbutton' )->text(),
540 $realAttrs = array_merge(
545 return Html::element(
'input', $realAttrs );
551 $buttonAttrs = array_merge(
556 unset( $buttonAttrs[
'src'] );
557 unset( $buttonAttrs[
'alt'] );
558 unset( $buttonAttrs[
'width'] );
559 unset( $buttonAttrs[
'height'] );
561 'src' => $attrs[
'src'],
562 'alt' => $attrs[
'alt'] ??
wfMessage(
'searchbutton' )->text(),
563 'width' => $attrs[
'width'] ??
null,
564 'height' => $attrs[
'height'] ??
null,
566 return Html::rawElement(
'button', $buttonAttrs, Html::element(
'img', $imgAttrs ) );
568 throw new MWException(
'Unknown mode passed to BaseTemplate::makeSearchButton' );
582 $footerlinks = $this->
get(
'footerlinks' );
585 $validFooterLinks = [];
586 foreach ( $footerlinks as $category => $links ) {
587 $validFooterLinks[$category] = [];
588 foreach ( $links as $link ) {
589 if ( isset( $this->data[$link] ) && $this->data[$link] ) {
590 $validFooterLinks[$category][] = $link;
593 if ( count( $validFooterLinks[$category] ) <= 0 ) {
594 unset( $validFooterLinks[$category] );
598 if ( $option ==
'flat' ) {
600 $validFooterLinks = array_merge( ...array_values( $validFooterLinks ) );
603 return $validFooterLinks;
620 $footericons = $this->
get(
'footericons' );
622 if ( $option ==
'icononly' ) {
624 foreach ( $footericons as &$footerIconsBlock ) {
625 foreach ( $footerIconsBlock as $footerIconKey => $footerIcon ) {
626 if ( !is_string( $footerIcon ) && !isset( $footerIcon[
'src'] ) ) {
627 unset( $footerIconsBlock[$footerIconKey] );
632 foreach ( $footericons as $footerIconsKey => &$footerIconsBlock ) {
633 if ( count( $footerIconsBlock ) <= 0 ) {
634 unset( $footericons[$footerIconsKey] );
637 } elseif ( $option ==
'nocopyright' ) {
638 unset( $footericons[
'copyright'][
'copyright'] );
639 if ( count( $footericons[
'copyright'] ) <= 0 ) {
640 unset( $footericons[
'copyright'] );
656 protected function getFooter( $iconStyle =
'icononly', $linkStyle =
'flat' ) {
662 if ( count( $validFooterIcons ) + count( $validFooterLinks ) > 0 ) {
663 $html .= Html::openElement(
'div', [
664 'id' =>
'footer-bottom',
665 'role' =>
'contentinfo',
666 'lang' => $this->
get(
'userlang' ),
667 'dir' => $this->
get(
'dir' )
669 $footerEnd = Html::closeElement(
'div' );
673 foreach ( $validFooterIcons as $blockName => $footerIcons ) {
674 $html .= Html::openElement(
'div', [
675 'id' => Sanitizer::escapeIdForAttribute(
"f-{$blockName}ico" ),
676 'class' =>
'footer-icons'
678 foreach ( $footerIcons as $icon ) {
679 $html .= $this->
getSkin()->makeFooterIcon( $icon );
681 $html .= Html::closeElement(
'div' );
683 if ( count( $validFooterLinks ) > 0 ) {
684 $html .= Html::openElement(
'ul', [
'id' =>
'f-list',
'class' =>
'footer-places' ] );
685 foreach ( $validFooterLinks as $aLink ) {
686 $html .= Html::rawElement(
688 [
'id' => Sanitizer::escapeIdForAttribute( $aLink ) ],
692 $html .= Html::closeElement(
'ul' );
695 $html .= $this->
getClear() . $footerEnd;
707 return Html::element(
'div', [
'class' =>
'visualClear' ] );
726 $out =
"<div class=\"mw-indicators mw-body-content\">\n";
727 foreach ( $this->data[
'indicators'] as $id =>
$content ) {
728 $out .= Html::rawElement(
731 'id' => Sanitizer::escapeIdForAttribute(
"mw-indicator-$id" ),
732 'class' =>
'mw-indicator',
757 return WrappedString::join(
"\n", [
760 $this->
get(
'bottomscripts' ),
761 $this->
get(
'reporttime' )