Index: trunk/extensions/Loops/Loops.i18n.magic.php |
— | — | @@ -0,0 +1,22 @@ |
| 2 | +<?php |
| 3 | +#coding: utf-8 |
| 4 | + |
| 5 | +/** |
| 6 | + * Internationalization file for magic words of the 'Loops' extension. |
| 7 | + * |
| 8 | + * @since 0.4 |
| 9 | + * |
| 10 | + * @file Loops.i18n.magic.php |
| 11 | + * @ingroup Loops |
| 12 | + * @author Daniel Werner < danweetz@web.de > |
| 13 | + */ |
| 14 | + |
| 15 | +$magicWords = array(); |
| 16 | + |
| 17 | +$magicWords['en'] = array( |
| 18 | + 'while' => array( 0, 'while' ), |
| 19 | + 'dowhile' => array( 0, 'dowhile' ), |
| 20 | + 'loop' => array( 0, 'loop' ), |
| 21 | + 'forargs' => array( 0, 'forargs' ), |
| 22 | + 'fornumargs' => array( 0, 'fornumargs' ), |
| 23 | +); |
Property changes on: trunk/extensions/Loops/Loops.i18n.magic.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 24 | + native |
Index: trunk/extensions/Loops/Loops.i18n.php |
— | — | @@ -1,55 +1,30 @@ |
2 | 2 | <?php |
3 | | - |
4 | | -class Loops_i18n { |
5 | | - private $words = array( |
6 | | - // English |
7 | | - 'en' => array( |
8 | | - 'dowhile' => array( 0, 'dowhile' ), |
9 | | - 'while' => array( 0, 'while' ), |
10 | | - 'forargs' => array( 0, 'forargs' ), |
11 | | - 'fornumargs' => array( 0, 'fornumargs' ), |
12 | | - 'loop' => array( 0, 'loop' ), |
13 | | - ), |
14 | | - ); |
15 | | - |
16 | | - private $messages = array( |
17 | | - // English |
18 | | - 'en' => array( |
19 | | - 'loops_max' => 'Maximum number of loops have been performed', |
20 | | - ) |
21 | | - ); |
22 | | - |
23 | | - private static $instance = null; |
24 | | - |
25 | | - public static function getInstance() { |
26 | | - // create the singleton if needed |
27 | | - if ( self::$instance === null ) { |
28 | | - self::$instance = new self(); |
29 | | - } |
30 | | - |
31 | | - return self::$instance; |
32 | | - } |
33 | | - |
34 | | - /** |
35 | | - * limited-access constructor to insure singleton |
36 | | - */ |
37 | | - protected function __construct() { } |
38 | | - |
39 | | - /** |
40 | | - * Get translated magic words, if available |
41 | | - * |
42 | | - * @param $lang String: language code |
43 | | - * @return array |
44 | | - */ |
45 | | - public function magicWords( $lang ) { |
46 | | - // English is used as a fallback, and the English synonyms are |
47 | | - // used if a translation has not been provided for a given word |
48 | | - return ( $lang == 'en' || !isset( $this->words[$lang] ) ) ? |
49 | | - $this->words['en'] : |
50 | | - array_merge( $this->words['en'], $this->words[$lang] ); |
51 | | - } |
52 | | - |
53 | | - public function getMessages() { |
54 | | - return $this->messages; |
55 | | - } |
56 | | -} |
| 3 | +#coding: utf-8 |
| 4 | + |
| 5 | +/** |
| 6 | + * Internationalization file of the 'Loops' extension. |
| 7 | + * |
| 8 | + * @since 0.4 |
| 9 | + * |
| 10 | + * @file Loops.i18n.php |
| 11 | + * @ingroup Loops |
| 12 | + * @author Daniel Werner < danweetz@web.de > |
| 13 | + */ |
| 14 | + |
| 15 | +$messages = array(); |
| 16 | + |
| 17 | +/** English |
| 18 | + * @author David M. Sledge |
| 19 | + */ |
| 20 | +$messages['en'] = array( |
| 21 | + 'loops-desc' => 'Parser functions for performing loops', |
| 22 | + 'loops_max' => 'Maximum number of loops have been performed' |
| 23 | +); |
| 24 | + |
| 25 | +/** German |
| 26 | + * @author Daniel Werner |
| 27 | + */ |
| 28 | +$messages['de'] = array( |
| 29 | + 'loops-desc' => 'Parser-Funktionen zur Schleifen-Ausführung', |
| 30 | + 'loops_max' => 'Die maximal erlaubte Anzahl an Schleifen wurde ausgeführt' |
| 31 | +); |
Index: trunk/extensions/Loops/Loops.php |
— | — | @@ -1,4 +1,5 @@ |
2 | 2 | <?php |
| 3 | + |
3 | 4 | /** |
4 | 5 | * 'Loops' is a MediaWiki extension expanding the parser with loops functions |
5 | 6 | * |
— | — | @@ -6,7 +7,7 @@ |
7 | 8 | * Support: http://www.mediawiki.org/wiki/Extension_talk:Loops |
8 | 9 | * Source code: http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/Loops |
9 | 10 | * |
10 | | - * @version: 0.3.2 |
| 11 | + * @version: 0.4 alpha |
11 | 12 | * @license: GNU GPL v2 or higher |
12 | 13 | * @author: David M. Sledge |
13 | 14 | * @author: Daniel Werner < danweetz@web.de > |
— | — | @@ -14,297 +15,321 @@ |
15 | 16 | * @file Loops.php |
16 | 17 | * @ingroup Loops |
17 | 18 | */ |
| 19 | + |
| 20 | +if ( ! defined( 'MEDIAWIKI' ) ) { die( ); } |
18 | 21 | |
19 | | -if ( !defined( 'MEDIAWIKI' ) ) { |
20 | | - die( 'This file is a MediaWiki extension, it is not a valid entry point' ); |
21 | | -} |
22 | | - |
23 | | -$wgHooks['ParserFirstCallInit'][] = $wgHooks['LanguageGetMagic'][] |
24 | | - = $wgHooks['ParserLimitReport'][] = ExtLoops::getInstance(); |
25 | | - |
26 | | -// Extension credits that will show up on Special:Version |
27 | 22 | $wgExtensionCredits['parserhook'][] = array( |
28 | | - 'path' => __FILE__, |
29 | | - 'author' => 'David M. Sledge', |
| 23 | + 'path' => __FILE__, |
| 24 | + 'author' => array( 'David M. Sledge', '[http://www.mediawiki.org/wiki/User:Danwe Daniel Werner]' ), |
30 | 25 | 'name' => 'Loops', |
31 | 26 | 'version' => ExtLoops::VERSION, |
32 | | - 'description' => 'Parser functions for performing loops', |
| 27 | + 'descriptionmsg' => 'loops-desc', |
33 | 28 | 'url' => 'http://www.mediawiki.org/wiki/Extension:Loops', |
34 | 29 | ); |
35 | | - |
| 30 | + |
| 31 | +// language files: |
| 32 | +$wgExtensionMessagesFiles['Loops' ] = ExtLoops::getDir() . '/Loops.i18n.php'; |
| 33 | +$wgExtensionMessagesFiles['LoopsMagic'] = ExtLoops::getDir() . '/Loops.i18n.magic.php'; |
| 34 | + |
| 35 | +// hooks registration: |
| 36 | +$wgHooks['ParserFirstCallInit'][] = 'ExtLoops::init'; |
| 37 | +$wgHooks['ParserClearState' ][] = 'ExtLoops::onParserClearState'; |
| 38 | + |
| 39 | + |
| 40 | +/** |
| 41 | + * Class representing extension 'Loops', containing all parser functions and other |
| 42 | + * extension logic stuff. |
| 43 | + */ |
36 | 44 | class ExtLoops { |
37 | | - const VERSION = '0.3.2'; |
| 45 | + |
| 46 | + const VERSION = '0.4 alpha'; |
38 | 47 | |
39 | | - public static $maxLoops = 100; // maximum number of loops allowed |
40 | | - // (-1 = no limit). #forargs is |
41 | | - // not limited by this. |
42 | | - private static $instance = null; |
43 | | - |
44 | | - private $parserFunctions = array( |
45 | | - 'dowhile' => array( 'dowhile', SFH_OBJECT_ARGS ), |
46 | | - 'while' => array( 'whileHook', SFH_OBJECT_ARGS ), |
47 | | - 'loop' => array( 'loop', SFH_OBJECT_ARGS ), |
48 | | - 'forargs' => array( 'forArgs', SFH_OBJECT_ARGS ), |
49 | | - 'fornumargs' => array( 'forNumArgs', SFH_OBJECT_ARGS ), |
50 | | - ); |
51 | | - |
52 | | - private $loopCount = 0; |
53 | | - |
54 | | - public static function getInstance() { |
55 | | - // create the singleton if needed |
56 | | - if ( self::$instance === null ) { |
57 | | - self::$instance = new self(); |
58 | | - } |
59 | | - |
60 | | - return self::$instance; |
61 | | - } |
62 | | - |
63 | 48 | /** |
64 | | - * limited-access constructor to insure singleton |
| 49 | + * Configuration variable defining maximum allowed number of loops ('-1' => no limit). |
| 50 | + * '#forargs' and '#fornumargs' are not limited by this. |
| 51 | + * |
| 52 | + * @var int |
65 | 53 | */ |
66 | | - protected function __construct() { } |
67 | | - |
68 | | - public function onParserFirstCallInit( &$parser ) { |
69 | | - global $wgMessageCache, $wgHooks; |
70 | | - |
71 | | - // These functions accept DOM-style arguments |
72 | | - foreach( $this->parserFunctions as $hook => $callAndFlags ) { |
73 | | - $parser->setFunctionHook( $hook, array( $this, $callAndFlags[0] ), |
74 | | - $callAndFlags[1] ); |
| 54 | + public static $maxLoops = 100; |
| 55 | + |
| 56 | + /** |
| 57 | + * Returns the extensions base installation directory. |
| 58 | + * |
| 59 | + * @since 0.4 |
| 60 | + * |
| 61 | + * @return boolean |
| 62 | + */ |
| 63 | + public static function getDir() { |
| 64 | + static $dir = null; |
| 65 | + |
| 66 | + if( $dir === null ) { |
| 67 | + $dir = dirname( __FILE__ ); |
75 | 68 | } |
76 | | - |
77 | | - require_once( dirname( __FILE__ ) . '/Loops.i18n.php' ); |
78 | | - |
79 | | - // @todo FIXME: $wgMessageCache and the addMessages method were |
80 | | - // removed in http://www.mediawiki.org/wiki/Special:Code/MediaWiki/81027 |
81 | | - foreach( Loops_i18n::getInstance()->getMessages() |
82 | | - as $lang => $messages ) |
83 | | - { |
84 | | - $wgMessageCache->addMessages( $messages, $lang ); |
85 | | - } |
86 | | - |
87 | | - $wgHooks['ParserClearState'][] = $this; |
88 | | - |
89 | | - return true; |
| 69 | + return $dir; |
90 | 70 | } |
91 | | - |
92 | | - public function onLanguageGetMagic( &$magicWords, $langCode ) { |
93 | | - require_once( dirname( __FILE__ ) . '/Loops.i18n.php' ); |
94 | | - |
95 | | - foreach( Loops_i18n::getInstance()->magicWords( $langCode ) |
96 | | - as $word => $trans ) |
97 | | - { |
98 | | - $magicWords[$word] = $trans; |
99 | | - } |
100 | | - |
| 71 | + |
| 72 | + /** |
| 73 | + * Sets up parser functions |
| 74 | + * |
| 75 | + * @since 0.4 |
| 76 | + */ |
| 77 | + public static function init( Parser &$parser ) { |
| 78 | + |
| 79 | + /* |
| 80 | + * store for loops count per parser object. This will solve several bugs related to |
| 81 | + * 'ParserClearState' hook resetting the count early in combination with certain |
| 82 | + * other extensions or special page inclusion. (since v0.4) |
| 83 | + */ |
| 84 | + $parser->mExtLoopsCounter = 0; |
| 85 | + |
| 86 | + self::initFunction( $parser, 'while' ); |
| 87 | + self::initFunction( $parser, 'dowhile' ); |
| 88 | + self::initFunction( $parser, 'loop' ); |
| 89 | + self::initFunction( $parser, 'forargs' ); |
| 90 | + self::initFunction( $parser, 'fornumargs' ); |
| 91 | + |
101 | 92 | return true; |
102 | 93 | } |
103 | | - |
| 94 | + private static function initFunction( Parser &$parser, $name ) { |
| 95 | + $functionCallback = array( __CLASS__, 'pfObj_' . $name ); |
| 96 | + $parser->setFunctionHook( $name, $functionCallback, SFH_OBJECT_ARGS ); |
| 97 | + } |
| 98 | + |
| 99 | + |
104 | 100 | public function onParserLimitReport( $parser, &$report ) { |
105 | | - if ( isset( $this->loopCount ) ) { |
106 | | - $report .= "ExtLoops count: {$this->loopCount}/" . self::$maxLoops . "\n"; |
| 101 | + // add performed loops to limit report: |
| 102 | + $report .= 'ExtLoops count: ' . self::getLoopsCount( $parser ); |
| 103 | + |
| 104 | + if( self::$maxLoops > -1 ) { |
| 105 | + // if limit is set, communicate the limit as well: |
| 106 | + $report .= '/' . self::$maxLoops . "\n"; |
107 | 107 | } |
108 | | - |
109 | 108 | return true; |
110 | 109 | } |
111 | | - |
112 | | - public function whileHook( &$parser, $frame, $args ) { |
113 | | - // bug 12842: first argument is automatically |
114 | | - // expanded, so we ignore this one |
115 | | - array_shift( $args ); |
116 | | - $test = array_shift( $args ); |
117 | | - $loopStatement = array_shift( $args ); |
118 | | - $output = ''; |
119 | | - |
120 | | - while ( isset( $test ) && trim( $frame->expand( $test ) ) !== '' ) { |
121 | | - if ( |
122 | | - self::$maxLoops >= 0 && |
123 | | - ++$this->loopCount > self::$maxLoops |
124 | | - ) |
125 | | - { |
126 | | - return $output . wfMsgForContent( 'loops_max' ); |
127 | | - } |
128 | | - |
129 | | - $output .= isset( $loopStatement ) ? |
130 | | - trim( $frame->expand( $loopStatement ) ) : ''; |
| 110 | + |
| 111 | + |
| 112 | + #################### |
| 113 | + # Parser Functions # |
| 114 | + #################### |
| 115 | + |
| 116 | + public static function pfObj_while( Parser &$parser, $frame, $args ) { |
| 117 | + return self::perform_while( $parser, $frame, $args, false ); |
| 118 | + } |
| 119 | + |
| 120 | + public static function pfObj_dowhile( Parser &$parser, $frame, $args ) { |
| 121 | + return self::perform_while( $parser, $frame, $args, true ); |
| 122 | + } |
| 123 | + |
| 124 | + /** |
| 125 | + * Generic function handling '#while' and '#dowhile' as one |
| 126 | + */ |
| 127 | + protected static function perform_while( Parser &$parser, $frame, $args, $dowhile = false ) { |
| 128 | + // #(do)while: | condition | code |
| 129 | + $rawCond = isset( $args[1] ) ? $args[1] : ''; // unexpanded condition |
| 130 | + $rawCode = isset( $args[2] ) ? $args[2] : ''; // unexpanded loop code |
| 131 | + |
| 132 | + if( |
| 133 | + $dowhile === false |
| 134 | + && trim( $frame->expand( $rawCond ) ) === '' |
| 135 | + ) { |
| 136 | + // while, but condition not fullfilled from the start |
| 137 | + return ''; |
131 | 138 | } |
132 | | - |
133 | | - //return '<pre><nowiki>'. $output . '</nowiki></'. 'pre>'; |
134 | | - return $output; |
135 | | - } |
136 | | - |
137 | | - public function dowhile( &$parser, $frame, $args ) { |
138 | | - // bug 12842: first argument is automatically |
139 | | - // expanded, so we ignore this one |
140 | | - array_shift( $args ); |
141 | | - $test = array_shift( $args ); |
142 | | - $loopStatement = array_shift( $args ); |
| 139 | + |
143 | 140 | $output = ''; |
144 | | - |
| 141 | + |
145 | 142 | do { |
146 | | - if ( |
147 | | - self::$maxLoops >= 0 && |
148 | | - ++$this->loopCount > self::$maxLoops |
149 | | - ) |
150 | | - { |
151 | | - return $output . wfMsgForContent( 'loops_max' ); |
| 143 | + // limit check: |
| 144 | + if( ! self::incrCounter( $parser ) ) { |
| 145 | + return self::msgLoopsLimit( $output ); |
152 | 146 | } |
153 | | - |
154 | | - $output .= isset( $loopStatement ) ? |
155 | | - trim( $frame->expand( $loopStatement ) ) : ''; |
156 | | - } while ( isset( $test ) && trim( $frame->expand( $test ) ) !== '' ); |
157 | | - |
158 | | - //return '<pre><nowiki>'. $output . '</nowiki></'. 'pre>'; |
| 147 | + $output .= trim( $frame->expand( $rawCode ) ); |
| 148 | + |
| 149 | + } while( trim( $frame->expand( $rawCond ) ) ); |
| 150 | + |
159 | 151 | return $output; |
160 | 152 | } |
161 | | - |
162 | | - public function forArgs( &$parser, $frame, $args ) { |
163 | | - if ( !( $frame instanceof PPTemplateFrame_DOM ) ) { |
164 | | - $arg = array_shift( $args ); |
165 | | - $arg = isset( $arg ) ? $frame->expand( $arg ) : ''; |
166 | | - |
167 | | - // TODO: get the synonym for the content language |
168 | | - $out = "{{#forargs:$arg"; |
169 | | - |
170 | | - // expand and display each argument |
171 | | - while ( ( $arg = array_shift( $args ) ) !== null ) { |
172 | | - $out .= '|' . $frame->expand( $arg ); |
173 | | - } |
174 | | - |
175 | | - $out .= '}}'; |
176 | | - |
177 | | - return $out; |
| 153 | + |
| 154 | + public static function pfObj_loop( Parser &$parser, PPFrame $frame, $args ) { |
| 155 | + // #loop: var | start | count | code |
| 156 | + $varName = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : ''; |
| 157 | + $startVal = isset( $args[1] ) ? (int)trim( $frame->expand( $args[1] ) ) : 0; |
| 158 | + $loops = isset( $args[2] ) ? (int)trim( $frame->expand( $args[2] ) ) : 0; |
| 159 | + $rawCode = isset( $args[3] ) ? $args[3] : ''; // unexpanded loop code |
| 160 | + |
| 161 | + if( $loops === 0 ) { |
| 162 | + // no loops to perform |
| 163 | + return ''; |
178 | 164 | } |
179 | | - |
| 165 | + |
180 | 166 | global $wgExtVariables; |
181 | | - |
182 | | - // The first arg is already expanded, but this is a good habit to have. |
183 | | - $filter = isset( $args[0] ) ? trim( $frame->expand( $args[0] ) ) : ''; |
184 | | - // name of the variable to store the argument name. this |
185 | | - // will be accessed in the loop by using {{#var:}} |
186 | | - $keyVarName = isset( $args[1] ) ? |
187 | | - trim( $frame->expand( $args[1] ) ) : ''; |
188 | | - // name of the variable to store the argument value. |
189 | | - $valueVarName = isset( $args[2] ) ? |
190 | | - trim( $frame->expand( $args[2] ) ) : ''; |
191 | | - $loopStatement = isset( $args[3] ) ? $args[3] : ''; |
| 167 | + |
192 | 168 | $output = ''; |
193 | | - |
194 | | - $tArgs = preg_match( '/^([1-9][0-9]*)?$/', $filter ) > 0 |
195 | | - ? $frame->getArguments() : $frame->getNamedArguments(); |
196 | | - |
197 | | - foreach ( $tArgs as $argName => $argVal ) { |
198 | | - if ( $filter == '' || strpos( $argName, $filter ) === 0 ) { |
199 | | - if ( $keyVarName !== $valueVarName ) { |
200 | | - $wgExtVariables->vardefine( $parser, $keyVarName, |
201 | | - trim( substr( $argName, strlen( $filter ) ) ) ); |
202 | | - } |
203 | | - |
204 | | - $wgExtVariables->vardefine( $parser, $valueVarName, trim( $argVal ) ); |
205 | | - $output .= trim( $frame->expand( $loopStatement ) ); |
| 169 | + $endVal = $startVal + $loops; |
| 170 | + |
| 171 | + while( $startVal !== $endVal ) { |
| 172 | + // limit check: |
| 173 | + if( ! self::incrCounter( $parser ) ) { |
| 174 | + return self::msgLoopsLimit( $output ); |
206 | 175 | } |
| 176 | + |
| 177 | + $wgExtVariables->vardefine( $parser, $varName, (string)$startVal ); |
| 178 | + |
| 179 | + $output .= trim( $frame->expand( $rawCode ) ); |
| 180 | + |
| 181 | + // in-/decrease loop count (count can be negative): |
| 182 | + ( $startVal < $endVal ) ? $startVal++ : $startVal--; |
207 | 183 | } |
208 | | - |
209 | 184 | return $output; |
210 | 185 | } |
211 | | - |
212 | | - public function forNumArgs( &$parser, $frame, $args ) { |
213 | | - if ( !( $frame instanceof PPTemplateFrame_DOM ) ) { |
214 | | - $arg = array_shift( $args ); |
215 | | - $arg = isset( $arg ) ? $frame->expand( $arg ) : ''; |
216 | | - |
217 | | - // TODO: get the synonym for the content language |
218 | | - $out = "{{#forargs:$arg"; |
219 | | - |
220 | | - // expand and display each argument |
221 | | - while ( ( $arg = array_shift( $args ) ) !== null ) { |
222 | | - $out .= '|' . $frame->expand( $arg ); |
| 186 | + |
| 187 | + /** |
| 188 | + * #forargs: filter | keyVarName | valVarName | code |
| 189 | + */ |
| 190 | + public static function pfObj_forargs( Parser &$parser, $frame, $args ) { |
| 191 | + // The first arg is already expanded, but this is a good habit to have... |
| 192 | + $filter = array_shift( $args ); |
| 193 | + $filter = $filter !== null ? trim( $frame->expand( $filter ) ) : ''; |
| 194 | + |
| 195 | + // if prefix contains numbers only or isn't set, get all arguments, otherwise just non-numeric |
| 196 | + $tArgs = ( preg_match( '/^([1-9][0-9]*)?$/', $filter ) > 0 ) |
| 197 | + ? $frame->getArguments() |
| 198 | + : $frame->getNamedArguments(); |
| 199 | + |
| 200 | + return self::perform_forargs( $parser, $frame, $args, $tArgs, $filter ); |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * #fornumargs: keyVarName | valVarName | code |
| 205 | + */ |
| 206 | + public static function pfObj_fornumargs( Parser &$parser, $frame, $args ) { |
| 207 | + /* |
| 208 | + * get numeric arguments, don't use PPFrame::getNumberedArguments because it would |
| 209 | + * return explicitely numbered arguments only. |
| 210 | + */ |
| 211 | + $tNumArgs = $frame->getArguments(); |
| 212 | + foreach( $tNumArgs as $argKey => $argVal ) { |
| 213 | + // allow all numeric, including negative values! |
| 214 | + if( is_string( $argKey ) ) { |
| 215 | + unset( $tNumArgs[ $argKey ] ); |
223 | 216 | } |
224 | | - |
225 | | - $out .= '}}'; |
226 | | - |
227 | | - return $out; |
228 | 217 | } |
229 | | - |
| 218 | + ksort( $tNumArgs ); // sort from lowest to highest |
| 219 | + |
| 220 | + return self::perform_forargs( $parser, $frame, $args, $tNumArgs, '' ); |
| 221 | + } |
| 222 | + |
| 223 | + /** |
| 224 | + * Generic function handling '#forargs' and '#fornumargs' as one |
| 225 | + */ |
| 226 | + protected static function perform_forargs( Parser &$parser, PPFrame $frame, array $funcArgs, array $templateArgs, $prefix = '' ) { |
| 227 | + // if not called within template instance: |
| 228 | + if( !( $frame instanceof PPTemplateFrame_DOM ) ) { |
| 229 | + return array( 'found' => false ); |
| 230 | + } |
| 231 | + |
| 232 | + // name of the variable to store the argument name: |
| 233 | + $keyVar = array_shift( $funcArgs ); |
| 234 | + $keyVar = $keyVar !== null ? trim( $frame->expand( $keyVar ) ) : ''; |
| 235 | + // name of the variable to store the argument value: |
| 236 | + $valVar = array_shift( $funcArgs ); |
| 237 | + $valVar = $valVar !== null ? trim( $frame->expand( $valVar ) ) : ''; |
| 238 | + // unexpanded code: |
| 239 | + $rawCode = array_shift( $funcArgs ); |
| 240 | + $rawCode = $rawCode !== null ? $rawCode : ''; |
| 241 | + |
230 | 242 | global $wgExtVariables; |
231 | | - |
232 | | - // The first arg is already expanded, but this is a good habit to have. |
233 | | - // name of the variable to store the argument name. this |
234 | | - // will be accessed in the loop by using {{#var:}} |
235 | | - $keyVarName = isset( $args[0] ) ? |
236 | | - trim( $frame->expand( $args[0] ) ) : ''; |
237 | | - // name of the variable to store the argument value. |
238 | | - $valueVarName = isset( $args[1] ) ? |
239 | | - trim( $frame->expand( $args[1] ) ) : ''; |
240 | | - $loopStatement = isset( $args[2] ) ? $args[2] : ''; |
241 | 243 | $output = ''; |
242 | | - $numArgs = $frame->getArguments(); |
243 | | - ksort( $numArgs ); |
244 | | - |
245 | | - foreach ( $numArgs as $argNumber => $argVal ) { |
246 | | - if ( is_string( $argNumber ) ) { |
| 244 | + |
| 245 | + // if prefix contains numbers only or isn't set, get all arguments, otherwise just non-numeric |
| 246 | + $tArgs = preg_match( '/^([1-9][0-9]*)?$/', $prefix ) > 0 |
| 247 | + ? $frame->getArguments() : $frame->getNamedArguments(); |
| 248 | + |
| 249 | + foreach( $templateArgs as $argName => $argVal ) { |
| 250 | + // if no filter or prefix in argument name: |
| 251 | + if( $prefix !== '' && strpos( $argName, $prefix ) !== 0 ) { |
247 | 252 | continue; |
248 | 253 | } |
249 | | - |
250 | | - if ( $keyVarName !== $valueVarName ) { |
251 | | - $wgExtVariables->vardefine( $parser, $keyVarName, trim( $argNumber ) ); |
| 254 | + if ( $keyVar !== $valVar ) { |
| 255 | + // variable with the argument name as value |
| 256 | + $wgExtVariables->vardefine( |
| 257 | + $parser, |
| 258 | + $keyVar, |
| 259 | + trim( substr( $argName, strlen( $prefix ) ) ) |
| 260 | + ); |
252 | 261 | } |
253 | | - |
254 | | - $wgExtVariables->vardefine( $parser, $valueVarName, trim( $argVal ) ); |
255 | | - $output .= trim( $frame->expand( $loopStatement ) ); |
| 262 | + // variable with the arguments value |
| 263 | + $wgExtVariables->vardefine( $parser, $valVar, trim( $argVal ) ); |
| 264 | + |
| 265 | + // expand current run: |
| 266 | + $output .= trim( $frame->expand( $rawCode ) ); |
256 | 267 | } |
257 | | - |
| 268 | + |
258 | 269 | return $output; |
259 | 270 | } |
260 | | - |
261 | | - public function loop( &$parser, $frame, $args ) { |
262 | | - global $wgExtVariables; |
263 | | - // snag the variable name |
264 | | - $varName = array_shift( $args ); |
265 | | - $varName = $varName === null ? '' : trim( $frame->expand( $varName ) ); |
266 | | - // grab the intitial value for the variable (default to 0) |
267 | | - $startVal = array_shift( $args ); |
268 | | - $startVal = $startVal === null |
269 | | - ? 0 : intval( trim( $frame->expand( $startVal ) ) ); |
270 | | - // How many times are we gonna loop? |
271 | | - $count = array_shift( $args ); |
272 | | - |
273 | | - if ( $count === null ) { |
274 | | - return ''; |
| 271 | + |
| 272 | + |
| 273 | + ############### |
| 274 | + # Loops Count # |
| 275 | + ############### |
| 276 | + |
| 277 | + /** |
| 278 | + * Returns how many loops have been performed for a given Parser instance. |
| 279 | + * |
| 280 | + * @since 0.4 |
| 281 | + * |
| 282 | + * @param Parser $parser |
| 283 | + * @return int |
| 284 | + */ |
| 285 | + public static function getLoopsCount( Parser &$parser ) { |
| 286 | + return $parser->mExtLoopsCounter; |
| 287 | + } |
| 288 | + |
| 289 | + /** |
| 290 | + * Returns whether the maximum number of loops for the given Parser instance have |
| 291 | + * been performed already. |
| 292 | + * |
| 293 | + * @since 0.4 |
| 294 | + * |
| 295 | + * @param Parser $parser |
| 296 | + * @return bool |
| 297 | + */ |
| 298 | + public static function maxLoopsPerformed( Parser &$parser ) { |
| 299 | + $count = $parser->mExtLoopsCounter; |
| 300 | + return $count > -1 && $count >= self::$maxLoops; |
| 301 | + } |
| 302 | + |
| 303 | + /** |
| 304 | + * If limit has not been exceeded already, this will increase the counter. If |
| 305 | + * exceeded false will be returned, otherwise the new counter value |
| 306 | + * |
| 307 | + * @return false|int |
| 308 | + */ |
| 309 | + protected static function incrCounter( Parser &$parser ) { |
| 310 | + if( self::maxLoopsPerformed( $parser ) ) { |
| 311 | + return false; |
275 | 312 | } |
276 | | - |
277 | | - $endVal = $startVal + intval( trim( $frame->expand( $count ) ) ); |
278 | | - |
279 | | - if ( $endVal == $startVal ) { |
280 | | - return ''; |
| 313 | + return ++$parser->mExtLoopsCounter; |
| 314 | + } |
| 315 | + |
| 316 | + /** |
| 317 | + * div wrapped error message stating maximum number of loops have been performed. |
| 318 | + */ |
| 319 | + protected static function msgLoopsLimit( $output = '' ) { |
| 320 | + if( trim( $output ) !== '' ) { |
| 321 | + $output .= "\n"; |
281 | 322 | } |
282 | | - |
283 | | - // grab the unexpanded loop statement |
284 | | - $loopStatement = array_shift( $args ); |
285 | | - $output = ''; |
286 | | - |
287 | | - for ( ; $startVal != $endVal; |
288 | | - $startVal < $endVal ? $startVal++ : $startVal-- ) |
289 | | - { |
290 | | - if ( |
291 | | - self::$maxLoops >= 0 && |
292 | | - ++$this->loopCount > self::$maxLoops |
293 | | - ) { |
294 | | - return $output . wfMsgForContent( 'loops_max' ); |
295 | | - } |
296 | | - |
297 | | - $wgExtVariables->vardefine( $parser, $varName, trim( $startVal ) ); |
298 | | - |
299 | | - $output .= isset( $loopStatement ) ? |
300 | | - trim( $frame->expand( $loopStatement ) ) : ''; |
301 | | - } |
302 | | - |
303 | | - return $output; |
| 323 | + return $output .= '<div class="error">' . wfMsgForContent( 'loops_max' ) . '</div>'; |
304 | 324 | } |
305 | | - |
306 | | - public function onParserClearState( &$parser ) { |
307 | | - $this->loopCount = 0; |
308 | | - |
| 325 | + |
| 326 | + |
| 327 | + ################## |
| 328 | + # Hooks handling # |
| 329 | + ################## |
| 330 | + |
| 331 | + public static function onParserClearState( Parser &$parser ) { |
| 332 | + // reset loops counter since the parser process finished one page |
| 333 | + $parser->mExtLoopsCounter = 0; |
309 | 334 | return true; |
310 | 335 | } |
311 | 336 | } |
Index: trunk/extensions/Loops/RELEASE-NOTES |
— | — | @@ -1,14 +1,23 @@ |
2 | 2 | Post svn Changelog: |
3 | 3 | =================== |
4 | 4 | |
5 | | - * Version 0.3.2 by Daniel Werner |
| 5 | + * (trunk) -- Version 0.4 alpha by Daniel Werner |
| 6 | + - The loops count now counts per parser instance. Before there were several hacks to reset the |
| 7 | + counter, for example including an special page in wikitext. This has been solved now. |
| 8 | + - Language file clean-up. |
| 9 | + - Internal structure changed, ExtLoops now works as a static class. |
| 10 | + @ToDo: Open points for final 0.4 release: |
| 11 | + - Compatbility to Variables Version 2.0. |
| 12 | + - '#fornumargs' parameter 1 optional to make it more consistent with other functions. |
| 13 | + |
| 14 | + * November 15, 2011 -- Version 0.3.2 by Daniel Werner |
6 | 15 | - Bug solved where '#loop' didn't create variables with name or starting index '0'. |
7 | 16 | - minor '#forargs' and '#fornumargs' bug solved where unnamed numeric argument values |
8 | 17 | could have ended up as variable value with leading space. |
9 | 18 | - 'README' and 'RELEASE-NOTES' files added. |
10 | 19 | - Put into mediawiki.org svn repository. |
| 20 | + |
11 | 21 | |
12 | | - |
13 | 22 | Pre svn Changelog: |
14 | 23 | ================== |
15 | 24 | |