MediaWiki REL1_41
SkinComponentFooter.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Skin;
4
5use Action;
6use Article;
13
16 private $skinContext;
17
21 public function __construct( SkinComponentRegistryContext $skinContext ) {
22 $this->skinContext = $skinContext;
23 }
24
30 private function getTemplateDataFooter(): array {
31 $data = [
32 'info' => $this->formatFooterInfoData(
33 $this->getFooterInfoData()
34 ),
35 'places' => $this->getSiteFooterLinks(),
36 ];
37 foreach ( $data as $key => $existingItems ) {
38 $newItems = [];
39 $this->skinContext->runHook( 'onSkinAddFooterLinks', [ $key, &$newItems ] );
40 // @phan-suppress-next-line PhanEmptyForeach False positive as hooks modify
41 foreach ( $newItems as $index => $linkHTML ) {
42 $data[ $key ][ $index ] = [
43 'id' => 'footer-' . $key . '-' . $index,
44 'html' => $linkHTML,
45 ];
46 }
47 }
48 return $data;
49 }
50
54 public function getTemplateData(): array {
55 $footerData = $this->getTemplateDataFooter();
56
57 // Create the menu components from the footer data.
58 $footerInfoMenuData = new SkinComponentMenu(
59 'footer-info',
60 $footerData['info'],
61 $this->skinContext->getMessageLocalizer()
62 );
63 $footerSiteMenuData = new SkinComponentMenu(
64 'footer-places',
65 $footerData['places'],
66 $this->skinContext->getMessageLocalizer()
67 );
68
69 // To conform the footer menu data to the current SkinMustache specification,
70 // run the derived data through a cleanup function to unset unexpected data properties
71 // until the spec is updated to reflect the new properties introduced by the menu component.
72 // See https://www.mediawiki.org/wiki/Manual:SkinMustache.php#DataFooter
73 $footerMenuData = [];
74 $footerMenuData['data-info'] = $footerInfoMenuData->getTemplateData();
75 $footerMenuData['data-places'] = $footerSiteMenuData->getTemplateData();
76 $footerMenuData['data-icons'] = $this->getFooterIcons();
77 $footerMenuData = $this->formatFooterDataForCurrentSpec( $footerMenuData );
78
79 return [
80 'data-info' => $footerMenuData['data-info'],
81 'data-places' => $footerMenuData['data-places'],
82 'data-icons' => $footerMenuData['data-icons']
83 ];
84 }
85
96 private function getFooterInfoData(): array {
97 $action = null;
98 $skinContext = $this->skinContext;
99 $out = $skinContext->getOutput();
100 $ctx = $skinContext->getContextSource();
101 $title = $out->getTitle();
102 $titleExists = $title && $title->exists();
103 $config = $skinContext->getConfig();
104 $maxCredits = $config->get( MainConfigNames::MaxCredits );
105 $showCreditsIfMax = $config->get( MainConfigNames::ShowCreditsIfMax );
106 $useCredits = $titleExists
107 && $out->isArticle()
108 && $out->isRevisionCurrent()
109 && $maxCredits !== 0;
110
112 if ( $useCredits ) {
113 $article = Article::newFromWikiPage( $skinContext->getWikiPage(), $ctx );
114 $action = Action::factory( 'credits', $article, $ctx );
115 }
116
117 '@phan-var CreditsAction $action';
118 return [
119 'lastmod' => !$useCredits ? $this->lastModified() : null,
120 'numberofwatchingusers' => null,
121 'credits' => $useCredits && $action ?
122 $action->getCredits( $maxCredits, $showCreditsIfMax ) : null,
123 'copyright' => $titleExists &&
124 $out->showsCopyright() ? $this->getCopyright() : null,
125 ];
126 }
127
131 private function getCopyright() {
132 $copyright = new SkinComponentCopyright( $this->skinContext );
133 return $copyright->getTemplateData()[ 'html' ];
134 }
135
145 private function formatFooterInfoData( array $data ): array {
146 $formattedData = [];
147 foreach ( $data as $key => $item ) {
148 if ( $item ) {
149 $formattedData[ $key ] = [
150 'id' => 'footer-info-' . $key,
151 'html' => $item
152 ];
153 }
154 }
155 return $formattedData;
156 }
157
164 private function getSiteFooterLinks(): array {
165 $siteLinksData = [];
166 $siteLinks = [
167 'privacy' => [ 'privacy', 'privacypage' ],
168 'about' => [ 'aboutsite', 'aboutpage' ],
169 'disclaimers' => [ 'disclaimers', 'disclaimerpage' ]
170 ];
171 $localizer = $this->skinContext->getMessageLocalizer();
172
173 foreach ( $siteLinks as $key => $siteLink ) {
174 // Check if the link description has been disabled in the default language.
175 // If disabled, it is disabled for all languages.
176 if ( !$localizer->msg( $siteLink[0] )->inContentLanguage()->isDisabled() ) {
177 // Display the link for the user, described in their language (which may or may not be the same as the
178 // default language), but make the link target be the one site-wide page.
179 $title = Title::newFromText( $localizer->msg( $siteLink[1] )->inContentLanguage()->text() );
180 if ( $title !== null ) {
181 $siteLinksData[$key] = [
182 'id' => "footer-places-$key",
183 'text' => $localizer->msg( $siteLink[0] )->text(),
184 'href' => $title->fixSpecialName()->getLinkURL()
185 ];
186 }
187 }
188 }
189 return $siteLinksData;
190 }
191
203 public static function makeFooterIconHTML( Config $config, $icon, string $withImage = 'withImage' ): string {
204 if ( is_string( $icon ) ) {
205 $html = $icon;
206 } else { // Assuming array
207 $url = $icon['url'] ?? null;
208 unset( $icon['url'] );
209 if ( isset( $icon['src'] ) && $withImage === 'withImage' ) {
210 // Lazy-load footer icons, since they're not part of the printed view.
211 $icon['loading'] = 'lazy';
212 // do this the lazy way, just pass icon data as an attribute array
213 $html = Html::element( 'img', $icon );
214 } else {
215 $html = htmlspecialchars( $icon['alt'] ?? '' );
216 }
217 if ( $url ) {
218 $html = Html::rawElement( 'a', [
219 'href' => $url,
220 'target' => $config->get( MainConfigNames::ExternalLinkTarget ),
221 ],
222 $html );
223 }
224 }
225 return $html;
226 }
227
235 public static function getFooterIconsData( Config $config ) {
236 $footericons = [];
237 foreach (
238 $config->get( MainConfigNames::FooterIcons ) as $footerIconsKey => &$footerIconsBlock
239 ) {
240 if ( count( $footerIconsBlock ) > 0 ) {
241 $footericons[$footerIconsKey] = [];
242 foreach ( $footerIconsBlock as &$footerIcon ) {
243 if ( isset( $footerIcon['src'] ) ) {
244 if ( !isset( $footerIcon['width'] ) ) {
245 $footerIcon['width'] = 88;
246 }
247 if ( !isset( $footerIcon['height'] ) ) {
248 $footerIcon['height'] = 31;
249 }
250 }
251
252 // Only output icons which have an image.
253 // For historic reasons this mimics the `icononly` option
254 // for BaseTemplate::getFooterIcons.
255 // In some cases the icon may be an empty array.
256 // Filter these out. (See T269776)
257 if ( is_string( $footerIcon ) || isset( $footerIcon['src'] ) ) {
258 $footericons[$footerIconsKey][] = $footerIcon;
259 }
260 }
261
262 // If no valid icons with images were added, unset the parent array
263 // Should also prevent empty arrays from when no copyright is set.
264 if ( !count( $footericons[$footerIconsKey] ) ) {
265 unset( $footericons[$footerIconsKey] );
266 }
267 }
268 }
269 return $footericons;
270 }
271
279 private function getFooterIcons(): array {
280 $dataIcons = [];
281 $skinContext = $this->skinContext;
282 // If footer icons are enabled append to the end of the rows
283 $footerIcons = $skinContext->getFooterIcons();
284
285 if ( count( $footerIcons ) > 0 ) {
286 $icons = [];
287 foreach ( $footerIcons as $blockName => $blockIcons ) {
288 $html = '';
289 foreach ( $blockIcons as $icon ) {
290 $html .= $skinContext->makeFooterIcon( $icon );
291 }
292 // For historic reasons this mimics the `icononly` option
293 // for BaseTemplate::getFooterIcons. Empty rows should not be output.
294 if ( $html ) {
295 $block = htmlspecialchars( $blockName );
296 $icons[$block] = [
297 'name' => $block,
298 'id' => 'footer-' . $block . 'ico',
299 'html' => $html,
300 'class' => [ 'noprint' ],
301 ];
302 }
303 }
304
305 // Empty rows should not be output.
306 // This is how Vector has behaved historically but we can revisit later if necessary.
307 if ( count( $icons ) > 0 ) {
308 $dataIcons = new SkinComponentMenu(
309 'footer-icons',
310 $icons,
311 $this->skinContext->getMessageLocalizer(),
312 '',
313 []
314 );
315 }
316 }
317
318 return $dataIcons ? $dataIcons->getTemplateData() : [];
319 }
320
332 private function formatFooterDataForCurrentSpec( array $data ): array {
333 $formattedData = [];
334 foreach ( $data as $key => $item ) {
335 unset( $item['html-tooltip'] );
336 unset( $item['html-items'] );
337 unset( $item['html-after-portal'] );
338 unset( $item['html-before-portal'] );
339 unset( $item['label'] );
340 unset( $item['class'] );
341 foreach ( $item['array-items'] ?? [] as $index => $arrayItem ) {
342 unset( $item['array-items'][$index]['html-item'] );
343 }
344 $formattedData[$key] = $item;
345 $formattedData[$key]['className'] = $key === 'data-icons' ? 'noprint' : null;
346 }
347 return $formattedData;
348 }
349
356 private function lastModified() {
357 $skinContext = $this->skinContext;
358 $out = $skinContext->getOutput();
359 $timestamp = $out->getRevisionTimestamp();
360
361 // No cached timestamp, load it from the database
362 // TODO: This code shouldn't be necessary, revision ID should always be available
363 // Move this logic to OutputPage::getRevisionTimestamp if needed.
364 if ( $timestamp === null ) {
365 $revId = $out->getRevisionId();
366 if ( $revId !== null ) {
367 $timestamp = MediaWikiServices::getInstance()->getRevisionLookup()->getTimestampFromId( $revId );
368 }
369 }
370
371 $lastModified = new SkinComponentLastModified(
372 $skinContext,
373 $timestamp
374 );
375
376 return $lastModified->getTemplateData()['text'];
377 }
378}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:88
Actions are things which can be done to pages (edit, delete, rollback, etc).
Definition Action.php:49
Legacy class representing an editable page and handling UI for some page actions.
Definition Article.php:61
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
getTemplateData()
This returns all the data that is needed to the component.Returned array must be serialized....
__construct(SkinComponentRegistryContext $skinContext)
static makeFooterIconHTML(Config $config, $icon, string $withImage='withImage')
Renders a $wgFooterIcons icon according to the method's arguments.
static getFooterIconsData(Config $config)
Get data representation of icons.
makeFooterIcon( $icon, $withImage='withImage')
Renders a $wgFooterIcons icon according to the method's arguments It exists to support skins overridi...
getConfig()
Returns the config needed for the component.Config
getFooterIcons()
Temporarily allows access to Skin method.
Represents a title within MediaWiki.
Definition Title.php:76
Interface for configuration instances.
Definition Config.php:32
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...