17 use ProtectedHookAccessorTrait;
23 private $cachedTemplateData;
26 $this->skinContext = $skinContext;
32 private function getTemplateDataFooter(): array {
34 'info' => $this->formatFooterInfoData(
35 $this->getFooterInfoData()
37 'places' => $this->getSiteFooterLinks(),
39 $skin = $this->skinContext->getContextSource()->getSkin();
40 foreach ( $data as $key => $existingItems ) {
42 $this->getHookRunner()->onSkinAddFooterLinks( $skin, $key, $newItems );
43 foreach ( $newItems as $index => $linkHTML ) {
44 $data[ $key ][ $index ] = [
45 'id' =>
'footer-' . $key .
'-' . $index,
58 if ( $this->cachedTemplateData ) {
59 return $this->cachedTemplateData;
62 $footerData = $this->getTemplateDataFooter();
68 $this->skinContext->getMessageLocalizer()
72 $footerData[
'places'],
73 $this->skinContext->getMessageLocalizer()
80 $footerMenuData = $this->formatFooterDataForCurrentSpec( [
81 'data-info' => $footerInfoMenuData->getTemplateData(),
82 'data-places' => $footerSiteMenuData->getTemplateData(),
83 'data-icons' => $this->getFooterIcons(),
86 $this->cachedTemplateData = [
87 'data-info' => $footerMenuData[
'data-info'],
88 'data-places' => $footerMenuData[
'data-places'],
89 'data-icons' => $footerMenuData[
'data-icons']
91 return $this->cachedTemplateData;
104 private function getFooterInfoData(): array {
106 $skinContext = $this->skinContext;
111 $titleExists = $title && $title->exists();
113 $maxCredits = $config->get( MainConfigNames::MaxCredits );
114 $showCreditsIfMax = $config->get( MainConfigNames::ShowCreditsIfMax );
115 $useCredits = $titleExists
117 && $out->isRevisionCurrent()
118 && $maxCredits !== 0;
122 $article = Article::newFromWikiPage( $skinContext->
getWikiPage(), $ctx );
123 $action = Action::factory(
'credits', $article, $ctx );
126 '@phan-var CreditsAction $action';
128 'lastmod' => !$useCredits ? $this->lastModified() : null,
129 'numberofwatchingusers' => null,
130 'credits' => $useCredits && $action ?
131 $action->getCredits( $maxCredits, $showCreditsIfMax ) : null,
132 'renderedwith' => $this->renderedWith(),
135 'copyright' => $titleExists &&
136 $out->showsCopyright() ? $this->getCopyright() : null,
143 private function getCopyright() {
144 $copyright =
new SkinComponentCopyright( $this->skinContext );
145 return $copyright->getTemplateData()[
'html' ];
157 private function formatFooterInfoData( array $data ): array {
159 foreach ( $data as $key => $item ) {
161 $formattedData[ $key ] = [
162 'id' =>
'footer-info-' . $key,
167 return $formattedData;
176 private function getSiteFooterLinks(): array {
179 'privacy' => [
'privacy',
'privacypage' ],
180 'about' => [
'aboutsite',
'aboutpage' ],
181 'disclaimers' => [
'disclaimers',
'disclaimerpage' ]
183 $localizer = $this->skinContext->getMessageLocalizer();
185 foreach ( $siteLinks as $key => $siteLink ) {
188 if ( !$localizer->msg( $siteLink[0] )->inContentLanguage()->isDisabled() ) {
191 $title = Title::newFromText( $localizer->msg( $siteLink[1] )->inContentLanguage()->text() );
192 if ( $title !==
null ) {
193 $siteLinksData[$key] = [
194 'id' =>
"footer-places-$key",
195 'text' => $localizer->msg( $siteLink[0] )->text(),
196 'href' => $title->fixSpecialName()->getLinkURL()
201 return $siteLinksData;
216 if ( is_string( $icon ) ) {
219 $url = $icon[
'url'] ??
null;
220 unset( $icon[
'url'] );
223 if ( isset( $icon[
'sources'] ) ) {
224 foreach ( $icon[
'sources'] as
$source ) {
225 $sources .= Html::element(
'source',
$source );
227 unset( $icon[
'sources'] );
230 if ( isset( $icon[
'src'] ) && $withImage ===
'withImage' ) {
232 $icon[
'loading'] =
'lazy';
234 $html = Html::element(
'img', $icon );
236 $html = Html::rawElement(
'picture', [], $sources . $html );
239 $html = htmlspecialchars( $icon[
'alt'] ??
'' );
242 $html = Html::rawElement(
248 'cdx-button',
'cdx-button--fake-button',
249 'cdx-button--size-large',
'cdx-button--fake-button--enabled'
251 'target' => $config->get( MainConfigNames::ExternalLinkTarget ),
270 $config->
get( MainConfigNames::FooterIcons ) as $footerIconsKey => &$footerIconsBlock
272 if ( count( $footerIconsBlock ) > 0 ) {
273 $footericons[$footerIconsKey] = [];
274 foreach ( $footerIconsBlock as &$footerIcon ) {
275 if ( isset( $footerIcon[
'src'] ) ) {
276 if ( !isset( $footerIcon[
'width'] ) ) {
277 $footerIcon[
'width'] = 88;
279 if ( !isset( $footerIcon[
'height'] ) ) {
280 $footerIcon[
'height'] = 31;
289 if ( is_string( $footerIcon ) || isset( $footerIcon[
'src'] ) ) {
290 $footericons[$footerIconsKey][] = $footerIcon;
296 if ( !count( $footericons[$footerIconsKey] ) ) {
297 unset( $footericons[$footerIconsKey] );
311 private function getFooterIcons(): array {
313 $skinContext = $this->skinContext;
316 $footerIcons = self::getFooterIconsData(
320 if ( count( $footerIcons ) > 0 ) {
322 foreach ( $footerIcons as $blockName => $blockIcons ) {
324 foreach ( $blockIcons as $icon ) {
325 $html .= self::makeFooterIconHTML(
332 $block = htmlspecialchars( $blockName );
335 'id' =>
'footer-' . $block .
'ico',
337 'class' => [
'noprint' ],
344 if ( count( $icons ) > 0 ) {
345 $dataIcons =
new SkinComponentMenu(
348 $this->skinContext->getMessageLocalizer(),
355 return $dataIcons ? $dataIcons->getTemplateData() : [];
369 private function formatFooterDataForCurrentSpec( array $data ): array {
371 foreach ( $data as $key => $item ) {
372 unset( $item[
'html-tooltip'] );
373 unset( $item[
'html-items'] );
374 unset( $item[
'html-after-portal'] );
375 unset( $item[
'html-before-portal'] );
376 unset( $item[
'label'] );
377 unset( $item[
'class'] );
378 foreach ( $item[
'array-items'] ?? [] as $index => $arrayItem ) {
379 unset( $item[
'array-items'][$index][
'html-item'] );
381 $formattedData[$key] = $item;
382 $formattedData[$key][
'className'] = $key ===
'data-icons' ?
'noprint' :
null;
384 return $formattedData;
393 private function lastModified() {
394 $skinContext = $this->skinContext;
396 $timestamp = $out->getRevisionTimestamp();
401 if ( $timestamp ===
null ) {
402 $revId = $out->getRevisionId();
403 if ( $revId !==
null ) {
404 $timestamp = MediaWikiServices::getInstance()->getRevisionLookup()->getTimestampFromId( $revId );
408 $lastModified =
new SkinComponentLastModified(
413 return $lastModified->getTemplateData()[
'text'];
422 private function renderedWith() {
423 $skinContext = $this->skinContext;
425 $useParsoid = $out->getOutputFlag( ParserOutputFlags::USE_PARSOID );
427 $renderedWith =
new SkinComponentRenderedWith(
428 $this->skinContext->getMessageLocalizer(),
432 return $renderedWith->getTemplateData()[
'text'];