<?php
-// require_once('Tokenizer.php');
-
/**
* File for Parser and related classes
*
# only once and last
$text = $this->doBlockLevels( $text, $linestart );
$text = $this->unstripNoWiki( $text, $this->mStripState );
- if($wgUseTidy) {
- $text = $this->tidy($text);
- }
$this->mOutput->setText( $text );
wfProfileOut( $fname );
return $this->mOutput;
/**
* interface with html tidy, used if $wgUseTidy = true
*
- * @access private
+ * @access public
+ * @static
*/
function tidy ( $text ) {
global $wgTidyConf, $wgTidyBin, $wgTidyOpts;
wfProfileIn( $fname );
$cleansource = '';
+ $opts = '';
switch(strtoupper($wgOutputEncoding)) {
case 'ISO-8859-1':
- $wgTidyOpts .= ($wgInputEncoding == $wgOutputEncoding)? ' -latin1':' -raw';
+ $opts .= ($wgInputEncoding == $wgOutputEncoding)? ' -latin1':' -raw';
break;
case 'UTF-8':
- $wgTidyOpts .= ($wgInputEncoding == $wgOutputEncoding)? ' -utf8':' -raw';
+ $opts .= ($wgInputEncoding == $wgOutputEncoding)? ' -utf8':' -raw';
break;
default:
- $wgTidyOpts .= ' -raw';
+ $opts .= ' -raw';
}
$wrappedtext = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'.
1 => array('pipe', 'w'),
2 => array('file', '/dev/null', 'a')
);
- $process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts", $descriptorspec, $pipes);
+ $process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts$opts", $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], $wrappedtext);
fclose($pipes[0]);
* @access private
*/
function internalParse( $text, $linestart, $args = array(), $isMain=true ) {
- global $wgLang;
+ global $wgContLang;
$fname = 'Parser::internalParse';
wfProfileIn( $fname );
$text = $this->removeHTMLtags( $text );
$text = $this->replaceVariables( $text, $args );
- $text = $wgLang->convert($text);
+ $text = $wgContLang->convert($text);
$text = preg_replace( '/(^|\n)-----*/', '\\1<hr />', $text );
$text = $wgDateFormatter->reformat( $this->mOptions->getDateFormat(), $text );
}
$text = $this->doAllQuotes( $text );
- $text = $this->replaceExternalLinks( $text );
- $text = $this->doMagicLinks( $text );
$text = $this->replaceInternalLinks ( $text );
# Another call to replace links and images inside captions of images
$text = $this->replaceInternalLinks ( $text );
-
- $text = $this->unstrip( $text, $this->mStripState );
- $text = $this->unstripNoWiki( $text, $this->mStripState );
-
+ $text = $this->replaceExternalLinks( $text );
+ $text = $this->doMagicLinks( $text );
$text = $this->doTableStuff( $text );
$text = $this->formatHeadings( $text, $isMain );
$sk =& $this->mOptions->getSkin();
wfProfileIn( $fname );
$sk =& $this->mOptions->getSkin();
- $linktrail = wfMsg('linktrail');
+ $linktrail = wfMsgForContent('linktrail');
$bits = preg_split( EXT_LINK_BRACKETED, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
$s = $this->replaceFreeExternalLinks( 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 .= "<a href=\"{$url}\" {$la}>{$text}</a>{$dtrail}{$paren}{$trail}";
+ $s .= "<a href=\"{$url}\"{$la}>{$text}</a>{$dtrail}{$paren}{$trail}";
}
wfProfileOut( $fname );
* @access private
*/
function replaceInternalLinks( $s ) {
- global $wgLang, $wgLinkCache;
- global $wgNamespacesWithSubpages;
+ global $wgLang, $wgContLang, $wgLinkCache;
static $fname = 'Parser::replaceInternalLinks' ;
wfProfileIn( $fname );
# e.g. in the case of 'The Arab al[[Razi]]', 'al' will be matched
static $e2 = '/^(.*?)([a-zA-Z\x80-\xff]+)$/sD';
- $useLinkPrefixExtension = $wgLang->linkPrefixExtension();
+ $useLinkPrefixExtension = $wgContLang->linkPrefixExtension();
# Special and Media are pseudo-namespaces; no pages actually exist in them
$nottalk = !Namespace::isTalk( $this->mTitle->getNamespace() );
continue;
}
- # 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
-
- # Look at the first character
- $c = substr($m[1],0,1);
- $noforce = ($c != ':');
-
- # subpage
- if( $c == '/' ) {
- # / at end means we don't want the slash to be shown
- if(substr($m[1],-1,1)=='/') {
- $m[1]=substr($m[1],1,strlen($m[1])-2);
- $noslash=$m[1];
- } else {
- $noslash=substr($m[1],1);
- }
-
- # Some namespaces don't allow subpages
- if(!empty($wgNamespacesWithSubpages[$this->mTitle->getNamespace()])) {
- # subpages allowed here
- $link = $this->mTitle->getPrefixedText(). '/' . trim($noslash);
- if( '' == $text ) {
- $text= $m[1];
- } # this might be changed for ugliness reasons
- } else {
- # no subpage allowed, use standard link
- $link = $noslash;
- }
-
- } elseif( $noforce ) { # no subpage
- $link = $m[1];
- } else {
- # We don't want to keep the first character
- $link = substr( $m[1], 1 );
+ # Don't allow internal links to pages containing
+ # PROTO: where PROTO is a valid URL protocol; these
+ # should be external links.
+ if (preg_match('/((?:'.URL_PROTOCOLS.'):)/', $m[1])) {
+ $s .= $prefix . '[[' . $line ;
+ continue;
+ }
+
+ # Make subpage if necessary
+ $link = $this->maybeDoSubpageLink( $m[1], $text );
+
+ $noforce = (substr($m[1], 0, 1) != ':');
+ if (!$noforce) {
+ # Strip off leading ':'
+ $link = substr($link, 1);
}
$wasblank = ( '' == $text );
if( $noforce ) {
# Interwikis
- if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgLang->getLanguageName( $iw ) ) {
+ if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) {
array_push( $this->mOutput->mLanguageLinks, $nt->getFullText() );
$tmp = $prefix . $trail ;
$s .= (trim($tmp) == '')? '': $tmp;
return $s;
}
+ /**
+ * Handle link to subpage if necessary
+ * @param $target string the source of the link
+ * @param &$text the link text, modified as necessary
+ * @return string the full name of the link
+ * @access 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
+ global $wgNamespacesWithSubpages;
+
+ $fname = 'Parser::maybeDoSubpageLink';
+ wfProfileIn( $fname );
+ # Look at the first character
+ if( $target{0} == '/' ) {
+ # / at end means we don't want the slash to be shown
+ if(substr($target,-1,1)=='/') {
+ $target=substr($target,1,-1);
+ $noslash=$target;
+ } else {
+ $noslash=substr($target,1);
+ }
+
+ # Some namespaces don't allow subpages
+ if(!empty($wgNamespacesWithSubpages[$this->mTitle->getNamespace()])) {
+ # subpages allowed here
+ $ret = $this->mTitle->getPrefixedText(). '/' . trim($noslash);
+ if( '' === $text ) {
+ $text = $target;
+ } # this might be changed for ugliness reasons
+ } else {
+ # no subpage allowed, use standard link
+ $ret = $target;
+ }
+ } else {
+ # no subpage
+ $ret = $target;
+ }
+
+ wfProfileOut( $fname );
+ return $ret;
+ }
+
/**#@+
* Used by doBlockLevels()
* @access private
# So we check for : in the remainder text to split up the
# title and definition, without b0rking links.
# FIXME: This is not foolproof. Something better in Tokenizer might help.
- if( preg_match( '/^(.*?(?:\s| )):(.*)$/', $t, $match ) ) {
+ if( preg_match( '/^(.*?):(.*)$/', $t, $match ) ) {
$term = $match[1];
$output .= $term . $this->nextItem( ':' );
$t = $match[2];
if ( ';' == $char ) {
# FIXME: This is dupe of code above
- if( preg_match( '/^(.*?(?:\s| )):(.*)$/', $t, $match ) ) {
+ if( preg_match( '/^(.*?):(.*)$/', $t, $match ) ) {
$term = $match[1];
$output .= $term . $this->nextItem( ':' );
$t = $match[2];
* @access private
*/
function getVariableValue( $index ) {
- global $wgLang, $wgSitename, $wgServer;
+ global $wgContLang, $wgSitename, $wgServer;
switch ( $index ) {
case MAG_CURRENTMONTH:
- return $wgLang->formatNum( date( 'm' ) );
+ return $wgContLang->formatNum( date( 'm' ) );
case MAG_CURRENTMONTHNAME:
- return $wgLang->getMonthName( date('n') );
+ return $wgContLang->getMonthName( date('n') );
case MAG_CURRENTMONTHNAMEGEN:
- return $wgLang->getMonthNameGen( date('n') );
+ return $wgContLang->getMonthNameGen( date('n') );
case MAG_CURRENTDAY:
- return $wgLang->formatNum( date('j') );
+ return $wgContLang->formatNum( date('j') );
case MAG_PAGENAME:
return $this->mTitle->getText();
case MAG_PAGENAMEE:
return $this->mTitle->getPartialURL();
case MAG_NAMESPACE:
# return Namespace::getCanonicalName($this->mTitle->getNamespace());
- return $wgLang->getNsText($this->mTitle->getNamespace()); # Patch by Dori
+ return $wgContLang->getNsText($this->mTitle->getNamespace()); # Patch by Dori
case MAG_CURRENTDAYNAME:
- return $wgLang->getWeekdayName( date('w')+1 );
+ return $wgContLang->getWeekdayName( date('w')+1 );
case MAG_CURRENTYEAR:
- return $wgLang->formatNum( date( 'Y' ) );
+ return $wgContLang->formatNum( date( 'Y' ) );
case MAG_CURRENTTIME:
- return $wgLang->time( wfTimestampNow(), false );
+ return $wgContLang->time( wfTimestampNow(), false );
case MAG_NUMBEROFARTICLES:
- return $wgLang->formatNum( wfNumberOfArticles() );
+ return $wgContLang->formatNum( wfNumberOfArticles() );
case MAG_SITENAME:
return $wgSitename;
case MAG_SERVER:
* @access private
*/
function initialiseVariables() {
+ $fname = 'Parser::initialiseVariables';
+ wfProfileIn( $fname );
global $wgVariableIDs;
$this->mVariables = array();
foreach ( $wgVariableIDs as $id ) {
$mw =& MagicWord::get( $id );
$mw->addToArray( $this->mVariables, $this->getVariableValue( $id ) );
}
+ wfProfileOut( $fname );
}
/**
# PHP global rebinding syntax is a bit weird, need to use the GLOBALS array
$GLOBALS['wgCurParser'] =& $this;
- if ( $this->mOutputType == OT_HTML ) {
+ # Variable substitution
+ $text = preg_replace_callback( "/{{([$titleChars]*?)}}/", 'wfVariableSubstitution', $text );
+
+ if ( $this->mOutputType == OT_HTML || $this->mOutputType == OT_WIKI ) {
# Argument substitution
$text = preg_replace_callback( "/{{{([$titleChars]*?)}}}/", 'wfArgSubstitution', $text );
}
# Template substitution
- $regex = '/{{(['.$titleChars.']*)(\\|.*?|)}}/s';
+ $regex = '/(\\n|{)?{{(['.$titleChars.']*)(\\|.*?|)}}/s';
$text = preg_replace_callback( $regex, 'wfBraceSubstitution', $text );
array_pop( $this->mArgStack );
return $text;
}
+ /**
+ * Replace magic variables
+ * @access private
+ */
+ function variableSubstitution( $matches ) {
+ if ( !$this->mVariables ) {
+ $this->initialiseVariables();
+ }
+ $skip = false;
+ if ( $this->mOutputType == OT_WIKI ) {
+ # Do only magic variables prefixed by SUBST
+ $mwSubst =& MagicWord::get( MAG_SUBST );
+ if (!$mwSubst->matchStartAndRemove( $matches[1] ))
+ $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.
+ }
+ if ( !$skip && array_key_exists( $matches[1], $this->mVariables ) ) {
+ $text = $this->mVariables[$matches[1]];
+ $this->mOutput->mContainsOldMagic = true;
+ } else {
+ $text = $matches[0];
+ }
+ return $text;
+ }
+
# Split template arguments
function getTemplateArgs( $argsString ) {
if ( $argsString === '' ) {
* @access private
*/
function braceSubstitution( $matches ) {
- global $wgLinkCache, $wgLang;
+ global $wgLinkCache, $wgContLang;
$fname = 'Parser::braceSubstitution';
$found = false;
$nowiki = false;
$noparse = false;
- $itcamefromthedatabase = false;
$title = NULL;
+ # Need to know if the template comes at the start of a line,
+ # to treat the beginning of the template like the beginning
+ # of a line for tables and block-level elements.
+ $linestart = $matches[1];
+
# $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
- $part1 = $matches[1];
- # If the second subpattern matched anything, it will start with |
+ $part1 = $matches[2];
+ # If the third subpattern matched anything, it will start with |
- $args = $this->getTemplateArgs($matches[2]);
+ $args = $this->getTemplateArgs($matches[3]);
$argc = count( $args );
- # {{{}}}
- if ( strpos( $matches[0], '{{{' ) !== false ) {
+ # Don't parse {{{}}} because that's only for template arguments
+ if ( $linestart === '{' ) {
$text = $matches[0];
$found = true;
$noparse = true;
# SUBST
if ( !$found ) {
$mwSubst =& MagicWord::get( MAG_SUBST );
- if ( $mwSubst->matchStartAndRemove( $part1 ) ) {
- if ( $this->mOutputType != OT_WIKI ) {
- # Invalid SUBST not replaced at PST time
- # Return without further processing
- $text = $matches[0];
- $found = true;
- $noparse= true;
- }
- } elseif ( $this->mOutputType == OT_WIKI ) {
- # SUBST not found in PST pass, do nothing
+ if ( $mwSubst->matchStartAndRemove( $part1 ) xor ($this->mOutputType == OT_WIKI) ) {
+ # 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
$text = $matches[0];
$found = true;
+ $noparse = true;
}
}
$mwInt =& MagicWord::get( MAG_INT );
if ( $mwInt->matchStartAndRemove( $part1 ) ) {
if ( $this->incrementIncludeCount( 'int:'.$part1 ) ) {
- $text = wfMsgReal( $part1, $args, true );
+ $text = $linestart . wfMsgReal( $part1, $args, true );
$found = true;
}
}
$mwNs = MagicWord::get( MAG_NS );
if ( $mwNs->matchStartAndRemove( $part1 ) ) {
if ( intval( $part1 ) ) {
- $text = $wgLang->getNsText( intval( $part1 ) );
+ $text = $linestart . $wgContLang->getNsText( intval( $part1 ) );
$found = true;
} else {
$index = Namespace::getCanonicalIndex( strtolower( $part1 ) );
if ( !is_null( $index ) ) {
- $text = $wgLang->getNsText( $index );
+ $text = $linestart . $wgContLang->getNsText( $index );
$found = true;
}
}
$title = Title::newFromText( $part1 );
if ( !is_null( $title ) ) {
if ( $argc > 0 ) {
- $text = $title->$func( $args[0] );
+ $text = $linestart . $title->$func( $args[0] );
} else {
- $text = $title->$func();
+ $text = $linestart . $title->$func();
}
$found = true;
}
}
}
- # Internal variables
- if ( !$this->mVariables ) {
- $this->initialiseVariables();
- }
- if ( !$found && array_key_exists( $part1, $this->mVariables ) ) {
- $text = $this->mVariables[$part1];
- $found = true;
- $this->mOutput->mContainsOldMagic = true;
- }
-
# GRAMMAR
if ( !$found && $argc == 1 ) {
$mwGrammar =& MagicWord::get( MAG_GRAMMAR );
if ( $mwGrammar->matchStartAndRemove( $part1 ) ) {
- $text = $wgLang->convertGrammar( $args[0], $part1 );
+ $text = $linestart . $wgContLang->convertGrammar( $args[0], $part1 );
$found = true;
}
}
# Did we encounter this template already? If yes, it is in the cache
# and we need to check for loops.
- if ( isset( $this->mTemplates[$part1] ) ) {
+ if ( !$found && isset( $this->mTemplates[$part1] ) ) {
+ # set $text to cached message.
+ $text = $linestart . $this->mTemplates[$part1];
+ $found = true;
+
# Infinite loop test
if ( isset( $this->mTemplatePath[$part1] ) ) {
$noparse = true;
$found = true;
+ $text .= '<!-- WARNING: template loop detected -->';
}
- # set $text to cached message.
- $text = $this->mTemplates[$part1];
- $found = true;
}
# Load from database
+ $itcamefromthedatabase = false;
if ( !$found ) {
- $title = Title::newFromText( $part1, NS_TEMPLATE );
+ $ns = NS_TEMPLATE;
+ $part1 = $this->maybeDoSubpageLink( $part1, $subpage='' );
+ if ($subpage !== '') {
+ $ns = $this->mTitle->getNamespace();
+ }
+ $title = Title::newFromText( $part1, $ns );
if ( !is_null( $title ) && !$title->isExternal() ) {
# Check for excessive inclusion
$dbk = $title->getPrefixedDBkey();
$articleContent = $article->getContentWithoutUsingSoManyDamnGlobals();
if ( $articleContent !== false ) {
$found = true;
- $text = $articleContent;
+ $text = $linestart . $articleContent;
$itcamefromthedatabase = true;
}
}
# If the title is valid but undisplayable, make a link to it
if ( $this->mOutputType == OT_HTML && !$found ) {
- $text = '[['.$title->getPrefixedText().']]';
+ $text = $linestart . '[['.$title->getPrefixedText().']]';
$found = true;
}
# Only for HTML output
if ( $nowiki && $found && $this->mOutputType == OT_HTML ) {
$text = wfEscapeWikiText( $text );
- } elseif ( $this->mOutputType == OT_HTML && $found && !$noparse) {
+ } elseif ( ($this->mOutputType == OT_HTML || $this->mOutputType == OT_WIKI) && $found && !$noparse) {
# Clean up argument array
$assocArgs = array();
$index = 1;
}
}
- # Do not enter included links in link table
- if ( !is_null( $title ) ) {
- $wgLinkCache->suspend();
- }
-
# Add a new element to the templace recursion path
$this->mTemplatePath[$part1] = 1;
$text = $this->replaceVariables( $text, $assocArgs );
# Resume the link cache and register the inclusion as a link
- if ( !is_null( $title ) ) {
- $wgLinkCache->resume();
+ if ( $this->mOutputType == OT_HTML && !is_null( $title ) ) {
$wgLinkCache->addLinkObj( $title );
}
+
+ # If the template begins with a table or block-level
+ # element, it should be treated as beginning a new line.
+ if ($linestart !== '\n' && preg_match('/^({\\||:|;|#|\*)/', $text)) {
+ $text = "\n" . $text;
+ }
}
# Empties the template path
$encodedname = base64_encode($title->getPrefixedDBkey());
else
$encodedname = base64_encode("");
- $matches = preg_split('/(^={1,6}.*?={1,6}\s*?$)/m', $text, -1,
+ $m = preg_split('/(^={1,6}.*?={1,6}\s*?$)/m', $text, -1,
PREG_SPLIT_DELIM_CAPTURE);
$text = '';
$nsec = 0;
- for( $i = 0; $i < count($matches); $i += 2 ) {
- wfDebug("text=[".$matches[$i]."] heading=[".$matches[$i+1]."]\n");
- if ($matches[$i] == "" && $matches[$i + 1] == "") break;
- $text .= $matches[$i];
- $hl = $matches[$i + 1];
- if ($hl == "") continue;
+ 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;
$nsec++;
}
}
+ }
+
+ # Empties the template path
+ $this->mTemplatePath = array();
+ if ( !$found ) {
+ return $matches[0];
+ } else {
return $text;
}
}
$inputArgs = end( $this->mArgStack );
if ( array_key_exists( $arg, $inputArgs ) ) {
- $text = $this->strip( $inputArgs[$arg], $this->mStripState );
- $text = $this->removeHTMLtags( $text );
- $text = $this->replaceVariables( $text, array() );
+ $text = $inputArgs[$arg];
}
return $text;
* @access private
*/
/* private */ function formatHeadings( $text, $isMain=true ) {
- global $wgInputEncoding, $wgMaxTocLevel, $wgLang, $wgLinkHolders;
+ global $wgInputEncoding, $wgMaxTocLevel, $wgContLang, $wgLinkHolders;
$doNumberHeadings = $this->mOptions->getNumberHeadings();
$doShowToc = $this->mOptions->getShowToc();
if( $dot ) {
$numbering .= '.';
}
- $numbering .= $wgLang->formatNum( $sublevelCount[$i] );
+ $numbering .= $wgContLang->formatNum( $sublevelCount[$i] );
$dot = 1;
}
}
* @access private
*/
function pstPass2( $text, &$user ) {
- global $wgLang, $wgLocaltimezone, $wgCurParser;
+ global $wgLang, $wgContLang, $wgLocaltimezone, $wgCurParser;
# Variable replacement
# Because mOutputType is OT_WIKI, this will only process {{subst:xxx}} type tags
$oldtz = getenv('TZ'); putenv('TZ='.$wgLocaltimezone);
}
/* Note: this is an ugly timezone hack for the European wikis */
- $d = $wgLang->timeanddate( date( 'YmdHis' ), false ) .
+ $d = $wgContLang->timeanddate( date( 'YmdHis' ), false ) .
' (' . date( 'T' ) . ')';
if(isset($wgLocaltimezone)) putenv('TZ='.$oldtzs);
$text = preg_replace( '/~~~~~/', $d, $text );
- $text = preg_replace( '/~~~~/', '[[' . $wgLang->getNsText( NS_USER ) . ":$n|$k]] $d", $text );
- $text = preg_replace( '/~~~/', '[[' . $wgLang->getNsText( NS_USER ) . ":$n|$k]]", $text );
+ $text = preg_replace( '/~~~~/', '[[' . $wgContLang->getNsText( NS_USER ) . ":$n|$k]] $d", $text );
+ $text = preg_replace( '/~~~/', '[[' . $wgContLang->getNsText( NS_USER ) . ":$n|$k]]", $text );
# Context links: [[|name]] and [[name (context)|]]
#
$text = preg_replace( $p2, "[[\\1 ({$context})|\\1]]", $text );
}
- /*
- $mw =& MagicWord::get( MAG_SUBST );
- $wgCurParser = $this->fork();
- $text = $mw->substituteCallback( $text, "wfBraceSubstitution" );
- $this->merge( $wgCurParser );
- */
-
# Trim trailing whitespace
# MAG_END (__END__) tag allows for trailing
# whitespace to be deliberately included
return $wgCurParser->argSubstitution( $matches );
}
+function wfVariableSubstitution( $matches ) {
+ global $wgCurParser;
+ return $wgCurParser->variableSubstitution( $matches );
+}
+
/**
* Return the total number of articles
*/