MediaWiki  master
SkinMustache.php
Go to the documentation of this file.
1 <?php
20 use Wikimedia\WrappedStringList;
21 
26 class SkinMustache extends SkinTemplate {
30  private $templateParser = null;
31 
40  protected function getPortletData( $name, array $items ) {
41  // Monobook and Vector historically render this portal as an element with ID p-cactions
42  // This inconsistency is regretful from a code point of view
43  // However this ensures compatibility with gadgets.
44  // In future we should port p-#cactions to #p-actions and drop this rename.
45  if ( $name === 'actions' ) {
46  $name = 'cactions';
47  }
48 
49  // user-menu is the new personal tools, without the notifications.
50  // A lot of user code and gadgets relies on it being named personal.
51  // This allows it to function as a drop-in replacement.
52  if ( $name === 'user-menu' ) {
53  $name = 'personal';
54  }
55 
56  $id = Sanitizer::escapeIdForAttribute( "p-$name" );
57  $data = [
58  'id' => $id,
59  'class' => 'mw-portlet ' . Sanitizer::escapeClass( "mw-portlet-$name" ),
60  'html-tooltip' => Linker::tooltip( $id ),
61  'html-items' => '',
62  // Will be populated by SkinAfterPortlet hook.
63  'html-after-portal' => '',
64  ];
65  // Run the SkinAfterPortlet
66  // hook and if content is added appends it to the html-after-portal
67  // for output.
68  // Currently in production this supports the wikibase 'edit' link.
69  $content = $this->getAfterPortlet( $name );
70  if ( $content !== '' ) {
71  $data['html-after-portal'] = Html::rawElement(
72  'div',
73  [
74  'class' => [
75  'after-portlet',
76  Sanitizer::escapeClass( "after-portlet-$name" ),
77  ],
78  ],
79  $content
80  );
81  }
82 
83  foreach ( $items as $key => $item ) {
84  $data['html-items'] .= $this->makeListItem( $key, $item );
85  }
86 
87  $data['label'] = $this->getPortletLabel( $name );
88  $data['class'] .= ( count( $items ) === 0 && $content === '' )
89  ? ' emptyPortlet' : '';
90  return $data;
91  }
92 
97  private function getPortletLabel( $name ) {
98  // For historic reasons for some menu items,
99  // there is no language key corresponding with its menu key.
100  $mappings = [
101  'tb' => 'toolbox',
102  'personal' => 'personaltools',
103  'lang' => 'otherlanguages',
104  ];
105  $msgObj = $this->msg( $mappings[ $name ] ?? $name );
106  // If no message exists fallback to plain text (T252727)
107  $labelText = $msgObj->exists() ? $msgObj->text() : $name;
108  return $labelText;
109  }
110 
118  protected function getTemplateParser() {
119  if ( $this->templateParser === null ) {
120  $this->templateParser = new TemplateParser( $this->options['templateDirectory'] );
121  }
122  return $this->templateParser;
123  }
124 
131  public function generateHTML() {
132  $this->setupTemplateContext();
133  $out = $this->getOutput();
134  $tp = $this->getTemplateParser();
135  $template = $this->options['template'] ?? 'skin';
136  $data = $this->getTemplateData();
137 
138  // T259955: OutputPage::headElement must be called last (after getTemplateData)
139  // as it calls OutputPage::getRlClient, which freezes the ResourceLoader
140  // modules queue for the current page load.
141  $html = $out->headElement( $this );
142 
143  $html .= $tp->processTemplate( $template, $data );
144  $html .= $this->tailElement( $out );
145  return $html;
146  }
147 
166  public function getTemplateData() {
167  $out = $this->getOutput();
168  $printSource = Html::rawElement( 'div', [ 'class' => 'printfooter' ], $this->printSource() );
169  $bodyContent = $out->getHTML() . "\n" . $printSource;
170 
171  $newTalksHtml = $this->getNewtalks() ?: null;
172 
173  $data = [
174  'data-logos' => $this->getLogoData(),
175  // Array objects
176  'array-indicators' => $this->getIndicatorsData( $out->getIndicators() ),
177  // Data objects
178  'data-search-box' => $this->buildSearchProps(),
179  // HTML strings
180  'html-site-notice' => $this->getSiteNotice() ?: null,
181  'html-user-message' => $newTalksHtml ?
182  Html::rawElement( 'div', [ 'class' => 'usermessage' ], $newTalksHtml ) : null,
183  'html-title' => $out->getPageTitle(),
184  'html-subtitle' => $this->prepareSubtitle(),
185  'html-body-content' => $this->wrapHTML( $out->getTitle(), $bodyContent ),
186  'html-categories' => $this->getCategories(),
187  'html-after-content' => $this->afterContentHook(),
188  'html-undelete-link' => $this->prepareUndeleteLink(),
189  'html-user-language-attributes' => $this->prepareUserLanguageAttributes(),
190 
191  // links
192  'link-mainpage' => Title::newMainPage()->getLocalUrl(),
193  ];
194 
195  foreach ( $this->options['messages'] ?? [] as $message ) {
196  $data["msg-{$message}"] = $this->msg( $message )->text();
197  }
198  return $data + $this->getPortletsTemplateData() + $this->getFooterTemplateData();
199  }
200 
204  private function getPortletsTemplateData() {
205  $portlets = [];
206  $contentNavigation = $this->buildContentNavigationUrls();
207  $sidebar = [];
208  $sidebarData = $this->buildSidebar();
209  foreach ( $sidebarData as $name => $items ) {
210  if ( is_array( $items ) ) {
211  // Numeric strings gets an integer when set as key, cast back - T73639
212  $name = (string)$name;
213  switch ( $name ) {
214  // ignore search
215  case 'SEARCH':
216  break;
217  // Map toolbox to `tb` id.
218  case 'TOOLBOX':
219  $sidebar[] = $this->getPortletData( 'tb', $items );
220  break;
221  // Languages is no longer be tied to sidebar
222  case 'LANGUAGES':
223  // The language portal will be added provided either
224  // languages exist or there is a value in html-after-portal
225  // for example to show the add language wikidata link (T252800)
226  $portal = $this->getPortletData( 'lang', $items );
227  if ( count( $items ) || $portal['html-after-portal'] ) {
228  $portlets['data-languages'] = $portal;
229  }
230  break;
231  default:
232  $sidebar[] = $this->getPortletData( $name, $items );
233  break;
234  }
235  }
236  }
237 
238  foreach ( $contentNavigation as $name => $items ) {
239  if ( $name === 'user-menu' ) {
240  $items = $this->getPersonalToolsForMakeListItem( $items );
241  }
242 
243  $portlets['data-' . $name] = $this->getPortletData( $name, $items );
244  }
245 
246  // A menu that includes the notifications. This will be deprecated in future versions
247  // of the skin API spec.
248  $portlets['data-personal'] = $this->getPortletData(
249  'personal',
251  $this->insertNotificationsIntoPersonalTools( $contentNavigation )
252  )
253  );
254 
255  return [
256  'data-portlets' => $portlets,
257  'data-portlets-sidebar' => [
258  'data-portlets-first' => $sidebar[0] ?? null,
259  'array-portlets-rest' => array_slice( $sidebar, 1 ),
260  ],
261  ];
262  }
263 
268  private function getFooterTemplateData() : array {
269  $data = [];
270  foreach ( $this->getFooterLinks() as $category => $links ) {
271  $items = [];
272  $rowId = "footer-$category";
273 
274  foreach ( $links as $key => $link ) {
275  // Link may be null. If so don't include it.
276  if ( $link ) {
277  $items[] = [
278  // Monobook uses name rather than id.
279  // We may want to change monobook to adhere to the same contract however.
280  'name' => $key,
281  'id' => "$rowId-$key",
282  'html' => $link,
283  ];
284  }
285  }
286 
287  $data['data-' . $category] = [
288  'id' => $rowId,
289  'className' => null,
290  'array-items' => $items
291  ];
292  }
293 
294  // If footer icons are enabled append to the end of the rows
295  $footerIcons = $this->getFooterIcons();
296 
297  if ( count( $footerIcons ) > 0 ) {
298  $icons = [];
299  foreach ( $footerIcons as $blockName => $blockIcons ) {
300  $html = '';
301  foreach ( $blockIcons as $key => $icon ) {
302  $html .= $this->makeFooterIcon( $icon );
303  }
304  // For historic reasons this mimics the `icononly` option
305  // for BaseTemplate::getFooterIcons. Empty rows should not be output.
306  if ( $html ) {
307  $block = htmlspecialchars( $blockName );
308  $icons[] = [
309  'name' => $block,
310  'id' => 'footer-' . $block . 'ico',
311  'html' => $html,
312  ];
313  }
314  }
315 
316  // Empty rows should not be output.
317  // This is how Vector has behaved historically but we can revisit later if necessary.
318  if ( count( $icons ) > 0 ) {
319  $data['data-icons'] = [
320  'id' => 'footer-icons',
321  'className' => 'noprint',
322  'array-items' => $icons,
323  ];
324  }
325  }
326 
327  return [
328  'data-footer' => $data,
329  ];
330  }
331 
335  private function getLogoData() : array {
337  // check if the logo supports variants
338  $variantsLogos = $logoData['variants'] ?? null;
339  if ( $variantsLogos ) {
340  $preferred = $this->getOutput()->getTitle()
341  ->getPageViewLanguage()->getCode();
342  $variantOverrides = $variantsLogos[$preferred] ?? null;
343  // Overrides the logo
344  if ( $variantOverrides ) {
345  foreach ( $variantOverrides as $key => $val ) {
346  $logoData[$key] = $val;
347  }
348  }
349  }
350  return $logoData;
351  }
352 
356  private function buildSearchProps() : array {
357  $config = $this->getConfig();
358 
359  $props = [
360  'form-action' => $config->get( 'Script' ),
361  'html-button-search-fallback' => $this->makeSearchButton(
362  'fulltext',
363  [ 'id' => 'mw-searchButton', 'class' => 'searchButton mw-fallbackSearchButton' ]
364  ),
365  'html-button-search' => $this->makeSearchButton(
366  'go',
367  [ 'id' => 'searchButton', 'class' => 'searchButton' ]
368  ),
369  'html-input' => $this->makeSearchInput( [ 'id' => 'searchInput' ] ),
370  'msg-search' => $this->msg( 'search' )->text(),
371  'page-title' => SpecialPage::getTitleFor( 'Search' )->getPrefixedDBkey(),
372  ];
373 
374  return $props;
375  }
376 
384  private function tailElement( $out ) {
385  $tail = [
386  MWDebug::getDebugHTML( $this ),
387  $this->bottomScripts(),
388  wfReportTime( $out->getCSP()->getNonce() ),
390  . Html::closeElement( 'body' )
391  . Html::closeElement( 'html' )
392  ];
393 
394  return WrappedStringList::join( "\n", $tail );
395  }
396 }
SkinMustache\buildSearchProps
buildSearchProps()
Definition: SkinMustache.php:356
Skin\prepareSubtitle
prepareSubtitle()
Prepare the subtitle of the page for output in the skin if one has been set.
Definition: Skin.php:2604
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:71
Skin\getSiteNotice
getSiteNotice()
Definition: Skin.php:2040
MWDebug\getDebugHTML
static getDebugHTML(IContextSource $context)
Returns the HTML to add to the page for the toolbar.
Definition: MWDebug.php:610
SkinMustache\getPortletLabel
getPortletLabel( $name)
Definition: SkinMustache.php:97
SkinTemplate\buildContentNavigationUrls
buildContentNavigationUrls()
a structured array of links usually used for the tabs in a skin
Definition: SkinTemplate.php:851
Sanitizer\escapeIdForAttribute
static escapeIdForAttribute( $id, $mode=self::ID_PRIMARY)
Given a section name or other user-generated or otherwise unsafe string, escapes it to be a valid HTM...
Definition: Sanitizer.php:812
SkinMustache\generateHTML
generateHTML()
Subclasses not wishing to use the QuickTemplate render method can rewrite this method,...
Definition: SkinMustache.php:131
SkinMustache\getPortletData
getPortletData( $name, array $items)
Definition: SkinMustache.php:40
MWDebug\getHTMLDebugLog
static getHTMLDebugLog()
Generate debug log in HTML for displaying at the bottom of the main content area.
Definition: MWDebug.php:644
SkinMustache\getLogoData
getLogoData()
Definition: SkinMustache.php:335
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:106
Sanitizer\escapeClass
static escapeClass( $class)
Given a value, escape it so that it can be used as a CSS class and return it.
Definition: Sanitizer.php:973
Title\newMainPage
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:688
SkinTemplate\wrapHTML
wrapHTML( $title, $html)
Wrap the body text with language information and identifiable element.
Definition: SkinTemplate.php:167
Skin\makeListItem
makeListItem( $key, $item, $options=[])
Generates a list item for a navigation, portlet, portal, sidebar...
Definition: Skin.php:2455
Skin\bottomScripts
bottomScripts()
This gets called shortly before the "</body>" tag.
Definition: Skin.php:739
Skin\afterContentHook
afterContentHook()
This runs a hook to allow extensions placing their stuff after content and article metadata (e....
Definition: Skin.php:701
SkinMustache\getPortletsTemplateData
getPortletsTemplateData()
Definition: SkinMustache.php:204
Html\closeElement
static closeElement( $element)
Returns "</$element>".
Definition: Html.php:318
wfReportTime
wfReportTime( $nonce=null)
Returns a script tag that stores the amount of time it took MediaWiki to handle the request in millis...
Definition: GlobalFunctions.php:1314
SkinMustache\tailElement
tailElement( $out)
The final bits that go to the bottom of a page HTML document including the closing tags.
Definition: SkinMustache.php:384
Skin\getIndicatorsData
getIndicatorsData( $indicators)
Return an array of indicator data.
Definition: Skin.php:2219
Skin\buildSidebar
buildSidebar()
Build an array that represents the sidebar(s), the navigation bar among them.
Definition: Skin.php:1726
SkinTemplate\getFooterIcons
getFooterIcons()
Get template representation of the footer.
Definition: SkinTemplate.php:215
Skin\getCategories
getCategories()
Definition: Skin.php:666
ContextSource\getOutput
getOutput()
Definition: ContextSource.php:125
ResourceLoaderSkinModule\getAvailableLogos
static getAvailableLogos( $conf)
Return an array of all available logos that a skin may use.
Definition: ResourceLoaderSkinModule.php:432
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:195
SkinTemplate\setupTemplateContext
setupTemplateContext()
Setup class properties that are necessary prior to calling setupTemplateForOutput.
Definition: SkinTemplate.php:92
$content
$content
Definition: router.php:76
Skin\makeSearchInput
makeSearchInput( $attrs=[])
Definition: Skin.php:2518
SkinMustache\$templateParser
TemplateParser null $templateParser
Definition: SkinMustache.php:30
SkinTemplate\$template
string $template
For QuickTemplate, the name of the subclass which will actually fill the template.
Definition: SkinTemplate.php:44
Skin\getPersonalToolsForMakeListItem
getPersonalToolsForMakeListItem( $urls)
Create an array of personal tools items from the data in the quicktemplate stored by SkinTemplate.
Definition: Skin.php:2243
Skin\printSource
printSource()
Text with the permalink to the source page, usually shown on the footer of a printed page.
Definition: Skin.php:762
Skin\getAfterPortlet
getAfterPortlet(string $name)
Allows extensions to hook into known portlets and add stuff to them.
Definition: Skin.php:2591
Linker\tooltip
static tooltip( $name, $options=null)
Returns raw bits of HTML, use titleAttrib()
Definition: Linker.php:2334
Skin\getNewtalks
getNewtalks()
Gets new talk page messages for the current user and returns an appropriate alert message (or an empt...
Definition: Skin.php:1886
SkinMustache\getFooterTemplateData
getFooterTemplateData()
Get rows that make up the footer.
Definition: SkinMustache.php:268
SkinTemplate\prepareUserLanguageAttributes
prepareUserLanguageAttributes()
Prepare user language attribute links.
Definition: SkinTemplate.php:192
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:212
SkinTemplate\insertNotificationsIntoPersonalTools
insertNotificationsIntoPersonalTools(array $contentNavigation)
Insert the notifications content navigation into the personal tools, in their old position,...
Definition: SkinTemplate.php:1276
Skin\getFooterLinks
getFooterLinks()
Get template representation of the footer containing site footer links as well as standard footer lin...
Definition: Skin.php:2624
SkinTemplate\prepareUndeleteLink
prepareUndeleteLink()
Prepare undelete link for output in page.
Definition: SkinTemplate.php:251
SkinMustache
Generic template for use with Mustache templates.
Definition: SkinMustache.php:26
SkinMustache\getTemplateData
getTemplateData()
Subclasses may extend this method to add additional template data.
Definition: SkinMustache.php:166
Skin\makeSearchButton
makeSearchButton( $mode, $attrs=[])
Definition: Skin.php:2540
TemplateParser
Definition: TemplateParser.php:27
SkinTemplate
Base class for template-based skins.
Definition: SkinTemplate.php:39
SkinMustache\getTemplateParser
getTemplateParser()
Get the template parser, it will be lazily created if not already set.
Definition: SkinMustache.php:118
Skin\makeFooterIcon
makeFooterIcon( $icon, $withImage='withImage')
Renders a $wgFooterIcons icon according to the method's arguments.
Definition: Skin.php:1069