SkinTemplate.php 34 KB

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