# Persistent:
var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables,
$mImageParams, $mImageParamsMagicArray, $mStripList, $mMarkerIndex, $mPreprocessor,
- $mExtLinkBracketedRegex, $mUrlProtocols, $mDefaultStripList, $mVarCache, $mConf;
+ $mExtLinkBracketedRegex, $mUrlProtocols, $mDefaultStripList, $mVarCache, $mConf,
+ $mFunctionTagHooks;
# Cleared with clearState():
var $mTplExpandCache; // empty-frame expansion cache
var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
var $mExpensiveFunctionCount; // number of expensive parser function calls
- var $mFileCache;
# Temporary
# These are variables reset at least once per parse regardless of $clearState
$this->mTagHooks = array();
$this->mTransparentTagHooks = array();
$this->mFunctionHooks = array();
+ $this->mFunctionTagHooks = array();
$this->mFunctionSynonyms = array( 0 => array(), 1 => array() );
$this->mDefaultStripList = $this->mStripList = array( 'nowiki', 'gallery' );
$this->mUrlProtocols = wfUrlProtocols();
$this->mHeadings = array();
$this->mDoubleUnderscores = array();
$this->mExpensiveFunctionCount = 0;
- $this->mFileCache = array();
# Fix cloning
if ( isset( $this->mPreprocessor ) && $this->mPreprocessor->parser !== $this ) {
/**
* Recursive parser entry point that can be called from an extension tag
* hook.
+ *
+ * If $frame is not provided, then template variables (e.g., {{{1}}}) within $text are not expanded
+ *
+ * @param $text String: text extension wants to have parsed
+ * @param PPFrame $frame: The frame to use for expanding any template variables
*/
- function recursiveTagParse( $text ) {
+ function recursiveTagParse( $text, $frame=false ) {
wfProfileIn( __METHOD__ );
wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
- $text = $this->internalParse( $text, false );
+ $text = $this->internalParse( $text, false, $frame );
wfProfileOut( __METHOD__ );
return $text;
}
$matches = array();
$taglist = implode( '|', $elements );
- $start = "/<($taglist)(\\s+[^>]*?|\\s*?)(\/?>)|<(!--)/i";
+ $start = "/<($taglist)(\\s+[^>]*?|\\s*?)(\/?" . ">)|<(!--)/i";
while ( '' != $text ) {
$p = preg_split( $start, $text, 2, PREG_SPLIT_DELIM_CAPTURE );
*
* @private
*/
- function internalParse( $text, $isMain = true ) {
+ function internalParse( $text, $isMain = true, $frame=false ) {
wfProfileIn( __METHOD__ );
$origText = $text;
return $text ;
}
- $text = $this->replaceVariables( $text );
+ // if $frame is provided, then use $frame for replacing any variables
+ if ($frame) {
+ // use frame depth to infer how include/noinclude tags should be handled
+ // depth=0 means this is the top-level document; otherwise it's an included document
+ if( !$frame->depth )
+ $flag = 0;
+ else
+ $flag = Parser::PTD_FOR_INCLUSION;
+ $dom = $this->preprocessToDom( $text, $flag );
+ $text = $frame->expand( $dom );
+ }
+ // if $frame is not provided, then use old-style replaceVariables
+ else {
+ $text = $this->replaceVariables( $text );
+ }
+
$text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ), false, array_keys( $this->mTransparentTagHooks ) );
wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text, &$this->mStripState ) );
return $this->makeFreeExternalLink( $m[0] );
} elseif ( isset( $m[4] ) && $m[4] !== '' ) {
# RFC or PMID
+ $CssClass = '';
if ( substr( $m[0], 0, 3 ) === 'RFC' ) {
$keyword = 'RFC';
$urlmsg = 'rfcurl';
+ $CssClass = 'mw-magiclink-rfc';
$id = $m[4];
} elseif ( substr( $m[0], 0, 4 ) === 'PMID' ) {
$keyword = 'PMID';
$urlmsg = 'pubmedurl';
+ $CssClass = 'mw-magiclink-pmid';
$id = $m[4];
} else {
throw new MWException( __METHOD__.': unrecognised match type "' .
}
$url = wfMsg( $urlmsg, $id);
$sk = $this->mOptions->getSkin();
- $la = $sk->getExternalLinkAttributes( $url, $keyword.$id );
+ $la = $sk->getExternalLinkAttributes( "external $CssClass" );
return "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>";
} elseif ( isset( $m[5] ) && $m[5] !== '' ) {
# ISBN
$titleObj = SpecialPage::getTitleFor( 'Booksources', $num );
return'<a href="' .
$titleObj->escapeLocalUrl() .
- "\" class=\"internal\">ISBN $isbn</a>";
+ "\" class=\"internal mw-magiclink-isbn\">ISBN $isbn</a>";
} else {
return $m[0];
}
* @private
*/
function maybeDoSubpageLink($target, &$text) {
- # 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
- # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
- # ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage
-
- wfProfileIn( __METHOD__ );
- $ret = $target; # default return value is no change
-
- # 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]) );
- } else {
- $noslash = substr( $target, 1 );
- }
-
- $ret = $this->mTitle->getPrefixedText(). '/' . trim($noslash) . $suffix;
- if( '' === $text ) {
- $text = $target . $suffix;
- } # this might be changed for ugliness reasons
- } else {
- # 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 ) {
- $text = $nodotdot . $suffix;
- }
- }
- $nodotdot = trim( $nodotdot );
- if( $nodotdot != '' ) {
- $ret .= '/' . $nodotdot;
- }
- $ret .= $suffix;
- }
- }
- }
- }
-
- wfProfileOut( __METHOD__ );
- return $ret;
+ return Linker::normalizeSubpageLink( $this->mTitle, $target, $text );
}
/**#@+
elseif ( ';' === $char ) {
$result .= '<dl><dt>';
$this->mDTopen = true;
- } elseif ( '>' === $char ) { $result .= "<blockquote><p>"; }
+ }
else { $result = '<!-- ERR 1 -->'; }
return $result;
/* private */ function nextItem( $char ) {
if ( '*' === $char || '#' === $char ) { return '</li><li>'; }
- elseif ( '>' === $char ) { return "</p><p>"; }
elseif ( ':' === $char || ';' === $char ) {
$close = '</dd>';
if ( $this->mDTopen ) { $close = '</dt>'; }
/* private */ function closeList( $char ) {
if ( '*' === $char ) { $text = '</li></ul>'; }
elseif ( '#' === $char ) { $text = '</li></ol>'; }
- elseif ( '>' === $char ) { $text = "</p></blockquote>"; }
elseif ( ':' === $char ) {
if ( $this->mDTopen ) {
$this->mDTopen = false;
// # = ol
// ; = dt
// : = dd
- // > = blockquote
$lastPrefixLength = strlen( $lastPrefix );
$preCloseMatch = preg_match('/<\\/pre/i', $oLine );
$preOpenMatch = preg_match('/<pre/i', $oLine );
-
- // Need to decode > --> > for blockquote syntax. Re-encode later.
- // To avoid collision with real >s, we temporarily convert them to >
- // This is a weird choice of armouring, but it's totally resistant to any
- // collision.
- $orig = $oLine;
- $oLine = strtr( $oLine, array( '>' => '>', '>' => '>' ) );
-
// If not in a <pre> element, scan for and figure out what prefixes are there.
if ( !$this->mInPre ) {
# Multiple prefixes may abut each other for nested lists.
- $prefixLength = strspn( $oLine, '*#:;>' );
+ $prefixLength = strspn( $oLine, '*#:;' );
$prefix = substr( $oLine, 0, $prefixLength );
# eh?
$prefix = $prefix2 = '';
$t = $oLine;
}
-
- // Re-encode >s now
- $t = strtr( $t, array( '>' => '>', '>' => '>' ) );
# List generation
if( $prefixLength && $lastPrefix === $prefix2 ) {
*
* @private
*/
- function getVariableValue( $index ) {
+ function getVariableValue( $index, $frame=false ) {
global $wgContLang, $wgSitename, $wgServer, $wgServerName, $wgScriptPath;
/**
return $wgContLanguageCode;
default:
$ret = null;
- if ( wfRunHooks( 'ParserGetVariableValueSwitch', array( &$this, &$this->mVarCache, &$index, &$ret ) ) )
+ if ( wfRunHooks( 'ParserGetVariableValueSwitch', array( &$this, &$this->mVarCache, &$index, &$ret, &$frame ) ) )
return $ret;
else
return null;
if ( !$found && $args->getLength() == 0 ) {
$id = $this->mVariables->matchStartToEnd( $part1 );
if ( $id !== false ) {
- $text = $this->getVariableValue( $id );
+ $text = $this->getVariableValue( $id, $frame );
if (MagicWord::getCacheTTL($id)>-1)
$this->mOutput->mContainsOldMagic = true;
$found = true;
$function = $this->mFunctionSynonyms[1][$function];
} else {
# Case insensitive functions
- $function = strtolower( $function );
+ $function = $wgContLang->lc( $function );
if ( isset( $this->mFunctionSynonyms[0][$function] ) ) {
$function = $this->mFunctionSynonyms[0][$function];
} else {
$marker = "{$this->mUniqPrefix}-$name-" . sprintf('%08X', $this->mMarkerIndex++) . self::MARKER_SUFFIX;
- if ( $this->ot['html'] ) {
+ $isFunctionTag = isset( $this->mFunctionTagHooks[strtolower($name)] ) &&
+ ( $this->ot['html'] || $this->ot['pre'] );
+ if ( $this->ot['html'] || $isFunctionTag ) {
$name = strtolower( $name );
-
$attributes = Sanitizer::decodeTagAttributes( $attrText );
if ( isset( $params['attributes'] ) ) {
$attributes = $attributes + $params['attributes'];
throw new MWException( "Tag hook for $name is not callable\n" );
}
$output = call_user_func_array( $this->mTagHooks[$name],
- array( $content, $attributes, $this ) );
+ array( $content, $attributes, $this, $frame ) );
+ } elseif( isset( $this->mFunctionTagHooks[$name] ) ) {
+ list( $callback, $flags ) = $this->mFunctionTagHooks[$name];
+ if( !is_callable( $callback ) )
+ throw new MWException( "Tag hook for $name is not callable\n" );
+
+ $output = call_user_func_array( $callback,
+ array( &$this, $frame, $content, $attributes ) );
} else {
$output = '<span class="error">Invalid tag extension name: ' .
htmlspecialchars( $name ) . '</span>';
}
}
- if ( $name === 'html' || $name === 'nowiki' ) {
+ if( $isFunctionTag ) {
+ return $output;
+ } elseif ( $name === 'html' || $name === 'nowiki' ) {
$this->mStripState->nowiki->setPair( $marker, $output );
} else {
$this->mStripState->general->setPair( $marker, $output );
$toc .= $sk->tocUnindent( $prevtoclevel - 1 );
}
$toc = $sk->tocList( $toc );
+ $this->mOutput->setTOCHTML( $toc );
}
if ( $isMain ) {
$this->mOutput->setSections( $tocraw );
- $this->mOutput->setTOCHTML( $toc );
}
# split up and insert constructed headlines
$username = $user->getName();
$nickname = $user->getOption( 'nickname' );
- $nickname = $nickname === null ? $username : $nickname;
+ $nickname = $nickname == null ? $username : $nickname;
if( mb_strlen( $nickname ) > $wgMaxSigChars ) {
$nickname = $username;
* @return The old callback function for this name, if any
*/
function setFunctionHook( $id, $callback, $flags = 0 ) {
+ global $wgContLang;
+
$oldVal = isset( $this->mFunctionHooks[$id] ) ? $this->mFunctionHooks[$id][0] : null;
$this->mFunctionHooks[$id] = array( $callback, $flags );
foreach ( $synonyms as $syn ) {
# Case
if ( !$sensitive ) {
- $syn = strtolower( $syn );
+ $syn = $wgContLang->lc( $syn );
}
# Add leading hash
if ( !( $flags & SFH_NO_HASH ) ) {
return array_keys( $this->mFunctionHooks );
}
+ /**
+ * Create a tag function, e.g. <test>some stuff</test>.
+ * Unlike tag hooks, tag functions are parsed at preprocessor level.
+ * Unlike parser functions, their content is not preprocessed.
+ */
+ function setFunctionTagHook( $tag, $callback, $flags ) {
+ $tag = strtolower( $tag );
+ $old = isset( $this->mFunctionTagHooks[$tag] ) ?
+ $this->mFunctionTagHooks[$tag] : null;
+ $this->mFunctionTagHooks[$tag] = array( $callback, $flags );
+
+ if( !in_array( $tag, $this->mStripList ) ) {
+ $this->mStripList[] = $tag;
+ }
+
+ return $old;
+ }
+
/**
* FIXME: update documentation. makeLinkObj() is deprecated.
* Replace <!--LINK--> link placeholders with actual links, in the buffer
# * upright reduce width for upright images, rounded to full __0 px
# * border draw a 1px border around the image
# * alt Text for HTML alt attribute (defaults to empty)
+ # * link Set the target of the image link. Can be external, interwiki, or local
# vertical-align values (no % or length right now):
# * baseline
# * sub
# Get the file
$imagename = $title->getDBkey();
- if ( isset( $this->mFileCache[$imagename][$time] ) ) {
- $file = $this->mFileCache[$imagename][$time];
- } else {
- $file = wfFindFile( $title, $time );
- if ( count( $this->mFileCache ) > 1000 ) {
- $this->mFileCache = array();
- }
- $this->mFileCache[$imagename][$time] = $file;
- }
+ $file = wfFindFile( $title, array( 'time' => $time ) );
# Get parameter map
$handler = $file ? $file->getHandler() : false;