X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2FOutputPage.php;h=92b416e3fe2761f468f189c39383aa887c32ff39;hb=f3c64eca2f28d0d3df9bedbadadac41a29304a2a;hp=bb12b4c8ce908dd627a23a4c3d6a4a6575d97a8b;hpb=cd0a89520dec7b2c5de5ff5a519e083bd546a798;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/OutputPage.php b/includes/OutputPage.php index bb12b4c8ce..f9859e6f9e 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -1,409 +1,454 @@ -"; -} - -function renderMath( $tex ) -{ - global $wgUser, $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding; - $mf = wfMsg( "math_failure" ); - $munk = wfMsg( "math_unknown_error" ); - - $fname = "renderMath"; - - $math = $wgUser->getOption("math"); - if ($math == 3) - return ('$ '.wfEscapeHTML($tex).' $'); - - $md5 = md5($tex); - $md5_sql = mysql_escape_string(pack("H32", $md5)); - if ($math == 0) - $sql = "SELECT math_outputhash FROM math WHERE math_inputhash = '".$md5_sql."'"; - else - $sql = "SELECT math_outputhash,math_html_conservativeness,math_html FROM math WHERE math_inputhash = '".$md5_sql."'"; - - $res = wfQuery( $sql, $fname ); - if ( wfNumRows( $res ) == 0 ) - { - $cmd = "./math/texvc ".escapeshellarg($wgTmpDirectory)." ". - escapeshellarg($wgMathDirectory)." ".escapeshellarg($tex)." ".escapeshellarg($wgInputEncoding); - $contents = `$cmd`; - - if (strlen($contents) == 0) - return "".$mf." (".$munk."): ".wfEscapeHTML($tex).""; - $retval = substr ($contents, 0, 1); - if (($retval == "C") || ($retval == "M") || ($retval == "L")) { - if ($retval == "C") - $conservativeness = 2; - else if ($retval == "M") - $conservativeness = 1; - else - $conservativeness = 0; - $outdata = substr ($contents, 33); - - $i = strpos($outdata, "\000"); - - $outhtml = substr($outdata, 0, $i); - $mathml = substr($outdata, $i+1); - - $sql_html = "'".mysql_escape_string($outhtml)."'"; - $sql_mathml = "'".mysql_escape_string($mathml)."'"; - } else if (($retval == "c") || ($retval == "m") || ($retval == "l")) { - $outhtml = substr ($contents, 33); - if ($retval == "c") - $conservativeness = 2; - else if ($retval == "m") - $conservativeness = 1; - else - $conservativeness = 0; - $sql_html = "'".mysql_escape_string($outhtml)."'"; - $mathml = ''; - $sql_mathml = 'NULL'; - } else if ($retval == "X") { - $outhtml = ''; - $mathml = substr ($contents, 33); - $sql_html = 'NULL'; - $sql_mathml = "'".mysql_escape_string($mathml)."'"; - $conservativeness = 0; - } else if ($retval == "+") { - $outhtml = ''; - $mathml = ''; - $sql_html = 'NULL'; - $sql_mathml = 'NULL'; - $conservativeness = 0; - } else { - if ($retval == "E") - $errmsg = wfMsg( "math_lexing_error" ); - else if ($retval == "S") - $errmsg = wfMsg( "math_syntax_error" ); - else if ($retval == "F") - $errmsg = wfMsg( "math_unknown_function" ); - else - $errmsg = $munk; - return "

".$mf." (".$errmsg.substr($contents, 1)."): ".wfEscapeHTML($tex)."

"; - } +".$mf." (".$munk."): ".wfEscapeHTML($tex).""; +/** + * This is not a valid entry point, perform no further processing unless MEDIAWIKI is defined + */ +if( defined( 'MEDIAWIKI' ) ) { - $outmd5_sql = mysql_escape_string(pack("H32", $outmd5)); +# See design.doc - $sql = "REPLACE INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$conservativeness.", ".$sql_html.", ".$sql_mathml.")"; - - $res = wfQuery( $sql, $fname ); - # we don't really care if it fails - - if (($math == 0) || ($rpage->math_html == '') || (($math == 1) && ($conservativeness != 2)) || (($math == 4) && ($conservativeness == 0))) - return linkToMathImage($tex, $outmd5); - else - return $outhtml; - } else { - $rpage = wfFetchObject ( $res ); - $outputhash = unpack( "H32md5", $rpage->math_outputhash . " " ); - $outputhash = $outputhash ['md5']; - - if (($math == 0) || ($rpage->math_html == '') || (($math == 1) && ($rpage->math_html_conservativeness != 2)) || (($math == 4) && ($rpage->math_html_conservativeness == 0))) - return linkToMathImage ( $tex, $outputhash ); - else - return $rpage->math_html; - } -} +if($wgUseTeX) require_once( 'Math.php' ); +/** + * @todo document + * @package MediaWiki + */ class OutputPage { var $mHeaders, $mCookies, $mMetatags, $mKeywords; var $mLinktags, $mPagetitle, $mBodytext, $mDebugtext; var $mHTMLtitle, $mRobotpolicy, $mIsarticle, $mPrintable; - var $mSubtitle, $mRedirect, $mAutonumber, $mHeadtext; - var $mLastModified; - - var $mDTopen, $mLastSection; # Used for processing DL, PRE - var $mLanguageLinks, $mSupressQuickbar; - - function OutputPage() - { + var $mSubtitle, $mRedirect; + var $mLastModified, $mCategoryLinks; + var $mScripts, $mLinkColours; + + var $mSuppressQuickbar; + var $mOnloadHandler; + var $mDoNothing; + var $mContainsOldMagic, $mContainsNewMagic; + var $mIsArticleRelated; + var $mParserOptions; + var $mShowFeedLinks = false; + var $mEnableClientCache = true; + + /** + * Constructor + * Initialise private variables + */ + function OutputPage() { $this->mHeaders = $this->mCookies = $this->mMetatags = $this->mKeywords = $this->mLinktags = array(); $this->mHTMLtitle = $this->mPagetitle = $this->mBodytext = - $this->mLastSection = $this->mRedirect = $this->mLastModified = - $this->mSubtitle = $this->mDebugtext = $this->mRobotpolicy = ""; - $this->mIsarticle = $this->mPrintable = true; - $this->mSupressQuickbar = $this->mDTopen = $this->mPrintable = false; + $this->mRedirect = $this->mLastModified = + $this->mSubtitle = $this->mDebugtext = $this->mRobotpolicy = + $this->mOnloadHandler = ''; + $this->mIsArticleRelated = $this->mIsarticle = $this->mPrintable = true; + $this->mSuppressQuickbar = $this->mPrintable = false; $this->mLanguageLinks = array(); - $this->mAutonumber = 0; + $this->mCategoryLinks = array() ; + $this->mDoNothing = false; + $this->mContainsOldMagic = $this->mContainsNewMagic = 0; + $this->mParserOptions = ParserOptions::newFromUser( $temp = NULL ); + $this->mSquidMaxage = 0; + $this->mScripts = ''; } - function addHeader( $name, $val ) { array_push( $this->mHeaders, "$name: $val" ) ; } + function addHeader( $name, $val ) { array_push( $this->mHeaders, $name.': '.$val ) ; } function addCookie( $name, $val ) { array_push( $this->mCookies, array( $name, $val ) ); } - function redirect( $url ) { $this->mRedirect = $url; } + function redirect( $url, $responsecode = '302' ) { $this->mRedirect = $url; $this->mRedirectCode = $responsecode; } # To add an http-equiv meta tag, precede the name with "http:" function addMeta( $name, $val ) { array_push( $this->mMetatags, array( $name, $val ) ); } function addKeyword( $text ) { array_push( $this->mKeywords, $text ); } - function addLink( $rel, $rev, $target ) { array_push( $this->mLinktags, array( $rel, $rev, $target ) ); } + function addScript( $script ) { $this->mScripts .= $script; } + function getScript() { return $this->mScripts; } + + function addLink( $linkarr ) { + # $linkarr should be an associative array of attributes. We'll escape on output. + array_push( $this->mLinktags, $linkarr ); + } - function checkLastModified ( $timestamp ) - { + function addMetadataLink( $linkarr ) { + # note: buggy CC software only reads first "meta" link + static $haveMeta = false; + $linkarr['rel'] = ($haveMeta) ? 'alternate meta' : 'meta'; + $this->addLink( $linkarr ); + $haveMeta = true; + } + + /** + * checkLastModified tells the client to use the client-cached page if + * possible. If sucessful, the OutputPage is disabled so that + * any future call to OutputPage->output() have no effect. The method + * returns true iff cache-ok headers was sent. + */ + function checkLastModified ( $timestamp ) { global $wgLang, $wgCachePages, $wgUser; - if( !$wgCachePages ) return; + $timestamp=wfTimestamp(TS_MW,$timestamp); + if( !$wgCachePages ) { + wfDebug( "CACHE DISABLED\n", false ); + return; + } if( preg_match( '/MSIE ([1-4]|5\.0)/', $_SERVER["HTTP_USER_AGENT"] ) ) { # IE 5.0 has probs with our caching - #wfDebug( "-- bad client, not caching\n", false ); + wfDebug( "-- bad client, not caching\n", false ); return; } - if( $wgUser->getOption( "nocache" ) ) return; - - if( $_SERVER["HTTP_IF_MODIFIED_SINCE"] != "" ) { - $ismodsince = wfUnix2Timestamp( strtotime( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) ); - #wfDebug( "-- client send If-Modified-Since: " . $_SERVER["HTTP_IF_MODIFIED_SINCE"] . "\n", false ); - $lastmod = gmdate( "D, j M Y H:i:s", wfTimestamp2Unix( - max( $timestamp, $wgUser->mTouched ) ) ) . " GMT"; - #wfDebug( "-- we might send Last-Modified : $lastmod\n", false ); - + if( $wgUser->getOption( 'nocache' ) ) { + wfDebug( "USER DISABLED CACHE\n", false ); + return; + } + + $lastmod = gmdate( 'D, j M Y H:i:s', wfTimestamp(TS_UNIX, max( $timestamp, $wgUser->mTouched ) ) ) . ' GMT'; + + if( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { + # IE sends sizes after the date like this: + # Wed, 20 Aug 2003 06:51:19 GMT; length=5202 + # this breaks strtotime(). + $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] ); + $ismodsince = wfTimestamp( TS_MW, strtotime( $modsince ) ); + wfDebug( "-- client send If-Modified-Since: " . $modsince . "\n", false ); + wfDebug( "-- we might send Last-Modified : $lastmod\n", false ); if( ($ismodsince >= $timestamp ) and $wgUser->validateCache( $ismodsince ) ) { # Make sure you're in a place you can leave when you call us! header( "HTTP/1.0 304 Not Modified" ); - header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page! - header( "Cache-Control: private, must-revalidate, max-age=0" ); - header( "Last-Modified: {$lastmod}" ); - #wfDebug( "CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false ); - exit; + $this->mLastModified = $lastmod; + $this->sendCacheControl(); + wfDebug( "CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false ); + $this->disable(); + return true; } else { - #wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false ); + wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false ); $this->mLastModified = $lastmod; } + } else { + wfDebug( "We're confused.\n", false ); + $this->mLastModified = $lastmod; + } + } + + function getPageTitleActionText () { + global $action; + switch($action) { + case 'edit': + return wfMsg('edit'); + case 'history': + return wfMsg('history_short'); + case 'protect': + return wfMsg('protect'); + case 'unprotect': + return wfMsg('unprotect'); + case 'delete': + return wfMsg('delete'); + case 'watch': + return wfMsg('watch'); + case 'unwatch': + return wfMsg('unwatch'); + case 'submit': + return wfMsg('preview'); + case 'info': + return wfMsg('info_short'); + default: + return ''; } } function setRobotpolicy( $str ) { $this->mRobotpolicy = $str; } - function setHTMLtitle( $name ) { $this->mHTMLtitle = $name; } - function setPageTitle( $name ) { $this->mPagetitle = $name; } + function setHTMLTitle( $name ) {$this->mHTMLtitle = $name; } + function setPageTitle( $name ) { + global $action, $wgContLang; + $name = $wgContLang->convert($name, true); + $this->mPagetitle = $name; + if(!empty($action)) { + $taction = $this->getPageTitleActionText(); + if( !empty( $taction ) ) { + $name .= ' - '.$taction; + } + } + $this->setHTMLTitle( $name . ' - ' . wfMsg( 'wikititlesuffix' ) ); + } + function getHTMLTitle() { return $this->mHTMLtitle; } function getPageTitle() { return $this->mPagetitle; } function setSubtitle( $str ) { $this->mSubtitle = $str; } function getSubtitle() { return $this->mSubtitle; } - function setArticleFlag( $v ) { $this->mIsarticle = $v; } function isArticle() { return $this->mIsarticle; } function setPrintable() { $this->mPrintable = true; } function isPrintable() { return $this->mPrintable; } + function setSyndicated( $show = true ) { $this->mShowFeedLinks = $show; } + function isSyndicated() { return $this->mShowFeedLinks; } + function setOnloadHandler( $js ) { $this->mOnloadHandler = $js; } + function getOnloadHandler() { return $this->mOnloadHandler; } + function disable() { $this->mDoNothing = true; } - function getLanguageLinks() { - global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode; - global $wgDBconnection, $wgDBname, $wgDBintlname; - - if ( ! $wgUseNewInterlanguage ) - return $this->mLanguageLinks; - - mysql_select_db( $wgDBintlname, $wgDBconnection ) or die( - htmlspecialchars(mysql_error()) ); + function setArticleRelated( $v ) { + $this->mIsArticleRelated = $v; + if ( !$v ) { + $this->mIsarticle = false; + } + } + function setArticleFlag( $v ) { + $this->mIsarticle = $v; + if ( $v ) { + $this->mIsArticleRelated = $v; + } + } - $list = array(); - $sql = "SELECT * FROM ilinks WHERE lang_from=\"" . - "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\""; - $res = mysql_query( $sql, $wgDBconnection ); + function isArticleRelated() { return $this->mIsArticleRelated; } - while ( $q = mysql_fetch_object ( $res ) ) { - $list[] = $q->lang_to . ":" . $q->title_to; - } - mysql_free_result( $res ); - mysql_select_db( $wgDBname, $wgDBconnection ) or die( - htmlspecialchars(mysql_error()) ); + function getLanguageLinks() { return $this->mLanguageLinks; } + function addLanguageLinks($newLinkArray) { + $this->mLanguageLinks += $newLinkArray; + } + function setLanguageLinks($newLinkArray) { + $this->mLanguageLinks = $newLinkArray; + } - return $list; + function getCategoryLinks() { + return $this->mCategoryLinks; + } + function addCategoryLinks($newLinkArray) { + $this->mCategoryLinks += $newLinkArray; + } + function setCategoryLinks($newLinkArray) { + $this->mCategoryLinks += $newLinkArray; } - function supressQuickbar() { $this->mSupressQuickbar = true; } - function isQuickbarSupressed() { return $this->mSupressQuickbar; } + function suppressQuickbar() { $this->mSuppressQuickbar = true; } + function isQuickbarSuppressed() { return $this->mSuppressQuickbar; } function addHTML( $text ) { $this->mBodytext .= $text; } - function addHeadtext( $text ) { $this->mHeadtext .= $text; } + function clearHTML() { $this->mBodytext = ''; } function debug( $text ) { $this->mDebugtext .= $text; } - # First pass--just handle sections, pass the rest off - # to doWikiPass2() which does all the real work. - # - - function addWikiText( $text, $linestart = true ) - { - global $wgUseTeX; - wfProfileIn( "OutputPage::addWikiText" ); - $unique = "3iyZiyA7iMwg5rhxP0Dcc9oTnj8qD1jm1Sfv4"; - $unique2 = "4LIQ9nXtiYFPCSfitVwDw7EYwQlL4GeeQ7qSO"; - $unique3 = "fPaA8gDfdLBqzj68Yjg9Hil3qEF8JGO0uszIp"; - $nwlist = array(); - $nwsecs = 0; - $mathlist = array(); - $mathsecs = 0; - $prelist = array (); - $presecs = 0; - $stripped = ""; - $stripped2 = ""; - $stripped3 = ""; - - while ( "" != $text ) { - $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 ); - $stripped .= $p[0]; - if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $text = ""; } - else { - $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 ); - ++$nwsecs; - $nwlist[$nwsecs] = wfEscapeHTMLTagsOnly($q[0]); - $stripped .= $unique; - $text = $q[1]; - } - } + function setParserOptions( $options ) { + return wfSetVar( $this->mParserOptions, $options ); + } - if( $wgUseTeX ) { - while ( "" != $stripped ) { - $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 ); - $stripped2 .= $p[0]; - if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $stripped = ""; } - else { - $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 ); - ++$mathsecs; - $mathlist[$mathsecs] = renderMath($q[0]); - $stripped2 .= $unique2; - $stripped = $q[1]; - } - } - } else { - $stripped2 = $stripped; - } + /** + * Convert wikitext to HTML and add it to the buffer + */ + function addWikiText( $text, $linestart = true ) { + global $wgParser, $wgTitle, $wgUseTidy; - while ( "" != $stripped2 ) { - $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 ); - $stripped3 .= $p[0]; - if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $stripped2 = ""; } - else { - $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 ); - ++$presecs; - $prelist[$presecs] = "
". wfEscapeHTMLTagsOnly($q[0]). "
"; - $stripped3 .= $unique3; - $stripped2 = $q[1]; - } - } + $parserOutput = $wgParser->parse( $text, $wgTitle, $this->mParserOptions, $linestart ); + $this->mLanguageLinks += $parserOutput->getLanguageLinks(); + $this->mCategoryLinks += $parserOutput->getCategoryLinks(); + $this->addHTML( $parserOutput->getText() ); + } - $text = $this->doWikiPass2( $stripped3, $linestart ); + /** + * Add wikitext to the buffer, assuming that this is the primary text for a page view + * Saves the text into the parser cache if possible + */ + function addPrimaryWikiText( $text, $cacheArticle ) { + global $wgParser, $wgParserCache, $wgUser, $wgTitle, $wgUseTidy; - for ( $i = 1; $i <= $presecs; ++$i ) { - $text = preg_replace( "/{$unique3}/", str_replace( '$', '\$', $prelist[$i] ), $text, 1 ); - } + $parserOutput = $wgParser->parse( $text, $wgTitle, $this->mParserOptions, true ); - for ( $i = 1; $i <= $mathsecs; ++$i ) { - $text = preg_replace( "/{$unique2}/", str_replace( '$', '\$', $mathlist[$i] ), $text, 1 ); + $text = $parserOutput->getText(); + + if ( $cacheArticle ) { + $wgParserCache->save( $parserOutput, $cacheArticle, $wgUser ); } - for ( $i = 1; $i <= $nwsecs; ++$i ) { - $text = preg_replace( "/{$unique}/", str_replace( '$', '\$', $nwlist[$i] ), $text, 1 ); - } + $this->mLanguageLinks += $parserOutput->getLanguageLinks(); + $this->mCategoryLinks += $parserOutput->getCategoryLinks(); $this->addHTML( $text ); - wfProfileOut(); + } + + /** + * Add the output of a QuickTemplate to the output buffer + * @param QuickTemplate $template + */ + function addTemplate( &$template ) { + ob_start(); + $template->execute(); + $this->addHtml( ob_get_contents() ); + ob_end_clean(); + } + + /** + * @param $article + * @param $user + */ + function tryParserCache( $article, $user ) { + global $wgParserCache; + $parserOutput = $wgParserCache->get( $article, $user ); + if ( $parserOutput !== false ) { + $this->mLanguageLinks += $parserOutput->getLanguageLinks(); + $this->mCategoryLinks += $parserOutput->getCategoryLinks(); + $this->addHTML( $parserOutput->getText() ); + return true; + } else { + return false; + } + } + + /** + * Set the maximum cache time on the Squid in seconds + * @param $maxage + */ + function setSquidMaxage( $maxage ) { + $this->mSquidMaxage = $maxage; + } + + /** + * Use enableClientCache(false) to force it to send nocache headers + * @param $state + */ + function enableClientCache( $state ) { + return wfSetVar( $this->mEnableClientCache, $state ); } function sendCacheControl() { - if( $this->mLastModified != "" ) { - header( "Cache-Control: private, must-revalidate, max-age=0" ); - header( "Last-modified: {$this->mLastModified}" ); + global $wgUseSquid, $wgUseESI; + # FIXME: This header may cause trouble with some versions of Internet Explorer + header( 'Vary: Accept-Encoding, Cookie' ); + if( $this->mEnableClientCache ) { + if( $wgUseSquid && ! isset( $_COOKIE[ini_get( 'session.name') ] ) && + ! $this->isPrintable() && $this->mSquidMaxage != 0 ) + { + if ( $wgUseESI ) { + # We'll purge the proxy cache explicitly, but require end user agents + # to revalidate against the proxy on each visit. + # Surrogate-Control controls our Squid, Cache-Control downstream caches + wfDebug( "** proxy caching with ESI; {$this->mLastModified} **\n", false ); + # start with a shorter timeout for initial testing + # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"'); + header( 'Surrogate-Control: max-age='.$wgSquidMaxage.'+'.$this->mSquidMaxage.', content="ESI/1.0"'); + header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' ); + } else { + # We'll purge the proxy cache for anons explicitly, but require end user agents + # to revalidate against the proxy on each visit. + # IMPORTANT! The Squid needs to replace the Cache-Control header with + # Cache-Control: s-maxage=0, must-revalidate, max-age=0 + wfDebug( "** local proxy caching; {$this->mLastModified} **\n", false ); + # start with a shorter timeout for initial testing + # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" ); + header( 'Cache-Control: s-maxage='.$this->mSquidMaxage.', must-revalidate, max-age=0' ); + } + } else { + # We do want clients to cache if they can, but they *must* check for updates + # on revisiting the page. + wfDebug( "** private caching; {$this->mLastModified} **\n", false ); + header( "Expires: -1" ); + header( "Cache-Control: private, must-revalidate, max-age=0" ); + } + if($this->mLastModified) header( "Last-modified: {$this->mLastModified}" ); } else { - header( "Cache-Control: no-cache" ); # Experimental - see below - header( "Pragma: no-cache" ); - header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" ); + wfDebug( "** no caching **\n", false ); + + # In general, the absence of a last modified header should be enough to prevent + # the client from using its cache. We send a few other things just to make sure. + header( 'Expires: -1' ); + header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' ); + header( 'Pragma: no-cache' ); } - header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page! } - - # Finally, all the text has been munged and accumulated into - # the object, let's actually output it: - # - function output() - { + + /** + * Finally, all the text has been munged and accumulated into + * the object, let's actually output it: + */ + function output() { global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration; - global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode; - wfProfileIn( "OutputPage::output" ); + global $wgInputEncoding, $wgOutputEncoding, $wgContLanguageCode; + global $wgDebugRedirects, $wgMimeType, $wgProfiler; + + if( $this->mDoNothing ){ + return; + } + $fname = 'OutputPage::output'; + wfProfileIn( $fname ); $sk = $wgUser->getSkin(); - wfProfileIn( "OutputPage::output-headers" ); - $this->sendCacheControl(); + if ( '' != $this->mRedirect ) { + if( substr( $this->mRedirect, 0, 4 ) != 'http' ) { + # Standards require redirect URLs to be absolute + global $wgServer; + $this->mRedirect = $wgServer . $this->mRedirect; + } + if( $this->mRedirectCode == '301') { + if( !$wgDebugRedirects ) { + header("HTTP/1.1 {$this->mRedirectCode} Moved Permanently"); + } + $this->mLastModified = gmdate( 'D, j M Y H:i:s' ) . ' GMT'; + } - header( "Content-type: text/html; charset={$wgOutputEncoding}" ); - header( "Content-language: {$wgLanguageCode}" ); - - if ( "" != $this->mRedirect ) { - header( "Location: {$this->mRedirect}" ); - wfProfileOut(); + $this->sendCacheControl(); + + if( $wgDebugRedirects ) { + $url = htmlspecialchars( $this->mRedirect ); + print "\n\nRedirect\n\n\n"; + print "

Location: $url

\n"; + print "\n\n"; + } else { + header( 'Location: '.$this->mRedirect ); + } + if ( isset( $wgProfiler ) ) { wfDebug( $wgProfiler->getOutput() ); } return; } + + # Buffer output; final headers may depend on later processing + ob_start(); + + $this->transformBuffer(); + + # Disable temporary placeholders, so that the skin produces HTML + $sk->postParseLinkColour( false ); + + header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" ); + header( 'Content-language: '.$wgContLanguageCode ); + $exp = time() + $wgCookieExpiration; foreach( $this->mCookies as $name => $val ) { - setcookie( $name, $val, $exp, "/" ); + setcookie( $name, $val, $exp, '/' ); } - wfProfileOut(); - - wfProfileIn( "OutputPage::output-middle" ); - $sk->initPage(); - $this->out( $this->headElement() ); - $this->out( "\ngetBodyOptions(); - foreach ( $ops as $name => $val ) { - $this->out( " $name='$val'" ); - } - $this->out( ">\n" ); - if ( $wgDebugComments ) { - $this->out( "\n" ); - } - $this->out( $sk->beforeContent() ); - wfProfileOut(); + wfProfileIn( 'Output-skin' ); + $sk->outputPage( $this ); + wfProfileOut( 'Output-skin' ); - wfProfileIn( "OutputPage::output-bodytext" ); - $this->out( $this->mBodytext ); - wfProfileOut(); - wfProfileIn( "OutputPage::output-after" ); - $this->out( $sk->afterContent() ); - wfProfileOut(); - - wfProfileOut(); # A hack - we can't report after here - $this->out( $this->reportTime() ); - - $this->out( "\n" ); - flush(); + $this->sendCacheControl(); + ob_end_flush(); } - function out( $ins ) - { - global $wgInputEncoding, $wgOutputEncoding, $wgLang; + function out( $ins ) { + global $wgInputEncoding, $wgOutputEncoding, $wgContLang; if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) { $outs = $ins; } else { - $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins ); + $outs = $wgContLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins ); if ( false === $outs ) { $outs = $ins; } } print $outs; } - function setEncodings() - { - global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding; - global $wgUser, $wgLang; + function setEncodings() { + global $wgInputEncoding, $wgOutputEncoding; + global $wgUser, $wgContLang; $wgInputEncoding = strtolower( $wgInputEncoding ); - $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET']; - + if( $wgUser->getOption( 'altencoding' ) ) { - $wgLang->setAltEncoding(); + $wgContLang->setAltEncoding(); return; } - if ( "" == $s ) { + if ( empty( $_SERVER['HTTP_ACCEPT_CHARSET'] ) ) { $wgOutputEncoding = strtolower( $wgOutputEncoding ); return; } - $a = explode( ",", $s ); + + /* + # This code is unused anyway! + # Commenting out. --bv 2003-11-15 + + $a = explode( ",", $_SERVER['HTTP_ACCEPT_CHARSET'] ); $best = 0.0; $bestset = "*"; @@ -426,973 +471,348 @@ class OutputPage { # Disable for now # + */ $wgOutputEncoding = $wgInputEncoding; } - function reportTime() - { - global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS; - global $wgProfiling, $wgProfileStack, $wgUser; + /** + * Returns a HTML comment with the elapsed time since request. + * This method has no side effects. + */ + function reportTime() { + global $wgRequestTime; - list( $usec, $sec ) = explode( " ", microtime() ); - $now = (float)$sec + (float)$usec; - - list( $usec, $sec ) = explode( " ", $wgRequestTime ); + $now = wfTime(); + list( $usec, $sec ) = explode( ' ', $wgRequestTime ); $start = (float)$sec + (float)$usec; $elapsed = $now - $start; - if ( "" != $wgDebugLogFile ) { - $prof = ""; - if( $wgProfiling and count( $wgProfileStack ) ) { - $lasttime = $start; - foreach( $wgProfileStack as $ile ) { - # "foo::bar 99 0.12345 1 0.23456 2" - if( preg_match( '/^(\S+)\s+([0-9]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/', $ile, $m ) ) { - $thisstart = (float)$m[3] + (float)$m[4] - $start; - $thisend = (float)$m[5] + (float)$m[6] - $start; - $thiselapsed = $thisend - $thisstart; - $thispercent = $thiselapsed / $elapsed * 100.0; - - $prof .= sprintf( "\tat %04.3f in %04.3f (%2.1f%%) - %s %s\n", - $thisstart, $thiselapsed, $thispercent, - str_repeat( "*", $m[2] ), $m[1] ); - $lasttime = $thistime; - #$prof .= "\t(^ $ile)\n"; - } else { - $prof .= "\t?broken? $ile\n"; - } - } - } - - if( $forward = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'] ) - $forward = " forwarded for $forward"; - if( $client = $HTTP_SERVER_VARS['HTTP_CLIENT_IP'] ) - $forward .= " client IP $client"; - if( $from = $HTTP_SERVER_VARS['HTTP_FROM'] ) - $forward .= " from $from"; - if( $forward ) - $forward = "\t(proxied via {$HTTP_SERVER_VARS['REMOTE_ADDR']}{$forward})"; - if($wgUser->getId() == 0) - $forward .= " anon"; - $log = sprintf( "%s\t%04.3f\t%s\n", - gmdate( "YmdHis" ), $elapsed, - urldecode( $HTTP_SERVER_VARS['REQUEST_URI'] . $forward ) ); - error_log( $log . $prof, 3, $wgDebugLogFile ); + # Use real server name if available, so we know which machine + # in a server farm generated the current page. + if ( function_exists( 'posix_uname' ) ) { + $uname = @posix_uname(); + } else { + $uname = false; + } + if( is_array( $uname ) && isset( $uname['nodename'] ) ) { + $hostname = $uname['nodename']; + } else { + # This may be a virtual server. + $hostname = $_SERVER['SERVER_NAME']; } - $com = sprintf( "", - $elapsed ); + $com = sprintf( "", + $hostname, $elapsed ); return $com; } - # Note: these arguments are keys into wfMsg(), not text! - # - function errorpage( $title, $msg ) - { + /** + * Note: these arguments are keys into wfMsg(), not text! + */ + function errorpage( $title, $msg ) { global $wgTitle; - $this->mDebugtext .= "Original title: " . + $this->mDebugtext .= 'Original title: ' . $wgTitle->getPrefixedText() . "\n"; - $this->setHTMLTitle( wfMsg( "errorpagetitle" ) ); $this->setPageTitle( wfMsg( $title ) ); - $this->setRobotpolicy( "noindex,nofollow" ); - $this->setArticleFlag( false ); + $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) ); + $this->setRobotpolicy( 'noindex,nofollow' ); + $this->setArticleRelated( false ); + $this->suppressQuickbar(); + + $this->enableClientCache( false ); + $this->mRedirect = ''; - $this->mBodytext = ""; - $this->addHTML( "

" . wfMsg( $msg ) . "\n" ); + $this->mBodytext = ''; + $this->addHTML( '

' . wfMsg( $msg ) . "

\n" ); $this->returnToMain( false ); $this->output(); - exit; + wfErrorExit(); } - function sysopRequired() - { + function sysopRequired() { global $wgUser; - $this->setHTMLTitle( wfMsg( "errorpagetitle" ) ); - $this->setPageTitle( wfMsg( "sysoptitle" ) ); - $this->setRobotpolicy( "noindex,nofollow" ); - $this->setArticleFlag( false ); - $this->mBodytext = ""; + $this->setPageTitle( wfMsg( 'sysoptitle' ) ); + $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) ); + $this->setRobotpolicy( 'noindex,nofollow' ); + $this->setArticleRelated( false ); + $this->mBodytext = ''; $sk = $wgUser->getSkin(); - $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" ); - $text = str_replace( "$1", $ap, wfMsg( "sysoptext" ) ); - $this->addHTML( $text ); + $ap = $sk->makeKnownLink( wfMsgForContent( 'administrators' ), '' ); + $this->addHTML( wfMsg( 'sysoptext', $ap ) ); $this->returnToMain(); } - function developerRequired() - { + function developerRequired() { global $wgUser; - $this->setHTMLTitle( wfMsg( "errorpagetitle" ) ); - $this->setPageTitle( wfMsg( "developertitle" ) ); - $this->setRobotpolicy( "noindex,nofollow" ); - $this->setArticleFlag( false ); - $this->mBodytext = ""; + $this->setPageTitle( wfMsg( 'developertitle' ) ); + $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) ); + $this->setRobotpolicy( 'noindex,nofollow' ); + $this->setArticleRelated( false ); + $this->mBodytext = ''; $sk = $wgUser->getSkin(); - $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" ); - $text = str_replace( "$1", $ap, wfMsg( "developertext" ) ); - $this->addHTML( $text ); + $ap = $sk->makeKnownLink( wfMsgForContent( 'administrators' ), '' ); + $this->addHTML( wfMsg( 'developertext', $ap ) ); $this->returnToMain(); } - function databaseError( $fname ) - { - global $wgUser, $wgCommandLineMode; + function loginToUse() { + global $wgUser, $wgTitle, $wgContLang; - $this->setPageTitle( wfMsg( "databaseerror" ) ); - $this->setRobotpolicy( "noindex,nofollow" ); + $this->setPageTitle( wfMsg( 'loginreqtitle' ) ); + $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) ); + $this->setRobotpolicy( 'noindex,nofollow' ); $this->setArticleFlag( false ); + $this->mBodytext = ''; + $this->addWikiText( wfMsg( 'loginreqtext' ) ); + + # We put a comment in the .html file so a Sysop can diagnose the page the + # user can't see. + $this->addHTML( "\n' ); + $this->returnToMain(); # Flip back to the main page after 10 seconds. + } + + function databaseError( $fname, $sql, $error, $errno ) { + global $wgUser, $wgCommandLineMode; + + $this->setPageTitle( wfMsgNoDB( 'databaseerror' ) ); + $this->setRobotpolicy( 'noindex,nofollow' ); + $this->setArticleRelated( false ); + $this->enableClientCache( false ); + $this->mRedirect = ''; if ( $wgCommandLineMode ) { - $msg = wfMsg( "dberrortextcl" ); + $msg = wfMsgNoDB( 'dberrortextcl', htmlspecialchars( $sql ), + htmlspecialchars( $fname ), $errno, htmlspecialchars( $error ) ); } else { - $msg = wfMsg( "dberrortextcl" ); + $msg = wfMsgNoDB( 'dberrortext', htmlspecialchars( $sql ), + htmlspecialchars( $fname ), $errno, htmlspecialchars( $error ) ); } - $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), $msg ); - $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg ); - $msg = str_replace( "$3", wfLastErrno(), $msg ); - $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg ); - if ( $wgCommandLineMode ) { - print $msg; - exit(); + if ( $wgCommandLineMode || !is_object( $wgUser )) { + print $msg."\n"; + wfErrorExit(); } - $sk = $wgUser->getSkin(); - $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ), - wfMsg( "searchingwikipedia" ) ); - $msg = str_replace( "$5", $shlink, $msg ); - $this->mBodytext = $msg; $this->output(); - exit(); + wfErrorExit(); } - function readOnlyPage() - { + function readOnlyPage( $source = null, $protected = false ) { global $wgUser, $wgReadOnlyFile; - $this->setPageTitle( wfMsg( "readonly" ) ); - $this->setRobotpolicy( "noindex,nofollow" ); - $this->setArticleFlag( false ); + $this->setRobotpolicy( 'noindex,nofollow' ); + $this->setArticleRelated( false ); + + if( $protected ) { + $this->setPageTitle( wfMsg( 'viewsource' ) ); + $this->addWikiText( wfMsg( 'protectedtext' ) ); + } else { + $this->setPageTitle( wfMsg( 'readonly' ) ); + $reason = file_get_contents( $wgReadOnlyFile ); + $this->addWikiText( wfMsg( 'readonlytext', $reason ) ); + } + + if( is_string( $source ) ) { + if( strcmp( $source, '' ) == 0 ) { + $source = wfMsg( 'noarticletext' ); + } + $rows = $wgUser->getOption( 'rows' ); + $cols = $wgUser->getOption( 'cols' ); + $text = "\n"; + $this->addHTML( $text ); + } - $reason = implode( "", file( $wgReadOnlyFile ) ); - $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) ); - $this->addHTML( $text ); $this->returnToMain( false ); } - function fatalError( $message ) - { + function fatalError( $message ) { $this->setPageTitle( wfMsg( "internalerror" ) ); $this->setRobotpolicy( "noindex,nofollow" ); - $this->setArticleFlag( false ); + $this->setArticleRelated( false ); + $this->enableClientCache( false ); + $this->mRedirect = ''; $this->mBodytext = $message; $this->output(); - exit; - } - - function unexpectedValueError( $name, $val ) - { - $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) ); - $msg = str_replace( "$2", $val, $msg ); - $this->fatalError( $msg ); - } - - function fileCopyError( $old, $new ) - { - $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) ); - $msg = str_replace( "$2", $new, $msg ); - $this->fatalError( $msg ); - } - - function fileRenameError( $old, $new ) - { - $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) ); - $msg = str_replace( "$2", $new, $msg ); - $this->fatalError( $msg ); + wfErrorExit(); } - function fileDeleteError( $name ) - { - $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) ); - $this->fatalError( $msg ); + function unexpectedValueError( $name, $val ) { + $this->fatalError( wfMsg( 'unexpected', $name, $val ) ); } - function fileNotFoundError( $name ) - { - $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) ); - $this->fatalError( $msg ); + function fileCopyError( $old, $new ) { + $this->fatalError( wfMsg( 'filecopyerror', $old, $new ) ); } - function returnToMain( $auto = true ) - { - global $wgUser, $wgOut, $returnto; - - $sk = $wgUser->getSkin(); - if ( "" == $returnto ) { - $returnto = wfMsg( "mainpage" ); - } - $link = $sk->makeKnownLink( $returnto, "" ); - - $r = str_replace( "$1", $link, wfMsg( "returnto" ) ); - if ( $auto ) { - $wgOut->addMeta( "http:Refresh", "10;url=" . - wfLocalUrlE( wfUrlencode( $returnto ) ) ); - } - $wgOut->addHTML( "\n

$r\n" ); + function fileRenameError( $old, $new ) { + $this->fatalError( wfMsg( 'filerenameerror', $old, $new ) ); } - # Well, OK, it's actually about 14 passes. But since all the - # hard lifting is done inside PHP's regex code, it probably - # wouldn't speed things up much to add a real parser. - # - function doWikiPass2( $text, $linestart ) - { - global $wgUser, $wgLang, $wgMungeDates; - wfProfileIn( "OutputPage::doWikiPass2" ); - - $text = $this->removeHTMLtags( $text ); - $text = $this->replaceVariables( $text ); - - $text = preg_replace( "/(^|\n)-----*/", "\\1


", $text ); - $text = str_replace ( "
", "
", $text ); - - $text = $this->doQuotes( $text ); - $text = $this->doHeadings( $text ); - $text = $this->doBlockLevels( $text, $linestart ); - - if($wgMungeDates) - $text = $wgLang->replaceDates( $text ); - $text = $this->replaceExternalLinks( $text ); - $text = $this->replaceInternalLinks ( $text ); - - $text = $this->magicISBN( $text ); - $text = $this->magicRFC( $text ); - $text = $this->formatHeadings( $text ); - - $sk = $wgUser->getSkin(); - $text = $sk->transformContent( $text ); - - wfProfileOut(); - return $text; + function fileDeleteError( $name ) { + $this->fatalError( wfMsg( 'filedeleteerror', $name ) ); } - /* private */ function doQuotes( $text ) - { - $text = preg_replace( "/'''(.+)'''/mU", "\$1", $text ); - $text = preg_replace( "/''(.+)''/mU", "\$1", $text ); - return $text; + function fileNotFoundError( $name ) { + $this->fatalError( wfMsg( 'filenotfound', $name ) ); } - /* private */ function doHeadings( $text ) - { - for ( $i = 6; $i >= 1; --$i ) { - $h = substr( "======", 0, $i ); - $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m", - "\\1\\2", $text ); - } - return $text; - } + /** + * return from error messages or notes + * @param $auto automatically redirect the user after 10 seconds + * @param $returnto page title to return to. Default is Main Page. + */ + function returnToMain( $auto = true, $returnto = NULL ) { + global $wgUser, $wgOut, $wgRequest; - # Note: we have to do external links before the internal ones, - # and otherwise take great care in the order of things here, so - # that we don't end up interpreting some URLs twice. - - /* private */ function replaceExternalLinks( $text ) - { - wfProfileIn( "OutputPage::replaceExternalLinks" ); - $text = $this->subReplaceExternalLinks( $text, "http", true ); - $text = $this->subReplaceExternalLinks( $text, "https", true ); - $text = $this->subReplaceExternalLinks( $text, "ftp", false ); - $text = $this->subReplaceExternalLinks( $text, "gopher", false ); - $text = $this->subReplaceExternalLinks( $text, "news", false ); - $text = $this->subReplaceExternalLinks( $text, "mailto", false ); - wfProfileOut(); - return $text; - } - - /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber ) - { - global $wgUser, $printable; - global $wgAllowExternalImages; - - - $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3"; - $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF"; - - # this is the list of separators that should be ignored if they - # are the last character of an URL but that should be included - # if they occur within the URL, e.g. "go to www.foo.com, where .." - # in this case, the last comma should not become part of the URL, - # but in "www.foo.com/123,2342,32.htm" it should. - $sep = ",;\.:"; - $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF"; - $images = "gif|png|jpg|jpeg"; - - # PLEASE NOTE: The curly braces { } are not part of the regex, - # they are interpreted as part of the string (used to tell PHP - # that the content of the string should be inserted there). - $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." . - "((?i){$images})([^{$uc}]|$)/"; - - $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/"; - $sk = $wgUser->getSkin(); - - if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls - $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" . - "/\\4.\\5", "\\4.\\5" ) . "\\6", $s ); + if ( $returnto == NULL ) { + $returnto = $wgRequest->getText( 'returnto' ); } - $s = preg_replace( $e2, "\\1" . "getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML( - "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) . - "\\5", $s ); - $s = str_replace( $unique, $protocol, $s ); - - $a = explode( "[{$protocol}:", " " . $s ); - $s = array_shift( $a ); - $s = substr( $s, 1 ); - - $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD"; - $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD"; - - foreach ( $a as $line ) { - if ( preg_match( $e1, $line, $m ) ) { - $link = "{$protocol}:{$m[1]}"; - $trail = $m[2]; - if ( $autonumber ) { $text = "[" . ++$this->mAutonumber . "]"; } - else { $text = wfEscapeHTML( $link ); } - } else if ( preg_match( $e2, $line, $m ) ) { - $link = "{$protocol}:{$m[1]}"; - $text = $m[2]; - $trail = $m[3]; - } else { - $s .= "[{$protocol}:" . $line; - continue; - } - if ( $printable == "yes") $paren = " (" . htmlspecialchars ( $link ) . ")"; - else $paren = ""; - $la = $sk->getExternalLinkAttributes( $link, $text ); - $s .= "{$text}{$paren}{$trail}"; + $returnto = htmlspecialchars( $returnto ); - } - return $s; - } - - /* private */ function replaceInternalLinks( $s ) - { - global $wgTitle, $wgUser, $wgLang; - global $wgLinkCache, $wgInterwikiMagic; - global $wgNamespacesWithSubpages; - wfProfileIn( $fname = "OutputPage::replaceInternalLinks" ); - - wfProfileIn( "$fname-setup" ); - $tc = Title::legalChars() . "#"; $sk = $wgUser->getSkin(); - - $a = explode( "[[", " " . $s ); - $s = array_shift( $a ); - $s = substr( $s, 1 ); - - $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD"; - $e2 = "/^([{$tc}]+)]](.*)\$/sD"; - wfProfileOut(); - - wfProfileIn( "$fname-loop" ); - foreach ( $a as $line ) { - if ( preg_match( $e1, $line, $m ) ) { # page with alternate text - - $text = $m[2]; - $trail = $m[3]; - - } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text - - $text = ""; - $trail = $m[2]; - } - - else { # Invalid form; output directly - $s .= "[[" . $line ; - continue; - } - if(substr($m[1],0,1)=="/") { # subpage - if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown - $m[1]=substr($m[1],1,strlen($m[1])-2); - $noslash=$m[1]; - - } else { - $noslash=substr($m[1],1); - } - if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here - $link = $wgTitle->getPrefixedText(). "/" . trim($noslash); - if(!$text) { - $text= $m[1]; - } # this might be changed for ugliness reasons - } else { - $link = $noslash; # no subpage allowed, use standard link - } - } else { # no subpage - $link = $m[1]; - } - - if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) { - $pre = strtolower( $m[1] ); - $suf = $m[2]; - if ( $wgLang->getNsIndex( $pre ) == - Namespace::getImage() ) { - $nt = Title::newFromText( $suf ); - $name = $nt->getDBkey(); - if ( "" == $text ) { $text = $nt->GetText(); } - - $wgLinkCache->addImageLink( $name ); - $s .= $sk->makeImageLink( $name, - wfImageUrl( $name ), $text ); - $s .= $trail; - } else if ( "media" == $pre ) { - $nt = Title::newFromText( $suf ); - $name = $nt->getDBkey(); - if ( "" == $text ) { $text = $nt->GetText(); } - - $wgLinkCache->addImageLink( $name ); - $s .= $sk->makeMediaLink( $name, - wfImageUrl( $name ), $text ); - $s .= $trail; - } else { - $l = $wgLang->getLanguageName( $pre ); - if ( "" == $l or !$wgInterwikiMagic or - Namespace::isTalk( $wgTitle->getNamespace() ) ) { - if ( "" == $text ) { $text = $link; } - $s .= $sk->makeLink( $link, $text, "", $trail ); - } else { - array_push( $this->mLanguageLinks, "$pre:$suf" ); - $s .= $trail; - } - } -# } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) { -# $link = substr( $link, 2 ); -# $s .= "{$text}{$trail}"; - } else { - if ( "" == $text ) { $text = $link; } - $s .= $sk->makeLink( $link, $text, "", $trail ); - } + if ( '' == $returnto ) { + $returnto = wfMsgForContent( 'mainpage' ); } - wfProfileOut(); - wfProfileOut(); - return $s; - } + $link = $sk->makeKnownLink( $returnto, '' ); - # Some functions here used by doBlockLevels() - # - /* private */ function closeParagraph() - { - $result = ""; - if ( 0 != strcmp( "p", $this->mLastSection ) && - 0 != strcmp( "", $this->mLastSection ) ) { - $result = "mLastSection . ">"; - } - $this->mLastSection = ""; - return $result; - } - # getCommon() returns the length of the longest common substring - # of both arguments, starting at the beginning of both. - # - /* private */ function getCommon( $st1, $st2 ) - { - $fl = strlen( $st1 ); - $shorter = strlen( $st2 ); - if ( $fl < $shorter ) { $shorter = $fl; } - - for ( $i = 0; $i < $shorter; ++$i ) { - if ( $st1{$i} != $st2{$i} ) { break; } - } - return $i; - } - # These next three functions open, continue, and close the list - # element appropriate to the prefix character passed into them. - # - /* private */ function openList( $char ) - { - $result = $this->closeParagraph(); - - if ( "*" == $char ) { $result .= ""; } - else if ( "#" == $char ) { return ""; } - else if ( ":" == $char ) { - if ( $this->mDTopen ) { - $this->mDTopen = false; - return ""; - } else { - return ""; - } - } - return ""; + $wgOut->addMeta ( 'KEYWORDS' , $a ) ; } - /* private */ function doBlockLevels( $text, $linestart ) - { - wfProfileIn( "OutputPage::doBlockLevels" ); - # Parsing through the text line by line. The main thing - # happening here is handling of block-level elements p, pre, - # and making lists from lines starting with * # : etc. - # - $a = explode( "\n", $text ); - $text = $lastPref = ""; - $this->mDTopen = $inBlockElem = false; - - if ( ! $linestart ) { $text .= array_shift( $a ); } - foreach ( $a as $t ) { - if ( "" != $text ) { $text .= "\n"; } - - $oLine = $t; - $opl = strlen( $lastPref ); - $npl = strspn( $t, "*#:;" ); - $pref = substr( $t, 0, $npl ); - $pref2 = str_replace( ";", ":", $pref ); - $t = substr( $t, $npl ); - - if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) { - $text .= $this->nextItem( substr( $pref, -1 ) ); - - if ( ";" == substr( $pref, -1 ) ) { - $cpos = strpos( $t, ":" ); - if ( ! ( false === $cpos ) ) { - $term = substr( $t, 0, $cpos ); - $text .= $term . $this->nextItem( ":" ); - $t = substr( $t, $cpos + 1 ); - } - } - } else if (0 != $npl || 0 != $opl) { - $cpl = $this->getCommon( $pref, $lastPref ); + /** + * @private + */ + function headElement() { + global $wgDocType, $wgDTD, $wgContLanguageCode, $wgOutputEncoding, $wgMimeType; + global $wgUser, $wgContLang, $wgRequest; - while ( $cpl < $opl ) { - $text .= $this->closeList( $lastPref{$opl-1} ); - --$opl; - } - if ( $npl <= $cpl && $cpl > 0 ) { - $text .= $this->nextItem( $pref{$cpl-1} ); - } - while ( $npl > $cpl ) { - $char = substr( $pref, $cpl, 1 ); - $text .= $this->openList( $char ); - - if ( ";" == $char ) { - $cpos = strpos( $t, ":" ); - if ( ! ( false === $cpos ) ) { - $term = substr( $t, 0, $cpos ); - $text .= $term . $this->nextItem( ":" ); - $t = substr( $t, $cpos + 1 ); - } - } - ++$cpl; - } - $lastPref = $pref2; - } - if ( 0 == $npl ) { # No prefix--go to paragraph mode - if ( preg_match( - "/(closeParagraph(); - $inBlockElem = true; - } - if ( ! $inBlockElem ) { - if ( " " == $t{0} ) { - $newSection = "pre"; - # $t = wfEscapeHTML( $t ); - } - else { $newSection = "p"; } - - if ( 0 == strcmp( "", trim( $oLine ) ) ) { - $text .= $this->closeParagraph(); - $text .= "<" . $newSection . ">"; - } else if ( 0 != strcmp( $this->mLastSection, - $newSection ) ) { - $text .= $this->closeParagraph(); - if ( 0 != strcmp( "p", $newSection ) ) { - $text .= "<" . $newSection . ">"; - } - } - $this->mLastSection = $newSection; - } - if ( $inBlockElem && - preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) { - $inBlockElem = false; - } - } - $text .= $t; - } - while ( $npl ) { - $text .= $this->closeList( $pref2{$npl-1} ); - --$npl; - } - if ( "" != $this->mLastSection ) { - if ( "p" != $this->mLastSection ) { - $text .= "mLastSection . ">"; - } - $this->mLastSection = ""; + $xml = ($wgMimeType == 'text/xml'); + if( $xml ) { + $ret = "<" . "?xml version=\"1.0\" encoding=\"$wgOutputEncoding\" ?" . ">\n"; + } else { + $ret = ''; } - wfProfileOut(); - return $text; - } - /* private */ function replaceVariables( $text ) - { - global $wgLang; - wfProfileIn( "OutputPage:replaceVariables" ); - - /* As with sigs, use server's local time -- - ensure this is appropriate for your audience! */ - $v = date( "m" ); - $text = str_replace( "{{CURRENTMONTH}}", $v, $text ); - $v = $wgLang->getMonthName( date( "n" ) ); - $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text ); - $v = $wgLang->getMonthNameGen( date( "n" ) ); - $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text ); - $v = date( "j" ); - $text = str_replace( "{{CURRENTDAY}}", $v, $text ); - $v = $wgLang->getWeekdayName( date( "w" )+1 ); - $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text ); - $v = date( "Y" ); - $text = str_replace( "{{CURRENTYEAR}}", $v, $text ); - $v = $wgLang->time( wfTimestampNow(), false ); - $text = str_replace( "{{CURRENTTIME}}", $v, $text ); - - if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) { - $v = wfNumberOfArticles(); - $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text ); - } - wfProfileOut(); - return $text; - } + $ret .= "\n"; - /* private */ function removeHTMLtags( $text ) - { - wfProfileIn( "OutputPage::removeHTMLtags" ); - $htmlpairs = array( # Tags that must be closed - "b", "i", "u", "font", "big", "small", "sub", "sup", "h1", - "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s", - "strike", "strong", "tt", "var", "div", "center", - "blockquote", "ol", "ul", "dl", "table", "caption", "pre", - "ruby", "rt" , "rb" , "rp" - ); - $htmlsingle = array( - "br", "p", "hr", "li", "dt", "dd" - ); - $htmlnest = array( # Tags that can be nested--?? - "table", "tr", "td", "th", "div", "blockquote", "ol", "ul", - "dl", "font", "big", "small", "sub", "sup" - ); - $tabletags = array( # Can only appear inside table - "td", "th", "tr" - ); - - $htmlsingle = array_merge( $tabletags, $htmlsingle ); - $htmlelements = array_merge( $htmlsingle, $htmlpairs ); - - $htmlattrs = array( # Allowed attributes--no scripting, etc. - "title", "align", "lang", "dir", "width", "height", - "bgcolor", "clear", /* BR */ "noshade", /* HR */ - "cite", /* BLOCKQUOTE, Q */ "size", "face", "color", - /* FONT */ "type", "start", "value", "compact", - /* For various lists, mostly deprecated but safe */ - "summary", "width", "border", "frame", "rules", - "cellspacing", "cellpadding", "valign", "char", - "charoff", "colgroup", "col", "span", "abbr", "axis", - "headers", "scope", "rowspan", "colspan", /* Tables */ - "id", "class", "name", "style" /* For CSS */ - ); - - # Remove HTML comments - $text = preg_replace( "//sU", "", $text ); - - $bits = explode( "<", $text ); - $text = array_shift( $bits ); - $tagstack = array(); $tablestack = array(); - - foreach ( $bits as $x ) { - $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) ); - preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/", - $x, $regs ); - list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs; - error_reporting( $prev ); - - $badtag = 0 ; - if ( in_array( $t = strtolower( $t ), $htmlelements ) ) { - # Check our stack - if ( $slash ) { - # Closing a tag... - if ( ! in_array( $t, $htmlsingle ) && - ( $ot = array_pop( $tagstack ) ) != $t ) { - array_push( $tagstack, $ot ); - $badtag = 1; - } else { - if ( $t == "table" ) { - $tagstack = array_pop( $tablestack ); - } - $newparams = ""; - } - } else { - # Keep track for later - if ( in_array( $t, $tabletags ) && - ! in_array( "table", $tagstack ) ) { - $badtag = 1; - } else if ( in_array( $t, $tagstack ) && - ! in_array ( $t , $htmlnest ) ) { - $badtag = 1 ; - } else if ( ! in_array( $t, $htmlsingle ) ) { - if ( $t == "table" ) { - array_push( $tablestack, $tagstack ); - $tagstack = array(); - } - array_push( $tagstack, $t ); - } - # Strip non-approved attributes from the tag - $newparams = preg_replace( - "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e", - "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')", - $params); - } - if ( ! $badtag ) { - $rest = str_replace( ">", ">", $rest ); - $text .= "<$slash$t$newparams$brace$rest"; - continue; - } - } - $text .= "<" . str_replace( ">", ">", $x); - } - # Close off any remaining tags - while ( $t = array_pop( $tagstack ) ) { - $text .= "\n"; - if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); } + if ( "" == $this->mHTMLtitle ) { + $this->mHTMLtitle = wfMsg( "pagetitle", $this->mPagetitle ); } - wfProfileOut(); - return $text; - } - - -/* - * - * This function accomplishes several tasks: - * 1) Auto-number headings if that option is enabled - * 2) Add an [edit] link to sections for logged in users who have enabled the option - * 3) Add a Table of contents on the top for users who have enabled the option - * 4) Auto-anchor headings - * - * It loops through all headlines, collects the necessary data, then splits up the - * string and re-inserts the newly formatted headlines. - * - * */ - /* private */ function formatHeadings( $text ) - { - global $wgUser,$wgArticle,$wgTitle,$wpPreview; - $nh=$wgUser->getOption( "numberheadings" ); - $st=$wgUser->getOption( "showtoc" ); - $es=$wgUser->getID() && $wgUser->getOption( "editsection" ); - if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;} - - $sk=$wgUser->getSkin(); - preg_match_all("/)(.*?)<\/H[1-6]>/i",$text,$matches); - - $c=0; - - foreach($matches[3] as $headline) { - if($level) { $prevlevel=$level;} - $level=$matches[1][$c]; - if(($nh||$st) && $prevlevel && $level>$prevlevel) { - - $h[$level]=0; // reset when we enter a new level - $toc.=$sk->tocIndent($level-$prevlevel); - $toclevel+=$level-$prevlevel; - - } - if(($nh||$st) && $level<$prevlevel) { - $h[$level+1]=0; // reset when we step back a level - $toc.=$sk->tocUnindent($prevlevel-$level); - $toclevel-=$prevlevel-$level; - - } - $h[$level]++; // count number of headlines for each level - - if($nh||$st) { - for($i=1;$i<=$level;$i++) { - if($h[$i]) { - if($dot) {$numbering.=".";} - $numbering.=$h[$i]; - $dot=1; - } - } - } - - - $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML - $tocline=$canonized_headline; - $canonized_headline=str_replace('"',"",$canonized_headline); - $canonized_headline=str_replace(" ","_",trim($canonized_headline)); - $refer[$c]=$canonized_headline; - $refers[$canonized_headline]++; // count how many in assoc. array so we can track dupes in anchors - $refcount[$c]=$refers[$canonized_headline]; - if($nh||$st) { - $tocline=$numbering ." ". $tocline; - if($nh) { - $headline=$numbering . " " . $headline; // the two are different if the line contains a link - } - } - $anchor=$canonized_headline; - if($refcount[$c]>1) {$anchor.="_".$refcount[$c];} - if($st) { - $toc.=$sk->tocLine($anchor,$tocline,$toclevel); - } - if($es && !isset($wpPreview)) { - $head[$c].=$sk->editSectionLink($c+1); - } - $head[$c].="" - .$headline - ."" - .""; - $numbering=""; - $c++; - $dot=0; - } - - if($st) { - $toclines=$c; - $toc.=$sk->tocUnindent($toclevel); - $toc=$sk->tocTable($toc); + if( $xml ) { + $xmlbits = "xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\""; + } else { + $xmlbits = ''; } + $rtl = $wgContLang->isRTL() ? " dir='RTL'" : ''; + $ret .= "\n"; + $ret .= "\n" . htmlspecialchars( $this->mHTMLtitle ) . "\n"; + array_push( $this->mMetatags, array( "http:Content-type", "$wgMimeType; charset={$wgOutputEncoding}" ) ); - // split up and insert constructed headlines - - $blocks=preg_split("/.*?<\/H[1-6]>/i",$text); - $i=0; - - - foreach($blocks as $block) { - if($es && !isset($wpPreview) && $c>0 && $i==0) { - $full.=$sk->editSectionLink(0); - } - $full.=$block; - if($st && $toclines>3 && !$i) { - $full="".$full.$toc; - } - - $full.=$head[$i]; - $i++; + $ret .= $this->getHeadLinks(); + global $wgStylePath; + if( $this->isPrintable() ) { + $media = ''; + } else { + $media = "media='print'"; } - return $full; - } - - /* private */ function magicISBN( $text ) - { - global $wgLang; + $printsheet = htmlspecialchars( "$wgStylePath/common/wikiprintable.css" ); + $ret .= "\n"; - $a = split( "ISBN ", " $text" ); - if ( count ( $a ) < 2 ) return $text; - $text = substr( array_shift( $a ), 1); - $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - foreach ( $a as $x ) { - $isbn = $blank = "" ; - while ( " " == $x{0} ) { - $blank .= " "; - $x = substr( $x, 1 ); - } - while ( strstr( $valid, $x{0} ) != false ) { - $isbn .= $x{0}; - $x = substr( $x, 1 ); - } - $num = str_replace( "-", "", $isbn ); - $num = str_replace( " ", "", $num ); - - if ( "" == $num ) { - $text .= "ISBN $blank$x"; - } else { - $text .= "specialPage( - "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn"; - $text .= $x; - } - } - return $text; - } + $sk = $wgUser->getSkin(); + $ret .= $sk->getHeadScripts(); + $ret .= $this->mScripts; + $ret .= $sk->getUserStyles(); - /* private */ function magicRFC( $text ) - { - return $text; + $ret .= "\n"; + return $ret; } - /* private */ function headElement() - { - global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding; - - $ret = "\n"; - - if ( "" == $this->mHTMLtitle ) { - $this->mHTMLtitle = $this->mPagetitle; - } - $ret .= "{$this->mHTMLtitle}\n"; - array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) ); + function getHeadLinks() { + global $wgRequest, $wgStylePath; + $ret = ''; foreach ( $this->mMetatags as $tag ) { - if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) { - $a = "http-equiv"; + if ( 0 == strcasecmp( 'http:', substr( $tag[0], 0, 5 ) ) ) { + $a = 'http-equiv'; $tag[0] = substr( $tag[0], 5 ); } else { - $a = "name"; + $a = 'name'; } - $ret .= "\n"; + $ret .= "\n"; } $p = $this->mRobotpolicy; - if ( "" == $p ) { $p = "index,follow"; } - $ret .= "\n"; + if ( '' == $p ) { $p = 'index,follow'; } + $ret .= "\n"; if ( count( $this->mKeywords ) > 0 ) { + $strip = array( + "/<.*?" . ">/" => '', + "/[_]/" => ' ' + ); $ret .= "mKeywords ) . "\">\n"; + htmlspecialchars(preg_replace(array_keys($strip), array_values($strip),implode( ",", $this->mKeywords ))) . "\" />\n"; } foreach ( $this->mLinktags as $tag ) { - $ret .= "\n"; - } - $sk = $wgUser->getSkin(); - $ret .= $sk->getHeadScripts(); - $ret .= $sk->getUserStyles(); - - $ret .= "\n"; + $ret .= ' $val ) { + $ret .= " $attr=\"" . htmlspecialchars( $val ) . "\""; + } + $ret .= " />\n"; + } + if( $this->isSyndicated() ) { + # FIXME: centralize the mime-type and name information in Feed.php + $link = $wgRequest->escapeAppendQuery( 'feed=rss' ); + $ret .= "\n"; + $link = $wgRequest->escapeAppendQuery( 'feed=atom' ); + $ret .= "\n"; + } + # FIXME: get these working + # $fix = htmlspecialchars( $wgStylePath . "/ie-png-fix.js" ); + # $ret .= ""; return $ret; } + + /** + * Run any necessary pre-output transformations on the buffer text + */ + function transformBuffer( $options = 0 ) { + } + +} + } ?>