* Added page_props backend, generic parser-driven page properties which, when changed, invalidate the cache of backlinked pages on any links table. Could be used to add overlays to images depending on their deletion status, or to add icons to articles based on category membership.
* Refactored double-underscore handling in the parser.
* Moved CoreParserFunctions registration to CoreParserFunctions.
* @addtogroup Parser
*/
class CoreParserFunctions {
+ static function register( $parser ) {
+ global $wgAllowDisplayTitle, $wgAllowSlowParserFunctions;
+
+ # Syntax for arguments (see self::setFunctionHook):
+ # "name for lookup in localized magic words array",
+ # function callback,
+ # optional SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}
+ # instead of {{#int:...}})
+
+ $parser->setFunctionHook( 'int', array( __CLASS__, 'intFunction' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'ns', array( __CLASS__, 'ns' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'urlencode', array( __CLASS__, 'urlencode' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'lcfirst', array( __CLASS__, 'lcfirst' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'ucfirst', array( __CLASS__, 'ucfirst' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'lc', array( __CLASS__, 'lc' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'uc', array( __CLASS__, 'uc' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'localurl', array( __CLASS__, 'localurl' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'localurle', array( __CLASS__, 'localurle' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'fullurl', array( __CLASS__, 'fullurl' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'fullurle', array( __CLASS__, 'fullurle' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'formatnum', array( __CLASS__, 'formatnum' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'grammar', array( __CLASS__, 'grammar' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'plural', array( __CLASS__, 'plural' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'numberofpages', array( __CLASS__, 'numberofpages' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'numberofusers', array( __CLASS__, 'numberofusers' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'numberofarticles', array( __CLASS__, 'numberofarticles' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'numberoffiles', array( __CLASS__, 'numberoffiles' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'numberofadmins', array( __CLASS__, 'numberofadmins' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'numberofedits', array( __CLASS__, 'numberofedits' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'language', array( __CLASS__, 'language' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'padleft', array( __CLASS__, 'padleft' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'padright', array( __CLASS__, 'padright' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'anchorencode', array( __CLASS__, 'anchorencode' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'special', array( __CLASS__, 'special' ) );
+ $parser->setFunctionHook( 'defaultsort', array( __CLASS__, 'defaultsort' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'filepath', array( __CLASS__, 'filepath' ), SFH_NO_HASH );
+ $parser->setFunctionHook( 'tag', array( __CLASS__, 'tagObj' ), SFH_OBJECT_ARGS );
+
+ if ( $wgAllowDisplayTitle ) {
+ $parser->setFunctionHook( 'displaytitle', array( __CLASS__, 'displaytitle' ), SFH_NO_HASH );
+ }
+ if ( $wgAllowSlowParserFunctions ) {
+ $parser->setFunctionHook( 'pagesinnamespace', array( __CLASS__, 'pagesinnamespace' ), SFH_NO_HASH );
+ }
+ }
+
static function intFunction( $parser, $part1 = '' /*, ... */ ) {
if ( strval( $part1 ) !== '' ) {
$args = array_slice( func_get_args(), 2 );
* Hooks should return strings or false
*/
$wgExceptionHooks = array();
+
+/**
+ * Page property link table invalidation lists.
+ * Should only be set by extensions.
+ */
+$wgPagePropLinkInvalidations = array(
+ 'hiddencat' => 'categorylinks',
+);
* Return an array mapping PDBK to ID
*/
function executeInto( &$cache ) {
- $fname = 'LinkBatch::executeInto';
- wfProfileIn( $fname );
- // Do query
+ wfProfileIn( __METHOD__ );
$res = $this->doQuery();
+ $ids = $this->addResultToCache( $cache, $res );
+ wfProfileOut( __METHOD__ );
+ return $ids;
+ }
+
+ /**
+ * Add a ResultWrapper containing IDs and titles to a LinkCache object
+ */
+ function addResultToCache( $cache, $res ) {
if ( !$res ) {
- wfProfileOut( $fname );
return array();
}
$ids[$title->getPrefixedDBkey()] = $row->page_id;
unset( $remaining[$row->page_namespace][$row->page_title] );
}
- $res->free();
// The remaining links in $data are bad links, register them as such
foreach ( $remaining as $ns => $dbkeys ) {
$ids[$title->getPrefixedDBkey()] = 0;
}
}
- wfProfileOut( $fname );
return $ids;
}
* Perform the existence test query, return a ResultWrapper with page_id fields
*/
function doQuery() {
- $fname = 'LinkBatch::doQuery';
-
if ( $this->isEmpty() ) {
return false;
}
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
// Construct query
// This is very similar to Parser::replaceLinkHolders
$page = $dbr->tableName( 'page' );
$set = $this->constructSet( 'page', $dbr );
if ( $set === false ) {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return false;
}
$sql = "SELECT page_id, page_namespace, page_title FROM $page WHERE $set";
// Do query
- $res = new ResultWrapper( $dbr, $dbr->query( $sql, $fname ) );
- wfProfileOut( $fname );
+ $res = new ResultWrapper( $dbr, $dbr->query( $sql, __METHOD__ ) );
+ wfProfileOut( __METHOD__ );
return $res;
}
/**
* Construct a WHERE clause which will match all the given titles.
- * Give the appropriate table's field name prefix ('page', 'pl', etc).
*
- * @param $prefix String: ??
+ * @param string $prefix the appropriate table's field name prefix ('page', 'pl', etc)
* @return string
* @public
*/
$mExternals, //!< URLs of external links, array key only
$mCategories, //!< Map of category names to sort keys
$mInterlangs, //!< Map of language codes to titles
+ $mProperties, //!< Map of arbitrary name to value
$mDb, //!< Database connection reference
$mOptions, //!< SELECT options to be used (array)
$mRecursive; //!< Whether to queue jobs for recursive updates
$this->mTemplates = $parserOutput->getTemplates();
$this->mExternals = $parserOutput->getExternalLinks();
$this->mCategories = $parserOutput->getCategories();
+ $this->mProperties = $parserOutput->getProperties();
# Convert the format of the interlanguage links
# I didn't want to change it in the ParserOutput, because that array is passed all
}
function doIncrementalUpdate() {
- $fname = 'LinksUpdate::doIncrementalUpdate';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
# Page links
$existing = $this->getExistingLinks();
$categoryUpdates = array_diff_assoc( $existing, $this->mCategories ) + array_diff_assoc( $this->mCategories, $existing );
$this->invalidateCategories( $categoryUpdates );
+ # Page properties
+ $existing = $this->getExistingProperties();
+ $this->incrTableUpdate( 'page_props', 'pp', $this->getPropertyDeletions( $existing ),
+ $this->getPropertyInsertions( $existing ) );
+
+ # Invalidate the necessary pages
+ $changed = array_diff_assoc( $existing, $this->mProperties ) + array_diff_assoc( $this->mProperties, $existing );
+ $this->invalidateProperties( $changed );
+
# Refresh links of all pages including this page
# This will be in a separate transaction
if ( $this->mRecursive ) {
$this->queueRecursiveJobs();
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
/**
* Also useful where link table corruption needs to be repaired, e.g. in refreshLinks.php
*/
function doDumbUpdate() {
- $fname = 'LinksUpdate::doDumbUpdate';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
# Refresh category pages and image description pages
$existing = $this->getExistingCategories();
$this->dumbTableUpdate( 'templatelinks', $this->getTemplateInsertions(), 'tl_from' );
$this->dumbTableUpdate( 'externallinks', $this->getExternalInsertions(), 'el_from' );
$this->dumbTableUpdate( 'langlinks', $this->getInterlangInsertions(), 'll_from' );
+ $this->dumbTableUpdate( 'page_props', $this->getPropertyInsertions(), 'pp_page' );
# Update the cache of all the category pages and image description pages which were changed
$this->invalidateCategories( $categoryUpdates );
$this->queueRecursiveJobs();
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
function queueRecursiveJobs() {
* @param array $dbkeys
*/
function invalidatePages( $namespace, $dbkeys ) {
- $fname = 'LinksUpdate::invalidatePages';
-
if ( !count( $dbkeys ) ) {
return;
}
'page_namespace' => $namespace,
'page_title IN (' . $this->mDb->makeList( $dbkeys ) . ')',
'page_touched < ' . $this->mDb->addQuotes( $now )
- ), $fname
+ ), __METHOD__
);
while ( $row = $this->mDb->fetchObject( $res ) ) {
$ids[] = $row->page_id;
array(
'page_id IN (' . $this->mDb->makeList( $ids ) . ')',
'page_touched < ' . $this->mDb->addQuotes( $now )
- ), $fname
+ ), __METHOD__
);
}
}
function dumbTableUpdate( $table, $insertions, $fromField ) {
- $fname = 'LinksUpdate::dumbTableUpdate';
- $this->mDb->delete( $table, array( $fromField => $this->mId ), $fname );
+ $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ );
if ( count( $insertions ) ) {
# The link array was constructed without FOR UPDATE, so there may be collisions
# This may cause minor link table inconsistencies, which is better than
# crippling the site with lock contention.
- $this->mDb->insert( $table, $insertions, $fname, array( 'IGNORE' ) );
+ $this->mDb->insert( $table, $insertions, __METHOD__, array( 'IGNORE' ) );
}
}
* @private
*/
function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
- $fname = 'LinksUpdate::incrTableUpdate';
- $where = array( "{$prefix}_from" => $this->mId );
+ if ( $table == 'page_props' ) {
+ $fromField = 'pp_page';
+ } else {
+ $fromField = "{$prefix}_from";
+ }
+ $where = array( $fromField => $this->mId );
if ( $table == 'pagelinks' || $table == 'templatelinks' ) {
$clause = $this->makeWhereFrom2d( $deletions, $prefix );
if ( $clause ) {
} else {
if ( $table == 'langlinks' ) {
$toField = 'll_lang';
+ } elseif ( $table == 'page_props' ) {
+ $toField = 'pp_propname';
} else {
$toField = $prefix . '_to';
}
}
}
if ( $where ) {
- $this->mDb->delete( $table, $where, $fname );
+ $this->mDb->delete( $table, $where, __METHOD__ );
}
if ( count( $insertions ) ) {
- $this->mDb->insert( $table, $insertions, $fname, 'IGNORE' );
+ $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
}
}
return $arr;
}
+ /**
+ * Get an array of page property insertions
+ */
+ function getPropertyInsertions( $existing = array() ) {
+ $diffs = array_diff_assoc( $this->mProperties, $existing );
+ $arr = array();
+ foreach ( $diffs as $name => $value ) {
+ $arr[] = array(
+ 'pp_page' => $this->mId,
+ 'pp_propname' => $name,
+ 'pp_value' => $value,
+ );
+ }
+ return $arr;
+ }
+
+
/**
* Given an array of existing links, returns those links which are not in $this
* and thus should be deleted.
return array_diff_assoc( $existing, $this->mInterlangs );
}
+ /**
+ * Get array of properties which should be deleted.
+ * @private
+ */
+ function getPropertyDeletions( $existing ) {
+ return array_diff_assoc( $existing, $this->mProperties );
+ }
+
/**
* Get an array of existing links, as a 2-D array
* @private
*/
function getExistingLinks() {
- $fname = 'LinksUpdate::getExistingLinks';
$res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
- array( 'pl_from' => $this->mId ), $fname, $this->mOptions );
+ array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
$arr = array();
while ( $row = $this->mDb->fetchObject( $res ) ) {
if ( !isset( $arr[$row->pl_namespace] ) ) {
* @private
*/
function getExistingTemplates() {
- $fname = 'LinksUpdate::getExistingTemplates';
$res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
- array( 'tl_from' => $this->mId ), $fname, $this->mOptions );
+ array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
$arr = array();
while ( $row = $this->mDb->fetchObject( $res ) ) {
if ( !isset( $arr[$row->tl_namespace] ) ) {
* @private
*/
function getExistingImages() {
- $fname = 'LinksUpdate::getExistingImages';
$res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
- array( 'il_from' => $this->mId ), $fname, $this->mOptions );
+ array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
$arr = array();
while ( $row = $this->mDb->fetchObject( $res ) ) {
$arr[$row->il_to] = 1;
* @private
*/
function getExistingExternals() {
- $fname = 'LinksUpdate::getExistingExternals';
$res = $this->mDb->select( 'externallinks', array( 'el_to' ),
- array( 'el_from' => $this->mId ), $fname, $this->mOptions );
+ array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
$arr = array();
while ( $row = $this->mDb->fetchObject( $res ) ) {
$arr[$row->el_to] = 1;
* @private
*/
function getExistingCategories() {
- $fname = 'LinksUpdate::getExistingCategories';
$res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey' ),
- array( 'cl_from' => $this->mId ), $fname, $this->mOptions );
+ array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
$arr = array();
while ( $row = $this->mDb->fetchObject( $res ) ) {
$arr[$row->cl_to] = $row->cl_sortkey;
* @private
*/
function getExistingInterlangs() {
- $fname = 'LinksUpdate::getExistingInterlangs';
$res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
- array( 'll_from' => $this->mId ), $fname, $this->mOptions );
+ array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
$arr = array();
while ( $row = $this->mDb->fetchObject( $res ) ) {
$arr[$row->ll_lang] = $row->ll_title;
}
return $arr;
}
+
+ /**
+ * Get an array of existing categories, with the name in the key and sort key in the value.
+ * @private
+ */
+ function getExistingProperties() {
+ $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
+ array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
+ $arr = array();
+ while ( $row = $this->mDb->fetchObject( $res ) ) {
+ $arr[$row->pp_propname] = $row->pp_value;
+ }
+ $this->mDb->freeResult( $res );
+ return $arr;
+ }
+
/**
* Return the title object of the page being updated
function getTitle() {
return $this->mTitle;
}
+
+ /**
+ * Invalidate any necessary link lists related to page property changes
+ */
+ function invalidateProperties( $changed ) {
+ global $wgPagePropLinkInvalidations;
+
+ foreach ( $changed as $name => $value ) {
+ if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
+ $inv = $wgPagePropLinkInvalidations[$name];
+ if ( !is_array( $inv ) ) {
+ $inv = array( $inv );
+ }
+ foreach ( $inv as $table ) {
+ $update = new HTMLCacheUpdate( $this->mTitle, $table );
+ $update->doUpdate();
+ }
+ }
+ }
+ }
}
'numberofadmins' => 3600,
);
+ static public $mDoubleUnderscoreIDs = array(
+ 'notoc',
+ 'nogallery',
+ 'forcetoc',
+ 'toc',
+ 'noeditsection',
+ 'newsectionlink',
+ 'hiddencat',
+ );
+
+
static public $mObjects = array();
+ static public $mDoubleUnderscoreArray = null;
/**#@-*/
return -1;
}
}
-
+
+ /** Get a MagicWordArray of double-underscore entities */
+ static function getDoubleUnderscoreArray() {
+ if ( is_null( self::$mDoubleUnderscoreArray ) ) {
+ self::$mDoubleUnderscoreArray = new MagicWordArray( self::$mDoubleUnderscoreIDs );
+ }
+ return self::$mDoubleUnderscoreArray;
+ }
# Initialises this object with an ID
function load( $id ) {
var $names = array();
var $hash;
var $baseRegex, $regex;
+ var $matches;
function __construct( $names = array() ) {
$this->names = $names;
/**
* Parse a match array from preg_match
+ * Returns array(magic word ID, parameter value)
+ * If there is no parameter value, that element will be false.
*/
function parseMatch( $m ) {
reset( $m );
}
return false;
}
+
+ /**
+ * Returns an associative array, ID => param value, for all items that match
+ * Removes the matched items from the input string (passed by reference)
+ */
+ public function matchAndRemove( &$text ) {
+ $found = array();
+ $regexes = $this->getRegex();
+ foreach ( $regexes as $regex ) {
+ if ( $regex === '' ) {
+ continue;
+ }
+ preg_match_all( $regex, $text, $matches, PREG_SET_ORDER );
+ foreach ( $matches as $m ) {
+ list( $name, $param ) = $this->parseMatch( $m );
+ $found[$name] = $param;
+ }
+ $text = preg_replace( $regex, '', $text );
+ }
+ return $found;
+ }
}
/**
* Add an array of categories, with names in the keys
*/
- public function addCategoryLinks($categories) {
+ public function addCategoryLinks( $categories ) {
global $wgUser, $wgContLang;
if ( !is_array( $categories ) ) {
return;
}
- # Add the links to the link cache in a batch
+ if ( count( $categories ) == 0 ) {
+ return;
+ }
+ # Add the links to a LinkBatch
$arr = array( NS_CATEGORY => $categories );
$lb = new LinkBatch;
$lb->setArray( $arr );
- $lb->execute();
+ # Fetch existence plus the hiddencat property
+ $dbr = wfGetDB( DB_SLAVE );
+ $pageTable = $dbr->tableName( 'page' );
+ $propsTable = $dbr->tableName( 'page_props' );
+ $where = $lb->constructSet( 'page', $dbr );
+ $sql = "SELECT page_id, page_namespace, page_title, pp_value FROM $pageTable LEFT JOIN $propsTable " .
+ " ON pp_page=page_id WHERE ($where) AND pp_propname='hiddencat'";
+ $res = $dbr->query( $sql, __METHOD__ );
+
+ # Add the results to the link cache
+ $lb->addResultToCache( LinkCache::singleton(), $res );
+
+ # Remove categories with hiddencat
+ foreach ( $res as $row ) {
+ if ( isset( $row->pp_value ) ) {
+ unset( $categories[$row->page_title] );
+ }
+ }
+
+ # Add the remaining categories to the skin
$sk = $wgUser->getSkin();
foreach ( $categories as $category => $unused ) {
$title = Title::makeTitleSafe( NS_CATEGORY, $category );
// Versioning...
$this->mTemplateIds += (array)$parserOutput->mTemplateIds;
- # Display title
+ // Display title
if( ( $dt = $parserOutput->getDisplayTitle() ) !== false )
$this->setPageTitle( $dt );
- # Hooks registered in the object
+ // Hooks registered in the object
global $wgParserOutputHooks;
foreach ( $parserOutput->getOutputHooks() as $hookInfo ) {
list( $hookName, $data ) = $hookInfo;
var $mInterwikiLinkHolders, $mLinkHolders;
var $mIncludeSizes, $mPPNodeCount, $mDefaultSort;
var $mTplExpandCache; // empty-frame expansion cache
- var $mTplRedirCache, $mTplDomCache, $mHeadings;
+ var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
# Temporary
# These are variables reset at least once per parse regardless of $clearState
$this->mFirstCall = false;
wfProfileIn( __METHOD__ );
- global $wgAllowDisplayTitle, $wgAllowSlowParserFunctions;
$this->setHook( 'pre', array( $this, 'renderPreTag' ) );
-
- # Syntax for arguments (see self::setFunctionHook):
- # "name for lookup in localized magic words array",
- # function callback,
- # optional SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}
- # instead of {{#int:...}})
- $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( 'lcfirst', array( 'CoreParserFunctions', 'lcfirst' ), SFH_NO_HASH );
- $this->setFunctionHook( 'ucfirst', array( 'CoreParserFunctions', 'ucfirst' ), SFH_NO_HASH );
- $this->setFunctionHook( 'lc', array( 'CoreParserFunctions', 'lc' ), SFH_NO_HASH );
- $this->setFunctionHook( 'uc', array( 'CoreParserFunctions', 'uc' ), SFH_NO_HASH );
- $this->setFunctionHook( 'localurl', array( 'CoreParserFunctions', 'localurl' ), SFH_NO_HASH );
- $this->setFunctionHook( 'localurle', array( 'CoreParserFunctions', 'localurle' ), SFH_NO_HASH );
- $this->setFunctionHook( 'fullurl', array( 'CoreParserFunctions', 'fullurl' ), SFH_NO_HASH );
- $this->setFunctionHook( 'fullurle', array( 'CoreParserFunctions', 'fullurle' ), SFH_NO_HASH );
- $this->setFunctionHook( 'formatnum', array( 'CoreParserFunctions', 'formatnum' ), SFH_NO_HASH );
- $this->setFunctionHook( 'grammar', array( 'CoreParserFunctions', 'grammar' ), SFH_NO_HASH );
- $this->setFunctionHook( 'plural', array( 'CoreParserFunctions', 'plural' ), SFH_NO_HASH );
- $this->setFunctionHook( 'numberofpages', array( 'CoreParserFunctions', 'numberofpages' ), SFH_NO_HASH );
- $this->setFunctionHook( 'numberofusers', array( 'CoreParserFunctions', 'numberofusers' ), 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 );
- $this->setFunctionHook( 'anchorencode', array( 'CoreParserFunctions', 'anchorencode' ), SFH_NO_HASH );
- $this->setFunctionHook( 'special', array( 'CoreParserFunctions', 'special' ) );
- $this->setFunctionHook( 'defaultsort', array( 'CoreParserFunctions', 'defaultsort' ), SFH_NO_HASH );
- $this->setFunctionHook( 'filepath', array( 'CoreParserFunctions', 'filepath' ), SFH_NO_HASH );
- $this->setFunctionHook( 'tag', array( 'CoreParserFunctions', 'tagObj' ), SFH_OBJECT_ARGS );
-
- if ( $wgAllowDisplayTitle ) {
- $this->setFunctionHook( 'displaytitle', array( 'CoreParserFunctions', 'displaytitle' ), SFH_NO_HASH );
- }
- if ( $wgAllowSlowParserFunctions ) {
- $this->setFunctionHook( 'pagesinnamespace', array( 'CoreParserFunctions', 'pagesinnamespace' ), SFH_NO_HASH );
- }
-
+ CoreParserFunctions::register( $this );
$this->initialiseVariables();
wfRunHooks( 'ParserFirstCallInit', array( &$this ) );
$this->mPPNodeCount = 0;
$this->mDefaultSort = false;
$this->mHeadings = array();
+ $this->mDoubleUnderscores = array();
# Fix cloning
if ( isset( $this->mPreprocessor ) && $this->mPreprocessor->parser !== $this ) {
$text = preg_replace( '/(^|\n)-----*/', '\\1<hr />', $text );
- $text = $this->stripToc( $text );
- $this->stripNoGallery( $text );
+ $text = $this->doDoubleUnderscore( $text );
$text = $this->doHeadings( $text );
if($this->mOptions->getUseDynamicDates()) {
$df =& DateFormatter::getInstance();
}
/**
- * Detect __NOGALLERY__ magic word and set a placeholder
+ * Strip double-underscore items like __NOGALLERY__ and __NOTOC__
+ * Fills $this->mDoubleUnderscores, returns the modified text
*/
- function stripNoGallery( &$text ) {
- # if the string __NOGALLERY__ (not case-sensitive) occurs in the HTML,
- # do not add TOC
- $mw = MagicWord::get( 'nogallery' );
- $this->mOutput->mNoGallery = $mw->matchAndRemove( $text ) ;
- }
-
- /**
- * 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,
- # do not add TOC
- $mw = MagicWord::get( 'notoc' );
- if( $mw->matchAndRemove( $text ) ) {
- $this->mShowToc = false;
- }
-
+ function doDoubleUnderscore( $text ) {
+ // The position of __TOC__ needs to be recorded
$mw = MagicWord::get( 'toc' );
if( $mw->match( $text ) ) {
$this->mShowToc = true;
// Only keep the first one.
$text = $mw->replace( '', $text );
}
+
+ // Now match and remove the rest of them
+ $mwa = MagicWord::getDoubleUnderscoreArray();
+ $this->mDoubleUnderscores = $mwa->matchAndRemove( $text );
+
+ if ( isset( $this->mDoubleUnderscores['nogallery'] ) ) {
+ $this->mOutput->mNoGallery = true;
+ }
+ if ( isset( $this->mDoubleUnderscores['notoc'] ) && !$this->mForceTocPosition ) {
+ $this->mShowToc = false;
+ }
+ if ( isset( $this->mDoubleUnderscores['hiddencat'] ) ) {
+ $this->mOutput->setProperty( 'hiddencat', 'y' );
+ }
return $text;
}
}
# Inhibit editsection links if requested in the page
- $esw =& MagicWord::get( 'noeditsection' );
- if( $esw->matchAndRemove( $text ) ) {
+ if ( isset( $this->mDoubleUnderscores['noeditsection'] ) ) {
$showEditLink = 0;
}
# Allow user to stipulate that a page should have a "new section"
# link added via __NEWSECTIONLINK__
- $mw =& MagicWord::get( 'newsectionlink' );
- if( $mw->matchAndRemove( $text ) )
+ if ( isset( $this->mDoubleUnderscores['newsectionlink'] ) ) {
$this->mOutput->setNewSection( true );
+ }
# if the string __FORCETOC__ (not case-sensitive) occurs in the HTML,
# override above conditions and always show TOC above first header
- $mw =& MagicWord::get( 'forcetoc' );
- if ($mw->matchAndRemove( $text ) ) {
+ if ( isset( $this->mDoubleUnderscores['forcetoc'] ) ) {
$this->mShowToc = true;
$enoughToc = true;
}
$mHeadItems, # Items to put in the <head> section
$mOutputHooks, # Hook tags as per $wgParserOutputHooks
$mWarnings, # Warning text to be returned to the user. Wikitext formatted.
- $mSections; # Table of contents
-
+ $mSections, # Table of contents
+ $mProperties; # Name/value pairs to be cached in the DB
+
/**
* Overridden title for display
*/
$this->mTemplateIds = array();
$this->mOutputHooks = array();
$this->mWarnings = array();
+ $this->mProperties = array();
}
function getText() { return $this->mText; }
public function getFlag( $flag ) {
return isset( $this->mFlags[$flag] );
}
-
+
+ /**
+ * Set a property to be cached in the DB
+ */
+ public function setProperty( $name, $value ) {
+ $this->mProperties[$name] = $value;
+ }
+
+ public function getProperty( $name ){
+ return isset( $this->mProperties[$name] ) ? $this->mProperties[$name] : false;
+ }
+
+ public function getProperties() {
+ if ( !isset( $this->mProperties ) ) {
+ $this->mProperties = array();
+ }
+ return $this->mProperties;
+ }
}
'defaultsort' => array( 1, 'DEFAULTSORT:', 'DEFAULTSORTKEY:', 'DEFAULTCATEGORYSORT:' ),
'filepath' => array( 0, 'FILEPATH:' ),
'tag' => array( 0, 'tag' ),
+ 'hiddencat' => array( 1, '__HIDDENCAT__' ),
);
/**
--- /dev/null
+-- Name/value pairs indexed by page_id
+CREATE TABLE /*$wgDBprefix*/page_props (
+ pp_page int NOT NULL,
+ pp_propname varbinary(60) NOT NULL,
+ pp_value blob NOT NULL,
+
+ PRIMARY KEY (pp_page,pp_propname)
+);
+
KEY pt_timestamp (pt_timestamp)
) /*$wgDBTableOptions*/;
+-- Name/value pairs indexed by page_id
+CREATE TABLE /*$wgDBprefix*/page_props (
+ pp_page int NOT NULL,
+ pp_propname varbinary(60) NOT NULL,
+ pp_value blob NOT NULL,
+
+ PRIMARY KEY (pp_page,pp_propname)
+);
+
-- vim: sw=2 sts=2 et
array( 'querycachetwo', 'patch-querycachetwo.sql' ),
array( 'redirect', 'patch-redirect.sql' ),
array( 'protected_titles', 'patch-protected_titles.sql' ),
+ array( 'page_props', 'patch-page_props.sql' ),
);
$wgNewFields = array(