]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/SkinTemplate.php
MediaWiki 1.16.4
[autoinstalls/mediawiki.git] / includes / SkinTemplate.php
1 <?php
2 if ( ! defined( 'MEDIAWIKI' ) )
3         die( 1 );
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 /**
21  * Wrapper object for MediaWiki's localization functions,
22  * to be passed to the template engine.
23  *
24  * @private
25  * @ingroup Skins
26  */
27 class MediaWiki_I18N {
28         var $_context = array();
29
30         function set( $varName, $value ) {
31                 $this->_context[$varName] = $value;
32         }
33
34         function translate( $value ) {
35                 wfProfileIn( __METHOD__ );
36
37                 // Hack for i18n:attributes in PHPTAL 1.0.0 dev version as of 2004-10-23
38                 $value = preg_replace( '/^string:/', '', $value );
39
40                 $value = wfMsg( $value );
41                 // interpolate variables
42                 $m = array();
43                 while( preg_match( '/\$([0-9]*?)/sm', $value, $m ) ) {
44                         list( $src, $var ) = $m;
45                         wfSuppressWarnings();
46                         $varValue = $this->_context[$var];
47                         wfRestoreWarnings();
48                         $value = str_replace( $src, $varValue, $value );
49                 }
50                 wfProfileOut( __METHOD__ );
51                 return $value;
52         }
53 }
54
55 /**
56  * Template-filler skin base class
57  * Formerly generic PHPTal (http://phptal.sourceforge.net/) skin
58  * Based on Brion's smarty skin
59  * @copyright Copyright © Gabriel Wicke -- http://www.aulinx.de/
60  *
61  * @todo Needs some serious refactoring into functions that correspond
62  * to the computations individual esi snippets need. Most importantly no body
63  * parsing for most of those of course.
64  *
65  * @ingroup Skins
66  */
67 class SkinTemplate extends Skin {
68         /**#@+
69          * @private
70          */
71
72         /**
73          * Name of our skin, it probably needs to be all lower case.  Child classes
74          * should override the default.
75          */
76         var $skinname = 'monobook';
77
78         /**
79          * Stylesheets set to use.  Subdirectory in skins/ where various stylesheets
80          * are located.  Child classes should override the default.
81          */
82         var $stylename = 'monobook';
83
84         /**
85          * For QuickTemplate, the name of the subclass which will actually fill the
86          * template.  Child classes should override the default.
87          */
88         var $template = 'QuickTemplate';
89
90         /**
91          * Whether this skin use OutputPage::headElement() to generate the <head>
92          * tag
93          */
94         var $useHeadElement = false;
95
96         /**#@-*/
97
98         /**
99          * Add specific styles for this skin
100          *
101          * @param $out OutputPage
102          */
103         function setupSkinUserCss( OutputPage $out ){
104                 $out->addStyle( 'common/shared.css', 'screen' );
105                 $out->addStyle( 'common/commonPrint.css', 'print' );
106         }
107
108         /**
109          * Create the template engine object; we feed it a bunch of data
110          * and eventually it spits out some HTML. Should have interface
111          * roughly equivalent to PHPTAL 0.7.
112          *
113          * @param $callback string (or file)
114          * @param $repository string: subdirectory where we keep template files
115          * @param $cache_dir string
116          * @return object
117          * @private
118          */
119         function setupTemplate( $classname, $repository = false, $cache_dir = false ) {
120                 return new $classname();
121         }
122
123         /**
124          * initialize various variables and generate the template
125          *
126          * @param $out OutputPage
127          */
128         function outputPage( OutputPage $out ) {
129                 global $wgArticle, $wgUser, $wgLang, $wgContLang;
130                 global $wgScript, $wgStylePath, $wgContLanguageCode;
131                 global $wgMimeType, $wgJsMimeType, $wgOutputEncoding, $wgRequest;
132                 global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces, $wgHtml5Version;
133                 global $wgDisableCounters, $wgLogo, $wgHideInterlanguageLinks;
134                 global $wgMaxCredits, $wgShowCreditsIfMax;
135                 global $wgPageShowWatchingUsers;
136                 global $wgUseTrackbacks, $wgUseSiteJs, $wgDebugComments;
137                 global $wgArticlePath, $wgScriptPath, $wgServer;
138
139                 wfProfileIn( __METHOD__ );
140
141                 $oldid = $wgRequest->getVal( 'oldid' );
142                 $diff = $wgRequest->getVal( 'diff' );
143                 $action = $wgRequest->getVal( 'action', 'view' );
144
145                 wfProfileIn( __METHOD__ . '-init' );
146                 $this->initPage( $out );
147
148                 $this->setMembers();
149                 $tpl = $this->setupTemplate( $this->template, 'skins' );
150
151                 #if ( $wgUseDatabaseMessages ) { // uncomment this to fall back to GetText
152                 $tpl->setTranslator( new MediaWiki_I18N() );
153                 #}
154                 wfProfileOut( __METHOD__ . '-init' );
155
156                 wfProfileIn( __METHOD__ . '-stuff' );
157                 $this->thispage = $this->mTitle->getPrefixedDBkey();
158                 $this->thisurl = $this->mTitle->getPrefixedURL();
159                 $query = array();
160                 if ( !$wgRequest->wasPosted() ) {
161                         $query = $wgRequest->getValues();
162                         unset( $query['title'] );
163                         unset( $query['returnto'] );
164                         unset( $query['returntoquery'] );
165                 }
166                 $this->thisquery = wfUrlencode( wfArrayToCGI( $query ) );
167                 $this->loggedin = $wgUser->isLoggedIn();
168                 $this->iscontent = ( $this->mTitle->getNamespace() != NS_SPECIAL );
169                 $this->iseditable = ( $this->iscontent and !( $action == 'edit' or $action == 'submit' ) );
170                 $this->username = $wgUser->getName();
171
172                 if ( $wgUser->isLoggedIn() || $this->showIPinHeader() ) {
173                         $this->userpageUrlDetails = self::makeUrlDetails( $this->userpage );
174                 } else {
175                         # This won't be used in the standard skins, but we define it to preserve the interface
176                         # To save time, we check for existence
177                         $this->userpageUrlDetails = self::makeKnownUrlDetails( $this->userpage );
178                 }
179
180                 $this->titletxt = $this->mTitle->getPrefixedText();
181                 wfProfileOut( __METHOD__ . '-stuff' );
182
183                 wfProfileIn( __METHOD__ . '-stuff-head' );
184                 if ( $this->useHeadElement ) {
185                         $pagecss = $this->setupPageCss();
186                         if( $pagecss )
187                                 $out->addInlineStyle( $pagecss );
188                 } else {
189                         $this->setupUserCss( $out );
190
191                         $tpl->set( 'pagecss', $this->setupPageCss() );
192                         $tpl->setRef( 'usercss', $this->usercss );
193
194                         $this->userjs = $this->userjsprev = false;
195                         $this->setupUserJs( $out->isUserJsAllowed() );
196                         $tpl->setRef( 'userjs', $this->userjs );
197                         $tpl->setRef( 'userjsprev', $this->userjsprev );
198
199                         if( $wgUseSiteJs ) {
200                                 $jsCache = $this->loggedin ? '&smaxage=0' : '';
201                                 $tpl->set( 'jsvarurl',
202                                                   self::makeUrl( '-',
203                                                                                 "action=raw$jsCache&gen=js&useskin=" .
204                                                                                 urlencode( $this->getSkinName() ) ) );
205                         } else {
206                                 $tpl->set( 'jsvarurl', false );
207                         }
208
209                         $tpl->setRef( 'xhtmldefaultnamespace', $wgXhtmlDefaultNamespace );
210                         $tpl->set( 'xhtmlnamespaces', $wgXhtmlNamespaces );
211                         $tpl->set( 'html5version', $wgHtml5Version );
212                         $tpl->set( 'headlinks', $out->getHeadLinks() );
213                         $tpl->set( 'csslinks', $out->buildCssLinks() );
214
215                         if( $wgUseTrackbacks && $out->isArticleRelated() ) {
216                                 $tpl->set( 'trackbackhtml', $out->getTitle()->trackbackRDF() );
217                         } else {
218                                 $tpl->set( 'trackbackhtml', null );
219                         }
220                 }
221                 wfProfileOut( __METHOD__ . '-stuff-head' );
222
223                 wfProfileIn( __METHOD__ . '-stuff2' );
224                 $tpl->set( 'title', $out->getPageTitle() );
225                 $tpl->set( 'pagetitle', $out->getHTMLTitle() );
226                 $tpl->set( 'displaytitle', $out->mPageLinkTitle );
227                 $tpl->set( 'pageclass', $this->getPageClasses( $this->mTitle ) );
228                 $tpl->set( 'skinnameclass', ( 'skin-' . Sanitizer::escapeClass( $this->getSkinName() ) ) );
229
230                 $nsname = MWNamespace::exists( $this->mTitle->getNamespace() ) ?
231                                         MWNamespace::getCanonicalName( $this->mTitle->getNamespace() ) :
232                                         $this->mTitle->getNsText();
233
234                 $tpl->set( 'nscanonical', $nsname );
235                 $tpl->set( 'nsnumber', $this->mTitle->getNamespace() );
236                 $tpl->set( 'titleprefixeddbkey', $this->mTitle->getPrefixedDBKey() );
237                 $tpl->set( 'titletext', $this->mTitle->getText() );
238                 $tpl->set( 'articleid', $this->mTitle->getArticleId() );
239                 $tpl->set( 'currevisionid', isset( $wgArticle ) ? $wgArticle->getLatest() : 0 );
240
241                 $tpl->set( 'isarticle', $out->isArticle() );
242
243                 $tpl->setRef( 'thispage', $this->thispage );
244                 $subpagestr = $this->subPageSubtitle();
245                 $tpl->set(
246                         'subtitle', !empty( $subpagestr ) ?
247                         '<span class="subpages">'.$subpagestr.'</span>'.$out->getSubtitle() :
248                         $out->getSubtitle()
249                 );
250                 $undelete = $this->getUndeleteLink();
251                 $tpl->set(
252                         'undelete', !empty( $undelete ) ?
253                         '<span class="subpages">'.$undelete.'</span>' :
254                         ''
255                 );
256
257                 $tpl->set( 'catlinks', $this->getCategories() );
258                 if( $out->isSyndicated() ) {
259                         $feeds = array();
260                         foreach( $out->getSyndicationLinks() as $format => $link ) {
261                                 $feeds[$format] = array(
262                                         'text' => wfMsg( "feed-$format" ),
263                                         'href' => $link
264                                 );
265                         }
266                         $tpl->setRef( 'feeds', $feeds );
267                 } else {
268                         $tpl->set( 'feeds', false );
269                 }
270
271                 $tpl->setRef( 'mimetype', $wgMimeType );
272                 $tpl->setRef( 'jsmimetype', $wgJsMimeType );
273                 $tpl->setRef( 'charset', $wgOutputEncoding );
274                 $tpl->setRef( 'wgScript', $wgScript );
275                 $tpl->setRef( 'skinname', $this->skinname );
276                 $tpl->set( 'skinclass', get_class( $this ) );
277                 $tpl->setRef( 'stylename', $this->stylename );
278                 $tpl->set( 'printable', $out->isPrintable() );
279                 $tpl->set( 'handheld', $wgRequest->getBool( 'handheld' ) );
280                 $tpl->setRef( 'loggedin', $this->loggedin );
281                 $tpl->set( 'notspecialpage', $this->mTitle->getNamespace() != NS_SPECIAL );
282                 /* XXX currently unused, might get useful later
283                 $tpl->set( "editable", ($this->mTitle->getNamespace() != NS_SPECIAL ) );
284                 $tpl->set( "exists", $this->mTitle->getArticleID() != 0 );
285                 $tpl->set( "watch", $this->mTitle->userIsWatching() ? "unwatch" : "watch" );
286                 $tpl->set( "protect", count($this->mTitle->isProtected()) ? "unprotect" : "protect" );
287                 $tpl->set( "helppage", wfMsg('helppage'));
288                 */
289                 $tpl->set( 'searchaction', $this->escapeSearchLink() );
290                 $tpl->set( 'searchtitle', SpecialPage::getTitleFor( 'Search' )->getPrefixedDBKey() );
291                 $tpl->set( 'search', trim( $wgRequest->getVal( 'search' ) ) );
292                 $tpl->setRef( 'stylepath', $wgStylePath );
293                 $tpl->setRef( 'articlepath', $wgArticlePath );
294                 $tpl->setRef( 'scriptpath', $wgScriptPath );
295                 $tpl->setRef( 'serverurl', $wgServer );
296                 $tpl->setRef( 'logopath', $wgLogo );
297                 $tpl->setRef( 'lang', $wgContLanguageCode );
298                 $tpl->set( 'dir', $wgContLang->getDir() );
299                 $tpl->set( 'rtl', $wgContLang->isRTL() );
300                 $tpl->set( 'capitalizeallnouns', $wgLang->capitalizeAllNouns() ? ' capitalize-all-nouns' : '' );
301                 $tpl->set( 'langname', $wgContLang->getLanguageName( $wgContLanguageCode ) );
302                 $tpl->set( 'showjumplinks', $wgUser->getOption( 'showjumplinks' ) );
303                 $tpl->set( 'username', $wgUser->isAnon() ? null : $this->username );
304                 $tpl->setRef( 'userpage', $this->userpage );
305                 $tpl->setRef( 'userpageurl', $this->userpageUrlDetails['href'] );
306                 $tpl->set( 'userlang', $wgLang->getCode() );
307
308                 // Users can have their language set differently than the
309                 // content of the wiki. For these users, tell the web browser
310                 // that interface elements are in a different language.
311                 $tpl->set( 'userlangattributes', '');
312                 $tpl->set( 'specialpageattributes', '');
313
314                 $lang = $wgLang->getCode();
315                 $dir  = $wgLang->getDir();
316                 if ( $lang !== $wgContLang->getCode() || $dir !== $wgContLang->getDir() ) {
317                         $attrs = " lang='$lang' dir='$dir'";
318
319                         $tpl->set( 'userlangattributes', $attrs );
320
321                         // The content of SpecialPages should be presented in the
322                         // user's language. Content of regular pages should not be touched.
323                         if($this->mTitle->isSpecialPage()) {
324                                 $tpl->set( 'specialpageattributes', $attrs );
325                         }
326                 }
327
328                 $newtalks = $wgUser->getNewMessageLinks();
329                 $ntl = '';
330
331                 if( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
332                         $usertitle = $this->mUser->getUserPage();
333                         $usertalktitle = $usertitle->getTalkPage();
334
335                         if( !$usertalktitle->equals( $this->mTitle ) ) {
336                                 $newmessageslink = $this->link(
337                                         $usertalktitle,
338                                         wfMsgHtml( 'newmessageslink' ),
339                                         array(),
340                                         array( 'redirect' => 'no' ),
341                                         array( 'known', 'noclasses' )
342                                 );
343
344                                 $newmessagesdifflink = $this->link(
345                                         $usertalktitle,
346                                         wfMsgHtml( 'newmessagesdifflink' ),
347                                         array(),
348                                         array( 'diff' => 'cur' ),
349                                         array( 'known', 'noclasses' )
350                                 );
351
352                                 $ntl = wfMsg(
353                                         'youhavenewmessages',
354                                         $newmessageslink,
355                                         $newmessagesdifflink
356                                 );
357                                 # Disable Cache
358                                 $out->setSquidMaxage( 0 );
359                         }
360                 } else if( count( $newtalks ) ) {
361                         // _>" " for BC <= 1.16
362                         $sep = str_replace( '_', ' ', wfMsgHtml( 'newtalkseparator' ) );
363                         $msgs = array();
364                         foreach( $newtalks as $newtalk ) {
365                                 $msgs[] = Xml::element('a',
366                                         array( 'href' => $newtalk['link'] ), $newtalk['wiki'] );
367                         }
368                         $parts = implode( $sep, $msgs );
369                         $ntl = wfMsgHtml( 'youhavenewmessagesmulti', $parts );
370                         $out->setSquidMaxage( 0 );
371                 }
372                 wfProfileOut( __METHOD__ . '-stuff2' );
373
374                 wfProfileIn( __METHOD__ . '-stuff3' );
375                 $tpl->setRef( 'newtalk', $ntl );
376                 $tpl->setRef( 'skin', $this );
377                 $tpl->set( 'logo', $this->logoText() );
378                 if ( $out->isArticle() and ( !isset( $oldid ) or isset( $diff ) ) and
379                         $wgArticle and 0 != $wgArticle->getID() ){
380                         if ( !$wgDisableCounters ) {
381                                 $viewcount = $wgLang->formatNum( $wgArticle->getCount() );
382                                 if ( $viewcount ) {
383                                         $tpl->set( 'viewcount', wfMsgExt( 'viewcount', array( 'parseinline' ), $viewcount ) );
384                                 } else {
385                                         $tpl->set( 'viewcount', false );
386                                 }
387                         } else {
388                                 $tpl->set( 'viewcount', false );
389                         }
390
391                         if( $wgPageShowWatchingUsers ) {
392                                 $dbr = wfGetDB( DB_SLAVE );
393                                 $watchlist = $dbr->tableName( 'watchlist' );
394                                 $res = $dbr->select( 'watchlist',
395                                         array( 'COUNT(*) AS n' ),
396                                         array( 'wl_title' => $dbr->strencode( $this->mTitle->getDBkey() ), 'wl_namespace' => $this->mTitle->getNamespace() ),
397                                         __METHOD__
398                                 );
399                                 $x = $dbr->fetchObject( $res );
400                                 $numberofwatchingusers = $x->n;
401                                 if( $numberofwatchingusers > 0 ) {
402                                         $tpl->set( 'numberofwatchingusers',
403                                                 wfMsgExt( 'number_of_watching_users_pageview', array( 'parseinline' ),
404                                                 $wgLang->formatNum( $numberofwatchingusers ) )
405                                         );
406                                 } else {
407                                         $tpl->set( 'numberofwatchingusers', false );
408                                 }
409                         } else {
410                                 $tpl->set( 'numberofwatchingusers', false );
411                         }
412
413                         $tpl->set( 'copyright', $this->getCopyright() );
414
415                         $this->credits = false;
416
417                         if( $wgMaxCredits != 0 ){
418                                 $this->credits = Credits::getCredits( $wgArticle, $wgMaxCredits, $wgShowCreditsIfMax );
419                         } else {
420                                 $tpl->set( 'lastmod', $this->lastModified() );
421                         }
422
423                         $tpl->setRef( 'credits', $this->credits );
424
425                 } elseif ( isset( $oldid ) && !isset( $diff ) ) {
426                         $tpl->set( 'copyright', $this->getCopyright() );
427                         $tpl->set( 'viewcount', false );
428                         $tpl->set( 'lastmod', false );
429                         $tpl->set( 'credits', false );
430                         $tpl->set( 'numberofwatchingusers', false );
431                 } else {
432                         $tpl->set( 'copyright', false );
433                         $tpl->set( 'viewcount', false );
434                         $tpl->set( 'lastmod', false );
435                         $tpl->set( 'credits', false );
436                         $tpl->set( 'numberofwatchingusers', false );
437                 }
438                 wfProfileOut( __METHOD__ . '-stuff3' );
439
440                 wfProfileIn( __METHOD__ . '-stuff4' );
441                 $tpl->set( 'copyrightico', $this->getCopyrightIcon() );
442                 $tpl->set( 'poweredbyico', $this->getPoweredBy() );
443                 $tpl->set( 'disclaimer', $this->disclaimerLink() );
444                 $tpl->set( 'privacy', $this->privacyLink() );
445                 $tpl->set( 'about', $this->aboutLink() );
446
447                 if ( $wgDebugComments ) {
448                         $tpl->setRef( 'debug', $out->mDebugtext );
449                 } else {
450                         $tpl->set( 'debug', '' );
451                 }
452
453                 $tpl->set( 'reporttime', wfReportTime() );
454                 $tpl->set( 'sitenotice', wfGetSiteNotice() );
455                 $tpl->set( 'bottomscripts', $this->bottomScripts() );
456
457                 $printfooter = "<div class=\"printfooter\">\n" . $this->printSource() . "</div>\n";
458                 $out->mBodytext .= $printfooter . $this->generateDebugHTML();
459                 $tpl->setRef( 'bodytext', $out->mBodytext );
460
461                 # Language links
462                 $language_urls = array();
463
464                 if ( !$wgHideInterlanguageLinks ) {
465                         foreach( $out->getLanguageLinks() as $l ) {
466                                 $tmp = explode( ':', $l, 2 );
467                                 $class = 'interwiki-' . $tmp[0];
468                                 unset( $tmp );
469                                 $nt = Title::newFromText( $l );
470                                 if ( $nt ) {
471                                         $language_urls[] = array(
472                                                 'href' => $nt->getFullURL(),
473                                                 'text' => ( $wgContLang->getLanguageName( $nt->getInterwiki() ) != '' ?
474                                                                         $wgContLang->getLanguageName( $nt->getInterwiki() ) : $l ),
475                                                 'class' => $class
476                                         );
477                                 }
478                         }
479                 }
480                 if( count( $language_urls ) ) {
481                         $tpl->setRef( 'language_urls', $language_urls );
482                 } else {
483                         $tpl->set( 'language_urls', false );
484                 }
485                 wfProfileOut( __METHOD__ . '-stuff4' );
486
487                 wfProfileIn( __METHOD__ . '-stuff5' );
488                 # Personal toolbar
489                 $tpl->set( 'personal_urls', $this->buildPersonalUrls() );
490                 $content_actions = $this->buildContentActionUrls();
491                 $tpl->setRef( 'content_actions', $content_actions );
492
493                 $tpl->set( 'sidebar', $this->buildSidebar() );
494                 $tpl->set( 'nav_urls', $this->buildNavUrls() );
495
496                 // Set the head scripts near the end, in case the above actions resulted in added scripts
497                 if ( $this->useHeadElement ) {
498                         $tpl->set( 'headelement', $out->headElement( $this ) );
499                 } else {
500                         $tpl->set( 'headscripts', $out->getScript() );
501                 }
502
503                 // original version by hansm
504                 if( !wfRunHooks( 'SkinTemplateOutputPageBeforeExec', array( &$this, &$tpl ) ) ) {
505                         wfDebug( __METHOD__ . ": Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!\n" );
506                 }
507
508                 // allow extensions adding stuff after the page content.
509                 // See Skin::afterContentHook() for further documentation.
510                 $tpl->set( 'dataAfterContent', $this->afterContentHook() );
511                 wfProfileOut( __METHOD__ . '-stuff5' );
512
513                 // execute template
514                 wfProfileIn( __METHOD__ . '-execute' );
515                 $res = $tpl->execute();
516                 wfProfileOut( __METHOD__ . '-execute' );
517
518                 // result may be an error
519                 $this->printOrError( $res );
520                 wfProfileOut( __METHOD__ );
521         }
522
523         /**
524          * Output the string, or print error message if it's
525          * an error object of the appropriate type.
526          * For the base class, assume strings all around.
527          *
528          * @param mixed $str
529          * @private
530          */
531         function printOrError( $str ) {
532                 echo $str;
533         }
534
535         /**
536          * build array of urls for personal toolbar
537          * @return array
538          * @private
539          */
540         function buildPersonalUrls() {
541                 global $wgOut, $wgRequest;
542
543                 $title = $wgOut->getTitle();
544                 $pageurl = $title->getLocalURL();
545                 wfProfileIn( __METHOD__ );
546
547                 /* set up the default links for the personal toolbar */
548                 $personal_urls = array();
549                 $page = $wgRequest->getVal( 'returnto', $this->thisurl );
550                 $query = $wgRequest->getVal( 'returntoquery', $this->thisquery );
551                 $returnto = "returnto=$page";
552                 if( $this->thisquery != '' )
553                         $returnto .= "&returntoquery=$query";
554                 if( $this->loggedin ) {
555                         $personal_urls['userpage'] = array(
556                                 'text' => $this->username,
557                                 'href' => &$this->userpageUrlDetails['href'],
558                                 'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
559                                 'active' => ( $this->userpageUrlDetails['href'] == $pageurl )
560                         );
561                         $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
562                         $personal_urls['mytalk'] = array(
563                                 'text' => wfMsg( 'mytalk' ),
564                                 'href' => &$usertalkUrlDetails['href'],
565                                 'class' => $usertalkUrlDetails['exists'] ? false : 'new',
566                                 'active' => ( $usertalkUrlDetails['href'] == $pageurl )
567                         );
568                         $href = self::makeSpecialUrl( 'Preferences' );
569                         $personal_urls['preferences'] = array(
570                                 'text' => wfMsg( 'mypreferences' ),
571                                 'href' => $href,
572                                 'active' => ( $href == $pageurl )
573                         );
574                         $href = self::makeSpecialUrl( 'Watchlist' );
575                         $personal_urls['watchlist'] = array(
576                                 'text' => wfMsg( 'mywatchlist' ),
577                                 'href' => $href,
578                                 'active' => ( $href == $pageurl )
579                         );
580
581                         # We need to do an explicit check for Special:Contributions, as we
582                         # have to match both the title, and the target (which could come
583                         # from request values or be specified in "sub page" form. The plot
584                         # thickens, because $wgTitle is altered for special pages, so doesn't
585                         # contain the original alias-with-subpage.
586                         $origTitle = Title::newFromText( $wgRequest->getText( 'title' ) );
587                         if( $origTitle instanceof Title && $origTitle->getNamespace() == NS_SPECIAL ) {
588                                 list( $spName, $spPar ) =
589                                         SpecialPage::resolveAliasWithSubpage( $origTitle->getText() );
590                                 $active = $spName == 'Contributions'
591                                         && ( ( $spPar && $spPar == $this->username )
592                                                 || $wgRequest->getText( 'target' ) == $this->username );
593                         } else {
594                                 $active = false;
595                         }
596
597                         $href = self::makeSpecialUrlSubpage( 'Contributions', $this->username );
598                         $personal_urls['mycontris'] = array(
599                                 'text' => wfMsg( 'mycontris' ),
600                                 'href' => $href,
601                                 'active' => $active
602                         );
603                         $personal_urls['logout'] = array(
604                                 'text' => wfMsg( 'userlogout' ),
605                                 'href' => self::makeSpecialUrl( 'Userlogout',
606                                         $title->isSpecial( 'Preferences' ) ? '' : $returnto
607                                 ),
608                                 'active' => false
609                         );
610                 } else {
611                         global $wgUser;
612                         $loginlink = $wgUser->isAllowed( 'createaccount' )
613                                 ? 'nav-login-createaccount'
614                                 : 'login';
615                         if( $this->showIPinHeader() ) {
616                                 $href = &$this->userpageUrlDetails['href'];
617                                 $personal_urls['anonuserpage'] = array(
618                                         'text' => $this->username,
619                                         'href' => $href,
620                                         'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
621                                         'active' => ( $pageurl == $href )
622                                 );
623                                 $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
624                                 $href = &$usertalkUrlDetails['href'];
625                                 $personal_urls['anontalk'] = array(
626                                         'text' => wfMsg( 'anontalk' ),
627                                         'href' => $href,
628                                         'class' => $usertalkUrlDetails['exists'] ? false : 'new',
629                                         'active' => ( $pageurl == $href )
630                                 );
631                                 $personal_urls['anonlogin'] = array(
632                                         'text' => wfMsg( $loginlink ),
633                                         'href' => self::makeSpecialUrl( 'Userlogin', $returnto ),
634                                         'active' => $title->isSpecial( 'Userlogin' )
635                                 );
636                         } else {
637                                 $personal_urls['login'] = array(
638                                         'text' => wfMsg( $loginlink ),
639                                         'href' => self::makeSpecialUrl( 'Userlogin', $returnto ),
640                                         'active' => $title->isSpecial( 'Userlogin' )
641                                 );
642                         }
643                 }
644
645                 wfRunHooks( 'PersonalUrls', array( &$personal_urls, &$title ) );
646                 wfProfileOut( __METHOD__ );
647                 return $personal_urls;
648         }
649
650         function tabAction( $title, $message, $selected, $query = '', $checkEdit = false ) {
651                 $classes = array();
652                 if( $selected ) {
653                         $classes[] = 'selected';
654                 }
655                 if( $checkEdit && !$title->isKnown() ) {
656                         $classes[] = 'new';
657                         $query = 'action=edit&redlink=1';
658                 }
659
660                 $text = wfMsg( $message );
661                 if ( wfEmptyMsg( $message, $text ) ) {
662                         global $wgContLang;
663                         $text = $wgContLang->getFormattedNsText( MWNamespace::getSubject( $title->getNamespace() ) );
664                 }
665
666                 $result = array();
667                 if( !wfRunHooks( 'SkinTemplateTabAction', array( &$this,
668                                 $title, $message, $selected, $checkEdit,
669                                 &$classes, &$query, &$text, &$result ) ) ) {
670                         return $result;
671                 }
672
673                 return array(
674                         'class' => implode( ' ', $classes ),
675                         'text' => $text,
676                         'href' => $title->getLocalUrl( $query ) );
677         }
678
679         function makeTalkUrlDetails( $name, $urlaction = '' ) {
680                 $title = Title::newFromText( $name );
681                 if( !is_object( $title ) ) {
682                         throw new MWException( __METHOD__ . " given invalid pagename $name" );
683                 }
684                 $title = $title->getTalkPage();
685                 self::checkTitle( $title, $name );
686                 return array(
687                         'href' => $title->getLocalURL( $urlaction ),
688                         'exists' => $title->getArticleID() != 0 ? true : false
689                 );
690         }
691
692         function makeArticleUrlDetails( $name, $urlaction = '' ) {
693                 $title = Title::newFromText( $name );
694                 $title= $title->getSubjectPage();
695                 self::checkTitle( $title, $name );
696                 return array(
697                         'href' => $title->getLocalURL( $urlaction ),
698                         'exists' => $title->getArticleID() != 0 ? true : false
699                 );
700         }
701
702         /**
703          * an array of edit links by default used for the tabs
704          * @return array
705          * @private
706          */
707         function buildContentActionUrls() {
708                 global $wgContLang, $wgLang, $wgOut, $wgUser, $wgRequest, $wgArticle;
709
710                 wfProfileIn( __METHOD__ );
711
712                 $action = $wgRequest->getVal( 'action', 'view' );
713                 $section = $wgRequest->getVal( 'section' );
714                 $content_actions = array();
715
716                 $prevent_active_tabs = false;
717                 wfRunHooks( 'SkinTemplatePreventOtherActiveTabs', array( &$this, &$prevent_active_tabs ) );
718
719                 if( $this->iscontent ) {
720                         $subjpage = $this->mTitle->getSubjectPage();
721                         $talkpage = $this->mTitle->getTalkPage();
722
723                         $nskey = $this->mTitle->getNamespaceKey();
724                         $content_actions[$nskey] = $this->tabAction(
725                                 $subjpage,
726                                 $nskey,
727                                 !$this->mTitle->isTalkPage() && !$prevent_active_tabs,
728                                 '', true
729                         );
730
731                         $content_actions['talk'] = $this->tabAction(
732                                 $talkpage,
733                                 'talk',
734                                 $this->mTitle->isTalkPage() && !$prevent_active_tabs,
735                                 '',
736                                 true
737                         );
738
739                         wfProfileIn( __METHOD__ . '-edit' );
740                         if ( $this->mTitle->quickUserCan( 'edit' ) && ( $this->mTitle->exists() || $this->mTitle->quickUserCan( 'create' ) ) ) {
741                                 $istalk = $this->mTitle->isTalkPage();
742                                 $istalkclass = $istalk?' istalk':'';
743                                 $content_actions['edit'] = array(
744                                         'class' => ( ( ( $action == 'edit' or $action == 'submit' ) and $section != 'new' ) ? 'selected' : '' ) . $istalkclass,
745                                         'text' => $this->mTitle->exists()
746                                                 ? wfMsg( 'edit' )
747                                                 : wfMsg( 'create' ),
748                                         'href' => $this->mTitle->getLocalUrl( $this->editUrlOptions() )
749                                 );
750
751                                 // adds new section link if page is a current revision of a talk page or
752                                 if ( ( $wgArticle && $wgArticle->isCurrent() && $istalk ) || $wgOut->showNewSectionLink() ) {
753                                         if ( !$wgOut->forceHideNewSectionLink() ) {
754                                                 $content_actions['addsection'] = array(
755                                                         'class' => $section == 'new' ? 'selected' : false,
756                                                         'text' => wfMsg( 'addsection' ),
757                                                         'href' => $this->mTitle->getLocalUrl( 'action=edit&section=new' )
758                                                 );
759                                         }
760                                 }
761                         } elseif ( $this->mTitle->isKnown() ) {
762                                 $content_actions['viewsource'] = array(
763                                         'class' => ($action == 'edit') ? 'selected' : false,
764                                         'text' => wfMsg( 'viewsource' ),
765                                         'href' => $this->mTitle->getLocalUrl( $this->editUrlOptions() )
766                                 );
767                         }
768                         wfProfileOut( __METHOD__ . '-edit' );
769
770                         wfProfileIn( __METHOD__ . '-live' );
771                         if ( $this->mTitle->exists() ) {
772
773                                 $content_actions['history'] = array(
774                                         'class' => ($action == 'history') ? 'selected' : false,
775                                         'text' => wfMsg( 'history_short' ),
776                                         'href' => $this->mTitle->getLocalUrl( 'action=history' ),
777                                         'rel' => 'archives',
778                                 );
779
780                                 if( $wgUser->isAllowed( 'delete' ) ) {
781                                         $content_actions['delete'] = array(
782                                                 'class' => ($action == 'delete') ? 'selected' : false,
783                                                 'text' => wfMsg( 'delete' ),
784                                                 'href' => $this->mTitle->getLocalUrl( 'action=delete' )
785                                         );
786                                 }
787                                 if ( $this->mTitle->quickUserCan( 'move' ) ) {
788                                         $moveTitle = SpecialPage::getTitleFor( 'Movepage', $this->thispage );
789                                         $content_actions['move'] = array(
790                                                 'class' => $this->mTitle->isSpecial( 'Movepage' ) ? 'selected' : false,
791                                                 'text' => wfMsg( 'move' ),
792                                                 'href' => $moveTitle->getLocalUrl()
793                                         );
794                                 }
795
796                                 if ( $this->mTitle->getNamespace() !== NS_MEDIAWIKI && $wgUser->isAllowed( 'protect' ) ) {
797                                         if( !$this->mTitle->isProtected() ){
798                                                 $content_actions['protect'] = array(
799                                                         'class' => ($action == 'protect') ? 'selected' : false,
800                                                         'text' => wfMsg( 'protect' ),
801                                                         'href' => $this->mTitle->getLocalUrl( 'action=protect' )
802                                                 );
803
804                                         } else {
805                                                 $content_actions['unprotect'] = array(
806                                                         'class' => ($action == 'unprotect') ? 'selected' : false,
807                                                         'text' => wfMsg( 'unprotect' ),
808                                                         'href' => $this->mTitle->getLocalUrl( 'action=unprotect' )
809                                                 );
810                                         }
811                                 }
812                         } else {
813                                 //article doesn't exist or is deleted
814                                 if( $wgUser->isAllowed( 'deletedhistory' ) && $wgUser->isAllowed( 'deletedtext' ) ) {
815                                         if( $n = $this->mTitle->isDeleted() ) {
816                                                 $undelTitle = SpecialPage::getTitleFor( 'Undelete' );
817                                                 $content_actions['undelete'] = array(
818                                                         'class' => false,
819                                                         'text' => wfMsgExt( 'undelete_short', array( 'parsemag' ), $wgLang->formatNum( $n ) ),
820                                                         'href' => $undelTitle->getLocalUrl( 'target=' . urlencode( $this->thispage ) )
821                                                         #'href' => self::makeSpecialUrl( "Undelete/$this->thispage" )
822                                                 );
823                                         }
824                                 }
825
826                                 if ( $this->mTitle->getNamespace() !== NS_MEDIAWIKI && $wgUser->isAllowed( 'protect' ) ) {
827                                         if( !$this->mTitle->getRestrictions( 'create' ) ) {
828                                                 $content_actions['protect'] = array(
829                                                         'class' => ($action == 'protect') ? 'selected' : false,
830                                                         'text' => wfMsg( 'protect' ),
831                                                         'href' => $this->mTitle->getLocalUrl( 'action=protect' )
832                                                 );
833
834                                         } else {
835                                                 $content_actions['unprotect'] = array(
836                                                         'class' => ($action == 'unprotect') ? 'selected' : false,
837                                                         'text' => wfMsg( 'unprotect' ),
838                                                         'href' => $this->mTitle->getLocalUrl( 'action=unprotect' )
839                                                 );
840                                         }
841                                 }
842                         }
843
844                         wfProfileOut( __METHOD__ . '-live' );
845
846                         if( $this->loggedin ) {
847                                 if( !$this->mTitle->userIsWatching()) {
848                                         $content_actions['watch'] = array(
849                                                 'class' => ($action == 'watch' or $action == 'unwatch') ? 'selected' : false,
850                                                 'text' => wfMsg( 'watch' ),
851                                                 'href' => $this->mTitle->getLocalUrl( 'action=watch' )
852                                         );
853                                 } else {
854                                         $content_actions['unwatch'] = array(
855                                                 'class' => ($action == 'unwatch' or $action == 'watch') ? 'selected' : false,
856                                                 'text' => wfMsg( 'unwatch' ),
857                                                 'href' => $this->mTitle->getLocalUrl( 'action=unwatch' )
858                                         );
859                                 }
860                         }
861
862
863                         wfRunHooks( 'SkinTemplateTabs', array( $this, &$content_actions ) );
864                 } else {
865                         /* show special page tab */
866
867                         $content_actions[$this->mTitle->getNamespaceKey()] = array(
868                                 'class' => 'selected',
869                                 'text' => wfMsg('nstab-special'),
870                                 'href' => $wgRequest->getRequestURL(), // @bug 2457, 2510
871                         );
872
873                         wfRunHooks( 'SkinTemplateBuildContentActionUrlsAfterSpecialPage', array( &$this, &$content_actions ) );
874                 }
875
876                 /* show links to different language variants */
877                 global $wgDisableLangConversion;
878                 $variants = $wgContLang->getVariants();
879                 if( !$wgDisableLangConversion && sizeof( $variants ) > 1 ) {
880                         $preferred = $wgContLang->getPreferredVariant();
881                         $vcount=0;
882                         foreach( $variants as $code ) {
883                                 $varname = $wgContLang->getVariantname( $code );
884                                 if( $varname == 'disable' )
885                                         continue;
886                                 $selected = ( $code == $preferred )? 'selected' : false;
887                                 $content_actions['varlang-' . $vcount] = array(
888                                         'class' => $selected,
889                                         'text' => $varname,
890                                         'href' => $this->mTitle->getLocalURL( '', $code )
891                                 );
892                                 $vcount ++;
893                         }
894                 }
895
896                 wfRunHooks( 'SkinTemplateContentActions', array( &$content_actions ) );
897
898                 wfProfileOut( __METHOD__ );
899                 return $content_actions;
900         }
901
902         /**
903          * build array of common navigation links
904          * @return array
905          * @private
906          */
907         function buildNavUrls() {
908                 global $wgUseTrackbacks, $wgOut, $wgUser, $wgRequest;
909                 global $wgEnableUploads, $wgUploadNavigationUrl;
910
911                 wfProfileIn( __METHOD__ );
912
913                 $action = $wgRequest->getVal( 'action', 'view' );
914
915                 $nav_urls = array();
916                 $nav_urls['mainpage'] = array( 'href' => self::makeMainPageUrl() );
917                 if( $wgUploadNavigationUrl ) {
918                         $nav_urls['upload'] = array( 'href' => $wgUploadNavigationUrl );
919                 } elseif( $wgEnableUploads && $wgUser->isAllowed( 'upload' ) ) {
920                         $nav_urls['upload'] = array( 'href' => self::makeSpecialUrl( 'Upload' ) );
921                 } else {
922                         $nav_urls['upload'] = false;
923                 }
924                 $nav_urls['specialpages'] = array( 'href' => self::makeSpecialUrl( 'Specialpages' ) );
925
926                 // default permalink to being off, will override it as required below.
927                 $nav_urls['permalink'] = false;
928
929                 // A print stylesheet is attached to all pages, but nobody ever
930                 // figures that out. :)  Add a link...
931                 if( $this->iscontent && ( $action == 'view' || $action == 'purge' ) ) {
932                         if ( !$wgOut->isPrintable() ) {
933                                 $nav_urls['print'] = array(
934                                         'text' => wfMsg( 'printableversion' ),
935                                         'href' => $wgRequest->appendQuery( 'printable=yes' )
936                                 );
937                         }
938
939                         // Also add a "permalink" while we're at it
940                         if ( $this->mRevisionId ) {
941                                 $nav_urls['permalink'] = array(
942                                         'text' => wfMsg( 'permalink' ),
943                                         'href' => $wgOut->getTitle()->getLocalURL( "oldid=$this->mRevisionId" )
944                                 );
945                         }
946
947                         // Copy in case this undocumented, shady hook tries to mess with internals
948                         $revid = $this->mRevisionId;
949                         wfRunHooks( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink', array( &$this, &$nav_urls, &$revid, &$revid ) );
950                 }
951
952                 if( $this->mTitle->getNamespace() != NS_SPECIAL ) {
953                         $wlhTitle = SpecialPage::getTitleFor( 'Whatlinkshere', $this->thispage );
954                         $nav_urls['whatlinkshere'] = array(
955                                 'href' => $wlhTitle->getLocalUrl()
956                         );
957                         if( $this->mTitle->getArticleId() ) {
958                                 $rclTitle = SpecialPage::getTitleFor( 'Recentchangeslinked', $this->thispage );
959                                 $nav_urls['recentchangeslinked'] = array(
960                                         'href' => $rclTitle->getLocalUrl()
961                                 );
962                         } else {
963                                 $nav_urls['recentchangeslinked'] = false;
964                         }
965                         if( $wgUseTrackbacks )
966                                 $nav_urls['trackbacklink'] = array(
967                                         'href' => $wgOut->getTitle()->trackbackURL()
968                                 );
969                 }
970
971                 if( $this->mTitle->getNamespace() == NS_USER || $this->mTitle->getNamespace() == NS_USER_TALK ) {
972                         $id = User::idFromName( $this->mTitle->getText() );
973                         $ip = User::isIP( $this->mTitle->getText() );
974                 } else {
975                         $id = 0;
976                         $ip = false;
977                 }
978
979                 if( $id || $ip ) { # both anons and non-anons have contribs list
980                         $nav_urls['contributions'] = array(
981                                 'href' => self::makeSpecialUrlSubpage( 'Contributions', $this->mTitle->getText() )
982                         );
983
984                         if( $id ) {
985                                 $logPage = SpecialPage::getTitleFor( 'Log' );
986                                 $nav_urls['log'] = array(
987                                         'href' => $logPage->getLocalUrl(
988                                                 array(
989                                                         'user' => $this->mTitle->getText()
990                                                 )
991                                         )
992                                 );
993                         } else {
994                                 $nav_urls['log'] = false;
995                         }
996
997                         if ( $wgUser->isAllowed( 'block' ) ) {
998                                 $nav_urls['blockip'] = array(
999                                         'href' => self::makeSpecialUrlSubpage( 'Blockip', $this->mTitle->getText() )
1000                                 );
1001                         } else {
1002                                 $nav_urls['blockip'] = false;
1003                         }
1004                 } else {
1005                         $nav_urls['contributions'] = false;
1006                         $nav_urls['log'] = false;
1007                         $nav_urls['blockip'] = false;
1008                 }
1009                 $nav_urls['emailuser'] = false;
1010                 if( $this->showEmailUser( $id ) ) {
1011                         $nav_urls['emailuser'] = array(
1012                                 'href' => self::makeSpecialUrlSubpage( 'Emailuser', $this->mTitle->getText() )
1013                         );
1014                 }
1015                 wfProfileOut( __METHOD__ );
1016                 return $nav_urls;
1017         }
1018
1019         /**
1020          * Generate strings used for xml 'id' names
1021          * @return string
1022          * @private
1023          */
1024         function getNameSpaceKey() {
1025                 return $this->mTitle->getNamespaceKey();
1026         }
1027
1028         /**
1029          * @private
1030          */
1031         function setupUserJs( $allowUserJs ) {
1032                 global $wgRequest, $wgJsMimeType;
1033                 wfProfileIn( __METHOD__ );
1034
1035                 $action = $wgRequest->getVal( 'action', 'view' );
1036
1037                 if( $allowUserJs && $this->loggedin ) {
1038                         if( $this->mTitle->isJsSubpage() and $this->userCanPreview( $action ) ) {
1039                                 # XXX: additional security check/prompt?
1040                                 $this->userjsprev = '/*<![CDATA[*/ ' . $wgRequest->getText( 'wpTextbox1' ) . ' /*]]>*/';
1041                         } else {
1042                                 $this->userjs = self::makeUrl( $this->userpage . '/' . $this->skinname . '.js', 'action=raw&ctype=' . $wgJsMimeType );
1043                         }
1044                 }
1045                 wfProfileOut( __METHOD__ );
1046         }
1047
1048         /**
1049          * Code for extensions to hook into to provide per-page CSS, see
1050          * extensions/PageCSS/PageCSS.php for an implementation of this.
1051          *
1052          * @private
1053          */
1054         function setupPageCss() {
1055                 wfProfileIn( __METHOD__ );
1056                 $out = false;
1057                 wfRunHooks( 'SkinTemplateSetupPageCss', array( &$out ) );
1058                 wfProfileOut( __METHOD__ );
1059                 return $out;
1060         }
1061
1062         public function commonPrintStylesheet() {
1063                 return false;
1064         }
1065 }
1066
1067 /**
1068  * Generic wrapper for template functions, with interface
1069  * compatible with what we use of PHPTAL 0.7.
1070  * @ingroup Skins
1071  */
1072 abstract class QuickTemplate {
1073         /**
1074          * Constructor
1075          */
1076         public function QuickTemplate() {
1077                 $this->data = array();
1078                 $this->translator = new MediaWiki_I18N();
1079         }
1080
1081         /**
1082          * Sets the value $value to $name
1083          * @param $name
1084          * @param $value
1085          */
1086         public function set( $name, $value ) {
1087                 $this->data[$name] = $value;
1088         }
1089
1090         /**
1091          * @param $name
1092          * @param $value
1093          */
1094         public function setRef( $name, &$value ) {
1095                 $this->data[$name] =& $value;
1096         }
1097
1098         /**
1099          * @param $t
1100          */
1101         public function setTranslator( &$t ) {
1102                 $this->translator = &$t;
1103         }
1104
1105         /**
1106          * Main function, used by classes that subclass QuickTemplate
1107          * to show the actual HTML output
1108          */
1109         abstract public function execute();
1110
1111         /**
1112          * @private
1113          */
1114         function text( $str ) {
1115                 echo htmlspecialchars( $this->data[$str] );
1116         }
1117
1118         /**
1119          * @private
1120          */
1121         function jstext( $str ) {
1122                 echo Xml::escapeJsString( $this->data[$str] );
1123         }
1124
1125         /**
1126          * @private
1127          */
1128         function html( $str ) {
1129                 echo $this->data[$str];
1130         }
1131
1132         /**
1133          * @private
1134          */
1135         function msg( $str ) {
1136                 echo htmlspecialchars( $this->translator->translate( $str ) );
1137         }
1138
1139         /**
1140          * @private
1141          */
1142         function msgHtml( $str ) {
1143                 echo $this->translator->translate( $str );
1144         }
1145
1146         /**
1147          * An ugly, ugly hack.
1148          * @private
1149          */
1150         function msgWiki( $str ) {
1151                 global $wgParser, $wgOut;
1152
1153                 $text = $this->translator->translate( $str );
1154                 $parserOutput = $wgParser->parse( $text, $wgOut->getTitle(),
1155                         $wgOut->parserOptions(), true );
1156                 echo $parserOutput->getText();
1157         }
1158
1159         /**
1160          * @private
1161          */
1162         function haveData( $str ) {
1163                 return isset( $this->data[$str] );
1164         }
1165
1166         /**
1167          * @private
1168          */
1169         function haveMsg( $str ) {
1170                 $msg = $this->translator->translate( $str );
1171                 return ( $msg != '-' ) && ( $msg != '' ); # ????
1172         }
1173 }