~ubuntu-branches/ubuntu/saucy/mediawiki-extensions/saucy

« back to all changes in this revision

Viewing changes to dist/mediawiki-extensions-base/usr/share/mediawiki-extensions/base/Cite/Cite_body.php

  • Committer: Bazaar Package Importer
  • Author(s): Romain Beauxis
  • Date: 2010-05-04 15:13:35 UTC
  • mfrom: (0.1.1 experimental)
  • Revision ID: james.westby@ubuntu.com-20100504151335-54qeucg3ec108q28
Tags: 2.2
* Added Replaces:/Conflicts: to allow a proper upgrade.
Closes: #580066
* Fixed package descriptions.
Closes: #579667
* Patched mediawiki-extensions-fckeditor to make it work with
  php 5.3. The fix may not be perfect but at least it work.
  Not closing the bug (#579822) for now..

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
/**#@+
 
4
 * A parser extension that adds two tags, <ref> and <references> for adding
 
5
 * citations to pages
 
6
 *
 
7
 * @addtogroup Extensions
 
8
 *
 
9
 * @link http://meta.wikimedia.org/wiki/Cite/Cite.php Documentation
 
10
 * @link http://www.w3.org/TR/html4/struct/text.html#edef-CITE <cite> definition in HTML
 
11
 * @link http://www.w3.org/TR/2005/WD-xhtml2-20050527/mod-text.html#edef_text_cite <cite> definition in XHTML 2.0
 
12
 *
 
13
 * @bug 4579
 
14
 *
 
15
 * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
 
16
 * @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
 
17
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 
18
 */
 
19
 
 
20
class Cite {
 
21
        /**#@+
 
22
         * @access private
 
23
         */
 
24
        
 
25
        /**
 
26
         * Datastructure representing <ref> input, in the format of:
 
27
         * <code>
 
28
         * array(
 
29
         *      'user supplied' => array(
 
30
         *              'text' => 'user supplied reference & key',
 
31
         *              'count' => 1, // occurs twice
 
32
         *              'number' => 1, // The first reference, we want
 
33
         *                             // all occourances of it to
 
34
         *                             // use the same number
 
35
         *      ),
 
36
         *      0 => 'Anonymous reference',
 
37
         *      1 => 'Another anonymous reference',
 
38
         *      'some key' => array(
 
39
         *              'text' => 'this one occurs once'
 
40
         *              'count' => 0,
 
41
         *              'number' => 4
 
42
         *      ),
 
43
         *      3 => 'more stuff'
 
44
         * );
 
45
         * </code>
 
46
         *
 
47
         * This works because:
 
48
         * * PHP's datastructures are guarenteed to be returned in the
 
49
         *   order that things are inserted into them (unless you mess
 
50
         *   with that)
 
51
         * * User supplied keys can't be integers, therefore avoiding
 
52
         *   conflict with anonymous keys
 
53
         *
 
54
         * @var array
 
55
         **/
 
56
        var $mRefs = array();
 
57
        
 
58
        /**
 
59
         * Count for user displayed output (ref[1], ref[2], ...)
 
60
         *
 
61
         * @var int
 
62
         */
 
63
        var $mOutCnt = 0;
 
64
        var $mGroupCnt = array();
 
65
 
 
66
        /**
 
67
         * Internal counter for anonymous references, separate from
 
68
         * $mOutCnt because anonymous references won't increment it,
 
69
         * but will incremement $mOutCnt
 
70
         *
 
71
         * @var int
 
72
         */
 
73
        var $mInCnt = 0;
 
74
 
 
75
        /**
 
76
         * The backlinks, in order, to pass as $3 to
 
77
         * 'cite_references_link_many_format', defined in
 
78
         * 'cite_references_link_many_format_backlink_labels
 
79
         *
 
80
         * @var array
 
81
         */
 
82
        var $mBacklinkLabels;
 
83
        
 
84
        /**
 
85
         * @var object
 
86
         */
 
87
        var $mParser;
 
88
        
 
89
        /**
 
90
         * True when a <ref> or <references> tag is being processed.
 
91
         * Used to avoid infinite recursion
 
92
         * 
 
93
         * @var boolean
 
94
         */
 
95
        var $mInCite = false;
 
96
        
 
97
        /**#@-*/
 
98
 
 
99
        /**
 
100
         * Constructor
 
101
         */
 
102
        function Cite() {
 
103
                $this->setHooks();
 
104
        }
 
105
 
 
106
        /**#@+ @access private */
 
107
 
 
108
        /**
 
109
         * Callback function for <ref>
 
110
         *
 
111
         * @param string $str Input
 
112
         * @param array $argv Arguments
 
113
         * @return string
 
114
         */
 
115
        function ref( $str, $argv, $parser ) {
 
116
                wfLoadExtensionMessages( 'Cite' );
 
117
                if ( $this->mInCite ) {
 
118
                        return htmlspecialchars( "<ref>$str</ref>" );
 
119
                } else {
 
120
                        $this->mInCite = true;
 
121
                        $ret = $this->guardedRef( $str, $argv, $parser );
 
122
                        $this->mInCite = false;
 
123
                        return $ret;
 
124
                }
 
125
        }
 
126
 
 
127
        function guardedRef( $str, $argv, $parser, $default_group=CITE_DEFAULT_GROUP ) {
 
128
                $this->mParser = $parser;
 
129
                
 
130
                # The key here is the "name" attribute.
 
131
                list($key,$group) = $this->refArg( $argv );
 
132
                
 
133
                if( $str === '' ) {
 
134
                        # <ref ...></ref>.  This construct is  invalid if
 
135
                        # it's a contentful ref, but OK if it's a named duplicate and should
 
136
                        # be equivalent <ref ... />, for compatability with #tag.
 
137
                        if ( $key == false )
 
138
                                return $this->error( 'cite_error_ref_no_input' );
 
139
                        else
 
140
                                $str = null;
 
141
                }
 
142
                                
 
143
                if( $key === false ) {
 
144
                        # TODO: Comment this case; what does this condition mean?
 
145
                        return $this->error( 'cite_error_ref_too_many_keys' );
 
146
                }
 
147
 
 
148
                if( $str === null and $key === null ) {
 
149
                        # Something like <ref />; this makes no sense.
 
150
                        return $this->error( 'cite_error_ref_no_key' );
 
151
                }
 
152
                
 
153
                if( preg_match( '/^[0-9]+$/', $key ) ) {
 
154
                        # Numeric names mess up the resulting id's, potentially produ-
 
155
                        # cing duplicate id's in the XHTML.  The Right Thing To Do
 
156
                        # would be to mangle them, but it's not really high-priority
 
157
                        # (and would produce weird id's anyway).
 
158
                        return $this->error( 'cite_error_ref_numeric_key' );
 
159
                }
 
160
 
 
161
                if( preg_match(
 
162
                        '/<ref\b[^<]*?>/',
 
163
                        preg_replace( '#<([^ ]+?).*?>.*?</\\1 *>|<!--.*?-->#', '', $str )
 
164
                ) ) {
 
165
                        # (bug 6199) This most likely implies that someone left off the
 
166
                        # closing </ref> tag, which will cause the entire article to be
 
167
                        # eaten up until the next <ref>.  So we bail out early instead.
 
168
                        # The fancy regex above first tries chopping out anything that
 
169
                        # looks like a comment or SGML tag, which is a crude way to avoid
 
170
                        # false alarms for <nowiki>, <pre>, etc.
 
171
                        #
 
172
                        # Possible improvement: print the warning, followed by the contents
 
173
                        # of the <ref> tag.  This way no part of the article will be eaten
 
174
                        # even temporarily.
 
175
                        return $this->error( 'cite_error_included_ref' );
 
176
                }
 
177
 
 
178
                # Split these into groups.
 
179
                if( $group === null ) {
 
180
                        $group = $default_group;
 
181
                }
 
182
                
 
183
                if( is_string( $key ) or is_string( $str ) ) {
 
184
                        # We don't care about the content: if the key exists, the ref
 
185
                        # is presumptively valid.  Either it stores a new ref, or re-
 
186
                        # fers to an existing one.  If it refers to a nonexistent ref,
 
187
                        # we'll figure that out later.  Likewise it's definitely valid
 
188
                        # if there's any content, regardless of key.
 
189
                        return $this->stack( $str, $key, $group );
 
190
                }
 
191
 
 
192
                # Not clear how we could get here, but something is probably
 
193
                # wrong with the types.  Let's fail fast.
 
194
                $this->croak( 'cite_error_key_str_invalid', serialize( "$str; $key" ) );
 
195
        }
 
196
 
 
197
        /**
 
198
         * Parse the arguments to the <ref> tag
 
199
         *
 
200
         * @static
 
201
         *
 
202
         * @param array $argv The argument vector
 
203
         * @return mixed false on invalid input, a string on valid
 
204
         *               input and null on no input
 
205
         */
 
206
        function refArg( $argv ) {
 
207
                global $wgAllowCiteGroups;
 
208
                $cnt = count( $argv );
 
209
                $group = null;
 
210
                $key = null;
 
211
 
 
212
                if ( $cnt > 2 )
 
213
                        // There should only be one key and one group
 
214
                        return false;
 
215
                else if ( $cnt >= 1 ) {
 
216
                        if ( isset( $argv['name'] ) ) {
 
217
                                // Key given.
 
218
                                $key = Sanitizer::escapeId( $argv['name'], 'noninitial' );
 
219
                                unset( $argv['name']);
 
220
                                --$cnt;
 
221
                        }
 
222
                        if ( isset( $argv['group'] ) ){
 
223
                                if (! $wgAllowCiteGroups ) return array(false); //remove when groups are fully tested.
 
224
                                // Group given.
 
225
                                $group = $argv['group'];
 
226
                                unset( $argv['group']);
 
227
                                --$cnt;
 
228
                        }
 
229
 
 
230
                        if ( $cnt == 0)
 
231
                                return array ($key,$group);
 
232
                        else
 
233
                                // Invalid key
 
234
                                return array(false,false);
 
235
                }
 
236
                else
 
237
                        // No key
 
238
                        return array(null,$group);
 
239
        }
 
240
 
 
241
        /**
 
242
         * Populate $this->mRefs based on input and arguments to <ref>
 
243
         *
 
244
         * @param string $str Input from the <ref> tag
 
245
         * @param mixed $key Argument to the <ref> tag as returned by $this->refArg()
 
246
         * @return string 
 
247
         */
 
248
        function stack( $str, $key = null, $group ) {
 
249
                if (! isset($this->mRefs[$group])) 
 
250
                        $this->mRefs[$group]=array();
 
251
                if (! isset($this->mGroupCnt[$group]))
 
252
                        $this->mGroupCnt[$group]=0;
 
253
 
 
254
                if ( $key === null ) {
 
255
                        // No key
 
256
                        //$this->mRefs[$group][] = $str;
 
257
                        $this->mRefs[$group][] = array('count'=>-1, 'text'=>$str, 'key'=>++$this->mOutCnt);
 
258
 
 
259
                        return $this->linkRef( $group, $this->mInCnt++ );
 
260
                } else if ( is_string( $key ) ) {
 
261
                        // Valid key
 
262
                        if ( ! isset( $this->mRefs[$group][$key] ) || ! is_array( $this->mRefs[$group][$key] ) ) {
 
263
                                // First occurance
 
264
                                $this->mRefs[$group][$key] = array(
 
265
                                        'text' => $str,
 
266
                                        'count' => 0,
 
267
                                        'key' => ++$this->mOutCnt,
 
268
                                        'number' => ++$this->mGroupCnt[$group]
 
269
                                );
 
270
                                $this->mInCnt++;
 
271
                                return
 
272
                                        $this->linkRef(
 
273
                                                $group,
 
274
                                                $key,
 
275
                                                $this->mRefs[$group][$key]['key']."-".$this->mRefs[$group][$key]['count'],
 
276
                                                $this->mRefs[$group][$key]['number'],
 
277
                                                "-".$this->mRefs[$group][$key]['key']
 
278
                                        );
 
279
                        } else {
 
280
                                // We've been here before
 
281
                                if ( $this->mRefs[$group][$key]['text'] === null && $str !== '' ) {
 
282
                                        // If no text found before, use this text
 
283
                                        $this->mRefs[$group][$key]['text'] = $str;
 
284
                                };
 
285
                                return 
 
286
                                        $this->linkRef(
 
287
                                                $group,
 
288
                                                $key,
 
289
                                                $this->mRefs[$group][$key]['key']."-".++$this->mRefs[$group][$key]['count'],
 
290
                                                $this->mRefs[$group][$key]['number'],
 
291
                                                "-".$this->mRefs[$group][$key]['key']
 
292
                                        ); }
 
293
                }
 
294
 
 
295
                else
 
296
                        $this->croak( 'cite_error_stack_invalid_input', serialize( array( $key, $str ) ) );
 
297
        }
 
298
        
 
299
        /**
 
300
         * Callback function for <references>
 
301
         *
 
302
         * @param string $str Input
 
303
         * @param array $argv Arguments
 
304
         * @return string
 
305
         */
 
306
        function references( $str, $argv, $parser ) {
 
307
                wfLoadExtensionMessages( 'Cite' );
 
308
                if ( $this->mInCite ) {
 
309
                        if ( is_null( $str ) ) {
 
310
                                return htmlspecialchars( "<references/>" );
 
311
                        } else {
 
312
                                return htmlspecialchars( "<references>$str</references>" );
 
313
                        }
 
314
                } else {
 
315
                        $this->mInCite = true;
 
316
                        $ret = $this->guardedReferences( $str, $argv, $parser );
 
317
                        $this->mInCite = false;
 
318
                        return $ret;
 
319
                }
 
320
        }
 
321
 
 
322
        function guardedReferences( $str, $argv, $parser, $group = CITE_DEFAULT_GROUP ) {
 
323
                global $wgAllowCiteGroups;
 
324
 
 
325
                $this->mParser = $parser;
 
326
                
 
327
                if ( strval( $str ) !== '' )
 
328
                        return $this->error( 'cite_error_references_invalid_input' );
 
329
 
 
330
                
 
331
                if ( isset( $argv['group'] ) and $wgAllowCiteGroups) {
 
332
                        $group = $argv['group'];
 
333
                        unset ($argv['group']);
 
334
                        
 
335
                }
 
336
                
 
337
                if ( count( $argv ) && $wgAllowCiteGroups )
 
338
                        return $this->error( 'cite_error_references_invalid_parameters_group' );
 
339
                elseif ( count( $argv ) )
 
340
                        return $this->error( 'cite_error_references_invalid_parameters' );
 
341
                else
 
342
                        return $this->referencesFormat($group);
 
343
        }
 
344
 
 
345
        /**
 
346
         * Make output to be returned from the references() function
 
347
         *
 
348
         * @return string XHTML ready for output
 
349
         */
 
350
        function referencesFormat($group) {
 
351
                if (( count( $this->mRefs ) == 0 ) or (empty( $this->mRefs[$group] ) ))
 
352
                        return '';
 
353
                
 
354
                wfProfileIn( __METHOD__ );
 
355
                wfProfileIn( __METHOD__ .'-entries' );
 
356
                $ent = array();
 
357
                foreach ( $this->mRefs[$group] as $k => $v )
 
358
                        $ent[] = $this->referencesFormatEntry( $k, $v );
 
359
                
 
360
                $prefix = wfMsgForContentNoTrans( 'cite_references_prefix' );
 
361
                $suffix = wfMsgForContentNoTrans( 'cite_references_suffix' );
 
362
                $content = implode( "\n", $ent );
 
363
 
 
364
                // Let's try to cache it.
 
365
                $parserInput = $prefix . $content . $suffix;
 
366
                global $wgMemc;
 
367
                $cacheKey = wfMemcKey( 'citeref', md5($parserInput), $this->mParser->Title()->getArticleID() );
 
368
 
 
369
                wfProfileOut( __METHOD__ .'-entries' );
 
370
                
 
371
                global $wgCiteCacheReferences;
 
372
                if ( $wgCiteCacheReferences ) {
 
373
                        wfProfileIn( __METHOD__.'-cache-get' );
 
374
                        $data = $wgMemc->get( $cacheKey );
 
375
                        wfProfileOut( __METHOD__.'-cache-get' );
 
376
                }
 
377
                
 
378
                if ( empty($data) ) {
 
379
                        wfProfileIn( __METHOD__ .'-parse' );
 
380
                        
 
381
                        // Live hack: parse() adds two newlines on WM, can't reproduce it locally -ævar
 
382
                        $ret = rtrim( $this->parse( $parserInput ), "\n" );
 
383
                        
 
384
                        if ( $wgCiteCacheReferences ) {
 
385
                                $serData = $this->mParser->serialiseHalfParsedText( $ret );
 
386
                                $wgMemc->set( $cacheKey, $serData, 86400 );
 
387
                        }
 
388
                        
 
389
                        wfProfileOut( __METHOD__ .'-parse' );
 
390
                } else {
 
391
                        $ret = $this->mParser->unserialiseHalfParsedText( $data );
 
392
                }
 
393
 
 
394
                wfProfileOut( __METHOD__ );
 
395
                
 
396
                //done, clean up so we can reuse the group
 
397
                unset ($this->mRefs[$group]);
 
398
                unset($this->mGroupCnt[$group]);
 
399
                        
 
400
                return $ret;
 
401
        }
 
402
 
 
403
        /**
 
404
         * Format a single entry for the referencesFormat() function
 
405
         *
 
406
         * @param string $key The key of the reference
 
407
         * @param mixed $val The value of the reference, string for anonymous
 
408
         *                   references, array for user-suppplied
 
409
         * @return string Wikitext
 
410
         */
 
411
        function referencesFormatEntry( $key, $val ) {
 
412
                // Anonymous reference
 
413
                if ( ! is_array( $val ) )
 
414
                        return
 
415
                                wfMsgForContentNoTrans(
 
416
                                        'cite_references_link_one',
 
417
                                        $this->referencesKey( $key ),
 
418
                                        $this->refKey( $key ),
 
419
                                        $val
 
420
                                );
 
421
                else if ($val['text']=='') return
 
422
                                wfMsgForContentNoTrans(
 
423
                                        'cite_references_link_one',
 
424
                                        $this->referencesKey( $key ),
 
425
                                        $this->refKey( $key, $val['count'] ),
 
426
                                        $this->error( 'cite_error_references_no_text', $key )
 
427
                                );
 
428
                if ( $val['count'] < 0 )
 
429
                        return
 
430
                                wfMsgForContentNoTrans(
 
431
                                        'cite_references_link_one',
 
432
                                        $this->referencesKey( $val['key'] ),
 
433
                                        #$this->refKey( $val['key'], $val['count'] ),
 
434
                                        $this->refKey( $val['key'] ),
 
435
 
 
436
                                        ( $val['text'] != '' ? $val['text'] : $this->error( 'cite_error_references_no_text', $key ) )                                           
 
437
                                );
 
438
                // Standalone named reference, I want to format this like an
 
439
                // anonymous reference because displaying "1. 1.1 Ref text" is
 
440
                // overkill and users frequently use named references when they
 
441
                // don't need them for convenience
 
442
                else if ( $val['count'] === 0 )
 
443
                        return
 
444
                                wfMsgForContentNoTrans(
 
445
                                        'cite_references_link_one',
 
446
                                        $this->referencesKey( $key ."-" . $val['key'] ),
 
447
                                        #$this->refKey( $key, $val['count'] ),
 
448
                                        $this->refKey( $key, $val['key']."-".$val['count'] ),
 
449
                                        ( $val['text'] != '' ? $val['text'] : $this->error( 'cite_error_references_no_text', $key ) )
 
450
                                );
 
451
                // Named references with >1 occurrences
 
452
                else {
 
453
                        $links = array();
 
454
//for group handling, we have an extra key here.
 
455
                        for ( $i = 0; $i <= $val['count']; ++$i ) {
 
456
                                $links[] = wfMsgForContentNoTrans(
 
457
                                                'cite_references_link_many_format',
 
458
                                                $this->refKey( $key, $val['key']."-$i" ),
 
459
                                                $this->referencesFormatEntryNumericBacklinkLabel( $val['number'], $i, $val['count'] ),
 
460
                                                $this->referencesFormatEntryAlternateBacklinkLabel( $i )
 
461
                                );
 
462
                        }
 
463
 
 
464
                        $list = $this->listToText( $links );
 
465
 
 
466
                        return
 
467
                                wfMsgForContentNoTrans( 'cite_references_link_many',
 
468
                                        $this->referencesKey( $key ."-" . $val['key'] ),
 
469
                                        $list,
 
470
                                        ( $val['text'] != '' ? $val['text'] : $this->error( 'cite_error_references_no_text', $key ) )
 
471
                                );
 
472
                }
 
473
        }
 
474
 
 
475
        /**
 
476
         * Generate a numeric backlink given a base number and an
 
477
         * offset, e.g. $base = 1, $offset = 2; = 1.2
 
478
         * Since bug #5525, it correctly does 1.9 -> 1.10 as well as 1.099 -> 1.100
 
479
         *
 
480
         * @static
 
481
         *
 
482
         * @param int $base The base
 
483
         * @param int $offset The offset
 
484
         * @param int $max Maximum value expected.
 
485
         * @return string
 
486
         */
 
487
        function referencesFormatEntryNumericBacklinkLabel( $base, $offset, $max ) {
 
488
                global $wgContLang;
 
489
                $scope = strlen( $max );
 
490
                $ret = $wgContLang->formatNum(
 
491
                        sprintf("%s.%0{$scope}s", $base, $offset)
 
492
                );
 
493
                return $ret;
 
494
        }
 
495
 
 
496
        /**
 
497
         * Generate a custom format backlink given an offset, e.g.
 
498
         * $offset = 2; = c if $this->mBacklinkLabels = array( 'a',
 
499
         * 'b', 'c', ...). Return an error if the offset > the # of
 
500
         * array items
 
501
         *
 
502
         * @param int $offset The offset
 
503
         *
 
504
         * @return string
 
505
         */
 
506
        function referencesFormatEntryAlternateBacklinkLabel( $offset ) {
 
507
                if ( !isset( $this->mBacklinkLabels ) ) {
 
508
                        $this->genBacklinkLabels();
 
509
                }
 
510
                if ( isset( $this->mBacklinkLabels[$offset] ) ) {
 
511
                        return $this->mBacklinkLabels[$offset];
 
512
                } else {
 
513
                        // Feed me!
 
514
                        return $this->error( 'cite_error_references_no_backlink_label' );
 
515
                }
 
516
        }
 
517
 
 
518
        /**
 
519
         * Return an id for use in wikitext output based on a key and
 
520
         * optionally the number of it, used in <references>, not <ref>
 
521
         * (since otherwise it would link to itself)
 
522
         *
 
523
         * @static
 
524
         *
 
525
         * @param string $key The key
 
526
         * @param int $num The number of the key
 
527
         * @return string A key for use in wikitext
 
528
         */
 
529
        function refKey( $key, $num = null ) {
 
530
                $prefix = wfMsgForContent( 'cite_reference_link_prefix' );
 
531
                $suffix = wfMsgForContent( 'cite_reference_link_suffix' );
 
532
                if ( isset( $num ) )
 
533
                        $key = wfMsgForContentNoTrans( 'cite_reference_link_key_with_num', $key, $num );
 
534
                
 
535
                return $prefix . $key . $suffix;
 
536
        }
 
537
 
 
538
        /**
 
539
         * Return an id for use in wikitext output based on a key and
 
540
         * optionally the number of it, used in <ref>, not <references>
 
541
         * (since otherwise it would link to itself)
 
542
         *
 
543
         * @static
 
544
         *
 
545
         * @param string $key The key
 
546
         * @param int $num The number of the key
 
547
         * @return string A key for use in wikitext
 
548
         */
 
549
        function referencesKey( $key, $num = null ) {
 
550
                $prefix = wfMsgForContent( 'cite_references_link_prefix' );
 
551
                $suffix = wfMsgForContent( 'cite_references_link_suffix' );
 
552
                if ( isset( $num ) )
 
553
                        $key = wfMsgForContentNoTrans( 'cite_reference_link_key_with_num', $key, $num );
 
554
                
 
555
                return $prefix . $key . $suffix;
 
556
        }
 
557
 
 
558
        /**
 
559
         * Generate a link (<sup ...) for the <ref> element from a key
 
560
         * and return XHTML ready for output
 
561
         *
 
562
         * @param string $key The key for the link
 
563
         * @param int $count The index of the key, used for distinguishing
 
564
         *                   multiple occurances of the same key
 
565
         * @param int $label The label to use for the link, I want to
 
566
         *                   use the same label for all occourances of
 
567
         *                   the same named reference.
 
568
         * @return string
 
569
         */
 
570
        function linkRef( $group, $key, $count = null, $label = null, $subkey = '' ) {
 
571
                global $wgContLang;
 
572
                return
 
573
                        $this->parse(
 
574
                                wfMsgForContentNoTrans(
 
575
                                        'cite_reference_link',
 
576
                                        $this->refKey( $key, $count ),
 
577
                                        $this->referencesKey( $key . $subkey ),
 
578
                                        (($group == CITE_DEFAULT_GROUP)?'':"$group ").$wgContLang->formatNum( is_null( $label ) ? ++$this->mGroupCnt[$group] : $label )
 
579
                                )
 
580
                        );
 
581
        }
 
582
 
 
583
        /**
 
584
         * This does approximately the same thing as
 
585
         * Language::listToText() but due to this being used for a
 
586
         * slightly different purpose (people might not want , as the
 
587
         * first separator and not 'and' as the second, and this has to
 
588
         * use messages from the content language) I'm rolling my own.
 
589
         *
 
590
         * @static
 
591
         *
 
592
         * @param array $arr The array to format
 
593
         * @return string
 
594
         */
 
595
        function listToText( $arr ) {
 
596
                $cnt = count( $arr );
 
597
 
 
598
                $sep = wfMsgForContentNoTrans( 'cite_references_link_many_sep' );
 
599
                $and = wfMsgForContentNoTrans( 'cite_references_link_many_and' );
 
600
 
 
601
                if ( $cnt == 1 )
 
602
                        // Enforce always returning a string
 
603
                        return (string)$arr[0];
 
604
                else {
 
605
                        $t = array_slice( $arr, 0, $cnt - 1 );
 
606
                        return implode( $sep, $t ) . $and . $arr[$cnt - 1];
 
607
                }
 
608
        }
 
609
 
 
610
        /**
 
611
         * Parse a given fragment and fix up Tidy's trail of blood on
 
612
         * it...
 
613
         *
 
614
         * @param string $in The text to parse
 
615
         * @return string The parsed text
 
616
         */
 
617
        function parse( $in ) {
 
618
                if ( method_exists( $this->mParser, 'recursiveTagParse' ) ) {
 
619
                        // New fast method
 
620
                        return $this->mParser->recursiveTagParse( $in );
 
621
                } else {
 
622
                        // Old method
 
623
                        $ret = $this->mParser->parse(
 
624
                                $in,
 
625
                                $this->mParser->mTitle,
 
626
                                $this->mParser->mOptions,
 
627
                                // Avoid whitespace buildup
 
628
                                false,
 
629
                                // Important, otherwise $this->clearState()
 
630
                                // would get run every time <ref> or
 
631
                                // <references> is called, fucking the whole
 
632
                                // thing up.
 
633
                                false
 
634
                        );
 
635
                        $text = $ret->getText();
 
636
                        
 
637
                        return $this->fixTidy( $text );
 
638
                }
 
639
        }
 
640
 
 
641
        /**
 
642
         * Tidy treats all input as a block, it will e.g. wrap most
 
643
         * input in <p> if it isn't already, fix that and return the fixed text
 
644
         *
 
645
         * @static
 
646
         *
 
647
         * @param string $text The text to fix
 
648
         * @return string The fixed text
 
649
         */
 
650
        function fixTidy( $text ) {
 
651
                global $wgUseTidy;
 
652
 
 
653
                if ( ! $wgUseTidy )
 
654
                        return $text;
 
655
                else {
 
656
                        $text = preg_replace( '~^<p>\s*~', '', $text );
 
657
                        $text = preg_replace( '~\s*</p>\s*~', '', $text );
 
658
                        $text = preg_replace( '~\n$~', '', $text );
 
659
                        
 
660
                        return $text;
 
661
                }
 
662
        }
 
663
 
 
664
        /**
 
665
         * Generate the labels to pass to the
 
666
         * 'cite_references_link_many_format' message, the format is an
 
667
         * arbitary number of tokens separated by [\t\n ]
 
668
         */
 
669
        function genBacklinkLabels() {
 
670
                wfProfileIn( __METHOD__ );
 
671
                $text = wfMsgForContentNoTrans( 'cite_references_link_many_format_backlink_labels' );
 
672
                $this->mBacklinkLabels = preg_split( '#[\n\t ]#', $text );
 
673
                wfProfileOut( __METHOD__ );
 
674
        }
 
675
 
 
676
        /**
 
677
         * Gets run when Parser::clearState() gets run, since we don't
 
678
         * want the counts to transcend pages and other instances
 
679
         */
 
680
        function clearState() {
 
681
                # Don't clear state when we're in the middle of parsing
 
682
                # a <ref> tag
 
683
                if($this->mInCite)
 
684
                        return true;
 
685
 
 
686
                $this->mGroupCnt = array();
 
687
                $this->mOutCnt = -1;
 
688
                $this->mInCnt = 0;
 
689
                $this->mRefs = array();
 
690
 
 
691
                return true;
 
692
        }
 
693
 
 
694
        /**
 
695
         * Called at the end of page processing to append an error if refs were 
 
696
         * used without a references tag.
 
697
         */
 
698
        function checkRefsNoReferences(&$parser, &$text){
 
699
                if ( $parser->getOptions()->getIsSectionPreview() ) return true;
 
700
 
 
701
                foreach ( $this->mRefs as $group => $refs ) {
 
702
                        if ( count( $refs ) == 0 ) continue;
 
703
                        $text .= "\n<br />";
 
704
                        if ( $group == CITE_DEFAULT_GROUP ) {
 
705
                                $text .= $this->error( 'cite_error_refs_without_references' );
 
706
                        } else {
 
707
                                $text .= $this->error( 'cite_error_group_refs_without_references', htmlspecialchars( $group ) );
 
708
                        }
 
709
                }
 
710
                return true;
 
711
        }
 
712
 
 
713
        /**
 
714
         * Initialize the parser hooks
 
715
         */
 
716
        function setHooks() {
 
717
                global $wgParser, $wgHooks;
 
718
 
 
719
                $wgParser->setHook( 'ref' , array( &$this, 'ref' ) );
 
720
                $wgParser->setHook( 'references' , array( &$this, 'references' ) );
 
721
 
 
722
                $wgHooks['ParserClearState'][] = array( &$this, 'clearState' );
 
723
                $wgHooks['ParserBeforeTidy'][] = array( &$this, 'checkRefsNoReferences' );
 
724
        }
 
725
 
 
726
        /**
 
727
         * Return an error message based on an error ID
 
728
         *
 
729
         * @param string $key   Message name for the error
 
730
         * @param string $param Parameter to pass to the message
 
731
         * @return string XHTML ready for output
 
732
         */
 
733
        function error( $key, $param=null ) {
 
734
                # We rely on the fact that PHP is okay with passing unused argu-
 
735
                # ments to functions.  If $1 is not used in the message, wfMsg will
 
736
                # just ignore the extra parameter.
 
737
                return 
 
738
                        $this->parse(
 
739
                                '<strong class="error">' .
 
740
                                wfMsgNoTrans( 'cite_error', wfMsgNoTrans( $key, $param ) ) .
 
741
                                '</strong>'
 
742
                        );
 
743
        }
 
744
 
 
745
        /**
 
746
         * Die with a backtrace if something happens in the code which
 
747
         * shouldn't have
 
748
         *
 
749
         * @param int $error  ID for the error
 
750
         * @param string $data Serialized error data
 
751
         */
 
752
        function croak( $error, $data ) {
 
753
                wfDebugDieBacktrace( wfMsgForContent( 'cite_croak', $this->error( $error ), $data ) );
 
754
        }
 
755
 
 
756
        /**#@-*/
 
757
}
 
758
 
 
759
?>