2004-02-26 13:37:26 +00:00
< ? php
2007-05-01 23:41:44 +00:00
2004-09-02 23:28:24 +00:00
/**
2007-05-01 23:41:44 +00:00
*
2004-09-21 05:49:12 +00:00
* File for Parser and related classes
2004-09-02 23:28:24 +00:00
*
2007-01-20 15:09:52 +00:00
* @ addtogroup Parser
2004-09-02 23:28:24 +00:00
*/
2004-11-25 22:02:30 +00:00
/**
* Update this version number when the ParserOutput format
* changes in an incompatible way , so the parser cache
* can automatically discard old data .
*/
2007-06-25 16:07:49 +00:00
define ( 'MW_PARSER_VERSION' , '1.6.2' );
2004-11-25 22:02:30 +00:00
2004-10-15 17:39:10 +00:00
define ( 'RLH_FOR_UPDATE' , 1 );
2004-03-20 15:03:26 +00:00
# Allowed values for $mOutputType
2004-08-22 17:24:50 +00:00
define ( 'OT_HTML' , 1 );
define ( 'OT_WIKI' , 2 );
define ( 'OT_MSG' , 3 );
2006-08-14 07:10:31 +00:00
define ( 'OT_PREPROCESS' , 4 );
2004-02-26 13:37:26 +00:00
2006-07-03 11:07:00 +00:00
# Flags for setFunctionHook
define ( 'SFH_NO_HASH' , 1 );
2004-04-28 04:50:35 +00:00
# string parameter for extractTags which will cause it
# to strip HTML comments in addition to regular
# <XML>-style tags. This should not be anything we
# may want to use in wikisyntax
2004-08-07 18:24:12 +00:00
define ( 'STRIP_COMMENTS' , 'HTMLCommentStrip' );
2004-04-28 04:50:35 +00:00
2004-08-07 18:24:12 +00:00
# Constants needed for external link processing
2005-07-08 05:54:16 +00:00
define ( 'HTTP_PROTOCOLS' , 'http:\/\/|https:\/\/' );
2004-08-07 18:24:12 +00:00
# Everything except bracket, space, or control characters
2005-12-24 10:07:26 +00:00
define ( 'EXT_LINK_URL_CLASS' , '[^][<>"\\x00-\\x20\\x7F]' );
2006-06-07 09:18:34 +00:00
# Including space, but excluding newlines
define ( 'EXT_LINK_TEXT_CLASS' , '[^\]\\x0a\\x0d]' );
2004-08-07 18:24:12 +00:00
define ( 'EXT_IMAGE_FNAME_CLASS' , '[A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]' );
define ( 'EXT_IMAGE_EXTENSIONS' , 'gif|png|jpg|jpeg' );
2006-06-07 09:18:34 +00:00
define ( 'EXT_LINK_BRACKETED' , '/\[(\b(' . wfUrlProtocols () . ')' .
EXT_LINK_URL_CLASS . '+) *(' . EXT_LINK_TEXT_CLASS . '*?)\]/S' );
2004-08-14 22:38:46 +00:00
define ( 'EXT_IMAGE_REGEX' ,
2005-07-08 05:54:16 +00:00
'/^(' . HTTP_PROTOCOLS . ')' . # Protocol
2004-08-07 18:24:12 +00:00
'(' . EXT_LINK_URL_CLASS . '+)\\/' . # Hostname and path
'(' . EXT_IMAGE_FNAME_CLASS . '+)\\.((?i)' . EXT_IMAGE_EXTENSIONS . ')$/S' # Filename
);
2004-04-09 07:31:35 +00:00
2006-06-02 20:54:34 +00:00
// State constants for the definition list colon extraction
define ( 'MW_COLON_STATE_TEXT' , 0 );
define ( 'MW_COLON_STATE_TAG' , 1 );
define ( 'MW_COLON_STATE_TAGSTART' , 2 );
define ( 'MW_COLON_STATE_CLOSETAG' , 3 );
define ( 'MW_COLON_STATE_TAGSLASH' , 4 );
define ( 'MW_COLON_STATE_COMMENT' , 5 );
define ( 'MW_COLON_STATE_COMMENTDASH' , 6 );
define ( 'MW_COLON_STATE_COMMENTDASHDASH' , 7 );
2004-09-02 23:28:24 +00:00
/**
2007-04-04 05:22:37 +00:00
* PHP Parser - Processes wiki markup ( which uses a more user - friendly
* syntax , such as " [[link]] " for making links ), and provides a one - way
* transformation of that wiki markup it into XHTML output / markup
* ( which in turn the browser understands , and can display ) .
2004-09-21 05:49:12 +00:00
*
* < pre >
2006-10-18 09:19:28 +00:00
* There are four main entry points into the Parser class :
2004-09-21 05:49:12 +00:00
* parse ()
* produces HTML output
* preSaveTransform () .
* produces altered wiki markup .
* transformMsg ()
* performs brace substitution on MediaWiki messages
2006-10-18 09:19:28 +00:00
* preprocess ()
* removes HTML comments and expands templates
2004-09-21 05:49:12 +00:00
*
* Globals used :
2006-03-01 01:57:53 +00:00
* objects : $wgLang , $wgContLang
2004-09-21 05:49:12 +00:00
*
* NOT $wgArticle , $wgUser or $wgTitle . Keep them away !
*
* settings :
* $wgUseTex * , $wgUseDynamicDates * , $wgInterwikiMagic * ,
* $wgNamespacesWithSubpages , $wgAllowExternalImages * ,
2006-10-17 08:49:27 +00:00
* $wgLocaltimezone , $wgAllowSpecialInclusion * ,
2006-08-10 21:28:49 +00:00
* $wgMaxArticleSize *
2004-09-21 05:49:12 +00:00
*
* * only within ParserOptions
* </ pre >
*
2007-04-20 08:55:14 +00:00
* @ addtogroup Parser
2004-09-02 23:28:24 +00:00
*/
2004-02-26 13:37:26 +00:00
class Parser
{
2007-01-20 21:22:31 +00:00
const VERSION = MW_PARSER_VERSION ;
2004-09-21 05:49:12 +00:00
/** #@+
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2004-06-12 06:15:09 +00:00
# Persistent:
2006-07-03 11:07:00 +00:00
var $mTagHooks , $mFunctionHooks , $mFunctionSynonyms , $mVariables ;
2007-05-01 23:41:44 +00:00
2004-02-29 08:43:29 +00:00
# Cleared with clearState():
2006-11-21 09:53:45 +00:00
var $mOutput , $mAutonumber , $mDTopen , $mStripState ;
2006-07-03 11:07:00 +00:00
var $mIncludeCount , $mArgStack , $mLastSection , $mInPre ;
2006-05-11 22:40:38 +00:00
var $mInterwikiLinkHolders , $mLinkHolders , $mUniqPrefix ;
2006-12-29 10:39:35 +00:00
var $mIncludeSizes , $mDefaultSort ;
2006-05-11 22:40:38 +00:00
var $mTemplates , // cache of already loaded templates, avoids
2006-02-02 13:42:50 +00:00
// multiple SQL queries for the same string
$mTemplatePath ; // stores an unsorted hash of all the templates already loaded
// in this path. Used for loop detection.
2004-02-29 08:43:29 +00:00
2006-02-02 13:42:50 +00:00
# Temporary
# These are variables reset at least once per parse regardless of $clearState
2006-05-11 22:40:38 +00:00
var $mOptions , // ParserOptions object
2005-12-30 09:33:11 +00:00
$mTitle , // Title context, used for self-link rendering and similar things
$mOutputType , // Output type, one of the OT_xxx constants
2006-08-14 07:10:31 +00:00
$ot , // Shortcut alias, see setOutputType()
2006-11-21 09:53:45 +00:00
$mRevisionId , // ID to display in {{REVISIONID}} tags
$mRevisionTimestamp , // The timestamp of the specified revision ID
2007-01-17 19:48:48 +00:00
$mRevIdForTs ; // The revision ID which was used to fetch the timestamp
2005-07-03 07:15:53 +00:00
2004-09-21 05:49:12 +00:00
/**#@-*/
2004-02-29 08:43:29 +00:00
2004-09-21 05:49:12 +00:00
/**
* Constructor
2005-07-03 07:15:53 +00:00
*
2006-04-19 15:46:24 +00:00
* @ public
2004-09-21 05:49:12 +00:00
*/
2004-06-12 06:15:09 +00:00
function Parser () {
$this -> mTagHooks = array ();
2006-04-05 09:40:25 +00:00
$this -> mFunctionHooks = array ();
2006-07-03 11:07:00 +00:00
$this -> mFunctionSynonyms = array ( 0 => array (), 1 => array () );
$this -> mFirstCall = true ;
2004-02-26 13:37:26 +00:00
}
2007-05-01 23:41:44 +00:00
2006-07-03 11:07:00 +00:00
/**
* Do various kinds of initialisation on the first call of the parser
*/
function firstCallInit () {
if ( ! $this -> mFirstCall ) {
return ;
}
2007-05-01 23:41:44 +00:00
2006-07-03 11:07:00 +00:00
wfProfileIn ( __METHOD__ );
global $wgAllowDisplayTitle , $wgAllowSlowParserFunctions ;
2007-05-01 23:41:44 +00:00
2006-07-03 11:07:00 +00:00
$this -> setHook ( 'pre' , array ( $this , 'renderPreTag' ) );
2007-05-01 23:41:44 +00:00
2006-09-30 04:53:36 +00:00
$this -> setFunctionHook ( 'int' , array ( 'CoreParserFunctions' , 'intFunction' ), SFH_NO_HASH );
2006-07-14 15:39:23 +00:00
$this -> setFunctionHook ( 'ns' , array ( 'CoreParserFunctions' , 'ns' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'urlencode' , array ( 'CoreParserFunctions' , 'urlencode' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'lcfirst' , array ( 'CoreParserFunctions' , 'lcfirst' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'ucfirst' , array ( 'CoreParserFunctions' , 'ucfirst' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'lc' , array ( 'CoreParserFunctions' , 'lc' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'uc' , array ( 'CoreParserFunctions' , 'uc' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'localurl' , array ( 'CoreParserFunctions' , 'localurl' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'localurle' , array ( 'CoreParserFunctions' , 'localurle' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'fullurl' , array ( 'CoreParserFunctions' , 'fullurl' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'fullurle' , array ( 'CoreParserFunctions' , 'fullurle' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'formatnum' , array ( 'CoreParserFunctions' , 'formatnum' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'grammar' , array ( 'CoreParserFunctions' , 'grammar' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'plural' , array ( 'CoreParserFunctions' , 'plural' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'numberofpages' , array ( 'CoreParserFunctions' , 'numberofpages' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'numberofusers' , array ( 'CoreParserFunctions' , 'numberofusers' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'numberofarticles' , array ( 'CoreParserFunctions' , 'numberofarticles' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'numberoffiles' , array ( 'CoreParserFunctions' , 'numberoffiles' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'numberofadmins' , array ( 'CoreParserFunctions' , 'numberofadmins' ), SFH_NO_HASH );
2007-04-17 09:23:31 +00:00
$this -> setFunctionHook ( 'numberofedits' , array ( 'CoreParserFunctions' , 'numberofedits' ), SFH_NO_HASH );
2006-07-14 15:39:23 +00:00
$this -> setFunctionHook ( 'language' , array ( 'CoreParserFunctions' , 'language' ), SFH_NO_HASH );
2006-08-18 17:30:35 +00:00
$this -> setFunctionHook ( 'padleft' , array ( 'CoreParserFunctions' , 'padleft' ), SFH_NO_HASH );
$this -> setFunctionHook ( 'padright' , array ( 'CoreParserFunctions' , 'padright' ), SFH_NO_HASH );
2006-08-30 07:51:44 +00:00
$this -> setFunctionHook ( 'anchorencode' , array ( 'CoreParserFunctions' , 'anchorencode' ), SFH_NO_HASH );
2006-10-31 13:25:47 +00:00
$this -> setFunctionHook ( 'special' , array ( 'CoreParserFunctions' , 'special' ) );
2006-12-29 10:39:35 +00:00
$this -> setFunctionHook ( 'defaultsort' , array ( 'CoreParserFunctions' , 'defaultsort' ), SFH_NO_HASH );
2006-07-03 11:07:00 +00:00
if ( $wgAllowDisplayTitle ) {
2006-07-14 15:39:23 +00:00
$this -> setFunctionHook ( 'displaytitle' , array ( 'CoreParserFunctions' , 'displaytitle' ), SFH_NO_HASH );
2006-07-03 11:07:00 +00:00
}
if ( $wgAllowSlowParserFunctions ) {
2006-07-14 15:39:23 +00:00
$this -> setFunctionHook ( 'pagesinnamespace' , array ( 'CoreParserFunctions' , 'pagesinnamespace' ), SFH_NO_HASH );
2006-07-03 11:07:00 +00:00
}
2006-10-17 08:49:27 +00:00
2006-07-03 11:07:00 +00:00
$this -> initialiseVariables ();
$this -> mFirstCall = false ;
wfProfileOut ( __METHOD__ );
2006-10-17 08:49:27 +00:00
}
2006-07-03 11:07:00 +00:00
2004-09-21 05:49:12 +00:00
/**
* Clear Parser state
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2004-06-12 06:15:09 +00:00
function clearState () {
2006-08-06 14:01:47 +00:00
wfProfileIn ( __METHOD__ );
2006-07-03 11:07:00 +00:00
if ( $this -> mFirstCall ) {
$this -> firstCallInit ();
}
2004-02-26 13:37:26 +00:00
$this -> mOutput = new ParserOutput ;
$this -> mAutonumber = 0 ;
2005-01-31 22:59:55 +00:00
$this -> mLastSection = '' ;
2004-02-26 13:37:26 +00:00
$this -> mDTopen = false ;
2004-03-20 15:03:26 +00:00
$this -> mIncludeCount = array ();
2006-11-21 09:53:45 +00:00
$this -> mStripState = new StripState ;
2004-04-11 16:46:06 +00:00
$this -> mArgStack = array ();
2004-06-01 22:19:22 +00:00
$this -> mInPre = false ;
2005-06-03 05:46:24 +00:00
$this -> mInterwikiLinkHolders = array (
'texts' => array (),
'titles' => array ()
);
2005-04-27 07:48:14 +00:00
$this -> mLinkHolders = array (
'namespaces' => array (),
'dbkeys' => array (),
'queries' => array (),
'texts' => array (),
'titles' => array ()
);
2006-11-21 09:53:45 +00:00
$this -> mRevisionTimestamp = $this -> mRevisionId = null ;
2007-01-17 19:48:48 +00:00
2006-05-26 12:11:54 +00:00
/**
* Prefix for temporary replacement strings for the multipass parser .
* \x07 should never appear in input as it ' s disallowed in XML .
* Using it at the front also gives us a little extra robustness
* since it shouldn ' t match when butted up against identifier - like
* string constructs .
*/
$this -> mUniqPrefix = " \x07 UNIQ " . Parser :: getRandomString ();
2005-12-24 23:05:18 +00:00
2006-02-02 13:42:50 +00:00
# Clear these on every parse, bug 4549
$this -> mTemplates = array ();
$this -> mTemplatePath = array ();
2006-05-23 07:19:01 +00:00
$this -> mShowToc = true ;
$this -> mForceTocPosition = false ;
2006-08-10 21:28:49 +00:00
$this -> mIncludeSizes = array (
'pre-expand' => 0 ,
'post-expand' => 0 ,
'arg' => 0
);
2006-12-29 10:39:35 +00:00
$this -> mDefaultSort = false ;
2006-07-02 17:43:32 +00:00
2005-12-24 23:05:18 +00:00
wfRunHooks ( 'ParserClearState' , array ( & $this ) );
2006-08-06 14:01:47 +00:00
wfProfileOut ( __METHOD__ );
2004-02-26 13:37:26 +00:00
}
2004-04-12 23:59:37 +00:00
2006-08-14 07:10:31 +00:00
function setOutputType ( $ot ) {
$this -> mOutputType = $ot ;
// Shortcut alias
$this -> ot = array (
'html' => $ot == OT_HTML ,
'wiki' => $ot == OT_WIKI ,
'msg' => $ot == OT_MSG ,
'pre' => $ot == OT_PREPROCESS ,
);
}
2006-01-24 18:36:45 +00:00
/**
* Accessor for mUniqPrefix .
*
2006-04-19 15:46:24 +00:00
* @ public
2006-01-24 18:36:45 +00:00
*/
2006-08-06 14:01:47 +00:00
function uniqPrefix () {
2006-01-24 18:36:45 +00:00
return $this -> mUniqPrefix ;
}
2006-01-07 13:09:30 +00:00
/**
2005-12-30 09:33:11 +00:00
* Convert wikitext to HTML
* Do not call this function recursively .
2004-09-21 05:49:12 +00:00
*
2005-01-31 22:59:55 +00:00
* @ param string $text Text we want to parse
* @ param Title & $title A title object
* @ param array $options
* @ param boolean $linestart
* @ param boolean $clearState
2005-11-27 06:04:41 +00:00
* @ param int $revid number to pass in {{ REVISIONID }}
2004-09-21 05:49:12 +00:00
* @ return ParserOutput a ParserOutput
*/
2007-01-15 16:03:27 +00:00
public function parse ( $text , & $title , $options , $linestart = true , $clearState = true , $revid = null ) {
2005-12-30 09:33:11 +00:00
/**
* First pass -- just handle < nowiki > sections , pass the rest off
* to internalParse () which does all the real work .
*/
2006-01-07 13:31:29 +00:00
2006-01-23 20:57:57 +00:00
global $wgUseTidy , $wgAlwaysUseTidy , $wgContLang ;
2006-10-02 17:04:13 +00:00
$fname = 'Parser::parse-' . wfGetCaller ();
2006-11-12 10:47:10 +00:00
wfProfileIn ( __METHOD__ );
2004-02-26 13:37:26 +00:00
wfProfileIn ( $fname );
if ( $clearState ) {
$this -> clearState ();
}
2004-04-12 23:59:37 +00:00
2004-02-29 08:43:29 +00:00
$this -> mOptions = $options ;
$this -> mTitle =& $title ;
2006-07-10 18:25:56 +00:00
$oldRevisionId = $this -> mRevisionId ;
2006-11-21 09:53:45 +00:00
$oldRevisionTimestamp = $this -> mRevisionTimestamp ;
2006-07-10 18:25:56 +00:00
if ( $revid !== null ) {
$this -> mRevisionId = $revid ;
2006-11-21 09:53:45 +00:00
$this -> mRevisionTimestamp = null ;
2006-07-10 18:25:56 +00:00
}
2006-08-14 07:10:31 +00:00
$this -> setOutputType ( OT_HTML );
2006-11-21 09:53:45 +00:00
wfRunHooks ( 'ParserBeforeStrip' , array ( & $this , & $text , & $this -> mStripState ) );
$text = $this -> strip ( $text , $this -> mStripState );
wfRunHooks ( 'ParserAfterStrip' , array ( & $this , & $text , & $this -> mStripState ) );
2005-04-21 06:30:48 +00:00
$text = $this -> internalParse ( $text );
2006-11-21 09:53:45 +00:00
$text = $this -> mStripState -> unstripGeneral ( $text );
2005-07-03 07:15:53 +00:00
2004-04-15 14:59:46 +00:00
# Clean up special characters, only run once, next-to-last before doBlockLevels
2005-04-21 01:33:32 +00:00
$fixtags = array (
# french spaces, last one Guillemet-left
# only if there is something before the space
2007-06-26 08:56:30 +00:00
'/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1 \\2' ,
2005-04-21 01:33:32 +00:00
# french spaces, Guillemet-right
'/(\\302\\253) /' => '\\1 ' ,
);
$text = preg_replace ( array_keys ( $fixtags ), array_values ( $fixtags ), $text );
2005-07-03 07:15:53 +00:00
2004-04-15 14:59:46 +00:00
# only once and last
$text = $this -> doBlockLevels ( $text , $linestart );
2004-10-08 04:27:07 +00:00
2004-10-15 17:39:10 +00:00
$this -> replaceLinkHolders ( $text );
2005-04-26 20:50:16 +00:00
2006-03-01 01:57:53 +00:00
# the position of the parserConvert() call should not be changed. it
2006-02-28 05:18:36 +00:00
# assumes that the links are all replaced and the only thing left
2005-04-26 20:50:16 +00:00
# is the <nowiki> mark.
2006-03-01 01:57:53 +00:00
# Side-effects: this calls $this->mOutput->setTitleText()
$text = $wgContLang -> parserConvert ( $text , $this );
2005-04-26 20:50:16 +00:00
2006-11-21 09:53:45 +00:00
$text = $this -> mStripState -> unstripNoWiki ( $text );
2005-07-02 09:15:51 +00:00
wfRunHooks ( 'ParserBeforeTidy' , array ( & $this , & $text ) );
2005-07-03 07:15:53 +00:00
2005-04-21 11:40:58 +00:00
$text = Sanitizer :: normalizeCharReferences ( $text );
2006-01-07 13:31:29 +00:00
2006-01-23 20:57:57 +00:00
if (( $wgUseTidy and $this -> mOptions -> mTidy ) or $wgAlwaysUseTidy ) {
2007-04-23 18:52:51 +00:00
$text = Parser :: tidy ( $text );
2006-03-24 16:36:29 +00:00
} else {
# attempt to sanitize at least some nesting problems
# (bug #2702 and quite a few others)
2006-07-11 17:40:11 +00:00
$tidyregs = array (
# ''Something [http://www.cool.com cool''] -->
2006-03-24 16:36:29 +00:00
# <i>Something</i><a href="http://www.cool.com"..><i>cool></i></a>
'/(<([bi])>)(<([bi])>)?([^<]*)(<\/?a[^<]*>)([^<]*)(<\/\\4>)?(<\/\\2>)/' =>
'\\1\\3\\5\\8\\9\\6\\1\\3\\7\\8\\9' ,
# fix up an anchor inside another anchor, only
# at least for a single single nested link (bug 3695)
'/(<a[^>]+>)([^<]*)(<a[^>]+>[^<]*)<\/a>(.*)<\/a>/' =>
'\\1\\2</a>\\3</a>\\1\\4</a>' ,
# fix div inside inline elements- doBlockLevels won't wrap a line which
# contains a div, so fix it up here; replace
# div with escaped text
'/(<([aib]) [^>]+>)([^<]*)(<div([^>]*)>)(.*)(<\/div>)([^<]*)(<\/\\2>)/' =>
'\\1\\3<div\\5>\\6</div>\\8\\9' ,
# remove empty italic or bold tag pairs, some
# introduced by rules above
2006-07-11 17:40:11 +00:00
'/<([bi])><\/\\1>/' => '' ,
2006-03-24 16:36:29 +00:00
);
2006-07-11 17:40:11 +00:00
$text = preg_replace (
2006-03-24 16:36:29 +00:00
array_keys ( $tidyregs ),
array_values ( $tidyregs ),
$text );
2004-10-15 17:39:10 +00:00
}
2005-07-02 09:15:51 +00:00
wfRunHooks ( 'ParserAfterTidy' , array ( & $this , & $text ) );
2006-08-10 21:28:49 +00:00
# Information on include size limits, for the benefit of users who try to skirt them
if ( max ( $this -> mIncludeSizes ) > 1000 ) {
$max = $this -> mOptions -> getMaxIncludeSize ();
$text .= " <!-- \n " .
" Pre-expand include size: { $this -> mIncludeSizes [ 'pre-expand' ] } bytes \n " .
" Post-expand include size: { $this -> mIncludeSizes [ 'post-expand' ] } bytes \n " .
" Template argument size: { $this -> mIncludeSizes [ 'arg' ] } bytes \n " .
" Maximum: $max bytes \n " .
" --> \n " ;
}
2004-03-06 01:49:16 +00:00
$this -> mOutput -> setText ( $text );
2006-07-10 18:25:56 +00:00
$this -> mRevisionId = $oldRevisionId ;
2006-11-21 09:53:45 +00:00
$this -> mRevisionTimestamp = $oldRevisionTimestamp ;
2004-03-06 01:49:16 +00:00
wfProfileOut ( $fname );
2006-11-12 10:47:10 +00:00
wfProfileOut ( __METHOD__ );
2005-12-30 09:33:11 +00:00
2004-03-06 01:49:16 +00:00
return $this -> mOutput ;
}
2006-08-06 14:01:47 +00:00
/**
* Recursive parser entry point that can be called from an extension tag
* hook .
*/
function recursiveTagParse ( $text ) {
wfProfileIn ( __METHOD__ );
2006-11-23 08:25:56 +00:00
wfRunHooks ( 'ParserBeforeStrip' , array ( & $this , & $text , & $this -> mStripState ) );
$text = $this -> strip ( $text , $this -> mStripState );
wfRunHooks ( 'ParserAfterStrip' , array ( & $this , & $text , & $this -> mStripState ) );
2006-08-06 14:01:47 +00:00
$text = $this -> internalParse ( $text );
wfProfileOut ( __METHOD__ );
return $text ;
}
2006-08-14 07:10:31 +00:00
/**
2006-08-15 02:24:59 +00:00
* Expand templates and variables in the text , producing valid , static wikitext .
* Also removes comments .
2006-08-14 07:10:31 +00:00
*/
2007-05-31 16:01:26 +00:00
function preprocess ( $text , $title , $options , $revid = null ) {
2006-08-14 07:10:31 +00:00
wfProfileIn ( __METHOD__ );
$this -> clearState ();
$this -> setOutputType ( OT_PREPROCESS );
$this -> mOptions = $options ;
$this -> mTitle = $title ;
2007-05-31 16:01:26 +00:00
if ( $revid !== null ) {
$this -> mRevisionId = $revid ;
}
2006-11-21 09:53:45 +00:00
wfRunHooks ( 'ParserBeforeStrip' , array ( & $this , & $text , & $this -> mStripState ) );
$text = $this -> strip ( $text , $this -> mStripState );
wfRunHooks ( 'ParserAfterStrip' , array ( & $this , & $text , & $this -> mStripState ) );
2006-08-15 02:40:20 +00:00
if ( $this -> mOptions -> getRemoveComments () ) {
2006-08-15 02:24:59 +00:00
$text = Sanitizer :: removeHTMLcomments ( $text );
}
2006-08-14 07:10:31 +00:00
$text = $this -> replaceVariables ( $text );
2006-11-21 09:53:45 +00:00
$text = $this -> mStripState -> unstripBoth ( $text );
2006-08-14 07:10:31 +00:00
wfProfileOut ( __METHOD__ );
return $text ;
}
2004-09-21 05:49:12 +00:00
/**
* Get a random string
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
* @ static
*/
function getRandomString () {
2004-03-06 01:49:16 +00:00
return dechex ( mt_rand ( 0 , 0x7fffffff )) . dechex ( mt_rand ( 0 , 0x7fffffff ));
}
2004-03-26 17:14:23 +00:00
2006-02-28 05:18:36 +00:00
function & getTitle () { return $this -> mTitle ; }
function getOptions () { return $this -> mOptions ; }
2006-07-03 11:07:00 +00:00
function getFunctionLang () {
global $wgLang , $wgContLang ;
return $this -> mOptions -> getInterfaceMessage () ? $wgLang : $wgContLang ;
}
2005-07-03 07:15:53 +00:00
/**
2006-06-01 19:38:14 +00:00
* Replaces all occurrences of HTML - style comments and the given tags
* in the text with a random marker and returns teh next text . The output
* parameter $matches will be an associative array filled with data in
* the form :
* 'UNIQ-xxxxx' => array (
* 'element' ,
* 'tag content' ,
* array ( 'param' => 'x' ),
* '<element param="x">tag content</element>' ) )
2005-07-03 07:15:53 +00:00
*
2006-06-01 19:38:14 +00:00
* @ param $elements list of element names . Comments are always extracted .
* @ param $text Source text string .
* @ param $uniq_prefix
2004-09-21 05:49:12 +00:00
*
2007-05-19 20:07:01 +00:00
* @ public
2004-09-21 05:49:12 +00:00
* @ static
*/
2006-06-01 06:16:55 +00:00
function extractTagsAndParams ( $elements , $text , & $matches , $uniq_prefix = '' ){
2006-08-06 14:01:47 +00:00
static $n = 1 ;
2004-06-08 18:11:28 +00:00
$stripped = '' ;
2006-06-01 06:16:55 +00:00
$matches = array ();
2005-07-03 07:15:53 +00:00
2006-06-01 19:38:14 +00:00
$taglist = implode ( '|' , $elements );
$start = " /<( $taglist )( \\ s+[^>]*?| \\ s*?)( \ /?>)|<(!--)/i " ;
2004-03-26 17:14:23 +00:00
2004-06-08 18:11:28 +00:00
while ( '' != $text ) {
2005-06-03 08:12:48 +00:00
$p = preg_split ( $start , $text , 2 , PREG_SPLIT_DELIM_CAPTURE );
2004-03-26 17:14:23 +00:00
$stripped .= $p [ 0 ];
2006-06-01 19:38:14 +00:00
if ( count ( $p ) < 5 ) {
2005-06-03 08:12:48 +00:00
break ;
}
2006-06-01 19:38:14 +00:00
if ( count ( $p ) > 5 ) {
// comment
$element = $p [ 4 ];
$attributes = '' ;
$close = '' ;
$inside = $p [ 5 ];
2006-04-08 04:40:09 +00:00
} else {
2006-06-01 19:38:14 +00:00
// tag
$element = $p [ 1 ];
$attributes = $p [ 2 ];
$close = $p [ 3 ];
$inside = $p [ 4 ];
2006-04-08 04:40:09 +00:00
}
2005-07-03 07:15:53 +00:00
2007-07-11 20:44:33 +00:00
$marker = " $uniq_prefix - $element - " . sprintf ( '%08X' , $n ++ ) . " -QINU \x07 " ;
2005-06-03 08:12:48 +00:00
$stripped .= $marker ;
2005-07-03 07:15:53 +00:00
2006-06-01 19:38:14 +00:00
if ( $close === '/>' ) {
2005-11-13 04:47:03 +00:00
// Empty element tag, <tag />
2006-06-01 06:16:55 +00:00
$content = null ;
2005-11-13 04:47:03 +00:00
$text = $inside ;
2006-06-01 19:38:14 +00:00
$tail = null ;
2004-03-26 17:14:23 +00:00
} else {
2006-06-01 19:38:14 +00:00
if ( $element == '!--' ) {
$end = '/(-->)/' ;
2006-06-01 06:16:55 +00:00
} else {
2006-06-01 19:38:14 +00:00
$end = " /(< \\ / $element\\s *>)/i " ;
2006-06-01 06:16:55 +00:00
}
2006-06-01 19:38:14 +00:00
$q = preg_split ( $end , $inside , 2 , PREG_SPLIT_DELIM_CAPTURE );
2006-06-01 06:16:55 +00:00
$content = $q [ 0 ];
2006-06-01 19:38:14 +00:00
if ( count ( $q ) < 3 ) {
2005-11-13 04:47:03 +00:00
# No end tag -- let it run out to the end of the text.
2006-06-01 19:38:14 +00:00
$tail = '' ;
2006-06-01 08:24:22 +00:00
$text = '' ;
2005-11-13 04:47:03 +00:00
} else {
2006-06-01 19:38:14 +00:00
$tail = $q [ 1 ];
$text = $q [ 2 ];
2005-11-13 04:47:03 +00:00
}
2004-03-26 17:14:23 +00:00
}
2006-10-17 08:49:27 +00:00
2006-06-01 06:16:55 +00:00
$matches [ $marker ] = array ( $element ,
$content ,
Sanitizer :: decodeTagAttributes ( $attributes ),
2006-06-01 19:38:14 +00:00
" < $element $attributes $close $content $tail " );
2004-03-26 17:14:23 +00:00
}
return $stripped ;
2004-04-12 23:59:37 +00:00
}
2004-03-26 17:14:23 +00:00
2004-09-21 05:49:12 +00:00
/**
* Strips and renders nowiki , pre , math , hiero
* If $render is set , performs necessary rendering operations on plugins
* Returns the text , and fills an array with data needed in unstrip ()
2006-11-21 09:53:45 +00:00
*
* @ param StripState $state
2004-09-21 05:49:12 +00:00
*
* @ param bool $stripcomments when set , HTML comments <!-- like this -->
* will be stripped in addition to other tags . This is important
* for section editing , where these comments cause confusion when
* counting the sections in the wikisource
2006-10-17 08:49:27 +00:00
*
2006-06-13 10:11:12 +00:00
* @ param array dontstrip contains tags which should not be stripped ;
* used to prevent stipping of < gallery > when saving ( fixes bug 2700 )
2004-09-21 05:49:12 +00:00
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2006-11-21 09:53:45 +00:00
function strip ( $text , $state , $stripcomments = false , $dontstrip = array () ) {
2006-12-11 23:33:27 +00:00
global $wgContLang ;
2006-08-06 14:01:47 +00:00
wfProfileIn ( __METHOD__ );
2004-03-20 15:03:26 +00:00
$render = ( $this -> mOutputType == OT_HTML );
2004-07-12 19:49:20 +00:00
2005-12-21 14:02:20 +00:00
$uniq_prefix = $this -> mUniqPrefix ;
2006-11-21 09:53:45 +00:00
$commentState = new ReplacementArray ;
2007-01-04 21:56:37 +00:00
$nowikiItems = array ();
$generalItems = array ();
2006-10-17 08:49:27 +00:00
2006-06-01 06:16:55 +00:00
$elements = array_merge (
2006-06-06 22:56:38 +00:00
array ( 'nowiki' , 'gallery' ),
2006-06-01 06:16:55 +00:00
array_keys ( $this -> mTagHooks ) );
2005-06-25 06:24:46 +00:00
global $wgRawHtml ;
if ( $wgRawHtml ) {
2006-06-01 06:16:55 +00:00
$elements [] = 'html' ;
2004-07-24 22:59:44 +00:00
}
2005-08-29 23:34:37 +00:00
if ( $this -> mOptions -> getUseTeX () ) {
2006-06-01 06:16:55 +00:00
$elements [] = 'math' ;
2004-02-26 13:37:26 +00:00
}
2006-10-17 08:49:27 +00:00
2006-06-13 10:11:12 +00:00
# Removing $dontstrip tags from $elements list (currently only 'gallery', fixing bug 2700)
foreach ( $elements AS $k => $v ) {
if ( ! in_array ( $v , $dontstrip ) ) continue ;
unset ( $elements [ $k ] );
}
2006-10-17 08:49:27 +00:00
2006-06-01 06:16:55 +00:00
$matches = array ();
$text = Parser :: extractTagsAndParams ( $elements , $text , $matches , $uniq_prefix );
2006-06-13 10:11:12 +00:00
2006-06-01 06:16:55 +00:00
foreach ( $matches as $marker => $data ) {
list ( $element , $content , $params , $tag ) = $data ;
if ( $render ) {
2006-06-01 08:31:12 +00:00
$tagName = strtolower ( $element );
2006-08-06 14:01:47 +00:00
wfProfileIn ( __METHOD__ . " -render- $tagName " );
2006-06-01 08:31:12 +00:00
switch ( $tagName ) {
2006-06-01 19:38:14 +00:00
case '!--' :
// Comment
if ( substr ( $tag , - 3 ) == '-->' ) {
$output = $tag ;
} else {
// Unclosed comment in input.
// Close it so later stripping can remove it
$output = " $tag --> " ;
}
break ;
2006-06-01 06:16:55 +00:00
case 'html' :
if ( $wgRawHtml ) {
$output = $content ;
break ;
}
// Shouldn't happen otherwise. :)
case 'nowiki' :
2006-11-21 09:53:45 +00:00
$output = Xml :: escapeTagsOnly ( $content );
2006-06-01 06:16:55 +00:00
break ;
case 'math' :
2007-07-13 17:25:06 +00:00
$output = $wgContLang -> armourMath (
MathRenderer :: renderMath ( $content , $params ) );
2006-06-01 06:16:55 +00:00
break ;
case 'gallery' :
2006-06-24 00:12:34 +00:00
$output = $this -> renderImageGallery ( $content , $params );
2006-06-01 06:16:55 +00:00
break ;
default :
if ( isset ( $this -> mTagHooks [ $tagName ] ) ) {
$output = call_user_func_array ( $this -> mTagHooks [ $tagName ],
array ( $content , $params , $this ) );
2005-11-13 04:47:03 +00:00
} else {
2006-06-07 06:40:24 +00:00
throw new MWException ( " Invalid call hook $element " );
2005-11-13 04:47:03 +00:00
}
2004-06-09 12:15:42 +00:00
}
2006-08-06 14:01:47 +00:00
wfProfileOut ( __METHOD__ . " -render- $tagName " );
2006-06-01 06:16:55 +00:00
} else {
// Just stripping tags; keep the source
2006-06-01 19:38:14 +00:00
$output = $tag ;
}
2006-08-06 14:01:47 +00:00
2007-01-04 21:56:37 +00:00
// Unstrip the output, to support recursive strip() calls
2006-11-21 09:53:45 +00:00
$output = $state -> unstripBoth ( $output );
2006-08-06 14:01:47 +00:00
2006-06-01 19:38:14 +00:00
if ( ! $stripcomments && $element == '!--' ) {
2006-11-21 09:53:45 +00:00
$commentState -> setPair ( $marker , $output );
2006-08-06 14:01:47 +00:00
} elseif ( $element == 'html' || $element == 'nowiki' ) {
2007-01-04 21:56:37 +00:00
$nowikiItems [ $marker ] = $output ;
2006-06-01 19:38:14 +00:00
} else {
2007-01-04 21:56:37 +00:00
$generalItems [ $marker ] = $output ;
2004-06-09 12:15:42 +00:00
}
}
2007-01-04 21:56:37 +00:00
# Add the new items to the state
# We do this after the loop instead of during it to avoid slowing
# down the recursive unstrip
$state -> nowiki -> mergeArray ( $nowikiItems );
$state -> general -> mergeArray ( $generalItems );
2006-07-11 17:40:11 +00:00
2006-03-26 02:38:01 +00:00
# Unstrip comments unless explicitly told otherwise.
# (The comments are always stripped prior to this point, so as to
# not invoke any extension tags / parser hooks contained within
# a comment.)
2006-06-01 19:38:14 +00:00
if ( ! $stripcomments ) {
2006-06-01 06:16:55 +00:00
// Put them all back and forget them
2006-11-21 09:53:45 +00:00
$text = $commentState -> replace ( $text );
2004-04-11 16:46:06 +00:00
}
2006-05-29 23:02:21 +00:00
2006-08-06 14:01:47 +00:00
wfProfileOut ( __METHOD__ );
2004-03-26 17:14:23 +00:00
return $text ;
2004-03-06 01:49:16 +00:00
}
2004-02-26 13:37:26 +00:00
2004-09-21 05:49:12 +00:00
/**
2006-06-01 06:16:55 +00:00
* Restores pre , math , and other extensions removed by strip ()
2004-09-21 05:49:12 +00:00
*
* always call unstripNoWiki () after this one
2006-04-19 15:46:24 +00:00
* @ private
2006-11-21 09:53:45 +00:00
* @ deprecated use $this -> mStripState -> unstrip ()
2004-09-21 05:49:12 +00:00
*/
2006-10-17 08:49:27 +00:00
function unstrip ( $text , $state ) {
2006-11-21 09:53:45 +00:00
return $state -> unstripGeneral ( $text );
2004-02-26 13:37:26 +00:00
}
2004-09-21 05:49:12 +00:00
/**
2006-06-01 06:16:55 +00:00
* Always call this after unstrip () to preserve the order
2004-09-21 05:49:12 +00:00
*
2006-04-19 15:46:24 +00:00
* @ private
2006-11-21 09:53:45 +00:00
* @ deprecated use $this -> mStripState -> unstrip ()
2004-09-21 05:49:12 +00:00
*/
2006-10-17 08:49:27 +00:00
function unstripNoWiki ( $text , $state ) {
2006-11-21 09:53:45 +00:00
return $state -> unstripNoWiki ( $text );
}
2006-10-17 08:49:27 +00:00
2006-11-21 09:53:45 +00:00
/**
* @ deprecated use $this -> mStripState -> unstripBoth ()
*/
function unstripForHTML ( $text ) {
return $this -> mStripState -> unstripBoth ( $text );
2004-06-02 12:29:15 +00:00
}
2004-04-12 23:59:37 +00:00
2004-09-21 05:49:12 +00:00
/**
* Add an item to the strip state
* Returns the unique tag which must be inserted into the stripped text
* The tag will be replaced with the original text in unstrip ()
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2004-06-12 06:15:09 +00:00
function insertStripItem ( $text , & $state ) {
2005-12-21 14:02:20 +00:00
$rnd = $this -> mUniqPrefix . '-item' . Parser :: getRandomString ();
2006-11-21 09:53:45 +00:00
$state -> general -> setPair ( $rnd , $text );
2004-04-09 15:29:33 +00:00
return $rnd ;
}
2004-04-12 23:59:37 +00:00
2007-04-23 18:52:51 +00:00
/**
* Interface with html tidy , used if $wgUseTidy = true .
* If tidy isn ' t able to correct the markup , the original will be
* returned in all its glory with a warning comment appended .
*
* Either the external tidy program or the in - process tidy extension
* will be used depending on availability . Override the default
* $wgTidyInternal setting to disable the internal if it ' s not working .
*
* @ param string $text Hideous HTML input
* @ return string Corrected HTML output
* @ public
* @ static
*/
function tidy ( $text ) {
global $wgTidyInternal ;
$wrappedtext = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' .
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>' .
'<head><title>test</title></head><body>' . $text . '</body></html>' ;
if ( $wgTidyInternal ) {
$correctedtext = Parser :: internalTidy ( $wrappedtext );
} else {
$correctedtext = Parser :: externalTidy ( $wrappedtext );
}
if ( is_null ( $correctedtext ) ) {
wfDebug ( " Tidy error detected! \n " );
return $text . " \n <!-- Tidy found serious XHTML errors --> \n " ;
}
return $correctedtext ;
}
/**
* Spawn an external HTML tidy process and get corrected markup back from it .
*
* @ private
* @ static
*/
function externalTidy ( $text ) {
global $wgTidyConf , $wgTidyBin , $wgTidyOpts ;
$fname = 'Parser::externalTidy' ;
wfProfileIn ( $fname );
$cleansource = '' ;
$opts = ' -utf8' ;
$descriptorspec = array (
0 => array ( 'pipe' , 'r' ),
1 => array ( 'pipe' , 'w' ),
2 => array ( 'file' , '/dev/null' , 'a' ) // FIXME: this line in UNIX-specific, it generates a warning on Windows, because /dev/null is not a valid Windows file.
);
$pipes = array ();
$process = proc_open ( " $wgTidyBin -config $wgTidyConf $wgTidyOpts $opts " , $descriptorspec , $pipes );
if ( is_resource ( $process )) {
// Theoretically, this style of communication could cause a deadlock
// here. If the stdout buffer fills up, then writes to stdin could
// block. This doesn't appear to happen with tidy, because tidy only
// writes to stdout after it's finished reading from stdin. Search
// for tidyParseStdin and tidySaveStdout in console/tidy.c
fwrite ( $pipes [ 0 ], $text );
fclose ( $pipes [ 0 ]);
while ( ! feof ( $pipes [ 1 ])) {
$cleansource .= fgets ( $pipes [ 1 ], 1024 );
}
fclose ( $pipes [ 1 ]);
proc_close ( $process );
}
wfProfileOut ( $fname );
if ( $cleansource == '' && $text != '' ) {
// Some kind of error happened, so we couldn't get the corrected text.
// Just give up; we'll use the source text and append a warning.
return null ;
} else {
return $cleansource ;
}
}
/**
* Use the HTML tidy PECL extension to use the tidy library in - process ,
* saving the overhead of spawning a new process . Currently written to
* the PHP 4.3 . x version of the extension , may not work on PHP 5.
*
* 'pear install tidy' should be able to compile the extension module .
*
* @ private
* @ static
*/
function internalTidy ( $text ) {
global $wgTidyConf ;
$fname = 'Parser::internalTidy' ;
wfProfileIn ( $fname );
tidy_load_config ( $wgTidyConf );
tidy_set_encoding ( 'utf8' );
tidy_parse_string ( $text );
tidy_clean_repair ();
if ( tidy_get_status () == 2 ) {
// 2 is magic number for fatal error
// http://www.php.net/manual/en/function.tidy-get-status.php
$cleansource = null ;
} else {
$cleansource = tidy_get_output ();
}
wfProfileOut ( $fname );
return $cleansource ;
}
2004-09-21 05:49:12 +00:00
/**
* parse the wiki syntax used to render tables
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2006-12-04 20:08:53 +00:00
function doTableStuff ( $text ) {
2004-07-10 03:09:26 +00:00
$fname = 'Parser::doTableStuff' ;
wfProfileIn ( $fname );
2004-07-12 19:49:20 +00:00
2006-12-04 20:08:53 +00:00
$lines = explode ( " \n " , $text );
$td_history = array (); // Is currently a td tag open?
$last_tag_history = array (); // Save history of last lag activated (td, th or caption)
$tr_history = array (); // Is currently a tr tag open?
$tr_attributes = array (); // history of tr attributes
$has_opened_tr = array (); // Did this table open a <tr> element?
$indent_level = 0 ; // indent level of the table
foreach ( $lines as $key => $line )
2004-07-12 19:49:20 +00:00
{
2006-12-04 20:08:53 +00:00
$line = trim ( $line );
if ( $line == '' ) { // empty line, go to next line
continue ;
}
$first_character = $line { 0 };
2006-10-17 08:49:27 +00:00
$matches = array ();
2006-12-04 20:08:53 +00:00
if ( preg_match ( '/^(:*)\{\|(.*)$/' , $line , $matches ) ) {
// First check if we are starting a new table
2004-08-04 16:37:48 +00:00
$indent_level = strlen ( $matches [ 1 ] );
2006-01-07 13:31:29 +00:00
2006-11-21 09:53:45 +00:00
$attributes = $this -> mStripState -> unstripBoth ( $matches [ 2 ] );
2006-12-04 20:08:53 +00:00
$attributes = Sanitizer :: fixTagAttributes ( $attributes , 'table' );
$lines [ $key ] = str_repeat ( '<dl><dd>' , $indent_level ) . " <table { $attributes } > " ;
array_push ( $td_history , false );
array_push ( $last_tag_history , '' );
array_push ( $tr_history , false );
array_push ( $tr_attributes , '' );
array_push ( $has_opened_tr , false );
} else if ( count ( $td_history ) == 0 ) {
// Don't do any of the following
continue ;
} else if ( substr ( $line , 0 , 2 ) == '|}' ) {
// We are ending a table
$line = '</table>' . substr ( $line , 2 );
$last_tag = array_pop ( $last_tag_history );
if ( ! array_pop ( $has_opened_tr ) ) {
$line = " <tr><td></td></tr> { $line } " ;
}
2005-08-29 23:34:37 +00:00
2006-12-04 20:08:53 +00:00
if ( array_pop ( $tr_history ) ) {
$line = " </tr> { $line } " ;
}
if ( array_pop ( $td_history ) ) {
$line = " </ { $last_tag } > { $line } " ;
}
array_pop ( $tr_attributes );
$lines [ $key ] = $line . str_repeat ( '</dd></dl>' , $indent_level );
} else if ( substr ( $line , 0 , 2 ) == '|-' ) {
// Now we have a table row
$line = preg_replace ( '#^\|-+#' , '' , $line );
// Whats after the tag is now only attributes
$attributes = $this -> mStripState -> unstripBoth ( $line );
$attributes = Sanitizer :: fixTagAttributes ( $attributes , 'tr' );
array_pop ( $tr_attributes );
array_push ( $tr_attributes , $attributes );
$line = '' ;
$last_tag = array_pop ( $last_tag_history );
2006-02-06 15:47:14 +00:00
array_pop ( $has_opened_tr );
2006-12-04 20:08:53 +00:00
array_push ( $has_opened_tr , true );
if ( array_pop ( $tr_history ) ) {
$line = '</tr>' ;
}
if ( array_pop ( $td_history ) ) {
$line = " </ { $last_tag } > { $line } " ;
}
$lines [ $key ] = $line ;
array_push ( $tr_history , false );
array_push ( $td_history , false );
array_push ( $last_tag_history , '' );
2004-07-12 19:49:20 +00:00
}
2007-01-17 19:48:48 +00:00
else if ( $first_character == '|' || $first_character == '!' || substr ( $line , 0 , 2 ) == '|+' ) {
2006-12-04 20:08:53 +00:00
// This might be cell elements, td, th or captions
if ( substr ( $line , 0 , 2 ) == '|+' ) {
$first_character = '+' ;
$line = substr ( $line , 1 );
}
$line = substr ( $line , 1 );
if ( $first_character == '!' ) {
$line = str_replace ( '!!' , '||' , $line );
2004-02-29 08:43:29 +00:00
}
2006-07-11 17:40:11 +00:00
2006-04-03 08:36:17 +00:00
// Split up multiple cells on the same line.
2006-12-04 20:08:53 +00:00
// FIXME : This can result in improper nesting of tags processed
2006-04-03 08:36:17 +00:00
// by earlier parser steps, but should avoid splitting up eg
// attribute values containing literal "||".
2006-12-04 20:08:53 +00:00
$cells = StringUtils :: explodeMarkup ( '||' , $line );
2006-07-11 17:40:11 +00:00
2006-12-04 20:08:53 +00:00
$lines [ $key ] = '' ;
2004-10-01 21:20:47 +00:00
2006-12-04 20:08:53 +00:00
// Loop through each table cell
foreach ( $cells as $cell )
2004-02-29 08:43:29 +00:00
{
2006-12-04 20:08:53 +00:00
$previous = '' ;
if ( $first_character != '+' )
2004-02-29 08:43:29 +00:00
{
2006-12-04 20:08:53 +00:00
$tr_after = array_pop ( $tr_attributes );
if ( ! array_pop ( $tr_history ) ) {
$previous = " <tr { $tr_after } > \n " ;
}
array_push ( $tr_history , true );
array_push ( $tr_attributes , '' );
2006-02-06 15:47:14 +00:00
array_pop ( $has_opened_tr );
2006-12-04 20:08:53 +00:00
array_push ( $has_opened_tr , true );
2004-02-29 08:43:29 +00:00
}
2006-12-04 20:08:53 +00:00
$last_tag = array_pop ( $last_tag_history );
if ( array_pop ( $td_history ) ) {
$previous = " </ { $last_tag } > { $previous } " ;
2006-10-17 08:49:27 +00:00
}
2006-12-04 20:08:53 +00:00
if ( $first_character == '|' ) {
$last_tag = 'td' ;
} else if ( $first_character == '!' ) {
$last_tag = 'th' ;
} else if ( $first_character == '+' ) {
$last_tag = 'caption' ;
} else {
$last_tag = '' ;
2004-10-01 21:20:47 +00:00
}
2006-12-04 20:08:53 +00:00
array_push ( $last_tag_history , $last_tag );
// A cell could contain both parameters and data
$cell_data = explode ( '|' , $cell , 2 );
// Bug 553: Note that a '|' inside an invalid link should not
// be mistaken as delimiting cell parameters
if ( strpos ( $cell_data [ 0 ], '[[' ) !== false ) {
$cell = " { $previous } < { $last_tag } > { $cell } " ;
} else if ( count ( $cell_data ) == 1 )
$cell = " { $previous } < { $last_tag } > { $cell_data [ 0 ] } " ;
2005-08-29 23:34:37 +00:00
else {
2006-12-04 20:08:53 +00:00
$attributes = $this -> mStripState -> unstripBoth ( $cell_data [ 0 ] );
$attributes = Sanitizer :: fixTagAttributes ( $attributes , $last_tag );
$cell = " { $previous } < { $last_tag } { $attributes } > { $cell_data [ 1 ] } " ;
2005-08-29 23:34:37 +00:00
}
2006-12-04 20:08:53 +00:00
$lines [ $key ] .= $cell ;
array_push ( $td_history , true );
2004-02-28 05:55:13 +00:00
}
}
2004-07-12 19:49:20 +00:00
}
2004-02-29 08:43:29 +00:00
2006-12-04 20:08:53 +00:00
// Closing open td, tr && table
while ( count ( $td_history ) > 0 )
2004-02-29 08:43:29 +00:00
{
2006-12-04 20:08:53 +00:00
if ( array_pop ( $td_history ) ) {
$lines [] = '</td>' ;
}
if ( array_pop ( $tr_history ) ) {
$lines [] = '</tr>' ;
}
if ( ! array_pop ( $has_opened_tr ) ) {
$lines [] = " <tr><td></td></tr> " ;
}
$lines [] = '</table>' ;
}
$output = implode ( " \n " , $lines ) ;
// special case: don't return empty table
if ( $output == " <table> \n <tr><td></td></tr> \n </table> " ) {
$output = '' ;
2004-02-28 05:55:13 +00:00
}
2004-02-26 13:37:26 +00:00
2004-07-10 03:09:26 +00:00
wfProfileOut ( $fname );
2006-12-04 20:08:53 +00:00
return $output ;
2004-02-28 05:55:13 +00:00
}
2004-02-26 13:37:26 +00:00
2004-09-21 05:49:12 +00:00
/**
* Helper function for parse () that transforms wiki markup into
* HTML . Only called for $mOutputType == OT_HTML .
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2005-04-21 06:30:48 +00:00
function internalParse ( $text ) {
$args = array ();
$isMain = true ;
2004-06-08 18:11:28 +00:00
$fname = 'Parser::internalParse' ;
2004-02-26 13:37:26 +00:00
wfProfileIn ( $fname );
2004-04-11 16:46:06 +00:00
2006-08-06 14:01:47 +00:00
# Hook to suspend the parser in this state
2006-11-23 08:25:56 +00:00
if ( ! wfRunHooks ( 'ParserBeforeInternalParse' , array ( & $this , & $text , & $this -> mStripState ) ) ) {
2006-08-06 14:01:47 +00:00
wfProfileOut ( $fname );
return $text ;
}
2005-08-07 14:33:40 +00:00
# Remove <noinclude> tags and <includeonly> sections
2005-10-25 07:35:48 +00:00
$text = strtr ( $text , array ( '<onlyinclude>' => '' , '</onlyinclude>' => '' ) );
2005-08-07 14:33:40 +00:00
$text = strtr ( $text , array ( '<noinclude>' => '' , '</noinclude>' => '' ) );
2006-11-21 09:53:45 +00:00
$text = StringUtils :: delimiterReplace ( '<includeonly>' , '</includeonly>' , '' , $text );
2006-07-11 17:40:11 +00:00
2005-08-23 21:49:48 +00:00
$text = Sanitizer :: removeHTMLtags ( $text , array ( & $this , 'attributeStripCallback' ) );
2006-05-23 07:19:01 +00:00
2004-04-11 16:46:06 +00:00
$text = $this -> replaceVariables ( $text , $args );
2007-04-25 21:23:43 +00:00
wfRunHooks ( 'InternalParseBeforeLinks' , array ( & $this , & $text , & $this -> mStripState ) );
2004-02-26 13:37:26 +00:00
2006-05-23 07:19:01 +00:00
// Tables need to come after variable replacement for things to work
// properly; putting them before other transformations should keep
// exciting things like link expansions from showing up in surprising
// places.
$text = $this -> doTableStuff ( $text );
2004-06-08 18:11:28 +00:00
$text = preg_replace ( '/(^|\n)-----*/' , '\\1<hr />' , $text );
2004-02-26 13:37:26 +00:00
2006-05-23 07:19:01 +00:00
$text = $this -> stripToc ( $text );
2006-06-13 11:37:09 +00:00
$this -> stripNoGallery ( $text );
2004-02-26 13:37:26 +00:00
$text = $this -> doHeadings ( $text );
2004-02-29 08:43:29 +00:00
if ( $this -> mOptions -> getUseDynamicDates ()) {
2005-04-20 15:42:08 +00:00
$df =& DateFormatter :: getInstance ();
$text = $df -> reformat ( $this -> mOptions -> getDateFormat (), $text );
2004-02-26 13:37:26 +00:00
}
2004-05-26 16:29:04 +00:00
$text = $this -> doAllQuotes ( $text );
2004-11-28 03:29:50 +00:00
$text = $this -> replaceInternalLinks ( $text );
2005-07-03 07:15:53 +00:00
$text = $this -> replaceExternalLinks ( $text );
2004-10-25 05:24:23 +00:00
# replaceInternalLinks may sometimes leave behind
# absolute URLs, which have to be masked to hide them from replaceExternalLinks
2005-12-21 14:02:20 +00:00
$text = str_replace ( $this -> mUniqPrefix . " NOPARSE " , " " , $text );
2005-07-03 07:15:53 +00:00
2004-09-26 17:59:08 +00:00
$text = $this -> doMagicLinks ( $text );
2004-05-13 17:16:50 +00:00
$text = $this -> formatHeadings ( $text , $isMain );
2004-04-12 16:10:17 +00:00
2004-02-26 13:37:26 +00:00
wfProfileOut ( $fname );
return $text ;
}
2004-08-07 12:35:59 +00:00
2004-09-21 05:49:12 +00:00
/**
* Replace special strings like " ISBN xxx " and " RFC xxx " with
* magic external links .
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
function & doMagicLinks ( & $text ) {
2006-08-06 14:01:47 +00:00
wfProfileIn ( __METHOD__ );
2006-10-17 08:49:27 +00:00
$text = preg_replace_callback (
2006-08-06 14:01:47 +00:00
' ! ( ? : # Start cases
2006-08-12 06:12:54 +00:00
< a .* ? </ a > | # Skip link text
2006-08-06 14:01:47 +00:00
<.* ?> | # Skip stuff inside HTML elements
( ? : RFC | PMID ) \s + ([ 0 - 9 ] + ) | # RFC or PMID, capture number as m[1]
2006-12-18 01:12:19 +00:00
ISBN\s + ( \b # ISBN, capture number as m[2]
( ? : 97 [ 89 ] [ \ \ - ] ? ) ? # optional 13-digit ISBN prefix
( ? : [ 0 - 9 ] [ \ \ - ] ? ){ 9 } # 9 digits with opt. delimiters
[ 0 - 9 Xx ] # check digit
\b )
2006-08-06 14:01:47 +00:00
) ! x ', array( &$this, ' magicLinkCallback ' ), $text );
wfProfileOut ( __METHOD__ );
2006-10-17 08:49:27 +00:00
return $text ;
2006-08-06 14:01:47 +00:00
}
function magicLinkCallback ( $m ) {
if ( substr ( $m [ 0 ], 0 , 1 ) == '<' ) {
# Skip HTML element
return $m [ 0 ];
} elseif ( substr ( $m [ 0 ], 0 , 4 ) == 'ISBN' ) {
$isbn = $m [ 2 ];
2006-10-17 08:49:27 +00:00
$num = strtr ( $isbn , array (
2006-08-06 14:01:47 +00:00
'-' => '' ,
' ' => '' ,
'x' => 'X' ,
));
2006-10-30 06:25:31 +00:00
$titleObj = SpecialPage :: getTitleFor ( 'Booksources' );
2006-08-06 14:01:47 +00:00
$text = '<a href="' .
$titleObj -> escapeLocalUrl ( " isbn= $num " ) .
" \" class= \" internal \" >ISBN $isbn </a> " ;
} else {
if ( substr ( $m [ 0 ], 0 , 3 ) == 'RFC' ) {
$keyword = 'RFC' ;
$urlmsg = 'rfcurl' ;
$id = $m [ 1 ];
} elseif ( substr ( $m [ 0 ], 0 , 4 ) == 'PMID' ) {
$keyword = 'PMID' ;
$urlmsg = 'pubmedurl' ;
$id = $m [ 1 ];
} else {
2006-10-17 08:49:27 +00:00
throw new MWException ( __METHOD__ . ': unrecognised match type "' .
2006-08-06 14:01:47 +00:00
substr ( $m [ 0 ], 0 , 20 ) . '"' );
}
2006-10-17 08:49:27 +00:00
2006-11-08 07:12:03 +00:00
$url = wfMsg ( $urlmsg , $id );
2007-01-22 23:50:42 +00:00
$sk = $this -> mOptions -> getSkin ();
2006-11-08 07:12:03 +00:00
$la = $sk -> getExternalLinkAttributes ( $url , $keyword . $id );
2006-11-08 06:03:01 +00:00
$text = " <a href= \" { $url } \" { $la } > { $keyword } { $id } </a> " ;
2006-08-06 14:01:47 +00:00
}
2004-08-04 01:53:29 +00:00
return $text ;
}
2004-07-12 19:49:20 +00:00
2004-09-21 05:49:12 +00:00
/**
* Parse headers and return html
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
function doHeadings ( $text ) {
2004-06-08 18:11:28 +00:00
$fname = 'Parser::doHeadings' ;
2004-06-05 04:51:24 +00:00
wfProfileIn ( $fname );
2004-02-26 13:37:26 +00:00
for ( $i = 6 ; $i >= 1 ; -- $i ) {
2005-11-29 09:55:50 +00:00
$h = str_repeat ( '=' , $i );
2006-06-06 00:51:34 +00:00
$text = preg_replace ( " /^ { $h } (.+) { $h } \\ s* $ /m " ,
2004-02-26 13:37:26 +00:00
" <h { $i } > \\ 1</h { $i } > \\ 2 " , $text );
}
2004-06-05 04:51:24 +00:00
wfProfileOut ( $fname );
2004-02-26 13:37:26 +00:00
return $text ;
}
2004-09-21 05:49:12 +00:00
/**
* Replace single quotes with HTML markup
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
* @ return string the altered text
*/
function doAllQuotes ( $text ) {
2004-06-08 18:11:28 +00:00
$fname = 'Parser::doAllQuotes' ;
2004-06-05 04:51:24 +00:00
wfProfileIn ( $fname );
2004-06-08 18:11:28 +00:00
$outtext = '' ;
2004-05-27 16:08:12 +00:00
$lines = explode ( " \n " , $text );
2004-05-26 16:29:04 +00:00
foreach ( $lines as $line ) {
2004-08-06 20:47:21 +00:00
$outtext .= $this -> doQuotes ( $line ) . " \n " ;
2004-05-26 16:29:04 +00:00
}
2004-06-05 04:51:24 +00:00
$outtext = substr ( $outtext , 0 , - 1 );
wfProfileOut ( $fname );
return $outtext ;
2004-05-26 16:29:04 +00:00
}
2004-09-21 05:49:12 +00:00
/**
* Helper function for doAllQuotes ()
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
function doQuotes ( $text ) {
2004-11-20 11:28:37 +00:00
$arr = preg_split ( " /(''+)/ " , $text , - 1 , PREG_SPLIT_DELIM_CAPTURE );
if ( count ( $arr ) == 1 )
2004-08-06 20:47:21 +00:00
return $text ;
else
{
2004-08-06 21:13:30 +00:00
# First, do some preliminary work. This may shift some apostrophes from
# being mark-up to being text. It also counts the number of occurrences
# of bold and italics mark-ups.
2004-08-06 20:47:21 +00:00
$i = 0 ;
2004-08-06 21:13:30 +00:00
$numbold = 0 ;
$numitalics = 0 ;
2004-11-20 11:28:37 +00:00
foreach ( $arr as $r )
2004-08-06 20:47:21 +00:00
{
2004-11-20 11:28:37 +00:00
if ( ( $i % 2 ) == 1 )
2004-08-06 20:47:21 +00:00
{
# If there are ever four apostrophes, assume the first is supposed to
# be text, and the remaining three constitute mark-up for bold text.
2004-11-20 11:28:37 +00:00
if ( strlen ( $arr [ $i ] ) == 4 )
2004-08-06 20:47:21 +00:00
{
$arr [ $i - 1 ] .= " ' " ;
$arr [ $i ] = " ''' " ;
}
# If there are more than 5 apostrophes in a row, assume they're all
# text except for the last 5.
2004-11-20 11:28:37 +00:00
else if ( strlen ( $arr [ $i ] ) > 5 )
2004-08-06 20:47:21 +00:00
{
2004-11-20 11:28:37 +00:00
$arr [ $i - 1 ] .= str_repeat ( " ' " , strlen ( $arr [ $i ] ) - 5 );
2004-08-06 20:47:21 +00:00
$arr [ $i ] = " ''''' " ;
}
2004-08-06 21:13:30 +00:00
# Count the number of occurrences of bold and italics mark-ups.
2004-08-07 12:37:20 +00:00
# We are not counting sequences of five apostrophes.
2006-10-17 08:49:27 +00:00
if ( strlen ( $arr [ $i ] ) == 2 ) { $numitalics ++ ; }
else if ( strlen ( $arr [ $i ] ) == 3 ) { $numbold ++ ; }
else if ( strlen ( $arr [ $i ] ) == 5 ) { $numitalics ++ ; $numbold ++ ; }
2004-05-26 16:29:04 +00:00
}
2004-08-06 20:47:21 +00:00
$i ++ ;
2004-05-26 16:29:04 +00:00
}
2004-08-07 12:35:59 +00:00
2004-08-06 20:47:21 +00:00
# If there is an odd number of both bold and italics, it is likely
# that one of the bold ones was meant to be an apostrophe followed
# by italics. Which one we cannot know for certain, but it is more
# likely to be one that has a single-letter word before it.
2004-11-20 11:28:37 +00:00
if ( ( $numbold % 2 == 1 ) && ( $numitalics % 2 == 1 ) )
2004-08-06 20:47:21 +00:00
{
$i = 0 ;
$firstsingleletterword = - 1 ;
$firstmultiletterword = - 1 ;
$firstspace = - 1 ;
2004-11-20 11:28:37 +00:00
foreach ( $arr as $r )
2004-08-06 20:47:21 +00:00
{
2004-11-20 11:28:37 +00:00
if ( ( $i % 2 == 1 ) and ( strlen ( $r ) == 3 ) )
2004-08-06 20:47:21 +00:00
{
$x1 = substr ( $arr [ $i - 1 ], - 1 );
$x2 = substr ( $arr [ $i - 1 ], - 2 , 1 );
2004-08-22 17:24:50 +00:00
if ( $x1 == ' ' ) {
2004-08-06 20:47:21 +00:00
if ( $firstspace == - 1 ) $firstspace = $i ;
2004-08-22 17:24:50 +00:00
} else if ( $x2 == ' ' ) {
2004-08-06 20:47:21 +00:00
if ( $firstsingleletterword == - 1 ) $firstsingleletterword = $i ;
} else {
if ( $firstmultiletterword == - 1 ) $firstmultiletterword = $i ;
}
}
$i ++ ;
}
2004-08-07 12:35:59 +00:00
2004-08-06 20:47:21 +00:00
# If there is a single-letter word, use it!
if ( $firstsingleletterword > - 1 )
{
$arr [ $firstsingleletterword ] = " '' " ;
$arr [ $firstsingleletterword - 1 ] .= " ' " ;
}
# If not, but there's a multi-letter word, use that one.
else if ( $firstmultiletterword > - 1 )
{
$arr [ $firstmultiletterword ] = " '' " ;
$arr [ $firstmultiletterword - 1 ] .= " ' " ;
}
# ... otherwise use the first one that has neither.
2004-08-14 08:32:57 +00:00
# (notice that it is possible for all three to be -1 if, for example,
# there is only one pentuple-apostrophe in the line)
else if ( $firstspace > - 1 )
2004-08-06 20:47:21 +00:00
{
$arr [ $firstspace ] = " '' " ;
$arr [ $firstspace - 1 ] .= " ' " ;
}
}
2004-08-07 12:35:59 +00:00
2004-08-06 20:47:21 +00:00
# Now let's actually convert our apostrophic mush to HTML!
$output = '' ;
$buffer = '' ;
$state = '' ;
$i = 0 ;
foreach ( $arr as $r )
{
if (( $i % 2 ) == 0 )
{
if ( $state == 'both' )
$buffer .= $r ;
else
$output .= $r ;
}
else
{
if ( strlen ( $r ) == 2 )
{
2004-09-11 08:40:26 +00:00
if ( $state == 'i' )
{ $output .= '</i>' ; $state = '' ; }
else if ( $state == 'bi' )
{ $output .= '</i>' ; $state = 'b' ; }
else if ( $state == 'ib' )
{ $output .= '</b></i><b>' ; $state = 'b' ; }
2004-08-06 20:47:21 +00:00
else if ( $state == 'both' )
2004-09-11 08:40:26 +00:00
{ $output .= '<b><i>' . $buffer . '</i>' ; $state = 'b' ; }
else # $state can be 'b' or ''
{ $output .= '<i>' ; $state .= 'i' ; }
2004-08-06 20:47:21 +00:00
}
else if ( strlen ( $r ) == 3 )
{
2004-09-11 08:40:26 +00:00
if ( $state == 'b' )
{ $output .= '</b>' ; $state = '' ; }
else if ( $state == 'bi' )
{ $output .= '</i></b><i>' ; $state = 'i' ; }
else if ( $state == 'ib' )
{ $output .= '</b>' ; $state = 'i' ; }
2004-08-06 20:47:21 +00:00
else if ( $state == 'both' )
2004-09-11 08:40:26 +00:00
{ $output .= '<i><b>' . $buffer . '</b>' ; $state = 'i' ; }
else # $state can be 'i' or ''
{ $output .= '<b>' ; $state .= 'b' ; }
2004-08-06 20:47:21 +00:00
}
else if ( strlen ( $r ) == 5 )
{
2004-09-11 08:40:26 +00:00
if ( $state == 'b' )
{ $output .= '</b><i>' ; $state = 'i' ; }
else if ( $state == 'i' )
{ $output .= '</i><b>' ; $state = 'b' ; }
else if ( $state == 'bi' )
{ $output .= '</i></b>' ; $state = '' ; }
else if ( $state == 'ib' )
{ $output .= '</b></i>' ; $state = '' ; }
2004-08-06 20:47:21 +00:00
else if ( $state == 'both' )
2004-09-11 08:40:26 +00:00
{ $output .= '<i><b>' . $buffer . '</b></i>' ; $state = '' ; }
2004-08-06 20:47:21 +00:00
else # ($state == '')
{ $buffer = '' ; $state = 'both' ; }
}
}
$i ++ ;
2004-05-26 16:29:04 +00:00
}
2004-08-07 12:37:20 +00:00
# Now close all remaining tags. Notice that the order is important.
2004-09-11 08:40:26 +00:00
if ( $state == 'b' || $state == 'ib' )
$output .= '</b>' ;
if ( $state == 'i' || $state == 'bi' || $state == 'ib' )
$output .= '</i>' ;
if ( $state == 'bi' )
$output .= '</b>' ;
2007-03-13 18:09:20 +00:00
# There might be lonely ''''', so make sure we have a buffer
if ( $state == 'both' && $buffer )
2004-09-11 08:40:26 +00:00
$output .= '<b><i>' . $buffer . '</i></b>' ;
2004-08-06 20:47:21 +00:00
return $output ;
2004-05-26 16:29:04 +00:00
}
}
2004-09-21 05:49:12 +00:00
/**
* Replace external links
*
2004-10-25 05:24:23 +00:00
* Note : this is all very hackish and the order of execution matters a lot .
* Make sure to run maintenance / parserTests . php if you change this code .
2004-09-21 05:49:12 +00:00
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
function replaceExternalLinks ( $text ) {
2005-04-27 20:19:35 +00:00
global $wgContLang ;
2004-06-08 18:11:28 +00:00
$fname = 'Parser::replaceExternalLinks' ;
2004-02-26 13:37:26 +00:00
wfProfileIn ( $fname );
2004-08-07 08:54:52 +00:00
2007-01-22 23:50:42 +00:00
$sk = $this -> mOptions -> getSkin ();
2006-11-08 07:12:03 +00:00
2004-08-07 18:24:12 +00:00
$bits = preg_split ( EXT_LINK_BRACKETED , $text , - 1 , PREG_SPLIT_DELIM_CAPTURE );
$s = $this -> replaceFreeExternalLinks ( array_shift ( $bits ) );
$i = 0 ;
while ( $i < count ( $bits ) ) {
$url = $bits [ $i ++ ];
$protocol = $bits [ $i ++ ];
$text = $bits [ $i ++ ];
$trail = $bits [ $i ++ ];
2004-08-14 22:38:46 +00:00
2004-10-11 16:57:49 +00:00
# The characters '<' and '>' (which were escaped by
# removeHTMLtags()) should not be included in
# URLs, per RFC 2396.
2006-10-17 08:49:27 +00:00
$m2 = array ();
2004-10-11 16:57:49 +00:00
if ( preg_match ( '/&(lt|gt);/' , $url , $m2 , PREG_OFFSET_CAPTURE )) {
2004-10-11 19:15:24 +00:00
$text = substr ( $url , $m2 [ 0 ][ 1 ]) . ' ' . $text ;
2004-10-11 16:57:49 +00:00
$url = substr ( $url , 0 , $m2 [ 0 ][ 1 ]);
}
2004-08-07 18:24:12 +00:00
# If the link text is an image URL, replace it with an <img> tag
# This happened by accident in the original parser, but some people used it extensively
2005-04-27 07:48:14 +00:00
$img = $this -> maybeMakeExternalImage ( $text );
2004-08-07 18:24:12 +00:00
if ( $img !== false ) {
$text = $img ;
}
2004-04-12 23:59:37 +00:00
2004-08-07 18:24:12 +00:00
$dtrail = '' ;
2004-08-07 08:54:52 +00:00
2005-01-15 23:56:26 +00:00
# Set linktype for CSS - if URL==text, link is essentially free
$linktype = ( $text == $url ) ? 'free' : 'text' ;
2004-08-07 18:24:12 +00:00
# No link text, e.g. [http://domain.tld/some.link]
if ( $text == '' ) {
2006-05-13 19:29:24 +00:00
# Autonumber if allowed. See bug #5918
if ( strpos ( wfUrlProtocols (), substr ( $protocol , 0 , strpos ( $protocol , ':' )) ) !== false ) {
2004-08-22 17:24:50 +00:00
$text = '[' . ++ $this -> mAutonumber . ']' ;
2005-01-15 23:56:26 +00:00
$linktype = 'autonumber' ;
2004-08-14 22:38:46 +00:00
} else {
2004-08-07 18:24:12 +00:00
# Otherwise just use the URL
2004-08-14 22:38:46 +00:00
$text = htmlspecialchars ( $url );
2005-01-15 23:56:26 +00:00
$linktype = 'free' ;
2004-08-07 18:24:12 +00:00
}
} else {
# Have link text, e.g. [http://domain.tld/some.link text]s
# Check for trail
2005-04-27 07:48:14 +00:00
list ( $dtrail , $trail ) = Linker :: splitTrail ( $trail );
2004-08-07 18:24:12 +00:00
}
2004-08-14 22:38:46 +00:00
2005-04-26 20:50:16 +00:00
$text = $wgContLang -> markNoConversion ( $text );
2006-10-17 08:49:27 +00:00
2006-07-11 19:54:20 +00:00
$url = Sanitizer :: cleanUrl ( $url );
2005-01-30 04:11:22 +00:00
2004-08-07 18:24:12 +00:00
# Process the trail (i.e. everything after this link up until start of the next link),
# replacing any non-bracketed links
$trail = $this -> replaceFreeExternalLinks ( $trail );
# Use the encoded URL
# This means that users can paste URLs directly into the text
# Funny characters like ö aren't valid in URLs anyway
# This was changed in August 2004
2006-11-08 07:12:03 +00:00
$s .= $sk -> makeExternalLink ( $url , $text , false , $linktype , $this -> mTitle -> getNamespace () ) . $dtrail . $trail ;
2006-01-26 13:29:14 +00:00
2006-03-17 01:02:14 +00:00
# Register link in the output object.
# Replace unnecessary URL escape codes with the referenced character
# This prevents spammers from hiding links from the filters
$pasteurized = Parser :: replaceUnusualEscapes ( $url );
$this -> mOutput -> addExternalLink ( $pasteurized );
2004-08-07 18:24:12 +00:00
}
2004-08-07 08:54:52 +00:00
2004-08-07 18:24:12 +00:00
wfProfileOut ( $fname );
return $s ;
}
2004-02-26 13:37:26 +00:00
2004-09-21 05:49:12 +00:00
/**
* Replace anything that looks like a URL with a link
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2004-08-07 18:24:12 +00:00
function replaceFreeExternalLinks ( $text ) {
2005-04-26 20:50:16 +00:00
global $wgContLang ;
2004-11-26 12:10:24 +00:00
$fname = 'Parser::replaceFreeExternalLinks' ;
wfProfileIn ( $fname );
2005-07-03 07:15:53 +00:00
2005-10-30 11:24:43 +00:00
$bits = preg_split ( '/(\b(?:' . wfUrlProtocols () . '))/S' , $text , - 1 , PREG_SPLIT_DELIM_CAPTURE );
2004-08-07 18:24:12 +00:00
$s = array_shift ( $bits );
$i = 0 ;
2004-08-07 08:54:52 +00:00
2007-01-22 23:50:42 +00:00
$sk = $this -> mOptions -> getSkin ();
2006-11-08 07:12:03 +00:00
2004-08-07 18:24:12 +00:00
while ( $i < count ( $bits ) ){
$protocol = $bits [ $i ++ ];
$remainder = $bits [ $i ++ ];
2004-08-07 08:54:52 +00:00
2006-10-17 08:49:27 +00:00
$m = array ();
2004-08-07 18:24:12 +00:00
if ( preg_match ( '/^(' . EXT_LINK_URL_CLASS . '+)(.*)$/s' , $remainder , $m ) ) {
# Found some characters after the protocol that look promising
$url = $protocol . $m [ 1 ];
2004-02-26 13:37:26 +00:00
$trail = $m [ 2 ];
2004-08-14 22:38:46 +00:00
2006-03-24 16:38:34 +00:00
# special case: handle urls as url args:
# http://www.example.com/foo?=http://www.example.com/bar
2006-10-17 08:49:27 +00:00
if ( strlen ( $trail ) == 0 &&
2006-03-24 16:38:34 +00:00
isset ( $bits [ $i ]) &&
preg_match ( '/^' . wfUrlProtocols () . '$/S' , $bits [ $i ]) &&
2006-10-17 08:49:27 +00:00
preg_match ( '/^(' . EXT_LINK_URL_CLASS . '+)(.*)$/s' , $bits [ $i + 1 ], $m ))
2006-03-24 16:38:34 +00:00
{
# add protocol, arg
2006-05-01 20:22:44 +00:00
$url .= $bits [ $i ] . $m [ 1 ]; # protocol, url as arg to previous link
2006-03-24 16:38:34 +00:00
$i += 2 ;
$trail = $m [ 2 ];
}
2004-10-11 16:57:49 +00:00
# The characters '<' and '>' (which were escaped by
# removeHTMLtags()) should not be included in
# URLs, per RFC 2396.
2006-10-17 08:49:27 +00:00
$m2 = array ();
2004-10-11 16:57:49 +00:00
if ( preg_match ( '/&(lt|gt);/' , $url , $m2 , PREG_OFFSET_CAPTURE )) {
$trail = substr ( $url , $m2 [ 0 ][ 1 ]) . $trail ;
$url = substr ( $url , 0 , $m2 [ 0 ][ 1 ]);
}
2004-08-07 18:45:30 +00:00
# Move trailing punctuation to $trail
$sep = ',;\.:!?' ;
# If there is no left bracket, then consider right brackets fair game too
if ( strpos ( $url , '(' ) === false ) {
$sep .= ')' ;
}
2004-08-07 18:24:12 +00:00
$numSepChars = strspn ( strrev ( $url ), $sep );
if ( $numSepChars ) {
$trail = substr ( $url , - $numSepChars ) . $trail ;
$url = substr ( $url , 0 , - $numSepChars );
2004-07-13 00:01:33 +00:00
}
2004-07-12 22:57:13 +00:00
2006-07-11 19:54:20 +00:00
$url = Sanitizer :: cleanUrl ( $url );
2004-08-10 11:24:24 +00:00
2004-08-07 18:24:12 +00:00
# Is this an external image?
2005-04-27 07:48:14 +00:00
$text = $this -> maybeMakeExternalImage ( $url );
2004-08-07 18:24:12 +00:00
if ( $text === false ) {
# Not an image, make a link
2006-11-08 07:12:03 +00:00
$text = $sk -> makeExternalLink ( $url , $wgContLang -> markNoConversion ( $url ), true , 'free' , $this -> mTitle -> getNamespace () );
2006-03-17 01:02:14 +00:00
# Register it in the output object...
# Replace unnecessary URL escape codes with their equivalent characters
$pasteurized = Parser :: replaceUnusualEscapes ( $url );
$this -> mOutput -> addExternalLink ( $pasteurized );
2004-08-07 18:24:12 +00:00
}
$s .= $text . $trail ;
2004-03-21 11:28:44 +00:00
} else {
2004-08-07 18:24:12 +00:00
$s .= $protocol . $remainder ;
2004-03-21 11:28:44 +00:00
}
2004-02-26 13:37:26 +00:00
}
2005-07-25 07:00:20 +00:00
wfProfileOut ( $fname );
2004-02-26 13:37:26 +00:00
return $s ;
}
2004-08-14 22:38:46 +00:00
2006-01-26 13:29:14 +00:00
/**
* Replace unusual URL escape codes with their equivalent characters
2006-03-11 17:13:49 +00:00
* @ param string
2006-01-26 13:29:14 +00:00
* @ return string
* @ static
2007-04-04 05:22:37 +00:00
* @ todo This can merge genuinely required bits in the path or query string ,
2006-03-17 01:02:14 +00:00
* breaking legit URLs . A proper fix would treat the various parts of
* the URL differently ; as a workaround , just use the output for
* statistical records , not for actual linking / output .
2006-01-26 13:29:14 +00:00
*/
2006-07-10 15:41:30 +00:00
static function replaceUnusualEscapes ( $url ) {
2006-03-11 17:13:49 +00:00
return preg_replace_callback ( '/%[0-9A-Fa-f]{2}/' ,
2006-01-26 13:29:14 +00:00
array ( 'Parser' , 'replaceUnusualEscapesCallback' ), $url );
}
/**
* Callback function used in replaceUnusualEscapes () .
* Replaces unusual URL escape codes with their equivalent character
* @ static
2006-04-19 15:46:24 +00:00
* @ private
2006-01-26 13:29:14 +00:00
*/
2006-07-10 15:41:30 +00:00
private static function replaceUnusualEscapesCallback ( $matches ) {
2006-01-26 13:29:14 +00:00
$char = urldecode ( $matches [ 0 ] );
$ord = ord ( $char );
// Is it an unsafe or HTTP reserved character according to RFC 1738?
if ( $ord > 32 && $ord < 127 && strpos ( '<>"#{}|\^~[]`;/?' , $char ) === false ) {
// No, shouldn't be escaped
return $char ;
} else {
// Yes, leave it escaped
return $matches [ 0 ];
}
}
2004-09-21 05:49:12 +00:00
/**
2005-10-26 22:13:02 +00:00
* make an image if it ' s allowed , either through the global
* option or through the exception
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2005-04-27 07:48:14 +00:00
function maybeMakeExternalImage ( $url ) {
2007-01-22 23:50:42 +00:00
$sk = $this -> mOptions -> getSkin ();
2005-10-26 22:13:02 +00:00
$imagesfrom = $this -> mOptions -> getAllowExternalImagesFrom ();
$imagesexception = ! empty ( $imagesfrom );
2004-08-07 18:24:12 +00:00
$text = false ;
2006-01-07 13:09:30 +00:00
if ( $this -> mOptions -> getAllowExternalImages ()
2005-10-26 22:13:02 +00:00
|| ( $imagesexception && strpos ( $url , $imagesfrom ) === 0 ) ) {
2004-08-07 18:24:12 +00:00
if ( preg_match ( EXT_IMAGE_REGEX , $url ) ) {
# Image found
2006-11-08 07:12:03 +00:00
$text = $sk -> makeExternalImage ( htmlspecialchars ( $url ) );
2004-08-07 18:24:12 +00:00
}
}
return $text ;
}
2005-07-03 07:15:53 +00:00
2004-09-21 05:49:12 +00:00
/**
* Process [[ ]] wikilinks
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
function replaceInternalLinks ( $s ) {
2006-01-05 02:05:53 +00:00
global $wgContLang ;
2004-06-08 18:11:28 +00:00
static $fname = 'Parser::replaceInternalLinks' ;
2004-10-23 13:00:33 +00:00
2004-02-28 23:38:08 +00:00
wfProfileIn ( $fname );
2004-02-26 13:37:26 +00:00
2004-06-08 18:11:28 +00:00
wfProfileIn ( $fname . '-setup' );
2004-02-28 23:38:08 +00:00
static $tc = FALSE ;
2004-05-25 14:26:14 +00:00
# the % is needed to support urlencoded titles as well
2004-06-08 18:11:28 +00:00
if ( ! $tc ) { $tc = Title :: legalChars () . '#%' ; }
2005-07-03 07:15:53 +00:00
2007-01-22 23:50:42 +00:00
$sk = $this -> mOptions -> getSkin ();
2006-11-08 07:12:03 +00:00
2004-10-05 03:55:41 +00:00
#split the entire text string on occurences of [[
2004-06-08 18:11:28 +00:00
$a = explode ( '[[' , ' ' . $s );
2004-10-05 03:55:41 +00:00
#get the first element (all text up to first [[), and remove the space we added
2004-05-26 16:29:04 +00:00
$s = array_shift ( $a );
$s = substr ( $s , 1 );
2004-02-26 13:37:26 +00:00
# Match a link having the form [[namespace:link|alternate]]trail
2004-02-28 23:38:08 +00:00
static $e1 = FALSE ;
2005-01-13 09:13:22 +00:00
if ( ! $e1 ) { $e1 = " /^([ { $tc } ]+)(?: \\ |(.+?))?]](.*) \$ /sD " ; }
2004-10-05 03:55:41 +00:00
# Match cases where there is no "]]", which might still be images
static $e1_img = FALSE ;
if ( ! $e1_img ) { $e1_img = " /^([ { $tc } ]+) \\ |(.*) \$ /sD " ; }
2004-02-26 13:37:26 +00:00
# Match the end of a line for a word that's not followed by whitespace,
# e.g. in the case of 'The Arab al[[Razi]]', 'al' will be matched
2005-07-30 20:03:38 +00:00
$e2 = wfMsgForContent ( 'linkprefix' );
2004-02-26 13:37:26 +00:00
2004-09-24 13:14:52 +00:00
$useLinkPrefixExtension = $wgContLang -> linkPrefixExtension ();
2005-02-21 11:28:07 +00:00
if ( is_null ( $this -> mTitle ) ) {
2006-08-06 14:01:47 +00:00
throw new MWException ( __METHOD__ . " : \$ this->mTitle is null \n " );
2005-02-21 11:28:07 +00:00
}
$nottalk = ! $this -> mTitle -> isTalkPage ();
2004-02-26 13:37:26 +00:00
2004-06-02 22:54:01 +00:00
if ( $useLinkPrefixExtension ) {
2006-10-17 08:49:27 +00:00
$m = array ();
2004-06-02 22:54:01 +00:00
if ( preg_match ( $e2 , $s , $m ) ) {
$first_prefix = $m [ 2 ];
} else {
$first_prefix = false ;
}
2004-05-26 16:29:04 +00:00
} else {
2004-06-02 22:54:01 +00:00
$prefix = '' ;
2004-05-26 16:29:04 +00:00
}
2007-01-09 20:57:10 +00:00
if ( $wgContLang -> hasVariants ()) {
2006-12-26 12:19:45 +00:00
$selflink = $wgContLang -> convertLinkToAllVariants ( $this -> mTitle -> getPrefixedText ());
2007-01-09 20:57:10 +00:00
} else {
2006-12-26 12:19:45 +00:00
$selflink = array ( $this -> mTitle -> getPrefixedText ());
2007-01-09 20:57:10 +00:00
}
2004-11-28 03:29:50 +00:00
$useSubpages = $this -> areSubpagesAllowed ();
2006-08-06 14:01:47 +00:00
wfProfileOut ( $fname . '-setup' );
2005-07-03 07:15:53 +00:00
2004-10-05 03:55:41 +00:00
# Loop for each link
for ( $k = 0 ; isset ( $a [ $k ] ); $k ++ ) {
$line = $a [ $k ];
2004-06-02 22:54:01 +00:00
if ( $useLinkPrefixExtension ) {
2004-11-22 10:05:11 +00:00
wfProfileIn ( $fname . '-prefixhandling' );
2004-06-02 22:54:01 +00:00
if ( preg_match ( $e2 , $s , $m ) ) {
$prefix = $m [ 2 ];
$s = $m [ 1 ];
} else {
$prefix = '' ;
}
# first link
if ( $first_prefix ) {
$prefix = $first_prefix ;
$first_prefix = false ;
}
2004-11-22 10:05:11 +00:00
wfProfileOut ( $fname . '-prefixhandling' );
2004-06-02 22:39:06 +00:00
}
2004-07-12 19:49:20 +00:00
2004-10-05 03:55:41 +00:00
$might_be_img = false ;
2005-07-03 07:15:53 +00:00
2006-08-06 14:01:47 +00:00
wfProfileIn ( " $fname -e1 " );
2004-05-26 16:29:04 +00:00
if ( preg_match ( $e1 , $line , $m ) ) { # page with normal text or alt
$text = $m [ 2 ];
2005-04-12 06:07:23 +00:00
# If we get a ] at the beginning of $m[3] that means we have a link that's something like:
# [[Image:Foo.jpg|[http://example.com desc]]] <- having three ] in a row fucks up,
# the real problem is with the $e1 regex
# See bug 1300.
2005-05-18 09:21:47 +00:00
#
# Still some problems for cases where the ] is meant to be outside punctuation,
# and no image is in sight. See bug 2095.
#
2006-10-17 08:49:27 +00:00
if ( $text !== '' &&
substr ( $m [ 3 ], 0 , 1 ) === ']' &&
strpos ( $text , '[' ) !== false
)
2006-03-24 16:40:31 +00:00
{
2005-04-12 06:07:23 +00:00
$text .= ']' ; # so that replaceExternalLinks($text) works later
2006-08-06 14:01:47 +00:00
$m [ 3 ] = substr ( $m [ 3 ], 1 );
2005-04-12 06:07:23 +00:00
}
2004-05-26 16:29:04 +00:00
# fix up urlencoded title texts
2006-08-06 14:01:47 +00:00
if ( strpos ( $m [ 1 ], '%' ) !== false ) {
2006-03-24 16:43:57 +00:00
# Should anchors '#' also be rejected?
$m [ 1 ] = str_replace ( array ( '<' , '>' ), array ( '<' , '>' ), urldecode ( $m [ 1 ]) );
2006-08-06 14:01:47 +00:00
}
2004-05-26 16:29:04 +00:00
$trail = $m [ 3 ];
2004-10-05 03:55:41 +00:00
} elseif ( preg_match ( $e1_img , $line , $m ) ) { # Invalid, but might be an image with a link in its caption
$might_be_img = true ;
$text = $m [ 2 ];
2006-08-06 14:01:47 +00:00
if ( strpos ( $m [ 1 ], '%' ) !== false ) {
2007-03-30 19:38:11 +00:00
$m [ 1 ] = urldecode ( $m [ 1 ]);
2006-08-06 14:01:47 +00:00
}
2004-10-05 03:55:41 +00:00
$trail = " " ;
2004-10-05 00:21:52 +00:00
} else { # Invalid form; output directly
$s .= $prefix . '[[' . $line ;
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -e1 " );
2004-10-05 00:21:52 +00:00
continue ;
2004-02-26 13:37:26 +00:00
}
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -e1 " );
wfProfileIn ( " $fname -misc " );
2004-05-26 16:29:04 +00:00
2004-09-24 18:29:01 +00:00
# Don't allow internal links to pages containing
# PROTO: where PROTO is a valid URL protocol; these
# should be external links.
2007-04-13 00:23:49 +00:00
if ( preg_match ( '/^\b(?:' . wfUrlProtocols () . ')/' , $m [ 1 ])) {
2004-09-24 18:29:01 +00:00
$s .= $prefix . '[[' . $line ;
continue ;
}
2004-09-25 20:35:38 +00:00
# Make subpage if necessary
2004-11-28 03:29:50 +00:00
if ( $useSubpages ) {
$link = $this -> maybeDoSubpageLink ( $m [ 1 ], $text );
} else {
$link = $m [ 1 ];
}
2004-09-25 20:35:38 +00:00
$noforce = ( substr ( $m [ 1 ], 0 , 1 ) != ':' );
if ( ! $noforce ) {
# Strip off leading ':'
$link = substr ( $link , 1 );
}
2005-07-03 07:15:53 +00:00
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -misc " );
wfProfileIn ( " $fname -title " );
2006-11-21 09:53:45 +00:00
$nt = Title :: newFromText ( $this -> mStripState -> unstripNoWiki ( $link ) );
2004-05-26 16:29:04 +00:00
if ( ! $nt ) {
2004-06-08 18:11:28 +00:00
$s .= $prefix . '[[' . $line ;
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -title " );
2004-05-26 16:29:04 +00:00
continue ;
2004-02-26 13:37:26 +00:00
}
2004-10-04 03:47:39 +00:00
2004-05-26 16:29:04 +00:00
$ns = $nt -> getNamespace ();
$iw = $nt -> getInterWiki ();
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -title " );
2006-10-17 08:49:27 +00:00
2004-10-05 03:55:41 +00:00
if ( $might_be_img ) { # if this is actually an invalid link
2006-08-06 14:01:47 +00:00
wfProfileIn ( " $fname -might_be_img " );
2004-10-05 03:55:41 +00:00
if ( $ns == NS_IMAGE && $noforce ) { #but might be an image
$found = false ;
while ( isset ( $a [ $k + 1 ]) ) {
#look at the next 'line' to see if we can close it there
2005-09-18 01:19:33 +00:00
$spliced = array_splice ( $a , $k + 1 , 1 );
$next_line = array_shift ( $spliced );
2006-08-06 14:01:47 +00:00
$m = explode ( ']]' , $next_line , 3 );
if ( count ( $m ) == 3 ) {
# the first ]] closes the inner link, the second the image
2004-10-05 03:55:41 +00:00
$found = true ;
2006-08-06 14:01:47 +00:00
$text .= " [[ { $m [ 0 ] } ]] { $m [ 1 ] } " ;
2004-10-05 03:55:41 +00:00
$trail = $m [ 2 ];
break ;
2006-08-06 14:01:47 +00:00
} elseif ( count ( $m ) == 2 ) {
2004-10-05 03:55:41 +00:00
#if there's exactly one ]] that's fine, we'll keep looking
2006-08-06 14:01:47 +00:00
$text .= " [[ { $m [ 0 ] } ]] { $m [ 1 ] } " ;
2004-10-05 03:55:41 +00:00
} else {
#if $next_line is invalid too, we need look no further
$text .= '[[' . $next_line ;
break ;
}
}
if ( ! $found ) {
# we couldn't find the end of this imageLink, so output it raw
2004-10-06 19:22:42 +00:00
#but don't ignore what might be perfectly normal links in the text we've examined
$text = $this -> replaceInternalLinks ( $text );
2006-08-06 14:01:47 +00:00
$s .= " { $prefix } [[ $link | $text " ;
2004-10-05 03:55:41 +00:00
# note: no $trail, because without an end, there *is* no trail
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -might_be_img " );
2004-10-05 03:55:41 +00:00
continue ;
}
} else { #it's not an image, so output it raw
2006-08-06 14:01:47 +00:00
$s .= " { $prefix } [[ $link | $text " ;
2004-10-05 03:55:41 +00:00
# note: no $trail, because without an end, there *is* no trail
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -might_be_img " );
2004-10-05 03:55:41 +00:00
continue ;
}
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -might_be_img " );
2004-10-05 03:55:41 +00:00
}
$wasblank = ( '' == $text );
if ( $wasblank ) $text = $link ;
2004-08-16 20:01:21 +00:00
# Link not escaped by : , create the various objects
2004-05-26 16:29:04 +00:00
if ( $noforce ) {
2004-08-16 20:01:21 +00:00
# Interwikis
2006-08-06 14:01:47 +00:00
wfProfileIn ( " $fname -interwiki " );
2004-09-24 13:14:52 +00:00
if ( $iw && $this -> mOptions -> getInterwikiMagic () && $nottalk && $wgContLang -> getLanguageName ( $iw ) ) {
2005-12-30 09:33:11 +00:00
$this -> mOutput -> addLanguageLink ( $nt -> getFullText () );
2007-02-06 07:45:06 +00:00
$s = rtrim ( $s . $prefix );
$s .= trim ( $trail , " \n " ) == '' ? '' : $prefix . $trail ;
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -interwiki " );
2004-05-26 16:29:04 +00:00
continue ;
}
2006-08-06 14:01:47 +00:00
wfProfileOut ( " $fname -interwiki " );
2005-07-03 07:15:53 +00:00
2004-08-07 03:50:46 +00:00
if ( $ns == NS_IMAGE ) {
2004-11-23 07:41:07 +00:00
wfProfileIn ( " $fname -image " );
2006-09-25 01:35:41 +00:00
if ( ! wfIsBadImage ( $nt -> getDBkey (), $this -> mTitle ) ) {
2005-04-12 04:03:21 +00:00
# recursively parse links inside the image caption
# actually, this will parse them in any other parameters, too,
# but it might be hard to fix that, and it doesn't matter ATM
$text = $this -> replaceExternalLinks ( $text );
$text = $this -> replaceInternalLinks ( $text );
2005-07-03 07:15:53 +00:00
2005-04-12 04:03:21 +00:00
# cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them
2005-12-28 22:58:54 +00:00
$s .= $prefix . $this -> armorLinks ( $this -> makeImage ( $nt , $text ) ) . $trail ;
2006-01-07 13:09:30 +00:00
$this -> mOutput -> addImage ( $nt -> getDBkey () );
2005-07-03 07:15:53 +00:00
2005-04-12 04:03:21 +00:00
wfProfileOut ( " $fname -image " );
continue ;
2006-07-04 03:40:37 +00:00
} else {
# We still need to record the image's presence on the page
$this -> mOutput -> addImage ( $nt -> getDBkey () );
2005-04-12 04:03:21 +00:00
}
2004-11-23 07:41:07 +00:00
wfProfileOut ( " $fname -image " );
2005-04-12 04:03:21 +00:00
2004-05-27 01:10:01 +00:00
}
2005-07-03 07:15:53 +00:00
2004-08-07 03:50:46 +00:00
if ( $ns == NS_CATEGORY ) {
2004-11-23 07:41:07 +00:00
wfProfileIn ( " $fname -category " );
2005-04-13 08:26:08 +00:00
$s = rtrim ( $s . " \n " ); # bug 87
2004-05-26 16:29:04 +00:00
2004-09-07 22:08:01 +00:00
if ( $wasblank ) {
2006-12-29 10:39:35 +00:00
$sortkey = $this -> getDefaultSort ();
2004-09-07 22:08:01 +00:00
} else {
$sortkey = $text ;
}
2005-12-05 08:19:52 +00:00
$sortkey = Sanitizer :: decodeCharReferences ( $sortkey );
2006-06-23 09:20:44 +00:00
$sortkey = str_replace ( " \n " , '' , $sortkey );
2005-04-28 03:33:54 +00:00
$sortkey = $wgContLang -> convertCategoryKey ( $sortkey );
2005-12-30 09:33:11 +00:00
$this -> mOutput -> addCategory ( $nt -> getDBkey (), $sortkey );
2005-07-03 07:15:53 +00:00
2005-04-09 00:31:40 +00:00
/**
* Strip the whitespace Category links produce , see bug 87
* @ todo We might want to use trim ( $tmp , " \n " ) here .
*/
2005-04-13 08:26:08 +00:00
$s .= trim ( $prefix . $trail , " \n " ) == '' ? '' : $prefix . $trail ;
2005-07-03 07:15:53 +00:00
2004-11-23 07:41:07 +00:00
wfProfileOut ( " $fname -category " );
2004-05-26 16:29:04 +00:00
continue ;
}
2004-02-26 13:37:26 +00:00
}
2004-10-08 04:27:07 +00:00
2007-01-17 19:29:11 +00:00
# Self-link checking
if ( $nt -> getFragment () === '' ) {
if ( in_array ( $nt -> getPrefixedText (), $selflink , true ) ) {
$s .= $prefix . $sk -> makeSelfLinkObj ( $nt , $text , '' , $trail );
continue ;
2007-01-17 19:48:48 +00:00
}
2004-04-20 21:08:24 +00:00
}
2004-03-20 12:10:25 +00:00
2004-10-05 03:55:41 +00:00
# Special and Media are pseudo-namespaces; no pages actually exist in them
2004-08-07 03:50:46 +00:00
if ( $ns == NS_MEDIA ) {
2006-11-08 07:12:03 +00:00
$link = $sk -> makeMediaLinkObj ( $nt , $text );
2005-12-21 14:02:20 +00:00
# Cloak with NOPARSE to avoid replacement in replaceExternalLinks
2005-12-28 22:58:54 +00:00
$s .= $prefix . $this -> armorLinks ( $link ) . $trail ;
2005-12-30 09:33:11 +00:00
$this -> mOutput -> addImage ( $nt -> getDBkey () );
2004-06-05 02:22:16 +00:00
continue ;
2004-08-07 03:50:46 +00:00
} elseif ( $ns == NS_SPECIAL ) {
2007-05-26 16:23:34 +00:00
if ( SpecialPage :: exists ( $nt -> getDBkey () ) ) {
$s .= $this -> makeKnownLinkHolder ( $nt , $text , '' , $trail , $prefix );
} else {
$s .= $this -> makeLinkHolder ( $nt , $text , '' , $trail , $prefix );
}
2004-05-26 16:29:04 +00:00
continue ;
2005-12-05 07:08:04 +00:00
} elseif ( $ns == NS_IMAGE ) {
2007-05-30 21:02:32 +00:00
$img = wfFindFile ( $nt );
if ( $img ) {
2005-12-05 07:08:04 +00:00
// Force a blue link if the file exists; may be a remote
// upload on the shared repository, and we want to see its
// auto-generated page.
2005-12-29 00:31:18 +00:00
$s .= $this -> makeKnownLinkHolder ( $nt , $text , '' , $trail , $prefix );
2006-08-18 05:00:16 +00:00
$this -> mOutput -> addLink ( $nt );
2005-12-05 07:08:04 +00:00
continue ;
}
2004-05-26 16:29:04 +00:00
}
2005-08-20 13:47:12 +00:00
$s .= $this -> makeLinkHolder ( $nt , $text , '' , $trail , $prefix );
2004-02-28 23:38:08 +00:00
}
2004-02-26 13:37:26 +00:00
wfProfileOut ( $fname );
return $s ;
}
2005-04-27 07:48:14 +00:00
/**
* Make a link placeholder . The text returned can be later resolved to a real link with
2005-07-03 07:15:53 +00:00
* replaceLinkHolders () . This is done for two reasons : firstly to avoid further
2006-08-06 14:01:47 +00:00
* parsing of interwiki links , and secondly to allow all existence checks and
2005-04-27 07:48:14 +00:00
* article length checks ( for stub links ) to be bundled into a single query .
*
*/
function makeLinkHolder ( & $nt , $text = '' , $query = '' , $trail = '' , $prefix = '' ) {
2006-08-06 14:01:47 +00:00
wfProfileIn ( __METHOD__ );
2005-04-27 07:48:14 +00:00
if ( ! is_object ( $nt ) ) {
# Fail gracefully
$retVal = " <!-- ERROR --> { $prefix } { $text } { $trail } " ;
} else {
# Separate the link trail from the rest of the link
list ( $inside , $trail ) = Linker :: splitTrail ( $trail );
2005-07-03 07:15:53 +00:00
2005-04-27 07:48:14 +00:00
if ( $nt -> isExternal () ) {
2005-06-03 05:46:24 +00:00
$nr = array_push ( $this -> mInterwikiLinkHolders [ 'texts' ], $prefix . $text . $inside );
2005-06-26 13:38:43 +00:00
$this -> mInterwikiLinkHolders [ 'titles' ][] = $nt ;
2005-04-27 07:48:14 +00:00
$retVal = '<!--IWLINK ' . ( $nr - 1 ) . " --> { $trail } " ;
} else {
$nr = array_push ( $this -> mLinkHolders [ 'namespaces' ], $nt -> getNamespace () );
$this -> mLinkHolders [ 'dbkeys' ][] = $nt -> getDBkey ();
$this -> mLinkHolders [ 'queries' ][] = $query ;
$this -> mLinkHolders [ 'texts' ][] = $prefix . $text . $inside ;
2005-06-26 13:38:43 +00:00
$this -> mLinkHolders [ 'titles' ][] = $nt ;
2005-04-27 07:48:14 +00:00
$retVal = '<!--LINK ' . ( $nr - 1 ) . " --> { $trail } " ;
}
}
2006-08-06 14:01:47 +00:00
wfProfileOut ( __METHOD__ );
2005-04-27 07:48:14 +00:00
return $retVal ;
}
2006-01-07 13:31:29 +00:00
2005-12-29 00:31:18 +00:00
/**
* Render a forced - blue link inline ; protect against double expansion of
* URLs if we ' re in a mode that prepends full URL prefixes to internal links .
* Since this little disaster has to split off the trail text to avoid
* breaking URLs in the following text without breaking trails on the
* wiki links , it ' s been made into a horrible function .
*
* @ param Title $nt
* @ param string $text
* @ param string $query
* @ param string $trail
* @ param string $prefix
* @ return string HTML - wikitext mix oh yuck
*/
function makeKnownLinkHolder ( $nt , $text = '' , $query = '' , $trail = '' , $prefix = '' ) {
list ( $inside , $trail ) = Linker :: splitTrail ( $trail );
2007-01-22 23:50:42 +00:00
$sk = $this -> mOptions -> getSkin ();
2006-11-08 07:12:03 +00:00
$link = $sk -> makeKnownLinkObj ( $nt , $text , $query , $inside , $prefix );
2005-12-29 00:31:18 +00:00
return $this -> armorLinks ( $link ) . $trail ;
}
2006-01-07 13:31:29 +00:00
2005-12-28 22:58:54 +00:00
/**
* Insert a NOPARSE hacky thing into any inline links in a chunk that ' s
* going to go through further parsing steps before inline URL expansion .
*
* In particular this is important when using action = render , which causes
* full URLs to be included .
*
* Oh man I hate our multi - layer parser !
*
* @ param string more - or - less HTML
* @ return string less - or - more HTML with NOPARSE bits
*/
function armorLinks ( $text ) {
2006-11-23 08:25:56 +00:00
return preg_replace ( '/\b(' . wfUrlProtocols () . ')/' ,
2005-12-28 22:58:54 +00:00
" { $this -> mUniqPrefix } NOPARSE $ 1 " , $text );
}
2005-04-27 07:48:14 +00:00
2004-11-28 03:29:50 +00:00
/**
* Return true if subpage links should be expanded on this page .
* @ return bool
*/
function areSubpagesAllowed () {
# Some namespaces don't allow subpages
global $wgNamespacesWithSubpages ;
return ! empty ( $wgNamespacesWithSubpages [ $this -> mTitle -> getNamespace ()]);
}
2005-07-03 07:15:53 +00:00
2004-09-25 20:13:14 +00:00
/**
* Handle link to subpage if necessary
2004-10-15 17:39:10 +00:00
* @ param string $target the source of the link
* @ param string & $text the link text , modified as necessary
2004-09-25 20:13:14 +00:00
* @ return string the full name of the link
2006-04-19 15:46:24 +00:00
* @ private
2004-09-25 20:13:14 +00:00
*/
2004-09-25 20:35:38 +00:00
function maybeDoSubpageLink ( $target , & $text ) {
2004-09-25 20:13:14 +00:00
# Valid link forms:
# Foobar -- normal
# :Foobar -- override special treatment of prefix (images, language links)
# /Foobar -- convert to CurrentPage/Foobar
# /Foobar/ -- convert to CurrentPage/Foobar, strip the initial / from text
2004-12-07 01:23:32 +00:00
# ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
# ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage
2004-09-25 20:13:14 +00:00
2004-09-27 04:44:00 +00:00
$fname = 'Parser::maybeDoSubpageLink' ;
wfProfileIn ( $fname );
2004-12-07 01:23:32 +00:00
$ret = $target ; # default return value is no change
2005-07-03 07:15:53 +00:00
# Some namespaces don't allow subpages,
2004-12-07 01:23:32 +00:00
# so only perform processing if subpages are allowed
2005-07-03 07:15:53 +00:00
if ( $this -> areSubpagesAllowed () ) {
2007-07-12 21:02:25 +00:00
$hash = strpos ( $target , '#' );
if ( $hash !== false ) {
$suffix = substr ( $target , $hash );
$target = substr ( $target , 0 , $hash );
} else {
$suffix = '' ;
}
# bug 7425
$target = trim ( $target );
2004-12-07 01:23:32 +00:00
# Look at the first character
if ( $target != '' && $target { 0 } == '/' ) {
# / at end means we don't want the slash to be shown
2007-05-08 09:09:46 +00:00
$m = array ();
2007-01-21 03:47:38 +00:00
$trailingSlashes = preg_match_all ( '%(/+)$%' , $target , $m );
if ( $trailingSlashes ) {
$noslash = $target = substr ( $target , 1 , - strlen ( $m [ 0 ][ 0 ]) );
2004-12-07 01:23:32 +00:00
} else {
$noslash = substr ( $target , 1 );
}
2005-07-03 07:15:53 +00:00
2007-07-12 21:02:25 +00:00
$ret = $this -> mTitle -> getPrefixedText () . '/' . trim ( $noslash ) . $suffix ;
2004-09-25 20:13:14 +00:00
if ( '' === $text ) {
2007-07-12 21:02:25 +00:00
$text = $target . $suffix ;
2004-09-25 20:13:14 +00:00
} # this might be changed for ugliness reasons
} else {
2004-12-07 01:23:32 +00:00
# check for .. subpage backlinks
$dotdotcount = 0 ;
$nodotdot = $target ;
while ( strncmp ( $nodotdot , " ../ " , 3 ) == 0 ) {
++ $dotdotcount ;
$nodotdot = substr ( $nodotdot , 3 );
}
if ( $dotdotcount > 0 ) {
$exploded = explode ( '/' , $this -> mTitle -> GetPrefixedText () );
if ( count ( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
$ret = implode ( '/' , array_slice ( $exploded , 0 , - $dotdotcount ) );
# / at the end means don't show full path
if ( substr ( $nodotdot , - 1 , 1 ) == '/' ) {
$nodotdot = substr ( $nodotdot , 0 , - 1 );
if ( '' === $text ) {
2007-07-12 21:02:25 +00:00
$text = $nodotdot . $suffix ;
2004-12-07 01:23:32 +00:00
}
}
$nodotdot = trim ( $nodotdot );
if ( $nodotdot != '' ) {
$ret .= '/' . $nodotdot ;
}
2007-07-12 21:02:25 +00:00
$ret .= $suffix ;
2004-12-07 01:23:32 +00:00
}
}
2004-09-25 20:13:14 +00:00
}
}
2004-09-27 04:44:00 +00:00
wfProfileOut ( $fname );
2004-09-25 20:13:14 +00:00
return $ret ;
}
2004-09-21 05:49:12 +00:00
/** #@+
* Used by doBlockLevels ()
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2004-06-12 06:15:09 +00:00
/* private */ function closeParagraph () {
2004-06-08 18:11:28 +00:00
$result = '' ;
2004-04-04 01:45:02 +00:00
if ( '' != $this -> mLastSection ) {
2004-06-08 18:11:28 +00:00
$result = '</' . $this -> mLastSection . " > \n " ;
2004-02-26 13:37:26 +00:00
}
2004-04-15 14:25:34 +00:00
$this -> mInPre = false ;
2004-06-08 18:11:28 +00:00
$this -> mLastSection = '' ;
2004-04-09 13:40:50 +00:00
return $result ;
2004-02-26 13:37:26 +00:00
}
# getCommon() returns the length of the longest common substring
# of both arguments, starting at the beginning of both.
#
2004-06-12 06:15:09 +00:00
/* private */ function getCommon ( $st1 , $st2 ) {
2004-02-26 13:37:26 +00:00
$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.
#
2004-08-16 15:23:46 +00:00
/* private */ function openList ( $char ) {
2004-02-26 13:37:26 +00:00
$result = $this -> closeParagraph ();
2004-06-08 18:11:28 +00:00
if ( '*' == $char ) { $result .= '<ul><li>' ; }
else if ( '#' == $char ) { $result .= '<ol><li>' ; }
else if ( ':' == $char ) { $result .= '<dl><dd>' ; }
else if ( ';' == $char ) {
$result .= '<dl><dt>' ;
2004-02-26 13:37:26 +00:00
$this -> mDTopen = true ;
}
2004-06-08 18:11:28 +00:00
else { $result = '<!-- ERR 1 -->' ; }
2004-02-26 13:37:26 +00:00
return $result ;
}
2004-06-12 06:15:09 +00:00
/* private */ function nextItem ( $char ) {
2004-06-08 18:11:28 +00:00
if ( '*' == $char || '#' == $char ) { return '</li><li>' ; }
else if ( ':' == $char || ';' == $char ) {
2004-08-16 15:29:17 +00:00
$close = '</dd>' ;
2004-06-08 18:11:28 +00:00
if ( $this -> mDTopen ) { $close = '</dt>' ; }
if ( ';' == $char ) {
2004-02-26 13:37:26 +00:00
$this -> mDTopen = true ;
2004-06-08 18:11:28 +00:00
return $close . '<dt>' ;
2004-02-26 13:37:26 +00:00
} else {
$this -> mDTopen = false ;
2004-06-08 18:11:28 +00:00
return $close . '<dd>' ;
2004-02-26 13:37:26 +00:00
}
}
2004-06-08 18:11:28 +00:00
return '<!-- ERR 2 -->' ;
2004-02-26 13:37:26 +00:00
}
2004-08-16 20:01:21 +00:00
/* private */ function closeList ( $char ) {
2004-06-08 18:11:28 +00:00
if ( '*' == $char ) { $text = '</li></ul>' ; }
else if ( '#' == $char ) { $text = '</li></ol>' ; }
else if ( ':' == $char ) {
2004-02-26 13:37:26 +00:00
if ( $this -> mDTopen ) {
$this -> mDTopen = false ;
2004-06-08 18:11:28 +00:00
$text = '</dt></dl>' ;
2004-02-26 13:37:26 +00:00
} else {
2004-06-08 18:11:28 +00:00
$text = '</dd></dl>' ;
2004-02-26 13:37:26 +00:00
}
}
2004-06-08 18:11:28 +00:00
else { return '<!-- ERR 3 -->' ; }
2004-02-26 13:37:26 +00:00
return $text . " \n " ;
}
2004-09-21 05:49:12 +00:00
/**#@-*/
2004-02-26 13:37:26 +00:00
2004-09-21 05:49:12 +00:00
/**
* Make lists from lines starting with ':' , '*' , '#' , etc .
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
* @ return string the lists rendered as HTML
*/
function doBlockLevels ( $text , $linestart ) {
2004-06-08 18:11:28 +00:00
$fname = 'Parser::doBlockLevels' ;
2004-02-26 13:37:26 +00:00
wfProfileIn ( $fname );
2004-07-12 19:49:20 +00:00
2004-02-26 13:37:26 +00:00
# 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.
#
2004-04-29 06:16:21 +00:00
$textLines = explode ( " \n " , $text );
2004-04-11 16:46:06 +00:00
2005-04-21 06:30:48 +00:00
$lastPrefix = $output = '' ;
2004-04-11 16:46:06 +00:00
$this -> mDTopen = $inBlockElem = false ;
2004-04-29 06:16:21 +00:00
$prefixLength = 0 ;
$paragraphStack = false ;
if ( ! $linestart ) {
$output .= array_shift ( $textLines );
}
foreach ( $textLines as $oLine ) {
$lastPrefixLength = strlen ( $lastPrefix );
2004-08-16 15:29:17 +00:00
$preCloseMatch = preg_match ( '/<\\/pre/i' , $oLine );
$preOpenMatch = preg_match ( '/<pre/i' , $oLine );
2004-07-14 18:46:02 +00:00
if ( ! $this -> mInPre ) {
2004-04-29 06:16:21 +00:00
# Multiple prefixes may abut each other for nested lists.
2004-06-08 18:11:28 +00:00
$prefixLength = strspn ( $oLine , '*#:;' );
2004-04-29 06:16:21 +00:00
$pref = substr ( $oLine , 0 , $prefixLength );
2004-07-12 19:49:20 +00:00
2004-04-29 06:16:21 +00:00
# eh?
2004-06-08 18:11:28 +00:00
$pref2 = str_replace ( ';' , ':' , $pref );
2004-04-29 06:16:21 +00:00
$t = substr ( $oLine , $prefixLength );
2004-07-14 18:46:02 +00:00
$this -> mInPre = ! empty ( $preOpenMatch );
2004-04-16 11:21:51 +00:00
} else {
2004-04-29 06:16:21 +00:00
# Don't interpret any other prefixes in preformatted text
$prefixLength = 0 ;
2004-04-16 11:21:51 +00:00
$pref = $pref2 = '' ;
2004-04-29 06:16:21 +00:00
$t = $oLine ;
2004-04-16 11:21:51 +00:00
}
2004-04-29 06:16:21 +00:00
# List generation
if ( $prefixLength && 0 == strcmp ( $lastPrefix , $pref2 ) ) {
# Same as the last item, so no need to deal with nesting or opening stuff
$output .= $this -> nextItem ( substr ( $pref , - 1 ) );
$paragraphStack = false ;
2004-04-16 11:21:51 +00:00
2004-08-16 15:29:17 +00:00
if ( substr ( $pref , - 1 ) == ';' ) {
2004-04-29 06:16:21 +00:00
# The one nasty exception: definition lists work like this:
# ; title : definition text
# So we check for : in the remainder text to split up the
# title and definition, without b0rking links.
2005-04-21 06:30:48 +00:00
$term = $t2 = '' ;
2004-09-27 21:01:39 +00:00
if ( $this -> findColonNoLinks ( $t , $term , $t2 ) !== false ) {
$t = $t2 ;
2004-06-08 18:11:28 +00:00
$output .= $term . $this -> nextItem ( ':' );
2004-04-16 11:21:51 +00:00
}
}
2004-04-29 06:16:21 +00:00
} elseif ( $prefixLength || $lastPrefixLength ) {
# Either open or close a level...
$commonPrefixLength = $this -> getCommon ( $pref , $lastPrefix );
$paragraphStack = false ;
while ( $commonPrefixLength < $lastPrefixLength ) {
$output .= $this -> closeList ( $lastPrefix { $lastPrefixLength - 1 } );
-- $lastPrefixLength ;
2004-04-16 11:21:51 +00:00
}
2004-04-29 06:16:21 +00:00
if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) {
$output .= $this -> nextItem ( $pref { $commonPrefixLength - 1 } );
2004-04-16 11:21:51 +00:00
}
2004-04-29 06:16:21 +00:00
while ( $prefixLength > $commonPrefixLength ) {
$char = substr ( $pref , $commonPrefixLength , 1 );
$output .= $this -> openList ( $char );
2004-04-16 11:21:51 +00:00
2004-06-08 18:11:28 +00:00
if ( ';' == $char ) {
2004-04-29 06:16:21 +00:00
# FIXME: This is dupe of code above
2004-09-27 21:01:39 +00:00
if ( $this -> findColonNoLinks ( $t , $term , $t2 ) !== false ) {
$t = $t2 ;
2004-08-22 17:24:50 +00:00
$output .= $term . $this -> nextItem ( ':' );
2004-02-26 13:37:26 +00:00
}
}
2004-04-29 06:16:21 +00:00
++ $commonPrefixLength ;
2004-02-26 13:37:26 +00:00
}
2004-04-29 06:16:21 +00:00
$lastPrefix = $pref2 ;
2004-02-26 13:37:26 +00:00
}
2004-04-29 06:16:21 +00:00
if ( 0 == $prefixLength ) {
2004-11-26 10:49:46 +00:00
wfProfileIn ( " $fname -paragraph " );
2004-04-29 06:16:21 +00:00
# No prefix (not in list)--go to paragraph mode
2004-04-09 13:40:50 +00:00
// XXX: use a stack for nestable elements like span, table and div
2007-04-13 00:23:49 +00:00
$openmatch = preg_match ( '/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS' , $t );
2004-04-12 23:59:37 +00:00
$closematch = preg_match (
2007-04-13 00:23:49 +00:00
'/(?:<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|' .
2006-12-17 16:51:01 +00:00
'<td|<th|<\\/?div|<hr|<\\/pre|<\\/p|' . $this -> mUniqPrefix . '-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS' , $t );
2004-04-09 13:40:50 +00:00
if ( $openmatch or $closematch ) {
2004-04-29 06:16:21 +00:00
$paragraphStack = false ;
2006-04-28 18:56:37 +00:00
# TODO bug 5718: paragraph closed
2004-04-29 06:16:21 +00:00
$output .= $this -> closeParagraph ();
2005-07-22 22:14:05 +00:00
if ( $preOpenMatch and ! $preCloseMatch ) {
$this -> mInPre = true ;
}
2004-08-16 15:23:46 +00:00
if ( $closematch ) {
2004-04-09 13:40:50 +00:00
$inBlockElem = false ;
2004-04-09 14:55:12 +00:00
} else {
$inBlockElem = true ;
2004-04-09 13:40:50 +00:00
}
2004-06-02 12:29:15 +00:00
} else if ( ! $inBlockElem && ! $this -> mInPre ) {
2004-08-22 17:24:50 +00:00
if ( ' ' == $t { 0 } and ( $this -> mLastSection == 'pre' or trim ( $t ) != '' ) ) {
2004-04-10 15:11:14 +00:00
// pre
2004-04-10 03:09:52 +00:00
if ( $this -> mLastSection != 'pre' ) {
2004-04-29 06:16:21 +00:00
$paragraphStack = false ;
$output .= $this -> closeParagraph () . '<pre>' ;
2004-04-10 15:11:14 +00:00
$this -> mLastSection = 'pre' ;
2004-04-10 03:09:52 +00:00
}
2004-09-12 13:07:52 +00:00
$t = substr ( $t , 1 );
2004-04-12 23:59:37 +00:00
} else {
2004-04-10 15:11:14 +00:00
// paragraph
if ( '' == trim ( $t ) ) {
2004-04-29 06:16:21 +00:00
if ( $paragraphStack ) {
2004-05-31 00:58:57 +00:00
$output .= $paragraphStack . '<br />' ;
2004-04-29 06:16:21 +00:00
$paragraphStack = false ;
2004-04-10 15:11:14 +00:00
$this -> mLastSection = 'p' ;
} else {
if ( $this -> mLastSection != 'p' ) {
2004-04-29 06:16:21 +00:00
$output .= $this -> closeParagraph ();
2004-04-10 15:11:14 +00:00
$this -> mLastSection = '' ;
2004-06-08 18:11:28 +00:00
$paragraphStack = '<p>' ;
2004-04-10 15:11:14 +00:00
} else {
2004-04-29 06:16:21 +00:00
$paragraphStack = '</p><p>' ;
2004-04-10 15:11:14 +00:00
}
}
} else {
2004-04-29 06:16:21 +00:00
if ( $paragraphStack ) {
$output .= $paragraphStack ;
$paragraphStack = false ;
2004-04-10 15:11:14 +00:00
$this -> mLastSection = 'p' ;
} else if ( $this -> mLastSection != 'p' ) {
2004-04-29 06:16:21 +00:00
$output .= $this -> closeParagraph () . '<p>' ;
2004-04-10 15:11:14 +00:00
$this -> mLastSection = 'p' ;
}
}
2004-04-09 13:40:50 +00:00
}
2004-04-12 23:59:37 +00:00
}
2004-11-26 10:49:46 +00:00
wfProfileOut ( " $fname -paragraph " );
2004-02-26 13:37:26 +00:00
}
2005-07-13 15:17:28 +00:00
// somewhere above we forget to get out of pre block (bug 785)
if ( $preCloseMatch && $this -> mInPre ) {
$this -> mInPre = false ;
}
2004-04-29 06:16:21 +00:00
if ( $paragraphStack === false ) {
$output .= $t . " \n " ;
2004-04-10 15:11:14 +00:00
}
2004-02-26 13:37:26 +00:00
}
2004-04-29 06:16:21 +00:00
while ( $prefixLength ) {
$output .= $this -> closeList ( $pref2 { $prefixLength - 1 } );
-- $prefixLength ;
2004-02-26 13:37:26 +00:00
}
2004-06-08 18:11:28 +00:00
if ( '' != $this -> mLastSection ) {
$output .= '</' . $this -> mLastSection . '>' ;
$this -> mLastSection = '' ;
2004-02-26 13:37:26 +00:00
}
2004-04-12 23:59:37 +00:00
2004-02-26 13:37:26 +00:00
wfProfileOut ( $fname );
2004-04-29 06:16:21 +00:00
return $output ;
2004-02-26 13:37:26 +00:00
}
2004-09-27 21:01:39 +00:00
/**
2006-06-02 20:54:34 +00:00
* Split up a string on ':' , ignoring any occurences inside tags
* to prevent illegal overlapping .
2004-10-15 17:39:10 +00:00
* @ param string $str the string to split
* @ param string & $before set to everything before the ':'
* @ param string & $after set to everything after the ':'
2004-09-27 21:01:39 +00:00
* return string the position of the ':' , or false if none found
*/
function findColonNoLinks ( $str , & $before , & $after ) {
$fname = 'Parser::findColonNoLinks' ;
wfProfileIn ( $fname );
2006-07-11 17:40:11 +00:00
2006-06-02 20:54:34 +00:00
$pos = strpos ( $str , ':' );
if ( $pos === false ) {
// Nothing to find!
wfProfileOut ( $fname );
return false ;
}
2006-07-11 17:40:11 +00:00
2006-06-02 23:56:19 +00:00
$lt = strpos ( $str , '<' );
if ( $lt === false || $lt > $pos ) {
2006-06-02 20:54:34 +00:00
// Easy; no tag nesting to worry about
$before = substr ( $str , 0 , $pos );
$after = substr ( $str , $pos + 1 );
wfProfileOut ( $fname );
return $pos ;
}
2006-07-11 17:40:11 +00:00
2006-06-02 20:54:34 +00:00
// Ugly state machine to walk through avoiding tags.
$state = MW_COLON_STATE_TEXT ;
$stack = 0 ;
$len = strlen ( $str );
for ( $i = 0 ; $i < $len ; $i ++ ) {
$c = $str { $i };
2006-07-11 17:40:11 +00:00
2006-06-02 20:54:34 +00:00
switch ( $state ) {
// (Using the number is a performance hack for common cases)
case 0 : // MW_COLON_STATE_TEXT:
switch ( $c ) {
case " < " :
// Could be either a <start> tag or an </end> tag
$state = MW_COLON_STATE_TAGSTART ;
break ;
case " : " :
if ( $stack == 0 ) {
// We found it!
$before = substr ( $str , 0 , $i );
$after = substr ( $str , $i + 1 );
wfProfileOut ( $fname );
return $i ;
}
// Embedded in a tag; don't break it.
break ;
default :
2006-06-02 23:56:19 +00:00
// Skip ahead looking for something interesting
$colon = strpos ( $str , ':' , $i );
if ( $colon === false ) {
// Nothing else interesting
wfProfileOut ( $fname );
return false ;
}
$lt = strpos ( $str , '<' , $i );
if ( $stack === 0 ) {
if ( $lt === false || $colon < $lt ) {
// We found it!
$before = substr ( $str , 0 , $colon );
$after = substr ( $str , $colon + 1 );
wfProfileOut ( $fname );
return $i ;
}
}
if ( $lt === false ) {
// Nothing else interesting to find; abort!
// We're nested, but there's no close tags left. Abort!
break 2 ;
}
// Skip ahead to next tag start
$i = $lt ;
$state = MW_COLON_STATE_TAGSTART ;
2006-06-02 20:54:34 +00:00
}
break ;
case 1 : // MW_COLON_STATE_TAG:
// In a <tag>
switch ( $c ) {
case " > " :
$stack ++ ;
$state = MW_COLON_STATE_TEXT ;
break ;
case " / " :
// Slash may be followed by >?
$state = MW_COLON_STATE_TAGSLASH ;
break ;
default :
// ignore
}
break ;
case 2 : // MW_COLON_STATE_TAGSTART:
switch ( $c ) {
case " / " :
$state = MW_COLON_STATE_CLOSETAG ;
2004-09-27 21:01:39 +00:00
break ;
2006-06-02 20:54:34 +00:00
case " ! " :
$state = MW_COLON_STATE_COMMENT ;
break ;
case " > " :
// Illegal early close? This shouldn't happen D:
$state = MW_COLON_STATE_TEXT ;
break ;
default :
$state = MW_COLON_STATE_TAG ;
}
break ;
case 3 : // MW_COLON_STATE_CLOSETAG:
// In a </tag>
if ( $c == " > " ) {
$stack -- ;
if ( $stack < 0 ) {
wfDebug ( " Invalid input in $fname ; too many close tags \n " );
wfProfileOut ( $fname );
return false ;
}
$state = MW_COLON_STATE_TEXT ;
}
break ;
case MW_COLON_STATE_TAGSLASH :
if ( $c == " > " ) {
// Yes, a self-closed tag <blah/>
$state = MW_COLON_STATE_TEXT ;
} else {
// Probably we're jumping the gun, and this is an attribute
$state = MW_COLON_STATE_TAG ;
}
break ;
case 5 : // MW_COLON_STATE_COMMENT:
if ( $c == " - " ) {
$state = MW_COLON_STATE_COMMENTDASH ;
}
break ;
case MW_COLON_STATE_COMMENTDASH :
if ( $c == " - " ) {
$state = MW_COLON_STATE_COMMENTDASHDASH ;
} else {
$state = MW_COLON_STATE_COMMENT ;
}
break ;
case MW_COLON_STATE_COMMENTDASHDASH :
if ( $c == " > " ) {
$state = MW_COLON_STATE_TEXT ;
} else {
$state = MW_COLON_STATE_COMMENT ;
2004-09-27 21:01:39 +00:00
}
2006-06-02 20:54:34 +00:00
break ;
default :
2006-06-07 06:40:24 +00:00
throw new MWException ( " State machine error in $fname " );
2004-09-27 21:01:39 +00:00
}
2006-06-02 20:54:34 +00:00
}
if ( $stack > 0 ) {
wfDebug ( " Invalid input in $fname ; not enough close tags (stack $stack , state $state ) \n " );
return false ;
}
2004-09-27 21:01:39 +00:00
wfProfileOut ( $fname );
2006-06-02 20:54:34 +00:00
return false ;
2004-09-27 21:01:39 +00:00
}
2004-09-21 05:49:12 +00:00
/**
* Return value of a magic variable ( like PAGENAME )
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2004-03-20 15:03:26 +00:00
function getVariableValue ( $index ) {
2005-11-27 06:04:41 +00:00
global $wgContLang , $wgSitename , $wgServer , $wgServerName , $wgScriptPath ;
2005-07-03 07:15:53 +00:00
2004-11-21 14:07:24 +00:00
/**
* Some of these require message or data lookups and can be
* expensive to check many times .
*/
static $varCache = array ();
2006-10-17 08:49:27 +00:00
if ( wfRunHooks ( 'ParserGetVariableValueVarCache' , array ( & $this , & $varCache ) ) ) {
if ( isset ( $varCache [ $index ] ) ) {
2005-11-26 23:04:05 +00:00
return $varCache [ $index ];
2006-10-17 08:49:27 +00:00
}
}
2005-11-26 23:04:05 +00:00
$ts = time ();
wfRunHooks ( 'ParserGetVariableValueTs' , array ( & $this , & $ts ) );
2005-07-03 07:15:53 +00:00
2006-08-23 16:45:49 +00:00
# Use the time zone
global $wgLocaltimezone ;
if ( isset ( $wgLocaltimezone ) ) {
$oldtz = getenv ( 'TZ' );
putenv ( 'TZ=' . $wgLocaltimezone );
}
2007-06-20 18:41:15 +00:00
2007-06-20 22:25:39 +00:00
wfSuppressWarnings (); // E_STRICT system time bitching
2006-08-23 16:45:49 +00:00
$localTimestamp = date ( 'YmdHis' , $ts );
$localMonth = date ( 'm' , $ts );
$localMonthName = date ( 'n' , $ts );
$localDay = date ( 'j' , $ts );
$localDay2 = date ( 'd' , $ts );
$localDayOfWeek = date ( 'w' , $ts );
$localWeek = date ( 'W' , $ts );
$localYear = date ( 'Y' , $ts );
$localHour = date ( 'H' , $ts );
if ( isset ( $wgLocaltimezone ) ) {
putenv ( 'TZ=' . $oldtz );
}
2007-06-20 22:25:39 +00:00
wfRestoreWarnings ();
2006-08-23 16:45:49 +00:00
2004-03-20 15:03:26 +00:00
switch ( $index ) {
2006-07-14 15:39:23 +00:00
case 'currentmonth' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( gmdate ( 'm' , $ts ) );
2006-07-14 15:39:23 +00:00
case 'currentmonthname' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> getMonthName ( gmdate ( 'n' , $ts ) );
2006-07-14 15:39:23 +00:00
case 'currentmonthnamegen' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> getMonthNameGen ( gmdate ( 'n' , $ts ) );
2006-07-14 15:39:23 +00:00
case 'currentmonthabbrev' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> getMonthAbbreviation ( gmdate ( 'n' , $ts ) );
2006-07-14 15:39:23 +00:00
case 'currentday' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( gmdate ( 'j' , $ts ) );
2006-07-14 15:39:23 +00:00
case 'currentday2' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( gmdate ( 'd' , $ts ) );
2006-08-23 16:45:49 +00:00
case 'localmonth' :
return $varCache [ $index ] = $wgContLang -> formatNum ( $localMonth );
case 'localmonthname' :
return $varCache [ $index ] = $wgContLang -> getMonthName ( $localMonthName );
case 'localmonthnamegen' :
return $varCache [ $index ] = $wgContLang -> getMonthNameGen ( $localMonthName );
case 'localmonthabbrev' :
return $varCache [ $index ] = $wgContLang -> getMonthAbbreviation ( $localMonthName );
case 'localday' :
return $varCache [ $index ] = $wgContLang -> formatNum ( $localDay );
case 'localday2' :
return $varCache [ $index ] = $wgContLang -> formatNum ( $localDay2 );
2006-07-14 15:39:23 +00:00
case 'pagename' :
2007-06-18 13:34:07 +00:00
return wfEscapeWikiText ( $this -> mTitle -> getText () );
2006-07-14 15:39:23 +00:00
case 'pagenamee' :
2004-08-12 13:32:04 +00:00
return $this -> mTitle -> getPartialURL ();
2006-07-14 15:39:23 +00:00
case 'fullpagename' :
2007-06-18 13:34:07 +00:00
return wfEscapeWikiText ( $this -> mTitle -> getPrefixedText () );
2006-07-14 15:39:23 +00:00
case 'fullpagenamee' :
2006-01-06 21:03:50 +00:00
return $this -> mTitle -> getPrefixedURL ();
2006-07-14 15:39:23 +00:00
case 'subpagename' :
2007-06-18 13:34:07 +00:00
return wfEscapeWikiText ( $this -> mTitle -> getSubpageText () );
2006-07-14 15:39:23 +00:00
case 'subpagenamee' :
2006-04-02 16:19:29 +00:00
return $this -> mTitle -> getSubpageUrlForm ();
2006-07-14 15:39:23 +00:00
case 'basepagename' :
2007-06-18 13:34:07 +00:00
return wfEscapeWikiText ( $this -> mTitle -> getBaseText () );
2006-07-14 15:39:23 +00:00
case 'basepagenamee' :
2006-05-14 03:51:36 +00:00
return wfUrlEncode ( str_replace ( ' ' , '_' , $this -> mTitle -> getBaseText () ) );
2006-07-14 15:39:23 +00:00
case 'talkpagename' :
2006-04-12 15:38:17 +00:00
if ( $this -> mTitle -> canTalk () ) {
$talkPage = $this -> mTitle -> getTalkPage ();
2007-06-18 13:34:07 +00:00
return wfEscapeWikiText ( $talkPage -> getPrefixedText () );
2006-04-12 15:38:17 +00:00
} else {
return '' ;
}
2006-07-14 15:39:23 +00:00
case 'talkpagenamee' :
2006-04-12 15:38:17 +00:00
if ( $this -> mTitle -> canTalk () ) {
$talkPage = $this -> mTitle -> getTalkPage ();
return $talkPage -> getPrefixedUrl ();
} else {
return '' ;
}
2006-07-14 15:39:23 +00:00
case 'subjectpagename' :
2006-04-12 15:38:17 +00:00
$subjPage = $this -> mTitle -> getSubjectPage ();
2007-06-18 13:34:07 +00:00
return wfEscapeWikiText ( $subjPage -> getPrefixedText () );
2006-07-14 15:39:23 +00:00
case 'subjectpagenamee' :
2006-04-12 15:38:17 +00:00
$subjPage = $this -> mTitle -> getSubjectPage ();
return $subjPage -> getPrefixedUrl ();
2006-07-14 15:39:23 +00:00
case 'revisionid' :
2005-11-27 06:04:41 +00:00
return $this -> mRevisionId ;
2006-09-26 17:20:36 +00:00
case 'revisionday' :
2006-11-21 09:53:45 +00:00
return intval ( substr ( $this -> getRevisionTimestamp (), 6 , 2 ) );
2006-09-26 17:20:36 +00:00
case 'revisionday2' :
2006-11-21 09:53:45 +00:00
return substr ( $this -> getRevisionTimestamp (), 6 , 2 );
2006-09-26 17:20:36 +00:00
case 'revisionmonth' :
2006-11-21 09:53:45 +00:00
return intval ( substr ( $this -> getRevisionTimestamp (), 4 , 2 ) );
2006-09-26 17:20:36 +00:00
case 'revisionyear' :
2006-11-21 09:53:45 +00:00
return substr ( $this -> getRevisionTimestamp (), 0 , 4 );
2006-09-26 17:20:36 +00:00
case 'revisiontimestamp' :
2006-11-21 09:53:45 +00:00
return $this -> getRevisionTimestamp ();
2006-07-14 15:39:23 +00:00
case 'namespace' :
2006-05-24 19:55:48 +00:00
return str_replace ( '_' , ' ' , $wgContLang -> getNsText ( $this -> mTitle -> getNamespace () ) );
2006-07-14 15:39:23 +00:00
case 'namespacee' :
2005-11-21 17:50:10 +00:00
return wfUrlencode ( $wgContLang -> getNsText ( $this -> mTitle -> getNamespace () ) );
2006-07-14 15:39:23 +00:00
case 'talkspace' :
2006-05-24 19:55:48 +00:00
return $this -> mTitle -> canTalk () ? str_replace ( '_' , ' ' , $this -> mTitle -> getTalkNsText ()) : '' ;
2006-07-14 15:39:23 +00:00
case 'talkspacee' :
2006-04-12 15:38:17 +00:00
return $this -> mTitle -> canTalk () ? wfUrlencode ( $this -> mTitle -> getTalkNsText () ) : '' ;
2006-07-14 15:39:23 +00:00
case 'subjectspace' :
2006-04-12 15:38:17 +00:00
return $this -> mTitle -> getSubjectNsText ();
2006-07-14 15:39:23 +00:00
case 'subjectspacee' :
2006-04-12 15:38:17 +00:00
return ( wfUrlencode ( $this -> mTitle -> getSubjectNsText () ) );
2006-07-14 15:39:23 +00:00
case 'currentdayname' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> getWeekdayName ( gmdate ( 'w' , $ts ) + 1 );
2006-07-14 15:39:23 +00:00
case 'currentyear' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( gmdate ( 'Y' , $ts ), true );
2006-07-14 15:39:23 +00:00
case 'currenttime' :
2006-06-20 21:03:47 +00:00
return $varCache [ $index ] = $wgContLang -> time ( wfTimestamp ( TS_MW , $ts ), false , false );
2006-08-14 22:43:50 +00:00
case 'currenthour' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( gmdate ( 'H' , $ts ), true );
2006-07-14 15:39:23 +00:00
case 'currentweek' :
2006-01-13 16:10:12 +00:00
// @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
// int to remove the padding
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( ( int ) gmdate ( 'W' , $ts ) );
2006-07-14 15:39:23 +00:00
case 'currentdow' :
2007-06-15 19:07:15 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( gmdate ( 'w' , $ts ) );
2006-08-23 16:45:49 +00:00
case 'localdayname' :
return $varCache [ $index ] = $wgContLang -> getWeekdayName ( $localDayOfWeek + 1 );
case 'localyear' :
return $varCache [ $index ] = $wgContLang -> formatNum ( $localYear , true );
case 'localtime' :
return $varCache [ $index ] = $wgContLang -> time ( $localTimestamp , false , false );
case 'localhour' :
return $varCache [ $index ] = $wgContLang -> formatNum ( $localHour , true );
case 'localweek' :
// @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
// int to remove the padding
return $varCache [ $index ] = $wgContLang -> formatNum ( ( int ) $localWeek );
case 'localdow' :
return $varCache [ $index ] = $wgContLang -> formatNum ( $localDayOfWeek );
2006-07-14 15:39:23 +00:00
case 'numberofarticles' :
2006-11-21 09:53:45 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( SiteStats :: articles () );
2006-07-14 15:39:23 +00:00
case 'numberoffiles' :
2006-11-21 09:53:45 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( SiteStats :: images () );
2006-07-14 15:39:23 +00:00
case 'numberofusers' :
2006-11-21 09:53:45 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( SiteStats :: users () );
2006-07-14 15:39:23 +00:00
case 'numberofpages' :
2006-11-21 09:53:45 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( SiteStats :: pages () );
2006-07-14 15:39:23 +00:00
case 'numberofadmins' :
2006-11-21 09:53:45 +00:00
return $varCache [ $index ] = $wgContLang -> formatNum ( SiteStats :: admins () );
2007-04-17 09:23:31 +00:00
case 'numberofedits' :
return $varCache [ $index ] = $wgContLang -> formatNum ( SiteStats :: edits () );
2006-07-14 15:39:23 +00:00
case 'currenttimestamp' :
2006-06-20 21:03:47 +00:00
return $varCache [ $index ] = wfTimestampNow ();
2006-08-23 16:45:49 +00:00
case 'localtimestamp' :
return $varCache [ $index ] = $localTimestamp ;
2006-07-14 15:39:23 +00:00
case 'currentversion' :
2006-08-30 08:03:10 +00:00
return $varCache [ $index ] = SpecialVersion :: getVersion ();
2006-07-14 15:39:23 +00:00
case 'sitename' :
2004-04-05 10:38:40 +00:00
return $wgSitename ;
2006-07-14 15:39:23 +00:00
case 'server' :
2004-04-05 10:38:40 +00:00
return $wgServer ;
2006-07-14 15:39:23 +00:00
case 'servername' :
2005-06-24 02:54:52 +00:00
return $wgServerName ;
2006-07-14 15:39:23 +00:00
case 'scriptpath' :
2005-06-19 16:09:00 +00:00
return $wgScriptPath ;
2006-07-14 15:39:23 +00:00
case 'directionmark' :
2006-05-28 21:30:43 +00:00
return $wgContLang -> getDirMark ();
2006-07-14 15:39:23 +00:00
case 'contentlanguage' :
2007-04-27 15:12:22 +00:00
global $wgContLanguageCode ;
return $wgContLanguageCode ;
2004-03-20 15:03:26 +00:00
default :
2005-11-25 06:53:20 +00:00
$ret = null ;
if ( wfRunHooks ( 'ParserGetVariableValueSwitch' , array ( & $this , & $varCache , & $index , & $ret ) ) )
return $ret ;
else
return null ;
2004-03-20 15:03:26 +00:00
}
}
2004-09-21 05:49:12 +00:00
/**
* initialise the magic variables ( like CURRENTMONTHNAME )
*
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2004-06-12 06:15:09 +00:00
function initialiseVariables () {
2004-09-27 04:44:00 +00:00
$fname = 'Parser::initialiseVariables' ;
2004-09-26 17:59:08 +00:00
wfProfileIn ( $fname );
2006-07-14 16:36:35 +00:00
$variableIDs = MagicWord :: getVariableIDs ();
2006-07-03 11:07:00 +00:00
2004-03-20 15:03:26 +00:00
$this -> mVariables = array ();
2006-07-14 16:36:35 +00:00
foreach ( $variableIDs as $id ) {
2004-03-20 15:03:26 +00:00
$mw =& MagicWord :: get ( $id );
2005-01-18 11:52:06 +00:00
$mw -> addToArray ( $this -> mVariables , $id );
2004-03-20 15:03:26 +00:00
}
2004-09-26 17:59:08 +00:00
wfProfileOut ( $fname );
2004-03-20 15:03:26 +00:00
}
2005-10-19 06:24:30 +00:00
/**
* parse any parentheses in format (( title | part | part ))
* and call callbacks to get a replacement text for any found piece
*
* @ param string $text The text to parse
* @ param array $callbacks rules in form :
* '{' => array ( # opening parentheses
* 'end' => '}' , # closing parentheses
* 'cb' => array ( 2 => callback , # replacement callback to call if {{..}} is found
2006-08-06 14:01:47 +00:00
* 3 => callback # replacement callback to call if {{{..}}} is found
2005-10-19 06:24:30 +00:00
* )
* )
2006-08-06 14:01:47 +00:00
* 'min' => 2 , # Minimum parenthesis count in cb
* 'max' => 3 , # Maximum parenthesis count in cb
2006-04-19 15:46:24 +00:00
* @ private
2005-10-19 06:24:30 +00:00
*/
function replace_callback ( $text , $callbacks ) {
2006-08-06 14:01:47 +00:00
wfProfileIn ( __METHOD__ );
2005-10-19 06:24:30 +00:00
$openingBraceStack = array (); # this array will hold a stack of parentheses which are not closed yet
$lastOpeningBrace = - 1 ; # last not closed parentheses
2006-08-06 14:01:47 +00:00
$validOpeningBraces = implode ( '' , array_keys ( $callbacks ) );
2006-10-17 08:49:27 +00:00
2006-08-06 14:01:47 +00:00
$i = 0 ;
while ( $i < strlen ( $text ) ) {
# Find next opening brace, closing brace or pipe
if ( $lastOpeningBrace == - 1 ) {
$currentClosing = '' ;
$search = $validOpeningBraces ;
} else {
$currentClosing = $openingBraceStack [ $lastOpeningBrace ][ 'braceEnd' ];
$search = $validOpeningBraces . '|' . $currentClosing ;
2005-10-19 06:24:30 +00:00
}
2006-08-06 14:01:47 +00:00
$rule = null ;
$i += strcspn ( $text , $search , $i );
if ( $i < strlen ( $text ) ) {
if ( $text [ $i ] == '|' ) {
$found = 'pipe' ;
} elseif ( $text [ $i ] == $currentClosing ) {
$found = 'close' ;
2006-10-04 08:43:09 +00:00
} elseif ( isset ( $callbacks [ $text [ $i ]] ) ) {
2006-08-06 14:01:47 +00:00
$found = 'open' ;
$rule = $callbacks [ $text [ $i ]];
2006-10-04 08:43:09 +00:00
} else {
# Some versions of PHP have a strcspn which stops on null characters
# Ignore and continue
++ $i ;
continue ;
2005-10-19 06:24:30 +00:00
}
2006-08-06 14:01:47 +00:00
} else {
# All done
2005-10-19 06:24:30 +00:00
break ;
2006-08-06 14:01:47 +00:00
}
2005-10-19 06:24:30 +00:00
2006-08-06 14:01:47 +00:00
if ( $found == 'open' ) {
# found opening brace, let's add it to parentheses stack
2005-10-19 06:24:30 +00:00
$piece = array ( 'brace' => $text [ $i ],
'braceEnd' => $rule [ 'end' ],
'title' => '' ,
'parts' => null );
2006-08-06 14:01:47 +00:00
# count opening brace characters
$piece [ 'count' ] = strspn ( $text , $piece [ 'brace' ], $i );
$piece [ 'startAt' ] = $piece [ 'partStart' ] = $i + $piece [ 'count' ];
$i += $piece [ 'count' ];
# we need to add to stack only if opening brace count is enough for one of the rules
if ( $piece [ 'count' ] >= $rule [ 'min' ] ) {
$lastOpeningBrace ++ ;
$openingBraceStack [ $lastOpeningBrace ] = $piece ;
}
} elseif ( $found == 'close' ) {
# lets check if it is enough characters for closing brace
$maxCount = $openingBraceStack [ $lastOpeningBrace ][ 'count' ];
$count = strspn ( $text , $text [ $i ], $i , $maxCount );
2006-10-17 08:49:27 +00:00
# check for maximum matching characters (if there are 5 closing
2006-08-06 14:01:47 +00:00
# characters, we will probably need only 3 - depending on the rules)
$matchingCount = 0 ;
$matchingCallback = null ;
$cbType = $callbacks [ $openingBraceStack [ $lastOpeningBrace ][ 'brace' ]];
if ( $count > $cbType [ 'max' ] ) {
2006-10-17 08:49:27 +00:00
# The specified maximum exists in the callback array, unless the caller
2006-08-06 14:01:47 +00:00
# has made an error
$matchingCount = $cbType [ 'max' ];
} else {
# Count is less than the maximum
# Skip any gaps in the callback array to find the true largest match
# Need to use array_key_exists not isset because the callback can be null
$matchingCount = $count ;
while ( $matchingCount > 0 && ! array_key_exists ( $matchingCount , $cbType [ 'cb' ] ) ) {
-- $matchingCount ;
2005-10-19 06:24:30 +00:00
}
}
2006-08-06 14:01:47 +00:00
if ( $matchingCount <= 0 ) {
$i += $count ;
continue ;
}
$matchingCallback = $cbType [ 'cb' ][ $matchingCount ];
2005-10-19 06:24:30 +00:00
2006-08-06 14:01:47 +00:00
# let's set a title or last part (if '|' was found)
if ( null === $openingBraceStack [ $lastOpeningBrace ][ 'parts' ]) {
2006-10-17 08:49:27 +00:00
$openingBraceStack [ $lastOpeningBrace ][ 'title' ] =
substr ( $text , $openingBraceStack [ $lastOpeningBrace ][ 'partStart' ],
2006-08-06 14:01:47 +00:00
$i - $openingBraceStack [ $lastOpeningBrace ][ 'partStart' ]);
} else {
2006-10-17 08:49:27 +00:00
$openingBraceStack [ $lastOpeningBrace ][ 'parts' ][] =
substr ( $text , $openingBraceStack [ $lastOpeningBrace ][ 'partStart' ],
2006-08-06 14:01:47 +00:00
$i - $openingBraceStack [ $lastOpeningBrace ][ 'partStart' ]);
}
$pieceStart = $openingBraceStack [ $lastOpeningBrace ][ 'startAt' ] - $matchingCount ;
$pieceEnd = $i + $matchingCount ;
if ( is_callable ( $matchingCallback ) ) {
$cbArgs = array (
'text' => substr ( $text , $pieceStart , $pieceEnd - $pieceStart ),
'title' => trim ( $openingBraceStack [ $lastOpeningBrace ][ 'title' ]),
'parts' => $openingBraceStack [ $lastOpeningBrace ][ 'parts' ],
'lineStart' => (( $pieceStart > 0 ) && ( $text [ $pieceStart - 1 ] == " \n " )),
);
# finally we can call a user callback and replace piece of text
$replaceWith = call_user_func ( $matchingCallback , $cbArgs );
$text = substr ( $text , 0 , $pieceStart ) . $replaceWith . substr ( $text , $pieceEnd );
$i = $pieceStart + strlen ( $replaceWith );
} else {
# null value for callback means that parentheses should be parsed, but not replaced
$i += $matchingCount ;
}
2005-10-19 06:24:30 +00:00
2006-08-06 14:01:47 +00:00
# reset last opening parentheses, but keep it in case there are unused characters
$piece = array ( 'brace' => $openingBraceStack [ $lastOpeningBrace ][ 'brace' ],
'braceEnd' => $openingBraceStack [ $lastOpeningBrace ][ 'braceEnd' ],
'count' => $openingBraceStack [ $lastOpeningBrace ][ 'count' ],
'title' => '' ,
'parts' => null ,
'startAt' => $openingBraceStack [ $lastOpeningBrace ][ 'startAt' ]);
$openingBraceStack [ $lastOpeningBrace -- ] = null ;
if ( $matchingCount < $piece [ 'count' ]) {
$piece [ 'count' ] -= $matchingCount ;
$piece [ 'startAt' ] -= $matchingCount ;
$piece [ 'partStart' ] = $piece [ 'startAt' ];
# do we still qualify for any callback with remaining count?
$currentCbList = $callbacks [ $piece [ 'brace' ]][ 'cb' ];
while ( $piece [ 'count' ] ) {
if ( array_key_exists ( $piece [ 'count' ], $currentCbList ) ) {
$lastOpeningBrace ++ ;
$openingBraceStack [ $lastOpeningBrace ] = $piece ;
break ;
2005-10-19 06:24:30 +00:00
}
2006-08-06 14:01:47 +00:00
-- $piece [ 'count' ];
2005-10-19 06:24:30 +00:00
}
}
2006-08-06 14:01:47 +00:00
} elseif ( $found == 'pipe' ) {
2005-10-19 06:24:30 +00:00
# lets set a title if it is a first separator, or next part otherwise
2006-08-06 14:01:47 +00:00
if ( null === $openingBraceStack [ $lastOpeningBrace ][ 'parts' ]) {
2006-10-17 08:49:27 +00:00
$openingBraceStack [ $lastOpeningBrace ][ 'title' ] =
substr ( $text , $openingBraceStack [ $lastOpeningBrace ][ 'partStart' ],
2006-08-06 14:01:47 +00:00
$i - $openingBraceStack [ $lastOpeningBrace ][ 'partStart' ]);
$openingBraceStack [ $lastOpeningBrace ][ 'parts' ] = array ();
} else {
2006-10-17 08:49:27 +00:00
$openingBraceStack [ $lastOpeningBrace ][ 'parts' ][] =
substr ( $text , $openingBraceStack [ $lastOpeningBrace ][ 'partStart' ],
2006-08-06 14:01:47 +00:00
$i - $openingBraceStack [ $lastOpeningBrace ][ 'partStart' ]);
2005-10-19 06:24:30 +00:00
}
2006-08-06 14:01:47 +00:00
$openingBraceStack [ $lastOpeningBrace ][ 'partStart' ] = ++ $i ;
2005-10-19 06:24:30 +00:00
}
}
2006-08-06 14:01:47 +00:00
wfProfileOut ( __METHOD__ );
2005-10-19 06:24:30 +00:00
return $text ;
}
2004-09-21 05:49:12 +00:00
/**
* Replace magic variables , templates , and template arguments
* with the appropriate text . Templates are substituted recursively ,
* taking care to avoid infinite loops .
*
* Note that the substitution depends on value of $mOutputType :
* OT_WIKI : only {{ subst : }} templates
* OT_MSG : only magic variables
* OT_HTML : all templates and magic variables
2005-07-03 07:15:53 +00:00
*
2004-09-21 05:49:12 +00:00
* @ param string $tex The text to transform
* @ param array $args Key - value pairs representing template parameters to substitute
2006-02-01 04:41:53 +00:00
* @ param bool $argsOnly Only do argument ( triple - brace ) expansion , not double - brace expansion
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2006-02-01 04:41:53 +00:00
function replaceVariables ( $text , $args = array (), $argsOnly = false ) {
2004-08-16 15:23:46 +00:00
# Prevent too big inclusions
2006-08-10 21:28:49 +00:00
if ( strlen ( $text ) > $this -> mOptions -> getMaxIncludeSize () ) {
2004-11-21 14:07:24 +00:00
return $text ;
}
2004-07-24 00:37:24 +00:00
2006-08-06 14:01:47 +00:00
$fname = __METHOD__ /*. '-L' . count( $this->mArgStack )*/ ;
2004-02-26 13:37:26 +00:00
wfProfileIn ( $fname );
2004-04-12 23:59:37 +00:00
2004-04-11 16:46:06 +00:00
# This function is called recursively. To keep track of arguments we need a stack:
array_push ( $this -> mArgStack , $args );
2004-04-09 15:29:33 +00:00
2005-10-19 06:24:30 +00:00
$braceCallbacks = array ();
2006-02-01 04:41:53 +00:00
if ( ! $argsOnly ) {
$braceCallbacks [ 2 ] = array ( & $this , 'braceSubstitution' );
}
2006-09-18 18:10:20 +00:00
if ( $this -> mOutputType != OT_MSG ) {
2005-10-19 06:24:30 +00:00
$braceCallbacks [ 3 ] = array ( & $this , 'argSubstitution' );
2004-05-23 03:39:24 +00:00
}
2006-08-06 14:01:47 +00:00
if ( $braceCallbacks ) {
2006-10-17 08:49:27 +00:00
$callbacks = array (
2006-08-06 14:01:47 +00:00
'{' => array (
'end' => '}' ,
'cb' => $braceCallbacks ,
'min' => $argsOnly ? 3 : 2 ,
'max' => isset ( $braceCallbacks [ 3 ] ) ? 3 : 2 ,
),
2006-10-17 08:49:27 +00:00
'[' => array (
'end' => ']' ,
2006-08-06 14:01:47 +00:00
'cb' => array ( 2 => null ),
'min' => 2 ,
'max' => 2 ,
)
);
$text = $this -> replace_callback ( $text , $callbacks );
2004-04-12 23:59:37 +00:00
2006-08-06 14:01:47 +00:00
array_pop ( $this -> mArgStack );
}
2004-05-26 12:30:36 +00:00
wfProfileOut ( $fname );
2004-04-11 16:46:06 +00:00
return $text ;
2004-03-20 15:03:26 +00:00
}
2006-07-11 17:40:11 +00:00
2004-09-21 23:30:46 +00:00
/**
* Replace magic variables
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 23:30:46 +00:00
*/
function variableSubstitution ( $matches ) {
2006-10-08 13:00:42 +00:00
global $wgContLang ;
2005-12-26 21:43:30 +00:00
$fname = 'Parser::variableSubstitution' ;
2006-10-08 13:00:42 +00:00
$varname = $wgContLang -> lc ( $matches [ 1 ]);
2004-11-28 04:05:05 +00:00
wfProfileIn ( $fname );
2004-09-21 23:30:46 +00:00
$skip = false ;
if ( $this -> mOutputType == OT_WIKI ) {
# Do only magic variables prefixed by SUBST
2006-07-14 15:39:23 +00:00
$mwSubst =& MagicWord :: get ( 'subst' );
2005-01-18 11:52:06 +00:00
if ( ! $mwSubst -> matchStartAndRemove ( $varname ))
2004-09-21 23:30:46 +00:00
$skip = true ;
# Note that if we don't substitute the variable below,
# we don't remove the {{subst:}} magic word, in case
# it is a template rather than a magic variable.
}
2005-01-18 11:52:06 +00:00
if ( ! $skip && array_key_exists ( $varname , $this -> mVariables ) ) {
$id = $this -> mVariables [ $varname ];
2006-10-08 13:00:42 +00:00
# Now check if we did really match, case sensitive or not
$mw =& MagicWord :: get ( $id );
if ( $mw -> match ( $matches [ 1 ])) {
$text = $this -> getVariableValue ( $id );
$this -> mOutput -> mContainsOldMagic = true ;
} else {
$text = $matches [ 0 ];
}
2004-09-21 23:30:46 +00:00
} else {
$text = $matches [ 0 ];
}
2004-11-28 04:05:05 +00:00
wfProfileOut ( $fname );
2004-09-21 23:30:46 +00:00
return $text ;
}
2007-01-17 19:48:48 +00:00
2006-12-11 23:50:41 +00:00
/// Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
static function createAssocArgs ( $args ) {
$assocArgs = array ();
$index = 1 ;
foreach ( $args as $arg ) {
$eqpos = strpos ( $arg , '=' );
if ( $eqpos === false ) {
$assocArgs [ $index ++ ] = $arg ;
} else {
$name = trim ( substr ( $arg , 0 , $eqpos ) );
$value = trim ( substr ( $arg , $eqpos + 1 ) );
if ( $value === false ) {
$value = '' ;
}
if ( $name !== false ) {
$assocArgs [ $name ] = $value ;
}
}
}
2007-01-17 19:48:48 +00:00
2006-12-11 23:50:41 +00:00
return $assocArgs ;
}
2007-01-17 19:48:48 +00:00
2004-09-21 05:49:12 +00:00
/**
* Return the text of a template , after recursively
* replacing any variables or templates within the template .
*
2005-10-19 06:24:30 +00:00
* @ param array $piece The parts of the template
* $piece [ 'text' ] : matched text
* $piece [ 'title' ] : the title , i . e . the part before the |
* $piece [ 'parts' ] : the parameter array
2004-09-21 05:49:12 +00:00
* @ return string the text of the template
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2005-10-19 06:24:30 +00:00
function braceSubstitution ( $piece ) {
2007-02-15 01:23:47 +00:00
global $wgContLang , $wgLang , $wgAllowDisplayTitle , $wgNonincludableNamespaces ;
2006-08-06 14:01:47 +00:00
$fname = __METHOD__ /*. '-L' . count( $this->mArgStack )*/ ;
2004-11-28 04:05:05 +00:00
wfProfileIn ( $fname );
2006-08-06 14:01:47 +00:00
wfProfileIn ( __METHOD__ . '-setup' );
2005-07-03 07:15:53 +00:00
2006-03-11 17:13:49 +00:00
# Flags
2006-02-01 04:41:53 +00:00
$found = false ; # $text has been filled
$nowiki = false ; # wiki markup in $text should be escaped
$noparse = false ; # Unsafe HTML tags should not be stripped, etc.
$noargs = false ; # Don't replace triple-brace arguments in $text
$replaceHeadings = false ; # Make the edit section links go to the template not the article
2006-12-07 18:48:29 +00:00
$headingOffset = 0 ; # Skip headings when number, to account for those that weren't transcluded.
2006-02-01 04:41:53 +00:00
$isHTML = false ; # $text is HTML, armour it against wikitext transformation
$forceRawInterwiki = false ; # Force interwiki transclusion to be done in raw mode not rendered
2004-07-12 19:49:20 +00:00
2006-02-01 04:41:53 +00:00
# Title object, where $text came from
2004-04-11 16:46:06 +00:00
$title = NULL ;
2004-04-12 23:59:37 +00:00
2005-10-19 06:24:30 +00:00
$linestart = '' ;
2004-09-25 05:16:38 +00:00
2006-10-17 08:49:27 +00:00
2004-04-11 16:46:06 +00:00
# $part1 is the bit before the first |, and must contain only title characters
# $args is a list of arguments, starting from index 0, not including $part1
2004-04-12 23:59:37 +00:00
2006-08-17 22:20:06 +00:00
$titleText = $part1 = $piece [ 'title' ];
2004-09-25 05:16:38 +00:00
# If the third subpattern matched anything, it will start with |
2004-07-31 14:13:07 +00:00
2005-10-19 06:24:30 +00:00
if ( null == $piece [ 'parts' ]) {
$replaceWith = $this -> variableSubstitution ( array ( $piece [ 'text' ], $piece [ 'title' ]));
if ( $replaceWith != $piece [ 'text' ]) {
$text = $replaceWith ;
$found = true ;
$noparse = true ;
2006-02-01 04:41:53 +00:00
$noargs = true ;
2005-10-19 06:24:30 +00:00
}
2004-05-23 03:39:24 +00:00
}
2004-03-20 15:03:26 +00:00
2005-10-19 06:24:30 +00:00
$args = ( null == $piece [ 'parts' ]) ? array () : $piece [ 'parts' ];
2006-08-06 14:01:47 +00:00
wfProfileOut ( __METHOD__ . '-setup' );
2005-10-19 06:24:30 +00:00
2004-03-20 15:03:26 +00:00
# SUBST
2006-08-06 14:01:47 +00:00
wfProfileIn ( __METHOD__ . '-modifiers' );
2004-05-23 03:39:24 +00:00
if ( ! $found ) {
2006-07-14 15:39:23 +00:00
$mwSubst =& MagicWord :: get ( 'subst' );
2006-08-14 07:10:31 +00:00
if ( $mwSubst -> matchStartAndRemove ( $part1 ) xor $this -> ot [ 'wiki' ] ) {
2004-09-22 00:15:54 +00:00
# One of two possibilities is true:
# 1) Found SUBST but not in the PST phase
# 2) Didn't find SUBST and in the PST phase
# In either case, return without further processing
2005-10-19 06:24:30 +00:00
$text = $piece [ 'text' ];
2004-03-20 15:03:26 +00:00
$found = true ;
2004-09-22 00:15:54 +00:00
$noparse = true ;
2006-02-01 04:41:53 +00:00
$noargs = true ;
2004-03-20 15:03:26 +00:00
}
2004-02-26 13:37:26 +00:00
}
2004-04-12 23:59:37 +00:00
2006-09-30 04:53:36 +00:00
# MSG, MSGNW and RAW
2004-03-20 15:03:26 +00:00
if ( ! $found ) {
# Check for MSGNW:
2006-07-14 15:39:23 +00:00
$mwMsgnw =& MagicWord :: get ( 'msgnw' );
2004-04-11 16:46:06 +00:00
if ( $mwMsgnw -> matchStartAndRemove ( $part1 ) ) {
2004-03-20 15:03:26 +00:00
$nowiki = true ;
2005-06-12 13:39:28 +00:00
} else {
# Remove obsolete MSG:
2006-07-14 15:39:23 +00:00
$mwMsg =& MagicWord :: get ( 'msg' );
2005-06-12 13:39:28 +00:00
$mwMsg -> matchStartAndRemove ( $part1 );
2004-03-20 15:03:26 +00:00
}
2006-10-17 08:49:27 +00:00
2006-02-01 04:41:53 +00:00
# Check for RAW:
2006-07-14 15:39:23 +00:00
$mwRaw =& MagicWord :: get ( 'raw' );
2006-02-01 04:41:53 +00:00
if ( $mwRaw -> matchStartAndRemove ( $part1 ) ) {
$forceRawInterwiki = true ;
}
2004-03-20 15:03:26 +00:00
}
2006-08-06 14:01:47 +00:00
wfProfileOut ( __METHOD__ . '-modifiers' );
2004-04-12 23:59:37 +00:00
2006-12-21 00:07:13 +00:00
//save path level before recursing into functions & templates.
$lastPathLevel = $this -> mTemplatePath ;
2006-07-03 11:07:00 +00:00
# Parser functions
2004-04-05 10:38:40 +00:00
if ( ! $found ) {
2006-07-03 11:07:00 +00:00
wfProfileIn ( __METHOD__ . '-pfunc' );
2006-10-17 08:49:27 +00:00
2006-07-03 11:07:00 +00:00
$colonPos = strpos ( $part1 , ':' );
if ( $colonPos !== false ) {
# Case sensitive functions
$function = substr ( $part1 , 0 , $colonPos );
if ( isset ( $this -> mFunctionSynonyms [ 1 ][ $function ] ) ) {
$function = $this -> mFunctionSynonyms [ 1 ][ $function ];
2004-04-05 10:38:40 +00:00
} else {
2006-07-03 11:07:00 +00:00
# Case insensitive functions
$function = strtolower ( $function );
if ( isset ( $this -> mFunctionSynonyms [ 0 ][ $function ] ) ) {
$function = $this -> mFunctionSynonyms [ 0 ][ $function ];
2006-04-30 18:02:03 +00:00
} else {
2006-07-03 11:07:00 +00:00
$function = false ;
2006-04-30 18:02:03 +00:00
}
}
2006-07-03 11:07:00 +00:00
if ( $function ) {
2006-04-18 02:48:48 +00:00
$funcArgs = array_map ( 'trim' , $args );
$funcArgs = array_merge ( array ( & $this , trim ( substr ( $part1 , $colonPos + 1 ) ) ), $funcArgs );
2006-04-05 09:40:25 +00:00
$result = call_user_func_array ( $this -> mFunctionHooks [ $function ], $funcArgs );
$found = true ;
2006-04-27 08:21:42 +00:00
// The text is usually already parsed, doesn't need triple-brace tags expanded, etc.
2006-04-27 15:46:33 +00:00
//$noargs = true;
//$noparse = true;
2006-07-03 11:07:00 +00:00
2006-04-05 09:40:25 +00:00
if ( is_array ( $result ) ) {
2006-07-03 11:07:00 +00:00
if ( isset ( $result [ 0 ] ) ) {
$text = $linestart . $result [ 0 ];
unset ( $result [ 0 ] );
}
2006-04-05 09:40:25 +00:00
// Extract flags into the local scope
// This allows callers to set flags such as nowiki, noparse, found, etc.
extract ( $result );
} else {
$text = $linestart . $result ;
}
}
}
2006-07-11 17:40:11 +00:00
wfProfileOut ( __METHOD__ . '-pfunc' );
2006-04-05 09:40:25 +00:00
}
2004-08-16 15:23:46 +00:00
# Template table test
2004-07-24 00:37:24 +00:00
# Did we encounter this template already? If yes, it is in the cache
# and we need to check for loops.
2006-02-01 04:41:53 +00:00
if ( ! $found && isset ( $this -> mTemplates [ $piece [ 'title' ]] ) ) {
2004-09-24 18:29:01 +00:00
$found = true ;
2004-07-24 00:37:24 +00:00
# Infinite loop test
if ( isset ( $this -> mTemplatePath [ $part1 ] ) ) {
$noparse = true ;
2006-02-01 04:41:53 +00:00
$noargs = true ;
2004-07-24 00:37:24 +00:00
$found = true ;
2005-02-11 09:02:15 +00:00
$text = $linestart .
2006-08-17 22:20:06 +00:00
" [[ $part1 ]]<!-- WARNING: template loop detected --> " ;
2006-08-06 14:01:47 +00:00
wfDebug ( __METHOD__ . " : template loop broken at ' $part1 ' \n " );
2005-02-11 09:02:15 +00:00
} else {
# set $text to cached message.
2006-02-01 04:41:53 +00:00
$text = $linestart . $this -> mTemplates [ $piece [ 'title' ]];
2007-03-29 19:31:25 +00:00
#treat title for cached page the same as others
$ns = NS_TEMPLATE ;
$subpage = '' ;
$part1 = $this -> maybeDoSubpageLink ( $part1 , $subpage );
if ( $subpage !== '' ) {
$ns = $this -> mTitle -> getNamespace ();
}
$title = Title :: newFromText ( $part1 , $ns );
//used by include size checking
$titleText = $title -> getPrefixedText ();
//used by edit section links
$replaceHeadings = true ;
2004-08-07 12:35:59 +00:00
}
2004-04-11 16:46:06 +00:00
}
2004-07-24 00:37:24 +00:00
2004-03-20 15:03:26 +00:00
# Load from database
if ( ! $found ) {
2006-07-04 08:11:44 +00:00
wfProfileIn ( __METHOD__ . '-loadtpl' );
2004-09-25 20:13:14 +00:00
$ns = NS_TEMPLATE ;
2006-03-24 16:44:52 +00:00
# declaring $subpage directly in the function call
# does not work correctly with references and breaks
# {{/subpage}}-style inclusions
$subpage = '' ;
$part1 = $this -> maybeDoSubpageLink ( $part1 , $subpage );
2004-09-25 20:13:14 +00:00
if ( $subpage !== '' ) {
$ns = $this -> mTitle -> getNamespace ();
}
$title = Title :: newFromText ( $part1 , $ns );
2005-07-03 07:15:53 +00:00
2006-06-16 22:30:39 +00:00
2006-01-31 03:44:08 +00:00
if ( ! is_null ( $title ) ) {
2006-08-17 22:20:06 +00:00
$titleText = $title -> getPrefixedText ();
2006-06-17 08:55:44 +00:00
# Check for language variants if the template is not found
2006-12-11 23:33:27 +00:00
if ( $wgContLang -> hasVariants () && $title -> getArticleID () == 0 ){
2006-06-17 08:55:44 +00:00
$wgContLang -> findVariantLink ( $part1 , $title );
}
2006-01-31 03:44:08 +00:00
if ( ! $title -> isExternal () ) {
2006-08-14 07:10:31 +00:00
if ( $title -> getNamespace () == NS_SPECIAL && $this -> mOptions -> getAllowSpecialInclusion () && $this -> ot [ 'html' ] ) {
2006-08-10 21:28:49 +00:00
$text = SpecialPage :: capturePath ( $title );
if ( is_string ( $text ) ) {
$found = true ;
$noparse = true ;
$noargs = true ;
$isHTML = true ;
$this -> disableCache ();
}
2007-02-15 01:23:47 +00:00
} else if ( $wgNonincludableNamespaces && in_array ( $title -> getNamespace (), $wgNonincludableNamespaces ) ) {
$found = false ; //access denied
wfDebug ( " $fname : template inclusion denied for " . $title -> getPrefixedDBkey () );
2006-08-10 21:28:49 +00:00
} else {
2007-05-01 22:42:41 +00:00
list ( $articleContent , $title ) = $this -> fetchTemplateAndtitle ( $title );
2006-08-10 21:28:49 +00:00
if ( $articleContent !== false ) {
$found = true ;
$text = $articleContent ;
$replaceHeadings = true ;
2005-05-28 11:09:22 +00:00
}
2004-04-12 23:59:37 +00:00
}
2004-02-26 13:37:26 +00:00
2006-01-31 03:44:08 +00:00
# If the title is valid but undisplayable, make a link to it
2006-08-14 07:10:31 +00:00
if ( ! $found && ( $this -> ot [ 'html' ] || $this -> ot [ 'pre' ] ) ) {
2006-10-09 11:53:12 +00:00
$text = " [[: $titleText ]] " ;
2006-01-31 03:44:08 +00:00
$found = true ;
}
} elseif ( $title -> isTrans () ) {
// Interwiki transclusion
2006-08-14 07:10:31 +00:00
if ( $this -> ot [ 'html' ] && ! $forceRawInterwiki ) {
2006-01-31 03:44:08 +00:00
$text = $this -> interwikiTransclude ( $title , 'render' );
$isHTML = true ;
$noparse = true ;
} else {
$text = $this -> interwikiTransclude ( $title , 'raw' );
2006-02-01 04:41:53 +00:00
$replaceHeadings = true ;
2006-01-31 03:44:08 +00:00
}
$found = true ;
2005-02-05 07:14:25 +00:00
}
2006-07-11 17:40:11 +00:00
2006-02-01 04:41:53 +00:00
# Template cache array insertion
# Use the original $piece['title'] not the mangled $part1, so that
# modifiers such as RAW: produce separate cache entries
if ( $found ) {
2006-06-04 02:41:52 +00:00
if ( $isHTML ) {
// A special page; don't store it in the template cache.
} else {
$this -> mTemplates [ $piece [ 'title' ]] = $text ;
}
2006-02-01 04:41:53 +00:00
$text = $linestart . $text ;
}
2004-03-20 15:03:26 +00:00
}
2006-07-11 17:40:11 +00:00
wfProfileOut ( __METHOD__ . '-loadtpl' );
2004-03-20 15:03:26 +00:00
}
2004-04-12 23:59:37 +00:00
2006-08-10 21:28:49 +00:00
if ( $found && ! $this -> incrementIncludeSize ( 'pre-expand' , strlen ( $text ) ) ) {
# Error, oversize inclusion
$text = $linestart .
2006-08-17 22:20:06 +00:00
" [[ $titleText ]]<!-- WARNING: template omitted, pre-expand include size too large --> " ;
2006-08-10 21:28:49 +00:00
$noparse = true ;
$noargs = true ;
}
2004-04-11 16:46:06 +00:00
# Recursive parsing, escaping and link table handling
# Only for HTML output
2006-08-14 07:10:31 +00:00
if ( $nowiki && $found && ( $this -> ot [ 'html' ] || $this -> ot [ 'pre' ] ) ) {
2004-04-11 16:46:06 +00:00
$text = wfEscapeWikiText ( $text );
2006-08-14 07:10:31 +00:00
} elseif ( ! $this -> ot [ 'msg' ] && $found ) {
2006-04-27 08:21:42 +00:00
if ( $noargs ) {
$assocArgs = array ();
} else {
2006-02-01 04:41:53 +00:00
# Clean up argument array
2006-12-12 16:12:30 +00:00
$assocArgs = self :: createAssocArgs ( $args );
2006-02-01 04:41:53 +00:00
# Add a new element to the templace recursion path
$this -> mTemplatePath [ $part1 ] = 1 ;
2005-11-16 10:16:46 +00:00
}
2006-01-07 13:31:29 +00:00
2006-02-01 04:41:53 +00:00
if ( ! $noparse ) {
# If there are any <onlyinclude> tags, only include them
if ( in_string ( '<onlyinclude>' , $text ) && in_string ( '</onlyinclude>' , $text ) ) {
2006-11-21 09:53:45 +00:00
$replacer = new OnlyIncludeReplacer ;
StringUtils :: delimiterReplaceCallback ( '<onlyinclude>' , '</onlyinclude>' ,
array ( & $replacer , 'replace' ), $text );
$text = $replacer -> output ;
2006-02-01 04:41:53 +00:00
}
# Remove <noinclude> sections and <includeonly> tags
2006-11-21 09:53:45 +00:00
$text = StringUtils :: delimiterReplace ( '<noinclude>' , '</noinclude>' , '' , $text );
2006-02-01 04:41:53 +00:00
$text = strtr ( $text , array ( '<includeonly>' => '' , '</includeonly>' => '' ) );
2006-08-14 07:10:31 +00:00
if ( $this -> ot [ 'html' ] || $this -> ot [ 'pre' ] ) {
2006-02-01 04:41:53 +00:00
# Strip <nowiki>, <pre>, etc.
$text = $this -> strip ( $text , $this -> mStripState );
2006-08-14 07:10:31 +00:00
if ( $this -> ot [ 'html' ] ) {
$text = Sanitizer :: removeHTMLtags ( $text , array ( & $this , 'replaceVariables' ), $assocArgs );
2006-08-15 02:40:20 +00:00
} elseif ( $this -> ot [ 'pre' ] && $this -> mOptions -> getRemoveComments () ) {
$text = Sanitizer :: removeHTMLcomments ( $text );
2006-08-14 07:10:31 +00:00
}
2006-02-01 04:41:53 +00:00
}
$text = $this -> replaceVariables ( $text , $assocArgs );
2004-04-12 23:59:37 +00:00
2006-02-01 04:41:53 +00:00
# If the template begins with a table or block-level
# element, it should be treated as beginning a new line.
2007-04-13 00:23:49 +00:00
if ( ! $piece [ 'lineStart' ] && preg_match ( '/^(?:{\\||:|;|#|\*)/' , $text )) /*}*/ {
2006-02-01 04:41:53 +00:00
$text = " \n " . $text ;
}
} elseif ( ! $noargs ) {
# $noparse and !$noargs
# Just replace the arguments, not any double-brace items
# This is used for rendered interwiki transclusion
$text = $this -> replaceVariables ( $text , $assocArgs , true );
2004-09-25 16:06:10 +00:00
}
}
2005-02-11 09:02:15 +00:00
# Prune lower levels off the recursion check path
$this -> mTemplatePath = $lastPathLevel ;
2005-07-03 07:15:53 +00:00
2006-08-10 21:28:49 +00:00
if ( $found && ! $this -> incrementIncludeSize ( 'post-expand' , strlen ( $text ) ) ) {
# Error, oversize inclusion
$text = $linestart .
2006-08-17 22:20:06 +00:00
" [[ $titleText ]]<!-- WARNING: template omitted, post-expand include size too large --> " ;
2006-08-10 21:28:49 +00:00
$noparse = true ;
$noargs = true ;
}
2004-09-25 16:06:10 +00:00
if ( ! $found ) {
2004-11-28 04:05:05 +00:00
wfProfileOut ( $fname );
2005-10-19 06:24:30 +00:00
return $piece [ 'text' ];
2004-09-25 16:06:10 +00:00
} else {
2006-07-04 08:11:44 +00:00
wfProfileIn ( __METHOD__ . '-placeholders' );
2005-05-28 11:09:22 +00:00
if ( $isHTML ) {
# Replace raw HTML by a placeholder
2005-07-03 07:15:53 +00:00
# Add a blank line preceding, to prevent it from mucking up
2005-05-28 11:09:22 +00:00
# immediately preceding headings
$text = " \n \n " . $this -> insertStripItem ( $text , $this -> mStripState );
} else {
# replace ==section headers==
# XXX this needs to go away once we have a better parser.
2006-08-14 07:10:31 +00:00
if ( ! $this -> ot [ 'wiki' ] && ! $this -> ot [ 'pre' ] && $replaceHeadings ) {
2007-05-01 23:08:23 +00:00
if ( ! is_null ( $title ) )
2007-05-01 22:56:22 +00:00
$encodedname = base64_encode ( $title -> getPrefixedDBkey ());
2005-05-28 11:09:22 +00:00
else
$encodedname = base64_encode ( " " );
$m = preg_split ( '/(^={1,6}.*?={1,6}\s*?$)/m' , $text , - 1 ,
PREG_SPLIT_DELIM_CAPTURE );
$text = '' ;
2006-12-07 18:48:29 +00:00
$nsec = $headingOffset ;
2007-05-01 17:26:57 +00:00
2005-05-28 11:09:22 +00:00
for ( $i = 0 ; $i < count ( $m ); $i += 2 ) {
$text .= $m [ $i ];
if ( ! isset ( $m [ $i + 1 ]) || $m [ $i + 1 ] == " " ) continue ;
$hl = $m [ $i + 1 ];
if ( strstr ( $hl , " <!--MWTEMPLATESECTION " ) ) {
$text .= $hl ;
continue ;
}
2006-10-17 08:49:27 +00:00
$m2 = array ();
2005-05-28 11:09:22 +00:00
preg_match ( '/^(={1,6})(.*?)(={1,6})\s*?$/m' , $hl , $m2 );
$text .= $m2 [ 1 ] . $m2 [ 2 ] . " <!--MWTEMPLATESECTION= "
. $encodedname . " & " . base64_encode ( " $nsec " ) . " --> " . $m2 [ 3 ];
2005-07-03 07:15:53 +00:00
2005-05-28 11:09:22 +00:00
$nsec ++ ;
2004-09-21 11:54:45 +00:00
}
2004-09-21 06:54:18 +00:00
}
2004-09-21 04:33:51 +00:00
}
2006-07-04 08:11:44 +00:00
wfProfileOut ( __METHOD__ . '-placeholders' );
2004-09-25 05:16:38 +00:00
}
2005-07-03 07:15:53 +00:00
2005-02-11 09:02:15 +00:00
# Prune lower levels off the recursion check path
$this -> mTemplatePath = $lastPathLevel ;
2005-07-03 07:15:53 +00:00
2004-09-25 05:16:38 +00:00
if ( ! $found ) {
2004-11-28 04:05:05 +00:00
wfProfileOut ( $fname );
2005-10-19 06:24:30 +00:00
return $piece [ 'text' ];
2004-09-25 05:16:38 +00:00
} else {
2004-11-28 04:05:05 +00:00
wfProfileOut ( $fname );
2004-05-02 23:18:25 +00:00
return $text ;
2004-03-20 15:03:26 +00:00
}
2004-02-26 13:37:26 +00:00
}
2006-01-07 13:31:29 +00:00
2006-01-11 02:19:41 +00:00
/**
2006-03-11 17:13:49 +00:00
* Fetch the unparsed text of a template and register a reference to it .
2006-01-11 02:19:41 +00:00
*/
2007-05-01 22:42:41 +00:00
function fetchTemplateAndtitle ( $title ) {
2007-05-31 16:01:26 +00:00
$text = $skip = false ;
2007-05-01 22:42:41 +00:00
$finalTitle = $title ;
2006-01-11 02:19:41 +00:00
// Loop to fetch the article, with up to 1 redirect
for ( $i = 0 ; $i < 2 && is_object ( $title ); $i ++ ) {
2007-05-31 16:01:26 +00:00
# Give extensions a chance to select the revision instead
$id = false ; // Assume current
wfRunHooks ( 'BeforeParserFetchTemplateAndtitle' , array ( & $this , & $title , & $skip , & $id ) );
if ( $skip ) {
$text = false ;
2007-05-31 17:15:44 +00:00
$this -> mOutput -> addTemplate ( $title , $title -> getArticleID (), null );
2007-05-31 16:01:26 +00:00
break ;
}
$rev = $id ? Revision :: newFromId ( $id ) : Revision :: newFromTitle ( $title );
$rev_id = $rev ? $rev -> getId () : 0 ;
$this -> mOutput -> addTemplate ( $title , $title -> getArticleID (), $rev_id );
if ( $rev ) {
2007-01-07 12:30:46 +00:00
$text = $rev -> getText ();
} elseif ( $title -> getNamespace () == NS_MEDIAWIKI ) {
global $wgLang ;
$message = $wgLang -> lcfirst ( $title -> getText () );
$text = wfMsgForContentNoTrans ( $message );
if ( wfEmptyMsg ( $message , $text ) ) {
$text = false ;
break ;
}
} else {
2006-01-11 02:19:41 +00:00
break ;
}
if ( $text === false ) {
break ;
}
// Redirect?
2007-05-01 22:42:41 +00:00
$finalTitle = $title ;
2006-01-11 02:19:41 +00:00
$title = Title :: newFromRedirect ( $text );
}
2007-05-01 22:42:41 +00:00
return array ( $text , $finalTitle );
}
function fetchTemplate ( $title ) {
2007-05-01 23:08:23 +00:00
$rv = $this -> fetchTemplateAndtitle ( $title );
return $rv [ 0 ];
2006-01-11 02:19:41 +00:00
}
2005-07-03 07:15:53 +00:00
/**
2006-01-31 03:44:08 +00:00
* Transclude an interwiki link .
2005-07-03 07:15:53 +00:00
*/
2006-01-31 03:44:08 +00:00
function interwikiTransclude ( $title , $action ) {
2006-12-31 03:04:49 +00:00
global $wgEnableScaryTranscluding ;
2005-07-03 07:15:53 +00:00
if ( ! $wgEnableScaryTranscluding )
return wfMsg ( 'scarytranscludedisabled' );
2006-12-31 03:04:49 +00:00
$url = $title -> getFullUrl ( " action= $action " );
2007-01-17 19:48:48 +00:00
2005-07-03 07:48:04 +00:00
if ( strlen ( $url ) > 255 )
return wfMsg ( 'scarytranscludetoolong' );
2006-01-31 03:44:08 +00:00
return $this -> fetchScaryTemplateMaybeFromCache ( $url );
2005-07-03 07:15:53 +00:00
}
function fetchScaryTemplateMaybeFromCache ( $url ) {
2006-01-31 03:44:08 +00:00
global $wgTranscludeCacheExpiry ;
2007-01-22 23:50:42 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2005-07-03 07:15:53 +00:00
$obj = $dbr -> selectRow ( 'transcache' , array ( 'tc_time' , 'tc_contents' ),
array ( 'tc_url' => $url ));
if ( $obj ) {
$time = $obj -> tc_time ;
$text = $obj -> tc_contents ;
2006-01-31 03:44:08 +00:00
if ( $time && time () < $time + $wgTranscludeCacheExpiry ) {
2005-07-03 07:15:53 +00:00
return $text ;
}
}
2006-07-02 15:57:59 +00:00
$text = Http :: get ( $url );
2005-07-03 07:15:53 +00:00
if ( ! $text )
2005-07-05 18:26:34 +00:00
return wfMsg ( 'scarytranscludefailed' , $url );
2005-07-03 07:15:53 +00:00
2007-01-22 23:50:42 +00:00
$dbw = wfGetDB ( DB_MASTER );
2006-01-31 03:44:08 +00:00
$dbw -> replace ( 'transcache' , array ( 'tc_url' ), array (
2005-07-03 07:15:53 +00:00
'tc_url' => $url ,
'tc_time' => time (),
'tc_contents' => $text ));
return $text ;
}
2004-09-21 05:49:12 +00:00
/**
* Triple brace replacement -- used for template arguments
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2004-06-12 06:15:09 +00:00
function argSubstitution ( $matches ) {
2005-10-19 06:24:30 +00:00
$arg = trim ( $matches [ 'title' ] );
$text = $matches [ 'text' ];
2004-05-23 03:39:24 +00:00
$inputArgs = end ( $this -> mArgStack );
if ( array_key_exists ( $arg , $inputArgs ) ) {
2004-09-21 23:56:25 +00:00
$text = $inputArgs [ $arg ];
2006-10-17 08:49:27 +00:00
} else if (( $this -> mOutputType == OT_HTML || $this -> mOutputType == OT_PREPROCESS ) &&
2006-08-14 07:10:31 +00:00
null != $matches [ 'parts' ] && count ( $matches [ 'parts' ]) > 0 ) {
2005-10-19 06:24:30 +00:00
$text = $matches [ 'parts' ][ 0 ];
2004-05-23 03:39:24 +00:00
}
2006-08-10 21:28:49 +00:00
if ( ! $this -> incrementIncludeSize ( 'arg' , strlen ( $text ) ) ) {
$text = $matches [ 'text' ] .
'<!-- WARNING: argument omitted, expansion size too large -->' ;
}
2004-07-12 19:49:20 +00:00
2004-05-23 03:39:24 +00:00
return $text ;
}
2004-09-21 05:49:12 +00:00
/**
2006-08-10 21:28:49 +00:00
* Increment an include size counter
*
* @ param string $type The type of expansion
* @ param integer $size The size of the text
* @ return boolean False if this inclusion would take it over the maximum , true otherwise
2004-09-21 05:49:12 +00:00
*/
2006-08-10 21:28:49 +00:00
function incrementIncludeSize ( $type , $size ) {
if ( $this -> mIncludeSizes [ $type ] + $size > $this -> mOptions -> getMaxIncludeSize () ) {
2004-04-11 16:46:06 +00:00
return false ;
2006-08-10 21:28:49 +00:00
} else {
$this -> mIncludeSizes [ $type ] += $size ;
return true ;
2004-04-11 16:46:06 +00:00
}
}
2006-06-13 11:37:09 +00:00
/**
* Detect __NOGALLERY__ magic word and set a placeholder
*/
function stripNoGallery ( & $text ) {
# if the string __NOGALLERY__ (not case-sensitive) occurs in the HTML,
# do not add TOC
2006-07-14 15:39:23 +00:00
$mw = MagicWord :: get ( 'nogallery' );
2006-06-13 11:37:09 +00:00
$this -> mOutput -> mNoGallery = $mw -> matchAndRemove ( $text ) ;
}
2006-05-23 07:19:01 +00:00
/**
2007-06-05 18:34:14 +00:00
* Find the first __TOC__ magic word and set a <!-- MWTOC -->
* placeholder that will then be replaced by the real TOC in
* -> formatHeadings , this works because at this points real
* comments will have already been discarded by the sanitizer .
*
* Any additional __TOC__ magic words left over will be discarded
* as there can only be one TOC on the page .
2006-05-23 07:19:01 +00:00
*/
function stripToc ( $text ) {
# if the string __NOTOC__ (not case-sensitive) occurs in the HTML,
# do not add TOC
2006-07-14 15:39:23 +00:00
$mw = MagicWord :: get ( 'notoc' );
2006-05-23 07:19:01 +00:00
if ( $mw -> matchAndRemove ( $text ) ) {
$this -> mShowToc = false ;
}
2006-07-11 17:40:11 +00:00
2006-07-14 15:39:23 +00:00
$mw = MagicWord :: get ( 'toc' );
2006-05-23 07:19:01 +00:00
if ( $mw -> match ( $text ) ) {
$this -> mShowToc = true ;
$this -> mForceTocPosition = true ;
2006-07-11 17:40:11 +00:00
2006-05-23 07:19:01 +00:00
// Set a placeholder. At the end we'll fill it in with the TOC.
$text = $mw -> replace ( '<!--MWTOC-->' , $text , 1 );
2006-07-11 17:40:11 +00:00
2006-05-23 07:19:01 +00:00
// Only keep the first one.
$text = $mw -> replace ( '' , $text );
}
return $text ;
}
2004-09-21 05:49:12 +00:00
/**
* 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
2005-07-03 07:15:53 +00:00
*
2004-09-21 05:49:12 +00:00
* It loops through all headlines , collects the necessary data , then splits up the
* string and re - inserts the newly formatted headlines .
2005-07-03 07:15:53 +00:00
*
2005-01-31 22:59:55 +00:00
* @ param string $text
* @ param boolean $isMain
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2005-01-31 22:59:55 +00:00
function formatHeadings ( $text , $isMain = true ) {
2005-12-04 18:27:59 +00:00
global $wgMaxTocLevel , $wgContLang ;
2004-07-12 19:49:20 +00:00
2004-03-21 11:28:44 +00:00
$doNumberHeadings = $this -> mOptions -> getNumberHeadings ();
2007-01-13 03:22:20 +00:00
if ( ! $this -> mTitle -> quickUserCan ( 'edit' ) ) {
2006-05-14 21:52:28 +00:00
$showEditLink = 0 ;
} else {
$showEditLink = $this -> mOptions -> getEditSection ();
}
2004-02-26 13:37:26 +00:00
# Inhibit editsection links if requested in the page
2006-07-14 15:39:23 +00:00
$esw =& MagicWord :: get ( 'noeditsection' );
2004-03-21 11:28:44 +00:00
if ( $esw -> matchAndRemove ( $text ) ) {
$showEditLink = 0 ;
2004-02-26 13:37:26 +00:00
}
2004-04-03 04:38:09 +00:00
# Get all headlines for numbering them and adding funky stuff like [edit]
# links - this is for later, but we need the number of headlines right now
2006-10-17 08:49:27 +00:00
$matches = array ();
2007-01-08 02:11:45 +00:00
$numMatches = preg_match_all ( '/<H(?P<level>[1-6])(?P<attrib>.*?' . '>)(?P<header>.*?)<\/H[1-6] *>/i' , $text , $matches );
2004-04-03 04:38:09 +00:00
# if there are fewer than 4 headlines in the article, do not show TOC
2006-05-23 10:01:45 +00:00
# unless it's been explicitly enabled.
$enoughToc = $this -> mShowToc &&
(( $numMatches >= 4 ) || $this -> mForceTocPosition );
2004-04-03 04:38:09 +00:00
2006-05-01 20:35:08 +00:00
# Allow user to stipulate that a page should have a "new section"
# link added via __NEWSECTIONLINK__
2006-07-14 15:39:23 +00:00
$mw =& MagicWord :: get ( 'newsectionlink' );
2006-05-01 20:35:08 +00:00
if ( $mw -> matchAndRemove ( $text ) )
$this -> mOutput -> setNewSection ( true );
2006-05-23 10:01:45 +00:00
# if the string __FORCETOC__ (not case-sensitive) occurs in the HTML,
# override above conditions and always show TOC above first header
2006-07-14 15:39:23 +00:00
$mw =& MagicWord :: get ( 'forcetoc' );
2006-05-23 10:01:45 +00:00
if ( $mw -> matchAndRemove ( $text ) ) {
2006-05-23 07:19:01 +00:00
$this -> mShowToc = true ;
2006-05-23 10:01:45 +00:00
$enoughToc = true ;
2004-04-03 04:38:09 +00:00
}
2004-07-12 19:49:20 +00:00
2006-11-08 07:12:03 +00:00
# We need this to perform operations on the HTML
2007-01-22 23:50:42 +00:00
$sk = $this -> mOptions -> getSkin ();
2006-11-08 07:12:03 +00:00
2004-02-26 13:37:26 +00:00
# headline counter
2004-03-21 11:28:44 +00:00
$headlineCount = 0 ;
2004-09-21 04:33:51 +00:00
$sectionCount = 0 ; # headlineCount excluding template sections
2007-04-30 19:51:56 +00:00
$numVisible = 0 ;
2004-02-26 13:37:26 +00:00
# Ugh .. the TOC should have neat indentation levels which can be
# passed to the skin functions. These are determined here
2004-06-08 18:11:28 +00:00
$toc = '' ;
$full = '' ;
2004-03-08 02:50:04 +00:00
$head = array ();
2004-03-21 11:28:44 +00:00
$sublevelCount = array ();
2005-01-15 23:21:52 +00:00
$levelCount = array ();
$toclevel = 0 ;
2004-03-29 14:48:07 +00:00
$level = 0 ;
$prevlevel = 0 ;
2005-01-15 23:21:52 +00:00
$toclevel = 0 ;
$prevtoclevel = 0 ;
2004-03-21 11:28:44 +00:00
foreach ( $matches [ 3 ] as $headline ) {
2004-09-21 04:33:51 +00:00
$istemplate = 0 ;
2005-01-15 23:21:52 +00:00
$templatetitle = '' ;
2004-09-21 08:31:25 +00:00
$templatesection = 0 ;
2005-01-15 23:21:52 +00:00
$numbering = '' ;
2006-10-17 08:49:27 +00:00
$mat = array ();
2004-09-21 18:20:56 +00:00
if ( preg_match ( " /<!--MWTEMPLATESECTION=([^&]+)&([^_]+)-->/ " , $headline , $mat )) {
2004-09-21 04:33:51 +00:00
$istemplate = 1 ;
2004-09-21 08:31:25 +00:00
$templatetitle = base64_decode ( $mat [ 1 ]);
$templatesection = 1 + ( int ) base64_decode ( $mat [ 2 ]);
2004-09-21 18:20:56 +00:00
$headline = preg_replace ( " /<!--MWTEMPLATESECTION=([^&]+)&([^_]+)-->/ " , " " , $headline );
2004-09-21 04:33:51 +00:00
}
2004-09-21 08:31:25 +00:00
2005-01-15 23:21:52 +00:00
if ( $toclevel ) {
2004-03-21 11:28:44 +00:00
$prevlevel = $level ;
2007-05-02 19:09:36 +00:00
$prevtoclevel = $toclevel ;
2004-03-21 11:28:44 +00:00
}
$level = $matches [ 1 ][ $headlineCount ];
2005-07-03 07:15:53 +00:00
2006-05-23 10:01:45 +00:00
if ( $doNumberHeadings || $enoughToc ) {
2005-07-03 07:15:53 +00:00
2005-01-15 23:21:52 +00:00
if ( $level > $prevlevel ) {
# Increase TOC level
$toclevel ++ ;
$sublevelCount [ $toclevel ] = 0 ;
2006-04-25 19:43:46 +00:00
if ( $toclevel < $wgMaxTocLevel ) {
2007-04-30 19:51:56 +00:00
$prevtoclevel = $toclevel ;
2006-11-08 07:12:03 +00:00
$toc .= $sk -> tocIndent ();
2007-04-30 19:51:56 +00:00
$numVisible ++ ;
2006-04-25 19:43:46 +00:00
}
2005-01-15 23:21:52 +00:00
}
elseif ( $level < $prevlevel && $toclevel > 1 ) {
# Decrease TOC level, find level to jump to
if ( $toclevel == 2 && $level <= $levelCount [ 1 ] ) {
# Can only go down to level 1
$toclevel = 1 ;
} else {
for ( $i = $toclevel ; $i > 0 ; $i -- ) {
if ( $levelCount [ $i ] == $level ) {
# Found last matching level
$toclevel = $i ;
break ;
}
elseif ( $levelCount [ $i ] < $level ) {
# Found first matching level below current level
$toclevel = $i + 1 ;
break ;
}
}
}
2006-04-25 19:43:46 +00:00
if ( $toclevel < $wgMaxTocLevel ) {
2007-05-03 19:36:24 +00:00
if ( $prevtoclevel < $wgMaxTocLevel ) {
# Unindent only if the previous toc level was shown :p
$toc .= $sk -> tocUnindent ( $prevtoclevel - $toclevel );
} else {
$toc .= $sk -> tocLineEnd ();
}
2006-04-25 19:43:46 +00:00
}
2005-01-15 23:21:52 +00:00
}
else {
# No change in level, end TOC line
2006-04-25 19:43:46 +00:00
if ( $toclevel < $wgMaxTocLevel ) {
2006-11-08 07:12:03 +00:00
$toc .= $sk -> tocLineEnd ();
2006-04-25 19:43:46 +00:00
}
2005-01-15 23:21:52 +00:00
}
2005-07-03 07:15:53 +00:00
2005-01-15 23:21:52 +00:00
$levelCount [ $toclevel ] = $level ;
# count number of headlines for each level
@ $sublevelCount [ $toclevel ] ++ ;
2004-03-29 14:48:07 +00:00
$dot = 0 ;
2005-01-15 23:21:52 +00:00
for ( $i = 1 ; $i <= $toclevel ; $i ++ ) {
2004-03-29 14:48:07 +00:00
if ( ! empty ( $sublevelCount [ $i ] ) ) {
2004-03-21 11:28:44 +00:00
if ( $dot ) {
2004-06-08 18:11:28 +00:00
$numbering .= '.' ;
2004-03-21 11:28:44 +00:00
}
2004-09-24 13:14:52 +00:00
$numbering .= $wgContLang -> formatNum ( $sublevelCount [ $i ] );
2004-04-12 23:59:37 +00:00
$dot = 1 ;
2004-02-26 13:37:26 +00:00
}
}
}
2004-03-21 11:28:44 +00:00
# The canonized header is a version of the header text safe to use for links
# Avoid insertion of weird stuff like <math> by expanding the relevant sections
2006-11-21 09:53:45 +00:00
$canonized_headline = $this -> mStripState -> unstripBoth ( $headline );
2004-04-12 23:59:37 +00:00
2004-08-26 18:48:13 +00:00
# Remove link placeholders by the link text.
2004-09-20 23:02:08 +00:00
# <!--LINK number-->
2005-07-03 07:15:53 +00:00
# turns into
2004-08-26 18:48:13 +00:00
# link text with suffix
2004-09-20 23:02:08 +00:00
$canonized_headline = preg_replace ( '/<!--LINK ([0-9]*)-->/e' ,
2005-04-27 07:48:14 +00:00
" \$ this->mLinkHolders['texts'][ \$ 1] " ,
2004-09-20 23:02:08 +00:00
$canonized_headline );
2005-01-22 09:34:56 +00:00
$canonized_headline = preg_replace ( '/<!--IWLINK ([0-9]*)-->/e' ,
2005-06-03 05:46:24 +00:00
" \$ this->mInterwikiLinkHolders['texts'][ \$ 1] " ,
2005-01-22 09:34:56 +00:00
$canonized_headline );
2004-09-20 23:02:08 +00:00
2004-03-21 11:28:44 +00:00
# strip out HTML
2004-06-08 18:11:28 +00:00
$canonized_headline = preg_replace ( '/<.*?' . '>/' , '' , $canonized_headline );
2004-04-12 23:59:37 +00:00
$tocline = trim ( $canonized_headline );
2006-03-04 03:24:33 +00:00
# Save headline for section edit hint before it's escaped
2006-03-11 17:13:49 +00:00
$headline_hint = trim ( $canonized_headline );
2006-01-07 09:13:38 +00:00
$canonized_headline = Sanitizer :: escapeId ( $tocline );
2005-04-21 06:30:48 +00:00
$refers [ $headlineCount ] = $canonized_headline ;
2004-04-12 23:59:37 +00:00
2004-03-21 11:28:44 +00:00
# count how many in assoc. array so we can track dupes in anchors
2006-11-29 05:45:03 +00:00
isset ( $refers [ $canonized_headline ] ) ? $refers [ $canonized_headline ] ++ : $refers [ $canonized_headline ] = 1 ;
2004-03-21 11:28:44 +00:00
$refcount [ $headlineCount ] = $refers [ $canonized_headline ];
2004-02-26 13:37:26 +00:00
2005-01-15 23:21:52 +00:00
# Don't number the heading if it is the only one (looks silly)
if ( $doNumberHeadings && count ( $matches [ 3 ] ) > 1 ) {
# the two are different if the line contains a link
$headline = $numbering . ' ' . $headline ;
2004-02-26 13:37:26 +00:00
}
2004-04-12 23:59:37 +00:00
2004-03-21 11:28:44 +00:00
# Create the anchor for linking from the TOC to the section
$anchor = $canonized_headline ;
if ( $refcount [ $headlineCount ] > 1 ) {
2004-06-08 18:11:28 +00:00
$anchor .= '_' . $refcount [ $headlineCount ];
2004-03-21 11:28:44 +00:00
}
2006-05-23 10:01:45 +00:00
if ( $enoughToc && ( ! isset ( $wgMaxTocLevel ) || $toclevel < $wgMaxTocLevel ) ) {
2006-11-08 07:12:03 +00:00
$toc .= $sk -> tocLine ( $anchor , $tocline , $numbering , $toclevel );
2004-02-26 13:37:26 +00:00
}
2006-10-18 06:53:19 +00:00
# give headline the correct <h#> tag
2004-09-21 11:54:45 +00:00
if ( $showEditLink && ( ! $istemplate || $templatetitle !== " " ) ) {
2004-09-21 08:31:25 +00:00
if ( $istemplate )
2007-01-08 02:11:45 +00:00
$editlink = $sk -> editSectionLinkForOther ( $templatetitle , $templatesection );
2004-09-21 08:31:25 +00:00
else
2007-01-08 02:11:45 +00:00
$editlink = $sk -> editSectionLink ( $this -> mTitle , $sectionCount + 1 , $headline_hint );
} else {
$editlink = '' ;
2004-02-26 13:37:26 +00:00
}
2007-01-08 02:11:45 +00:00
$head [ $headlineCount ] = $sk -> makeHeadline ( $level , $matches [ 'attrib' ][ $headlineCount ], $anchor , $headline , $editlink );
2004-04-12 23:59:37 +00:00
2004-03-21 11:28:44 +00:00
$headlineCount ++ ;
2004-09-21 04:33:51 +00:00
if ( ! $istemplate )
$sectionCount ++ ;
2004-04-12 23:59:37 +00:00
}
2004-02-26 13:37:26 +00:00
2007-04-30 19:51:56 +00:00
# Never ever show TOC if no headers
if ( $numVisible < 1 ) {
$enoughToc = false ;
}
2006-05-23 10:01:45 +00:00
if ( $enoughToc ) {
2007-04-30 19:51:56 +00:00
if ( $prevtoclevel > 0 && $prevtoclevel < $wgMaxTocLevel ) {
$toc .= $sk -> tocUnindent ( $prevtoclevel - 1 );
2006-04-25 19:43:46 +00:00
}
2006-11-08 07:12:03 +00:00
$toc = $sk -> tocList ( $toc );
2004-02-26 13:37:26 +00:00
}
2004-03-21 11:28:44 +00:00
# split up and insert constructed headlines
2004-04-12 23:59:37 +00:00
2004-06-08 18:11:28 +00:00
$blocks = preg_split ( '/<H[1-6].*?' . '>.*?<\/H[1-6]>/i' , $text );
2004-03-21 11:28:44 +00:00
$i = 0 ;
2004-02-26 13:37:26 +00:00
2004-03-21 11:28:44 +00:00
foreach ( $blocks as $block ) {
2004-03-29 14:48:07 +00:00
if ( $showEditLink && $headlineCount > 0 && $i == 0 && $block != " \n " ) {
2004-08-16 15:23:46 +00:00
# This is the [edit] link that appears for the top block of text when
2004-02-26 13:37:26 +00:00
# section editing is enabled
2004-04-11 16:46:06 +00:00
# Disabled because it broke block formatting
# For example, a bullet point in the top line
2006-11-08 07:12:03 +00:00
# $full .= $sk->editSectionLink(0);
2004-02-26 13:37:26 +00:00
}
2004-03-21 11:28:44 +00:00
$full .= $block ;
2006-05-23 10:01:45 +00:00
if ( $enoughToc && ! $i && $isMain && ! $this -> mForceTocPosition ) {
2006-05-23 07:19:01 +00:00
# Top anchor now in skin
2004-04-07 16:12:18 +00:00
$full = $full . $toc ;
2004-02-26 13:37:26 +00:00
}
2004-03-08 02:50:04 +00:00
if ( ! empty ( $head [ $i ] ) ) {
$full .= $head [ $i ];
}
2004-02-26 13:37:26 +00:00
$i ++ ;
}
2006-05-23 07:19:01 +00:00
if ( $this -> mForceTocPosition ) {
return str_replace ( '<!--MWTOC-->' , $toc , $full );
2004-06-29 23:59:30 +00:00
} else {
return $full ;
}
2004-02-26 13:37:26 +00:00
}
2004-09-21 05:49:12 +00:00
/**
* Transform wiki markup when saving a page by doing \r\n -> \n
* conversion , substitting signatures , {{ subst : }} templates , etc .
*
* @ param string $text the text to transform
* @ param Title & $title the Title object for the current article
* @ param User & $user the User object describing the current user
* @ param ParserOptions $options parsing options
* @ param bool $clearState whether to clear the parser state first
* @ return string the altered wiki markup
2006-04-19 15:46:24 +00:00
* @ public
2004-09-21 05:49:12 +00:00
*/
2006-10-17 08:49:27 +00:00
function preSaveTransform ( $text , & $title , $user , $options , $clearState = true ) {
2004-03-06 01:49:16 +00:00
$this -> mOptions = $options ;
2004-03-27 22:47:25 +00:00
$this -> mTitle =& $title ;
2006-08-14 07:10:31 +00:00
$this -> setOutputType ( OT_WIKI );
2004-04-12 23:59:37 +00:00
2004-03-06 01:49:16 +00:00
if ( $clearState ) {
2004-03-08 09:09:35 +00:00
$this -> clearState ();
2004-03-06 01:49:16 +00:00
}
2004-04-12 23:59:37 +00:00
2006-11-21 09:53:45 +00:00
$stripState = new StripState ;
2004-04-09 16:22:12 +00:00
$pairs = array (
" \r \n " => " \n " ,
2005-04-21 06:30:48 +00:00
);
2004-11-20 11:28:37 +00:00
$text = str_replace ( array_keys ( $pairs ), array_values ( $pairs ), $text );
2006-06-13 10:11:12 +00:00
$text = $this -> strip ( $text , $stripState , true , array ( 'gallery' ) );
2006-04-29 02:20:30 +00:00
$text = $this -> pstPass2 ( $text , $stripState , $user );
2006-11-21 09:53:45 +00:00
$text = $stripState -> unstripBoth ( $text );
2004-03-06 01:49:16 +00:00
return $text ;
}
2004-09-21 05:49:12 +00:00
/**
* Pre - save transform helper function
2006-04-19 15:46:24 +00:00
* @ private
2004-09-21 05:49:12 +00:00
*/
2006-10-17 08:49:27 +00:00
function pstPass2 ( $text , & $stripState , $user ) {
2005-04-21 06:30:48 +00:00
global $wgContLang , $wgLocaltimezone ;
2004-03-20 15:03:26 +00:00
2005-04-07 18:14:31 +00:00
/* Note : This is the timestamp saved as hardcoded wikitext to
* the database , we use $wgContLang here in order to give
2006-04-29 02:20:30 +00:00
* everyone the same signature and use the default one rather
* than the one selected in each user ' s preferences .
2005-04-07 18:14:31 +00:00
*/
2005-11-15 00:38:39 +00:00
if ( isset ( $wgLocaltimezone ) ) {
$oldtz = getenv ( 'TZ' );
putenv ( 'TZ=' . $wgLocaltimezone );
}
2005-07-03 02:08:12 +00:00
$d = $wgContLang -> timeanddate ( date ( 'YmdHis' ), false , false ) .
2004-06-08 18:11:28 +00:00
' (' . date ( 'T' ) . ')' ;
2004-11-20 11:28:37 +00:00
if ( isset ( $wgLocaltimezone ) ) {
2005-04-21 06:30:48 +00:00
putenv ( 'TZ=' . $oldtz );
2004-11-20 11:28:37 +00:00
}
2004-03-06 01:49:16 +00:00
2006-01-07 23:37:40 +00:00
# Variable replacement
# Because mOutputType is OT_WIKI, this will only process {{subst:xxx}} type tags
$text = $this -> replaceVariables ( $text );
2006-07-11 17:40:11 +00:00
2006-04-29 02:20:30 +00:00
# Strip out <nowiki> etc. added via replaceVariables
2006-06-13 10:11:12 +00:00
$text = $this -> strip ( $text , $stripState , false , array ( 'gallery' ) );
2006-07-11 17:40:11 +00:00
2006-01-12 15:42:38 +00:00
# Signatures
$sigText = $this -> getUserSig ( $user );
2006-03-11 17:13:49 +00:00
$text = strtr ( $text , array (
2006-01-12 15:42:38 +00:00
'~~~~~' => $d ,
'~~~~' => " $sigText $d " ,
'~~~' => $sigText
) );
2006-01-07 23:37:40 +00:00
2004-03-06 01:49:16 +00:00
# Context links: [[|name]] and [[name (context)|]]
#
2005-12-05 07:44:14 +00:00
global $wgLegalTitleChars ;
$tc = " [ $wgLegalTitleChars ] " ;
2006-10-04 21:32:09 +00:00
$nc = '[ _0-9A-Za-z\x80-\xff]' ; # Namespaces can use non-ascii!
2006-01-07 13:31:29 +00:00
2006-10-04 21:32:09 +00:00
$p1 = " / \ [ \ [(:? $nc +:|:|)( $tc +?)( \\ ( $tc + \\ )) \\ |]]/ " ; # [[ns:page (context)|]]
$p3 = " / \ [ \ [(:? $nc +:|:|)( $tc +?)( \\ ( $tc + \\ )|)(, $tc +|) \\ |]]/ " ; # [[ns:page (context), context|]]
$p2 = " / \ [ \ [ \\ |( $tc +)]]/ " ; # [[|page]]
2004-03-06 01:49:16 +00:00
2006-10-04 21:32:09 +00:00
# try $p1 first, to turn "[[A, B (C)|]]" into "[[A, B (C)|A, B]]"
2006-07-26 18:09:04 +00:00
$text = preg_replace ( $p1 , '[[\\1\\2\\3|\\2]]' , $text );
2006-10-04 21:32:09 +00:00
$text = preg_replace ( $p3 , '[[\\1\\2\\3\\4|\\2]]' , $text );
2006-07-26 18:09:04 +00:00
$t = $this -> mTitle -> getText ();
2006-10-17 08:49:27 +00:00
$m = array ();
2006-10-04 21:32:09 +00:00
if ( preg_match ( " /^( $nc +:|) $tc +?( \\ ( $tc + \\ )) $ / " , $t , $m ) ) {
$text = preg_replace ( $p2 , " [[ $m[1] \\ 1 $m[2] | \\ 1]] " , $text );
} elseif ( preg_match ( " /^( $nc +:|) $tc +?(, $tc +|) $ / " , $t , $m ) && '' != " $m[1] $m[2] " ) {
$text = preg_replace ( $p2 , " [[ $m[1] \\ 1 $m[2] | \\ 1]] " , $text );
2004-03-06 01:49:16 +00:00
} else {
2006-10-04 21:32:09 +00:00
# if there's no context, don't bother duplicating the title
2006-07-26 18:09:04 +00:00
$text = preg_replace ( $p2 , '[[\\1]]' , $text );
2004-03-06 01:49:16 +00:00
}
2004-04-12 23:59:37 +00:00
2004-03-06 01:49:16 +00:00
# Trim trailing whitespace
$text = rtrim ( $text );
return $text ;
}
2006-01-07 13:31:29 +00:00
2005-11-15 00:38:39 +00:00
/**
* Fetch the user ' s signature text , if any , and normalize to
* validated , ready - to - insert wikitext .
*
* @ param User $user
* @ return string
2006-04-19 15:46:24 +00:00
* @ private
2005-11-15 00:38:39 +00:00
*/
function getUserSig ( & $user ) {
2007-06-13 16:28:19 +00:00
global $wgMaxSigChars ;
2006-01-07 23:09:21 +00:00
$username = $user -> getName ();
2006-01-08 03:39:26 +00:00
$nickname = $user -> getOption ( 'nickname' );
$nickname = $nickname === '' ? $username : $nickname ;
2007-06-13 16:28:19 +00:00
2007-06-25 15:02:46 +00:00
if ( mb_strlen ( $nickname ) > $wgMaxSigChars ) {
2007-06-13 16:28:19 +00:00
$nickname = $username ;
wfDebug ( __METHOD__ . " : $username has overlong signature. \n " );
} elseif ( $user -> getBoolOption ( 'fancysig' ) !== false ) {
2006-01-07 23:09:21 +00:00
# Sig. might contain markup; validate this
2006-01-08 03:09:31 +00:00
if ( $this -> validateSig ( $nickname ) !== false ) {
2006-01-07 23:09:21 +00:00
# Validated; clean up (if needed) and return it
2006-04-30 20:09:44 +00:00
return $this -> cleanSig ( $nickname , true );
2005-11-15 00:38:39 +00:00
} else {
2006-01-07 23:09:21 +00:00
# Failed to validate; fall back to the default
$nickname = $username ;
2006-01-08 03:09:31 +00:00
wfDebug ( " Parser::getUserSig: $username has bad XML tags in signature. \n " );
2005-11-15 00:38:39 +00:00
}
}
2006-01-07 13:31:29 +00:00
2006-06-23 19:50:55 +00:00
// Make sure nickname doesnt get a sig in a sig
$nickname = $this -> cleanSigInSig ( $nickname );
2006-01-07 23:09:21 +00:00
# If we're still here, make it a link to the user page
$userpage = $user -> getUserPage ();
return ( '[[' . $userpage -> getPrefixedText () . '|' . wfEscapeWikiText ( $nickname ) . ']]' );
2005-11-15 00:38:39 +00:00
}
2006-01-07 13:31:29 +00:00
2005-11-15 00:38:39 +00:00
/**
2006-01-07 23:09:21 +00:00
* Check that the user ' s signature contains no bad XML
2005-11-15 00:38:39 +00:00
*
* @ param string $text
* @ return mixed An expanded string , or false if invalid .
*/
function validateSig ( $text ) {
2006-01-07 23:09:21 +00:00
return ( wfIsWellFormedXmlFragment ( $text ) ? $text : false );
2005-11-15 00:38:39 +00:00
}
2006-07-11 17:40:11 +00:00
2006-01-07 23:09:21 +00:00
/**
* Clean up signature text
*
2006-06-23 19:50:55 +00:00
* 1 ) Strip ~~~ , ~~~~ and ~~~~~ out of signatures @ see cleanSigInSig
2006-01-13 09:47:09 +00:00
* 2 ) Substitute all transclusions
2006-01-07 23:09:21 +00:00
*
* @ param string $text
2006-04-30 20:09:44 +00:00
* @ param $parsing Whether we ' re cleaning ( preferences save ) or parsing
2006-01-13 09:47:09 +00:00
* @ return string Signature text
2006-01-07 23:09:21 +00:00
*/
2006-04-30 20:09:44 +00:00
function cleanSig ( $text , $parsing = false ) {
global $wgTitle ;
$this -> startExternalParse ( $wgTitle , new ParserOptions (), $parsing ? OT_WIKI : OT_MSG );
2006-07-11 17:40:11 +00:00
2006-07-14 15:39:23 +00:00
$substWord = MagicWord :: get ( 'subst' );
2006-01-13 09:47:09 +00:00
$substRegex = '/\{\{(?!(?:' . $substWord -> getBaseRegex () . '))/x' . $substWord -> getRegexCase ();
$substText = '{{' . $substWord -> getSynonym ( 0 );
$text = preg_replace ( $substRegex , $substText , $text );
2006-06-23 21:54:31 +00:00
$text = $this -> cleanSigInSig ( $text );
2006-01-12 22:38:55 +00:00
$text = $this -> replaceVariables ( $text );
2006-07-11 17:40:11 +00:00
$this -> clearState ();
2006-01-08 05:29:58 +00:00
return $text ;
2006-01-07 23:09:21 +00:00
}
2006-06-23 19:50:55 +00:00
/**
* Strip ~~~ , ~~~~ and ~~~~~ out of signatures
* @ param string $text
* @ return string Signature text with /~ { 3 , 5 } / removed
*/
function cleanSigInSig ( $text ) {
$text = preg_replace ( '/~{3,5}/' , '' , $text );
return $text ;
}
2006-07-11 17:40:11 +00:00
2004-09-21 05:49:12 +00:00
/**
* Set up some variables which are usually set up in parse ()
* so that an external function can call some class members with confidence
2006-04-19 15:46:24 +00:00
* @ public
2004-09-21 05:49:12 +00:00
*/
2004-06-12 06:15:09 +00:00
function startExternalParse ( & $title , $options , $outputType , $clearState = true ) {
2004-03-27 22:47:25 +00:00
$this -> mTitle =& $title ;
$this -> mOptions = $options ;
2006-08-14 07:10:31 +00:00
$this -> setOutputType ( $outputType );
2004-03-27 22:47:25 +00:00
if ( $clearState ) {
$this -> clearState ();
}
}
2004-04-05 10:38:40 +00:00
2004-09-21 05:49:12 +00:00
/**
* Transform a MediaWiki message by replacing magic variables .
2005-07-03 07:15:53 +00:00
*
2004-09-21 05:49:12 +00:00
* @ param string $text the text to transform
* @ param ParserOptions $options options
* @ return string the text with variables substituted
2006-04-19 15:46:24 +00:00
* @ public
2004-09-21 05:49:12 +00:00
*/
2004-04-05 10:38:40 +00:00
function transformMsg ( $text , $options ) {
global $wgTitle ;
static $executing = false ;
2004-04-12 23:59:37 +00:00
2006-01-14 03:08:04 +00:00
$fname = " Parser::transformMsg " ;
2004-04-05 10:38:40 +00:00
# Guard against infinite recursion
if ( $executing ) {
return $text ;
}
$executing = true ;
2006-01-14 03:08:04 +00:00
wfProfileIn ( $fname );
2007-01-13 22:36:32 +00:00
if ( $wgTitle && ! ( $wgTitle instanceof FakeTitle ) ) {
2006-10-02 17:04:13 +00:00
$this -> mTitle = $wgTitle ;
} else {
$this -> mTitle = Title :: newFromText ( 'msg' );
}
2004-04-05 10:38:40 +00:00
$this -> mOptions = $options ;
2006-08-14 07:10:31 +00:00
$this -> setOutputType ( OT_MSG );
2004-04-05 10:38:40 +00:00
$this -> clearState ();
$text = $this -> replaceVariables ( $text );
2004-04-12 23:59:37 +00:00
2004-04-05 10:38:40 +00:00
$executing = false ;
2006-01-14 03:08:04 +00:00
wfProfileOut ( $fname );
2004-04-05 10:38:40 +00:00
return $text ;
}
2004-06-09 12:15:42 +00:00
2004-09-21 05:49:12 +00:00
/**
* Create an HTML - style tag , e . g . < yourtag > special text </ yourtag >
2006-02-28 05:18:36 +00:00
* The callback should have the following form :
* function myParserHook ( $text , $params , & $parser ) { ... }
*
* Transform and return $text . Use $parser for any required context , e . g . use
* $parser -> getTitle () and $parser -> getOptions () not $wgTitle or $wgOut -> mParserOptions
2005-11-26 23:04:05 +00:00
*
2006-04-19 15:46:24 +00:00
* @ public
2005-11-26 23:04:05 +00:00
*
* @ param mixed $tag The tag to use , e . g . 'hook' for < hook >
2005-11-27 02:33:20 +00:00
* @ param mixed $callback The callback function ( and object ) to use for the tag
2005-11-26 23:04:05 +00:00
*
* @ return The old value of the mTagHooks array associated with the hook
2004-09-21 05:49:12 +00:00
*/
2005-11-27 02:33:20 +00:00
function setHook ( $tag , $callback ) {
2006-06-01 06:41:32 +00:00
$tag = strtolower ( $tag );
2006-11-29 05:45:03 +00:00
$oldVal = isset ( $this -> mTagHooks [ $tag ] ) ? $this -> mTagHooks [ $tag ] : null ;
2004-06-12 06:15:09 +00:00
$this -> mTagHooks [ $tag ] = $callback ;
2006-01-07 13:31:29 +00:00
2004-06-09 12:15:42 +00:00
return $oldVal ;
}
2004-10-15 17:39:10 +00:00
2006-04-05 09:40:25 +00:00
/**
* Create a function , e . g . {{ sum : 1 | 2 | 3 }}
* The callback function should have the form :
* function myParserFunction ( & $parser , $arg1 , $arg2 , $arg3 ) { ... }
*
2006-10-17 08:49:27 +00:00
* The callback may either return the text result of the function , or an array with the text
* in element 0 , and a number of flags in the other elements . The names of the flags are
2006-04-05 09:40:25 +00:00
* specified in the keys . Valid flags are :
2006-10-17 08:49:27 +00:00
* found The text returned is valid , stop processing the template . This
2006-04-05 09:40:25 +00:00
* is on by default .
* nowiki Wiki markup in the return value should be escaped
* noparse Unsafe HTML tags should not be stripped , etc .
* noargs Don ' t replace triple - brace arguments in the return value
* isHTML The returned text is HTML , armour it against wikitext transformation
*
2006-04-19 15:46:24 +00:00
* @ public
2006-04-05 09:40:25 +00:00
*
2006-08-04 17:59:59 +00:00
* @ param string $id The magic word ID
2006-04-05 09:40:25 +00:00
* @ param mixed $callback The callback function ( and object ) to use
2006-10-17 08:49:27 +00:00
* @ param integer $flags a combination of the following flags :
2006-07-03 11:07:00 +00:00
* SFH_NO_HASH No leading hash , i . e . {{ plural :... }} instead of {{ #if:...}}
2006-04-05 09:40:25 +00:00
*
* @ return The old callback function for this name , if any
*/
2006-07-03 11:07:00 +00:00
function setFunctionHook ( $id , $callback , $flags = 0 ) {
2006-11-29 05:45:03 +00:00
$oldVal = isset ( $this -> mFunctionHooks [ $id ] ) ? $this -> mFunctionHooks [ $id ] : null ;
2006-06-25 21:24:14 +00:00
$this -> mFunctionHooks [ $id ] = $callback ;
2006-07-03 11:07:00 +00:00
2006-07-03 03:29:57 +00:00
# Add to function cache
2006-07-14 16:08:16 +00:00
$mw = MagicWord :: get ( $id );
2006-08-30 07:45:07 +00:00
if ( ! $mw )
throw new MWException ( 'Parser::setFunctionHook() expecting a magic word identifier.' );
2006-07-03 11:07:00 +00:00
2006-07-14 16:08:16 +00:00
$synonyms = $mw -> getSynonyms ();
$sensitive = intval ( $mw -> isCaseSensitive () );
2006-07-03 11:07:00 +00:00
foreach ( $synonyms as $syn ) {
# Case
if ( ! $sensitive ) {
$syn = strtolower ( $syn );
}
# Add leading hash
if ( ! ( $flags & SFH_NO_HASH ) ) {
$syn = '#' . $syn ;
}
# Remove trailing colon
if ( substr ( $syn , - 1 , 1 ) == ':' ) {
$syn = substr ( $syn , 0 , - 1 );
}
$this -> mFunctionSynonyms [ $sensitive ][ $syn ] = $id ;
2006-07-02 17:43:32 +00:00
}
2006-07-03 03:29:57 +00:00
return $oldVal ;
2006-07-02 17:43:32 +00:00
}
2006-08-30 07:45:07 +00:00
/**
* Get all registered function hook identifiers
*
* @ return array
*/
function getFunctionHooks () {
return array_keys ( $this -> mFunctionHooks );
}
2004-10-15 17:39:10 +00:00
/**
* Replace <!-- LINK --> link placeholders with actual links , in the buffer
2006-11-08 07:12:03 +00:00
* Placeholders created in Skin :: makeLinkObj ()
2004-10-15 17:39:10 +00:00
* Returns an array of links found , indexed by PDBK :
* 0 - broken
* 1 - normal link
* 2 - stub
* $options is a bit field , RLH_FOR_UPDATE to select for update
*/
function replaceLinkHolders ( & $text , $options = 0 ) {
2006-01-05 02:05:53 +00:00
global $wgUser ;
2006-10-17 08:49:27 +00:00
global $wgContLang ;
2004-10-15 17:39:10 +00:00
$fname = 'Parser::replaceLinkHolders' ;
wfProfileIn ( $fname );
$pdbks = array ();
$colours = array ();
2007-01-22 23:50:42 +00:00
$sk = $this -> mOptions -> getSkin ();
2006-01-05 02:05:53 +00:00
$linkCache =& LinkCache :: singleton ();
2005-07-03 07:15:53 +00:00
2005-04-27 07:48:14 +00:00
if ( ! empty ( $this -> mLinkHolders [ 'namespaces' ] ) ) {
2004-10-15 17:39:10 +00:00
wfProfileIn ( $fname . '-check' );
2007-01-22 23:50:42 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2004-12-19 08:00:50 +00:00
$page = $dbr -> tableName ( 'page' );
2004-10-15 17:39:10 +00:00
$threshold = $wgUser -> getOption ( 'stubthreshold' );
2005-07-03 07:15:53 +00:00
2004-10-15 17:39:10 +00:00
# Sort by namespace
2005-04-27 07:48:14 +00:00
asort ( $this -> mLinkHolders [ 'namespaces' ] );
2005-07-03 07:15:53 +00:00
2004-10-15 17:39:10 +00:00
# Generate query
$query = false ;
2006-10-17 08:49:27 +00:00
$current = null ;
2005-12-30 09:33:11 +00:00
foreach ( $this -> mLinkHolders [ 'namespaces' ] as $key => $ns ) {
2004-10-15 17:39:10 +00:00
# Make title object
2005-04-27 07:48:14 +00:00
$title = $this -> mLinkHolders [ 'titles' ][ $key ];
2004-10-15 17:39:10 +00:00
# Skip invalid entries.
# Result will be ugly, but prevents crash.
if ( is_null ( $title ) ) {
continue ;
}
$pdbk = $pdbks [ $key ] = $title -> getPrefixedDBkey ();
2005-12-30 09:33:11 +00:00
# Check if it's a static known link, e.g. interwiki
if ( $title -> isAlwaysKnown () ) {
$colours [ $pdbk ] = 1 ;
2006-01-05 02:05:53 +00:00
} elseif ( ( $id = $linkCache -> getGoodLinkID ( $pdbk ) ) != 0 ) {
2004-10-15 17:39:10 +00:00
$colours [ $pdbk ] = 1 ;
2005-12-30 11:10:51 +00:00
$this -> mOutput -> addLink ( $title , $id );
2006-01-05 02:05:53 +00:00
} elseif ( $linkCache -> isBadLink ( $pdbk ) ) {
2004-10-15 17:39:10 +00:00
$colours [ $pdbk ] = 0 ;
2007-05-26 16:23:34 +00:00
} elseif ( $title -> getNamespace () == NS_SPECIAL && ! SpecialPage :: exists ( $pdbk ) ) {
$colours [ $pdbk ] = 0 ;
2004-10-15 17:39:10 +00:00
} else {
# Not in the link cache, add it to the query
if ( ! isset ( $current ) ) {
2005-12-30 09:33:11 +00:00
$current = $ns ;
2004-12-19 08:00:50 +00:00
$query = " SELECT page_id, page_namespace, page_title " ;
2004-10-15 17:39:10 +00:00
if ( $threshold > 0 ) {
2005-03-12 11:51:02 +00:00
$query .= ', page_len, page_is_redirect' ;
2004-12-19 08:00:50 +00:00
}
2005-12-30 09:33:11 +00:00
$query .= " FROM $page WHERE (page_namespace= $ns AND page_title IN( " ;
} elseif ( $current != $ns ) {
$current = $ns ;
$query .= " )) OR (page_namespace= $ns AND page_title IN( " ;
2004-10-15 17:39:10 +00:00
} else {
$query .= ', ' ;
}
2005-07-03 07:15:53 +00:00
2005-04-27 07:48:14 +00:00
$query .= $dbr -> addQuotes ( $this -> mLinkHolders [ 'dbkeys' ][ $key ] );
2004-10-15 17:39:10 +00:00
}
}
if ( $query ) {
$query .= '))' ;
if ( $options & RLH_FOR_UPDATE ) {
$query .= ' FOR UPDATE' ;
}
2005-07-03 07:15:53 +00:00
2004-10-15 17:39:10 +00:00
$res = $dbr -> query ( $query , $fname );
2005-07-03 07:15:53 +00:00
2004-10-15 17:39:10 +00:00
# Fetch data and form into an associative array
# non-existent = broken
# 1 = known
# 2 = stub
while ( $s = $dbr -> fetchObject ( $res ) ) {
2004-12-19 08:00:50 +00:00
$title = Title :: makeTitle ( $s -> page_namespace , $s -> page_title );
2004-10-15 17:39:10 +00:00
$pdbk = $title -> getPrefixedDBkey ();
2006-01-05 02:05:53 +00:00
$linkCache -> addGoodLinkObj ( $s -> page_id , $title );
2005-12-30 11:10:51 +00:00
$this -> mOutput -> addLink ( $title , $s -> page_id );
2005-07-03 07:15:53 +00:00
2007-05-10 15:36:29 +00:00
$colours [ $pdbk ] = ( $threshold == 0 || (
$s -> page_len >= $threshold || # always true if $threshold <= 0
$s -> page_is_redirect ||
! Namespace :: isContent ( $s -> page_namespace ) )
2007-05-09 21:09:59 +00:00
? 1 : 2 );
2004-10-15 17:39:10 +00:00
}
}
wfProfileOut ( $fname . '-check' );
2005-07-03 07:15:53 +00:00
2006-12-11 23:33:27 +00:00
# Do a second query for different language variants of links and categories
2006-09-20 10:22:12 +00:00
if ( $wgContLang -> hasVariants ()){
2006-10-17 08:49:27 +00:00
$linkBatch = new LinkBatch ();
2006-12-11 23:33:27 +00:00
$variantMap = array (); // maps $pdbkey_Variant => $keys (of link holders)
$categoryMap = array (); // maps $category_variant => $category (dbkeys)
$varCategories = array (); // category replacements oldDBkey => newDBkey
$categories = $this -> mOutput -> getCategoryLinks ();
2006-09-20 10:22:12 +00:00
// Add variants of links to link batch
foreach ( $this -> mLinkHolders [ 'namespaces' ] as $key => $ns ) {
$title = $this -> mLinkHolders [ 'titles' ][ $key ];
if ( is_null ( $title ) )
continue ;
$pdbk = $title -> getPrefixedDBkey ();
2006-12-11 23:33:27 +00:00
$titleText = $title -> getText ();
2006-09-20 10:22:12 +00:00
// generate all variants of the link title text
2006-12-11 23:33:27 +00:00
$allTextVariants = $wgContLang -> convertLinkToAllVariants ( $titleText );
2006-09-20 10:22:12 +00:00
// if link was not found (in first query), add all variants to query
if ( ! isset ( $colours [ $pdbk ]) ){
foreach ( $allTextVariants as $textVariant ){
2006-12-11 23:33:27 +00:00
if ( $textVariant != $titleText ){
$variantTitle = Title :: makeTitle ( $ns , $textVariant );
if ( is_null ( $variantTitle )) continue ;
$linkBatch -> addObj ( $variantTitle );
$variantMap [ $variantTitle -> getPrefixedDBkey ()][] = $key ;
}
2006-09-20 10:22:12 +00:00
}
}
}
2006-10-17 08:49:27 +00:00
2006-12-11 23:33:27 +00:00
// process categories, check if a category exists in some variant
2007-05-31 16:01:26 +00:00
foreach ( $categories as $category ){
2006-12-11 23:33:27 +00:00
$variants = $wgContLang -> convertLinkToAllVariants ( $category );
foreach ( $variants as $variant ){
2006-12-14 01:06:28 +00:00
if ( $variant != $category ){
$variantTitle = Title :: newFromDBkey ( Title :: makeName ( NS_CATEGORY , $variant ) );
if ( is_null ( $variantTitle )) continue ;
$linkBatch -> addObj ( $variantTitle );
$categoryMap [ $variant ] = $category ;
}
2006-12-11 23:33:27 +00:00
}
}
2006-09-20 10:22:12 +00:00
if ( ! $linkBatch -> isEmpty ()){
// construct query
$titleClause = $linkBatch -> constructSet ( 'page' , $dbr );
$variantQuery = " SELECT page_id, page_namespace, page_title " ;
if ( $threshold > 0 ) {
$variantQuery .= ', page_len, page_is_redirect' ;
}
$variantQuery .= " FROM $page WHERE $titleClause " ;
if ( $options & RLH_FOR_UPDATE ) {
$variantQuery .= ' FOR UPDATE' ;
}
$varRes = $dbr -> query ( $variantQuery , $fname );
// for each found variants, figure out link holders and replace
while ( $s = $dbr -> fetchObject ( $varRes ) ) {
$variantTitle = Title :: makeTitle ( $s -> page_namespace , $s -> page_title );
$varPdbk = $variantTitle -> getPrefixedDBkey ();
2006-12-11 23:33:27 +00:00
$vardbk = $variantTitle -> getDBkey ();
2006-09-20 10:22:12 +00:00
2006-12-11 23:33:27 +00:00
$holderKeys = array ();
2006-12-12 18:57:19 +00:00
if ( isset ( $variantMap [ $varPdbk ])){
2006-12-11 23:33:27 +00:00
$holderKeys = $variantMap [ $varPdbk ];
2006-12-12 18:57:19 +00:00
$linkCache -> addGoodLinkObj ( $s -> page_id , $variantTitle );
$this -> mOutput -> addLink ( $variantTitle , $s -> page_id );
}
2006-09-20 10:22:12 +00:00
// loop over link holders
2006-10-17 08:49:27 +00:00
foreach ( $holderKeys as $key ){
2006-09-20 10:22:12 +00:00
$title = $this -> mLinkHolders [ 'titles' ][ $key ];
if ( is_null ( $title ) ) continue ;
$pdbk = $title -> getPrefixedDBkey ();
if ( ! isset ( $colours [ $pdbk ])){
// found link in some of the variants, replace the link holder data
$this -> mLinkHolders [ 'titles' ][ $key ] = $variantTitle ;
$this -> mLinkHolders [ 'dbkeys' ][ $key ] = $variantTitle -> getDBkey ();
2006-10-17 08:49:27 +00:00
2006-09-20 10:22:12 +00:00
// set pdbk and colour
$pdbks [ $key ] = $varPdbk ;
if ( $threshold > 0 ) {
$size = $s -> page_len ;
if ( $s -> page_is_redirect || $s -> page_namespace != 0 || $size >= $threshold ) {
$colours [ $varPdbk ] = 1 ;
} else {
$colours [ $varPdbk ] = 2 ;
}
2006-10-17 08:49:27 +00:00
}
2006-09-20 10:22:12 +00:00
else {
$colours [ $varPdbk ] = 1 ;
2006-10-17 08:49:27 +00:00
}
2006-09-20 10:22:12 +00:00
}
}
2006-12-11 23:33:27 +00:00
// check if the object is a variant of a category
if ( isset ( $categoryMap [ $vardbk ])){
$oldkey = $categoryMap [ $vardbk ];
if ( $oldkey != $vardbk )
2007-01-17 19:48:48 +00:00
$varCategories [ $oldkey ] = $vardbk ;
}
2006-12-11 23:33:27 +00:00
}
// rebuild the categories in original order (if there are replacements)
if ( count ( $varCategories ) > 0 ){
$newCats = array ();
$originalCats = $this -> mOutput -> getCategories ();
foreach ( $originalCats as $cat => $sortkey ){
// make the replacement
if ( array_key_exists ( $cat , $varCategories ) )
$newCats [ $varCategories [ $cat ]] = $sortkey ;
else $newCats [ $cat ] = $sortkey ;
}
$this -> mOutput -> setCategoryLinks ( $newCats );
2006-09-20 10:22:12 +00:00
}
}
}
2004-10-15 17:39:10 +00:00
# Construct search and replace arrays
wfProfileIn ( $fname . '-construct' );
2006-11-21 09:53:45 +00:00
$replacePairs = array ();
2005-04-27 07:48:14 +00:00
foreach ( $this -> mLinkHolders [ 'namespaces' ] as $key => $ns ) {
2004-10-15 17:39:10 +00:00
$pdbk = $pdbks [ $key ];
2005-04-27 07:48:14 +00:00
$searchkey = " <!--LINK $key --> " ;
$title = $this -> mLinkHolders [ 'titles' ][ $key ];
2004-10-15 17:39:10 +00:00
if ( empty ( $colours [ $pdbk ] ) ) {
2006-01-05 02:05:53 +00:00
$linkCache -> addBadLinkObj ( $title );
2004-10-15 17:39:10 +00:00
$colours [ $pdbk ] = 0 ;
2005-12-30 11:10:51 +00:00
$this -> mOutput -> addLink ( $title , 0 );
2006-11-21 09:53:45 +00:00
$replacePairs [ $searchkey ] = $sk -> makeBrokenLinkObj ( $title ,
2005-04-27 07:48:14 +00:00
$this -> mLinkHolders [ 'texts' ][ $key ],
$this -> mLinkHolders [ 'queries' ][ $key ] );
2004-10-15 17:39:10 +00:00
} elseif ( $colours [ $pdbk ] == 1 ) {
2006-11-21 09:53:45 +00:00
$replacePairs [ $searchkey ] = $sk -> makeKnownLinkObj ( $title ,
2005-04-27 07:48:14 +00:00
$this -> mLinkHolders [ 'texts' ][ $key ],
$this -> mLinkHolders [ 'queries' ][ $key ] );
2004-10-15 17:39:10 +00:00
} elseif ( $colours [ $pdbk ] == 2 ) {
2006-11-21 09:53:45 +00:00
$replacePairs [ $searchkey ] = $sk -> makeStubLinkObj ( $title ,
2005-04-27 07:48:14 +00:00
$this -> mLinkHolders [ 'texts' ][ $key ],
$this -> mLinkHolders [ 'queries' ][ $key ] );
2004-10-15 17:39:10 +00:00
}
}
2006-11-21 09:53:45 +00:00
$replacer = new HashtableReplacer ( $replacePairs , 1 );
2004-10-15 17:39:10 +00:00
wfProfileOut ( $fname . '-construct' );
# Do the thing
wfProfileIn ( $fname . '-replace' );
$text = preg_replace_callback (
'/(<!--LINK .*?-->)/' ,
2006-11-21 09:53:45 +00:00
$replacer -> cb (),
2004-10-15 17:39:10 +00:00
$text );
2005-04-27 07:48:14 +00:00
2004-10-15 17:39:10 +00:00
wfProfileOut ( $fname . '-replace' );
2004-11-13 14:24:42 +00:00
}
2004-10-15 17:39:10 +00:00
2005-04-27 07:48:14 +00:00
# Now process interwiki link holders
# This is quite a bit simpler than internal links
2005-06-03 05:46:24 +00:00
if ( ! empty ( $this -> mInterwikiLinkHolders [ 'texts' ] ) ) {
2004-10-15 17:39:10 +00:00
wfProfileIn ( $fname . '-interwiki' );
2005-04-27 07:48:14 +00:00
# Make interwiki link HTML
2006-11-21 09:53:45 +00:00
$replacePairs = array ();
2005-06-03 05:46:24 +00:00
foreach ( $this -> mInterwikiLinkHolders [ 'texts' ] as $key => $link ) {
$title = $this -> mInterwikiLinkHolders [ 'titles' ][ $key ];
2006-11-21 09:53:45 +00:00
$replacePairs [ $key ] = $sk -> makeLinkObj ( $title , $link );
2005-04-27 07:48:14 +00:00
}
2006-11-21 09:53:45 +00:00
$replacer = new HashtableReplacer ( $replacePairs , 1 );
2005-07-03 07:15:53 +00:00
2004-10-15 17:39:10 +00:00
$text = preg_replace_callback (
'/<!--IWLINK (.*?)-->/' ,
2006-11-21 09:53:45 +00:00
$replacer -> cb (),
2004-11-20 11:28:37 +00:00
$text );
2004-10-15 17:39:10 +00:00
wfProfileOut ( $fname . '-interwiki' );
}
2005-07-03 07:15:53 +00:00
2004-10-15 17:39:10 +00:00
wfProfileOut ( $fname );
return $colours ;
}
2005-07-03 07:15:53 +00:00
2005-05-31 08:49:03 +00:00
/**
* Replace <!-- LINK --> link placeholders with plain text of links
* ( not HTML - formatted ) .
* @ param string $text
* @ return string
*/
function replaceLinkHoldersText ( $text ) {
$fname = 'Parser::replaceLinkHoldersText' ;
wfProfileIn ( $fname );
$text = preg_replace_callback (
'/<!--(LINK|IWLINK) (.*?)-->/' ,
array ( & $this , 'replaceLinkHoldersTextCallback' ),
$text );
2005-07-03 07:15:53 +00:00
2005-05-31 08:49:03 +00:00
wfProfileOut ( $fname );
return $text ;
}
2005-07-03 07:15:53 +00:00
2005-05-31 08:49:03 +00:00
/**
* @ param array $matches
* @ return string
2006-04-19 15:46:24 +00:00
* @ private
2005-05-31 08:49:03 +00:00
*/
function replaceLinkHoldersTextCallback ( $matches ) {
$type = $matches [ 1 ];
$key = $matches [ 2 ];
if ( $type == 'LINK' ) {
if ( isset ( $this -> mLinkHolders [ 'texts' ][ $key ] ) ) {
return $this -> mLinkHolders [ 'texts' ][ $key ];
}
} elseif ( $type == 'IWLINK' ) {
2005-06-03 05:46:24 +00:00
if ( isset ( $this -> mInterwikiLinkHolders [ 'texts' ][ $key ] ) ) {
return $this -> mInterwikiLinkHolders [ 'texts' ][ $key ];
2005-05-31 08:49:03 +00:00
}
}
return $matches [ 0 ];
}
2004-11-20 11:28:37 +00:00
2006-06-06 22:56:38 +00:00
/**
* Tag hook handler for 'pre' .
*/
2006-10-17 08:49:27 +00:00
function renderPreTag ( $text , $attribs ) {
2006-06-06 22:56:38 +00:00
// Backwards-compatibility hack
2006-11-21 09:53:45 +00:00
$content = StringUtils :: delimiterReplace ( '<nowiki>' , '</nowiki>' , '$1' , $text , 'i' );
2006-07-11 17:40:11 +00:00
2006-06-06 22:56:38 +00:00
$attribs = Sanitizer :: validateTagAttributes ( $attribs , 'pre' );
return wfOpenElement ( 'pre' , $attribs ) .
2006-11-21 09:53:45 +00:00
Xml :: escapeTagsOnly ( $content ) .
2006-06-06 22:56:38 +00:00
'</pre>' ;
}
2006-07-11 17:40:11 +00:00
2004-11-13 12:04:31 +00:00
/**
* Renders an image gallery from a text with one line per image .
* text labels may be given by using |- style alternative text . E . g .
* Image : one . jpg | The number " 1 "
* Image : tree . jpg | A tree
* given as text will return the HTML of a gallery with two images ,
* labeled 'The number "1"' and
* 'A tree' .
*/
2006-06-24 00:12:34 +00:00
function renderImageGallery ( $text , $params ) {
2004-11-13 12:04:31 +00:00
$ig = new ImageGallery ();
2007-01-20 22:34:05 +00:00
$ig -> setContextTitle ( $this -> mTitle );
2004-11-13 12:04:31 +00:00
$ig -> setShowBytes ( false );
$ig -> setShowFilename ( false );
2006-05-16 19:15:58 +00:00
$ig -> setParsing ();
2007-07-13 17:25:06 +00:00
$ig -> setAttributes ( Sanitizer :: validateTagAttributes ( $params , 'table' ) );
2006-11-08 07:12:03 +00:00
$ig -> useSkin ( $this -> mOptions -> getSkin () );
2007-05-31 16:01:26 +00:00
$ig -> mRevisionId = $this -> mRevisionId ;
2004-11-20 11:28:37 +00:00
2007-01-04 19:47:11 +00:00
if ( isset ( $params [ 'caption' ] ) ) {
$caption = $params [ 'caption' ];
$caption = htmlspecialchars ( $caption );
$caption = $this -> replaceInternalLinks ( $caption );
2007-01-05 01:07:04 +00:00
$ig -> setCaptionHtml ( $caption );
2007-01-04 19:47:11 +00:00
}
2007-02-02 03:32:03 +00:00
if ( isset ( $params [ 'perrow' ] ) ) {
$ig -> setPerRow ( $params [ 'perrow' ] );
}
if ( isset ( $params [ 'widths' ] ) ) {
$ig -> setWidths ( $params [ 'widths' ] );
}
if ( isset ( $params [ 'heights' ] ) ) {
$ig -> setHeights ( $params [ 'heights' ] );
}
2007-05-31 16:01:26 +00:00
wfRunHooks ( 'BeforeParserrenderImageGallery' , array ( & $this , & $ig ) );
2006-07-11 17:40:11 +00:00
2006-06-24 00:12:34 +00:00
$lines = explode ( " \n " , $text );
2004-11-13 12:04:31 +00:00
foreach ( $lines as $line ) {
2004-11-20 11:28:37 +00:00
# match lines like these:
# Image:someimage.jpg|This is some image
2006-10-17 08:49:27 +00:00
$matches = array ();
2004-11-13 12:04:31 +00:00
preg_match ( " /^([^|]+)( \\ |(.*))? $ / " , $line , $matches );
# Skip empty lines
if ( count ( $matches ) == 0 ) {
continue ;
}
2006-07-11 14:03:17 +00:00
$tp = Title :: newFromText ( $matches [ 1 ] );
$nt =& $tp ;
2004-12-15 09:07:21 +00:00
if ( is_null ( $nt ) ) {
# Bogus title. Ignore these so we don't bomb out later.
continue ;
}
2004-11-13 12:04:31 +00:00
if ( isset ( $matches [ 3 ] ) ) {
$label = $matches [ 3 ];
} else {
$label = '' ;
}
2005-07-03 07:15:53 +00:00
2006-06-01 21:22:05 +00:00
$pout = $this -> parse ( $label ,
$this -> mTitle ,
$this -> mOptions ,
false , // Strip whitespace...?
false // Don't clear state!
);
2006-01-07 00:44:28 +00:00
$html = $pout -> getText ();
2005-07-03 07:15:53 +00:00
2007-05-30 21:02:32 +00:00
$ig -> add ( $nt , $html );
2006-05-09 18:01:15 +00:00
# Only add real images (bug #5586)
if ( $nt -> getNamespace () == NS_IMAGE ) {
$this -> mOutput -> addImage ( $nt -> getDBkey () );
}
2004-11-13 12:04:31 +00:00
}
return $ig -> toHTML ();
}
2005-04-27 07:48:14 +00:00
/**
* Parse image options text and use it to make an image
*/
2006-10-17 08:49:27 +00:00
function makeImage ( $nt , $options ) {
2007-04-20 12:31:36 +00:00
# @TODO: let the MediaHandler specify its transform parameters
#
2005-04-27 07:48:14 +00:00
# Check if the options text is of the form "options|alt text"
# Options are:
# * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang
# * left no resizing, just left align. label is used for alt= only
# * right same, but right aligned
# * none same, but not aligned
# * ___px scale to ___ pixels width, no aligning. e.g. use in taxobox
# * center center the image
# * framed Keep original image size, no magnify-button.
2007-05-28 19:15:43 +00:00
# * frameless like 'thumb' but without a frame. Keeps user preferences for width
2007-05-21 18:56:03 +00:00
# * upright reduce width for upright images, rounded to full __0 px
2007-05-22 21:39:57 +00:00
# * border draw a 1px border around the image
2007-02-02 00:44:42 +00:00
# vertical-align values (no % or length right now):
# * baseline
# * sub
# * super
# * top
# * text-top
# * middle
# * bottom
# * text-bottom
2005-04-27 07:48:14 +00:00
2007-04-20 12:31:36 +00:00
2007-03-17 19:18:21 +00:00
$part = array_map ( 'trim' , explode ( '|' , $options ) );
2005-04-27 07:48:14 +00:00
2007-02-02 00:44:42 +00:00
$mwAlign = array ();
$alignments = array ( 'left' , 'right' , 'center' , 'none' , 'baseline' , 'sub' , 'super' , 'top' , 'text-top' , 'middle' , 'bottom' , 'text-bottom' );
foreach ( $alignments as $alignment ) {
$mwAlign [ $alignment ] =& MagicWord :: get ( 'img_' . $alignment );
}
2006-07-14 15:39:23 +00:00
$mwThumb =& MagicWord :: get ( 'img_thumbnail' );
$mwManualThumb =& MagicWord :: get ( 'img_manualthumb' );
$mwWidth =& MagicWord :: get ( 'img_width' );
$mwFramed =& MagicWord :: get ( 'img_framed' );
2007-05-28 19:15:43 +00:00
$mwFrameless =& MagicWord :: get ( 'img_frameless' );
2007-05-21 18:56:03 +00:00
$mwUpright =& MagicWord :: get ( 'img_upright' );
2007-05-22 21:39:57 +00:00
$mwBorder =& MagicWord :: get ( 'img_border' );
2006-08-27 16:07:45 +00:00
$mwPage =& MagicWord :: get ( 'img_page' );
2005-04-27 07:48:14 +00:00
$caption = '' ;
2007-04-20 12:31:36 +00:00
$params = array ();
$framed = $thumb = false ;
2005-06-27 01:35:22 +00:00
$manual_thumb = '' ;
2007-02-02 00:44:42 +00:00
$align = $valign = '' ;
2007-04-20 12:31:36 +00:00
$sk = $this -> mOptions -> getSkin ();
2005-04-27 07:48:14 +00:00
2006-10-17 08:49:27 +00:00
foreach ( $part as $val ) {
2007-04-20 12:31:36 +00:00
if ( ! is_null ( $mwThumb -> matchVariableStartToEnd ( $val ) ) ) {
2005-04-27 07:48:14 +00:00
$thumb = true ;
2007-05-21 18:56:03 +00:00
} elseif ( ! is_null ( $match = $mwUpright -> matchVariableStartToEnd ( $val ) ) ) {
2007-05-28 19:15:43 +00:00
$params [ 'upright' ] = true ;
$params [ 'upright_factor' ] = floatval ( $match );
} elseif ( ! is_null ( $match = $mwFrameless -> matchVariableStartToEnd ( $val ) ) ) {
$params [ 'frameless' ] = true ;
2007-05-22 21:39:57 +00:00
} elseif ( ! is_null ( $mwBorder -> matchVariableStartToEnd ( $val ) ) ) {
2007-05-28 19:15:43 +00:00
$params [ 'border' ] = true ;
2005-08-20 13:27:21 +00:00
} elseif ( ! is_null ( $match = $mwManualThumb -> matchVariableStartToEnd ( $val ) ) ) {
2005-04-27 07:48:14 +00:00
# use manually specified thumbnail
$thumb = true ;
2005-08-20 13:27:21 +00:00
$manual_thumb = $match ;
2007-02-02 00:44:42 +00:00
} else {
foreach ( $alignments as $alignment ) {
if ( ! is_null ( $mwAlign [ $alignment ] -> matchVariableStartToEnd ( $val ) ) ) {
switch ( $alignment ) {
case 'left' : case 'right' : case 'center' : case 'none' :
$align = $alignment ; break ;
default :
$valign = $alignment ;
}
continue 2 ;
}
}
2007-04-20 12:31:36 +00:00
if ( ! is_null ( $match = $mwPage -> matchVariableStartToEnd ( $val ) ) ) {
2007-02-02 00:44:42 +00:00
# Select a page in a multipage document
2007-04-20 12:31:36 +00:00
$params [ 'page' ] = $match ;
} elseif ( ! isset ( $params [ 'width' ] ) && ! is_null ( $match = $mwWidth -> matchVariableStartToEnd ( $val ) ) ) {
2007-02-02 00:44:42 +00:00
wfDebug ( " img_width match: $match\n " );
# $match is the image width in pixels
$m = array ();
if ( preg_match ( '/^([0-9]*)x([0-9]*)$/' , $match , $m ) ) {
2007-04-20 12:31:36 +00:00
$params [ 'width' ] = intval ( $m [ 1 ] );
$params [ 'height' ] = intval ( $m [ 2 ] );
2007-02-02 00:44:42 +00:00
} else {
2007-04-20 12:31:36 +00:00
$params [ 'width' ] = intval ( $match );
2007-02-02 00:44:42 +00:00
}
} elseif ( ! is_null ( $mwFramed -> matchVariableStartToEnd ( $val ) ) ) {
$framed = true ;
2006-12-22 01:02:52 +00:00
} else {
2007-02-02 00:44:42 +00:00
$caption = $val ;
2005-04-27 07:48:14 +00:00
}
}
}
# Strip bad stuff out of the alt text
2005-05-31 08:49:03 +00:00
$alt = $this -> replaceLinkHoldersText ( $caption );
2006-03-24 16:48:10 +00:00
# make sure there are no placeholders in thumbnail attributes
# that are later expanded to html- so expand them now and
# remove the tags
2006-11-21 09:53:45 +00:00
$alt = $this -> mStripState -> unstripBoth ( $alt );
2005-04-27 07:48:14 +00:00
$alt = Sanitizer :: stripAllTags ( $alt );
2007-05-31 16:01:26 +00:00
# Give extensions a chance to select the file revision for us
$skip = $time = false ;
wfRunHooks ( 'BeforeParserMakeImageLinkObj' , array ( & $this , & $nt , & $skip , & $time ) );
2005-04-27 07:48:14 +00:00
# Linker does the rest
2007-05-31 16:01:26 +00:00
if ( $skip ) {
$link = $sk -> makeLinkObj ( $nt );
} else {
$link = $sk -> makeImageLinkObj ( $nt , $caption , $alt , $align , $params , $framed , $thumb , $manual_thumb , $valign , $time );
}
return $link ;
2005-04-27 07:48:14 +00:00
}
2005-08-07 12:09:46 +00:00
/**
2006-01-07 13:09:30 +00:00
* Set a flag in the output object indicating that the content is dynamic and
2005-08-07 12:09:46 +00:00
* shouldn ' t be cached .
*/
function disableCache () {
2006-05-13 17:40:59 +00:00
wfDebug ( " Parser output marked as uncacheable. \n " );
2005-08-07 12:09:46 +00:00
$this -> mOutput -> mCacheTime = - 1 ;
}
2006-01-07 13:31:29 +00:00
2006-03-11 17:13:49 +00:00
/** #@+
2005-08-23 21:49:48 +00:00
* Callback from the Sanitizer for expanding items found in HTML attribute
* values , so they can be safely tested and escaped .
* @ param string $text
* @ param array $args
* @ return string
2006-04-19 15:46:24 +00:00
* @ private
2005-08-23 21:49:48 +00:00
*/
function attributeStripCallback ( & $text , $args ) {
$text = $this -> replaceVariables ( $text , $args );
2006-11-21 09:53:45 +00:00
$text = $this -> mStripState -> unstripBoth ( $text );
2005-08-29 23:34:37 +00:00
return $text ;
}
2006-01-07 13:31:29 +00:00
2006-01-08 15:13:37 +00:00
/**#@-*/
2005-12-19 01:02:29 +00:00
2006-01-08 15:13:37 +00:00
/** #@+
* Accessor / mutator
*/
2005-12-19 01:02:29 +00:00
function Title ( $x = NULL ) { return wfSetVar ( $this -> mTitle , $x ); }
function Options ( $x = NULL ) { return wfSetVar ( $this -> mOptions , $x ); }
function OutputType ( $x = NULL ) { return wfSetVar ( $this -> mOutputType , $x ); }
2006-01-08 15:13:37 +00:00
/**#@-*/
/** #@+
* Accessor
*/
function getTags () { return array_keys ( $this -> mTagHooks ); }
/**#@-*/
2006-06-06 00:51:34 +00:00
/**
* Break wikitext input into sections , and either pull or replace
* some particular section ' s text .
*
* External callers should use the getSection and replaceSection methods .
*
* @ param $text Page wikitext
* @ param $section Numbered section . 0 pulls the text before the first
* heading ; other numbers will pull the given section
* along with its lower - level subsections .
* @ param $mode One of " get " or " replace "
* @ param $newtext Replacement text for section data .
* @ return string for " get " , the extracted section text .
* for " replace " , the whole page with the section replaced .
*/
private function extractSections ( $text , $section , $mode , $newtext = '' ) {
2007-06-19 16:00:11 +00:00
# I.... _hope_ this is right.
# Otherwise, sometimes we don't have things initialized properly.
$this -> clearState ();
2006-06-06 00:51:34 +00:00
# strip NOWIKI etc. to avoid confusion (true-parameter causes HTML
# comments to be stripped as well)
2006-11-21 09:53:45 +00:00
$stripState = new StripState ;
2006-07-11 17:40:11 +00:00
2006-06-06 00:51:34 +00:00
$oldOutputType = $this -> mOutputType ;
$oldOptions = $this -> mOptions ;
$this -> mOptions = new ParserOptions ();
2006-08-14 07:10:31 +00:00
$this -> setOutputType ( OT_WIKI );
2006-07-11 17:40:11 +00:00
2006-11-21 09:53:45 +00:00
$striptext = $this -> strip ( $text , $stripState , true );
2006-07-11 17:40:11 +00:00
2006-08-14 07:10:31 +00:00
$this -> setOutputType ( $oldOutputType );
2006-06-06 00:51:34 +00:00
$this -> mOptions = $oldOptions ;
# now that we can be sure that no pseudo-sections are in the source,
# split it up by section
$uniq = preg_quote ( $this -> uniqPrefix (), '/' );
2007-07-11 20:44:33 +00:00
$comment = " (?: $uniq -!--.*?QINU \x07 ) " ;
2006-06-06 00:51:34 +00:00
$secs = preg_split (
" /
(
^
( ? : $comment |< \ / ? noinclude > ) * # Initial comments will be stripped
( =+ ) # Should this be limited to 6?
.+ ? # Section title...
\\2 # Ending = count must match start
2006-06-21 23:13:05 +00:00
( ? : $comment |< \ / ? noinclude >| [ \\t ] + ) * # Trailing whitespace ok
2007-05-08 15:11:56 +00:00
$
2006-06-06 00:51:34 +00:00
|
< h ([ 1 - 6 ]) \b .* ?>
.* ?
< \ / h\\3\s *>
)
/ mix " ,
$striptext , - 1 ,
PREG_SPLIT_DELIM_CAPTURE );
2006-07-11 17:40:11 +00:00
2006-06-06 00:51:34 +00:00
if ( $mode == " get " ) {
if ( $section == 0 ) {
// "Section 0" returns the content before any other section.
$rv = $secs [ 0 ];
} else {
2007-03-14 18:20:21 +00:00
//track missing section, will replace if found.
$rv = $newtext ;
2006-06-06 00:51:34 +00:00
}
} elseif ( $mode == " replace " ) {
if ( $section == 0 ) {
$rv = $newtext . " \n \n " ;
$remainder = true ;
} else {
$rv = $secs [ 0 ];
$remainder = false ;
}
}
$count = 0 ;
$sectionLevel = 0 ;
for ( $index = 1 ; $index < count ( $secs ); ) {
$headerLine = $secs [ $index ++ ];
if ( $secs [ $index ] ) {
// A wiki header
$headerLevel = strlen ( $secs [ $index ++ ] );
} else {
// An HTML header
$index ++ ;
$headerLevel = intval ( $secs [ $index ++ ] );
}
$content = $secs [ $index ++ ];
$count ++ ;
if ( $mode == " get " ) {
if ( $count == $section ) {
$rv = $headerLine . $content ;
$sectionLevel = $headerLevel ;
} elseif ( $count > $section ) {
if ( $sectionLevel && $headerLevel > $sectionLevel ) {
$rv .= $headerLine . $content ;
} else {
// Broke out to a higher-level section
break ;
}
}
} elseif ( $mode == " replace " ) {
if ( $count < $section ) {
$rv .= $headerLine . $content ;
} elseif ( $count == $section ) {
$rv .= $newtext . " \n \n " ;
$sectionLevel = $headerLevel ;
} elseif ( $count > $section ) {
if ( $headerLevel <= $sectionLevel ) {
// Passed the section's sub-parts.
$remainder = true ;
}
if ( $remainder ) {
$rv .= $headerLine . $content ;
}
}
}
}
2007-03-14 18:20:21 +00:00
if ( is_string ( $rv ))
# reinsert stripped tags
$rv = trim ( $stripState -> unstripBoth ( $rv ) );
2006-06-06 00:51:34 +00:00
return $rv ;
}
2006-07-11 17:40:11 +00:00
2006-06-06 00:51:34 +00:00
/**
* This function returns the text of a section , specified by a number ( $section ) .
* A section is text under a heading like == Heading == or \ < h1\ > Heading\ </ h1\ > , or
* the first section before any such heading ( section 0 ) .
*
* If a section contains subsections , these are also returned .
*
* @ param $text String : text to look in
* @ param $section Integer : section number
2007-03-14 18:20:21 +00:00
* @ param $deftext : default to return if section is not found
2006-06-06 00:51:34 +00:00
* @ return string text of the requested section
*/
2007-03-14 18:20:21 +00:00
public function getSection ( $text , $section , $deftext = '' ) {
return $this -> extractSections ( $text , $section , " get " , $deftext );
2006-06-06 00:51:34 +00:00
}
2006-07-11 17:40:11 +00:00
2007-02-28 17:25:41 +00:00
public function replaceSection ( $oldtext , $section , $text ) {
2006-06-06 00:51:34 +00:00
return $this -> extractSections ( $oldtext , $section , " replace " , $text );
}
2006-11-21 09:53:45 +00:00
/**
2007-01-17 19:48:48 +00:00
* Get the timestamp associated with the current revision , adjusted for
2006-12-02 23:56:25 +00:00
* the default server - local timestamp
2006-11-21 09:53:45 +00:00
*/
function getRevisionTimestamp () {
if ( is_null ( $this -> mRevisionTimestamp ) ) {
wfProfileIn ( __METHOD__ );
global $wgContLang ;
2007-01-22 23:50:42 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2006-11-21 09:53:45 +00:00
$timestamp = $dbr -> selectField ( 'revision' , 'rev_timestamp' ,
2006-11-27 03:15:38 +00:00
array ( 'rev_id' => $this -> mRevisionId ), __METHOD__ );
2007-01-17 19:48:48 +00:00
2006-12-02 23:56:25 +00:00
// Normalize timestamp to internal MW format for timezone processing.
// This has the added side-effect of replacing a null value with
// the current time, which gives us more sensible behavior for
// previews.
$timestamp = wfTimestamp ( TS_MW , $timestamp );
2007-01-17 19:48:48 +00:00
2006-12-02 23:56:25 +00:00
// The cryptic '' timezone parameter tells to use the site-default
// timezone offset instead of the user settings.
//
// Since this value will be saved into the parser cache, served
// to other users, and potentially even used inside links and such,
// it needs to be consistent for all visitors.
$this -> mRevisionTimestamp = $wgContLang -> userAdjust ( $timestamp , '' );
2007-01-17 19:48:48 +00:00
2006-11-21 09:53:45 +00:00
wfProfileOut ( __METHOD__ );
}
return $this -> mRevisionTimestamp ;
}
2007-01-17 19:48:48 +00:00
2006-12-29 10:39:35 +00:00
/**
* Mutator for $mDefaultSort
*
* @ param $sort New value
*/
public function setDefaultSort ( $sort ) {
$this -> mDefaultSort = $sort ;
}
2007-01-17 19:48:48 +00:00
2006-12-29 10:39:35 +00:00
/**
* Accessor for $mDefaultSort
* Will use the title / prefixed title if none is set
*
* @ return string
*/
public function getDefaultSort () {
if ( $this -> mDefaultSort !== false ) {
return $this -> mDefaultSort ;
} else {
return $this -> mTitle -> getNamespace () == NS_CATEGORY
? $this -> mTitle -> getText ()
: $this -> mTitle -> getPrefixedText ();
}
}
2007-01-17 19:48:48 +00:00
2004-02-26 13:37:26 +00:00
}
2007-04-04 05:22:37 +00:00
/**
* @ todo document , briefly .
2007-04-20 08:55:14 +00:00
* @ addtogroup Parser
2007-04-04 05:22:37 +00:00
*/
2006-11-21 09:53:45 +00:00
class OnlyIncludeReplacer {
var $output = '' ;
2004-10-15 17:39:10 +00:00
2006-11-21 09:53:45 +00:00
function replace ( $matches ) {
if ( substr ( $matches [ 1 ], - 1 ) == " \n " ) {
$this -> output .= substr ( $matches [ 1 ], 0 , - 1 );
} else {
2007-03-04 16:11:25 +00:00
$this -> output .= $matches [ 1 ];
2006-11-21 09:53:45 +00:00
}
}
2006-04-29 01:58:39 +00:00
}
2007-04-04 05:22:37 +00:00
/**
* @ todo document , briefly .
2007-04-20 08:55:14 +00:00
* @ addtogroup Parser
2007-04-04 05:22:37 +00:00
*/
2006-11-21 09:53:45 +00:00
class StripState {
var $general , $nowiki ;
2006-05-13 17:30:42 +00:00
2006-11-21 09:53:45 +00:00
function __construct () {
$this -> general = new ReplacementArray ;
$this -> nowiki = new ReplacementArray ;
2006-06-16 13:58:42 +00:00
}
2006-11-21 09:53:45 +00:00
function unstripGeneral ( $text ) {
wfProfileIn ( __METHOD__ );
$text = $this -> general -> replace ( $text );
wfProfileOut ( __METHOD__ );
return $text ;
2006-06-12 12:38:41 +00:00
}
2006-11-21 09:53:45 +00:00
function unstripNoWiki ( $text ) {
wfProfileIn ( __METHOD__ );
$text = $this -> nowiki -> replace ( $text );
wfProfileOut ( __METHOD__ );
return $text ;
2004-08-13 15:55:59 +00:00
}
2006-11-21 09:53:45 +00:00
function unstripBoth ( $text ) {
wfProfileIn ( __METHOD__ );
$text = $this -> general -> replace ( $text );
$text = $this -> nowiki -> replace ( $text );
wfProfileOut ( __METHOD__ );
return $text ;
}
2004-08-13 15:55:59 +00:00
}
2006-01-23 19:05:09 +00:00
?>