]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - skins/vector/VectorTemplate.php
MediaWiki 1.30.2-scripts
[autoinstallsdev/mediawiki.git] / skins / vector / VectorTemplate.php
1 <?php
2 /**
3  * Vector - Modern version of MonoBook with fresh look and many usability
4  * improvements.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  * http://www.gnu.org/copyleft/gpl.html
20  *
21  * @file
22  * @ingroup Skins
23  */
24
25 /**
26  * QuickTemplate class for Vector skin
27  * @ingroup Skins
28  */
29 class VectorTemplate extends BaseTemplate {
30         /* Functions */
31
32         /**
33          * Outputs the entire contents of the (X)HTML page
34          */
35         public function execute() {
36                 $this->data['namespace_urls'] = $this->data['content_navigation']['namespaces'];
37                 $this->data['view_urls'] = $this->data['content_navigation']['views'];
38                 $this->data['action_urls'] = $this->data['content_navigation']['actions'];
39                 $this->data['variant_urls'] = $this->data['content_navigation']['variants'];
40
41                 // Move the watch/unwatch star outside of the collapsed "actions" menu to the main "views" menu
42                 if ( $this->config->get( 'VectorUseIconWatch' ) ) {
43                         $mode = $this->getSkin()->getUser()->isWatched( $this->getSkin()->getRelevantTitle() )
44                                 ? 'unwatch'
45                                 : 'watch';
46
47                         if ( isset( $this->data['action_urls'][$mode] ) ) {
48                                 $this->data['view_urls'][$mode] = $this->data['action_urls'][$mode];
49                                 unset( $this->data['action_urls'][$mode] );
50                         }
51                 }
52
53                 // Reverse horizontally rendered navigation elements
54                 if ( $this->data['rtl'] ) {
55                         $this->data['view_urls'] =
56                                 array_reverse( $this->data['view_urls'] );
57                         $this->data['namespace_urls'] =
58                                 array_reverse( $this->data['namespace_urls'] );
59                         $this->data['personal_urls'] =
60                                 array_reverse( $this->data['personal_urls'] );
61                 }
62
63                 $this->data['pageLanguage'] =
64                         $this->getSkin()->getTitle()->getPageViewLanguage()->getHtmlCode();
65
66                 // Output HTML Page
67                 $this->html( 'headelement' );
68                 ?>
69                 <div id="mw-page-base" class="noprint"></div>
70                 <div id="mw-head-base" class="noprint"></div>
71                 <div id="content" class="mw-body" role="main">
72                         <a id="top"></a>
73
74                         <?php
75                         if ( $this->data['sitenotice'] ) {
76                                 ?>
77                                 <div id="siteNotice" class="mw-body-content"><?php $this->html( 'sitenotice' ) ?></div>
78                         <?php
79                         }
80                         ?>
81                         <?php
82                         if ( is_callable( [ $this, 'getIndicators' ] ) ) {
83                                 echo $this->getIndicators();
84                         }
85                         // Loose comparison with '!=' is intentional, to catch null and false too, but not '0'
86                         if ( $this->data['title'] != '' ) {
87                         ?>
88                         <h1 id="firstHeading" class="firstHeading" lang="<?php $this->text( 'pageLanguage' ); ?>"><?php
89                                 $this->html( 'title' )
90                         ?></h1>
91                         <?php
92                         } ?>
93                         <?php $this->html( 'prebodyhtml' ) ?>
94                         <div id="bodyContent" class="mw-body-content">
95                                 <?php
96                                 if ( $this->data['isarticle'] ) {
97                                         ?>
98                                         <div id="siteSub" class="noprint"><?php $this->msg( 'tagline' ) ?></div>
99                                 <?php
100                                 }
101                                 ?>
102                                 <div id="contentSub"<?php $this->html( 'userlangattributes' ) ?>><?php
103                                         $this->html( 'subtitle' )
104                                 ?></div>
105                                 <?php
106                                 if ( $this->data['undelete'] ) {
107                                         ?>
108                                         <div id="contentSub2"><?php $this->html( 'undelete' ) ?></div>
109                                 <?php
110                                 }
111                                 ?>
112                                 <?php
113                                 if ( $this->data['newtalk'] ) {
114                                         ?>
115                                         <div class="usermessage"><?php $this->html( 'newtalk' ) ?></div>
116                                 <?php
117                                 }
118                                 ?>
119                                 <div id="jump-to-nav" class="mw-jump">
120                                         <?php $this->msg( 'jumpto' ) ?>
121                                         <a href="#mw-head"><?php
122                                                 $this->msg( 'jumptonavigation' )
123                                         ?></a><?php $this->msg( 'comma-separator' ) ?>
124                                         <a href="#p-search"><?php $this->msg( 'jumptosearch' ) ?></a>
125                                 </div>
126                                 <?php
127                                 $this->html( 'bodycontent' );
128
129                                 if ( $this->data['printfooter'] ) {
130                                         ?>
131                                         <div class="printfooter">
132                                                 <?php $this->html( 'printfooter' ); ?>
133                                         </div>
134                                 <?php
135                                 }
136
137                                 if ( $this->data['catlinks'] ) {
138                                         $this->html( 'catlinks' );
139                                 }
140
141                                 if ( $this->data['dataAfterContent'] ) {
142                                         $this->html( 'dataAfterContent' );
143                                 }
144                                 ?>
145                                 <div class="visualClear"></div>
146                                 <?php $this->html( 'debughtml' ); ?>
147                         </div>
148                 </div>
149                 <div id="mw-navigation">
150                         <h2><?php $this->msg( 'navigation-heading' ) ?></h2>
151
152                         <div id="mw-head">
153                                 <?php $this->renderNavigation( 'PERSONAL' ); ?>
154                                 <div id="left-navigation">
155                                         <?php $this->renderNavigation( [ 'NAMESPACES', 'VARIANTS' ] ); ?>
156                                 </div>
157                                 <div id="right-navigation">
158                                         <?php $this->renderNavigation( [ 'VIEWS', 'ACTIONS', 'SEARCH' ] ); ?>
159                                 </div>
160                         </div>
161                         <div id="mw-panel">
162                                 <div id="p-logo" role="banner"><a class="mw-wiki-logo" href="<?php
163                                         echo htmlspecialchars( $this->data['nav_urls']['mainpage']['href'] )
164                                         ?>" <?php
165                                         echo Xml::expandAttributes( Linker::tooltipAndAccesskeyAttribs( 'p-logo' ) )
166                                         ?>></a></div>
167                                 <?php $this->renderPortals( $this->data['sidebar'] ); ?>
168                         </div>
169                 </div>
170                 <div id="footer" role="contentinfo"<?php $this->html( 'userlangattributes' ) ?>>
171                         <?php
172                         foreach ( $this->getFooterLinks() as $category => $links ) {
173                                 ?>
174                                 <ul id="footer-<?php echo $category ?>">
175                                         <?php
176                                         foreach ( $links as $link ) {
177                                                 ?>
178                                                 <li id="footer-<?php echo $category ?>-<?php echo $link ?>"><?php $this->html( $link ) ?></li>
179                                         <?php
180                                         }
181                                         ?>
182                                 </ul>
183                         <?php
184                         }
185                         ?>
186                         <?php $footericons = $this->getFooterIcons( 'icononly' );
187                         if ( count( $footericons ) > 0 ) {
188                                 ?>
189                                 <ul id="footer-icons" class="noprint">
190                                         <?php
191                                         foreach ( $footericons as $blockName => $footerIcons ) {
192                                                 ?>
193                                                 <li id="footer-<?php echo htmlspecialchars( $blockName ); ?>ico">
194                                                         <?php
195                                                         foreach ( $footerIcons as $icon ) {
196                                                                 echo $this->getSkin()->makeFooterIcon( $icon );
197                                                         }
198                                                         ?>
199                                                 </li>
200                                         <?php
201                                         }
202                                         ?>
203                                 </ul>
204                         <?php
205                         }
206                         ?>
207                         <div style="clear:both"></div>
208                 </div>
209                 <?php $this->printTrail(); ?>
210
211         </body>
212 </html>
213 <?php
214         }
215
216         /**
217          * Render a series of portals
218          *
219          * @param array $portals
220          */
221         protected function renderPortals( $portals ) {
222                 // Force the rendering of the following portals
223                 if ( !isset( $portals['SEARCH'] ) ) {
224                         $portals['SEARCH'] = true;
225                 }
226                 if ( !isset( $portals['TOOLBOX'] ) ) {
227                         $portals['TOOLBOX'] = true;
228                 }
229                 if ( !isset( $portals['LANGUAGES'] ) ) {
230                         $portals['LANGUAGES'] = true;
231                 }
232                 // Render portals
233                 foreach ( $portals as $name => $content ) {
234                         if ( $content === false ) {
235                                 continue;
236                         }
237
238                         // Numeric strings gets an integer when set as key, cast back - T73639
239                         $name = (string)$name;
240
241                         switch ( $name ) {
242                                 case 'SEARCH':
243                                         break;
244                                 case 'TOOLBOX':
245                                         $this->renderPortal( 'tb', $this->getToolbox(), 'toolbox', 'SkinTemplateToolboxEnd' );
246                                         break;
247                                 case 'LANGUAGES':
248                                         if ( $this->data['language_urls'] !== false ) {
249                                                 $this->renderPortal( 'lang', $this->data['language_urls'], 'otherlanguages' );
250                                         }
251                                         break;
252                                 default:
253                                         $this->renderPortal( $name, $content );
254                                         break;
255                         }
256                 }
257         }
258
259         /**
260          * @param string $name
261          * @param array $content
262          * @param null|string $msg
263          * @param null|string|array $hook
264          */
265         protected function renderPortal( $name, $content, $msg = null, $hook = null ) {
266                 if ( $msg === null ) {
267                         $msg = $name;
268                 }
269                 $msgObj = wfMessage( $msg );
270                 $labelId = Sanitizer::escapeId( "p-$name-label" );
271                 ?>
272                 <div class="portal" role="navigation" id='<?php
273                 echo Sanitizer::escapeId( "p-$name" )
274                 ?>'<?php
275                 echo Linker::tooltip( 'p-' . $name )
276                 ?> aria-labelledby='<?php echo $labelId ?>'>
277                         <h3<?php $this->html( 'userlangattributes' ) ?> id='<?php echo $labelId ?>'><?php
278                                 echo htmlspecialchars( $msgObj->exists() ? $msgObj->text() : $msg );
279                                 ?></h3>
280
281                         <div class="body">
282                                 <?php
283                                 if ( is_array( $content ) ) {
284                                         ?>
285                                         <ul>
286                                                 <?php
287                                                 foreach ( $content as $key => $val ) {
288                                                         echo $this->makeListItem( $key, $val );
289                                                 }
290                                                 if ( $hook !== null ) {
291                                                         // Avoid PHP 7.1 warning
292                                                         $skin = $this;
293                                                         Hooks::run( $hook, [ &$skin, true ] );
294                                                 }
295                                                 ?>
296                                         </ul>
297                                 <?php
298                                 } else {
299                                         // Allow raw HTML block to be defined by extensions
300                                         echo $content;
301                                 }
302
303                                 $this->renderAfterPortlet( $name );
304                                 ?>
305                         </div>
306                 </div>
307         <?php
308         }
309
310         /**
311          * Render one or more navigations elements by name, automatically reveresed
312          * when UI is in RTL mode
313          *
314          * @param array $elements
315          */
316         protected function renderNavigation( $elements ) {
317                 // If only one element was given, wrap it in an array, allowing more
318                 // flexible arguments
319                 if ( !is_array( $elements ) ) {
320                         $elements = [ $elements ];
321                         // If there's a series of elements, reverse them when in RTL mode
322                 } elseif ( $this->data['rtl'] ) {
323                         $elements = array_reverse( $elements );
324                 }
325                 // Render elements
326                 foreach ( $elements as $name => $element ) {
327                         switch ( $element ) {
328                                 case 'NAMESPACES':
329                                         ?>
330                                         <div id="p-namespaces" role="navigation" class="vectorTabs<?php
331                                         if ( count( $this->data['namespace_urls'] ) == 0 ) {
332                                                 echo ' emptyPortlet';
333                                         }
334                                         ?>" aria-labelledby="p-namespaces-label">
335                                                 <h3 id="p-namespaces-label"><?php $this->msg( 'namespaces' ) ?></h3>
336                                                 <ul<?php $this->html( 'userlangattributes' ) ?>>
337                                                         <?php
338                                                         foreach ( $this->data['namespace_urls'] as $key => $item ) {
339                                                                 echo "\t\t\t\t\t\t\t" . $this->makeListItem( $key, $item, [
340                                                                         'vector-wrap' => true,
341                                                                 ] ) . "\n";
342                                                         }
343                                                         ?>
344                                                 </ul>
345                                         </div>
346                                         <?php
347                                         break;
348                                 case 'VARIANTS':
349                                         ?>
350                                         <div id="p-variants" role="navigation" class="vectorMenu<?php
351                                         if ( count( $this->data['variant_urls'] ) == 0 ) {
352                                                 echo ' emptyPortlet';
353                                         }
354                                         ?>" aria-labelledby="p-variants-label">
355                                                 <?php
356                                                 // Replace the label with the name of currently chosen variant, if any
357                                                 $variantLabel = $this->getMsg( 'variants' )->text();
358                                                 foreach ( $this->data['variant_urls'] as $item ) {
359                                                         if ( isset( $item['class'] ) && stripos( $item['class'], 'selected' ) !== false ) {
360                                                                 $variantLabel = $item['text'];
361                                                                 break;
362                                                         }
363                                                 }
364                                                 ?>
365                                                 <h3 id="p-variants-label">
366                                                         <span><?php echo htmlspecialchars( $variantLabel ) ?></span>
367                                                 </h3>
368
369                                                 <div class="menu">
370                                                         <ul>
371                                                                 <?php
372                                                                 foreach ( $this->data['variant_urls'] as $key => $item ) {
373                                                                         echo "\t\t\t\t\t\t\t\t" . $this->makeListItem( $key, $item ) . "\n";
374                                                                 }
375                                                                 ?>
376                                                         </ul>
377                                                 </div>
378                                         </div>
379                                         <?php
380                                         break;
381                                 case 'VIEWS':
382                                         ?>
383                                         <div id="p-views" role="navigation" class="vectorTabs<?php
384                                         if ( count( $this->data['view_urls'] ) == 0 ) {
385                                                 echo ' emptyPortlet';
386                                         }
387                                         ?>" aria-labelledby="p-views-label">
388                                                 <h3 id="p-views-label"><?php $this->msg( 'views' ) ?></h3>
389                                                 <ul<?php $this->html( 'userlangattributes' ) ?>>
390                                                         <?php
391                                                         foreach ( $this->data['view_urls'] as $key => $item ) {
392                                                                 echo "\t\t\t\t\t\t\t" . $this->makeListItem( $key, $item, [
393                                                                         'vector-wrap' => true,
394                                                                         'vector-collapsible' => true,
395                                                                 ] ) . "\n";
396                                                         }
397                                                         ?>
398                                                 </ul>
399                                         </div>
400                                         <?php
401                                         break;
402                                 case 'ACTIONS':
403                                         ?>
404                                         <div id="p-cactions" role="navigation" class="vectorMenu<?php
405                                         if ( count( $this->data['action_urls'] ) == 0 ) {
406                                                 echo ' emptyPortlet';
407                                         }
408                                         ?>" aria-labelledby="p-cactions-label">
409                                                 <h3 id="p-cactions-label"><span><?php
410                                                         $this->msg( 'vector-more-actions' )
411                                                 ?></span></h3>
412
413                                                 <div class="menu">
414                                                         <ul<?php $this->html( 'userlangattributes' ) ?>>
415                                                                 <?php
416                                                                 foreach ( $this->data['action_urls'] as $key => $item ) {
417                                                                         echo "\t\t\t\t\t\t\t\t" . $this->makeListItem( $key, $item ) . "\n";
418                                                                 }
419                                                                 ?>
420                                                         </ul>
421                                                 </div>
422                                         </div>
423                                         <?php
424                                         break;
425                                 case 'PERSONAL':
426                                         ?>
427                                         <div id="p-personal" role="navigation" class="<?php
428                                         if ( count( $this->data['personal_urls'] ) == 0 ) {
429                                                 echo ' emptyPortlet';
430                                         }
431                                         ?>" aria-labelledby="p-personal-label">
432                                                 <h3 id="p-personal-label"><?php $this->msg( 'personaltools' ) ?></h3>
433                                                 <ul<?php $this->html( 'userlangattributes' ) ?>>
434                                                         <?php
435                                                         $notLoggedIn = '';
436
437                                                         if ( !$this->getSkin()->getUser()->isLoggedIn() &&
438                                                                 User::groupHasPermission( '*', 'edit' )
439                                                         ) {
440                                                                 $notLoggedIn =
441                                                                         Html::rawElement( 'li',
442                                                                                 [ 'id' => 'pt-anonuserpage' ],
443                                                                                 $this->getMsg( 'notloggedin' )->escaped()
444                                                                         );
445                                                         }
446
447                                                         $personalTools = $this->getPersonalTools();
448
449                                                         $langSelector = '';
450                                                         if ( array_key_exists( 'uls', $personalTools ) ) {
451                                                                 $langSelector = $this->makeListItem( 'uls', $personalTools[ 'uls' ] );
452                                                                 unset( $personalTools[ 'uls' ] );
453                                                         }
454
455                                                         if ( !$this->data[ 'rtl' ] ) {
456                                                                 echo $langSelector;
457                                                                 echo $notLoggedIn;
458                                                         }
459
460                                                         foreach ( $personalTools as $key => $item ) {
461                                                                 echo $this->makeListItem( $key, $item );
462                                                         }
463
464                                                         if ( $this->data[ 'rtl' ] ) {
465                                                                 echo $notLoggedIn;
466                                                                 echo $langSelector;
467                                                         }
468                                                         ?>
469                                                 </ul>
470                                         </div>
471                                         <?php
472                                         break;
473                                 case 'SEARCH':
474                                         ?>
475                                         <div id="p-search" role="search">
476                                                 <h3<?php $this->html( 'userlangattributes' ) ?>>
477                                                         <label for="searchInput"><?php $this->msg( 'search' ) ?></label>
478                                                 </h3>
479
480                                                 <form action="<?php $this->text( 'wgScript' ) ?>" id="searchform">
481                                                         <div<?php echo $this->config->get( 'VectorUseSimpleSearch' ) ? ' id="simpleSearch"' : '' ?>>
482                                                         <?php
483                                                         echo $this->makeSearchInput( [ 'id' => 'searchInput' ] );
484                                                         echo Html::hidden( 'title', $this->get( 'searchtitle' ) );
485                                                         /* We construct two buttons (for 'go' and 'fulltext' search modes),
486                                                          * but only one will be visible and actionable at a time (they are
487                                                          * overlaid on top of each other in CSS).
488                                                          * * Browsers will use the 'fulltext' one by default (as it's the
489                                                          *   first in tree-order), which is desirable when they are unable
490                                                          *   to show search suggestions (either due to being broken or
491                                                          *   having JavaScript turned off).
492                                                          * * The mediawiki.searchSuggest module, after doing tests for the
493                                                          *   broken browsers, removes the 'fulltext' button and handles
494                                                          *   'fulltext' search itself; this will reveal the 'go' button and
495                                                          *   cause it to be used.
496                                                          */
497                                                         echo $this->makeSearchButton(
498                                                                 'fulltext',
499                                                                 [ 'id' => 'mw-searchButton', 'class' => 'searchButton mw-fallbackSearchButton' ]
500                                                         );
501                                                         echo $this->makeSearchButton(
502                                                                 'go',
503                                                                 [ 'id' => 'searchButton', 'class' => 'searchButton' ]
504                                                         );
505                                                         ?>
506                                                         </div>
507                                                 </form>
508                                         </div>
509                                         <?php
510
511                                         break;
512                         }
513                 }
514         }
515
516         /**
517          * @inheritDoc
518          */
519         public function makeLink( $key, $item, $options = [] ) {
520                 $html = parent::makeLink( $key, $item, $options );
521                 // Add an extra wrapper because our CSS is weird
522                 if ( isset( $options['vector-wrap'] ) && $options['vector-wrap'] ) {
523                         $html = Html::rawElement( 'span', [], $html );
524                 }
525                 return $html;
526         }
527
528         /**
529          * @inheritDoc
530          */
531         public function makeListItem( $key, $item, $options = [] ) {
532                 // For fancy styling of watch/unwatch star
533                 if (
534                         $this->config->get( 'VectorUseIconWatch' )
535                         && ( $key === 'watch' || $key === 'unwatch' )
536                 ) {
537                         $item['class'] = rtrim( 'icon ' . $item['class'], ' ' );
538                         $item['primary'] = true;
539                 }
540
541                 // Add CSS class 'collapsible' to links which are not marked as "primary"
542                 if (
543                         isset( $options['vector-collapsible'] ) && $options['vector-collapsible']
544                         && !( isset( $item['primary'] ) && $item['primary'] )
545                 ) {
546                         $item['class'] = rtrim( 'collapsible ' . $item['class'], ' ' );
547                 }
548
549                 // We don't use this, prevent it from popping up in HTML output
550                 unset( $item['redundant'] );
551
552                 return parent::makeListItem( $key, $item, $options );
553         }
554 }