Extension:LO Parser Functions

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manual
Crystal Clear action run.png
LO ParserFunctions

Release status: beta

Implementation Parser extension, Parser function
Description A collection of parser functions such as expression handling and time calculation unit.
Author(s) Tim Starling (this extraction from his ParserFunctions extension is maintained by David M. Sledge)
Latest version N/A (2007-11-15)
MediaWiki > 1.6.8
License GNU General Public License 2.0 or later
Download No link

Translate the LO Parser Functions extension if it is available at translatewiki.net

Check usage and version matrix; code metrics

This extension contains the parser functions from the ParserFunctions extension that aren't in the control structure functions extension; #expr, #time, and #rel2abs. See ParserFunctions for usage.

Installation[edit | edit source]

See Replacing ParserFunctions with Control Structure Functions before installing.

Download the file Expr.php and put it in a new directory called LOParserFunctions in your extensions directory. Then in the new directory, create the files LOParserFunctions.php and LOParserFunctions.i18n.php that contains the following source code:

LOParserFunctions.php:

<?php
 
if ( !defined( 'MEDIAWIKI' ) ) {
	die( 'This file is a MediaWiki extension, it is not a valid entry point' );
}
 
$wgExtensionFunctions[] = 'wfSetupLOParserFunctions';
$wgExtensionCredits['parserhook'][] = array(
	'name' => 'Left-Over Parser Functions',
	'url' => 'http://www.mediawiki.org/wiki/Extension:LO_Parser_Functions',
	'author' => 'Tim Starling',
	'description' => 'Enhance parser with logical functions',
);
 
$wgHooks['LanguageGetMagic'][]       = 'wfLOParserFunctionsLanguageGetMagic';
 
class ExtLOParserFunctions {
	var $mExprParser;
	var $mTimeCache = array();
	var $mTimeChars = 0;
	var $mMaxTimeChars = 6000; # ~10 seconds

	function clearState() {
		$this->mTimeChars = 0;
		return true;
	}
 
	function &getExprParser() {
		if ( !isset( $this->mExprParser ) ) {
			if ( !class_exists( 'ExprParser' ) ) {
				require( dirname( __FILE__ ) . '/Expr.php' );
				ExprParser::addMessages();
			}
			$this->mExprParser = new ExprParser;
		}
		return $this->mExprParser;
	}
 
	function expr( &$parser, $expr = '' ) {
		try {
			return $this->getExprParser()->doExpression( $expr );
		} catch(ExprError $e) {
			return $e->getMessage();
		}
	}
 
	/**
	 * Returns the absolute path to a subpage, relative to the current article
	 * title. Treats titles as slash-separated paths.
	 *
	 * Following subpage link syntax instead of standard path syntax, an 
	 * initial slash is treated as a relative path, and vice versa.
	 */
	public function rel2abs( &$parser , $to = '' , $from = '' ) {
 
		$from = trim($from);
		if( $from == '' ) {
			$from = $parser->getTitle()->getPrefixedText();
		}
 
		$to = rtrim( $to , ' /' );
 
		// if we have an empty path, or just one containing a dot
		if( $to == '' || $to == '.' ) {
			return $from;
		}
 
		// if the path isn't relative
		if ( substr( $to , 0 , 1) != '/' &&
		 substr( $to , 0 , 2) != './' &&
		 substr( $to , 0 , 3) != '../' &&
		 $to != '..' )
		{
			$from = '';
		}
		// Make a long path, containing both, enclose it in /.../
		$fullPath = '/' . $from . '/' .  $to . '/';
 
		// remove redundant current path dots
		$fullPath = preg_replace( '!/(\./)+!', '/', $fullPath );
 
		// remove double slashes
		$fullPath = preg_replace( '!/{2,}!', '/', $fullPath );
 
		// remove the enclosing slashes now
		$fullPath = trim( $fullPath , '/' );
		$exploded = explode ( '/' , $fullPath );
		$newExploded = array();
 
		foreach ( $exploded as $current ) {
			if( $current == '..' ) { // removing one level
				if( !count( $newExploded ) ){
					// attempted to access a node above root node
					return wfMsgForContent( 'pfunc_rel2abs_invalid_depth', $fullPath );
				}
				// remove last level from the stack
				array_pop( $newExploded );
			} else {
				// add the current level to the stack
				$newExploded[] = $current;
			}
		}
 
		// we can now join it again
		return implode( '/' , $newExploded );
	}
 
	function time( &$parser, $format = '', $date = '', $local = false ) {
		global $wgContLang, $wgLocaltimezone;
		if ( isset( $this->mTimeCache[$format][$date][$local] ) ) {
			return $this->mTimeCache[$format][$date][$local];
		}
 
		if ( $date !== '' ) {
			$unix = @strtotime( $date );
		} else {
			$unix = time();
		}
 
		if ( $unix == -1 || $unix == false ) {
			$result = wfMsgForContent( 'pfunc_time_error' );
		} else {
			$this->mTimeChars += strlen( $format );
			if ( $this->mTimeChars > $this->mMaxTimeChars ) {
				return wfMsgForContent( 'pfunc_time_too_long' );
			} else {
				if ( $local ) {
					# Use the time zone
					if ( isset( $wgLocaltimezone ) ) {
						$oldtz = getenv( 'TZ' );
						putenv( 'TZ='.$wgLocaltimezone );
					}
					wfSuppressWarnings(); // E_STRICT system time bitching
					$ts = date( 'YmdHis', $unix );
					wfRestoreWarnings();
					if ( isset( $wgLocaltimezone ) ) {
						putenv( 'TZ='.$oldtz );
					}
				} else {
					$ts = wfTimestamp( TS_MW, $unix );
				}
				if ( method_exists( $wgContLang, 'sprintfDate' ) ) {
					$result = $wgContLang->sprintfDate( $format, $ts );
				} else {
					if ( !class_exists( 'SprintfDateCompat' ) ) {
						require( dirname( __FILE__ ) . '/SprintfDateCompat.php' );
					}
 
					$result = SprintfDateCompat::sprintfDate( $format, $ts );
				}
			}
		}
		$this->mTimeCache[$format][$date][$local] = $result;
		return $result;
	}
 
	function localTime( &$parser, $format = '', $date = '' ) {
		return $this->time( $parser, $format, $date, true );
	}
 
	/**
	 * Obtain a specified number of slash-separated parts of a title,
	 * e.g. {{#titleparts:Hello/World|1}} => "Hello"
	 *
	 * @param Parser $parser Parent parser
	 * @param string $title Title to split
	 * @param int $parts Number of parts to keep
	 * @param int $offset Offset starting at 1
	 * @return string
	 */
	public function titleparts( $parser, $title = '', $parts = -1, $offset = 1 ) {
		$parts = intval( $parts );
		$offset = intval( $offset ) - 1;
		$ntitle = Title::newFromText( $title );
		if( $ntitle instanceof Title ) {
			$bits = explode( '/', $ntitle->getPrefixedText() );
			if( $parts <= 0 || $parts > count( $bits ) ) {
				return $ntitle->getPrefixedText();
			} elseif( $offset < 0 || $offset > count( $bits ) ) {
				return $ntitle->getPrefixedText();
			} else {
				$keep = array();
				for( $i = 0; $i < $offset; $i++ )
					array_shift( $bits );
				for( $i = 0; $i < $parts; $i++ )
					$keep[] = array_shift( $bits );
				return implode( '/', $keep );
			}
		} else {
			return $title;
		}
	}
}
 
function wfSetupLOParserFunctions() {
	global $wgParser, $wgMessageCache, $wgExtLOParserFunctions, $wgMessageCache, $wgHooks;
 
	$wgExtLOParserFunctions = new ExtLOParserFunctions;
 
	$wgParser->setFunctionHook( 'expr', array( &$wgExtLOParserFunctions, 'expr' ) );
	$wgParser->setFunctionHook( 'time', array( &$wgExtLOParserFunctions, 'time' ) );
	$wgParser->setFunctionHook( 'timel', array( &$wgExtLOParserFunctions, 'localTime' ) );
	$wgParser->setFunctionHook( 'rel2abs', array( &$wgExtLOParserFunctions, 'rel2abs' ) );
	$wgParser->setFunctionHook( 'titleparts', array( &$wgExtLOParserFunctions, 'titleparts' ) );
 
	require_once( dirname( __FILE__ ) . '/LOParserFunctions.i18n.php' );
	foreach( efLOParserFunctionsMessages() as $lang => $messages )
		$wgMessageCache->addMessages( $messages, $lang );
 
	$wgHooks['ParserClearState'][] = array( &$wgExtLOParserFunctions, 'clearState' );
}
 
function wfLOParserFunctionsLanguageGetMagic( &$magicWords, $langCode ) {
	require_once( dirname( __FILE__ ) . '/LOParserFunctions.i18n.php' );
	foreach( efLOParserFunctionsWords( $langCode ) as $word => $trans )
		$magicWords[$word] = $trans;
	return true;
}

LOParserFunctions.i18n.php:

<?php
 
/**
 * Get translated magic words, if available
 *
 * @param string $lang Language code
 * @return array
 */
function efLOParserFunctionsWords( $lang ) {
	$words = array();
 
	/**
	 * English
	 */
	$words['en'] = array(
		'expr' 		=> array( 0, 'expr' ),
		'time' 		=> array( 0, 'time' ),
		'timel' 	=> array( 0, 'timel' ),
		'rel2abs' 	=> array( 0, 'rel2abs' ),
		'titleparts' => array( 0, 'titleparts' ),
	);
 
	/**
	 * Farsi-Persian
	 */
	$words['fa'] = array(
		'expr' 		=> array( 0, 'حساب',         'expr' ),
		'time' 		=> array( 0, 'زمان',         'time' ),
		'rel2abs' 	=> array( 0, 'نسبی‌به‌مطلق',   'rel2abs' ),
	);
 
	/**
	 * Hebrew
	 */
	$words['he'] = array(
		'expr'       => array( 0, 'חשב',         'expr' ),
		'time'       => array( 0, 'זמן',         'time' ),
		'timel'      => array( 0, 'זמןמ',        'timel' ),
		'rel2abs'    => array( 0, 'יחסי למוחלט', 'rel2abs' ),
		'titleparts' => array( 0, 'חלק בכותרת',  'titleparts' ),
	);
 
	/**
	 * Indonesian
	 */
	$words['id'] = array(
		'expr'       => array( 0, 'hitung',       'expr' ),
		'time'       => array( 0, 'waktu',        'time' ),
		'rel2abs'    => array( 0, 'rel2abs' ),
		'titleparts' => array( 0, 'bagianjudul',  'titleparts' ),
	);
 
	# English is used as a fallback, and the English synonyms are
	# used if a translation has not been provided for a given word
	return ( $lang == 'en' || !isset( $words[$lang] ) )
		? $words['en']
		: array_merge( $words['en'], $words[$lang] );
}
 
/**
 * Get extension messages
 *
 * @return array
 */
function efLOParserFunctionsMessages() {
	$messages = array(
 
/* English */
'en' => array(
	 'pfunc_time_error'             => 'Error: invalid time',
	 'pfunc_time_too_long'          => 'Error: too many #time calls',
	 'pfunc_rel2abs_invalid_depth'  => 'Error: Invalid depth in path: \"$1\" (tried to access a node above the root node)',
),
 
/* German */
'de' => array(
	 'pfunc_time_error'             => 'Fehler: ungültige Zeitangabe',
	 'pfunc_time_too_long'          => 'Fehler: zu viele #time-Aufrufe',
	 'pfunc_rel2abs_invalid_depth'  => 'Fehler: ungültige Tiefe in Pfad: „$1“ (Versuch, auf einen Knotenpunkt oberhalb des Hauptknotenpunktes zuzugreifen)',
),
 
/* French */
'fr' => array(
	 'pfunc_time_error'            => 'Erreur: durée invalide',
	 'pfunc_time_too_long'         => 'Erreur: parser #time appelé trop de fois',
	 'pfunc_rel2abs_invalid_depth' => 'Erreur: niveau de répertoire invalide dans le chemin : \"$1\" (a essayé d’accéder à un niveau au-dessus du répertoire racine)',
),
 
/* Hebrew */
'he' => array(
	 'pfunc_time_error'             => 'שגי×?×”: זמן שגוי',
	 'pfunc_time_too_long'          => 'שגי×?×”: שימוש ב"#זמן" פעמי×? רבות מדי',
	 'pfunc_rel2abs_invalid_depth'  => 'שגי×?×”: עומק שגוי ×‘× ×ª×™×‘: \"$1\" (× ×™×¡×™×•×Ÿ ×›× ×™×¡×” לצומת מעל צומת השורש)',
),
 
/* Kazakh Cyrillic */
'kk-kz' => array(
	 'pfunc_time_error'             => 'Қате: жарамÑ?ыз уақыт',
	 'pfunc_time_too_long'          => 'Қате: #time әмірін шақыруы тым көп',
	 'pfunc_rel2abs_invalid_depth'  => 'Қате: Мына жолдың жарамÑ?ыз терендігі "$1" (тамыр түйіннің Ò¯Ñ?тіндегі түйінге қатынау талабы)',
),
/* Kazakh Latin */
'kk-tr' => array(
	 'pfunc_time_error'             => 'Qate: jaramsız waqıt',
	 'pfunc_time_too_long'          => 'Qate: #time ämirin şaqırwı tım köp',
	 'pfunc_rel2abs_invalid_depth'  => 'Qate: Mına joldıñ jaramsız terendigi "$1" (tamır tüýinniñ üstindegi tüýinge qatınaw talabı)',
),
/* Kazakh Arabic */
'kk-cn' => array(
	 'pfunc_time_error'             => 'قاتە: جارامسىز ۋاقىت',
	 'pfunc_time_too_long'          => 'قاتە: #time ٵمٸرٸن شاقىرۋى تىم كٶپ',
	 'pfunc_rel2abs_invalid_depth'  => 'قاتە: مىنا جولدىڭ جارامسىز تەرەندٸگٸ "$1" (تامىر تٷيٸننٸڭ ٷستٸندەگٸ تٷيٸنگە قاتىناۋ تالابى)',
),
 
/* Dutch */
'nl' => array(
	 'pfunc_time_error'             => 'Fout: ongeldige tijd',
	 'pfunc_time_too_long'          => 'Fout: #time te vaak aangeroepen',
	 'pfunc_rel2abs_invalid_depth'  => 'Fout: ongeldige diepte in pad: \"$1\" (probeerde een node boven de stamnode aan te roepen)',
),
 
/* Swedish */
'sv' => array(
	 'pfunc_time_error'             => 'Fel: ogiltig tid',
	 'pfunc_time_too_long'          => 'Fel: för många anrop av #time',
	 'pfunc_rel2abs_invalid_depth'  => 'Fel: felaktig djup i sökväg: "$1" (försöker nå en nod ovanför rotnoden)',
),
 
/* Cantonese */
'yue' => array(
	 'pfunc_time_error'             => '錯: 唔啱嘅時間',
	 'pfunc_time_too_long'          => '錯: 太多 #time 呼�',
	 'pfunc_rel2abs_invalid_depth'  => '錯: 唔啱路徑嘅深度: \"$1\" (已經試é?Žç”±é ­é»žè?½å€‹é»žåº¦)',
),
 
/* Chinese (Simplified) */
'zh-hans' => array(
	 'pfunc_time_error'             => '错误: �正确的时间',
	 'pfunc_time_too_long'          => '错误: 过多 #time 的呼�',
	 'pfunc_rel2abs_invalid_depth'  => '错误: �正确的路径深度: \"$1\" (已��试在顶点访问该点)',
),
 
 
/* Chinese (Traditional) */
'zh-hant' => array(
	 'pfunc_time_error'             => '錯誤: �正確的時間',
	 'pfunc_time_too_long'          => '錯誤: �多 #time 的呼�',
	 'pfunc_rel2abs_invalid_depth'  => '錯誤: ä¸?正確的路徑深度: \"$1\" (å·²ç¶“å˜—è©¦åœ¨é ‚é»žå­˜å?–該點)',
),
 
);
 
	/* Kazakh default, fallback to kk-kz */
	$messages['kk'] = $messages['kk-kz'];
 
	/* Chinese defaults, fallback to zh-hans */
	$messages['zh'] = $messages['zh-hans'];
	$messages['zh-cn'] = $messages['zh-hans'];
	$messages['zh-hk'] = $messages['zh-hant'];
	$messages['zh-sg'] = $messages['zh-hans'];
	$messages['zh-tw'] = $messages['zh-hant'];
 
	/* Cantonese default, fallback to yue */
	$messages['zh-yue'] = $messages['yue'];
 
	return $messages ;
}

Then put the following at the end of your LocalSettings.php:

require_once( "$IP/extensions/LOParserFunctions/LOParserFunctions.php" );

See Also[edit | edit source]