var $mHTMLtitle = '', $mIsarticle = true, $mPrintable = false;
var $mSubtitle = '', $mRedirect = '', $mStatusCode;
var $mLastModified = '', $mETag = false;
- var $mCategoryLinks = array(), $mLanguageLinks = array();
-
- var $mScriptLoaderClassList = array();
- // the most recent id of any script that is grouped in the script request
- var $mLatestScriptRevID = 0;
+ var $mCategoryLinks = array(), $mCategories = array(), $mLanguageLinks = array();
var $mScripts = '', $mLinkColours, $mPageLinkTitle = '', $mHeadItems = array();
+ var $mInlineMsg = array();
+
var $mTemplateIds = array();
var $mAllowUserJs;
var $mContainsOldMagic = 0, $mContainsNewMagic = 0;
var $mIsArticleRelated = true;
protected $mParserOptions = null; // lazy initialised, use parserOptions()
- var $mShowFeedLinks = false;
- var $mFeedLinksAppendQuery = false;
+
+ var $mFeedLinks = array();
+
var $mEnableClientCache = true;
var $mArticleBodyOnly = false;
private $mIndexPolicy = 'index';
private $mFollowPolicy = 'follow';
+ private $mVaryHeader = array( 'Accept-Encoding' => array('list-contains=gzip'),
+ 'Cookie' => null );
/**
* Constructor
* @param string $file filename in skins/common or complete on-server path (/foo/bar.js)
*/
function addScriptFile( $file ) {
- global $wgStylePath, $wgScript, $wgUser;
- global $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgEnableScriptLoader, $wgScriptPath;
-
+ global $wgStylePath, $wgStyleVersion, $wgJsMimeType;
if( substr( $file, 0, 1 ) == '/' ) {
$path = $file;
} else {
- $path = "{$wgStylePath}/common/{$file}";
- }
-
- if( $wgEnableScriptLoader ){
- if( strpos( $path, $wgScript ) !== false ){
- $reqPath = str_replace( $wgScript . '?', '', $path );
- $reqArgs = explode( '&', $reqPath );
- $reqSet = array();
-
- foreach( $reqArgs as $arg ){
- list( $key, $var ) = explode( '=', $arg );
- $reqSet[$key] = $var;
- }
-
- if( isset( $reqSet['title'] ) && $reqSet != '' ) {
- // extract any extra param (for now just skin)
- $ext_param = ( isset( $reqSet['useskin'] ) && $reqSet['useskin'] != '' ) ? '|useskin=' . ucfirst( $reqSet['useskin'] ) : '';
- $this->mScriptLoaderClassList[] = 'WT:' . $reqSet['title'] . $ext_param ;
- // add the title revision to the key
- $t = Title::newFromText( $reqSet['title'] );
- // if there is no title (don't worry we just use the $wgStyleVersion var (which should be updated on relevant commits)
- if( $t && $t->exists() ){
- if( $t->getLatestRevID() > $this->mLatestScriptRevID )
- $this->mLatestScriptRevID = $t->getLatestRevID();
- }
- return true;
- }
- }
-
- // check for class from path:
- $js_class = $this->getJsClassFromPath( $path );
- if( $js_class ){
- // add to the class list:
- $this->mScriptLoaderClassList[] = $js_class;
- return true;
- }
- }
-
- // if the script loader did not find a way to add the script than add using addScript
- $this->addScript( Html::linkedScript( wfAppendQuery( $path, $this->getURIDparam() ) ) );
- }
-
- /**
- * This is the core script that is included on every page
- * (they are requested separately to improve caching across
- * different page load types (edit, upload, view, etc)
- */
- function addCoreScripts2Top(){
- global $wgEnableScriptLoader, $wgJSAutoloadLocalClasses, $wgScriptPath, $wgEnableJS2system;
- //@@todo we should deprecate wikibits in favor of mv_embed and native jQuery functions
-
- if( $wgEnableJS2system ){
- $core_classes = array( 'window.jQuery', 'mv_embed', 'wikibits' );
- } else {
- $core_classes = array( 'wikibits' );
- }
-
- if( $wgEnableScriptLoader ){
- $this->mScripts = $this->getScriptLoaderJs( $core_classes ) . $this->mScripts;
- } else {
- $so = '';
- foreach( $core_classes as $s ){
- if( isset( $wgJSAutoloadLocalClasses[$s] ) ){
- $so .= Html::linkedScript( "{$wgScriptPath}/{$wgJSAutoloadLocalClasses[$s]}?" . $this->getURIDparam() );
- }
- }
- $this->mScripts = $so . $this->mScripts;
- }
- }
-
- /**
- * @param $js_class String: name of JavaScript class
- * @return Boolean: false if class wasn't found, true on success
- */
- function addScriptClass( $js_class ){
- global $wgDebugJavaScript, $wgJSAutoloadLocalClasses, $wgJSAutoloadClasses,
- $wgEnableScriptLoader, $wgStyleVersion, $wgScriptPath;
-
- if( isset( $wgJSAutoloadClasses[$js_class] ) || isset( $wgJSAutoloadLocalClasses[$js_class] ) ){
- if( $wgEnableScriptLoader ){
- if( !in_array( $js_class, $this->mScriptLoaderClassList ) ){
- $this->mScriptLoaderClassList[] = $js_class;
- }
- } else {
- // do a normal load of without the script-loader:
- $path = $wgScriptPath . '/';
- if( isset( $wgJSAutoloadClasses[$js_class] ) ){
- $path.= $wgJSAutoloadClasses[$js_class];
- }else if( isset( $wgJSAutoloadLocalClasses[$js_class] ) ){
- $path.= $wgJSAutoloadLocalClasses[$js_class];
- }
- $urlAppend = ( $wgDebugJavaScript ) ? time() : $wgStyleVersion;
- $this->addScript( Html::linkedScript( "$path?$urlAppend" ) );
- }
- return true;
- }
- wfDebug( __METHOD__ . ' could not find js_class: ' . $js_class );
- return false; // could not find the class
- }
-
- /**
- * gets the scriptLoader javascript include
- * @param $forcClassAry Boolean: false by default
- */
- function getScriptLoaderJs( $forceClassAry = false ){
- global $wgRequest, $wgDebugJavaScript;
-
- if( !$forceClassAry ){
- $class_list = implode( ',', $this->mScriptLoaderClassList );
- } else {
- $class_list = implode( ',', $forceClassAry );
+ $path = "{$wgStylePath}/common/{$file}";
}
-
- $debug_param = ( $wgDebugJavaScript ||
- $wgRequest->getVal( 'debug' ) == 'true' ||
- $wgRequest->getVal( 'debug' ) == '1' )
- ? '&debug=true' : '';
-
- //@@todo intelligent unique id generation based on svn version of file (rather than just grabbing the $wgStyleVersion var)
- //@@todo we should check the packaged message text in this javascript file for updates and update the $mScriptLoaderURID id (in getJsClassFromPath)
-
- //generate the unique request param (combine with the most recent revision id of any wiki page with the $wgStyleVersion var)
-
- return Html::linkedScript( wfScript( 'mwScriptLoader' ) . "?class={$class_list}{$debug_param}&" . $this->getURIDparam() );
- }
-
- function getURIDparam(){
- global $wgDebugJavaScript, $wgStyleVersion;
- if( $wgDebugJavaScript ){
- return 'urid=' . time();
- } else {
- return "urid={$wgStyleVersion}_{$this->mLatestScriptRevID}";
- }
- }
-
- function getJsClassFromPath( $path ){
- global $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgScriptPath;
-
- $scriptLoaderPaths = array_merge( $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses );
- foreach( $scriptLoaderPaths as $js_class => $js_path ){
- $js_path = "{$wgScriptPath}/{$js_path}";
- if( $path == $js_path )
- return $js_class;
- }
- return false;
+ $this->addScript(
+ Xml::element( 'script',
+ array(
+ 'type' => $wgJsMimeType,
+ 'src' => "$path?$wgStyleVersion",
+ ),
+ '', false
+ )
+ );
}
/**
$this->mScripts .= Html::inlineScript( "\n$script\n" ) . "\n";
}
+ /**
+ * Get all registered JS and CSS tags for the header.
+ */
function getScript() {
- global $wgEnableScriptLoader;
- if( $wgEnableScriptLoader ){
- //include $this->mScripts (for anything that we could not package into the scriptloader
- return $this->mScripts . "\n" . $this->getScriptLoaderJs() . $this->getHeadItems();
- } else {
- return $this->mScripts . $this->getHeadItems();
- }
+ return $this->mScripts . $this->getHeadItems();
}
function getHeadItems() {
function setETag($tag) { $this->mETag = $tag; }
function setArticleBodyOnly($only) { $this->mArticleBodyOnly = $only; }
- function getArticleBodyOnly($only) { return $this->mArticleBodyOnly; }
+ function getArticleBodyOnly() { return $this->mArticleBodyOnly; }
function addLink( $linkarr ) {
# $linkarr should be an associative array of attributes. We'll escape on output.
* @return null
*/
public function setRobotPolicy( $policy ) {
- $policy = explode( ',', $policy );
- $policy = array_map( 'trim', $policy );
-
- # The default policy is follow, so if nothing is said explicitly, we
- # do that.
- if( in_array( 'nofollow', $policy ) ) {
- $this->mFollowPolicy = 'nofollow';
- } else {
- $this->mFollowPolicy = 'follow';
- }
+ $policy = Article::formatRobotPolicy( $policy );
- if( in_array( 'noindex', $policy ) ) {
- $this->mIndexPolicy = 'noindex';
- } else {
- $this->mIndexPolicy = 'index';
- }
+ if( isset( $policy['index'] ) ){
+ $this->setIndexPolicy( $policy['index'] );
+ }
+ if( isset( $policy['follow'] ) ){
+ $this->setFollowPolicy( $policy['follow'] );
+ }
}
/**
* This function allows good tags like <sup> in the <h1> tag, but not bad tags like <script>.
* This function automatically sets <title> to the same content as <h1> but with all tags removed.
* Bad tags that were escaped in <h1> will still be escaped in <title>, and good tags like <i> will be dropped entirely.
- */
+ */
public function setPageTitle( $name ) {
global $wgContLang;
$name = $wgContLang->convert( $name, true );
public function isArticle() { return $this->mIsarticle; }
public function setPrintable() { $this->mPrintable = true; }
public function isPrintable() { return $this->mPrintable; }
- public function setSyndicated( $show = true ) { $this->mShowFeedLinks = $show; }
- public function isSyndicated() { return $this->mShowFeedLinks; }
- public function setFeedAppendQuery( $val ) { $this->mFeedLinksAppendQuery = $val; }
public function getFeedAppendQuery() { return $this->mFeedLinksAppendQuery; }
public function setOnloadHandler( $js ) { $this->mOnloadHandler = $js; }
public function getOnloadHandler() { return $this->mOnloadHandler; }
public function disable() { $this->mDoNothing = true; }
public function isDisabled() { return $this->mDoNothing; }
+ public function setSyndicated( $show = true ) { $this->mShowFeedLinks = $show; }
+
+ public function setFeedAppendQuery( $val ) {
+ global $wgFeedClasses;
+
+ $this->mFeedLinks = array();
+
+ foreach( $wgFeedClasses as $type => $class ) {
+ $query = "feed=$type&".$val;
+ $this->mFeedLinks[$type] = $this->getTitle()->getLocalURL( $query );
+ }
+ }
+
+ public function addFeedLink( $format, $href ) {
+ $this->mFeedLinks[$format] = $href;
+ }
+
+ public function isSyndicated() { return count($this->mFeedLinks); }
+
public function setArticleRelated( $v ) {
$this->mIsArticleRelated = $v;
if ( !$v ) {
return $this->mCategoryLinks;
}
+ public function getCategories() {
+ return $this->mCategories;
+ }
+
/**
* Add an array of categories, with names in the keys
*/
if ( array_key_exists( $category, $categories ) )
continue;
$text = $wgContLang->convertHtml( $title->getText() );
+ $this->mCategories[] = $title->getText();
$this->mCategoryLinks[$type][] = $sk->link( $title, $text );
}
}
$this->mNewSectionLink = $parserOutput->getNewSection();
$this->mHideNewSectionLink = $parserOutput->getHideNewSection();
- if( is_null( $wgExemptFromUserRobotsControl ) ) {
- $bannedNamespaces = $wgContentNamespaces;
- } else {
- $bannedNamespaces = $wgExemptFromUserRobotsControl;
- }
- if( !in_array( $this->getTitle()->getNamespace(), $bannedNamespaces ) ) {
- # FIXME (bug 14900): This overrides $wgArticleRobotPolicies, and it
- # shouldn't
- $this->setIndexPolicy( $parserOutput->getIndexPolicy() );
- }
-
$this->mParseWarnings = $parserOutput->getWarnings();
if ( $parserOutput->getCacheTime() == -1 ) {
$this->enableClientCache( false );
*/
function addParserOutput( &$parserOutput ) {
$this->addParserOutputNoText( $parserOutput );
- $text = $parserOutput->getText();
+ $text = $parserOutput->getText();
wfRunHooks( 'OutputPageBeforeHTML',array( &$this, &$text ) );
$this->addHTML( $text );
}
return false;
}
+ public function addVaryHeader( $header, $option = null ) {
+ if ( !array_key_exists( $header, $this->mVaryHeader ) ) {
+ $this->mVaryHeader[$header] = $option;
+ }
+ elseif( is_array( $option ) ) {
+ if( is_array( $this->mVaryHeader[$header] ) ) {
+ $this->mVaryHeader[$header] = array_merge( $this->mVaryHeader[$header], $option );
+ }
+ else {
+ $this->mVaryHeader[$header] = $option;
+ }
+ }
+ $this->mVaryHeader[$header] = array_unique( $this->mVaryHeader[$header] );
+ }
+
/** Get a complete X-Vary-Options header */
public function getXVO() {
$cvCookies = $this->getCacheVaryCookies();
- $xvo = 'X-Vary-Options: Accept-Encoding;list-contains=gzip,Cookie;';
- $first = true;
+
+ $cookiesOption = array();
foreach ( $cvCookies as $cookieName ) {
- if ( $first ) {
- $first = false;
- } else {
- $xvo .= ';';
- }
- $xvo .= 'string-contains=' . $cookieName;
- }
+ $cookiesOption[] = 'string-contains=' . $cookieName;
+ }
+ $this->addVaryHeader( 'Cookie', $cookiesOption );
+
+ $headers = array();
+ foreach( $this->mVaryHeader as $header => $option ) {
+ $newheader = $header;
+ if( is_array( $option ) )
+ $newheader .= ';' . implode( ';', $option );
+ $headers[] = $newheader;
+ }
+ $xvo = 'X-Vary-Options: ' . implode( ',', $headers );
+
return $xvo;
}
# don't serve compressed data to clients who can't handle it
# maintain different caches for logged-in users and non-logged in ones
- $response->header( 'Vary: Accept-Encoding, Cookie' );
+ $response->header( 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) ) );
if ( $wgUseXVO ) {
# Add an X-Vary-Options header for Squid with Wikimedia patches
$response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
$response->header( 'Pragma: no-cache' );
}
+ wfRunHooks('CacheHeadersAfterSet', array( $this ) );
}
/**
$sk = $wgUser->getSkin();
- // add our core scripts to output
- $this->addCoreScripts2Top();
-
if ( $wgUseAjax ) {
$this->addScriptFile( 'ajax.js' );
if( isset( $wgArticle ) && $this->getTitle() && $this->getTitle()->quickUserCan( 'edit' )
&& ( $this->getTitle()->exists() || $this->getTitle()->quickUserCan( 'create' ) ) ) {
// Original UniversalEditButton
+ $msg = wfMsg('edit');
$this->addLink( array(
'rel' => 'alternate',
'type' => 'application/x-wiki',
- 'title' => wfMsg( 'edit' ),
+ 'title' => $msg,
'href' => $this->getTitle()->getLocalURL( 'action=edit' )
) );
// Alternate edit link
$this->addLink( array(
'rel' => 'edit',
- 'title' => wfMsg( 'edit' ),
+ 'title' => $msg,
'href' => $this->getTitle()->getLocalURL( 'action=edit' )
) );
}
// Show source, if supplied
if( is_string( $source ) ) {
$this->addWikiMsg( 'viewsourcetext' );
- $text = Xml::openElement( 'textarea',
- array( 'id' => 'wpTextbox1',
- 'name' => 'wpTextbox1',
- 'cols' => $wgUser->getOption( 'cols' ),
- 'rows' => $wgUser->getOption( 'rows' ),
- 'readonly' => 'readonly' ) );
- $text .= htmlspecialchars( $source );
- $text .= Xml::closeElement( 'textarea' );
- $this->addHTML( $text );
+
+ $params = array(
+ 'id' => 'wpTextbox1',
+ 'name' => 'wpTextbox1',
+ 'cols' => $wgUser->getOption( 'cols' ),
+ 'rows' => $wgUser->getOption( 'rows' ),
+ 'readonly' => 'readonly'
+ );
+ $this->addHTML( Html::element( 'textarea', $params, $source ) );
// Show templates used by this article
$skin = $wgUser->getSkin();
/**
* @return string The doctype, opening <html>, and head element.
+ *
+ * @param $sk Skin The given Skin
*/
- public function headElement( Skin $sk ) {
+ public function headElement( Skin $sk, $includeStyle = true ) {
global $wgDocType, $wgDTD, $wgContLanguageCode, $wgOutputEncoding, $wgMimeType;
- global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces;
- global $wgContLang, $wgUseTrackbacks, $wgStyleVersion, $wgEnableScriptLoader, $wgHtml5;
+ global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces, $wgHtml5Version;
+ global $wgContLang, $wgUseTrackbacks, $wgStyleVersion, $wgHtml5;
$this->addMeta( "http:Content-Type", "$wgMimeType; charset={$wgOutputEncoding}" );
if ( $sk->commonPrintStylesheet() ) {
$dir = $wgContLang->getDir();
if ( $wgHtml5 ) {
- $ret .= "<!doctype html>\n";
- $ret .= "<html lang=\"$wgContLanguageCode\" dir=\"$dir\">\n";
+ $ret .= "<!DOCTYPE html>\n";
+ $ret .= "<html lang=\"$wgContLanguageCode\" dir=\"$dir\" ";
+ if ($wgHtml5Version) $ret .= " version=\"$wgHtml5Version\" ";
+ $ret .= ">\n";
} else {
$ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\" \"$wgDTD\">\n";
$ret .= "<html xmlns=\"{$wgXhtmlDefaultNamespace}\" ";
$ret .= implode( "\n", array(
$this->getHeadLinks(),
$this->buildCssLinks(),
- $sk->getHeadScripts( $this ),
+ $this->getHeadScripts( $sk ),
$this->getHeadItems(),
));
if( $sk->usercss ){
$ret .= Html::inlineStyle( $sk->usercss );
}
- if( $wgEnableScriptLoader )
- $ret .= $this->getScriptLoaderJs();
-
if ($wgUseTrackbacks && $this->isArticleRelated())
$ret .= $this->getTitle()->trackbackRDF();
return $ret;
}
+ /*
+ * gets the global variables and mScripts
+ *
+ * also adds userjs to the end if enabled:
+ */
+ function getHeadScripts( Skin $sk ) {
+ global $wgUser, $wgRequest, $wgJsMimeType, $wgUseSiteJs;
+ global $wgStylePath, $wgStyleVersion;
+
+ $scripts = Skin::makeGlobalVariablesScript( $sk->getSkinName() );
+ $scripts .= Html::linkedScript( "{$wgStylePath}/common/wikibits.js?$wgStyleVersion" );
+
+ //add site JS if enabled:
+ if( $wgUseSiteJs ) {
+ $jsCache = $wgUser->isLoggedIn() ? '&smaxage=0' : '';
+ $this->addScriptFile( Skin::makeUrl( '-',
+ "action=raw$jsCache&gen=js&useskin=" .
+ urlencode( $sk->getSkinName() )
+ )
+ );
+ }
+
+ //add user js if enabled:
+ if( $this->isUserJsAllowed() && $wgUser->isLoggedIn() ) {
+ $action = $wgRequest->getVal( 'action', 'view' );
+ if( $this->mTitle && $this->mTitle->isJsSubpage() and $sk->userCanPreview( $action ) ) {
+ # XXX: additional security check/prompt?
+ $this->addInlineScript( $wgRequest->getText( 'wpTextbox1' ) );
+ } else {
+ $userpage = $wgUser->getUserPage();
+ $userjs = Skin::makeUrl(
+ $userpage->getPrefixedText() . '/' . $sk->getSkinName() . '.js',
+ 'action=raw&ctype=' . $wgJsMimeType );
+ $this->addScriptFile( $userjs );
+ }
+ }
+
+ $scripts .= "\n" . $this->mScripts;
+ return $scripts;
+ }
+
protected function addDefaultMeta() {
global $wgVersion, $wgHtml5;
} else {
$a = 'name';
}
- $tags[] = Xml::element( 'meta',
+ $tags[] = Html::element( 'meta',
array(
$a => $tag[0],
'content' => $tag[1] ) );
}
foreach ( $this->mLinktags as $tag ) {
- $tags[] = Xml::element( 'link', $tag );
+ $tags[] = Html::element( 'link', $tag );
}
if( $wgFeed ) {
}
}
- return implode( "\n", $tags ) . "\n";
+ return implode( "\n", $tags );
}
/**
* @return array associating format keys with URLs
*/
public function getSyndicationLinks() {
- global $wgFeedClasses;
- $links = array();
-
- if( $this->isSyndicated() ) {
- if( is_string( $this->getFeedAppendQuery() ) ) {
- $appendQuery = "&" . $this->getFeedAppendQuery();
- } else {
- $appendQuery = "";
- }
-
- foreach( $wgFeedClasses as $format => $class ) {
- $links[$format] = $this->getTitle()->getLocalUrl( "feed=$format{$appendQuery}" );
- }
- }
- return $links;
+ return $this->mFeedLinks;
}
/**
* Generate a <link rel/> for an RSS feed.
*/
private function feedLink( $type, $url, $text ) {
- return Xml::element( 'link', array(
+ return Html::element( 'link', array(
'rel' => 'alternate',
'type' => "application/$type+xml",
'title' => $text,
return '';
}
} else {
- $media = null;
+ $media = 'all';
}
if( substr( $style, 0, 1 ) == '/' ||
* @param int $lag Slave lag
*/
public function showLagWarning( $lag ) {
- global $wgSlaveLagWarning, $wgSlaveLagCritical;
+ global $wgSlaveLagWarning, $wgSlaveLagCritical, $wgLang;
if( $lag >= $wgSlaveLagWarning ) {
$message = $lag < $wgSlaveLagCritical
? 'lag-warn-normal'
: 'lag-warn-high';
- $warning = wfMsgExt( $message, 'parse', $lag );
- $this->addHTML( "<div class=\"mw-{$message}\">\n{$warning}\n</div>\n" );
+ $wrap = Html::rawElement( 'div', array( 'class' => "mw-{$message}" ), "\n$1\n" );
+ $this->wrapWikiMsg( "$wrap\n", array( $message, $wgLang->formatNum( $lag ) ) );
}
}