summaryrefslogtreecommitdiff
path: root/extensions/Variables/Variables.php
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/Variables/Variables.php')
-rw-r--r--extensions/Variables/Variables.php375
1 files changed, 375 insertions, 0 deletions
diff --git a/extensions/Variables/Variables.php b/extensions/Variables/Variables.php
new file mode 100644
index 00000000..84d6c90c
--- /dev/null
+++ b/extensions/Variables/Variables.php
@@ -0,0 +1,375 @@
+<?php
+
+/**
+ * 'Variables' introduces parser functions for defining page-scoped variables within
+ * wiki pages.
+ *
+ * Documentation: https://www.mediawiki.org/wiki/Extension:Variables
+ * Support: https://www.mediawiki.org/wiki/Extension_talk:Variables
+ * Source code: https://phabricator.wikimedia.org/diffusion/EVAR/
+ *
+ * @license: ISC License
+ * @author: Rob Adams
+ * @author: Tom Hempel
+ * @author: Xiloynaha
+ * @author: Daniel Werner < danweetz@web.de >
+ *
+ * @file Variables.php
+ * @ingroup Variables
+ */
+
+// Ensure that the script cannot be executed outside of MediaWiki.
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( 'This is an extension to MediaWiki and cannot be run standalone.' );
+}
+
+// Display extension properties on MediaWiki.
+$wgExtensionCredits['parserhook'][] = array(
+ 'path' => __FILE__,
+ 'name' => 'Variables',
+ 'descriptionmsg' => 'variables-desc',
+ 'version' => ExtVariables::VERSION,
+ 'author' => array(
+ 'Rob Adams',
+ 'Tom Hempel',
+ 'Xiloynaha',
+ '[https://www.mediawiki.org/wiki/User:Danwe Daniel Werner]',
+ '...'
+ ),
+ 'url' => 'https://www.mediawiki.org/wiki/Extension:Variables',
+ 'license-name' => 'ISC'
+);
+
+// language files:
+$wgMessagesDirs['Variables'] = __DIR__ . '/i18n';
+$wgExtensionMessagesFiles['VariablesMagic'] = ExtVariables::getDir() . '/Variables.i18n.magic.php';
+
+// hooks registration:
+$wgHooks['ParserFirstCallInit'][] = 'ExtVariables::init';
+$wgHooks['ParserClearState'][] = 'ExtVariables::onParserClearState';
+$wgHooks['InternalParseBeforeSanitize'][] = 'ExtVariables::onInternalParseBeforeSanitize';
+
+// parser tests registration:
+$wgParserTestFiles[] = ExtVariables::getDir() . '/tests/mwparsertests/Variables.txt';
+
+// Include the settings file:
+require_once ExtVariables::getDir() . '/Variables.settings.php';
+
+
+/**
+ * Extension class with basic extension information. This class serves as static
+ * class with the static parser functions but also als variables store instance
+ * as object assigned to a Parser object.
+ */
+class ExtVariables {
+
+ /**
+ * Version of the 'Variables' extension.
+ *
+ * @since 1.4
+ *
+ * @var string
+ */
+ const VERSION = '2.2.0';
+
+ /**
+ * Internal store for variable values
+ *
+ * @private
+ * @var array
+ */
+ public $mVariables = array();
+
+ /**
+ * Array with all names of variables requested by '#var_final'. Key of the values is the
+ * stripSateId of the strip-item placed where the final var should appear.
+ *
+ * @since 2.0
+ *
+ * @private
+ * @var array
+ */
+ public $mFinalizedVars = array();
+
+ /**
+ * Variables extensions own private StripState manager to manage '#final_var' placeholders
+ * and their replacement with the final var value or a defined default.
+ *
+ * @since 2.0
+ *
+ * @private
+ * @var StripState
+ */
+ public $mFinalizedVarsStripState;
+
+ /**
+ * Sets up parser functions
+ *
+ * @since 1.4
+ */
+ public static function init( Parser &$parser ) {
+
+ /*
+ * store for variables per parser object. This will solve several bugs related to
+ * 'ParserClearState' hook clearing all variables early in combination with certain
+ * other extensions. (since v2.0)
+ */
+ $parser->mExtVariables = new self();
+
+ // Parser::SFH_OBJECT_ARGS available since MW 1.12
+ self::initFunction( $parser, 'var', array( __CLASS__, 'pfObj_var' ), Parser::SFH_OBJECT_ARGS );
+ self::initFunction( $parser, 'var_final' );
+ self::initFunction( $parser, 'vardefine' );
+ self::initFunction( $parser, 'vardefineecho' );
+ self::initFunction( $parser, 'varexists' );
+
+ return true;
+ }
+ private static function initFunction( Parser &$parser, $name, $functionCallback = null, $flags = 0 ) {
+ if( $functionCallback === null ) {
+ // prefix parser functions with 'pf_'
+ $functionCallback = array( __CLASS__, 'pf_' . $name );
+ }
+ global $egVariablesDisabledFunctions;
+
+ // register function only if not disabled by configuration:
+ if( ! in_array( $name, $egVariablesDisabledFunctions ) ) {
+ $parser->setFunctionHook( $name, $functionCallback, $flags );
+ }
+ }
+
+ /**
+ * Returns the extensions base installation directory.
+ *
+ * @since 2.0
+ *
+ * @return string
+ */
+ public static function getDir() {
+ static $dir = null;
+
+ if( $dir === null ) {
+ $dir = dirname( __FILE__ );
+ }
+ return $dir;
+ }
+
+
+ ####################
+ # Parser Functions #
+ ####################
+
+ static function pf_varexists( Parser &$parser, $varName = '', $exists=true, $noexists=false ) {
+ if( self::get( $parser )->varExists( $varName ) ) {
+ return $exists;
+ } else {
+ return $noexists;
+ }
+ }
+
+ static function pf_vardefine( Parser &$parser, $varName = '', $value = '' ) {
+ self::get( $parser )->setVarValue( $varName, $value );
+ return '';
+ }
+
+ static function pf_vardefineecho( Parser &$parser, $varName = '', $value = '' ) {
+ self::get( $parser )->setVarValue( $varName, $value );
+ return $value;
+ }
+
+ static function pfObj_var( Parser &$parser, $frame, $args) {
+ $varName = trim( $frame->expand( $args[0] ) ); // first argument expanded already but lets do this anyway
+ $varVal = self::get( $parser )->getVarValue( $varName, null );
+
+ // default applies if var doesn't exist but also in case it is an empty string!
+ if( $varVal === null || $varVal === '' ) {
+ // only expand argument when needed:
+ $defaultVal = isset( $args[1] ) ? trim( $frame->expand( $args[1] ) ) : '';
+ return $defaultVal;
+ }
+ return $varVal;
+ }
+
+ static function pf_var_final( Parser &$parser, $varName, $defaultVal = '' ) {
+ return self::get( $parser )->requestFinalizedVar( $parser, $varName, $defaultVal );
+ }
+
+
+ ##############
+ # Used Hooks #
+ ##############
+
+ /**
+ * Used for '#var_final' parser function to insert the final variable values.
+ * @see http://www.mediawiki.org/wiki/Manual:Hooks/InternalParseBeforeSanitize
+ *
+ * @since 2.0.1
+ */
+ static function onInternalParseBeforeSanitize( Parser &$parser, &$text ) {
+ $varStore = self::get( $parser );
+
+ // only do this if '#var_final' was used
+ if( $varStore->mFinalizedVarsStripState === null ) {
+ return true;
+ }
+
+ /*
+ * all vars are final now, check whether requested vars can be inserted for '#final_var' or
+ * if the default has to be inserted. In any case, adjust the strip item value
+ */
+ foreach( $varStore->mFinalizedVars as $stripStateId => $varName ) {
+
+ $varVal = $varStore->getVarValue( $varName, '' );
+ if( $varVal !== '' ) {
+ // replace strip item value with final variables value or registered default:
+ $varStore->stripStatePair( $stripStateId, $varVal );
+ }
+ }
+
+ /**
+ * Unstrip all '#var_final' strip-markers with their final '#var' or default values.
+ * This HAS to be done here and can't be done through the normal unstrip process of MW.
+ * This because the default value as well as the variables value stil have to be rendered properly since they
+ * may contain links or even category links. On the other hand, they can't be parsed with Parser::recursiveTagParse()
+ * since this would parse wiki templates and functions which are intended as normal text, kind of similar to
+ * returning a parser functions value with 'noparse' => true.
+ * Also, there is no way to expand the '#var_final' default value here, just if needed, since the output could be an
+ * entirely different, e.g. if variables are used.
+ * This method also takes care of recursive '#var_final' calls (within the default value) quite well.
+ */
+ $text = $varStore->mFinalizedVarsStripState->unstripGeneral( $text );
+ return true;
+ }
+
+ /**
+ * This will clean up the variables store after parsing has finished. It will prevent strange things to happen
+ * for example during import of several pages or job queue is running for multiple pages. In these cases variables
+ * would become some kind of superglobals, being passed from one page to the other.
+ */
+ static function onParserClearState( Parser &$parser ) {
+ /**
+ * MessageCaches Parser clone will mess things up if we don't reset the entire object.
+ * Only resetting the array would unset it in the original object as well! This instead
+ * will break the entire reference to the object
+ */
+ $parser->mExtVariables = new self();
+ return true;
+ }
+
+
+ ##################
+ # Private Helper #
+ ##################
+
+ /**
+ * Takes care of setting a strip state pair
+ */
+ protected function stripStatePair( $marker, $value ) {
+ $this->mFinalizedVarsStripState->addGeneral( $marker, $value );
+ }
+
+
+ ####################################
+ # Public functions for interaction #
+ ####################################
+ #
+ # public non-parser functions, accessible for
+ # other extensions doing interactive stuff
+ # with 'Variables' (like Extension:Loops)
+ #
+
+ /**
+ * Convenience function to return the 'Variables' extensions variables store connected
+ * to a certain Parser object. Each parser has its own store which will be reset after
+ * a parsing process [Parser::parse()] has finished.
+ *
+ * @param Parser &$parser
+ *
+ * @return ExtVariables by reference so we still have the right object after 'ParserClearState'
+ */
+ public static function &get( Parser &$parser ) {
+ return $parser->mExtVariables;
+ }
+
+ /**
+ * Defines a variable, accessible by getVarValue() or '#var' parser function. Name and
+ * value will be trimmed and converted to string.
+ *
+ * @param string $varName
+ * @param string $value will be converted to string if no string is given
+ */
+ public function setVarValue( $varName, $value = '' ) {
+ $this->mVariables[ trim( $varName ) ] = trim( $value );
+ }
+
+ /**
+ * Returns a variables value or null if it doesn't exist.
+ *
+ * @param string $varName
+ * @param mixed $defaultVal
+ *
+ * @return string or mixed in case $defaultVal is being returned and not of type string
+ */
+ public function getVarValue( $varName, $defaultVal = null ) {
+ $varName = trim( $varName );
+ if ( $this->varExists( $varName ) ) {
+ return $this->mVariables[ $varName ];
+ } else {
+ return $defaultVal;
+ }
+ }
+
+ /**
+ * Checks whether a variable exists within the scope.
+ *
+ * @param string $varName
+ *
+ * @return boolean
+ */
+ public function varExists( $varName ) {
+ $varName = trim( $varName );
+ return array_key_exists( $varName, $this->mVariables );
+ }
+
+ /**
+ * Allows to unset a certain variable
+ *
+ * @param type $varName
+ */
+ public function unsetVar( $varName ) {
+ unset( $this->mVariables[ $varName ] );
+ }
+
+ /**
+ * Allows to register the usage of '#var_final'. Meaning a variable can be set as well
+ * as a default value. The return value, a strip-item then can be inserted into any
+ * wikitext processed by the same parser. Later that strip-item will be replaced with
+ * the final var text.
+ * Note: It's not possible to use the returned strip-item within other stripped text
+ * since 'Variables' unstripping will happen before the general unstripping!
+ *
+ * @param Parser $parser
+ * @param string $varName
+ * @param string $defaultVal
+ *
+ * @return string strip-item
+ */
+ function requestFinalizedVar( Parser &$parser, $varName, $defaultVal = '' ) {
+ if( $this->mFinalizedVarsStripState === null ) {
+ $this->mFinalizedVarsStripState = new StripState;
+ }
+ $id = count( $this->mFinalizedVars );
+ /*
+ * strip-item which will be unstripped in self::onInternalParseBeforeSanitize()
+ * In case the requested final variable has a value in the end, this strip-item
+ * value will be replaced with that value before unstripping.
+ */
+ $rnd = "{$parser->mUniqPrefix}-finalizedvar-{$id}-" . Parser::MARKER_SUFFIX;
+
+ $this->stripStatePair( $rnd, trim( $defaultVal ) );
+ $this->mFinalizedVars[ $rnd ] = trim( $varName );
+
+ return $rnd;
+ }
+
+}