r29480 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r29479‎ | r29480 | r29481 >
Date:06:14, 9 January 2008
Author:tstarling
Status:old
Tags:
Comment:
MW 1.12 version of #lst/#lstx. Uses the document tree form of the template to find <section/> tags. No need for headingOffset since PPFrame::expand() adds the appropriate heading markers. The new preprocessor lacks the capricious stripping of trailing whitespace present in the old LST, so I updated the parser tests to suit. My other parser test changes are trivial, and all tests pass.

I didn't use XPath in the end, because I really want to migrate away from DOM at some stage, so I'd prefer to avoid references to such a heavyweight feature. This just uses firstChild/nextSibling to traverse the tree, and never refers to any DOM class by name.
Modified paths:
  • /trunk/extensions/LabeledSectionTransclusion/lst.php (modified) (history)
  • /trunk/extensions/LabeledSectionTransclusion/lstParserTests.txt (modified) (history)
  • /trunk/extensions/LabeledSectionTransclusion/lsth.php (modified) (history)

Diff [purge]

Index: trunk/extensions/LabeledSectionTransclusion/lstParserTests.txt
@@ -25,7 +25,7 @@
2626
2727 #test to see what happens when we don't use LST.
2828 !!test
29 -Normal rendering
 29+LST: Normal rendering
3030 !!input
3131 begin <section begin=test/>middle<section end=test/> end
3232 !!result
@@ -34,7 +34,7 @@
3535 !!end
3636
3737 !!test
38 -Normal (non-labelled) transclusion
 38+LST: Normal (non-labelled) transclusion
3939 !!input
4040 {{:sections}}
4141 !!result
@@ -47,7 +47,7 @@
4848 !!end
4949
5050 !!test
51 -Transclude a section (#lst)
 51+LST: Transclude a section (#lst)
5252 !!input
5353 {{#lst:sections|1}}
5454 !!result
@@ -56,7 +56,7 @@
5757 !!end
5858
5959 !!test
60 -Transclude a section (#section)
 60+LST: Transclude a section (#section)
6161 !!input
6262 {{#section:sections|1}}
6363 !!result
@@ -65,7 +65,7 @@
6666 !!end
6767
6868 !!test
69 -Substitute a section
 69+LST: Substitute a section
7070 !!options
7171 pst
7272 !!input
@@ -76,7 +76,7 @@
7777
7878
7979 !!test
80 - with quoted attributes
 80+LST: Substitute a section with quoted attributes
8181 !!input
8282 {{#lst:sections|2}}
8383 !!result
@@ -85,7 +85,7 @@
8686 !!end
8787
8888 !!test
89 - with an optional /
 89+LST: Substitute a section with an optional /
9090 !!input
9191 {{#lst:sections|3}}
9292 !!result
@@ -94,7 +94,7 @@
9595 !!end
9696
9797 !!test
98 - multiple sections with the same name
 98+LST: multiple sections with the same name
9999 !!input
100100 {{#lst:sections|a}}
101101 !!result
@@ -103,7 +103,7 @@
104104 !!end
105105
106106 !!test
107 -Transclude range of sections
 107+LST: Transclude range of sections
108108 !!input
109109 {{#lst:sections|1|3}}
110110 !!result
@@ -114,7 +114,7 @@
115115 !!end
116116
117117 !!test
118 -Exclude a section
 118+LST: Exclude a section
119119 !!input
120120 {{#lstx:sections|1}}
121121 !!result
@@ -127,7 +127,7 @@
128128 !!end
129129
130130 !!test
131 - Exclude a range
 131+LST: Exclude a range
132132 !!input
133133 {{#lstx:sections|1||3}}
134134 !!result
@@ -138,7 +138,7 @@
139139 !!end
140140
141141 !!test
142 -Replace a section
 142+LST: Replace a section
143143 !!input
144144 {{#lstx:sections|1|one}}
145145 !!result
@@ -151,7 +151,7 @@
152152 !!end
153153
154154 !!test
155 - Replace multiple sections
 155+LST: Replace multiple sections
156156 !!input
157157 {{#lstx:sections|a|A}}
158158 !!result
@@ -164,7 +164,7 @@
165165 !!end
166166
167167 !!test
168 - Replace a range
 168+LST: Replace a range
169169 !!input
170170 {{#lstx:sections|1|test|3}}
171171 !!result
@@ -214,17 +214,18 @@
215215 <section end=outer/>
216216 !!endarticle
217217 !!test
218 -Nested sections - inner section marks removed by tag hook.
 218+LST: Nested sections - inner section marks removed by tag hook.
219219 !!input
220220 {{#lst:nest|outer}}
221221 !!result
222222 <p>I am outer
223223 </p><p>inner
 224+</p><p><br />
224225 </p>
225226 !!end
226227
227228 !!test
228 -red link from nonexistent article
 229+LST: red link from nonexistent article
229230 !!input
230231 {{#lst:no such article|anything}}
231232 !!result
@@ -233,7 +234,7 @@
234235 !!end
235236
236237 # Note: MW <= 1.8 uses a different section header format. In these versions, the header out will look like this instead:
237 -# <div class="editsection" style="float:right;margin-left:5px;">[<a href="https://www.mediawiki.org/index.php?title=Headings&amp;action=edit&amp;section=1" title="Headings">edit</a>]</div><a name="section"></a><h2>section</h2>
 238+# <div class="editsection" style="float:right;margin-left:5px;">[<a href="https://www.mediawiki.org/index.php?title=Headings&amp;action=edit&amp;section=T-1" title="Headings">edit</a>]</div><a name="section"></a><h2>section</h2>
238239 !!article
239240 headings
240241 !!text
@@ -248,21 +249,21 @@
249250 !!endarticle
250251
251252 !!test
252 -Transcluded section headings create edit link to template
 253+LST: Transcluded section headings create edit link to template
253254 !!input
254255 {{#lst:headings|head}}
255256 !!result
256 -<a name="section"></a><h2><span class="editsection">[<a href="https://www.mediawiki.org/index.php?title=Headings&amp;action=edit&amp;section=1" title="Headings">edit</a>]</span> <span class="mw-headline">section</span></h2>
 257+<a name="section"></a><h2><span class="editsection">[<a href="https://www.mediawiki.org/index.php?title=Headings&amp;action=edit&amp;section=T-1" title="Headings">edit</a>]</span> <span class="mw-headline">section</span></h2>
257258 <p>blah
258259 </p>
259260 !!end
260261
261262 !!test
262 -Transcluded section headings create edit link to right section (mw 1.9r18218)
 263+LST: Transcluded section headings create edit link to right section (mw 1.9r18218)
263264 !!input
264265 {{#lst:headings|head2}}
265266 !!result
266 -<a name="section2"></a><h2><span class="editsection">[<a href="https://www.mediawiki.org/index.php?title=Headings&amp;action=edit&amp;section=2" title="Headings">edit</a>]</span> <span class="mw-headline">section2</span></h2>
 267+<a name="section2"></a><h2><span class="editsection">[<a href="https://www.mediawiki.org/index.php?title=Headings&amp;action=edit&amp;section=T-2" title="Headings">edit</a>]</span> <span class="mw-headline">section2</span></h2>
267268 <p>blah
268269 </p>
269270 !!end
@@ -284,16 +285,16 @@
285286 !!input
286287 {{#lst:lst_head_bounds|s}}
287288 !!result
288 -<a name="3"></a><h1><span class="editsection">[<a href="https://www.mediawiki.org/index.php?title=Lst_head_bounds&amp;action=edit&amp;section=3" title="Lst head bounds">edit</a>]</span> <span class="mw-headline">3</span></h1>
 289+<a name="3"></a><h1><span class="editsection">[<a href="https://www.mediawiki.org/index.php?title=Lst_head_bounds&amp;action=edit&amp;section=T-3" title="Lst head bounds">edit</a>]</span> <span class="mw-headline">3</span></h1>
289290 <p>hi
290291 </p>
291 -<a name="4"></a><h1><span class="editsection">[<a href="https://www.mediawiki.org/index.php?title=Lst_head_bounds&amp;action=edit&amp;section=4" title="Lst head bounds">edit</a>]</span> <span class="mw-headline">4</span></h1>
 292+<a name="4"></a><h1><span class="editsection">[<a href="https://www.mediawiki.org/index.php?title=Lst_head_bounds&amp;action=edit&amp;section=T-4" title="Lst head bounds">edit</a>]</span> <span class="mw-headline">4</span></h1>
292293
293294 !!end
294295
295296
296297 !!test
297 -Nick's bogus input
 298+LST: Nick's bogus input
298299 !!input
299300 {{#lstx:</
300301 includeonly>
@@ -351,7 +352,7 @@
352353
353354 #make sure we can still multiply-transclude (that is, path is cleared)
354355 !!test
355 -multiple transclude
 356+LST: multiple transclude
356357 !!input
357358 {{#lst:sections|1}}
358359 {{#lst:sections|1}}
@@ -383,7 +384,7 @@
384385 </p>
385386 !!end
386387 !!test
387 -useless options
 388+LST: useless options
388389 !!input
389390 {{#section:LST-alt|2}}
390391 !!result
Index: trunk/extensions/LabeledSectionTransclusion/lsth.php
@@ -11,6 +11,8 @@
1212 * extension is not enabled, and may not work if the two files are not in
1313 * sync.
1414 *
 15+ * @todo: MW 1.12 version, as per #lst/#lstx
 16+ *
1517 * @addtogroup Extensions
1618 *
1719 * @link http://www.mediawiki.org/wiki/Extension:Labeled_Section_Transclusion Documentation
@@ -43,7 +45,7 @@
4446 ///section inclusion - include all matching sections
4547 function wfLstIncludeHeading($parser, $page='', $sec='', $to='')
4648 {
47 - if (wfLst_text_($parser, $page, $title, $text) == false)
 49+ if (LabeledSectionTransclusion::getTemplateText_($parser, $page, $title, $text) == false)
4850 return $text;
4951
5052 //Generate a regex to match the === classical heading section(s) === we're
@@ -83,7 +85,7 @@
8486 //wfDebug("LSTH:head len is $head_len, pat is $pat, head is '.$m[1][0]'";
8587 }
8688
87 - $nhead = wfLst_count_headings_($text, $begin_off);
 89+ $nhead = LabeledSectionTransclusion::countHeadings_($text, $begin_off);
8890 wfDebug( "LSTH: head offset = $nhead" );
8991
9092 if (isset($end_off))
@@ -91,6 +93,6 @@
9294 else
9395 $result = substr($text, $begin_off);
9496
95 - return wfLst_parse_($parser,$title,$result, "#lsth:${page}|${sec}", $nhead);
 97+ return LabeledSectionTransclusion::parse_($parser,$title,$result, "#lsth:${page}|${sec}", $nhead);
9698 }
9799
Index: trunk/extensions/LabeledSectionTransclusion/lst.php
@@ -20,8 +20,8 @@
2121 # Standard initialisation code
2222 ##
2323
24 -$wgExtensionFunctions[]="wfLabeledSectionTransclusion";
25 -$wgHooks['LanguageGetMagic'][] = 'wfLabeledSectionTransclusionMagic';
 24+$wgExtensionFunctions[] = array( 'LabeledSectionTransclusion', "setup" );
 25+$wgHooks['LanguageGetMagic'][] = 'LabeledSectionTransclusion::setupMagic';
2626
2727 $wgExtensionCredits['parserhook'][] = array(
2828 'name' => 'LabeledSectionTransclusion',
@@ -31,279 +31,500 @@
3232 );
3333 $wgParserTestFiles[] = dirname( __FILE__ ) . "/lstParserTests.txt";
3434
35 -function wfLabeledSectionTransclusion()
36 -{
37 - global $wgParser;
38 -
39 - $wgParser->setHook( 'section', 'wfLstNoop' );
40 - $wgParser->setFunctionHook( 'lst', 'wfLstInclude' );
41 - $wgParser->setFunctionHook( 'lstx', 'wfLstExclude' );
42 -}
 35+// Local settings variable
 36+// Must be set now to avoid injection via register_globals
 37+$wgLstLocal = null;
4338
44 -/// Add the magic words - possibly with more readable aliases
45 -function wfLabeledSectionTransclusionMagic( &$magicWords, $langCode ) {
46 - global $wgParser, $wgLstLocal;
 39+class LabeledSectionTransclusion {
4740
48 - switch( $langCode ) {
49 - case 'de':
50 - $include = 'Abschnitt';
51 - $exclude = 'Abschnitt-x';
52 - $wgLstLocal = array( 'section' => 'Abschnitt', 'begin' => 'Anfang', 'end' => 'Ende') ;
53 - break;
54 - case 'he':
55 - $include = 'קטע';
56 - $exclude = 'בלי קטע';
57 - $wgLstLocal = array( 'section' => 'קטע', 'begin' => 'התחלה', 'end' => 'סוף') ;
58 - break;
59 - case 'pt':
60 - $include = 'trecho';
61 - $exclude = 'trecho-x';
62 - $wgLstLocal = array( 'section' => 'trecho', 'begin' => 'começo', 'end' => 'fim');
 41+ static function setup()
 42+ {
 43+ global $wgParser;
 44+
 45+ $wgParser->setHook( 'section', array( __CLASS__, 'noop' ) );
 46+ if ( defined( get_class( $wgParser ) . '::SFH_OBJECT_ARGS' ) ) {
 47+ $wgParser->setFunctionHook( 'lst', array( __CLASS__, 'pfuncIncludeObj' ), SFH_OBJECT_ARGS );
 48+ $wgParser->setFunctionHook( 'lstx', array( __CLASS__, 'pfuncExcludeObj' ), SFH_OBJECT_ARGS );
 49+ } else {
 50+ $wgParser->setFunctionHook( 'lst', array( __CLASS__, 'pfuncInclude' ) );
 51+ $wgParser->setFunctionHook( 'lstx', array( __CLASS__, 'pfuncExclude' ) );
 52+ }
 53+ }
 54+
 55+ /// Add the magic words - possibly with more readable aliases
 56+ static function setupMagic( &$magicWords, $langCode ) {
 57+ global $wgParser, $wgLstLocal;
 58+
 59+ switch( $langCode ) {
 60+ case 'de':
 61+ $include = 'Abschnitt';
 62+ $exclude = 'Abschnitt-x';
 63+ $wgLstLocal = array( 'section' => 'Abschnitt', 'begin' => 'Anfang', 'end' => 'Ende') ;
6364 break;
 65+ case 'he':
 66+ $include = 'קטע';
 67+ $exclude = 'בלי קטע';
 68+ $wgLstLocal = array( 'section' => 'קטע', 'begin' => 'התחלה', 'end' => 'סוף') ;
 69+ break;
 70+ case 'pt':
 71+ $include = 'trecho';
 72+ $exclude = 'trecho-x';
 73+ $wgLstLocal = array( 'section' => 'trecho', 'begin' => 'começo', 'end' => 'fim');
 74+ break;
 75+ }
 76+
 77+ if( isset( $include ) ) {
 78+ $magicWords['lst'] = array( 0, 'lst', 'section', $include );
 79+ $magicWords['lstx'] = array( 0, 'lstx', 'section-x', $exclude );
 80+ $wgParser->setHook( $include, array( __CLASS__, 'noop' ) );
 81+ } else {
 82+ $magicWords['lst'] = array( 0, 'lst', 'section' );
 83+ $magicWords['lstx'] = array( 0, 'lstx', 'section-x' );
 84+ }
 85+
 86+ return true;
6487 }
65 -
66 - if( isset( $include ) ) {
67 - $magicWords['lst'] = array( 0, 'lst', 'section', $include );
68 - $magicWords['lstx'] = array( 0, 'lstx', 'section-x', $exclude );
69 - $wgParser->setHook( $include, 'wfLstNoop' );
70 - } else {
71 - $magicWords['lst'] = array( 0, 'lst', 'section' );
72 - $magicWords['lstx'] = array( 0, 'lstx', 'section-x' );
 88+
 89+ ##############################################################
 90+ # To do transclusion from an extension, we need to interact with the parser
 91+ # at a low level. This is the general transclusion functionality
 92+ ##############################################################
 93+
 94+ ///Register what we're working on in the parser, so we don't fall into a trap.
 95+ static function open_($parser, $part1)
 96+ {
 97+ // Infinite loop test
 98+ if ( isset( $parser->mTemplatePath[$part1] ) ) {
 99+ wfDebug( __METHOD__.": template loop broken at '$part1'\n" );
 100+ return false;
 101+ } else {
 102+ $parser->mTemplatePath[$part1] = 1;
 103+ return true;
 104+ }
 105+
73106 }
74 -
75 - return true;
76 -}
77107
78 -##############################################################
79 -# To do transclusion from an extension, we need to interact with the parser
80 -# at a low level. This is the general transclusion functionality
81 -##############################################################
 108+ ///Finish processing the function.
 109+ static function close_($parser, $part1)
 110+ {
 111+ // Infinite loop test
 112+ if ( isset( $parser->mTemplatePath[$part1] ) ) {
 113+ unset( $parser->mTemplatePath[$part1] );
 114+ } else {
 115+ wfDebug( __METHOD__.": close unopened template loop at '$part1'\n" );
 116+ }
 117+ }
82118
83 -///Register what we're working on in the parser, so we don't fall into a trap.
84 -function wfLst_open_($parser, $part1)
85 -{
86 - // Infinite loop test
87 - if ( isset( $parser->mTemplatePath[$part1] ) ) {
88 - wfDebug( __METHOD__.": template loop broken at '$part1'\n" );
89 - return false;
90 - } else {
91 - $parser->mTemplatePath[$part1] = 1;
92 - return true;
 119+ /**
 120+ * Handle recursive substitution here, so we can break cycles, and set up
 121+ * return values so that edit sections will resolve correctly.
 122+ * @param Parser $parser
 123+ * @param Title $title of target page
 124+ * @param string $text
 125+ * @param string $part1 Key for cycle detection
 126+ * @param int $skiphead Number of source string headers to skip for numbering
 127+ * @return mixed string or magic array of bits
 128+ * @todo handle mixed-case </section>
 129+ * @private
 130+ */
 131+ static function parse_($parser, $title, $text, $part1, $skiphead=0)
 132+ {
 133+ // if someone tries something like<section begin=blah>lst only</section>
 134+ // text, may as well do the right thing.
 135+ $text = str_replace('</section>', '', $text);
 136+
 137+ if (self::open_($parser, $part1)) {
 138+ //Handle recursion here, so we can break cycles.
 139+ global $wgVersion;
 140+ if( version_compare( $wgVersion, "1.9" ) < 0 ) {
 141+ $text = $parser->replaceVariables($text);
 142+ self::close_($parser, $part1);
 143+ }
 144+
 145+ //Try to get edit sections correct by munging around the parser's guts.
 146+ return array($text, 'title'=>$title, 'replaceHeadings'=>true,
 147+ 'headingOffset'=>$skiphead, 'noparse'=>false, 'noargs'=>false);
 148+ } else {
 149+ return "[[" . $title->getPrefixedText() . "]]".
 150+ "<!-- WARNING: LST loop detected -->";
 151+ }
 152+
93153 }
94 -
95 -}
96154
97 -///Finish processing the function.
98 -function wfLst_close_($parser, $part1)
99 -{
100 - // Infinite loop test
101 - if ( isset( $parser->mTemplatePath[$part1] ) ) {
102 - unset( $parser->mTemplatePath[$part1] );
103 - } else {
104 - wfDebug( __METHOD__.": close unopened template loop at '$part1'\n" );
 155+ ##############################################################
 156+ # And now, the labeled section transclusion
 157+ ##############################################################
 158+
 159+ /**
 160+ * Parser tag hook for <section>.
 161+ * The section markers aren't paired, so we only need to remove them.
 162+ *
 163+ * @param string $in
 164+ * @param array $assocArgs
 165+ * @param Parser $parser
 166+ * @return string HTML output
 167+ */
 168+ static function noop( $in, $assocArgs=array(), $parser=null ) {
 169+ return '';
105170 }
106 -}
107171
108 -/**
109 - * Handle recursive substitution here, so we can break cycles, and set up
110 - * return values so that edit sections will resolve correctly.
111 - * @param Parser $parser
112 - * @param Title $title of target page
113 - * @param string $text
114 - * @param string $part1 Key for cycle detection
115 - * @param int $skiphead Number of source string headers to skip for numbering
116 - * @return mixed string or magic array of bits
117 - * @todo handle mixed-case </section>
118 - * @private
119 - */
120 -function wfLst_parse_($parser, $title, $text, $part1, $skiphead=0)
121 -{
122 - // if someone tries something like<section begin=blah>lst only</section>
123 - // text, may as well do the right thing.
124 - $text = str_replace('</section>', '', $text);
 172+ /**
 173+ * Generate a regex to match the section(s) we're interested in.
 174+ * @param string $sec Name of target section
 175+ * @param string $to Optional name of section to end with, if transcluding
 176+ * multiple sections in sequence. If blank, will assume
 177+ * same section name as started with.
 178+ * @return string regex
 179+ * @private
 180+ */
 181+ static function getPattern_($sec, $to)
 182+ {
 183+ global $wgLstLocal;
125184
126 - if (wfLst_open_($parser, $part1)) {
127 - //Handle recursion here, so we can break cycles.
128 - global $wgVersion;
129 - if( version_compare( $wgVersion, "1.9" ) < 0 ) {
130 - $text = $parser->replaceVariables($text);
131 - wfLst_close_($parser, $part1);
 185+ $beginAttr = self::getAttrPattern_( $sec, 'begin' );
 186+ if ( $to == '' ) {
 187+ $endAttr = self::getAttrPattern_( $sec, 'end' );
 188+ } else {
 189+ $endAttr = self::getAttrPattern_( $to, 'end' );
132190 }
 191+
 192+ $to_sec = ($to == '')?$sec : $to;
 193+ $sec = preg_quote($sec, '/');
 194+ $to_sec = preg_quote($to_sec, '/');
 195+ if (isset($wgLstLocal)){
 196+ $section_re = "(?i:section|$wgLstLocal[section])";
 197+ } else {
 198+ $section_re = "(?i:section)";
 199+ }
133200
134 - //Try to get edit sections correct by munging around the parser's guts.
135 - return array($text, 'title'=>$title, 'replaceHeadings'=>true,
136 - 'headingOffset'=>$skiphead, 'noparse'=>false, 'noargs'=>false);
137 - } else {
138 - return "[[" . $title->getPrefixedText() . "]]".
139 - "<!-- WARNING: LST loop detected -->";
 201+ return "/<$section_re$beginAttr\/?>(.*?)\n?<$section_re$endAttr\/?>/s";
140202 }
141 -
142 -}
143203
144 -##############################################################
145 -# And now, the labeled section transclusion
146 -##############################################################
 204+ /**
 205+ * Generate a regex fragment matching the attribute portion of a section tag
 206+ * @param string $sec Name of the target section
 207+ * @param string $type Either "begin" or "end" depending on the type of section tag to be matched
 208+ */
 209+ static function getAttrPattern_( $sec, $type ) {
 210+ global $wgLstLocal;
 211+ $sec = preg_quote($sec, '/');
 212+ $ws = "(?:\s+[^>]*)?"; //was like $ws="\s*"
 213+ if (isset($wgLstLocal)){
 214+ if ( $type == 'begin' ) {
 215+ $attrName = "(?i:begin|{$wgLstLocal['begin']})";
 216+ } else {
 217+ $attrName = "(?i:end|{$wgLstLocal['end']})";
 218+ }
 219+ } else {
 220+ if ( $type == 'begin' ) {
 221+ $attrName = "(?i:begin)";
 222+ } else {
 223+ $attrName = "(?i:end)";
 224+ }
 225+ }
 226+ return "$ws\s+$attrName=(?:$sec|\"$sec\"|'$sec')$ws";
 227+ }
147228
148 -/**
149 - * Parser tag hook for <section>.
150 - * The section markers aren't paired, so we only need to remove them.
151 - *
152 - * @param string $in
153 - * @param array $assocArgs
154 - * @param Parser $parser
155 - * @return string HTML output
156 - */
157 -function wfLstNoop( $in, $assocArgs=array(), $parser=null ) {
158 - return '';
159 -}
 229+ /**
 230+ * Count headings in skipped text.
 231+ *
 232+ * Count skipped headings, so parser (as of r18218) can skip them, to
 233+ * prevent wrong heading links (see bug 6563).
 234+ *
 235+ * @param string $text
 236+ * @param int $limit Cutoff point in the text to stop searching
 237+ * @return int Number of matches
 238+ * @private
 239+ */
 240+ static function countHeadings_($text,$limit)
 241+ {
 242+ $pat = '^(={1,6}).+\1\s*$()';
 243+
 244+ //return preg_match_all( "/$pat/im", substr($text,0,$limit), $m);
160245
161 -/**
162 - * Generate a regex to match the section(s) we're interested in.
163 - * @param string $sec Name of target section
164 - * @param string $to Optional name of section to end with, if transcluding
165 - * multiple sections in sequence. If blank, will assume
166 - * same section name as started with.
167 - * @return string regex
168 - * @private
169 - */
170 -function wfLst_pat_($sec, $to)
171 -{
172 - global $wgLstLocal;
173 -
174 - $to_sec = ($to == '')?$sec : $to;
175 - $sec = preg_quote($sec, '/');
176 - $to_sec = preg_quote($to_sec, '/');
177 - $ws="(?:\s+[^>]+)?"; //was like $ws="\s*"
178 - if (isset($wgLstLocal)){
179 - $begin="(?i:begin|$wgLstLocal[begin])";
180 - $end="(?i:end|$wgLstLocal[end])";
181 - $section_re = "(?i:section|$wgLstLocal[section])";
182 - } else {
183 - $begin="(?i:begin)";
184 - $end="(?i:end)";
185 - $section_re = "(?i:section)";
 246+ $count = 0;
 247+ $offset = 0;
 248+ while (preg_match("/$pat/im", $text, $m, PREG_OFFSET_CAPTURE, $offset)) {
 249+ if ($m[2][1] > $limit)
 250+ break;
 251+
 252+ $count++;
 253+ $offset = $m[2][1];
 254+ }
 255+
 256+ return $count;
186257 }
187 -
188 - return "/<$section_re$ws\s+$begin=".
189 - "(?:$sec|\"$sec\"|'$sec')".
190 - "$ws\/?>(.*?)\n?<$section_re$ws\s+(?:[^>]+\s+)?$end=".
191 - "(?:$to_sec|\"$to_sec\"|'$to_sec')".
192 - "$ws\/?>/s";
193 -}
194258
195 -/**
196 - * Count headings in skipped text.
197 - *
198 - * Count skipped headings, so parser (as of r18218) can skip them, to
199 - * prevent wrong heading links (see bug 6563).
200 - *
201 - * @param string $text
202 - * @param int $limit Cutoff point in the text to stop searching
203 - * @return int Number of matches
204 - * @private
205 - */
206 -function wfLst_count_headings_($text,$limit)
207 -{
208 - $pat = '^(={1,6}).+\1\s*$()';
209 -
210 - //return preg_match_all( "/$pat/im", substr($text,0,$limit), $m);
 259+ /**
 260+ * Fetches content of target page if valid and found, otherwise
 261+ * produces wikitext of a link to the target page.
 262+ *
 263+ * @param Parser $parser
 264+ * @param string $page title text of target page
 265+ * @param (out) Title $title normalized title object
 266+ * @param (out) string $text wikitext output
 267+ * @return string bool true if returning text, false if target not found
 268+ * @private
 269+ */
 270+ static function getTemplateText_($parser, $page, &$title, &$text)
 271+ {
 272+ $title = Title::newFromText($page);
 273+
 274+ if (is_null($title) ) {
 275+ $text = '';
 276+ return true;
 277+ } else {
 278+ if (method_exists($parser, 'fetchTemplateAndTitle')) {
 279+ list($text,$title) = $parser->fetchTemplateAndTitle($title);
 280+ } else {
 281+ $text = $parser->fetchTemplate($title);
 282+ }
 283+ }
 284+
 285+ //if article doesn't exist, return a red link.
 286+ if ($text == false) {
 287+ $text = "[[" . $title->getPrefixedText() . "]]";
 288+ return false;
 289+ } else {
 290+ return true;
 291+ }
 292+ }
211293
212 - $count = 0;
213 - $offset = 0;
214 - while (preg_match("/$pat/im", $text, $m, PREG_OFFSET_CAPTURE, $offset)) {
215 - if ($m[2][1] > $limit)
216 - break;
 294+ /**
 295+ * Parser function hook for '#lst:'
 296+ * section inclusion - include all matching sections
 297+ *
 298+ * @param Parser $parser
 299+ * @param string $page Title text of target page
 300+ * @param string $sec Named section to transclude
 301+ * @param string $to Optional named section to end at
 302+ * @return mixed wikitext output
 303+ */
 304+ function pfuncInclude($parser, $page='', $sec='', $to='')
 305+ {
 306+ if (self::getTemplateText_($parser, $page, $title, $text) == false)
 307+ return $text;
 308+ $pat = self::getPattern_($sec,$to);
217309
218 - $count++;
219 - $offset = $m[2][1];
 310+ if(preg_match_all( $pat, $text, $m, PREG_OFFSET_CAPTURE)) {
 311+ $headings = self::countHeadings_($text, $m[0][0][1]);
 312+ } else {
 313+ $headings = 0;
 314+ }
 315+
 316+ $text = '';
 317+ foreach ($m[1] as $piece) {
 318+ $text .= $piece[0];
 319+ }
 320+
 321+ //wfDebug(__METHOD__.": skip $headings headings");
 322+ return self::parse_($parser,$title,$text, "#lst:${page}|${sec}", $headings);
220323 }
221324
222 - return $count;
223 -}
 325+ /**
 326+ * Set up some variables for MW-1.12 parser functions
 327+ */
 328+ static function setupPfunc12( $parser, $frame, $args, $func = 'lst' ) {
 329+ global $wgLstLocal;
 330+ if ( !count( $args ) ) {
 331+ return '';
 332+ }
224333
225 -/**
226 - * Fetches content of target page if valid and found, otherwise
227 - * produces wikitext of a link to the target page.
228 - *
229 - * @param Parser $parser
230 - * @param string $page title text of target page
231 - * @param (out) Title $title normalized title object
232 - * @param (out) string $text wikitext output
233 - * @return string bool true if returning text, false if target not found
234 - * @private
235 - */
236 -function wfLst_text_($parser, $page, &$title, &$text)
237 -{
238 - $title = Title::newFromText($page);
239 -
240 - if (is_null($title) ) {
241 - $text = '';
242 - return true;
243 - } else {
244 - if (method_exists($parser, 'fetchTemplateAndTitle')) {
245 - list($text,$title) = $parser->fetchTemplateAndTitle($title);
 334+ $title = Title::newFromText( trim( $frame->expand( array_shift( $args ) ) ) );
 335+ if ( !$title ) {
 336+ return '';
 337+ }
 338+ list( $dom, $finalTitle ) = $parser->getTemplateDom( $title );
 339+
 340+ // if article doesn't exist, return a red link.
 341+ if ($dom === false) {
 342+ return "[[" . $title->getPrefixedText() . "]]";
 343+ }
 344+
 345+ $root = $dom->documentElement;
 346+ $newFrame = $frame->newChild( false, $finalTitle );
 347+ if ( !count( $args ) ) {
 348+ return $newFrame->expand( $root );
 349+ }
 350+
 351+ $begin = trim( $frame->expand( array_shift( $args ) ) );
 352+
 353+ if ( $func == 'lstx' ) {
 354+ if ( !count( $args ) ) {
 355+ $repl = '';
 356+ } else {
 357+ $repl = trim( $frame->expand( array_shift( $args ) ) );
 358+ }
 359+ }
 360+
 361+ if ( !count( $args ) ) {
 362+ $end = $begin;
246363 } else {
247 - $text = $parser->fetchTemplate($title);
 364+ $end = trim( $frame->expand( array_shift( $args ) ) );
248365 }
 366+
 367+ $beginAttr = self::getAttrPattern_( $begin, 'begin' );
 368+ $beginRegex = "/^$beginAttr$/s";
 369+ $endAttr = self::getAttrPattern_( $end, 'end' );
 370+ $endRegex = "/^$endAttr$/s";
 371+
 372+ return compact( 'dom', 'root', 'newFrame', 'repl', 'beginRegex', 'endRegex' );
249373 }
250 -
251 - //if article doesn't exist, return a red link.
252 - if ($text == false) {
253 - $text = "[[" . $title->getPrefixedText() . "]]";
254 - return false;
255 - } else {
256 - return true;
 374+
 375+ /**
 376+ * Returns true if the given extension name is "section"
 377+ */
 378+ static function isSection( $name ) {
 379+ global $wgLstLocal;
 380+ $name = strtolower( $name );
 381+ return $name == 'section'
 382+ || ( isset( $wgLstLocal['section'] ) && $wgLstLocal['section'] == $name );
257383 }
258 -}
259384
260 -/**
261 - * Parser function hook for '#lst:'
262 - * section inclusion - include all matching sections
263 - *
264 - * @param Parser $parser
265 - * @param string $page Title text of target page
266 - * @param string $sec Named section to transclude
267 - * @param string $to Optional named section to end at
268 - * @return mixed wikitext output
269 - */
270 -function wfLstInclude($parser, $page='', $sec='', $to='')
271 -{
272 - if (wfLst_text_($parser, $page, $title, $text) == false)
 385+ /**
 386+ * Returns the text for the inside of a split <section> node
 387+ */
 388+ static function expandSectionNode( $parser, $frame, $parts ) {
 389+ if ( isset( $parts['inner'] ) ) {
 390+ return $parser->replaceVariables( $parts['inner'], $frame );
 391+ } else {
 392+ return '';
 393+ }
 394+ }
 395+
 396+ /**
 397+ * MW 1.12 version of #lst
 398+ */
 399+ static function pfuncIncludeObj( $parser, $frame, $args ) {
 400+ $setup = self::setupPfunc12( $parser, $frame, $args, 'lst' );
 401+ if ( !is_array( $setup ) ) {
 402+ return $setup;
 403+ }
 404+ extract( $setup );
 405+
 406+ $text = '';
 407+ $node = $root->firstChild;
 408+ while ( $node ) {
 409+ // Find the begin node
 410+ $found = false;
 411+ for ( ; $node; $node = $node->nextSibling ) {
 412+ if ( $node->nodeName != 'ext' ) {
 413+ continue;
 414+ }
 415+ $parts = $newFrame->splitExtNode( $node );
 416+ $parts = array_map( array( $newFrame, 'expand' ), $parts );
 417+ if ( self::isSection( $parts['name'] ) ) {
 418+ if ( preg_match( $beginRegex, $parts['attr'] ) ) {
 419+ $found = true;
 420+ break;
 421+ }
 422+ }
 423+ }
 424+ if ( !$found || !$node ) {
 425+ break;
 426+ }
 427+
 428+ // Write the text out while looking for the end node
 429+ $found = false;
 430+ for ( ; $node; $node = $node->nextSibling ) {
 431+ if ( $node->nodeName === 'ext' ) {
 432+ $parts = $newFrame->splitExtNode( $node );
 433+ $parts = array_map( array( $newFrame, 'expand' ), $parts );
 434+ if ( self::isSection( $parts['name'] ) ) {
 435+ if ( preg_match( $endRegex, $parts['attr'] ) ) {
 436+ $found = true;
 437+ break;
 438+ }
 439+ $text .= self::expandSectionNode( $parser, $newFrame, $parts );
 440+ } else {
 441+ $text .= $newFrame->expand( $node );
 442+ }
 443+ } else {
 444+ $text .= $newFrame->expand( $node );
 445+ }
 446+ }
 447+ if ( !$found ) {
 448+ break;
 449+ }
 450+ $node = $node->nextSibling;
 451+ }
273452 return $text;
274 - $pat = wfLst_pat_($sec,$to);
 453+ }
275454
276 - if(preg_match_all( $pat, $text, $m, PREG_OFFSET_CAPTURE)) {
277 - $headings = wfLst_count_headings_($text, $m[0][0][1]);
278 - } else {
279 - $headings = 0;
 455+ /**
 456+ * Parser function hook for '#lstx:'
 457+ * section exclusion, with optional replacement
 458+ *
 459+ * @param Parser $parser
 460+ * @param string $page Title text of target page
 461+ * @param string $sec Named section to transclude
 462+ * @param string $repl Optional wikitext to use to fill in the excluded section
 463+ * @param string $to Optional named section to end at
 464+ * @return mixed wikitext output
 465+ */
 466+ static function pfuncExclude($parser, $page='', $sec='', $repl='',$to='')
 467+ {
 468+ if (self::getTemplateText_($parser, $page, $title, $text) == false)
 469+ return $text;
 470+ $pat = self::getPattern_($sec,$to);
 471+ $text = preg_replace( $pat, $repl, $text);
 472+ return self::parse_($parser,$title,$text, "#lstx:$page|$sec");
280473 }
281 -
282 - $text = '';
283 - foreach ($m[1] as $piece) {
284 - $text .= $piece[0];
285 - }
286474
287 - //wfDebug("wfLstInclude: skip $headings headings");
288 - return wfLst_parse_($parser,$title,$text, "#lst:${page}|${sec}", $headings);
289 -}
 475+ /**
 476+ * MW 1.12 version of #lstx
 477+ */
 478+ static function pfuncExcludeObj( $parser, $frame, $args ) {
 479+ $setup = self::setupPfunc12( $parser, $frame, $args, 'lstx' );
 480+ if ( !is_array( $setup ) ) {
 481+ return $setup;
 482+ }
 483+ extract( $setup );
290484
291 -/**
292 - * Parser function hook for '#lstx:'
293 - * section exclusion, with optional replacement
294 - *
295 - * @param Parser $parser
296 - * @param string $page Title text of target page
297 - * @param string $sec Named section to transclude
298 - * @param string $repl Optional wikitext to use to fill in the excluded section
299 - * @param string $to Optional named section to end at
300 - * @return mixed wikitext output
301 - */
302 -function wfLstExclude($parser, $page='', $sec='', $repl='',$to='')
303 -{
304 - if (wfLst_text_($parser, $page, $title, $text) == false)
 485+ $text = '';
 486+ for ( $node = $root->firstChild; $node; $node = $node ? $node->nextSibling : false ) {
 487+ // Search for the start tag
 488+ $found = false;
 489+ for ( ; $node; $node = $node->nextSibling ) {
 490+ if ( $node->nodeName == 'ext' ) {
 491+ $parts = $newFrame->splitExtNode( $node );
 492+ $parts = array_map( array( $newFrame, 'expand' ), $parts );
 493+ if ( self::isSection( $parts['name'] ) ) {
 494+ if ( preg_match( $beginRegex, $parts['attr'] ) ) {
 495+ $found = true;
 496+ break;
 497+ }
 498+ $text .= self::expandSectionNode( $parser, $newFrame, $parts );
 499+ } else {
 500+ $text .= $newFrame->expand( $node );
 501+ }
 502+ } else {
 503+ $text .= $newFrame->expand( $node );
 504+ }
 505+ }
 506+
 507+ if ( !$found ) {
 508+ break;
 509+ }
 510+
 511+ // Append replacement text
 512+ $text .= $repl;
 513+
 514+ // Search for the end tag
 515+ for ( ; $node; $node = $node->nextSibling ) {
 516+ if ( $node->nodeName == 'ext' ) {
 517+ $parts = $newFrame->splitExtNode( $node );
 518+ $parts = array_map( array( $newFrame, 'expand' ), $parts );
 519+ if ( self::isSection( $parts['name'] ) ) {
 520+ if ( preg_match( $endRegex, $parts['attr'] ) ) {
 521+ $text .= self::expandSectionNode( $parser, $newFrame, $parts );
 522+ break;
 523+ }
 524+ }
 525+ }
 526+ }
 527+ }
305528 return $text;
306 - $pat = wfLst_pat_($sec,$to);
307 - $text = preg_replace( $pat, $repl, $text);
308 - return wfLst_parse_($parser,$title,$text, "#lstx:$page|$sec");
 529+ }
309530 }
310 -
 531+# vim: sw=2 sts=2 et :

Status & tagging log