* NOT $wgArticle, $wgUser or $wgTitle. Keep them away!
*
* settings:
- * $wgUseTex*, $wgUseDynamicDates*, $wgInterwikiMagic*,
+ * $wgUseDynamicDates*, $wgInterwikiMagic*,
* $wgNamespacesWithSubpages, $wgAllowExternalImages*,
* $wgLocaltimezone, $wgAllowSpecialInclusion*,
* $wgMaxArticleSize*
var $mConf, $mPreprocessor, $mExtLinkBracketedRegex, $mUrlProtocols; # Initialised in constructor
# Cleared with clearState():
- var $mOutput, $mAutonumber, $mDTopen;
+ /**
+ * @var ParserOutput
+ */
+ var $mOutput;
+ var $mAutonumber, $mDTopen;
/**
* @var StripState
/**
* Constructor
- *
- * @public
*/
- function __construct( $conf = array() ) {
+ public function __construct( $conf = array() ) {
$this->mConf = $conf;
$this->mUrlProtocols = wfUrlProtocols();
$this->mExtLinkBracketedRegex = '/\[(\b(' . wfUrlProtocols() . ')'.
*/
function __destruct() {
if ( isset( $this->mLinkHolders ) ) {
- $this->mLinkHolders->__destruct();
+ unset( $this->mLinkHolders );
}
foreach ( $this as $name => $value ) {
unset( $this->$name );
* Do not call this function recursively.
*
* @param $text String: text we want to parse
- * @param $title A title object
+ * @param $title Title object
* @param $options ParserOptions
* @param $linestart boolean
* @param $clearState boolean
/**
* Get a random string
- *
- * @static
*/
static public function getRandomString() {
return dechex( mt_rand( 0, 0x7fffffff ) ) . dechex( mt_rand( 0, 0x7fffffff ) );
* @param $matches Out parameter, Array: extracted tags
* @param $uniq_prefix
* @return String: stripped text
- *
- * @static
*/
- public function extractTagsAndParams( $elements, $text, &$matches, $uniq_prefix = '' ) {
+ public static function extractTagsAndParams( $elements, $text, &$matches, $uniq_prefix = '' ) {
static $n = 1;
$stripped = '';
$matches = array();
return $this->mStripList;
}
- /**
- * @deprecated use replaceVariables
- */
- function strip( $text, $state, $stripcomments = false , $dontstrip = array() ) {
- return $text;
- }
-
- /**
- * Restores pre, math, and other extensions removed by strip()
- *
- * always call unstripNoWiki() after this one
- * @private
- * @deprecated use $this->mStripState->unstrip()
- */
- function unstrip( $text, $state ) {
- return $state->unstripGeneral( $text );
- }
-
- /**
- * Always call this after unstrip() to preserve the order
- *
- * @private
- * @deprecated use $this->mStripState->unstrip()
- */
- function unstripNoWiki( $text, $state ) {
- return $state->unstripNoWiki( $text );
- }
-
- /**
- * @deprecated use $this->mStripState->unstripBoth()
- */
- function unstripForHTML( $text ) {
- return $this->mStripState->unstripBoth( $text );
- }
-
/**
* Add an item to the strip state
* Returns the unique tag which must be inserted into the stripped text
return $rnd;
}
- /**
- * Interface with html tidy
- * @deprecated Use MWTidy::tidy()
- */
- public static function tidy( $text ) {
- wfDeprecated( __METHOD__ );
- return MWTidy::tidy( $text );
- }
-
/**
* parse the wiki syntax used to render tables
*
$lines = StringUtils::explode( "\n", $text );
$out = '';
- $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
+ $output =& $out;
foreach ( $lines as $outLine ) {
$line = trim( $outLine );
- if ( $line === '' ) { # empty line, go to next line
- $out .= $outLine."\n";
+ # empty line, go to next line,
+ # but only append \n if outside of table
+ if ( $line === '') {
+ $output .= $outLine . "\n";
continue;
}
-
- $first_character = $line[0];
+ $firstChars = $line[0];
+ if ( strlen( $line ) > 1 ) {
+ $firstChars .= in_array( $line[1], array( '}', '+', '-' ) ) ? $line[1] : '';
+ }
$matches = array();
- if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) {
- # First check if we are starting a new table
- $indent_level = strlen( $matches[1] );
+ if ( preg_match( '/^(:*)\s*\{\|(.*)$/', $line , $matches ) ) {
+ $tables[] = array();
+ $table =& $this->last( $tables );
+ $table[0] = array(); // first row
+ $currentRow =& $table[0];
+ $table['indent'] = strlen( $matches[1] );
$attributes = $this->mStripState->unstripBoth( $matches[2] );
$attributes = Sanitizer::fixTagAttributes( $attributes , 'table' );
- $outLine = 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 );
- } elseif ( count( $td_history ) == 0 ) {
- # Don't do any of the following
- $out .= $outLine."\n";
- continue;
- } elseif ( substr( $line , 0 , 2 ) === '|}' ) {
- # We are ending a table
- $line = '</table>' . substr( $line , 2 );
- $last_tag = array_pop( $last_tag_history );
+ if ( $attributes !== '' ) {
+ $table['attributes'] = $attributes;
+ }
+ } else if ( !isset( $tables[0] ) ) {
+ // we're outside the table
+
+ $out .= $outLine . "\n";
+ } else if ( $firstChars === '|}' ) {
+ // trim the |} code from the line
+ $line = substr ( $line , 2 );
+
+ // Shorthand for last row
+ $lastRow =& $this->last( $table );
+
+ // a thead at the end becomes a tfoot, unless there is only one row
+ // Do this before deleting empty last lines to allow headers at the bottom of tables
+ if ( isset( $lastRow['type'] ) && $lastRow['type'] == 'thead' && isset( $table[1] ) ) {
+ $lastRow['type'] = 'tfoot';
+ for ( $i = 0; isset( $lastRow[$i] ); $i++ ) {
+ $lastRow[$i]['type'] = 'th';
+ }
+ }
- if ( !array_pop( $has_opened_tr ) ) {
- $line = "<tr><td></td></tr>{$line}";
+ // Delete empty last lines
+ if ( empty( $lastRow ) ) {
+ $lastRow = NULL;
}
+ $o = '';
+ $curtable = array_pop( $tables );
- if ( array_pop( $tr_history ) ) {
- $line = "</tr>{$line}";
+ #Add a line-ending before the table, but only if there isn't one already
+ if ( substr( $out, -1 ) !== "\n" ) {
+ $o .= "\n";
}
+ $o .= $this->generateTableHTML( $curtable ) . $line . "\n";
+
+ if ( count( $tables ) > 0 ) {
+ $table =& $this->last( $tables );
+ $currentRow =& $this->last( $table );
+ $currentElement =& $this->last( $currentRow );
- if ( array_pop( $td_history ) ) {
- $line = "</{$last_tag}>{$line}";
+ $output =& $currentElement['content'];
+ } else {
+ $output =& $out;
}
- array_pop( $tr_attributes );
- $outLine = $line . str_repeat( '</dd></dl>' , $indent_level );
- } elseif ( substr( $line , 0 , 2 ) === '|-' ) {
- # Now we have a table row
- $line = preg_replace( '#^\|-+#', '', $line );
- # Whats after the tag is now only attributes
+ $output .= $o;
+
+ } else if ( $firstChars === '|-' ) {
+ // start a new row element
+ // but only when we haven't started one already
+ if ( count( $currentRow ) != 0 ) {
+ $table[] = array();
+ $currentRow =& $this->last( $table );
+ }
+ // Get the attributes, there's nothing else useful in $line now
+ $line = substr ( $line , 2 );
$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 );
- array_pop( $has_opened_tr );
- array_push( $has_opened_tr , true );
-
- if ( array_pop( $tr_history ) ) {
- $line = '</tr>';
+ if ( $attributes !== '' ) {
+ $currentRow['attributes'] = $attributes;
}
- if ( array_pop( $td_history ) ) {
- $line = "</{$last_tag}>{$line}";
+ } else if ( $firstChars === '|+' ) {
+ // a table caption, but only proceed if there isn't one already
+ if ( !isset ( $table['caption'] ) ) {
+ $line = substr ( $line , 2 );
+
+ $c = $this->getCellAttr( $line , 'caption' );
+ $table['caption'] = array();
+ $table['caption']['content'] = $c[0];
+ if ( isset( $c[1] ) ) $table['caption']['attributes'] = $c[1];
+ unset( $c );
+ $output =& $table['caption']['content'];
+ }
+ } else if ( $firstChars === '|' || $firstChars === '!' || $firstChars === '!+' ) {
+ // Which kind of cells are we dealing with
+ $currentTag = 'td';
+ $line = substr ( $line , 1 );
+
+ if ( $firstChars === '!' || $firstChars === '!+' ) {
+ $line = str_replace ( '!!' , '||' , $line );
+ $currentTag = 'th';
}
- $outLine = $line;
- array_push( $tr_history , false );
- array_push( $td_history , false );
- array_push( $last_tag_history , '' );
- } elseif ( $first_character === '|' || $first_character === '!' || substr( $line , 0 , 2 ) === '|+' ) {
- # This might be cell elements, td, th or captions
- if ( substr( $line , 0 , 2 ) === '|+' ) {
- $first_character = '+';
- $line = substr( $line , 1 );
+ // Split up multiple cells on the same line.
+ $cells = StringUtils::explodeMarkup( '||' , $line );
+ $line = ''; // save memory
+
+ // decide whether thead to tbody
+ if ( !array_key_exists( 'type', $currentRow ) ) {
+ $currentRow['type'] = ( $firstChars === '!' ) ? 'thead' : 'tbody' ;
+ } else if ( $firstChars === '|' ) {
+ $currentRow['type'] = 'tbody';
}
- $line = substr( $line , 1 );
+ // Loop through each table cell
+ foreach ( $cells as $cell ) {
+ // a new cell
+ $currentRow[] = array();
+ $currentElement =& $this->last( $currentRow );
+
+ $currentElement['type'] = $currentTag;
- if ( $first_character === '!' ) {
- $line = str_replace( '!!' , '||' , $line );
+ $c = $this->getCellAttr( $cell , $currentTag );
+ $currentElement['content'] = $c[0];
+ if ( isset( $c[1] ) ) $currentElement['attributes'] = $c[1];
+ unset( $c );
}
+ $output =& $currentElement['content'];
- # Split up multiple cells on the same line.
- # FIXME : This can result in improper nesting of tags processed
- # by earlier parser steps, but should avoid splitting up eg
- # attribute values containing literal "||".
- $cells = StringUtils::explodeMarkup( '||' , $line );
+ } else {
+ $output .= "\n$outLine";
+ }
+ }
- $outLine = '';
+ # Remove trailing line-ending (b/c)
+ if ( substr( $out, -1 ) === "\n" ) {
+ $out = substr( $out, 0, -1 );
+ }
- # Loop through each table cell
- foreach ( $cells as $cell ) {
- $previous = '';
- if ( $first_character !== '+' ) {
- $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 , '' );
- array_pop( $has_opened_tr );
- array_push( $has_opened_tr , true );
- }
+ # Close any unclosed tables
+ if ( isset( $tables ) && count( $tables ) > 0 ) {
+ for ( $i = 0; $i < count( $tables ); $i++ ) {
+ $curtable = array_pop( $tables );
+ $curtable = $this->generateTableHTML( $curtable );
+ #Add a line-ending before the table, but only if there isn't one already
+ if ( substr( $out, -1 ) !== "\n" && $curtable !== "" ) {
+ $out .= "\n";
+ }
+ $out .= $curtable;
+ }
+ }
- $last_tag = array_pop( $last_tag_history );
+ wfProfileOut( __METHOD__ );
- if ( array_pop( $td_history ) ) {
- $previous = "</{$last_tag}>\n{$previous}";
- }
+ return $out;
+ }
- if ( $first_character === '|' ) {
- $last_tag = 'td';
- } elseif ( $first_character === '!' ) {
- $last_tag = 'th';
- } elseif ( $first_character === '+' ) {
- $last_tag = 'caption';
- } else {
- $last_tag = '';
- }
+ /**
+ * Helper function for doTableStuff() separating the contents of cells from
+ * attributes. Particularly useful as there's a possible bug and this action
+ * is repeated twice.
+ *
+ * @private
+ */
+ function getCellAttr ( $cell, $tagName ) {
+ $content = null;
+ $attributes = null;
- array_push( $last_tag_history , $last_tag );
+ $cell = trim ( $cell );
- # A cell could contain both parameters and data
- $cell_data = explode( '|' , $cell , 2 );
+ // A cell could contain both parameters and data
+ $cellData = 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}";
- } elseif ( count( $cell_data ) == 1 ) {
- $cell = "{$previous}<{$last_tag}>{$cell_data[0]}";
- } else {
- $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
- $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag );
- $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
- }
+ // Bug 553: Note that a '|' inside an invalid link should not
+ // be mistaken as delimiting cell parameters
+ if ( strpos( $cellData[0], '[[' ) !== false ) {
+ $content = trim ( $cell );
+ }
+ else if ( count ( $cellData ) == 1 ) {
+ $content = trim ( $cellData[0] );
+ }
+ else {
+ $attributes = $this->mStripState->unstripBoth( $cellData[0] );
+ $attributes = Sanitizer::fixTagAttributes( $attributes , $tagName );
- $outLine .= $cell;
- array_push( $td_history , true );
- }
- }
- $out .= $outLine . "\n";
+ $content = trim ( $cellData[1] );
}
+ return array( $content, $attributes );
+ }
+
- # Closing open td, tr && table
- while ( count( $td_history ) > 0 ) {
- if ( array_pop( $td_history ) ) {
- $out .= "</td>\n";
+ /**
+ * Helper function for doTableStuff(). This converts the structured array into html.
+ *
+ * @private
+ */
+ function generateTableHTML ( &$table ) {
+ $return = "";
+ $return .= str_repeat( '<dl><dd>' , $table['indent'] );
+ $return .= '<table';
+ $return .= isset( $table['attributes'] ) ? $table['attributes'] : '';
+ $return .= '>';
+ unset( $table['attributes'] );
+
+ if ( isset( $table['caption'] ) ) {
+ $return .= "\n<caption";
+ $return .= isset( $table['caption']['attributes'] ) ? $table['caption']['attributes'] : '';
+ $return .= '>';
+ $return .= $table['caption']['content'];
+ $return .= "\n</caption>";
+ }
+ $lastSection = '';
+ $empty = true;
+ $simple = true;
+
+ // If we only have tbodies, mark table as simple
+ for ( $i = 0; isset( $table[$i] ); $i++ ) {
+ if ( !count( $table[$i] ) ) continue;
+ if ( !isset( $table[$i]['type'] ) ) $table[$i]['type'] = 'tbody';
+ if ( !$lastSection ) {
+ $lastSection = $table[$i]['type'];
+ } else if ( $lastSection != $table[$i]['type'] ) {
+ $simple = false;
+ break;
}
- if ( array_pop( $tr_history ) ) {
- $out .= "</tr>\n";
+ }
+ $lastSection = '';
+ for ( $i = 0; isset( $table[$i] ); $i++ ) {
+ // Check for empty tables
+ if ( count( $table[$i] ) ) {
+ $empty = false;
+ } else {
+ continue;
}
- if ( !array_pop( $has_opened_tr ) ) {
- $out .= "<tr><td></td></tr>\n" ;
+ if ( $table[$i]['type'] != $lastSection && !$simple ) {
+ $return .= "\n<" . $table[$i]['type'] . '>';
}
- $out .= "</table>\n";
- }
+ $return .= "\n<tr";
+ $return .= isset( $table[$i]['attributes'] ) ? $table[$i]['attributes'] : '';
+ $return .= '>';
+ for ( $j = 0; isset( $table[$i][$j] ); $j++ ) {
+ if ( !isset( $table[$i][$j]['type'] ) ) $table[$i][$j]['type'] = 'td';
+ $return .= "\n<" . $table[$i][$j]['type'];
+ $return .= isset( $table[$i][$j]['attributes'] ) ? $table[$i][$j]['attributes'] : '';
+ $return .= '>';
+
+ $return .= $table[$i][$j]['content'];
+ if ( $table[$i][$j]['content'] != '' )
+ $return .= "\n";
+
+ $return .= '</' . $table[$i][$j]['type'] . '>';
+ unset( $table[$i][$j] );
+ }
+ $return .= "\n</tr>";
- # Remove trailing line-ending (b/c)
- if ( substr( $out, -1 ) === "\n" ) {
- $out = substr( $out, 0, -1 );
+ if ( ( !isset( $table[$i + 1] ) && !$simple ) || ( isset( $table[$i + 1] ) && isset( $table[$i + 1]['type'] ) && $table[$i]['type'] != $table[$i + 1]['type'] ) ) {
+ $return .= '</' . $table[$i]['type'] . '>';
+ }
+ $lastSection = $table[$i]['type'];
+ unset( $table[$i] );
}
-
- # special case: don't return empty table
- if ( $out === "<table>\n<tr><td></td></tr>\n</table>" ) {
- $out = '';
+ if ( $empty ) {
+ if ( isset( $table['caption'] ) ) {
+ $return .= "\n<tr><td></td></tr>";
+ } else {
+ return '';
+ }
}
+ $return .= "\n</table>";
+ $return .= str_repeat( '</dd></dl>' , $table['indent'] );
- wfProfileOut( __METHOD__ );
+ return $return;
+ }
- return $out;
+ /**
+ * like end() but only works on the numeric array index and php's internal pointers
+ * returns a reference to the last element of an array much like "\$arr[-1]" in perl
+ * ignores associative elements and will create a 0 key will a NULL value if there were
+ * no numric elements and an array itself if not previously defined.
+ *
+ * @private
+ */
+ function &last ( &$arr ) {
+ for ( $i = count( $arr ); ( !isset( $arr[$i] ) && $i > 0 ); $i-- ) { }
+ return $arr[$i];
}
/**
substr( $m[0], 0, 20 ) . '"' );
}
$url = wfMsgForContent( $urlmsg, $id );
- $sk = $this->mOptions->getSkin( $this->mTitle );
- return $sk->makeExternalLink( $url, "{$keyword} {$id}", true, $CssClass );
+ return Linker::makeExternalLink( $url, "{$keyword} {$id}", true, $CssClass );
} elseif ( isset( $m[5] ) && $m[5] !== '' ) {
# ISBN
$isbn = $m[5];
global $wgContLang;
wfProfileIn( __METHOD__ );
- $sk = $this->mOptions->getSkin( $this->mTitle );
$trail = '';
# The characters '<' and '>' (which were escaped by
$text = $this->maybeMakeExternalImage( $url );
if ( $text === false ) {
# Not an image, make a link
- $text = $sk->makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free',
+ $text = Linker::makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free',
$this->getExternalLinkAttribs( $url ) );
# Register it in the output object...
# Replace unnecessary URL escape codes with their equivalent characters
global $wgContLang;
wfProfileIn( __METHOD__ );
- $sk = $this->mOptions->getSkin( $this->mTitle );
-
$bits = preg_split( $this->mExtLinkBracketedRegex, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
$s = array_shift( $bits );
# 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
- $s .= $sk->makeExternalLink( $url, $text, false, $linktype,
+ $s .= Linker::makeExternalLink( $url, $text, false, $linktype,
$this->getExternalLinkAttribs( $url ) ) . $dtrail . $trail;
# Register link in the output object.
return $attribs;
}
-
/**
* Replace unusual URL escape codes with their equivalent characters
*
* @private
*/
function maybeMakeExternalImage( $url ) {
- $sk = $this->mOptions->getSkin( $this->mTitle );
$imagesfrom = $this->mOptions->getAllowExternalImagesFrom();
$imagesexception = !empty( $imagesfrom );
$text = false;
|| ( $imagesexception && $imagematch ) ) {
if ( preg_match( self::EXT_IMAGE_REGEX, $url ) ) {
# Image found
- $text = $sk->makeExternalImage( $url );
+ $text = Linker::makeExternalImage( $url );
}
}
if ( !$text && $this->mOptions->getEnableImageWhitelist()
}
if ( preg_match( '/' . str_replace( '/', '\\/', $entry ) . '/i', $url ) ) {
# Image matches a whitelist entry
- $text = $sk->makeExternalImage( $url );
+ $text = Linker::makeExternalImage( $url );
break;
}
}
$e1_img = "/^([{$tc}]+)\\|(.*)\$/sD";
}
- $sk = $this->mOptions->getSkin( $this->mTitle );
$holders = new LinkHolderArray( $this );
# split the entire text string on occurences of [[
# Self-link checking
if ( $nt->getFragment() === '' && $ns != NS_SPECIAL ) {
if ( in_array( $nt->getPrefixedText(), $selflink, true ) ) {
- $s .= $prefix . $sk->makeSelfLinkObj( $nt, $text, '', $trail );
+ $s .= $prefix . Linker::makeSelfLinkObj( $nt, $text, '', $trail );
continue;
}
}
if ( $ns == NS_MEDIA ) {
wfProfileIn( __METHOD__."-media" );
# Give extensions a chance to select the file revision for us
- $skip = $time = $sha1 = $descQuery = false;
- wfRunHooks( 'BeforeParserMakeImageLinkObj',
- array( &$this, &$nt, &$skip, &$time, &$descQuery, &$sha1 ) );
- if ( $skip ) {
- $this->mOutput->addImage( $nt->getDBkey(), null, null ); // register
- $link = $sk->link( $nt );
- } else {
- # Fetch and register the file (file title may be different via hooks)
- list( $file, $nt ) = $this->fetchFileAndTitle( $nt, $time, $sha1 );
- $link = $sk->makeMediaLinkFile( $nt, $file, $text );
- }
+ $time = $sha1 = $descQuery = false;
+ wfRunHooks( 'BeforeParserFetchFileAndTitle',
+ array( $this, $nt, &$time, &$sha1, &$descQuery ) );
+ # Fetch and register the file (file title may be different via hooks)
+ list( $file, $nt ) = $this->fetchFileAndTitle( $nt, $time, $sha1 );
# Cloak with NOPARSE to avoid replacement in replaceExternalLinks
- $s .= $prefix . $this->armorLinks( $link ) . $trail;
+ $s .= $prefix . $this->armorLinks(
+ Linker::makeMediaLinkFile( $nt, $file, $text ) ) . $trail;
wfProfileOut( __METHOD__."-media" );
continue;
}
return $holders;
}
- /**
- * Make a link placeholder. The text returned can be later resolved to a real link with
- * replaceLinkHolders(). This is done for two reasons: firstly to avoid further
- * parsing of interwiki links, and secondly to allow all existence checks and
- * article length checks (for stub links) to be bundled into a single query.
- *
- * @deprecated
- */
- function makeLinkHolder( &$nt, $text = '', $query = array(), $trail = '', $prefix = '' ) {
- return $this->mLinkHolders->makeHolder( $nt, $text, $query, $trail, $prefix );
- }
-
/**
* 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.
$text = htmlspecialchars( $nt->getPrefixedText() );
}
- $sk = $this->mOptions->getSkin( $this->mTitle );
- $link = $sk->linkKnown( $nt, "$prefix$text$inside", array(), $query );
+ $link = Linker::linkKnown( $nt, "$prefix$text$inside", array(), $query );
return $this->armorLinks( $link ) . $trail;
}
/**#@+
* Used by doBlockLevels()
* @private
+ *
+ * @return string
*/
function closeParagraph() {
$result = '';
}
for ( $i = 0; $i < $shorter; ++$i ) {
- if ( $st1{$i} != $st2{$i} ) {
+ if ( $st1[$i] != $st2[$i] ) {
break;
}
}
* These next three functions open, continue, and close the list
* element appropriate to the prefix character passed into them.
* @private
+ *
+ * @return string
*/
function openList( $char ) {
$result = $this->closeParagraph();
* TODO: document
* @param $char String
* @private
+ *
+ * @return string
*/
function nextItem( $char ) {
if ( '*' === $char || '#' === $char ) {
* TODO: document
* @param $char String
* @private
+ *
+ * @return string
*/
function closeList( $char ) {
if ( '*' === $char ) {
'<td|<th|<\\/?div|<hr|<\\/pre|<\\/p|'.$this->mUniqPrefix.'-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS', $t );
if ( $openmatch or $closematch ) {
$paragraphStack = false;
- #Â TODO bug 5718: paragraph closed
+ # TODO bug 5718: paragraph closed
$output .= $this->closeParagraph();
if ( $preOpenMatch and !$preCloseMatch ) {
$this->mInPre = true;
$stack = 0;
$len = strlen( $str );
for( $i = 0; $i < $len; $i++ ) {
- $c = $str{$i};
+ $c = $str[$i];
switch( $state ) {
# (Using the number is a performance hack for common cases)
* Return value of a magic variable (like PAGENAME)
*
* @private
+ *
+ * @param $index integer
+ * @param $frame PPFrame
*/
function getVariableValue( $index, $frame=false ) {
global $wgContLang, $wgSitename, $wgServer;
* dependency requirements.
*
* @private
+ *
+ * @return PPNode
*/
function preprocessToDom( $text, $flags = 0 ) {
$dom = $this->getPreprocessor()->preprocessToObj( $text, $flags );
/**
* Return a three-element array: leading whitespace, string contents, trailing whitespace
+ *
+ * @return array
*/
public static function splitWhitespace( $s ) {
$ltrimmed = ltrim( $s );
* Providing arguments this way may be useful for extensions wishing to perform variable replacement explicitly.
* @param $argsOnly Boolean: only do argument (triple-brace) expansion, not double-brace expansion
* @private
+ *
+ * @return string
*/
function replaceVariables( $text, $frame = false, $argsOnly = false ) {
# Is there any text? Also, Prevent too big inclusions!
return $text;
}
- # Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
+ /**
+ * Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
+ *
+ * @return array
+ */
static function createAssocArgs( $args ) {
$assocArgs = array();
$index = 1;
&& $this->mOptions->getAllowSpecialInclusion()
&& $this->ot['html'] )
{
- $text = SpecialPage::capturePath( $title );
+ $text = SpecialPageFactory::capturePath( $title );
if ( is_string( $text ) ) {
$found = true;
$isHTML = true;
/**
* Get the semi-parsed DOM representation of a template with a given title,
* and its redirect destination title. Cached.
+ *
+ * @return array
*/
function getTemplateDom( $title ) {
$cacheTitle = $title;
/**
* Static function to get a template
* Can be overridden via ParserOptions::setTemplateCallback().
+ *
+ * @return array
*/
- static function statelessFetchTemplate( $title, $parser=false ) {
+ static function statelessFetchTemplate( $title, $parser = false ) {
$text = $skip = false;
$finalTitle = $title;
$deps = array();
# Give extensions a chance to select the revision instead
$id = false; # Assume current
wfRunHooks( 'BeforeParserFetchTemplateAndtitle',
- array( $parser, &$title, &$skip, &$id ) );
+ array( $parser, $title, &$skip, &$id ) );
if ( $skip ) {
$text = false;
);
break;
}
- $rev = $id ? Revision::newFromId( $id ) : Revision::newFromTitle( $title );
+ # Get the revision
+ $rev = $id
+ ? Revision::newFromId( $id )
+ : Revision::newFromTitle( $title );
$rev_id = $rev ? $rev->getId() : 0;
# If there is no current revision, there is no page
if ( $id === false && !$rev ) {
'title' => $title,
'page_id' => $title->getArticleID(),
'rev_id' => $rev_id );
+ if ( $rev && !$title->equals( $rev->getTitle() ) ) {
+ # We fetched a rev from a different title; register it too...
+ $deps[] = array(
+ 'title' => $rev->getTitle(),
+ 'page_id' => $rev->getPage(),
+ 'rev_id' => $rev_id );
+ }
if ( $rev ) {
$text = $rev->getText();
* @param Title $title
* @param string $time MW timestamp
* @param string $sha1 base 36 SHA-1
- * @return Array ( File or false, Title )
+ * @return Array ( File or false, Title of file )
*/
function fetchFileAndTitle( $title, $time = false, $sha1 = false ) {
if ( $time === '0' ) {
$file = false; // broken thumbnail forced by hook
} elseif ( $sha1 ) { // get by (sha1,timestamp)
$file = RepoGroup::singleton()->findFileFromKey( $sha1, array( 'time' => $time ) );
- if ( $file ) {
- $title = $file->getTitle(); // file title may not match $title
- }
} else { // get by (name,timestamp)
$file = wfFindFile( $title, array( 'time' => $time ) );
}
- # Register the file as a dependancy
- $time = $file ? $file->getTimestamp() : null;
- $sha1 = $file ? $file->getSha1() : null;
+ $time = $file ? $file->getTimestamp() : false;
+ $sha1 = $file ? $file->getSha1() : false;
+ # Register the file as a dependency...
$this->mOutput->addImage( $title->getDBkey(), $time, $sha1 );
-
- return array( $file, $title );
+ if ( $file && !$title->equals( $file->getTitle() ) ) {
+ # Update fetched file title
+ $title = $file->getTitle();
+ }
+ return array( $file, $title );
}
/**
* Transclude an interwiki link.
+ *
+ * @return string
*/
function interwikiTransclude( $title, $action ) {
global $wgEnableScaryTranscluding;
return $this->fetchScaryTemplateMaybeFromCache( $url );
}
+ /**
+ * @param $url string
+ * @return Mixed|String
+ */
function fetchScaryTemplateMaybeFromCache( $url ) {
global $wgTranscludeCacheExpiry;
$dbr = wfGetDB( DB_SLAVE );
return $text;
}
-
/**
* Triple brace replacement -- used for template arguments
* @private
+ *
+ * @param $peice array
+ * @param $frame PPFrame
+ *
+ * @return array
*/
function argSubstitution( $piece, $frame ) {
wfProfileIn( __METHOD__ );
* inner Contents of extension element
* noClose Original text did not have a close tag
* @param $frame PPFrame
+ *
+ * @return string
*/
function extensionSubstitution( $params, $frame ) {
$name = $frame->expand( $params['name'] );
$enoughToc = true;
}
- # We need this to perform operations on the HTML
- $sk = $this->mOptions->getSkin( $this->mTitle );
-
# headline counter
$headlineCount = 0;
$numVisible = 0;
$sublevelCount[$toclevel] = 0;
if ( $toclevel<$wgMaxTocLevel ) {
$prevtoclevel = $toclevel;
- $toc .= $sk->tocIndent();
+ $toc .= Linker::tocIndent();
$numVisible++;
}
} elseif ( $level < $prevlevel && $toclevel > 1 ) {
if ( $toclevel<$wgMaxTocLevel ) {
if ( $prevtoclevel < $wgMaxTocLevel ) {
# Unindent only if the previous toc level was shown :p
- $toc .= $sk->tocUnindent( $prevtoclevel - $toclevel );
+ $toc .= Linker::tocUnindent( $prevtoclevel - $toclevel );
$prevtoclevel = $toclevel;
} else {
- $toc .= $sk->tocLineEnd();
+ $toc .= Linker::tocLineEnd();
}
}
} else {
# No change in level, end TOC line
if ( $toclevel<$wgMaxTocLevel ) {
- $toc .= $sk->tocLineEnd();
+ $toc .= Linker::tocLineEnd();
}
}
$legacyAnchor .= '_' . $refers[$legacyArrayKey];
}
if ( $enoughToc && ( !isset( $wgMaxTocLevel ) || $toclevel < $wgMaxTocLevel ) ) {
- $toc .= $sk->tocLine( $anchor, $tocline,
+ $toc .= Linker::tocLine( $anchor, $tocline,
$numbering, $toclevel, ( $isTemplate ? false : $sectionIndex ) );
}
} else {
$editlink = '';
}
- $head[$headlineCount] = $sk->makeHeadline( $level,
+ $head[$headlineCount] = Linker::makeHeadline( $level,
$matches['attrib'][$headlineCount], $anchor, $headline,
$editlink, $legacyAnchor );
if ( $enoughToc ) {
if ( $prevtoclevel > 0 && $prevtoclevel < $wgMaxTocLevel ) {
- $toc .= $sk->tocUnindent( $prevtoclevel - 1 );
+ $toc .= Linker::tocUnindent( $prevtoclevel - 1 );
}
- $toc = $sk->tocList( $toc, $this->mOptions->getUserLang() );
+ $toc = Linker::tocList( $toc, $this->mOptions->getUserLang() );
$this->mOutput->setTOCHTML( $toc );
}
/**
* Create an HTML-style tag, e.g. <yourtag>special text</yourtag>
* The callback should have the following form:
- * function myParserHook( $text, $params, $parser ) { ... }
+ * function myParserHook( $text, $params, $parser, $frame ) { ... }
*
* Transform and return $text. Use $parser for any required context, e.g. use
* $parser->getTitle() and $parser->getOptions() not $wgTitle or $wgOut->mParserOptions
*
+ * Hooks may return extended information by returning an array, of which the
+ * first numbered element (index 0) must be the return string, and all other
+ * entries are extracted into local variables within an internal function
+ * in the Parser class.
+ *
+ * This interface (introduced r61913) appears to be undocumented, but
+ * 'markerName' is used by some core tag hooks to override which strip
+ * array their results are placed in. **Use great caution if attempting
+ * this interface, as it is not documented and injudicious use could smash
+ * private variables.**
+ *
* @param $tag Mixed: the tag to use, e.g. 'hook' for <hook>
* @param $callback Mixed: the callback function (and object) to use for the tag
* @return The old value of the mTagHooks array associated with the hook
return $oldVal;
}
+ /**
+ * As setHook(), but letting the contents be parsed.
+ *
+ * Transparent tag hooks are like regular XML-style tag hooks, except they
+ * operate late in the transformation sequence, on HTML instead of wikitext.
+ *
+ * This is probably obsoleted by things dealing with parser frames?
+ * The only extension currently using it is geoserver.
+ *
+ * @since 1.10
+ * @todo better document or deprecate this
+ *
+ * @param $tag Mixed: the tag to use, e.g. 'hook' for <hook>
+ * @param $callback Mixed: the callback function (and object) to use for the tag
+ * @return The old value of the mTagHooks array associated with the hook
+ */
function setTransparentTagHook( $tag, $callback ) {
$tag = strtolower( $tag );
if ( preg_match( '/[<>\r\n]/', $tag, $m ) ) throw new MWException( "Invalid character {$m[0]} in setTransparentHook('$tag', ...) call" );
* given as text will return the HTML of a gallery with two images,
* labeled 'The number "1"' and
* 'A tree'.
+ *
+ * @param string $text
+ * @param array $param
+ * @return string HTML
*/
function renderImageGallery( $text, $params ) {
$ig = new ImageGallery();
$ig->setParser( $this );
$ig->setHideBadImages();
$ig->setAttributes( Sanitizer::validateTagAttributes( $params, 'table' ) );
- $ig->useSkin( $this->mOptions->getSkin( $this->mTitle ) );
if ( isset( $params['showfilename'] ) ) {
$ig->setShowFilename( true );
if ( strpos( $matches[0], '%' ) !== false ) {
$matches[1] = rawurldecode( $matches[1] );
}
- $tp = Title::newFromText( $matches[1], NS_FILE );
- $nt =& $tp;
- if ( is_null( $nt ) ) {
+ $title = Title::newFromText( $matches[1], NS_FILE );
+ if ( is_null( $title ) ) {
# Bogus title. Ignore these so we don't bomb out later.
continue;
}
+
+ $label = '';
+ $alt = '';
if ( isset( $matches[3] ) ) {
- $label = $matches[3];
- } else {
- $label = '';
+ // look for an |alt= definition while trying not to break existing
+ // captions with multiple pipes (|) in it, until a more sensible grammar
+ // is defined for images in galleries
+
+ $matches[3] = $this->recursiveTagParse( trim( $matches[3] ) );
+ $altmatches = StringUtils::explode('|', $matches[3]);
+ $magicWordAlt = MagicWord::get( 'img_alt' );
+
+ foreach ( $altmatches as $altmatch ) {
+ $match = $magicWordAlt->matchVariableStartToEnd( $altmatch );
+ if ( $match ) {
+ $alt = $this->stripAltText( $match, false );
+ }
+ else {
+ // concatenate all other pipes
+ $label .= '|' . $altmatch;
+ }
+ }
+ // remove the first pipe
+ $label = substr( $label, 1 );
}
- $html = $this->recursiveTagParse( trim( $label ) );
-
- $ig->add( $nt, $html );
+ $ig->add( $title, $label, $alt );
}
return $ig->toHTML();
}
# * text-bottom
$parts = StringUtils::explode( "|", $options );
- $sk = $this->mOptions->getSkin( $this->mTitle );
# Give extensions a chance to select the file revision for us
- $skip = $time = $sha1 = $descQuery = false;
- wfRunHooks( 'BeforeParserMakeImageLinkObj',
- array( &$this, &$title, &$skip, &$time, &$descQuery, &$sha1 ) );
- if ( $skip ) {
- $this->mOutput->addImage( $title->getDBkey(), null, null ); // register
- return $sk->link( $title );
- }
+ $time = $sha1 = $descQuery = false;
+ wfRunHooks( 'BeforeParserFetchFileAndTitle',
+ array( $this, $title, &$time, &$sha1, &$descQuery ) );
# Fetch and register the file (file title may be different via hooks)
list( $file, $title ) = $this->fetchFileAndTitle( $title, $time, $sha1 );
+
# Get parameter map
$handler = $file ? $file->getHandler() : false;
list( $paramMap, $mwArray ) = $this->getImageParams( $handler );
+ if ( !$file ) {
+ $this->addTrackingCategory( 'broken-file-category' );
+ }
+
# Process the input parameters
$caption = '';
$params = array( 'frame' => array(), 'handler' => array(),
wfRunHooks( 'ParserMakeImageParams', array( $title, $file, &$params ) );
# Linker does the rest
- $ret = $sk->makeImageLink2( $title, $file, $params['frame'], $params['handler'],
+ $ret = Linker::makeImageLink2( $title, $file, $params['frame'], $params['handler'],
$time, $descQuery, $this->mOptions->getThumbSize() );
# Give the handler a chance to modify the parser object
* @param $text String
* @param $frame PPFrame
* @return String
- * @private
*/
function attributeStripCallback( &$text, $frame = false ) {
$text = $this->replaceVariables( $text, $frame );
/**
* Accessor
+ *
+ * @return array
*/
function getTags() {
return array_merge( array_keys( $this->mTransparentTagHooks ), array_keys( $this->mTagHooks ) );
function replaceTransparentTags( $text ) {
$matches = array();
$elements = array_keys( $this->mTransparentTagHooks );
- $text = $this->extractTagsAndParams( $elements, $text, $matches, $this->mUniqPrefix );
+ $text = self::extractTagsAndParams( $elements, $text, $matches, $this->mUniqPrefix );
foreach ( $matches as $marker => $data ) {
list( $element, $content, $params, $tag ) = $data;
* This function returns $oldtext after the content of the section
* specified by $section has been replaced with $text.
*
- * @param $text String: former text of the article
+ * @param $oldtext String: former text of the article
* @param $section Numeric: section identifier
* @param $text String: replacing text
- * #return String: modified text
+ * @return String: modified text
*/
public function replaceSection( $oldtext, $section, $text ) {
return $this->extractSections( $oldtext, $section, "replace", $text );
/**
* Get the revision object for $this->mRevisionId
*
- * @return either a Revision object or null
+ * @return Revision|null either a Revision object or null
*/
protected function getRevisionObject() {
if ( !is_null( $this->mRevisionObject ) ) {
/**
* strip/replaceVariables/unstrip for preprocessor regression testing
+ *
+ * @return string
*/
function testSrvus( $text, $title, ParserOptions $options, $outputType = self::OT_HTML ) {
if ( !$title instanceof Title ) {
* This will call the callback function twice, with 'aaa' and 'bbb'. Those
* two strings will be replaced with the value returned by the callback in
* each case.
+ *
+ * @return string
*/
function markerSkipCallback( $s, $callback ) {
$i = 0;
* array can later be loaded into another parser instance with
* unserializeHalfParsedText(). The text can then be safely incorporated into
* the return value of a parser hook.
+ *
+ * @return array
*/
function serializeHalfParsedText( $text ) {
wfProfileIn( __METHOD__ );
* serializeHalfParsedText(), is compatible with the current version of the
* parser.
*
- * @param $data Array.
+ * @param $data Array
+ *
+ * @return bool
*/
function isValidHalfParsedText( $data ) {
return isset( $data['version'] ) && $data['version'] == self::HALF_PARSED_VERSION;