]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/SpecialPage.php
MediaWiki 1.11.0
[autoinstallsdev/mediawiki.git] / includes / SpecialPage.php
1 <?php
2 /**
3  * SpecialPage: handling special pages and lists thereof.
4  *
5  * To add a special page in an extension, add to $wgSpecialPages either 
6  * an object instance or an array containing the name and constructor 
7  * parameters. The latter is preferred for performance reasons. 
8  *
9  * The object instantiated must be either an instance of SpecialPage or a 
10  * sub-class thereof. It must have an execute() method, which sends the HTML 
11  * for the special page to $wgOut. The parent class has an execute() method 
12  * which distributes the call to the historical global functions. Additionally, 
13  * execute() also checks if the user has the necessary access privileges 
14  * and bails out if not.
15  *
16  * To add a core special page, use the similar static list in 
17  * SpecialPage::$mList. To remove a core static special page at runtime, use
18  * a SpecialPage_initList hook.
19  *
20  * @addtogroup SpecialPage
21  */
22
23 /**
24  * @access private
25  */
26
27 /**
28  * Parent special page class, also static functions for handling the special
29  * page list.
30  * @addtogroup SpecialPage
31  */
32 class SpecialPage
33 {
34         /**#@+
35          * @access private
36          */
37         /**
38          * The canonical name of this special page
39          * Also used for the default <h1> heading, @see getDescription()
40          */
41         var $mName;
42         /**
43          * The local name of this special page
44          */
45         var $mLocalName;
46         /**
47          * Minimum user level required to access this page, or "" for anyone.
48          * Also used to categorise the pages in Special:Specialpages
49          */
50         var $mRestriction;
51         /**
52          * Listed in Special:Specialpages?
53          */
54         var $mListed;
55         /**
56          * Function name called by the default execute()
57          */
58         var $mFunction;
59         /**
60          * File which needs to be included before the function above can be called
61          */
62         var $mFile;
63         /**
64          * Whether or not this special page is being included from an article
65          */
66         var $mIncluding;
67         /**
68          * Whether the special page can be included in an article
69          */
70         var $mIncludable;
71         /**
72          * Query parameters that can be passed through redirects
73          */
74         var $mAllowedRedirectParams = array();
75
76         static public $mList = array(
77                 'DoubleRedirects'           => array( 'SpecialPage', 'DoubleRedirects' ),
78                 'BrokenRedirects'           => array( 'SpecialPage', 'BrokenRedirects' ),
79                 'Disambiguations'           => array( 'SpecialPage', 'Disambiguations' ),
80
81                 'Userlogin'                 => array( 'SpecialPage', 'Userlogin' ),
82                 'Userlogout'                => array( 'UnlistedSpecialPage', 'Userlogout' ),
83                 'Preferences'               => array( 'SpecialPage', 'Preferences' ),
84                 'Watchlist'                 => array( 'SpecialPage', 'Watchlist' ),
85
86                 'Recentchanges'             => array( 'IncludableSpecialPage', 'Recentchanges' ),
87                 'Upload'                    => array( 'SpecialPage', 'Upload' ),
88                 'Imagelist'                 => array( 'SpecialPage', 'Imagelist' ),
89                 'Newimages'                 => array( 'IncludableSpecialPage', 'Newimages' ),
90                 'Listusers'                 => array( 'SpecialPage', 'Listusers' ),
91                 'Statistics'                => array( 'SpecialPage', 'Statistics' ),
92                 'Randompage'                => array( 'SpecialPage', 'Randompage' ),
93                 'Lonelypages'               => array( 'SpecialPage', 'Lonelypages' ),
94                 'Uncategorizedpages'        => array( 'SpecialPage', 'Uncategorizedpages' ),
95                 'Uncategorizedcategories'   => array( 'SpecialPage', 'Uncategorizedcategories' ),
96                 'Uncategorizedimages'       => array( 'SpecialPage', 'Uncategorizedimages' ),
97                 'Uncategorizedtemplates'        => array( 'SpecialPage', 'Uncategorizedtemplates' ),
98                 'Unusedcategories'          => array( 'SpecialPage', 'Unusedcategories' ),
99                 'Unusedimages'              => array( 'SpecialPage', 'Unusedimages' ),
100                 'Wantedpages'               => array( 'IncludableSpecialPage', 'Wantedpages' ),
101                 'Wantedcategories'          => array( 'SpecialPage', 'Wantedcategories' ),
102                 'Mostlinked'                => array( 'SpecialPage', 'Mostlinked' ),
103                 'Mostlinkedcategories'      => array( 'SpecialPage', 'Mostlinkedcategories' ),
104                 'Mostlinkedtemplates'           => array( 'SpecialPage', 'Mostlinkedtemplates' ),
105                 'Mostcategories'            => array( 'SpecialPage', 'Mostcategories' ),
106                 'Mostimages'                => array( 'SpecialPage', 'Mostimages' ),
107                 'Mostrevisions'             => array( 'SpecialPage', 'Mostrevisions' ),
108                 'Fewestrevisions'             => array( 'SpecialPage', 'Fewestrevisions' ),
109                 'Shortpages'                => array( 'SpecialPage', 'Shortpages' ),
110                 'Longpages'                 => array( 'SpecialPage', 'Longpages' ),
111                 'Newpages'                  => array( 'IncludableSpecialPage', 'Newpages' ),
112                 'Ancientpages'              => array( 'SpecialPage', 'Ancientpages' ),
113                 'Deadendpages'              => array( 'SpecialPage', 'Deadendpages' ),
114                 'Protectedpages'            => array( 'SpecialPage', 'Protectedpages' ),
115                 'Allpages'                  => array( 'IncludableSpecialPage', 'Allpages' ),
116                 'Prefixindex'               => array( 'IncludableSpecialPage', 'Prefixindex' ) ,
117                 'Ipblocklist'               => array( 'SpecialPage', 'Ipblocklist' ),
118                 'Specialpages'              => array( 'UnlistedSpecialPage', 'Specialpages' ),
119                 'Contributions'             => array( 'SpecialPage', 'Contributions' ),
120                 'Emailuser'                 => array( 'UnlistedSpecialPage', 'Emailuser' ),
121                 'Whatlinkshere'             => array( 'UnlistedSpecialPage', 'Whatlinkshere' ),
122                 'Recentchangeslinked'       => array( 'UnlistedSpecialPage', 'Recentchangeslinked' ),
123                 'Movepage'                  => array( 'UnlistedSpecialPage', 'Movepage' ),
124                 'Blockme'                   => array( 'UnlistedSpecialPage', 'Blockme' ),
125                 'Resetpass'                 => array( 'UnlistedSpecialPage', 'Resetpass' ),
126                 'Booksources'               => 'SpecialBookSources',
127                 'Categories'                => array( 'SpecialPage', 'Categories' ),
128                 'Export'                    => array( 'SpecialPage', 'Export' ),
129                 'Version'                   => array( 'SpecialPage', 'Version' ),
130                 'Allmessages'               => array( 'SpecialPage', 'Allmessages' ),
131                 'Log'                       => array( 'SpecialPage', 'Log' ),
132                 'Blockip'                   => array( 'SpecialPage', 'Blockip', 'block' ),
133                 'Undelete'                  => array( 'SpecialPage', 'Undelete', 'deletedhistory' ),
134                 'Import'                    => array( 'SpecialPage', "Import", 'import' ),
135                 'Lockdb'                    => array( 'SpecialPage', 'Lockdb', 'siteadmin' ),
136                 'Unlockdb'                  => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ),
137                 'Userrights'                => array( 'SpecialPage', 'Userrights', 'userrights' ),
138                 'MIMEsearch'                => array( 'SpecialPage', 'MIMEsearch' ),
139                 'Unwatchedpages'            => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ),
140                 'Listredirects'             => array( 'SpecialPage', 'Listredirects' ),
141                 'Revisiondelete'            => array( 'SpecialPage', 'Revisiondelete', 'deleterevision' ),
142                 'Unusedtemplates'           => array( 'SpecialPage', 'Unusedtemplates' ),
143                 'Randomredirect'            => array( 'SpecialPage', 'Randomredirect' ),
144                 'Withoutinterwiki'                      => array( 'SpecialPage', 'Withoutinterwiki' ),
145
146                 'Mypage'                    => array( 'SpecialMypage' ),
147                 'Mytalk'                    => array( 'SpecialMytalk' ),
148                 'Mycontributions'           => array( 'SpecialMycontributions' ),
149                 'Listadmins'                => array( 'SpecialRedirectToSpecial', 'Listadmins', 'Listusers', 'sysop' ),
150         );
151
152         static public $mAliases;
153         static public $mListInitialised = false;
154
155         /**#@-*/
156
157         /**
158          * Initialise the special page list
159          * This must be called before accessing SpecialPage::$mList
160          */
161         static function initList() {
162                 global $wgSpecialPages;
163                 global $wgDisableCounters, $wgDisableInternalSearch, $wgEmailAuthentication;
164
165                 if ( self::$mListInitialised ) {
166                         return;
167                 }
168                 wfProfileIn( __METHOD__ );
169                 
170                 # Better to set this now, to avoid infinite recursion in carelessly written hooks
171                 self::$mListInitialised = true;
172
173                 if( !$wgDisableCounters ) {
174                         self::$mList['Popularpages'] = array( 'SpecialPage', 'Popularpages' );
175                 }
176
177                 if( !$wgDisableInternalSearch ) {
178                         self::$mList['Search'] = array( 'SpecialPage', 'Search' );
179                 }
180
181                 if( $wgEmailAuthentication ) {
182                         self::$mList['Confirmemail'] = 'EmailConfirmation';
183                 }
184
185                 # Add extension special pages
186                 self::$mList = array_merge( self::$mList, $wgSpecialPages );
187
188                 # Run hooks
189                 # This hook can be used to remove undesired built-in special pages
190                 wfRunHooks( 'SpecialPage_initList', array( &self::$mList ) );
191                 wfProfileOut( __METHOD__ );
192         }
193
194         static function initAliasList() {
195                 if ( !is_null( self::$mAliases ) ) {
196                         return;
197                 }
198
199                 global $wgContLang;
200                 $aliases = $wgContLang->getSpecialPageAliases();
201                 $missingPages = self::$mList;
202                 self::$mAliases = array();
203                 foreach ( $aliases as $realName => $aliasList ) {
204                         foreach ( $aliasList as $alias ) {
205                                 self::$mAliases[$wgContLang->caseFold( $alias )] = $realName;
206                         }
207                         unset( $missingPages[$realName] );
208                 }
209                 foreach ( $missingPages as $name => $stuff ) {
210                         self::$mAliases[$wgContLang->caseFold( $name )] = $name;
211                 }
212         }
213
214         /**
215          * Given a special page alias, return the special page name.
216          * Returns false if there is no such alias.
217          */
218         static function resolveAlias( $alias ) {
219                 global $wgContLang;
220
221                 if ( !self::$mListInitialised ) self::initList();
222                 if ( is_null( self::$mAliases ) ) self::initAliasList();
223                 $caseFoldedAlias = $wgContLang->caseFold( $alias );
224                 if ( isset( self::$mAliases[$caseFoldedAlias] ) ) {
225                         return self::$mAliases[$caseFoldedAlias];
226                 } else {
227                         return false;
228                 }
229         }
230
231         /**
232          * Given a special page name with a possible subpage, return an array 
233          * where the first element is the special page name and the second is the
234          * subpage.
235          */
236         static function resolveAliasWithSubpage( $alias ) {
237                 $bits = explode( '/', $alias, 2 );
238                 $name = self::resolveAlias( $bits[0] );
239                 if( !isset( $bits[1] ) ) { // bug 2087
240                         $par = NULL;
241                 } else {
242                         $par = $bits[1];
243                 }
244                 return array( $name, $par );
245         }
246
247         /**
248          * Add a page to the list of valid special pages. This used to be the preferred 
249          * method for adding special pages in extensions. It's now suggested that you add 
250          * an associative record to $wgSpecialPages. This avoids autoloading SpecialPage.
251          *
252          * @param mixed $page Must either be an array specifying a class name and 
253          *                    constructor parameters, or an object. The object,
254          *                    when constructed, must have an execute() method which
255          *                    sends HTML to $wgOut.
256          * @static
257          */
258         static function addPage( &$page ) {
259                 if ( !self::$mListInitialised ) {
260                         self::initList();
261                 }
262                 self::$mList[$page->mName] = $page;
263         }
264
265         /**
266          * Remove a special page from the list
267          * Formerly used to disable expensive or dangerous special pages. The 
268          * preferred method is now to add a SpecialPage_initList hook.
269          * 
270          * @static
271          */
272         static function removePage( $name ) {
273                 if ( !self::$mListInitialised ) {
274                         self::initList();
275                 }
276                 unset( self::$mList[$name] );
277         }
278
279         /**
280          * Check if a given name exist as a special page or as a special page alias
281          * @param $name string: name of a special page
282          * @return boolean: true if a special page exists with this name
283          */
284         static function exists( $name ) {
285                 global $wgContLang;
286                 if ( !self::$mListInitialised ) {
287                         self::initList();
288                 }
289                 if( !self::$mAliases ) {
290                         self::initAliasList();
291                 }
292
293                 # Remove special pages inline parameters:
294                 $bits = explode( '/', $name );
295                 $name = $wgContLang->caseFold($bits[0]);
296
297                 return
298                         array_key_exists( $name, self::$mList )
299                         or array_key_exists( $name, self::$mAliases )
300                 ;
301         }
302
303         /**
304          * Find the object with a given name and return it (or NULL)
305          * @static
306          * @param string $name
307          */
308         static function getPage( $name ) {
309                 if ( !self::$mListInitialised ) {
310                         self::initList();
311                 }
312                 if ( array_key_exists( $name, self::$mList ) ) {
313                         $rec = self::$mList[$name];
314                         if ( is_string( $rec ) ) {
315                                 $className = $rec;
316                                 self::$mList[$name] = new $className;
317                         } elseif ( is_array( $rec ) ) {
318                                 $className = array_shift( $rec );
319                                 self::$mList[$name] = wfCreateObject( $className, $rec );
320                         }
321                         return self::$mList[$name];
322                 } else {
323                         return NULL;
324                 }
325         }
326
327         /**
328          * Get a special page with a given localised name, or NULL if there
329          * is no such special page.
330          */
331         static function getPageByAlias( $alias ) {
332                 $realName = self::resolveAlias( $alias );
333                 if ( $realName ) {
334                         return self::getPage( $realName );
335                 } else {
336                         return NULL;
337                 }
338         }
339
340         /**
341          * Return categorised listable special pages for all users
342          * @static
343          */
344         static function getRegularPages() {
345                 if ( !self::$mListInitialised ) {
346                         self::initList();
347                 }
348                 $pages = array();
349
350                 foreach ( self::$mList as $name => $rec ) {
351                         $page = self::getPage( $name );
352                         if ( $page->isListed() && $page->getRestriction() == '' ) {
353                                 $pages[$name] = $page;
354                         }
355                 }
356                 return $pages;
357         }
358
359         /**
360          * Return categorised listable special pages which are available
361          * for the current user, but not for everyone
362          * @static
363          */
364         static function getRestrictedPages() {
365                 global $wgUser;
366                 if ( !self::$mListInitialised ) {
367                         self::initList();
368                 }
369                 $pages = array();
370
371                 foreach ( self::$mList as $name => $rec ) {
372                         $page = self::getPage( $name );
373                         if ( $page->isListed() ) {
374                                 $restriction = $page->getRestriction();
375                                 if ( $restriction != '' && $wgUser->isAllowed( $restriction ) ) {
376                                         $pages[$name] = $page;
377                                 }
378                         }
379                 }
380                 return $pages;
381         }
382
383         /**
384          * Execute a special page path.
385          * The path     may contain parameters, e.g. Special:Name/Params
386          * Extracts the special page name and call the execute method, passing the parameters
387          *
388          * Returns a title object if the page is redirected, false if there was no such special
389          * page, and true if it was successful.
390          *
391          * @param $title          a title object
392          * @param $including      output is being captured for use in {{special:whatever}}
393          */
394         static function executePath( &$title, $including = false ) {
395                 global $wgOut, $wgTitle, $wgRequest;
396                 wfProfileIn( __METHOD__ );
397
398                 # FIXME: redirects broken due to this call
399                 $bits = explode( '/', $title->getDBkey(), 2 );
400                 $name = $bits[0];
401                 if( !isset( $bits[1] ) ) { // bug 2087
402                         $par = NULL;
403                 } else {
404                         $par = $bits[1];
405                 }
406                 $page = SpecialPage::getPageByAlias( $name );
407
408                 # Nonexistent?
409                 if ( !$page ) {
410                         if ( !$including ) {
411                                 $wgOut->setArticleRelated( false );
412                                 $wgOut->setRobotpolicy( 'noindex,nofollow' );
413                                 $wgOut->setStatusCode( 404 );
414                                 $wgOut->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
415                         }
416                         wfProfileOut( __METHOD__ );
417                         return false;
418                 }
419
420                 # Check for redirect
421                 if ( !$including ) {
422                         $redirect = $page->getRedirect( $par );
423                         if ( $redirect ) {
424                                 $query = $page->getRedirectQuery();
425                                 $url = $redirect->getFullUrl( $query );
426                                 $wgOut->redirect( $url );
427                                 wfProfileOut( __METHOD__ );
428                                 return $redirect;
429                         }
430                 }
431
432                 # Redirect to canonical alias for GET commands
433                 # Not for POST, we'd lose the post data, so it's best to just distribute 
434                 # the request. Such POST requests are possible for old extensions that 
435                 # generate self-links without being aware that their default name has 
436                 # changed.
437                 if ( !$including && $name != $page->getLocalName() && !$wgRequest->wasPosted() ) {
438                         $query = $_GET;
439                         unset( $query['title'] );
440                         $query = wfArrayToCGI( $query );
441                         $title = $page->getTitle( $par );
442                         $url = $title->getFullUrl( $query );
443                         $wgOut->redirect( $url );
444                         wfProfileOut( __METHOD__ );
445                         return $redirect;
446                 }
447
448                 if ( $including && !$page->includable() ) {
449                         wfProfileOut( __METHOD__ );
450                         return false;
451                 } elseif ( !$including ) {
452                         $wgTitle = $page->getTitle();
453                 }
454                 $page->including( $including );
455
456                 // Execute special page
457                 $profName = 'Special:' . $page->getName();
458                 wfProfileIn( $profName );
459                 $page->execute( $par );
460                 wfProfileOut( $profName );
461                 wfProfileOut( __METHOD__ );
462                 return true;
463         }
464
465         /**
466          * Just like executePath() except it returns the HTML instead of outputting it
467          * Returns false if there was no such special page, or a title object if it was
468          * a redirect.
469          * @static
470          */
471         static function capturePath( &$title ) {
472                 global $wgOut, $wgTitle;
473
474                 $oldTitle = $wgTitle;
475                 $oldOut = $wgOut;
476                 $wgOut = new OutputPage;
477
478                 $ret = SpecialPage::executePath( $title, true );
479                 if ( $ret === true ) {
480                         $ret = $wgOut->getHTML();
481                 }
482                 $wgTitle = $oldTitle;
483                 $wgOut = $oldOut;
484                 return $ret;
485         }
486
487         /**
488          * Get the local name for a specified canonical name
489          */
490         static function getLocalNameFor( $name, $subpage = false ) {
491                 global $wgContLang;
492                 $aliases = $wgContLang->getSpecialPageAliases();
493                 if ( isset( $aliases[$name][0] ) ) {
494                         $name = $aliases[$name][0];
495                 }
496                 if ( $subpage !== false && !is_null( $subpage ) ) {
497                         $name = "$name/$subpage";
498                 }
499                 return $name;
500         }
501
502         /**
503          * Get a localised Title object for a specified special page name
504          */
505         static function getTitleFor( $name, $subpage = false ) {
506                 $name = self::getLocalNameFor( $name, $subpage );
507                 if ( $name ) {
508                         return Title::makeTitle( NS_SPECIAL, $name );
509                 } else {
510                         throw new MWException( "Invalid special page name \"$name\"" );
511                 }
512         }
513
514         /**
515          * Get a localised Title object for a page name with a possibly unvalidated subpage
516          */
517         static function getSafeTitleFor( $name, $subpage = false ) {
518                 $name = self::getLocalNameFor( $name, $subpage );
519                 if ( $name ) {
520                         return Title::makeTitleSafe( NS_SPECIAL, $name );
521                 } else {
522                         return null;
523                 }
524         }
525
526         /**
527          * Get a title for a given alias
528          * @return Title or null if there is no such alias
529          */
530         static function getTitleForAlias( $alias ) {
531                 $name = self::resolveAlias( $alias );
532                 if ( $name ) {
533                         return self::getTitleFor( $name );
534                 } else {
535                         return null;
536                 }
537         }
538
539         /**
540          * Default constructor for special pages
541          * Derivative classes should call this from their constructor
542          *     Note that if the user does not have the required level, an error message will
543          *     be displayed by the default execute() method, without the global function ever
544          *     being called.
545          *
546          *     If you override execute(), you can recover the default behaviour with userCanExecute()
547          *     and displayRestrictionError()
548          *
549          * @param string $name Name of the special page, as seen in links and URLs
550          * @param string $restriction User right required, e.g. "block" or "delete"
551          * @param boolean $listed Whether the page is listed in Special:Specialpages
552          * @param string $function Function called by execute(). By default it is constructed from $name
553          * @param string $file File which is included by execute(). It is also constructed from $name by default
554          */
555         function SpecialPage( $name = '', $restriction = '', $listed = true, $function = false, $file = 'default', $includable = false ) {
556                 $this->mName = $name;
557                 $this->mRestriction = $restriction;
558                 $this->mListed = $listed;
559                 $this->mIncludable = $includable;
560                 if ( $function == false ) {
561                         $this->mFunction = 'wfSpecial'.$name;
562                 } else {
563                         $this->mFunction = $function;
564                 }
565                 if ( $file === 'default' ) {
566                         $this->mFile = dirname(__FILE__) . "/Special{$name}.php";
567                 } else {
568                         $this->mFile = $file;
569                 }
570         }
571
572         /**#@+
573           * Accessor
574           *
575           * @deprecated
576           */
577         function getName() { return $this->mName; }
578         function getRestriction() { return $this->mRestriction; }
579         function getFile() { return $this->mFile; }
580         function isListed() { return $this->mListed; }
581         /**#@-*/
582
583         /**#@+
584           * Accessor and mutator
585           */
586         function name( $x = NULL ) { return wfSetVar( $this->mName, $x ); }
587         function restrictions( $x = NULL) { return wfSetVar( $this->mRestrictions, $x ); }
588         function listed( $x = NULL) { return wfSetVar( $this->mListed, $x ); }
589         function func( $x = NULL) { return wfSetVar( $this->mFunction, $x ); }
590         function file( $x = NULL) { return wfSetVar( $this->mFile, $x ); }
591         function includable( $x = NULL ) { return wfSetVar( $this->mIncludable, $x ); }
592         function including( $x = NULL ) { return wfSetVar( $this->mIncluding, $x ); }
593         /**#@-*/
594
595         /**
596          * Get the localised name of the special page
597          */
598         function getLocalName() {
599                 if ( !isset( $this->mLocalName ) ) {
600                         $this->mLocalName = self::getLocalNameFor( $this->mName );
601                 }
602                 return $this->mLocalName;
603         }
604
605         /**
606          * Checks if the given user (identified by an object) can execute this
607          * special page (as defined by $mRestriction)
608          */
609         function userCanExecute( &$user ) {
610                 return $user->isAllowed( $this->mRestriction );
611         }
612
613         /**
614          * Output an error message telling the user what access level they have to have
615          */
616         function displayRestrictionError() {
617                 global $wgOut;
618                 $wgOut->permissionRequired( $this->mRestriction );
619         }
620
621         /**
622          * Sets headers - this should be called from the execute() method of all derived classes!
623          */
624         function setHeaders() {
625                 global $wgOut;
626                 $wgOut->setArticleRelated( false );
627                 $wgOut->setRobotPolicy( "noindex,nofollow" );
628                 $wgOut->setPageTitle( $this->getDescription() );
629         }
630
631         /**
632          * Default execute method
633          * Checks user permissions, calls the function given in mFunction
634          *
635          * This may be overridden by subclasses. 
636          */
637         function execute( $par ) {
638                 global $wgUser;
639
640                 $this->setHeaders();
641
642                 if ( $this->userCanExecute( $wgUser ) ) {
643                         $func = $this->mFunction;
644                         // only load file if the function does not exist
645                         if(!function_exists($func) and $this->mFile) {
646                                 require_once( $this->mFile );
647                         }
648                         # FIXME: these hooks are broken for extensions and anything else that subclasses SpecialPage. 
649                         if ( wfRunHooks( 'SpecialPageExecuteBeforeHeader', array( &$this, &$par, &$func ) ) )
650                                 $this->outputHeader();
651                         if ( ! wfRunHooks( 'SpecialPageExecuteBeforePage', array( &$this, &$par, &$func ) ) )
652                                 return;
653                         $func( $par, $this );
654                         if ( ! wfRunHooks( 'SpecialPageExecuteAfterPage', array( &$this, &$par, &$func ) ) )
655                                 return;
656                 } else {
657                         $this->displayRestrictionError();
658                 }
659         }
660
661         function outputHeader() {
662                 global $wgOut, $wgContLang;
663
664                 $msg = $wgContLang->lc( $this->name() ) . '-summary';
665                 $out = wfMsg( $msg );
666                 if ( ! wfEmptyMsg( $msg, $out ) and  $out !== '' and ! $this->including() )
667                         $wgOut->addWikiText( $out );
668
669         }
670
671         # Returns the name that goes in the <h1> in the special page itself, and also the name that
672         # will be listed in Special:Specialpages
673         #
674         # Derived classes can override this, but usually it is easier to keep the default behaviour.
675         # Messages can be added at run-time, see MessageCache.php
676         function getDescription() {
677                 return wfMsg( strtolower( $this->mName ) );
678         }
679
680         /**
681          * Get a self-referential title object
682          */
683         function getTitle( $subpage = false) {
684                 return self::getTitleFor( $this->mName, $subpage );
685         }
686
687         /**
688          * Set whether this page is listed in Special:Specialpages, at run-time
689          */
690         function setListed( $listed ) {
691                 return wfSetVar( $this->mListed, $listed );
692         }
693
694         /**
695          * If the special page is a redirect, then get the Title object it redirects to. 
696          * False otherwise.
697          */
698         function getRedirect( $subpage ) {
699                 return false;
700         }
701
702         /**
703          * Return part of the request string for a special redirect page
704          * This allows passing, e.g. action=history to Special:Mypage, etc.
705          *
706          * @return string
707          */
708         function getRedirectQuery() {
709                 global $wgRequest;
710                 $params = array();
711                 foreach( $this->mAllowedRedirectParams as $arg ) {
712                         if( $val = $wgRequest->getVal( $arg, false ) )
713                                 $params[] = $arg . '=' . $val;
714                 }
715                 
716                 return count( $params ) ? implode( '&', $params ) : false;
717         }
718 }
719
720 /**
721  * Shortcut to construct a special page which is unlisted by default
722  * @addtogroup SpecialPage
723  */
724 class UnlistedSpecialPage extends SpecialPage
725 {
726         function UnlistedSpecialPage( $name, $restriction = '', $function = false, $file = 'default' ) {
727                 SpecialPage::SpecialPage( $name, $restriction, false, $function, $file );
728         }
729 }
730
731 /**
732  * Shortcut to construct an includable special  page
733  * @addtogroup SpecialPage
734  */
735 class IncludableSpecialPage extends SpecialPage
736 {
737         function IncludableSpecialPage( $name, $restriction = '', $listed = true, $function = false, $file = 'default' ) {
738                 SpecialPage::SpecialPage( $name, $restriction, $listed, $function, $file, true );
739         }
740 }
741
742 /**
743  * Shortcut to construct a special page alias.
744  * @addtogroup SpecialPage
745  */
746 class SpecialRedirectToSpecial extends UnlistedSpecialPage {
747         var $redirName, $redirSubpage;
748
749         function __construct( $name, $redirName, $redirSubpage = false, $redirectParams = array() ) {
750                 parent::__construct( $name );
751                 $this->redirName = $redirName;
752                 $this->redirSubpage = $redirSubpage;
753                 $this->mAllowedRedirectParams = $redirectParams;
754         }
755
756         function getRedirect( $subpage ) {
757                 if ( $this->redirSubpage === false ) {
758                         return SpecialPage::getTitleFor( $this->redirName, $subpage );
759                 } else {
760                         return SpecialPage::getTitleFor( $this->redirName, $this->redirSubpage );
761                 }
762         }
763 }
764
765 /** SpecialMypage, SpecialMytalk and SpecialMycontributions special pages
766  * are used to get user independant links pointing to the user page, talk
767  * page and list of contributions.
768  * This can let us cache a single copy of any generated content for all
769  * users.
770  */
771
772 /**
773  * Shortcut to construct a special page pointing to current user user's page.
774  * @addtogroup SpecialPage
775  */
776 class SpecialMypage extends UnlistedSpecialPage {
777         function __construct() {
778                 parent::__construct( 'Mypage' );
779                 $this->mAllowedRedirectParams = array( 'action' );
780         }
781
782         function getRedirect( $subpage ) {
783                 global $wgUser;
784                 if ( strval( $subpage ) !== '' ) {
785                         return Title::makeTitle( NS_USER, $wgUser->getName() . '/' . $subpage );
786                 } else {
787                         return Title::makeTitle( NS_USER, $wgUser->getName() );
788                 }
789         }
790 }
791
792 /**
793  * Shortcut to construct a special page pointing to current user talk page.
794  * @addtogroup SpecialPage
795  */
796 class SpecialMytalk extends UnlistedSpecialPage {
797         function __construct() {
798                 parent::__construct( 'Mytalk' );
799                 $this->mAllowedRedirectParams = array( 'action' );
800         }
801
802         function getRedirect( $subpage ) {
803                 global $wgUser;
804                 if ( strval( $subpage ) !== '' ) {
805                         return Title::makeTitle( NS_USER_TALK, $wgUser->getName() . '/' . $subpage );
806                 } else {
807                         return Title::makeTitle( NS_USER_TALK, $wgUser->getName() );
808                 }
809         }
810 }
811
812 /**
813  * Shortcut to construct a special page pointing to current user contributions.
814  * @addtogroup SpecialPage
815  */
816 class SpecialMycontributions extends UnlistedSpecialPage {
817         function __construct() {
818                 parent::__construct(  'Mycontributions' );
819         }
820
821         function getRedirect( $subpage ) {
822                 global $wgUser;
823                 return SpecialPage::getTitleFor( 'Contributions', $wgUser->getName() );
824         }
825 }
826
827