Consistently handle anonymous users on logged-in-only special pages
authorBartosz Dziewoński <matma.rex@gmail.com>
Sat, 12 Oct 2013 17:00:23 +0000 (19:00 +0200)
committerBartosz Dziewoński <matma.rex@gmail.com>
Tue, 19 Nov 2013 11:03:49 +0000 (12:03 +0100)
Added new helper function SpecialPage#requireLogin() to check if the
current user is logged in and, if not, format an error message linking
to Special:Userlogin and throw UserNotLoggedIn exception, to be
handled by OutputPage later.

Reused old error messages. Not all use the new parameter and they're
very inconsistent, but this is a matter for another patch.

Used it on 7 special pages. I don't think there are any other ones
which specifically require having an account, instead of just some
rights usually associated with logged-in users.

* SpecialChangeEmail
* SpecialChangePassword: It allows anonymous users under specific
  circumstances, but is logged-in-only in general.
* SpecialConfirmemail
* SpecialEditWatchlist
* SpecialPreferences
* SpecialResetTokens: It was missing the check, added it.
* SpecialWatchlist

Change-Id: I43ceaddb370d09784021b3fc2d5d1ff6616fef1f

12 files changed:
includes/SpecialPage.php
includes/specials/SpecialChangeEmail.php
includes/specials/SpecialChangePassword.php
includes/specials/SpecialConfirmemail.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialResetTokens.php
includes/specials/SpecialWatchlist.php
languages/messages/MessagesEn.php
languages/messages/MessagesQqq.php
maintenance/dictionary/mediawiki.dic
maintenance/language/messages.inc

index a6195fc..3aea4d9 100644 (file)
@@ -599,6 +599,49 @@ class SpecialPage {
                }
        }
 
+       /**
+        * If the user is not logged in, throws UserNotLoggedIn error.
+        *
+        * Default error message includes a link to Special:Userlogin with properly set 'returnto' query
+        * parameter.
+        *
+        * @since 1.23
+        * @param string|Message $reasonMsg [optional] Passed on to UserNotLoggedIn constructor. Strings
+        *     will be used as message keys. If a string is given, the message will also receive a
+        *     formatted login link (generated using the 'loginreqlink' message) as first parameter. If a
+        *     Message is given, it will be passed on verbatim.
+        * @param string|Message $titleMsg [optional] Passed on to UserNotLoggedIn constructor. Strings
+        *     will be used as message keys.
+        * @throws UserNotLoggedIn
+        */
+       public function requireLogin( $reasonMsg = null, $titleMsg = null ) {
+               if ( $this->getUser()->isAnon() ) {
+                       // Use default messages if not given or explicit null passed
+                       if ( !$reasonMsg ) {
+                               $reasonMsg = 'exception-nologin-text-manual';
+                       }
+                       if ( !$titleMsg ) {
+                               $titleMsg = 'exception-nologin';
+                       }
+
+                       // Convert to Messages with current context
+                       if ( is_string( $reasonMsg ) ) {
+                               $loginreqlink = Linker::linkKnown(
+                                       SpecialPage::getTitleFor( 'Userlogin' ),
+                                       $this->msg( 'loginreqlink' )->escaped(),
+                                       array(),
+                                       array( 'returnto' => $this->getTitle()->getPrefixedText() )
+                               );
+                               $reasonMsg = $this->msg( $reasonMsg )->rawParams( $loginreqlink );
+                       }
+                       if ( is_string( $titleMsg ) ) {
+                               $titleMsg = $this->msg( $titleMsg );
+                       }
+
+                       throw new UserNotLoggedIn( $reasonMsg, $titleMsg );
+               }
+       }
+
        /**
         * Sets headers - this should be called from the execute() method of all derived classes!
         */
index d02886f..e1531cc 100644 (file)
@@ -75,11 +75,7 @@ class SpecialChangeEmail extends UnlistedSpecialPage {
                $user = $this->getUser();
                $request = $this->getRequest();
 
-               if ( !$user->isLoggedIn() ) {
-                       $this->error( 'changeemail-no-info' );
-
-                       return;
-               }
+               $this->requireLogin( 'changeemail-no-info' );
 
                if ( $request->wasPosted() && $request->getBool( 'wpCancel' ) ) {
                        $this->doReturnTo();
@@ -92,7 +88,7 @@ class SpecialChangeEmail extends UnlistedSpecialPage {
 
                // This could also let someone check the current email address, so
                // require both permissions.
-               if ( !$this->getUser()->isAllowed( 'viewmyprivateinfo' ) ) {
+               if ( !$user->isAllowed( 'viewmyprivateinfo' ) ) {
                        throw new PermissionsError( 'viewmyprivateinfo' );
                }
 
index 7fcab19..8b96e6c 100644 (file)
@@ -45,6 +45,11 @@ class SpecialChangePassword extends UnlistedSpecialPage {
                $this->getOutput()->disallowUserJs();
 
                $request = $this->getRequest();
+
+               if ( !$request->wasPosted() ) {
+                       $this->requireLogin( 'resetpass-no-info' );
+               }
+
                $this->mUserName = trim( $request->getVal( 'wpName' ) );
                $this->mOldpass = $request->getVal( 'wpPassword' );
                $this->mNewpass = $request->getVal( 'wpNewPassword' );
@@ -52,11 +57,6 @@ class SpecialChangePassword extends UnlistedSpecialPage {
                $this->mDomain = $request->getVal( 'wpDomain' );
 
                $user = $this->getUser();
-               if ( !$request->wasPosted() && !$user->isLoggedIn() ) {
-                       $this->error( $this->msg( 'resetpass-no-info' )->text() );
-
-                       return;
-               }
 
                if ( $request->wasPosted() && $request->getBool( 'wpCancel' ) ) {
                        $titleObj = Title::newFromText( $request->getVal( 'returnto' ) );
index 6757990..bef155c 100644 (file)
@@ -45,6 +45,8 @@ class EmailConfirmation extends UnlistedSpecialPage {
                $this->checkReadOnly();
                $this->checkPermissions();
 
+               $this->requireLogin( 'confirmemail_needlogin' );
+
                // This could also let someone check the current email address, so
                // require both permissions.
                if ( !$this->getUser()->isAllowed( 'viewmyprivateinfo' ) ) {
@@ -52,22 +54,10 @@ class EmailConfirmation extends UnlistedSpecialPage {
                }
 
                if ( $code === null || $code === '' ) {
-                       if ( $this->getUser()->isLoggedIn() ) {
-                               if ( Sanitizer::validateEmail( $this->getUser()->getEmail() ) ) {
-                                       $this->showRequestForm();
-                               } else {
-                                       $this->getOutput()->addWikiMsg( 'confirmemail_noemail' );
-                               }
+                       if ( Sanitizer::validateEmail( $this->getUser()->getEmail() ) ) {
+                               $this->showRequestForm();
                        } else {
-                               $llink = Linker::linkKnown(
-                                       SpecialPage::getTitleFor( 'Userlogin' ),
-                                       $this->msg( 'loginreqlink' )->escaped(),
-                                       array(),
-                                       array( 'returnto' => $this->getTitle()->getPrefixedText() )
-                               );
-                               $this->getOutput()->addHTML(
-                                       $this->msg( 'confirmemail_needlogin' )->rawParams( $llink )->parse()
-                               );
+                               $this->getOutput()->addWikiMsg( 'confirmemail_noemail' );
                        }
                } else {
                        $this->attemptConfirm( $code );
index ca2daaf..e085240 100644 (file)
@@ -61,21 +61,10 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
        public function execute( $mode ) {
                $this->setHeaders();
 
-               $out = $this->getOutput();
-
                # Anons don't get a watchlist
-               if ( $this->getUser()->isAnon() ) {
-                       $out->setPageTitle( $this->msg( 'watchnologin' ) );
-                       $llink = Linker::linkKnown(
-                               SpecialPage::getTitleFor( 'Userlogin' ),
-                               $this->msg( 'loginreqlink' )->escaped(),
-                               array(),
-                               array( 'returnto' => $this->getTitle()->getPrefixedText() )
-                       );
-                       $out->addHTML( $this->msg( 'watchlistanontext' )->rawParams( $llink )->parse() );
+               $this->requireLogin( 'watchlistanontext', 'watchnologin' );
 
-                       return;
-               }
+               $out = $this->getOutput();
 
                $this->checkPermissions();
                $this->checkReadOnly();
index ecee0bb..5f4b208 100644 (file)
@@ -37,14 +37,7 @@ class SpecialPreferences extends SpecialPage {
                $out = $this->getOutput();
                $out->disallowUserJs(); # Prevent hijacked user scripts from sniffing passwords etc.
 
-               $user = $this->getUser();
-               if ( $user->isAnon() ) {
-                       throw new ErrorPageError(
-                               'prefsnologin',
-                               'prefsnologintext',
-                               array( $this->getTitle()->getPrefixedDBkey() )
-                       );
-               }
+               $this->requireLogin( 'prefsnologintext2', 'prefsnologin' );
                $this->checkReadOnly();
 
                if ( $par == 'reset' ) {
@@ -62,7 +55,7 @@ class SpecialPreferences extends SpecialPage {
                        );
                }
 
-               $htmlForm = Preferences::getFormObject( $user, $this->getContext() );
+               $htmlForm = Preferences::getFormObject( $this->getUser(), $this->getContext() );
                $htmlForm->setSubmitCallback( array( 'Preferences', 'tryUISubmit' ) );
 
                $htmlForm->show();
index ef2a45d..db98bea 100644 (file)
@@ -61,6 +61,7 @@ class SpecialResetTokens extends FormSpecialPage {
        public function execute( $par ) {
                // This is a preferences page, so no user JS for y'all.
                $this->getOutput()->disallowUserJs();
+               $this->requireLogin();
 
                parent::execute( $par );
 
index 336b05b..78216ca 100644 (file)
@@ -41,18 +41,7 @@ class SpecialWatchlist extends SpecialPage {
                $output = $this->getOutput();
 
                # Anons don't get a watchlist
-               if ( $user->isAnon() ) {
-                       $output->setPageTitle( $this->msg( 'watchnologin' ) );
-                       $output->setRobotPolicy( 'noindex,nofollow' );
-                       $llink = Linker::linkKnown(
-                               SpecialPage::getTitleFor( 'Userlogin' ),
-                               $this->msg( 'loginreqlink' )->escaped(),
-                               array(),
-                               array( 'returnto' => $this->getTitle()->getPrefixedText() )
-                       );
-                       $output->addHTML( $this->msg( 'watchlistanontext' )->rawParams( $llink )->parse() );
-                       return;
-               }
+               $this->requireLogin( 'watchlistanontext', 'watchnologin' );
 
                // Check permissions
                $this->checkPermissions();
index 39f0b8a..1ef2d5b 100644 (file)
@@ -1085,7 +1085,8 @@ The administrator who locked it offered this explanation: "$3".',
 'invalidtitle-knownnamespace'   => 'Invalid title with namespace "$2" and text "$3"',
 'invalidtitle-unknownnamespace' => 'Invalid title with unknown namespace number $1 and text "$2"',
 'exception-nologin'             => 'Not logged in',
-'exception-nologin-text'        => 'This page or action requires you to be logged in on this wiki.',
+'exception-nologin-text'        => 'Please [[Special:Userlogin|log in]] to be able to access this page or action.',
+'exception-nologin-text-manual' => 'Please $1 to be able to access this page or action.',
 
 # Virus scanner
 'virus-badscanner'     => "Bad configuration: Unknown virus scanner: ''$1''",
@@ -1899,7 +1900,7 @@ Note that their indexes of {{SITENAME}} content may be out of date.',
 'mypreferences'                 => 'Preferences',
 'prefs-edits'                   => 'Number of edits:',
 'prefsnologin'                  => 'Not logged in',
-'prefsnologintext'              => 'You must be <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} logged in]</span> to set user preferences.',
+'prefsnologintext2'             => 'Please $1 to set user preferences.',
 'changepassword'                => 'Change password',
 'changepassword-summary'        => '', # do not translate or duplicate this message to other languages
 'prefs-skin'                    => 'Skin',
index 14a9d3b..032fff2 100644 (file)
@@ -1222,6 +1222,10 @@ Parameters:
 'exception-nologin' => 'Generic page title used on error page when a user is not logged in. Message used by the UserNotLoggedIn exception.
 {{Identical|Not logged in}}',
 'exception-nologin-text' => 'Generic reason displayed on error page when a user is not logged in. Message used by the UserNotLoggedIn exception.',
+'exception-nologin-text-manual' => 'Generic reason displayed on error page when a user is not logged in.
+
+Parameters:
+* $1 - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description',
 
 # Virus scanner
 'virus-badscanner' => 'Used as error message. Parameters:
@@ -2963,8 +2967,8 @@ See also:
 {{Identical|Preferences}}',
 'prefs-edits' => 'In user preferences.',
 'prefsnologin' => '{{Identical|Not logged in}}',
-'prefsnologintext' => 'Parameters:
-* $1 - URI for "returnto" argument',
+'prefsnologintext2' => 'Parameters:
+* $1 - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description',
 'changepassword' => "Section heading on [[Special:Preferences]], tab 'User profile'.
 {{Identical|Change password}}",
 'prefs-skin' => 'Used in user preferences.
index 164b5b0..b6ca281 100644 (file)
@@ -3127,7 +3127,7 @@ prefixsearchdisabled
 prefs
 prefsection
 prefsnologin
-prefsnologintext
+prefsnologintext2
 prefsubmit
 preload
 preloads
index 1ac9500..c149ec6 100644 (file)
@@ -434,6 +434,7 @@ $wgMessageStructure = array(
                'invalidtitle-unknownnamespace',
                'exception-nologin',
                'exception-nologin-text',
+               'exception-nologin-text-manual',
        ),
        'virus' => array(
                'virus-badscanner',
@@ -1028,7 +1029,7 @@ $wgMessageStructure = array(
                'mypreferences',
                'prefs-edits',
                'prefsnologin',
-               'prefsnologintext',
+               'prefsnologintext2',
                'changepassword',
                'changepassword-summary',
                'prefs-skin',