Merge "Use a better regular expression to find documented hooks in findHooks.php"
[lhc/web/wiklou.git] / includes / specialpage / SpecialPage.php
1 <?php
2 /**
3 * Parent class for all special pages.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup SpecialPage
22 */
23
24 /**
25 * Parent class for all special pages.
26 *
27 * Includes some static functions for handling the special page list deprecated
28 * in favor of SpecialPageFactory.
29 *
30 * @ingroup SpecialPage
31 */
32 class SpecialPage {
33 // The canonical name of this special page
34 // Also used for the default <h1> heading, @see getDescription()
35 protected $mName;
36
37 // The local name of this special page
38 private $mLocalName;
39
40 // Minimum user level required to access this page, or "" for anyone.
41 // Also used to categorise the pages in Special:Specialpages
42 private $mRestriction;
43
44 // Listed in Special:Specialpages?
45 private $mListed;
46
47 // Function name called by the default execute()
48 private $mFunction;
49
50 // File which needs to be included before the function above can be called
51 private $mFile;
52
53 // Whether or not this special page is being included from an article
54 protected $mIncluding;
55
56 // Whether the special page can be included in an article
57 protected $mIncludable;
58
59 /**
60 * Current request context
61 * @var IContextSource
62 */
63 protected $mContext;
64
65 /**
66 * Initialise the special page list
67 * This must be called before accessing SpecialPage::$mList
68 * @deprecated since 1.18
69 */
70 static function initList() {
71 wfDeprecated( __METHOD__, '1.18' );
72 // Noop
73 }
74
75 /**
76 * @deprecated since 1.18
77 */
78 static function initAliasList() {
79 wfDeprecated( __METHOD__, '1.18' );
80 // Noop
81 }
82
83 /**
84 * Given a special page alias, return the special page name.
85 * Returns false if there is no such alias.
86 *
87 * @param $alias String
88 * @return String or false
89 * @deprecated since 1.18 call SpecialPageFactory method directly
90 */
91 static function resolveAlias( $alias ) {
92 wfDeprecated( __METHOD__, '1.18' );
93 list( $name, /*...*/ ) = SpecialPageFactory::resolveAlias( $alias );
94 return $name;
95 }
96
97 /**
98 * Given a special page name with a possible subpage, return an array
99 * where the first element is the special page name and the second is the
100 * subpage.
101 *
102 * @param $alias String
103 * @return Array
104 * @deprecated since 1.18 call SpecialPageFactory method directly
105 */
106 static function resolveAliasWithSubpage( $alias ) {
107 return SpecialPageFactory::resolveAlias( $alias );
108 }
109
110 /**
111 * Add a page to a certain display group for Special:SpecialPages
112 *
113 * @param $page Mixed: SpecialPage or string
114 * @param $group String
115 * @deprecated since 1.18 call SpecialPageFactory method directly
116 */
117 static function setGroup( $page, $group ) {
118 wfDeprecated( __METHOD__, '1.18' );
119 SpecialPageFactory::setGroup( $page, $group );
120 }
121
122 /**
123 * Get the group that the special page belongs in on Special:SpecialPage
124 *
125 * @param $page SpecialPage
126 * @return string
127 * @deprecated since 1.18 call SpecialPageFactory method directly
128 */
129 static function getGroup( &$page ) {
130 wfDeprecated( __METHOD__, '1.18' );
131 return SpecialPageFactory::getGroup( $page );
132 }
133
134 /**
135 * Remove a special page from the list
136 * Formerly used to disable expensive or dangerous special pages. The
137 * preferred method is now to add a SpecialPage_initList hook.
138 * @deprecated since 1.18
139 *
140 * @param string $name the page to remove
141 */
142 static function removePage( $name ) {
143 wfDeprecated( __METHOD__, '1.18' );
144 unset( SpecialPageFactory::getList()->$name );
145 }
146
147 /**
148 * Check if a given name exist as a special page or as a special page alias
149 *
150 * @param string $name name of a special page
151 * @return Boolean: true if a special page exists with this name
152 * @deprecated since 1.18 call SpecialPageFactory method directly
153 */
154 static function exists( $name ) {
155 wfDeprecated( __METHOD__, '1.18' );
156 return SpecialPageFactory::exists( $name );
157 }
158
159 /**
160 * Find the object with a given name and return it (or NULL)
161 *
162 * @param $name String
163 * @return SpecialPage object or null if the page doesn't exist
164 * @deprecated since 1.18 call SpecialPageFactory method directly
165 */
166 static function getPage( $name ) {
167 wfDeprecated( __METHOD__, '1.18' );
168 return SpecialPageFactory::getPage( $name );
169 }
170
171 /**
172 * Get a special page with a given localised name, or NULL if there
173 * is no such special page.
174 *
175 * @param $alias String
176 * @return SpecialPage object or null if the page doesn't exist
177 * @deprecated since 1.18 call SpecialPageFactory method directly
178 */
179 static function getPageByAlias( $alias ) {
180 wfDeprecated( __METHOD__, '1.18' );
181 return SpecialPageFactory::getPage( $alias );
182 }
183
184 /**
185 * Return categorised listable special pages which are available
186 * for the current user, and everyone.
187 *
188 * @param $user User object to check permissions, $wgUser will be used
189 * if not provided
190 * @return array Associative array mapping page's name to its SpecialPage object
191 * @deprecated since 1.18 call SpecialPageFactory method directly
192 */
193 static function getUsablePages( User $user = null ) {
194 wfDeprecated( __METHOD__, '1.18' );
195 return SpecialPageFactory::getUsablePages( $user );
196 }
197
198 /**
199 * Return categorised listable special pages for all users
200 *
201 * @return array Associative array mapping page's name to its SpecialPage object
202 * @deprecated since 1.18 call SpecialPageFactory method directly
203 */
204 static function getRegularPages() {
205 wfDeprecated( __METHOD__, '1.18' );
206 return SpecialPageFactory::getRegularPages();
207 }
208
209 /**
210 * Return categorised listable special pages which are available
211 * for the current user, but not for everyone
212 *
213 * @return array Associative array mapping page's name to its SpecialPage object
214 * @deprecated since 1.18 call SpecialPageFactory method directly
215 */
216 static function getRestrictedPages() {
217 wfDeprecated( __METHOD__, '1.18' );
218 return SpecialPageFactory::getRestrictedPages();
219 }
220
221 /**
222 * Execute a special page path.
223 * The path may contain parameters, e.g. Special:Name/Params
224 * Extracts the special page name and call the execute method, passing the parameters
225 *
226 * Returns a title object if the page is redirected, false if there was no such special
227 * page, and true if it was successful.
228 *
229 * @param $title Title object
230 * @param $context IContextSource
231 * @param $including Bool output is being captured for use in {{special:whatever}}
232 * @return Bool
233 * @deprecated since 1.18 call SpecialPageFactory method directly
234 */
235 public static function executePath( &$title, IContextSource &$context, $including = false ) {
236 wfDeprecated( __METHOD__, '1.18' );
237 return SpecialPageFactory::executePath( $title, $context, $including );
238 }
239
240 /**
241 * Get the local name for a specified canonical name
242 *
243 * @param $name String
244 * @param $subpage Mixed: boolean false, or string
245 *
246 * @return String
247 * @deprecated since 1.18 call SpecialPageFactory method directly
248 */
249 static function getLocalNameFor( $name, $subpage = false ) {
250 wfDeprecated( __METHOD__, '1.18' );
251 return SpecialPageFactory::getLocalNameFor( $name, $subpage );
252 }
253
254 /**
255 * Get a localised Title object for a specified special page name
256 *
257 * @param $name String
258 * @param string|Bool $subpage subpage string, or false to not use a subpage
259 * @param string $fragment the link fragment (after the "#")
260 * @throws MWException
261 * @return Title object
262 */
263 public static function getTitleFor( $name, $subpage = false, $fragment = '' ) {
264 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
265 return Title::makeTitle( NS_SPECIAL, $name, $fragment );
266 }
267
268 /**
269 * Get a localised Title object for a page name with a possibly unvalidated subpage
270 *
271 * @param $name String
272 * @param string|Bool $subpage subpage string, or false to not use a subpage
273 * @return Title object or null if the page doesn't exist
274 */
275 public static function getSafeTitleFor( $name, $subpage = false ) {
276 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
277 if ( $name ) {
278 return Title::makeTitleSafe( NS_SPECIAL, $name );
279 } else {
280 return null;
281 }
282 }
283
284 /**
285 * Get a title for a given alias
286 *
287 * @param $alias String
288 * @return Title or null if there is no such alias
289 * @deprecated since 1.18 call SpecialPageFactory method directly
290 */
291 static function getTitleForAlias( $alias ) {
292 wfDeprecated( __METHOD__, '1.18' );
293 return SpecialPageFactory::getTitleForAlias( $alias );
294 }
295
296 /**
297 * Default constructor for special pages
298 * Derivative classes should call this from their constructor
299 * Note that if the user does not have the required level, an error message will
300 * be displayed by the default execute() method, without the global function ever
301 * being called.
302 *
303 * If you override execute(), you can recover the default behavior with userCanExecute()
304 * and displayRestrictionError()
305 *
306 * @param string $name Name of the special page, as seen in links and URLs
307 * @param string $restriction User right required, e.g. "block" or "delete"
308 * @param bool $listed Whether the page is listed in Special:Specialpages
309 * @param Callback|Bool $function Function called by execute(). By default
310 * it is constructed from $name
311 * @param string $file File which is included by execute(). It is also
312 * constructed from $name by default
313 * @param bool $includable Whether the page can be included in normal pages
314 */
315 public function __construct(
316 $name = '', $restriction = '', $listed = true,
317 $function = false, $file = 'default', $includable = false
318 ) {
319 $this->init( $name, $restriction, $listed, $function, $file, $includable );
320 }
321
322 /**
323 * Do the real work for the constructor, mainly so __call() can intercept
324 * calls to SpecialPage()
325 * @param string $name Name of the special page, as seen in links and URLs
326 * @param string $restriction User right required, e.g. "block" or "delete"
327 * @param bool $listed Whether the page is listed in Special:Specialpages
328 * @param Callback|Bool $function Function called by execute(). By default
329 * it is constructed from $name
330 * @param string $file File which is included by execute(). It is also
331 * constructed from $name by default
332 * @param bool $includable Whether the page can be included in normal pages
333 */
334 private function init( $name, $restriction, $listed, $function, $file, $includable ) {
335 $this->mName = $name;
336 $this->mRestriction = $restriction;
337 $this->mListed = $listed;
338 $this->mIncludable = $includable;
339 if ( !$function ) {
340 $this->mFunction = 'wfSpecial' . $name;
341 } else {
342 $this->mFunction = $function;
343 }
344 if ( $file === 'default' ) {
345 $this->mFile = __DIR__ . "/specials/Special$name.php";
346 } else {
347 $this->mFile = $file;
348 }
349 }
350
351 /**
352 * Use PHP's magic __call handler to get calls to the old PHP4 constructor
353 * because PHP E_STRICT yells at you for having __construct() and SpecialPage()
354 *
355 * @param string $fName Name of called method
356 * @param array $a Arguments to the method
357 * @throws MWException
358 * @deprecated since 1.17, call parent::__construct()
359 */
360 public function __call( $fName, $a ) {
361 // Deprecated messages now, remove in 1.19 or 1.20?
362 wfDeprecated( __METHOD__, '1.17' );
363
364 // Sometimes $fName is SpecialPage, sometimes it's specialpage. <3 PHP
365 if ( strtolower( $fName ) == 'specialpage' ) {
366 $name = isset( $a[0] ) ? $a[0] : '';
367 $restriction = isset( $a[1] ) ? $a[1] : '';
368 $listed = isset( $a[2] ) ? $a[2] : true;
369 $function = isset( $a[3] ) ? $a[3] : false;
370 $file = isset( $a[4] ) ? $a[4] : 'default';
371 $includable = isset( $a[5] ) ? $a[5] : false;
372 $this->init( $name, $restriction, $listed, $function, $file, $includable );
373 } else {
374 $className = get_class( $this );
375 throw new MWException( "Call to undefined method $className::$fName" );
376 }
377 }
378
379 /**
380 * Get the name of this Special Page.
381 * @return String
382 */
383 function getName() {
384 return $this->mName;
385 }
386
387 /**
388 * Get the permission that a user must have to execute this page
389 * @return String
390 */
391 function getRestriction() {
392 return $this->mRestriction;
393 }
394
395 /**
396 * Get the file which will be included by SpecialPage::execute() if your extension is
397 * still stuck in the past and hasn't overridden the execute() method. No modern code
398 * should want or need to know this.
399 * @return String
400 * @deprecated since 1.18
401 */
402 function getFile() {
403 wfDeprecated( __METHOD__, '1.18' );
404 return $this->mFile;
405 }
406
407 // @todo FIXME: Decide which syntax to use for this, and stick to it
408 /**
409 * Whether this special page is listed in Special:SpecialPages
410 * @since r3583 (v1.3)
411 * @return Bool
412 */
413 function isListed() {
414 return $this->mListed;
415 }
416 /**
417 * Set whether this page is listed in Special:Specialpages, at run-time
418 * @since r3583 (v1.3)
419 * @param $listed Bool
420 * @return Bool
421 */
422 function setListed( $listed ) {
423 return wfSetVar( $this->mListed, $listed );
424 }
425 /**
426 * Get or set whether this special page is listed in Special:SpecialPages
427 * @since r11308 (v1.6)
428 * @param $x Bool
429 * @return Bool
430 */
431 function listed( $x = null ) {
432 return wfSetVar( $this->mListed, $x );
433 }
434
435 /**
436 * Whether it's allowed to transclude the special page via {{Special:Foo/params}}
437 * @return Bool
438 */
439 public function isIncludable() {
440 return $this->mIncludable;
441 }
442
443 /**
444 * These mutators are very evil, as the relevant variables should not mutate. So
445 * don't use them.
446 * @param $x Mixed
447 * @return Mixed
448 * @deprecated since 1.18
449 */
450 function name( $x = null ) {
451 wfDeprecated( __METHOD__, '1.18' );
452 return wfSetVar( $this->mName, $x );
453 }
454
455 /**
456 * These mutators are very evil, as the relevant variables should not mutate. So
457 * don't use them.
458 * @param $x Mixed
459 * @return Mixed
460 * @deprecated since 1.18
461 */
462 function restriction( $x = null ) {
463 wfDeprecated( __METHOD__, '1.18' );
464 return wfSetVar( $this->mRestriction, $x );
465 }
466
467 /**
468 * These mutators are very evil, as the relevant variables should not mutate. So
469 * don't use them.
470 * @param $x Mixed
471 * @return Mixed
472 * @deprecated since 1.18
473 */
474 function func( $x = null ) {
475 wfDeprecated( __METHOD__, '1.18' );
476 return wfSetVar( $this->mFunction, $x );
477 }
478
479 /**
480 * These mutators are very evil, as the relevant variables should not mutate. So
481 * don't use them.
482 * @param $x Mixed
483 * @return Mixed
484 * @deprecated since 1.18
485 */
486 function file( $x = null ) {
487 wfDeprecated( __METHOD__, '1.18' );
488 return wfSetVar( $this->mFile, $x );
489 }
490
491 /**
492 * These mutators are very evil, as the relevant variables should not mutate. So
493 * don't use them.
494 * @param $x Mixed
495 * @return Mixed
496 * @deprecated since 1.18
497 */
498 function includable( $x = null ) {
499 wfDeprecated( __METHOD__, '1.18' );
500 return wfSetVar( $this->mIncludable, $x );
501 }
502
503 /**
504 * Whether the special page is being evaluated via transclusion
505 * @param $x Bool
506 * @return Bool
507 */
508 function including( $x = null ) {
509 return wfSetVar( $this->mIncluding, $x );
510 }
511
512 /**
513 * Get the localised name of the special page
514 */
515 function getLocalName() {
516 if ( !isset( $this->mLocalName ) ) {
517 $this->mLocalName = SpecialPageFactory::getLocalNameFor( $this->mName );
518 }
519 return $this->mLocalName;
520 }
521
522 /**
523 * Is this page expensive (for some definition of expensive)?
524 * Expensive pages are disabled or cached in miser mode. Originally used
525 * (and still overridden) by QueryPage and subclasses, moved here so that
526 * Special:SpecialPages can safely call it for all special pages.
527 *
528 * @return Boolean
529 */
530 public function isExpensive() {
531 return false;
532 }
533
534 /**
535 * Is this page cached?
536 * Expensive pages are cached or disabled in miser mode.
537 * Used by QueryPage and subclasses, moved here so that
538 * Special:SpecialPages can safely call it for all special pages.
539 *
540 * @return Boolean
541 * @since 1.21
542 */
543 public function isCached() {
544 return false;
545 }
546
547 /**
548 * Can be overridden by subclasses with more complicated permissions
549 * schemes.
550 *
551 * @return Boolean: should the page be displayed with the restricted-access
552 * pages?
553 */
554 public function isRestricted() {
555 // DWIM: If anons can do something, then it is not restricted
556 return $this->mRestriction != '' && !User::groupHasPermission( '*', $this->mRestriction );
557 }
558
559 /**
560 * Checks if the given user (identified by an object) can execute this
561 * special page (as defined by $mRestriction). Can be overridden by sub-
562 * classes with more complicated permissions schemes.
563 *
564 * @param $user User: the user to check
565 * @return Boolean: does the user have permission to view the page?
566 */
567 public function userCanExecute( User $user ) {
568 return $user->isAllowed( $this->mRestriction );
569 }
570
571 /**
572 * Output an error message telling the user what access level they have to have
573 */
574 function displayRestrictionError() {
575 throw new PermissionsError( $this->mRestriction );
576 }
577
578 /**
579 * Checks if userCanExecute, and if not throws a PermissionsError
580 *
581 * @since 1.19
582 */
583 public function checkPermissions() {
584 if ( !$this->userCanExecute( $this->getUser() ) ) {
585 $this->displayRestrictionError();
586 }
587 }
588
589 /**
590 * If the wiki is currently in readonly mode, throws a ReadOnlyError
591 *
592 * @since 1.19
593 * @throws ReadOnlyError
594 */
595 public function checkReadOnly() {
596 if ( wfReadOnly() ) {
597 throw new ReadOnlyError;
598 }
599 }
600
601 /**
602 * If the user is not logged in, throws UserNotLoggedIn error.
603 *
604 * Default error message includes a link to Special:Userlogin with properly set 'returnto' query
605 * parameter.
606 *
607 * @since 1.23
608 * @param string|Message $reasonMsg [optional] Passed on to UserNotLoggedIn constructor. Strings
609 * will be used as message keys. If a string is given, the message will also receive a
610 * formatted login link (generated using the 'loginreqlink' message) as first parameter. If a
611 * Message is given, it will be passed on verbatim.
612 * @param string|Message $titleMsg [optional] Passed on to UserNotLoggedIn constructor. Strings
613 * will be used as message keys.
614 * @throws UserNotLoggedIn
615 */
616 public function requireLogin( $reasonMsg = null, $titleMsg = null ) {
617 if ( $this->getUser()->isAnon() ) {
618 // Use default messages if not given or explicit null passed
619 if ( !$reasonMsg ) {
620 $reasonMsg = 'exception-nologin-text-manual';
621 }
622 if ( !$titleMsg ) {
623 $titleMsg = 'exception-nologin';
624 }
625
626 // Convert to Messages with current context
627 if ( is_string( $reasonMsg ) ) {
628 $loginreqlink = Linker::linkKnown(
629 SpecialPage::getTitleFor( 'Userlogin' ),
630 $this->msg( 'loginreqlink' )->escaped(),
631 array(),
632 array( 'returnto' => $this->getTitle()->getPrefixedText() )
633 );
634 $reasonMsg = $this->msg( $reasonMsg )->rawParams( $loginreqlink );
635 }
636 if ( is_string( $titleMsg ) ) {
637 $titleMsg = $this->msg( $titleMsg );
638 }
639
640 throw new UserNotLoggedIn( $reasonMsg, $titleMsg );
641 }
642 }
643
644 /**
645 * Sets headers - this should be called from the execute() method of all derived classes!
646 */
647 function setHeaders() {
648 $out = $this->getOutput();
649 $out->setArticleRelated( false );
650 $out->setRobotPolicy( "noindex,nofollow" );
651 $out->setPageTitle( $this->getDescription() );
652 }
653
654 /**
655 * Entry point.
656 *
657 * @since 1.20
658 *
659 * @param $subPage string|null
660 */
661 final public function run( $subPage ) {
662 /**
663 * Gets called before @see SpecialPage::execute.
664 *
665 * @since 1.20
666 *
667 * @param $special SpecialPage
668 * @param $subPage string|null
669 */
670 wfRunHooks( 'SpecialPageBeforeExecute', array( $this, $subPage ) );
671
672 $this->beforeExecute( $subPage );
673 $this->execute( $subPage );
674 $this->afterExecute( $subPage );
675
676 /**
677 * Gets called after @see SpecialPage::execute.
678 *
679 * @since 1.20
680 *
681 * @param $special SpecialPage
682 * @param $subPage string|null
683 */
684 wfRunHooks( 'SpecialPageAfterExecute', array( $this, $subPage ) );
685 }
686
687 /**
688 * Gets called before @see SpecialPage::execute.
689 *
690 * @since 1.20
691 *
692 * @param $subPage string|null
693 */
694 protected function beforeExecute( $subPage ) {
695 // No-op
696 }
697
698 /**
699 * Gets called after @see SpecialPage::execute.
700 *
701 * @since 1.20
702 *
703 * @param $subPage string|null
704 */
705 protected function afterExecute( $subPage ) {
706 // No-op
707 }
708
709 /**
710 * Default execute method
711 * Checks user permissions, calls the function given in mFunction
712 *
713 * This must be overridden by subclasses; it will be made abstract in a future version
714 *
715 * @param $subPage string|null
716 */
717 public function execute( $subPage ) {
718 $this->setHeaders();
719 $this->checkPermissions();
720
721 $func = $this->mFunction;
722 // only load file if the function does not exist
723 if ( !is_callable( $func ) && $this->mFile ) {
724 require_once $this->mFile;
725 }
726 $this->outputHeader();
727 call_user_func( $func, $subPage, $this );
728 }
729
730 /**
731 * Outputs a summary message on top of special pages
732 * Per default the message key is the canonical name of the special page
733 * May be overridden, i.e. by extensions to stick with the naming conventions
734 * for message keys: 'extensionname-xxx'
735 *
736 * @param string $summaryMessageKey message key of the summary
737 */
738 function outputHeader( $summaryMessageKey = '' ) {
739 global $wgContLang;
740
741 if ( $summaryMessageKey == '' ) {
742 $msg = $wgContLang->lc( $this->getName() ) . '-summary';
743 } else {
744 $msg = $summaryMessageKey;
745 }
746 if ( !$this->msg( $msg )->isDisabled() && !$this->including() ) {
747 $this->getOutput()->wrapWikiMsg(
748 "<div class='mw-specialpage-summary'>\n$1\n</div>", $msg );
749 }
750
751 }
752
753 /**
754 * Returns the name that goes in the \<h1\> in the special page itself, and
755 * also the name that will be listed in Special:Specialpages
756 *
757 * Derived classes can override this, but usually it is easier to keep the
758 * default behavior.
759 *
760 * @return string
761 */
762 function getDescription() {
763 return $this->msg( strtolower( $this->mName ) )->text();
764 }
765
766 /**
767 * Get a self-referential title object
768 *
769 * @param $subpage String|Bool
770 * @return Title object
771 */
772 function getTitle( $subpage = false ) {
773 return self::getTitleFor( $this->mName, $subpage );
774 }
775
776 /**
777 * Sets the context this SpecialPage is executed in
778 *
779 * @param $context IContextSource
780 * @since 1.18
781 */
782 public function setContext( $context ) {
783 $this->mContext = $context;
784 }
785
786 /**
787 * Gets the context this SpecialPage is executed in
788 *
789 * @return IContextSource|RequestContext
790 * @since 1.18
791 */
792 public function getContext() {
793 if ( $this->mContext instanceof IContextSource ) {
794 return $this->mContext;
795 } else {
796 wfDebug( __METHOD__ . " called and \$mContext is null. " .
797 "Return RequestContext::getMain(); for sanity\n" );
798 return RequestContext::getMain();
799 }
800 }
801
802 /**
803 * Get the WebRequest being used for this instance
804 *
805 * @return WebRequest
806 * @since 1.18
807 */
808 public function getRequest() {
809 return $this->getContext()->getRequest();
810 }
811
812 /**
813 * Get the OutputPage being used for this instance
814 *
815 * @return OutputPage
816 * @since 1.18
817 */
818 public function getOutput() {
819 return $this->getContext()->getOutput();
820 }
821
822 /**
823 * Shortcut to get the User executing this instance
824 *
825 * @return User
826 * @since 1.18
827 */
828 public function getUser() {
829 return $this->getContext()->getUser();
830 }
831
832 /**
833 * Shortcut to get the skin being used for this instance
834 *
835 * @return Skin
836 * @since 1.18
837 */
838 public function getSkin() {
839 return $this->getContext()->getSkin();
840 }
841
842 /**
843 * Shortcut to get user's language
844 *
845 * @deprecated since 1.19 Use getLanguage instead
846 * @return Language
847 * @since 1.18
848 */
849 public function getLang() {
850 wfDeprecated( __METHOD__, '1.19' );
851 return $this->getLanguage();
852 }
853
854 /**
855 * Shortcut to get user's language
856 *
857 * @return Language
858 * @since 1.19
859 */
860 public function getLanguage() {
861 return $this->getContext()->getLanguage();
862 }
863
864 /**
865 * Return the full title, including $par
866 *
867 * @return Title
868 * @since 1.18
869 */
870 public function getFullTitle() {
871 return $this->getContext()->getTitle();
872 }
873
874 /**
875 * Wrapper around wfMessage that sets the current context.
876 *
877 * @return Message
878 * @see wfMessage
879 */
880 public function msg( /* $args */ ) {
881 $message = call_user_func_array(
882 array( $this->getContext(), 'msg' ),
883 func_get_args()
884 );
885 // RequestContext passes context to wfMessage, and the language is set from
886 // the context, but setting the language for Message class removes the
887 // interface message status, which breaks for example usernameless gender
888 // invocations. Restore the flag when not including special page in content.
889 if ( $this->including() ) {
890 $message->setInterfaceMessageFlag( false );
891 }
892 return $message;
893 }
894
895 /**
896 * Adds RSS/atom links
897 *
898 * @param $params array
899 */
900 protected function addFeedLinks( $params ) {
901 global $wgFeedClasses;
902
903 $feedTemplate = wfScript( 'api' );
904
905 foreach ( $wgFeedClasses as $format => $class ) {
906 $theseParams = $params + array( 'feedformat' => $format );
907 $url = wfAppendQuery( $feedTemplate, $theseParams );
908 $this->getOutput()->addFeedLink( $format, $url );
909 }
910 }
911
912 /**
913 * Get the group that the special page belongs in on Special:SpecialPage
914 * Use this method, instead of getGroupName to allow customization
915 * of the group name from the wiki side
916 *
917 * @return string Group of this special page
918 * @since 1.21
919 */
920 public function getFinalGroupName() {
921 global $wgSpecialPageGroups;
922 $name = $this->getName();
923
924 // Allow overbidding the group from the wiki side
925 $msg = $this->msg( 'specialpages-specialpagegroup-' . strtolower( $name ) )->inContentLanguage();
926 if ( !$msg->isBlank() ) {
927 $group = $msg->text();
928 } else {
929 // Than use the group from this object
930 $group = $this->getGroupName();
931
932 // Group '-' is used as default to have the chance to determine,
933 // if the special pages overrides this method,
934 // if not overridden, $wgSpecialPageGroups is checked for b/c
935 if ( $group === '-' && isset( $wgSpecialPageGroups[$name] ) ) {
936 $group = $wgSpecialPageGroups[$name];
937 }
938 }
939
940 // never give '-' back, change to 'other'
941 if ( $group === '-' ) {
942 $group = 'other';
943 }
944
945 return $group;
946 }
947
948 /**
949 * Under which header this special page is listed in Special:SpecialPages
950 * See messages 'specialpages-group-*' for valid names
951 * This method defaults to group 'other'
952 *
953 * @return string
954 * @since 1.21
955 */
956 protected function getGroupName() {
957 // '-' used here to determine, if this group is overridden or has a hardcoded 'other'
958 // Needed for b/c in getFinalGroupName
959 return '-';
960 }
961 }