<?php
+
/**
+ *
* File for Parser and related classes
*
* @addtogroup Parser
define( 'MW_COLON_STATE_COMMENTDASHDASH', 7 );
/**
- * PHP Parser
- *
- * Processes wiki markup
+ * 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).
*
* <pre>
* There are four main entry points into the Parser class:
* * only within ParserOptions
* </pre>
*
+ * @addtogroup Parser
*/
class Parser
{
*/
# Persistent:
var $mTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables;
-
+
# Cleared with clearState():
var $mOutput, $mAutonumber, $mDTopen, $mStripState;
var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
$this->mFunctionSynonyms = array( 0 => array(), 1 => array() );
$this->mFirstCall = true;
}
-
+
/**
* Do various kinds of initialisation on the first call of the 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 );
$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 );
+ $this->setFunctionHook( 'numberofedits', array( 'CoreParserFunctions', 'numberofedits' ), SFH_NO_HASH );
$this->setFunctionHook( 'language', array( 'CoreParserFunctions', 'language' ), SFH_NO_HASH );
$this->setFunctionHook( 'padleft', array( 'CoreParserFunctions', 'padleft' ), SFH_NO_HASH );
$this->setFunctionHook( 'padright', array( 'CoreParserFunctions', 'padright' ), SFH_NO_HASH );
$text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
$text = $this->replaceVariables( $text, $args );
- wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text ) );
+ wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text, &$this->mStripState ) );
// Tables need to come after variable replacement for things to work
// properly; putting them before other transformations should keep
* @param string
* @return string
* @static
- * @fixme This can merge genuinely required bits in the path or query string,
+ * @todo This can merge genuinely required bits in the path or query string,
* 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.
# Don't allow internal links to pages containing
# PROTO: where PROTO is a valid URL protocol; these
# should be external links.
- if (preg_match('/^(\b(?:' . wfUrlProtocols() . '))/', $m[1])) {
+ if (preg_match('/^\b(?:' . wfUrlProtocols() . ')/', $m[1])) {
$s .= $prefix . '[[' . $line ;
continue;
}
# 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]) );
wfProfileIn( "$fname-paragraph" );
# No prefix (not in list)--go to paragraph mode
// XXX: use a stack for nestable elements like span, table and div
- $openmatch = preg_match('/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t );
+ $openmatch = preg_match('/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t );
$closematch = preg_match(
- '/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'.
+ '/(?:<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'.
'<td|<th|<\\/?div|<hr|<\\/pre|<\\/p|'.$this->mUniqPrefix.'-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS', $t );
if ( $openmatch or $closematch ) {
$paragraphStack = false;
return $varCache[$index] = $wgContLang->formatNum( SiteStats::pages() );
case 'numberofadmins':
return $varCache[$index] = $wgContLang->formatNum( SiteStats::admins() );
+ case 'numberofedits':
+ return $varCache[$index] = $wgContLang->formatNum( SiteStats::edits() );
case 'currenttimestamp':
return $varCache[$index] = wfTimestampNow();
case 'localtimestamp':
$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;
# If the template begins with a table or block-level
# element, it should be treated as beginning a new line.
- if (!$piece['lineStart'] && preg_match('/^({\\||:|;|#|\*)/', $text)) /*}*/{
+ if (!$piece['lineStart'] && preg_match('/^(?:{\\||:|;|#|\*)/', $text)) /*}*/{
$text = "\n" . $text;
}
} elseif ( !$noargs ) {
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;
/**
* Fetch the unparsed text of a template and register a reference to it.
*/
- function fetchTemplate( $title ) {
+ function fetchTemplateAndtitle( $title ) {
$text = 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 );
break;
}
// Redirect?
+ $finalTitle = $title;
$title = Title::newFromRedirect( $text );
}
- return $text;
+ return array($text,$finalTitle);
+ }
+
+ function fetchTemplate( $title ) {
+ $rv = $this->fetchTemplateAndtitle($title);
+ return $rv[0];
}
/**
$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
$toclevel++;
$sublevelCount[$toclevel] = 0;
if( $toclevel<$wgMaxTocLevel ) {
+ $prevtoclevel = $toclevel;
$toc .= $sk->tocIndent();
+ $numVisible++;
}
}
elseif ( $level < $prevlevel && $toclevel > 1 ) {
}
}
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 {
$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 );
}
$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] = ( $s->page_len >= $threshold || # always true if $threshold <= 0
+ $s->page_is_redirect ||
+ !Namespace::isContent( $s->page_namespace )
+ ? 1 : 2 );
}
}
wfProfileOut( $fname.'-check' );
* Parse image options text and use it to make an image
*/
function makeImage( $nt, $options ) {
- global $wgUseImageResize, $wgDjvuRenderer;
-
+ # @TODO: let the MediaHandler specify its transform parameters
+ #
# 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
# * bottom
# * text-bottom
+
$part = array_map( 'trim', explode( '|', $options) );
$mwAlign = array();
$mwPage =& MagicWord::get( 'img_page' );
$caption = '';
- $width = $height = $framed = $thumb = false;
- $page = null;
+ $params = array();
+ $framed = $thumb = false;
$manual_thumb = '' ;
$align = $valign = '';
+ $sk = $this->mOptions->getSkin();
foreach( $part as $val ) {
- if ( $wgUseImageResize && ! is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
+ if ( !is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
$thumb=true;
} elseif ( ! is_null( $match = $mwManualThumb->matchVariableStartToEnd($val) ) ) {
# use manually specified thumbnail
continue 2;
}
}
- if ( isset( $wgDjvuRenderer ) && $wgDjvuRenderer
- && ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
+ if ( ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
# Select a page in a multipage document
- $page = $match;
- } elseif ( $wgUseImageResize && !$width && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
+ $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
$m = array();
if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
- $width = intval( $m[1] );
- $height = intval( $m[2] );
+ $params['width'] = intval( $m[1] );
+ $params['height'] = intval( $m[2] );
} else {
- $width = intval($match);
+ $params['width'] = intval($match);
}
} elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
$framed=true;
$alt = Sanitizer::stripAllTags( $alt );
# Linker does the rest
- $sk = $this->mOptions->getSkin();
- return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb, $page, $valign );
+ return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $params, $framed, $thumb, $manual_thumb, $valign );
}
/**
}
+/**
+ * @todo document, briefly.
+ * @addtogroup Parser
+ */
class OnlyIncludeReplacer {
var $output = '';
}
}
+/**
+ * @todo document, briefly.
+ * @addtogroup Parser
+ */
class StripState {
var $general, $nowiki;