MediaWiki REL1_34
VectorTemplate.php
Go to the documentation of this file.
1<?php
30
34 public function execute() {
35 $this->data['namespace_urls'] = $this->data['content_navigation']['namespaces'];
36 $this->data['view_urls'] = $this->data['content_navigation']['views'];
37 $this->data['action_urls'] = $this->data['content_navigation']['actions'];
38 $this->data['variant_urls'] = $this->data['content_navigation']['variants'];
39
40 // Move the watch/unwatch star outside of the collapsed "actions" menu to the main "views" menu
41 if ( $this->config->get( 'VectorUseIconWatch' ) ) {
42 $mode = $this->getSkin()->getUser()->isWatched( $this->getSkin()->getRelevantTitle() )
43 ? 'unwatch'
44 : 'watch';
45
46 if ( isset( $this->data['action_urls'][$mode] ) ) {
47 $this->data['view_urls'][$mode] = $this->data['action_urls'][$mode];
48 unset( $this->data['action_urls'][$mode] );
49 }
50 }
51
52 // Naming conventions for Mustache parameters:
53 // - Prefix "is" for boolean values.
54 // - Prefix "msg-" for interface messages.
55 // - Prefix "page-" for data relating to the current page (e.g. Title, WikiPage, or OutputPage).
56 // - Prefix "html-" for raw HTML (in front of other keys, if applicable).
57 // - Conditional values are null if absent.
58 $params = [
59 'html-headelement' => $this->get( 'headelement', '' ),
60 'html-sitenotice' => $this->get( 'sitenotice', null ),
61 'html-indicators' => $this->getIndicators(),
62 'page-langcode' => $this->getSkin()->getTitle()->getPageViewLanguage()->getHtmlCode(),
63 'page-isarticle' => (bool)$this->data['isarticle'],
64
65 // Remember that the string '0' is a valid title.
66 // From OutputPage::getPageTitle, via ::setPageTitle().
67 'html-title' => $this->get( 'title', '' ),
68
69 'html-prebodyhtml' => $this->get( 'prebodyhtml', '' ),
70 'msg-tagline' => $this->getMsg( 'tagline' )->text(),
71 // TODO: mediawiki/SkinTemplate should expose langCode and langDir properly.
72 'html-userlangattributes' => $this->get( 'userlangattributes', '' ),
73 // From OutputPage::getSubtitle()
74 'html-subtitle' => $this->get( 'subtitle', '' ),
75
76 // TODO: Use directly Skin::getUndeleteLink() directly.
77 // Always returns string, cast to null if empty.
78 'html-undelete' => $this->get( 'undelete', null ) ?: null,
79
80 // From Skin::getNewtalks(). Always returns string, cast to null if empty.
81 'html-newtalk' => $this->get( 'newtalk', '' ) ?: null,
82
83 'msg-jumptonavigation' => $this->getMsg( 'vector-jumptonavigation' )->text(),
84 'msg-jumptosearch' => $this->getMsg( 'vector-jumptosearch' )->text(),
85
86 // Result of OutputPage::addHTML calls
87 'html-bodycontent' => $this->get( 'bodycontent' ),
88
89 'html-printfooter' => $this->get( 'printfooter', null ),
90 'html-catlinks' => $this->get( 'catlinks', '' ),
91 'html-dataAfterContent' => $this->get( 'dataAfterContent', '' ),
92 // From MWDebug::getHTMLDebugLog (when $wgShowDebug is enabled)
93 'html-debuglog' => $this->get( 'debughtml', '' ),
94 // From BaseTemplate::getTrail (handles bottom JavaScript)
95 'html-printtail' => $this->getTrail(),
96 ];
97
98 // TODO: Convert the rest to Mustache
99 ob_start();
100 ?>
101 <div id="mw-navigation">
102 <h2><?php $this->msg( 'navigation-heading' ) ?></h2>
103 <div id="mw-head">
104 <?php $this->renderNavigation( [ 'PERSONAL' ] ); ?>
105 <div id="left-navigation">
106 <?php $this->renderNavigation( [ 'NAMESPACES', 'VARIANTS' ] ); ?>
107 </div>
108 <div id="right-navigation">
109 <?php $this->renderNavigation( [ 'VIEWS', 'ACTIONS', 'SEARCH' ] ); ?>
110 </div>
111 </div>
112 <div id="mw-panel">
113 <div id="p-logo" role="banner"><a class="mw-wiki-logo" href="<?php
114 echo htmlspecialchars( $this->data['nav_urls']['mainpage']['href'] )
115 ?>"<?php
116 echo Xml::expandAttributes( Linker::tooltipAndAccesskeyAttribs( 'p-logo' ) )
117 ?>></a></div>
118 <?php $this->renderPortals( $this->data['sidebar'] ); ?>
119 </div>
120 </div>
121 <?php Hooks::run( 'VectorBeforeFooter' ); ?>
122 <div id="footer" role="contentinfo"<?php $this->html( 'userlangattributes' ) ?>>
123 <?php
124 foreach ( $this->getFooterLinks() as $category => $links ) {
125 ?>
126 <ul id="footer-<?php echo $category ?>">
127 <?php
128 foreach ( $links as $link ) {
129 ?>
130 <li id="footer-<?php echo $category ?>-<?php echo $link ?>"><?php $this->html( $link ) ?></li>
131 <?php
132 }
133 ?>
134 </ul>
135 <?php
136 }
137 ?>
138 <?php $footericons = $this->getFooterIcons( 'icononly' );
139 if ( count( $footericons ) > 0 ) {
140 ?>
141 <ul id="footer-icons" class="noprint">
142 <?php
143 foreach ( $footericons as $blockName => $footerIcons ) {
144 ?>
145 <li id="footer-<?php echo htmlspecialchars( $blockName ); ?>ico">
146 <?php
147 foreach ( $footerIcons as $icon ) {
148 echo $this->getSkin()->makeFooterIcon( $icon );
149 }
150 ?>
151 </li>
152 <?php
153 }
154 ?>
155 </ul>
156 <?php
157 }
158 ?>
159 <div style="clear: both;"></div>
160 </div>
161 <?php
162 $params['html-unported'] = ob_get_contents();
163 ob_end_clean();
164
165 // Prepare and output the HTML response
166 $templates = new TemplateParser( __DIR__ . '/templates' );
167 echo $templates->processTemplate( 'index', $params );
168 }
169
175 protected function renderPortals( array $portals ) {
176 // Force the rendering of the following portals
177 if ( !isset( $portals['TOOLBOX'] ) ) {
178 $portals['TOOLBOX'] = true;
179 }
180 if ( !isset( $portals['LANGUAGES'] ) ) {
181 $portals['LANGUAGES'] = true;
182 }
183 // Render portals
184 foreach ( $portals as $name => $content ) {
185 if ( $content === false ) {
186 continue;
187 }
188
189 // Numeric strings gets an integer when set as key, cast back - T73639
190 $name = (string)$name;
191
192 switch ( $name ) {
193 case 'SEARCH':
194 break;
195 case 'TOOLBOX':
196 $this->renderPortal( 'tb', $this->getToolbox(), 'toolbox', 'SkinTemplateToolboxEnd' );
197 Hooks::run( 'VectorAfterToolbox' );
198 break;
199 case 'LANGUAGES':
200 if ( $this->data['language_urls'] !== false ) {
201 $this->renderPortal( 'lang', $this->data['language_urls'], 'otherlanguages' );
202 }
203 break;
204 default:
205 $this->renderPortal( $name, $content );
206 break;
207 }
208 }
209 }
210
217 protected function renderPortal( $name, $content, $msg = null, $hook = null ) {
218 if ( $msg === null ) {
219 $msg = $name;
220 }
221 $msgObj = $this->getMsg( $msg );
222 $labelId = Sanitizer::escapeIdForAttribute( "p-$name-label" );
223 ?>
224 <div class="portal" role="navigation" id="<?php
225 echo htmlspecialchars( Sanitizer::escapeIdForAttribute( "p-$name" ) )
226 ?>"<?php
227 echo Linker::tooltip( 'p-' . $name )
228 ?> aria-labelledby="<?php echo htmlspecialchars( $labelId ) ?>">
229 <h3<?php $this->html( 'userlangattributes' ) ?> id="<?php echo htmlspecialchars( $labelId )
230 ?>"><?php
231 echo htmlspecialchars( $msgObj->exists() ? $msgObj->text() : $msg );
232 ?></h3>
233 <div class="body">
234 <?php
235 if ( is_array( $content ) ) {
236 ?>
237 <ul>
238 <?php
239 foreach ( $content as $key => $val ) {
240 echo $this->makeListItem( $key, $val );
241 }
242 if ( $hook !== null ) {
243 // Avoid PHP 7.1 warning
244 $skin = $this;
245 Hooks::run( $hook, [ &$skin, true ] );
246 }
247 ?>
248 </ul>
249 <?php
250 } else {
251 // Allow raw HTML block to be defined by extensions
252 echo $content;
253 }
254
255 $this->renderAfterPortlet( $name );
256 ?>
257 </div>
258 </div>
259 <?php
260 }
261
268 protected function renderNavigation( array $elements ) {
269 // Render elements
270 foreach ( $elements as $name => $element ) {
271 switch ( $element ) {
272 case 'NAMESPACES':
273 ?>
274 <div id="p-namespaces" role="navigation" class="vectorTabs<?php
275 if ( count( $this->data['namespace_urls'] ) == 0 ) {
276 echo ' emptyPortlet';
277 }
278 ?>" aria-labelledby="p-namespaces-label">
279 <h3 id="p-namespaces-label"><?php $this->msg( 'namespaces' ) ?></h3>
280 <ul<?php $this->html( 'userlangattributes' ) ?>>
281 <?php
282 foreach ( $this->data['namespace_urls'] as $key => $item ) {
283 echo $this->makeListItem( $key, $item, [
284 'vector-wrap' => true,
285 ] );
286 }
287 ?>
288 </ul>
289 </div>
290 <?php
291 break;
292 case 'VARIANTS':
293 ?>
294 <div id="p-variants" role="navigation" class="vectorMenu<?php
295 if ( count( $this->data['variant_urls'] ) == 0 ) {
296 echo ' emptyPortlet';
297 }
298 ?>" aria-labelledby="p-variants-label">
299 <?php
300 // Replace the label with the name of currently chosen variant, if any
301 $variantLabel = $this->getMsg( 'variants' )->text();
302 foreach ( $this->data['variant_urls'] as $item ) {
303 if ( isset( $item['class'] ) && stripos( $item['class'], 'selected' ) !== false ) {
304 $variantLabel = $item['text'];
305 break;
306 }
307 }
308 ?>
309 <input type="checkbox" class="vectorMenuCheckbox" aria-labelledby="p-variants-label" />
310 <h3 id="p-variants-label">
311 <span><?php echo htmlspecialchars( $variantLabel ) ?></span>
312 </h3>
313 <ul class="menu">
314 <?php
315 foreach ( $this->data['variant_urls'] as $key => $item ) {
316 echo $this->makeListItem( $key, $item );
317 }
318 ?>
319 </ul>
320 </div>
321 <?php
322 break;
323 case 'VIEWS':
324 ?>
325 <div id="p-views" role="navigation" class="vectorTabs<?php
326 if ( count( $this->data['view_urls'] ) == 0 ) {
327 echo ' emptyPortlet';
328 }
329 ?>" aria-labelledby="p-views-label">
330 <h3 id="p-views-label"><?php $this->msg( 'views' ) ?></h3>
331 <ul<?php $this->html( 'userlangattributes' ) ?>>
332 <?php
333 foreach ( $this->data['view_urls'] as $key => $item ) {
334 echo $this->makeListItem( $key, $item, [
335 'vector-wrap' => true,
336 'vector-collapsible' => true,
337 ] );
338 }
339 ?>
340 </ul>
341 </div>
342 <?php
343 break;
344 case 'ACTIONS':
345 ?>
346 <div id="p-cactions" role="navigation" class="vectorMenu<?php
347 if ( count( $this->data['action_urls'] ) == 0 ) {
348 echo ' emptyPortlet';
349 }
350 ?>" aria-labelledby="p-cactions-label">
351 <input type="checkbox" class="vectorMenuCheckbox" aria-labelledby="p-cactions-label" />
352 <h3 id="p-cactions-label"><span><?php
353 $this->msg( 'vector-more-actions' )
354 ?></span></h3>
355 <ul class="menu"<?php $this->html( 'userlangattributes' ) ?>>
356 <?php
357 foreach ( $this->data['action_urls'] as $key => $item ) {
358 echo $this->makeListItem( $key, $item );
359 }
360 ?>
361 </ul>
362 </div>
363 <?php
364 break;
365 case 'PERSONAL':
366 ?>
367 <div id="p-personal" role="navigation"<?php
368 if ( count( $this->data['personal_urls'] ) == 0 ) {
369 echo ' class="emptyPortlet"';
370 }
371 ?> aria-labelledby="p-personal-label">
372 <h3 id="p-personal-label"><?php $this->msg( 'personaltools' ) ?></h3>
373 <ul<?php $this->html( 'userlangattributes' ) ?>>
374 <?php
375 $notLoggedIn = '';
376
377 if ( !$this->getSkin()->getUser()->isLoggedIn() &&
378 User::groupHasPermission( '*', 'edit' )
379 ) {
380 $notLoggedIn =
381 Html::element( 'li',
382 [ 'id' => 'pt-anonuserpage' ],
383 $this->getMsg( 'notloggedin' )->text()
384 );
385 }
386
387 $personalTools = $this->getPersonalTools();
388
389 $langSelector = '';
390 if ( array_key_exists( 'uls', $personalTools ) ) {
391 $langSelector = $this->makeListItem( 'uls', $personalTools[ 'uls' ] );
392 unset( $personalTools[ 'uls' ] );
393 }
394
395 echo $langSelector;
396 echo $notLoggedIn;
397 foreach ( $personalTools as $key => $item ) {
398 echo $this->makeListItem( $key, $item );
399 }
400 ?>
401 </ul>
402 </div>
403 <?php
404 break;
405 case 'SEARCH':
406 ?>
407 <div id="p-search" role="search">
408 <h3<?php $this->html( 'userlangattributes' ) ?>>
409 <label for="searchInput"><?php $this->msg( 'search' ) ?></label>
410 </h3>
411 <form action="<?php $this->text( 'wgScript' ) ?>" id="searchform">
412 <div<?php echo $this->config->get( 'VectorUseSimpleSearch' ) ? ' id="simpleSearch"' : '' ?>>
413 <?php
414 echo $this->makeSearchInput( [ 'id' => 'searchInput' ] );
415 echo Html::hidden( 'title', $this->get( 'searchtitle' ) );
416 /* We construct two buttons (for 'go' and 'fulltext' search modes),
417 * but only one will be visible and actionable at a time (they are
418 * overlaid on top of each other in CSS).
419 * * Browsers will use the 'fulltext' one by default (as it's the
420 * first in tree-order), which is desirable when they are unable
421 * to show search suggestions (either due to being broken or
422 * having JavaScript turned off).
423 * * The mediawiki.searchSuggest module, after doing tests for the
424 * broken browsers, removes the 'fulltext' button and handles
425 * 'fulltext' search itself; this will reveal the 'go' button and
426 * cause it to be used.
427 */
428 echo $this->makeSearchButton(
429 'fulltext',
430 [ 'id' => 'mw-searchButton', 'class' => 'searchButton mw-fallbackSearchButton' ]
431 );
432 echo $this->makeSearchButton(
433 'go',
434 [ 'id' => 'searchButton', 'class' => 'searchButton' ]
435 );
436 ?>
437 </div>
438 </form>
439 </div>
440 <?php
441
442 break;
443 }
444 }
445 }
446
450 public function makeLink( $key, $item, $options = [] ) {
451 $html = parent::makeLink( $key, $item, $options );
452 // Add an extra wrapper because our CSS is weird
453 if ( isset( $options['vector-wrap'] ) && $options['vector-wrap'] ) {
454 $html = Html::rawElement( 'span', [], $html );
455 }
456 return $html;
457 }
458
462 public function makeListItem( $key, $item, $options = [] ) {
463 // For fancy styling of watch/unwatch star
464 if (
465 $this->config->get( 'VectorUseIconWatch' )
466 && ( $key === 'watch' || $key === 'unwatch' )
467 ) {
468 $item['class'] = rtrim( 'icon ' . $item['class'], ' ' );
469 $item['primary'] = true;
470 }
471
472 // Add CSS class 'collapsible' to links which are not marked as "primary"
473 if (
474 isset( $options['vector-collapsible'] ) && $options['vector-collapsible'] ) {
475 $item['class'] = rtrim( 'collapsible ' . $item['class'], ' ' );
476 }
477
478 return parent::makeListItem( $key, $item, $options );
479 }
480}
getUser()
New base template for a skin's template extended from QuickTemplate this class features helper method...
getFooterLinks( $option=null)
Returns an array of footerlinks trimmed down to only those footer links that are valid.
makeSearchButton( $mode, $attrs=[])
renderAfterPortlet( $name)
getToolbox()
Create an array of common toolbox items from the data in the quicktemplate stored by SkinTemplate.
getTrail()
Get the basic end-page trail including bottomscripts, reporttime, and debug stuff.
getPersonalTools()
Create an array of personal tools items from the data in the quicktemplate stored by SkinTemplate.
getFooterIcons( $option=null)
Returns an array of footer icons filtered down by options relevant to how the skin wishes to display ...
getMsg( $name,... $params)
Get a Message object with its context set.
getIndicators()
Get the suggested HTML for page status indicators: icons (or short text snippets) usually displayed i...
makeSearchInput( $attrs=[])
static tooltip( $name, $options=null)
Returns raw bits of HTML, use titleAttrib()
Definition Linker.php:2223
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition Linker.php:2195
getSkin()
Get the Skin object related to this object.
QuickTemplate subclass for Vector.
makeLink( $key, $item, $options=[])
Makes a link, usually used by makeListItem to generate a link for an item in a list used in navigatio...
makeListItem( $key, $item, $options=[])
Generates a list item for a navigation, portlet, portal, sidebar... list.If you want something other ...
renderNavigation(array $elements)
Render one or more navigations elements by name, automatically reversed by css when UI is in RTL mode...
renderPortals(array $portals)
Render a series of portals.
execute()
Outputs the entire contents of the HTML page.
renderPortal( $name, $content, $msg=null, $hook=null)
$content
Definition router.php:78