Merge "Additional tests to keep Parsoid on track."
[lhc/web/wiklou.git] / skins / CologneBlue.php
1 <?php
2 /**
3 * Cologne Blue: A nicer-looking alternative to Standard.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @todo document
21 * @file
22 * @ingroup Skins
23 */
24
25 if( !defined( 'MEDIAWIKI' ) ) {
26 die( -1 );
27 }
28
29 /**
30 * @todo document
31 * @ingroup Skins
32 */
33 class SkinCologneBlue extends SkinTemplate {
34 var $skinname = 'cologneblue', $stylename = 'cologneblue',
35 $template = 'CologneBlueTemplate';
36 var $useHeadElement = true;
37
38 /**
39 * @param $out OutputPage
40 */
41 function setupSkinUserCss( OutputPage $out ){
42 $out->addModuleStyles( 'mediawiki.legacy.shared' );
43 $out->addModuleStyles( 'mediawiki.legacy.oldshared' );
44 $out->addModuleStyles( 'skins.cologneblue' );
45 }
46
47 /**
48 * Override langlink formatting behavior not to uppercase the language names.
49 * See otherLanguages() in CologneBlueTemplate.
50 */
51 function formatLanguageName( $name ) {
52 return $name;
53 }
54 }
55
56 class CologneBlueTemplate extends BaseTemplate {
57 function execute() {
58 $this->html( 'headelement' );
59 echo $this->beforeContent();
60 $this->html( 'bodytext' );
61 echo "\n";
62 echo $this->afterContent();
63 $this->html( 'dataAfterContent' );
64 $this->printTrail();
65 echo "\n</body></html>";
66 }
67
68
69 /**
70 * Language/charset variant links for classic-style skins
71 * @return string
72 *
73 * @fixed
74 */
75 function variantLinks() {
76 $s = array();
77
78 $variants = $this->data['content_navigation']['variants'];
79
80 foreach ( $variants as $key => $link ) {
81 $s[] = $this->makeListItem( $key, $link, array( 'tag' => 'span' ) );
82 }
83
84 return $this->getSkin()->getLanguage()->pipeList( $s );
85 }
86
87 // @fixed
88 function otherLanguages() {
89 global $wgHideInterlanguageLinks;
90 if ( $wgHideInterlanguageLinks ) {
91 return "";
92 }
93
94 // We override SkinTemplate->formatLanguageName() in SkinCologneBlue
95 // not to capitalize the language names.
96 $language_urls = $this->data['language_urls'];
97 if ( empty( $language_urls ) ) {
98 return "";
99 }
100
101 $s = array();
102 foreach ( $language_urls as $key => $data ) {
103 $s[] = $this->makeListItem( $key, $data, array( 'tag' => 'span' ) );
104 }
105
106 return wfMessage( 'otherlanguages' )->text()
107 . wfMessage( 'colon-separator' )->text()
108 . $this->getSkin()->getLanguage()->pipeList( $s );
109 }
110
111 // @fixed
112 function pageTitleLinks() {
113 $s = array();
114 $footlinks = $this->getFooterLinks();
115
116 foreach ( $footlinks['places'] as $item ) {
117 $s[] = $this->data[$item];
118 }
119
120 return $this->getSkin()->getLanguage()->pipeList( $s );
121 }
122
123 function bottomLinks() {
124 $sep = wfMessage( 'pipe-separator' )->escaped() . "\n";
125
126 $s = '';
127 if ( $this->getSkin()->getOutput()->isArticleRelated() ) {
128 $element[] = '<strong>' . $this->editThisPage() . '</strong>';
129
130 if ( $this->getSkin()->getUser()->isLoggedIn() ) {
131 $element[] = $this->watchThisPage();
132 }
133
134 $element[] = $this->talkLink();
135 $element[] = $this->historyLink();
136 $element[] = $this->whatLinksHere();
137 $element[] = $this->watchPageLinksLink();
138
139 $title = $this->getSkin()->getTitle();
140
141 if (
142 $title->getNamespace() == NS_USER ||
143 $title->getNamespace() == NS_USER_TALK
144 ) {
145 $id = User::idFromName( $title->getText() );
146 $ip = User::isIP( $title->getText() );
147
148 # Both anons and non-anons have contributions list
149 if ( $id || $ip ) {
150 $element[] = $this->userContribsLink();
151 }
152
153 if ( $this->getSkin()->showEmailUser( $id ) ) {
154 $element[] = $this->emailUserLink();
155 }
156 }
157
158 $s = implode( $element, $sep );
159
160 if ( $title->getArticleID() ) {
161 $s .= "\n<br />";
162
163 // Delete/protect/move links for privileged users
164 if ( $this->getSkin()->getUser()->isAllowed( 'delete' ) ) {
165 $s .= $this->deleteThisPage();
166 }
167
168 if ( $this->getSkin()->getUser()->isAllowed( 'protect' ) ) {
169 $s .= $sep . $this->protectThisPage();
170 }
171
172 if ( $this->getSkin()->getUser()->isAllowed( 'move' ) ) {
173 $s .= $sep . $this->moveThisPage();
174 }
175 }
176
177 $s .= "<br />\n" . $this->otherLanguages();
178 }
179
180 return $s;
181 }
182
183 function editThisPage() {
184 if ( !$this->getSkin()->getOutput()->isArticleRelated() ) {
185 $s = wfMessage( 'protectedpage' )->text();
186 } else {
187 $title = $this->getSkin()->getTitle();
188 if ( $title->quickUserCan( 'edit' ) && $title->exists() ) {
189 $t = wfMessage( 'editthispage' )->text();
190 } elseif ( $title->quickUserCan( 'create' ) && !$title->exists() ) {
191 $t = wfMessage( 'create-this-page' )->text();
192 } else {
193 $t = wfMessage( 'viewsource' )->text();
194 }
195
196 $s = Linker::linkKnown(
197 $title,
198 $t,
199 array(),
200 $this->getSkin()->editUrlOptions()
201 );
202 }
203
204 return $s;
205 }
206
207 function deleteThisPage() {
208 $diff = $this->getSkin()->getRequest()->getVal( 'diff' );
209 $title = $this->getSkin()->getTitle();
210
211 if ( $title->getArticleID() && ( !$diff ) && $this->getSkin()->getUser()->isAllowed( 'delete' ) ) {
212 $t = wfMessage( 'deletethispage' )->text();
213
214 $s = Linker::linkKnown(
215 $title,
216 $t,
217 array(),
218 array( 'action' => 'delete' )
219 );
220 } else {
221 $s = '';
222 }
223
224 return $s;
225 }
226
227 function protectThisPage() {
228 $diff = $this->getSkin()->getRequest()->getVal( 'diff' );
229 $title = $this->getSkin()->getTitle();
230
231 if ( $title->getArticleID() && ( ! $diff ) && $this->getSkin()->getUser()->isAllowed( 'protect' ) ) {
232 if ( $title->isProtected() ) {
233 $text = wfMessage( 'unprotectthispage' )->text();
234 $query = array( 'action' => 'unprotect' );
235 } else {
236 $text = wfMessage( 'protectthispage' )->text();
237 $query = array( 'action' => 'protect' );
238 }
239
240 $s = Linker::linkKnown(
241 $title,
242 $text,
243 array(),
244 $query
245 );
246 } else {
247 $s = '';
248 }
249
250 return $s;
251 }
252
253 function watchThisPage() {
254 // Cache
255 $title = $this->getSkin()->getTitle();
256
257 if ( $this->getSkin()->getOutput()->isArticleRelated() ) {
258 if ( $this->getSkin()->getUser()->isWatched( $title ) ) {
259 $text = wfMessage( 'unwatchthispage' )->text();
260 $query = array(
261 'action' => 'unwatch',
262 'token' => UnwatchAction::getUnwatchToken( $title, $this->getSkin()->getUser() ),
263 );
264 $id = 'mw-unwatch-link';
265 } else {
266 $text = wfMessage( 'watchthispage' )->text();
267 $query = array(
268 'action' => 'watch',
269 'token' => WatchAction::getWatchToken( $title, $this->getSkin()->getUser() ),
270 );
271 $id = 'mw-watch-link';
272 }
273
274 $s = Linker::linkKnown(
275 $title,
276 $text,
277 array( 'id' => $id ),
278 $query
279 );
280 } else {
281 $s = wfMessage( 'notanarticle' )->text();
282 }
283
284 return $s;
285 }
286
287 function moveThisPage() {
288 if ( $this->getSkin()->getTitle()->quickUserCan( 'move' ) ) {
289 return Linker::linkKnown(
290 SpecialPage::getTitleFor( 'Movepage' ),
291 wfMessage( 'movethispage' )->text(),
292 array(),
293 array( 'target' => $this->getSkin()->getTitle()->getPrefixedDBkey() )
294 );
295 } else {
296 // no message if page is protected - would be redundant
297 return '';
298 }
299 }
300
301 function historyLink() {
302 return Linker::link(
303 $this->getSkin()->getTitle(),
304 wfMessage( 'history' )->escaped(),
305 array( 'rel' => 'archives' ),
306 array( 'action' => 'history' )
307 );
308 }
309
310 function whatLinksHere() {
311 return Linker::linkKnown(
312 SpecialPage::getTitleFor( 'Whatlinkshere', $this->getSkin()->getTitle()->getPrefixedDBkey() ),
313 wfMessage( 'whatlinkshere' )->escaped()
314 );
315 }
316
317 function userContribsLink() {
318 return Linker::linkKnown(
319 SpecialPage::getTitleFor( 'Contributions', $this->getSkin()->getTitle()->getDBkey() ),
320 wfMessage( 'contributions' )->escaped()
321 );
322 }
323
324 function emailUserLink() {
325 return Linker::linkKnown(
326 SpecialPage::getTitleFor( 'Emailuser', $this->getSkin()->getTitle()->getDBkey() ),
327 wfMessage( 'emailuser' )->escaped()
328 );
329 }
330
331 function watchPageLinksLink() {
332 if ( !$this->getSkin()->getOutput()->isArticleRelated() ) {
333 return wfMessage( 'parentheses', wfMessage( 'notanarticle' )->text() )->escaped();
334 } else {
335 return Linker::linkKnown(
336 SpecialPage::getTitleFor( 'Recentchangeslinked', $this->getSkin()->getTitle()->getPrefixedDBkey() ),
337 wfMessage( 'recentchangeslinked-toolbox' )->escaped()
338 );
339 }
340 }
341
342 function talkLink() {
343 $title = $this->getSkin()->getTitle();
344 if ( NS_SPECIAL == $title->getNamespace() ) {
345 # No discussion links for special pages
346 return '';
347 }
348
349 $linkOptions = array();
350
351 if ( $title->isTalkPage() ) {
352 $link = $title->getSubjectPage();
353 switch( $link->getNamespace() ) {
354 case NS_MAIN:
355 $text = wfMessage( 'articlepage' );
356 break;
357 case NS_USER:
358 $text = wfMessage( 'userpage' );
359 break;
360 case NS_PROJECT:
361 $text = wfMessage( 'projectpage' );
362 break;
363 case NS_FILE:
364 $text = wfMessage( 'imagepage' );
365 # Make link known if image exists, even if the desc. page doesn't.
366 if ( wfFindFile( $link ) )
367 $linkOptions[] = 'known';
368 break;
369 case NS_MEDIAWIKI:
370 $text = wfMessage( 'mediawikipage' );
371 break;
372 case NS_TEMPLATE:
373 $text = wfMessage( 'templatepage' );
374 break;
375 case NS_HELP:
376 $text = wfMessage( 'viewhelppage' );
377 break;
378 case NS_CATEGORY:
379 $text = wfMessage( 'categorypage' );
380 break;
381 default:
382 $text = wfMessage( 'articlepage' );
383 }
384 } else {
385 $link = $title->getTalkPage();
386 $text = wfMessage( 'talkpage' );
387 }
388
389 $s = Linker::link( $link, $text->text(), array(), array(), $linkOptions );
390
391 return $s;
392 }
393
394 /**
395 * @return string
396 *
397 * @fixed
398 */
399 function beforeContent() {
400 ob_start();
401 ?>
402 <div id="content">
403 <div id="topbar">
404 <p id="sitetitle">
405 <a href="<?php echo htmlspecialchars( $this->data['nav_urls']['mainpage']['href'] ) ?>">
406 <?php echo wfMessage( 'sitetitle' )->escaped() ?>
407 </a>
408 </p>
409 <p id="sitesub"><?php echo wfMessage( 'sitesubtitle' )->escaped() ?></p>
410
411 <div id="toplinks">
412 <p id="syslinks"><?php echo $this->sysLinks() ?></p>
413 <p id="variantlinks"><?php echo $this->variantLinks() ?></p>
414 </div>
415 <div id="linkcollection">
416 <div id="langlinks"><?php echo str_replace( '<br />', '', $this->otherLanguages() ) ?></div>
417 <?php echo $this->getSkin()->getCategories() ?>
418 <div id="titlelinks"><?php echo $this->pageTitleLinks() ?></div>
419 <?php if ( $this->data['newtalk'] ) { ?>
420 <div class="usermessage"><strong><?php echo $this->data['newtalk'] ?></strong></div>
421 <?php } ?>
422 </div>
423 </div>
424 <div id="article">
425 <?php if ( $this->getSkin()->getSiteNotice() ) { ?>
426 <div id="siteNotice"><?php echo $this->getSkin()->getSiteNotice() ?></div>
427 <?php } ?>
428 <h1 id="firstHeading"><span dir="auto"><?php echo $this->data['title'] ?></span></h1>
429 <?php if ( $this->translator->translate( 'tagline' ) ) { ?>
430 <p class="tagline"><?php echo htmlspecialchars( $this->translator->translate( 'tagline' ) ) ?></p>
431 <?php } ?>
432 <?php if ( $this->getSkin()->getOutput()->getSubtitle() ) { ?>
433 <p class="subtitle"><?php echo $this->getSkin()->getOutput()->getSubtitle() ?></p>
434 <?php } ?>
435 <?php if ( $this->getSkin()->subPageSubtitle() ) { ?>
436 <p class="subpages"><?php echo $this->getSkin()->subPageSubtitle() ?></p>
437 <?php } ?>
438 <?php
439 $s = ob_get_contents();
440 ob_end_clean();
441
442 return $s;
443 }
444
445 /**
446 * @return string
447 *
448 * @fixed
449 */
450 function afterContent() {
451 ob_start();
452 ?>
453 </div>
454 <div id='footer'>
455 <?php
456 // Page-related links
457 echo $this->bottomLinks();
458 echo "\n<br />";
459
460 // Footer and second searchbox
461 echo $this->getSkin()->getLanguage()->pipeList( array(
462 $this->getSkin()->mainPageLink(),
463 $this->getSkin()->aboutLink(),
464 $this->searchForm( 'footer' )
465 ) );
466 echo "\n<br />";
467
468 // Standard footer info
469 $footlinks = $this->getFooterLinks();
470 if ( $footlinks['info'] ) {
471 foreach ( $footlinks['info'] as $item ) {
472 echo $this->data[$item] . ' ';
473 }
474 }
475 ?>
476 </div>
477 </div>
478 <?php echo $this->quickBar() ?>
479 <?php
480 $s = ob_get_contents();
481 ob_end_clean();
482
483 return $s;
484 }
485
486 /**
487 * @return string
488 *
489 * @fixed
490 */
491 function sysLinks() {
492 $s = array(
493 $this->getSkin()->mainPageLink(),
494 Linker::linkKnown(
495 Title::newFromText( wfMessage( 'aboutpage' )->inContentLanguage()->text() ),
496 wfMessage( 'about' )->text()
497 ),
498 Linker::linkKnown(
499 Title::newFromText( wfMessage( 'helppage' )->inContentLanguage()->text() ),
500 wfMessage( 'help' )->text()
501 ),
502 Linker::linkKnown(
503 Title::newFromText( wfMessage( 'faqpage' )->inContentLanguage()->text() ),
504 wfMessage( 'faq' )->text()
505 ),
506 );
507
508 $personalUrls = $this->getPersonalTools();
509 foreach ( array ( 'logout', 'createaccount', 'login', 'anonlogin' ) as $key ) {
510 if ( $personalUrls[$key] ) {
511 $s[] = $this->makeListItem( $key, $personalUrls[$key], array( 'tag' => 'span' ) );
512 }
513 }
514
515 return $this->getSkin()->getLanguage()->pipeList( $s );
516 }
517
518
519
520
521 /**
522 * @param $heading string
523 * @return string
524 *
525 * @fixed
526 */
527 function menuHead( $heading ) {
528 return "\n<h6>" . htmlspecialchars( $heading ) . "</h6>";
529 }
530
531 /**
532 * Compute the sidebar
533 * @access private
534 *
535 * @return string
536 *
537 * @fixed
538 */
539 function quickBar(){
540 $s = "\n<div id='quickbar'>";
541
542 $sep = "<br />\n";
543
544 $plain_bar = $this->data['sidebar'];
545 $bar = array();
546
547 // Massage the sidebar
548 // We want to place SEARCH at the beginning and a lot of stuff before TOOLBOX (or at the end, if it's missing)
549 $additions_done = false;
550 while ( !$additions_done ) {
551 $bar = array(); // Empty it out
552
553 // Always display search on top
554 $bar['SEARCH'] = true;
555
556 foreach ( $plain_bar as $heading => $links ) {
557 if ( $heading == 'TOOLBOX' ) {
558 if( $links !== NULL ) {
559 // If this is not a toolbox prosthetic we inserted outselves, fill it out
560 $plain_bar['TOOLBOX'] = $this->getToolbox();
561 }
562
563 // And insert the stuff
564
565 // "This page" and "Edit" menus
566 // We need to do some massaging here... we reuse all of the items, except for $...['views']['view'],
567 // as $...['namespaces']['main'] and $...['namespaces']['talk'] together serve the same purpose.
568 // We also don't use $...['variants'], these are displayed in the top menu.
569 $content_navigation = $this->data['content_navigation'];
570 $qbpageoptions = array_merge(
571 $content_navigation['namespaces'],
572 array(
573 'history' => $content_navigation['views']['history'],
574 'watch' => $content_navigation['actions']['watch'],
575 'unwatch' => $content_navigation['actions']['unwatch'],
576 )
577 );
578 $content_navigation['actions']['watch'] = null;
579 $content_navigation['actions']['unwatch'] = null;
580 $qbedit = array_merge(
581 array(
582 'edit' => $content_navigation['views']['edit'],
583 'addsection' => $content_navigation['views']['addsection'],
584 ),
585 $content_navigation['actions']
586 );
587 $bar['qbedit'] = $qbedit;
588 $bar['qbpageoptions'] = $qbpageoptions;
589
590 // Personal tools ("My pages")
591 $bar['qbmyoptions'] = $this->getPersonalTools();
592 foreach ( array ( 'logout', 'createaccount', 'login', 'anonlogin' ) as $key ) {
593 $bar['qbmyoptions'][$key] = false;
594 }
595
596 $additions_done = true;
597 }
598
599 // Re-insert current heading, unless it's SEARCH
600 if ( $heading != 'SEARCH' ) {
601 $bar[$heading] = $plain_bar[$heading];
602 }
603 }
604
605 // If TOOLBOX is missing, $additions_done is still false
606 if ( !$additions_done ) {
607 $plain_bar['TOOLBOX'] = false;
608 }
609 }
610
611 foreach ( $bar as $heading => $links ) {
612 if ( $heading == 'SEARCH' ) {
613 $s .= $this->menuHead( wfMessage( 'qbfind' )->text() );
614 $s .= $this->searchForm( 'sidebar' );
615 } elseif ( $heading == 'LANGUAGES' ) {
616 // discard these; we display languages below page content
617 } else {
618 if ( $links ) {
619 // Use the navigation heading from standard sidebar as the "browse" section
620 if ( $heading == 'navigation' ) {
621 $heading = 'qbbrowse';
622 }
623 if ( $heading == 'TOOLBOX' ) {
624 $heading = 'toolbox';
625 }
626
627 $headingMsg = wfMessage( $heading );
628 $any_link = false;
629 $t = $this->menuHead( $headingMsg->exists() ? $headingMsg->text() : $heading );
630
631 foreach ( $links as $key => $link ) {
632 // Can be empty due to rampant sidebar massaging we're doing above
633 if ( $link ) {
634 $any_link = true;
635 $t .= $this->makeListItem( $key, $link, array( 'tag' => 'span' ) ) . $sep;
636 }
637 }
638
639 if ( $any_link ) {
640 $s .= $t;
641 }
642 }
643 }
644 }
645
646 $s .= $sep . "\n</div>\n";
647 return $s;
648 }
649
650 /**
651 * @param $label string
652 * @return string
653 *
654 * @fixed
655 */
656 function searchForm( $which ) {
657 global $wgUseTwoButtonsSearchForm;
658
659 $search = $this->getSkin()->getRequest()->getText( 'search' );
660 $action = $this->data['searchaction'];
661 $s = "<form id=\"searchform-" . htmlspecialchars($which) . "\" method=\"get\" class=\"inline\" action=\"$action\">";
662 if( $which == 'footer' ) {
663 $s .= wfMessage( 'qbfind' )->text() . ": ";
664 }
665
666 $s .= "<input type='text' class=\"mw-searchInput\" name=\"search\" size=\"14\" value=\""
667 . htmlspecialchars( substr( $search, 0, 256 ) ) . "\" />"
668 . ($which == 'footer' ? " " : "<br />")
669 . "<input type='submit' class=\"searchButton\" name=\"go\" value=\"" . wfMessage( 'searcharticle' )->escaped() . "\" />";
670
671 if( $wgUseTwoButtonsSearchForm ) {
672 $s .= " <input type='submit' class=\"searchButton\" name=\"fulltext\" value=\"" . wfMessage( 'search' )->escaped() . "\" />\n";
673 } else {
674 $s .= '<div><a href="' . $action . '" rel="search">' . wfMessage( 'powersearch-legend' )->escaped() . "</a></div>\n";
675 }
676
677 $s .= '</form>';
678
679 return $s;
680 }
681 }