Extension:LoopFunctions
![]() |
This extension stores its source code on a wiki page. Please be aware that this code may be unreviewed or maliciously altered. They may contain security holes, outdated interfaces that are no longer compatible etc. The developer is encouraged and invited to request access to MediaWiki's code repository to address this. |
LoopFunctions Release status: beta |
|
---|---|
Implementation | Parser functions |
Description | provides limited looping functionality in wikitext |
Author(s) | Carl Fürstenberg |
Last version | 1.0.5 (Oct 23, 2008) |
MediaWiki | 1.7-1.11, 1.12+ |
License | No license specified |
Download | tarball (v1.0.5) or see below |
Check usage (experimental) |
This extension will enable limited looping functionality in the wikitext, where a variable ($n$ by default) is replaced by the current iteration number of the loop. The variable can be used in a plain text or link. Also the result of the whole loop can be put in an expression, e.g. to compute a factorial. However, the loop body is evaluated before replacing the variable, so e.g. a computation inside the loop body, depending on the value of the variable, is not possible.
Per standard the number of iterations is limited to 100 iterations per session.
Contents |
[edit] Functions
[edit] #for
{{ #for: n | text }} {{ #for: n | text | replacement parameter }}
where n is the count of iteration and the text is the text to be repeated. If, after expansion of templates, parser functions, and variables, the text contains the text $n$ or what is defined by the third parameter, that will be replaced by the current iteration, starting with 1.
[edit] Examples
calling the template {{ foo: |n=4 | list1=a | list2=b | list3=c | list4=d }} using the template foo, containing: {{ #for: {{{n}}} | {{{list$n$}}}<br/> }} will result in the text
a<br/>b<br/>c<br/>d<br/>
- Sum of squares 1^2 through 5^2: {{#expr:{{#for:5|+$n$*$n$}}}} gives 55.
- Factorial 6 (6!): {{#expr:1{{#for:6|*$n$}}}} gives 720.
Compare, using m:Template:for and m:Template:plus square: {{#expr:{{for|call=plus square|pv=1|1|2|3|4|5}}}} gives 55.
For a list of squares it seems that we can only use "for", not "#for".
[edit] Limitation
In the loop body, templates, parser functions, and variables are expanded before replacing the index variable by its values.
Examples:
Parameter of a parser function depending on the index variable:
- {{#for:3|{{lc:Ab$n$}} }} gives ab1ab2ab3; the result is in this case the same as when the repetition was done first, and then the evaluation of each item.
- {{#for:3|{{uc:Ab$n$}} }} gives AB$N$AB$N$AB$N$; the result is in this case not the same, because $n$ is changed to $N$, and therefore not treated as index variable.
- Applying #expr to an expression containing the index variable does not work: expansion of #expr in e.g. {{#expr:2*$n$}} gives the text "Expression error: Unrecognised punctuation character "$"", so this text will be repeated.
Template name depending on the index variable:
- {{#for:3|{{a$n$}} }} gives:
- if the template with the literal name Template:a$n$ does not exist: Template:a1 Template:a2 Template:a3 (the loop body is evaluated as Template:a$n$, after which the loop function repeats that, substituting $n$).
- if the template with the literal name Template:a$n$ does exist: the result of {{a$n$}}{{a$n$}}{{a$n$}}, i.e., the content is repeated unchanged.
[edit] Limitation in combination with m:VariablesExtension
Within the loop a variable can be assigned a value depending on the loop variable, and the value of this variable can be retrieved.
However, it seems that within the loop the variable cannot be assigned a new value depending on its own current value. Instead the value on entering the loop is taken.
[edit] #foreach
{{ #foreach: mask | text }} {{ #foreach: mask | text | replacement parameter }}
The mask is a string in the format prefix$n$suffix where $n$ can be changed via the replacement parameter. The function #foreach, called inside a template, will produce the text for $n$ = 1, 2, 3,... as long as prefix$n$suffix is a template parameter defined in the template call. If the text contains the text $n$ or what is defined by the third parameter, that will be replaced by the current value of $n$.
It seems that the whole call of #foreach is ignored if the loop body contains a template call or a parser function call, or if the loop body does not contain {{{$n$}}}.
[edit] Examples
Calling the template {{ foo | foo2_bar=a | foo1_bar=b | a=12 | 6 |foo4_bar=c }} using the template foo, containing: {{ #foreach: foo$n$_bar | foo$n$_bar = {{{foo$n$_bar}}}<br/> }} will result in the expanded wikitext
foo1_bar = b<br/>foo2_bar = a<br/>
and hence the output
foo1_bar = b
foo2_bar = a
Since foo3_bar is not defined, foo4_bar is not listed either; neither are a and 1, the parameter names which exist but do not match the pattern.
Example without prefix or suffix:
The call {{showpars | abc | de | w=fgh | ijk }} of Template:Showpars containing {{ #foreach: $n$ | $n$ = {{{$n$}}}<br/> }} gives:
1 = abc
2 = de
3 = ijk
Content of a template that links to the pages given by the values of the unnamed parameters:
{{#foreach:$n$|[[{{{$n$}}}]]<nowiki> </nowiki>}}
[edit] Installation (MediaWiki ≤ 1.11)
Note : For MediaWiki 1.12 and later versions, see Installation (MediaWiki 1.12+).
Copy the following text to extensions/LoopFunctions/LoopFunctions.php and extensions/LoopFunctions/LoopFunctions.i18n.php. Then, include it from LocalSettings.php:
require_once ( 'extensions/LoopFunctions/LoopFunctions.php' );
[edit] extensions/LoopFunctions/LoopFunctions.php
<?php /** * Some functions to enable limited looping functionallity, * will also replace the text '$n$' or given parameter in the given text to the current loop count plus one. * * @package MediaWiki * @subpackage Extensions * * @link http://www.mediawiki.org/wiki/Extension:LoopFunctions Documentation * * @author Carl Fürstenberg (AzaToth) <[email protected]> * @copyright Copyright © 2006 Carl Fürstenberg * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later */ if ( !defined( 'MEDIAWIKI' ) ) { die( 'This file is a MediaWiki extension, it is not a valid entry point' ); } require_once('LoopFunctions.i18n.php'); $wgExtensionFunctions[] = 'wfSetupLoopFunctions'; $wgExtensionCredits['parserhook'][] = array( 'version' => '1.0.3', 'description' => 'Provides limited looping functionallity in the wikitext', 'name' => 'LoopFunctions', 'url' => 'http://www.mediawiki.org/wiki/Extension:LoopFunctions', 'author' => 'Carl Fürstenberg (AzaToth)' ); $wgHooks['LanguageGetMagic'][] = 'wfLoopFunctionsLanguageGetMagic'; class ExtLoopFunctions { public static $mMaxLoopCount = 100; // Maximum number of loops allowed per session private static $mCurrentLoopCount = 0; // number of executed loops this session public function forHook(&$parser, $nbr_of_loops = 1 , $text = '' , $param = '$n$' ) { $return = ''; $text = trim($text); $param = trim($param); for($i = 0 ; $i < abs ( intval( $nbr_of_loops ) ) ; ++$i ) { if( ++self :: $mCurrentLoopCount > self :: $mMaxLoopCount ) { return wfMsg( 'loopfunc_max_loops' ); } $return .= strtr( $text , array( $param => $i + 1 ) ); } return $parser->replaceVariables( $return , current($parser->mArgStack) , true); } public function foreachHook(&$parser, $mask = '' , $text = '' , $param = '$n$' ) { $variables = current( $parser->mArgStack ); $param = trim( $param ); $text= trim( $text ); list ( $prefix , $suffix ) = $param == '' ? array( $mask , '' ) : explode ( $param , $mask , 2 ); for($i = 0; array_key_exists( $prefix . ( $i + 1 ) . $suffix , $variables ) ; ++$i ) { if( ++self :: $mCurrentLoopCount > self :: $mMaxLoopCount ) { return wfMsg( 'loopfunc_max_loops' ); } $return .= strtr( $text , array( $param => $i + 1 ) ); } return $parser->replaceVariables( $return , $variables , true); } } function wfSetupLoopFunctions() { global $wgParser, $wgExtLoopFunctions, $wgLoopFunctionsMessages, $wgMessageCache; $wgExtLoopFunctions = new ExtLoopFunctions(); $wgParser->setFunctionHook( 'for', array( &$wgExtLoopFunctions, 'forHook' ) ); $wgParser->setFunctionHook( 'foreach', array( &$wgExtLoopFunctions, 'foreachHook' ) ); foreach( $wgLoopFunctionsMessages as $key => $value ) { $wgMessageCache->addMessages( $value, $key ); } } function wfLoopFunctionsLanguageGetMagic( &$magicWords, $langCode ) { global $wgLoopFunctionsMagic; if(!in_array($langCode,$wgLoopFunctionsMagic)) $langCode = 'en'; $magicWords = array_merge($magicWords, $wgLoopFunctionsMagic[$langCode]); return true; }
[edit] extensions/LoopFunctions/LoopFunctions.i18n.php
<?php if ( !defined( 'MEDIAWIKI' ) ) { die( 'This file is a MediaWiki extension, it is not a valid entry point' ); } $wgLoopFunctionsMessages = array(); $wgLoopFunctionsMagic = array(); $wgLoopFunctionsMessages['cs'] = array( 'loopfunc_max_loops' => "Byl přesažen maximální povolený počet smyček", ); $wgLoopFunctionsMessages['en'] = array( 'loopfunc_max_loops' => "Maximum number of allowed loops reached", ); $wgLoopFunctionsMagic['en'] = array( 'for' => array( 0, 'for' ), 'foreach' => array( 0, 'foreach' ), );
[edit] Installation (MediaWiki 1.12+)
This version is for MediaWiki 1.12 and 1.13.
You can download a tarball here, or copy the code bellow in the right files.
Note : This work has not been checked by the author of LoopFunctions. Please tell me if you notice any regression.
Please note also that the text is now leaved untrimmed in the case of #for
, in order to allow to print a list :
{{#for:3|* foo $n$ }}
now displays :
- foo 1
- foo 2
- foo 3
If you want to keep the old behaviour, check the code of the function forHook()
(LoopFunctions.php, line 65) : just a line to uncoment.
And finaly, please excuse my poor English ! :-)
--Xiloynaha 14:37, 13 August 2008 (UTC)
[edit] extensions/LoopFunctions/LoopFunctions.php
<?php /** * Some functions to enable limited looping functionallity, * will also replace the text '$n$' or given parameter in the given text to the current loop count plus one. * * @package MediaWiki * @subpackage Extensions * * @link http://www.mediawiki.org/wiki/Extension:LoopFunctions Documentation * * @author Carl Fürstenberg (AzaToth) <[email protected]> * @copyright Copyright © 2006 Carl Fürstenberg * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later */ if ( !defined( 'MEDIAWIKI' ) ) { die( 'This file is a MediaWiki extension, it is not a valid entry point' ); } require_once('LoopFunctions.i18n.php'); $wgHooks['ParserFirstCallInit'][] = "ExtLoopFunctions::setup"; $wgExtensionCredits['parserhook'][] = array( 'version' => '1.0.5', 'description' => 'Provides limited looping functionallity in the wikitext', 'name' => 'LoopFunctions', 'url' => 'http://www.mediawiki.org/wiki/Extension:LoopFunctions', 'author' => 'Carl Fürstenberg (AzaToth), adapted to MediaWiki 1.12+ by Xiloynaha' ); $wgHooks['LanguageGetMagic'][] = 'ExtLoopFunctions::languageGetMagic'; class ExtLoopFunctions { public static $mMaxLoopCount = 100; // Maximum number of loops allowed per session private static $mCurrentLoopCount = 0; // number of executed loops this session public static function setup(&$parser) { global $wgParser, $wgExtLoopFunctions, $wgLoopFunctionsMessages, $wgMessageCache; $wgExtLoopFunctions = new ExtLoopFunctions(); $wgParser->setFunctionHook( 'for', array(&$wgExtLoopFunctions, 'forHook'), SFH_OBJECT_ARGS) ; $wgParser->setFunctionHook('foreach', array(__CLASS__, 'foreachHook'), SFH_OBJECT_ARGS) ; foreach( $wgLoopFunctionsMessages as $key => $value ) { $wgMessageCache->addMessages( $value, $key ); } return true ; } public static function languageGetMagic( &$magicWords, $langCode ) { global $wgLoopFunctionsMagic; if (!in_array($langCode, $wgLoopFunctionsMagic)) { $langCode = 'en'; } $magicWords = array_merge($magicWords, $wgLoopFunctionsMagic[$langCode]); return true ; } public function forHook(&$parser, $frame, $args) { $nbr_of_loops = isset($args[0]) ? abs(intval(trim($frame->expand($args[0])))) : 1 ; $text = isset($args[1]) ? $frame->expand($args[1]) : ''; // If you want the text to be trimmed, comment out the line above and uncomment the one below. //$text = isset($args[1]) ? trim($frame->expand($args[1])) : '' ; $param = isset($args[2]) ? trim($frame->expand($args[2])) : '$n$' ; $return = ''; for ($i = 0 ; $i < $nbr_of_loops ; ++$i) { if (++self::$mCurrentLoopCount > self::$mMaxLoopCount) { return wfMsg('loopfunc_max_loops') ; } $return .= strtr($text , array($param => $i + 1)) ; } return $parser->replaceVariables($return , $frame , true) ; } public function foreachHook(&$parser, $frame, $args) { $mask = isset($args[0]) ? trim($frame->expand($args[0])) : '' ; $text = isset($args[1]) ? trim($frame->expand($args[1])) : '' ; $param = isset($args[2]) ? trim($frame->expand($args[2])) : '$n$' ; $variables = $frame->namedArgs + $frame->numberedArgs ; list ($prefix , $suffix) = $param == '' ? array($mask , '') : explode($param , $mask , 2) ; $return = ''; for ($i = 0 ; array_key_exists($prefix . ($i + 1) . $suffix , $variables) ; ++$i) { if (++self::$mCurrentLoopCount > self::$mMaxLoopCount) { return wfMsg('loopfunc_max_loops') ; } $return .= strtr($text , array($param => $i + 1)) ; } return $parser->replaceVariables($return , $frame , true) ; } } ?>
[edit] extensions/LoopFunctions/LoopFunctions.i18n.php
<?php if ( !defined( 'MEDIAWIKI' ) ) { die( 'This file is a MediaWiki extension, it is not a valid entry point' ); } $wgLoopFunctionsMessages = array(); $wgLoopFunctionsMagic = array(); $wgLoopFunctionsMessages['fr'] = array( 'loopfunc_max_loops' => "Nombre maximal de boucles atteint", ); $wgLoopFunctionsMessages['cs'] = array( 'loopfunc_max_loops' => "Byl přesažen maximální povolený počet smyček", ); $wgLoopFunctionsMessages['en'] = array( 'loopfunc_max_loops' => "Maximum number of allowed loops reached", ); $wgLoopFunctionsMagic['en'] = array( 'for' => array( 0, 'for' ), 'foreach' => array( 0, 'foreach' ), ); ?>