summaryrefslogtreecommitdiff
path: root/includes/Parser.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/Parser.php')
-rw-r--r--includes/Parser.php420
1 files changed, 287 insertions, 133 deletions
diff --git a/includes/Parser.php b/includes/Parser.php
index 8e36e170..32e7f2a8 100644
--- a/includes/Parser.php
+++ b/includes/Parser.php
@@ -1,5 +1,7 @@
<?php
+
/**
+ *
* File for Parser and related classes
*
* @addtogroup Parser
@@ -10,7 +12,7 @@
* changes in an incompatible way, so the parser cache
* can automatically discard old data.
*/
-define( 'MW_PARSER_VERSION', '1.6.1' );
+define( 'MW_PARSER_VERSION', '1.6.2' );
define( 'RLH_FOR_UPDATE', 1 );
@@ -95,8 +97,9 @@ class Parser
* @private
*/
# Persistent:
- var $mTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables;
-
+ var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables,
+ $mImageParams, $mImageParamsMagicArray;
+
# Cleared with clearState():
var $mOutput, $mAutonumber, $mDTopen, $mStripState;
var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
@@ -126,11 +129,12 @@ class Parser
*/
function Parser() {
$this->mTagHooks = array();
+ $this->mTransparentTagHooks = array();
$this->mFunctionHooks = array();
$this->mFunctionSynonyms = array( 0 => array(), 1 => array() );
$this->mFirstCall = true;
}
-
+
/**
* Do various kinds of initialisation on the first call of the parser
*/
@@ -138,12 +142,12 @@ class Parser
if ( !$this->mFirstCall ) {
return;
}
-
+
wfProfileIn( __METHOD__ );
global $wgAllowDisplayTitle, $wgAllowSlowParserFunctions;
-
+
$this->setHook( 'pre', array( $this, 'renderPreTag' ) );
-
+
$this->setFunctionHook( 'int', array( 'CoreParserFunctions', 'intFunction' ), SFH_NO_HASH );
$this->setFunctionHook( 'ns', array( 'CoreParserFunctions', 'ns' ), SFH_NO_HASH );
$this->setFunctionHook( 'urlencode', array( 'CoreParserFunctions', 'urlencode' ), SFH_NO_HASH );
@@ -306,7 +310,7 @@ class Parser
$fixtags = array(
# french spaces, last one Guillemet-left
# only if there is something before the space
- '/(.) (?=\\?|:|;|!|\\302\\273)/' => '\\1&nbsp;\\2',
+ '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1&nbsp;\\2',
# french spaces, Guillemet-right
'/(\\302\\253) /' => '\\1&nbsp;',
);
@@ -327,6 +331,26 @@ class Parser
wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) );
+//!JF Move to its own function
+
+ $uniq_prefix = $this->mUniqPrefix;
+ $matches = array();
+ $elements = array_keys( $this->mTransparentTagHooks );
+ $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
+
+ foreach( $matches as $marker => $data ) {
+ list( $element, $content, $params, $tag ) = $data;
+ $tagName = strtolower( $element );
+ if( isset( $this->mTransparentTagHooks[$tagName] ) ) {
+ $output = call_user_func_array( $this->mTransparentTagHooks[$tagName],
+ array( $content, $params, $this ) );
+ } else {
+ $output = $tag;
+ }
+ $this->mStripState->general->setPair( $marker, $output );
+ }
+ $text = $this->mStripState->unstripGeneral( $text );
+
$text = Sanitizer::normalizeCharReferences( $text );
if (($wgUseTidy and $this->mOptions->mTidy) or $wgAlwaysUseTidy) {
@@ -398,12 +422,15 @@ class Parser
* Expand templates and variables in the text, producing valid, static wikitext.
* Also removes comments.
*/
- function preprocess( $text, $title, $options ) {
+ function preprocess( $text, $title, $options, $revid = null ) {
wfProfileIn( __METHOD__ );
$this->clearState();
$this->setOutputType( OT_PREPROCESS );
$this->mOptions = $options;
$this->mTitle = $title;
+ if( $revid !== null ) {
+ $this->mRevisionId = $revid;
+ }
wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
$text = $this->strip( $text, $this->mStripState );
wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
@@ -449,7 +476,7 @@ class Parser
* @param $text Source text string.
* @param $uniq_prefix
*
- * @private
+ * @public
* @static
*/
function extractTagsAndParams($elements, $text, &$matches, $uniq_prefix = ''){
@@ -480,7 +507,7 @@ class Parser
$inside = $p[4];
}
- $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . '-QINU';
+ $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . "-QINU\x07";
$stripped .= $marker;
if ( $close === '/>' ) {
@@ -587,7 +614,8 @@ class Parser
$output = Xml::escapeTagsOnly( $content );
break;
case 'math':
- $output = $wgContLang->armourMath( MathRenderer::renderMath( $content ) );
+ $output = $wgContLang->armourMath(
+ MathRenderer::renderMath( $content, $params ) );
break;
case 'gallery':
$output = $this->renderImageGallery( $content, $params );
@@ -725,7 +753,7 @@ class Parser
$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.
+ 2 => array('file', wfGetNull(), 'a')
);
$pipes = array();
$process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts$opts", $descriptorspec, $pipes);
@@ -1000,7 +1028,7 @@ class Parser
$text = strtr( $text, array( '<noinclude>' => '', '</noinclude>' => '') );
$text = StringUtils::delimiterReplace( '<includeonly>', '</includeonly>', '', $text );
- $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
+ $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ), array(), array_keys( $this->mTransparentTagHooks ) );
$text = $this->replaceVariables( $text, $args );
wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text, &$this->mStripState ) );
@@ -1797,11 +1825,15 @@ class Parser
$this->mOutput->addImage( $nt->getDBkey() );
continue;
} elseif( $ns == NS_SPECIAL ) {
- $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
+ if( SpecialPage::exists( $nt->getDBkey() ) ) {
+ $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
+ } else {
+ $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
+ }
continue;
} elseif( $ns == NS_IMAGE ) {
- $img = new Image( $nt );
- if( $img->exists() ) {
+ $img = wfFindFile( $nt );
+ if( $img ) {
// 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.
@@ -1918,15 +1950,22 @@ class Parser
wfProfileIn( $fname );
$ret = $target; # default return value is no change
- # bug 7425
- $target = trim( $target );
-
# Some namespaces don't allow subpages,
# so only perform processing if subpages are allowed
if( $this->areSubpagesAllowed() ) {
+ $hash = strpos( $target, '#' );
+ if( $hash !== false ) {
+ $suffix = substr( $target, $hash );
+ $target = substr( $target, 0, $hash );
+ } else {
+ $suffix = '';
+ }
+ # bug 7425
+ $target = trim( $target );
# Look at the first character
if( $target != '' && $target{0} == '/' ) {
# / at end means we don't want the slash to be shown
+ $m = array();
$trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
if( $trailingSlashes ) {
$noslash = $target = substr( $target, 1, -strlen($m[0][0]) );
@@ -1934,9 +1973,9 @@ class Parser
$noslash = substr( $target, 1 );
}
- $ret = $this->mTitle->getPrefixedText(). '/' . trim($noslash);
+ $ret = $this->mTitle->getPrefixedText(). '/' . trim($noslash) . $suffix;
if( '' === $text ) {
- $text = $target;
+ $text = $target . $suffix;
} # this might be changed for ugliness reasons
} else {
# check for .. subpage backlinks
@@ -1954,13 +1993,14 @@ class Parser
if( substr( $nodotdot, -1, 1 ) == '/' ) {
$nodotdot = substr( $nodotdot, 0, -1 );
if( '' === $text ) {
- $text = $nodotdot;
+ $text = $nodotdot . $suffix;
}
}
$nodotdot = trim( $nodotdot );
if( $nodotdot != '' ) {
$ret .= '/' . $nodotdot;
}
+ $ret .= $suffix;
}
}
}
@@ -2406,6 +2446,8 @@ class Parser
$oldtz = getenv( 'TZ' );
putenv( 'TZ='.$wgLocaltimezone );
}
+
+ wfSuppressWarnings(); // E_STRICT system time bitching
$localTimestamp = date( 'YmdHis', $ts );
$localMonth = date( 'm', $ts );
$localMonthName = date( 'n', $ts );
@@ -2418,20 +2460,21 @@ class Parser
if ( isset( $wgLocaltimezone ) ) {
putenv( 'TZ='.$oldtz );
}
+ wfRestoreWarnings();
switch ( $index ) {
case 'currentmonth':
- return $varCache[$index] = $wgContLang->formatNum( date( 'm', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'm', $ts ) );
case 'currentmonthname':
- return $varCache[$index] = $wgContLang->getMonthName( date( 'n', $ts ) );
+ return $varCache[$index] = $wgContLang->getMonthName( gmdate( 'n', $ts ) );
case 'currentmonthnamegen':
- return $varCache[$index] = $wgContLang->getMonthNameGen( date( 'n', $ts ) );
+ return $varCache[$index] = $wgContLang->getMonthNameGen( gmdate( 'n', $ts ) );
case 'currentmonthabbrev':
- return $varCache[$index] = $wgContLang->getMonthAbbreviation( date( 'n', $ts ) );
+ return $varCache[$index] = $wgContLang->getMonthAbbreviation( gmdate( 'n', $ts ) );
case 'currentday':
- return $varCache[$index] = $wgContLang->formatNum( date( 'j', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'j', $ts ) );
case 'currentday2':
- return $varCache[$index] = $wgContLang->formatNum( date( 'd', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'd', $ts ) );
case 'localmonth':
return $varCache[$index] = $wgContLang->formatNum( $localMonth );
case 'localmonthname':
@@ -2445,25 +2488,25 @@ class Parser
case 'localday2':
return $varCache[$index] = $wgContLang->formatNum( $localDay2 );
case 'pagename':
- return $this->mTitle->getText();
+ return wfEscapeWikiText( $this->mTitle->getText() );
case 'pagenamee':
return $this->mTitle->getPartialURL();
case 'fullpagename':
- return $this->mTitle->getPrefixedText();
+ return wfEscapeWikiText( $this->mTitle->getPrefixedText() );
case 'fullpagenamee':
return $this->mTitle->getPrefixedURL();
case 'subpagename':
- return $this->mTitle->getSubpageText();
+ return wfEscapeWikiText( $this->mTitle->getSubpageText() );
case 'subpagenamee':
return $this->mTitle->getSubpageUrlForm();
case 'basepagename':
- return $this->mTitle->getBaseText();
+ return wfEscapeWikiText( $this->mTitle->getBaseText() );
case 'basepagenamee':
return wfUrlEncode( str_replace( ' ', '_', $this->mTitle->getBaseText() ) );
case 'talkpagename':
if( $this->mTitle->canTalk() ) {
$talkPage = $this->mTitle->getTalkPage();
- return $talkPage->getPrefixedText();
+ return wfEscapeWikiText( $talkPage->getPrefixedText() );
} else {
return '';
}
@@ -2476,7 +2519,7 @@ class Parser
}
case 'subjectpagename':
$subjPage = $this->mTitle->getSubjectPage();
- return $subjPage->getPrefixedText();
+ return wfEscapeWikiText( $subjPage->getPrefixedText() );
case 'subjectpagenamee':
$subjPage = $this->mTitle->getSubjectPage();
return $subjPage->getPrefixedUrl();
@@ -2505,19 +2548,19 @@ class Parser
case 'subjectspacee':
return( wfUrlencode( $this->mTitle->getSubjectNsText() ) );
case 'currentdayname':
- return $varCache[$index] = $wgContLang->getWeekdayName( date( 'w', $ts ) + 1 );
+ return $varCache[$index] = $wgContLang->getWeekdayName( gmdate( 'w', $ts ) + 1 );
case 'currentyear':
- return $varCache[$index] = $wgContLang->formatNum( date( 'Y', $ts ), true );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'Y', $ts ), true );
case 'currenttime':
return $varCache[$index] = $wgContLang->time( wfTimestamp( TS_MW, $ts ), false, false );
case 'currenthour':
- return $varCache[$index] = $wgContLang->formatNum( date( 'H', $ts ), true );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'H', $ts ), true );
case 'currentweek':
// @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
// int to remove the padding
- return $varCache[$index] = $wgContLang->formatNum( (int)date( 'W', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( (int)gmdate( 'W', $ts ) );
case 'currentdow':
- return $varCache[$index] = $wgContLang->formatNum( date( 'w', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'w', $ts ) );
case 'localdayname':
return $varCache[$index] = $wgContLang->getWeekdayName( $localDayOfWeek + 1 );
case 'localyear':
@@ -3089,7 +3132,7 @@ class Parser
$found = false; //access denied
wfDebug( "$fname: template inclusion denied for " . $title->getPrefixedDBkey() );
} else {
- $articleContent = $this->fetchTemplate( $title );
+ list($articleContent,$title) = $this->fetchTemplateAndtitle( $title );
if ( $articleContent !== false ) {
$found = true;
$text = $articleContent;
@@ -3220,6 +3263,7 @@ class Parser
PREG_SPLIT_DELIM_CAPTURE);
$text = '';
$nsec = $headingOffset;
+
for( $i = 0; $i < count($m); $i += 2 ) {
$text .= $m[$i];
if (!isset($m[$i + 1]) || $m[$i + 1] == "") continue;
@@ -3255,13 +3299,26 @@ class Parser
/**
* Fetch the unparsed text of a template and register a reference to it.
*/
- function fetchTemplate( $title ) {
- $text = false;
+ function fetchTemplateAndtitle( $title ) {
+ $text = $skip = false;
+ $finalTitle = $title;
// Loop to fetch the article, with up to 1 redirect
for ( $i = 0; $i < 2 && is_object( $title ); $i++ ) {
- $rev = Revision::newFromTitle( $title );
- $this->mOutput->addTemplate( $title, $title->getArticleID() );
- if ( $rev ) {
+ # Give extensions a chance to select the revision instead
+ $id = false; // Assume current
+ wfRunHooks( 'BeforeParserFetchTemplateAndtitle', array( &$this, &$title, &$skip, &$id ) );
+
+ if( $skip ) {
+ $text = false;
+ $this->mOutput->addTemplate( $title, $title->getArticleID(), null );
+ 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 ) {
$text = $rev->getText();
} elseif( $title->getNamespace() == NS_MEDIAWIKI ) {
global $wgLang;
@@ -3278,9 +3335,15 @@ class Parser
break;
}
// Redirect?
+ $finalTitle = $title;
$title = Title::newFromRedirect( $text );
}
- return $text;
+ return array($text,$finalTitle);
+ }
+
+ function fetchTemplate( $title ) {
+ $rv = $this->fetchTemplateAndtitle($title);
+ return $rv[0];
}
/**
@@ -3375,7 +3438,13 @@ class Parser
}
/**
- * Detect __TOC__ magic word and set a placeholder
+ * 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.
*/
function stripToc( $text ) {
# if the string __NOTOC__ (not case-sensitive) occurs in the HTML,
@@ -3453,17 +3522,13 @@ class Parser
$enoughToc = true;
}
- # Never ever show TOC if no headers
- if( $numMatches < 1 ) {
- $enoughToc = false;
- }
-
# We need this to perform operations on the HTML
$sk = $this->mOptions->getSkin();
# headline counter
$headlineCount = 0;
$sectionCount = 0; # headlineCount excluding template sections
+ $numVisible = 0;
# Ugh .. the TOC should have neat indentation levels which can be
# passed to the skin functions. These are determined here
@@ -3504,7 +3569,9 @@ class Parser
$toclevel++;
$sublevelCount[$toclevel] = 0;
if( $toclevel<$wgMaxTocLevel ) {
+ $prevtoclevel = $toclevel;
$toc .= $sk->tocIndent();
+ $numVisible++;
}
}
elseif ( $level < $prevlevel && $toclevel > 1 ) {
@@ -3528,7 +3595,12 @@ class Parser
}
}
if( $toclevel<$wgMaxTocLevel ) {
- $toc .= $sk->tocUnindent( $prevtoclevel - $toclevel );
+ if($prevtoclevel < $wgMaxTocLevel) {
+ # Unindent only if the previous toc level was shown :p
+ $toc .= $sk->tocUnindent( $prevtoclevel - $toclevel );
+ } else {
+ $toc .= $sk->tocLineEnd();
+ }
}
}
else {
@@ -3569,12 +3641,21 @@ class Parser
"\$this->mInterwikiLinkHolders['texts'][\$1]",
$canonized_headline );
- # strip out HTML
- $canonized_headline = preg_replace( '/<.*?' . '>/','',$canonized_headline );
- $tocline = trim( $canonized_headline );
+ # Strip out HTML (other than plain <sup> and <sub>: bug 8393)
+ $tocline = preg_replace(
+ array( '#<(?!/?(sup|sub)).*?'.'>#', '#<(/?(sup|sub)).*?'.'>#' ),
+ array( '', '<$1>'),
+ $canonized_headline
+ );
+ $tocline = trim( $tocline );
+
+ # For the anchor, strip out HTML-y stuff period
+ $canonized_headline = preg_replace( '/<.*?'.'>/', '', $canonized_headline );
+ $canonized_headline = trim( $canonized_headline );
+
# Save headline for section edit hint before it's escaped
- $headline_hint = trim( $canonized_headline );
- $canonized_headline = Sanitizer::escapeId( $tocline );
+ $headline_hint = $canonized_headline;
+ $canonized_headline = Sanitizer::escapeId( $canonized_headline );
$refers[$headlineCount] = $canonized_headline;
# count how many in assoc. array so we can track dupes in anchors
@@ -3611,9 +3692,14 @@ class Parser
$sectionCount++;
}
+ # Never ever show TOC if no headers
+ if( $numVisible < 1 ) {
+ $enoughToc = false;
+ }
+
if( $enoughToc ) {
- if( $toclevel<$wgMaxTocLevel ) {
- $toc .= $sk->tocUnindent( $toclevel - 1 );
+ if( $prevtoclevel > 0 && $prevtoclevel < $wgMaxTocLevel ) {
+ $toc .= $sk->tocUnindent( $prevtoclevel - 1 );
}
$toc = $sk->tocList( $toc );
}
@@ -3759,11 +3845,16 @@ class Parser
* @private
*/
function getUserSig( &$user ) {
+ global $wgMaxSigChars;
+
$username = $user->getName();
$nickname = $user->getOption( 'nickname' );
$nickname = $nickname === '' ? $username : $nickname;
-
- if( $user->getBoolOption( 'fancysig' ) !== false ) {
+
+ if( mb_strlen( $nickname ) > $wgMaxSigChars ) {
+ $nickname = $username;
+ wfDebug( __METHOD__ . ": $username has overlong signature.\n" );
+ } elseif( $user->getBoolOption( 'fancysig' ) !== false ) {
# Sig. might contain markup; validate this
if( $this->validateSig( $nickname ) !== false ) {
# Validated; clean up (if needed) and return it
@@ -3903,6 +3994,14 @@ class Parser
return $oldVal;
}
+ function setTransparentTagHook( $tag, $callback ) {
+ $tag = strtolower( $tag );
+ $oldVal = isset( $this->mTransparentTagHooks[$tag] ) ? $this->mTransparentTagHooks[$tag] : null;
+ $this->mTransparentTagHooks[$tag] = $callback;
+
+ return $oldVal;
+ }
+
/**
* Create a function, e.g. {{sum:1|2|3}}
* The callback function should have the form:
@@ -4018,6 +4117,8 @@ class Parser
$this->mOutput->addLink( $title, $id );
} elseif ( $linkCache->isBadLink( $pdbk ) ) {
$colours[$pdbk] = 0;
+ } elseif ( $title->getNamespace() == NS_SPECIAL && !SpecialPage::exists( $pdbk ) ) {
+ $colours[$pdbk] = 0;
} else {
# Not in the link cache, add it to the query
if ( !isset( $current ) ) {
@@ -4055,16 +4156,11 @@ class Parser
$linkCache->addGoodLinkObj( $s->page_id, $title );
$this->mOutput->addLink( $title, $s->page_id );
- if ( $threshold > 0 ) {
- $size = $s->page_len;
- if ( $s->page_is_redirect || $s->page_namespace != 0 || $size >= $threshold ) {
- $colours[$pdbk] = 1;
- } else {
- $colours[$pdbk] = 2;
- }
- } else {
- $colours[$pdbk] = 1;
- }
+ $colours[$pdbk] = ( $threshold == 0 || (
+ $s->page_len >= $threshold || # always true if $threshold <= 0
+ $s->page_is_redirect ||
+ !Namespace::isContent( $s->page_namespace ) )
+ ? 1 : 2 );
}
}
wfProfileOut( $fname.'-check' );
@@ -4104,7 +4200,7 @@ class Parser
}
// process categories, check if a category exists in some variant
- foreach( $categories as $category){
+ foreach( $categories as $category ){
$variants = $wgContLang->convertLinkToAllVariants($category);
foreach($variants as $variant){
if($variant != $category){
@@ -4324,8 +4420,11 @@ class Parser
$ig->setContextTitle( $this->mTitle );
$ig->setShowBytes( false );
$ig->setShowFilename( false );
- $ig->setParsing();
+ $ig->setParser( $this );
+ $ig->setHideBadImages();
+ $ig->setAttributes( Sanitizer::validateTagAttributes( $params, 'table' ) );
$ig->useSkin( $this->mOptions->getSkin() );
+ $ig->mRevisionId = $this->mRevisionId;
if( isset( $params['caption'] ) ) {
$caption = $params['caption'];
@@ -4342,6 +4441,8 @@ class Parser
if( isset( $params['heights'] ) ) {
$ig->setHeights( $params['heights'] );
}
+
+ wfRunHooks( 'BeforeParserrenderImageGallery', array( &$this, &$ig ) );
$lines = explode( "\n", $text );
foreach ( $lines as $line ) {
@@ -4373,7 +4474,7 @@ class Parser
);
$html = $pout->getText();
- $ig->add( new Image( $nt ), $html );
+ $ig->add( $nt, $html );
# Only add real images (bug #5586)
if ( $nt->getNamespace() == NS_IMAGE ) {
@@ -4383,10 +4484,50 @@ class Parser
return $ig->toHTML();
}
+ function getImageParams( $handler ) {
+ if ( $handler ) {
+ $handlerClass = get_class( $handler );
+ } else {
+ $handlerClass = '';
+ }
+ if ( !isset( $this->mImageParams[$handlerClass] ) ) {
+ // Initialise static lists
+ static $internalParamNames = array(
+ 'horizAlign' => array( 'left', 'right', 'center', 'none' ),
+ 'vertAlign' => array( 'baseline', 'sub', 'super', 'top', 'text-top', 'middle',
+ 'bottom', 'text-bottom' ),
+ 'frame' => array( 'thumbnail', 'manualthumb', 'framed', 'frameless',
+ 'upright', 'border' ),
+ );
+ static $internalParamMap;
+ if ( !$internalParamMap ) {
+ $internalParamMap = array();
+ foreach ( $internalParamNames as $type => $names ) {
+ foreach ( $names as $name ) {
+ $magicName = str_replace( '-', '_', "img_$name" );
+ $internalParamMap[$magicName] = array( $type, $name );
+ }
+ }
+ }
+
+ // Add handler params
+ $paramMap = $internalParamMap;
+ if ( $handler ) {
+ $handlerParamMap = $handler->getParamMap();
+ foreach ( $handlerParamMap as $magic => $paramName ) {
+ $paramMap[$magic] = array( 'handler', $paramName );
+ }
+ }
+ $this->mImageParams[$handlerClass] = $paramMap;
+ $this->mImageParamsMagicArray[$handlerClass] = new MagicWordArray( array_keys( $paramMap ) );
+ }
+ return array( $this->mImageParams[$handlerClass], $this->mImageParamsMagicArray[$handlerClass] );
+ }
+
/**
* Parse image options text and use it to make an image
*/
- function makeImage( $nt, $options ) {
+ function makeImage( $title, $options ) {
# @TODO: let the MediaHandler specify its transform parameters
#
# Check if the options text is of the form "options|alt text"
@@ -4398,6 +4539,9 @@ class Parser
# * ___px scale to ___ pixels width, no aligning. e.g. use in taxobox
# * center center the image
# * framed Keep original image size, no magnify-button.
+ # * frameless like 'thumb' but without a frame. Keeps user preferences for width
+ # * upright reduce width for upright images, rounded to full __0 px
+ # * border draw a 1px border around the image
# vertical-align values (no % or length right now):
# * baseline
# * sub
@@ -4407,67 +4551,66 @@ class Parser
# * middle
# * bottom
# * text-bottom
+
+ $parts = array_map( 'trim', explode( '|', $options) );
+ $sk = $this->mOptions->getSkin();
+ # Give extensions a chance to select the file revision for us
+ $skip = $time = false;
+ wfRunHooks( 'BeforeParserMakeImageLinkObj', array( &$this, &$title, &$skip, &$time ) );
- $part = array_map( 'trim', explode( '|', $options) );
-
- $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 );
+ if ( $skip ) {
+ return $sk->makeLinkObj( $title );
}
- $mwThumb =& MagicWord::get( 'img_thumbnail' );
- $mwManualThumb =& MagicWord::get( 'img_manualthumb' );
- $mwWidth =& MagicWord::get( 'img_width' );
- $mwFramed =& MagicWord::get( 'img_framed' );
- $mwPage =& MagicWord::get( 'img_page' );
- $caption = '';
- $params = array();
- $framed = $thumb = false;
- $manual_thumb = '' ;
- $align = $valign = '';
- $sk = $this->mOptions->getSkin();
+ # Get parameter map
+ $file = wfFindFile( $title, $time );
+ $handler = $file ? $file->getHandler() : false;
- foreach( $part as $val ) {
- if ( !is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
- $thumb=true;
- } elseif ( ! is_null( $match = $mwManualThumb->matchVariableStartToEnd($val) ) ) {
- # use manually specified thumbnail
- $thumb=true;
- $manual_thumb = $match;
- } 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;
- }
- }
- if ( ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
- # Select a page in a multipage document
- $params['page'] = $match;
- } elseif ( !isset( $params['width'] ) && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
- wfDebug( "img_width match: $match\n" );
- # $match is the image width in pixels
+ list( $paramMap, $mwArray ) = $this->getImageParams( $handler );
+
+ # Process the input parameters
+ $caption = '';
+ $params = array( 'frame' => array(), 'handler' => array(),
+ 'horizAlign' => array(), 'vertAlign' => array() );
+ foreach( $parts as $part ) {
+ list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part );
+ if ( isset( $paramMap[$magicName] ) ) {
+ list( $type, $paramName ) = $paramMap[$magicName];
+ $params[$type][$paramName] = $value;
+
+ // Special case; width and height come in one variable together
+ if( $type == 'handler' && $paramName == 'width' ) {
$m = array();
- if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
- $params['width'] = intval( $m[1] );
- $params['height'] = intval( $m[2] );
+ if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $value, $m ) ) {
+ $params[$type]['width'] = intval( $m[1] );
+ $params[$type]['height'] = intval( $m[2] );
} else {
- $params['width'] = intval($match);
+ $params[$type]['width'] = intval( $value );
}
- } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
- $framed=true;
- } else {
- $caption = $val;
+ }
+ } else {
+ $caption = $part;
+ }
+ }
+
+ # Process alignment parameters
+ if ( $params['horizAlign'] ) {
+ $params['frame']['align'] = key( $params['horizAlign'] );
+ }
+ if ( $params['vertAlign'] ) {
+ $params['frame']['valign'] = key( $params['vertAlign'] );
+ }
+
+ # Validate the handler parameters
+ if ( $handler ) {
+ foreach ( $params['handler'] as $name => $value ) {
+ if ( !$handler->validateParam( $name, $value ) ) {
+ unset( $params['handler'][$name] );
}
}
}
+
# Strip bad stuff out of the alt text
$alt = $this->replaceLinkHoldersText( $caption );
@@ -4477,8 +4620,18 @@ class Parser
$alt = $this->mStripState->unstripBoth( $alt );
$alt = Sanitizer::stripAllTags( $alt );
+ $params['frame']['alt'] = $alt;
+ $params['frame']['caption'] = $caption;
+
# Linker does the rest
- return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $params, $framed, $thumb, $manual_thumb, $valign );
+ $ret = $sk->makeImageLink2( $title, $file, $params['frame'], $params['handler'] );
+
+ # Give the handler a chance to modify the parser object
+ if ( $handler ) {
+ $handler->parserTransformHook( $this, $file );
+ }
+
+ return $ret;
}
/**
@@ -4517,7 +4670,7 @@ class Parser
/**#@+
* Accessor
*/
- function getTags() { return array_keys( $this->mTagHooks ); }
+ function getTags() { return array_merge( array_keys($this->mTransparentTagHooks), array_keys( $this->mTagHooks ) ); }
/**#@-*/
@@ -4537,6 +4690,10 @@ class Parser
* for "replace", the whole page with the section replaced.
*/
private function extractSections( $text, $section, $mode, $newtext='' ) {
+ # I.... _hope_ this is right.
+ # Otherwise, sometimes we don't have things initialized properly.
+ $this->clearState();
+
# strip NOWIKI etc. to avoid confusion (true-parameter causes HTML
# comments to be stripped as well)
$stripState = new StripState;
@@ -4554,7 +4711,7 @@ class Parser
# now that we can be sure that no pseudo-sections are in the source,
# split it up by section
$uniq = preg_quote( $this->uniqPrefix(), '/' );
- $comment = "(?:$uniq-!--.*?QINU)";
+ $comment = "(?:$uniq-!--.*?QINU\x07)";
$secs = preg_split(
"/
(
@@ -4717,7 +4874,6 @@ class Parser
: $this->mTitle->getPrefixedText();
}
}
-
}
/**
@@ -4770,5 +4926,3 @@ class StripState {
return $text;
}
}
-
-?>