Extension:LO Parser Functions

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manual - list
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)
Last version N/A (2007-11-15)
MediaWiki > 1.6.8
License GNU GPL 2.0 or later
Download No link
Check usage and version matrix

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]

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]