Anomie has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/67876


Change subject: Add 'viewmyprivateinfo', 'editmyprivateinfo', and 
'editmyoptions' rights
......................................................................

Add 'viewmyprivateinfo', 'editmyprivateinfo', and 'editmyoptions' rights

These are needed for OAuth grants.

Note that we don't bother with a 'viewmyoptions' right, since the
majority will be determinable from just observing the interface.

Note that the fact of having a confirmed email address cannot be
reliably hidden, and if the user has 'sendemail' they may be able to
determine the real name and email address by sending an email to another
account that they control.

Change-Id: I3f03dd010020e8d43cc2d3bca7b3ef7196d1c548
---
M RELEASE-NOTES-1.22
M includes/Autopromote.php
M includes/DefaultSettings.php
M includes/Preferences.php
M includes/User.php
M includes/UserMailer.php
M includes/actions/WatchAction.php
M includes/api/ApiOptions.php
M includes/api/ApiQueryUserInfo.php
M includes/specials/SpecialChangeEmail.php
M includes/specials/SpecialChangePassword.php
M includes/specials/SpecialConfirmemail.php
M includes/specials/SpecialPasswordReset.php
M includes/specials/SpecialPreferences.php
M includes/specials/SpecialWatchlist.php
M languages/messages/MessagesEn.php
M languages/messages/MessagesQqq.php
M maintenance/dictionary/mediawiki.dic
M maintenance/language/messages.inc
19 files changed, 214 insertions(+), 71 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/76/67876/1

diff --git a/RELEASE-NOTES-1.22 b/RELEASE-NOTES-1.22
index d9f1e9c..a2df81d 100644
--- a/RELEASE-NOTES-1.22
+++ b/RELEASE-NOTES-1.22
@@ -32,8 +32,9 @@
   default for $wgLogAutopatrol is true.
 * The 'edit' right no longer allows for editing a user's own CSS and JS.
 * New rights 'editmyusercss', 'editmyuserjs', 'viewmywatchlist',
-  and 'editmywatchlist' restrict actions that were formerly allowed by default.
-  They have been added to the default for $wgGroupPermissions['*'].
+  'editmywatchlist', 'viewmyprivateinfo', 'editmyprivateinfo', and
+  'editmyoptions' restrict actions that were formerly allowed by default. They
+  have been added to the default for $wgGroupPermissions['*'].
 
 === New features in 1.22 ===
 * (bug 44525) mediawiki.jqueryMsg can now parse (whitelisted) HTML elements 
and attributes.
@@ -111,6 +112,11 @@
 ** editmyuserjs controls whether a user may edit their own JS subpages.
 ** viewmywatchlist controls whether a user may view their watchlist.
 ** editmywatchlist controls whether a user may edit their watchlist.
+** viewmyprivateinfo controls whether a user may access their private
+   information (e.g. registered email address, real name).
+** editmyprivateinfo controls whether a user may change their private
+   information.
+** editmyoptions controls whether a user may change their preferences.
 
 === Bug fixes in 1.22 ===
 * Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
diff --git a/includes/Autopromote.php b/includes/Autopromote.php
index ec9dcf5..e7c3e22 100644
--- a/includes/Autopromote.php
+++ b/includes/Autopromote.php
@@ -167,9 +167,9 @@
 
                switch ( $cond[0] ) {
                        case APCOND_EMAILCONFIRMED:
-                               if ( Sanitizer::validateEmail( 
$user->getEmail() ) ) {
+                               if ( Sanitizer::validateEmail( 
$user->getEmailInternal() ) ) {
                                        if ( $wgEmailAuthentication ) {
-                                               return 
(bool)$user->getEmailAuthenticationTimestamp();
+                                               return 
(bool)$user->getEmailAuthenticationTimestampInternal();
                                        } else {
                                                return true;
                                        }
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 652dfc2..273451d 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -3892,6 +3892,9 @@
 $wgGroupPermissions['*']['editmyuserjs'] = true;
 $wgGroupPermissions['*']['viewmywatchlist'] = true;
 $wgGroupPermissions['*']['editmywatchlist'] = true;
+$wgGroupPermissions['*']['viewmyprivateinfo'] = true;
+$wgGroupPermissions['*']['editmyprivateinfo'] = true;
+$wgGroupPermissions['*']['editmyoptions'] = true;
 #$wgGroupPermissions['*']['patrolmarks'] = false; // let anons see what was 
patrolled
 
 // Implicit group for all logged-in accounts
diff --git a/includes/Preferences.php b/includes/Preferences.php
index 848cd32..c751efe 100644
--- a/includes/Preferences.php
+++ b/includes/Preferences.php
@@ -56,6 +56,12 @@
                        'searchlimit' => array( 'Preferences', 'filterIntval' ),
        );
 
+       // Stuff that shouldn't be saved as a preference.
+       private static $saveBlacklist = array(
+               'realname',
+               'emailaddress',
+       );
+
        /**
         * @throws MWException
         * @param $user User
@@ -93,9 +99,14 @@
                ## Make sure that form fields have their parent set. See bug 
41337.
                $dummyForm = new HTMLForm( array(), $context );
 
+               $disable = !$user->isAllowed( 'editmyoptions' );
+
                ## Prod in defaults from the user
                foreach ( $defaultPreferences as $name => &$info ) {
                        $prefFromUser = self::getOptionFromUser( $name, $info, 
$user );
+                       if ( $disable && !in_array( $name, self::$saveBlacklist 
) ) {
+                               $info['disabled'] = 'disabled';
+                       }
                        $field = HTMLForm::loadInputFromParameters( $name, 
$info ); // For validation
                        $field->mParent = $dummyForm;
                        $defaultOptions = User::getDefaultOptions();
@@ -256,14 +267,19 @@
                        );
                }
 
+               $canViewPrivateInfo = $user->isAllowed( 'viewmyprivateinfo' );
+               $canEditPrivateInfo = $user->isAllowed( 'editmyprivateinfo' );
+
                // Actually changeable stuff
-               $defaultPreferences['realname'] = array(
-                       'type' => $wgAuth->allowPropChange( 'realname' ) ? 
'text' : 'info',
-                       'default' => $user->getRealName(),
-                       'section' => 'personal/info',
-                       'label-message' => 'yourrealname',
-                       'help-message' => 'prefs-help-realname',
-               );
+               if ( $canViewPrivateInfo ) {
+                       $defaultPreferences['realname'] = array(
+                               'type' => $canEditPrivateInfo && 
$wgAuth->allowPropChange( 'realname' ) ? 'text' : 'info',
+                               'default' => $user->getRealName(),
+                               'section' => 'personal/info',
+                               'label-message' => 'yourrealname',
+                               'help-message' => 'prefs-help-realname',
+                       );
+               }
 
                $defaultPreferences['gender'] = array(
                        'type' => 'select',
@@ -277,7 +293,7 @@
                        'help-message' => 'prefs-help-gender',
                );
 
-               if ( $wgAuth->allowPasswordChange() ) {
+               if ( $canEditPrivateInfo && $wgAuth->allowPasswordChange() ) {
                        $link = Linker::link( SpecialPage::getTitleFor( 
'ChangePassword' ),
                                $context->msg( 'prefs-resetpass' )->escaped(), 
array(),
                                array( 'returnto' => SpecialPage::getTitleFor( 
'Preferences' )->getPrefixedText() ) );
@@ -398,22 +414,24 @@
                                array( 'returnto' => SpecialPage::getTitleFor( 
'Preferences' )->getPrefixedText() ) );
 
                        $emailAddress = $user->getEmail() ? htmlspecialchars( 
$user->getEmail() ) : '';
-                       if ( $wgAuth->allowPropChange( 'emailaddress' ) ) {
+                       if ( $canEditPrivateInfo && $wgAuth->allowPropChange( 
'emailaddress' ) ) {
                                $emailAddress .= $emailAddress == '' ? $link : (
                                        $context->msg( 'word-separator' 
)->plain()
                                        . $context->msg( 'parentheses' 
)->rawParams( $link )->plain()
                                );
                        }
 
-                       $defaultPreferences['emailaddress'] = array(
-                               'type' => 'info',
-                               'raw' => true,
-                               'default' => $emailAddress,
-                               'label-message' => 'youremail',
-                               'section' => 'personal/email',
-                               'help-messages' => $helpMessages,
-                               # 'cssclass' chosen below
-                       );
+                       if ( $canViewPrivateInfo ) {
+                               $defaultPreferences['emailaddress'] = array(
+                                       'type' => 'info',
+                                       'raw' => true,
+                                       'default' => $emailAddress,
+                                       'label-message' => 'youremail',
+                                       'section' => 'personal/email',
+                                       'help-messages' => $helpMessages,
+                                       # 'cssclass' chosen below
+                               );
+                       }
 
                        $disableEmailPrefs = false;
 
@@ -457,7 +475,9 @@
                                        # Apply the same CSS class used on the 
input to the message:
                                        'cssclass' => $emailauthenticationclass,
                                );
-                               $defaultPreferences['emailaddress']['cssclass'] 
= $emailauthenticationclass;
+                               if ( isset( $defaultPreferences['emailaddress'] 
) ) {
+                                       
$defaultPreferences['emailaddress']['cssclass'] = $emailauthenticationclass;
+                               }
                        }
 
                        if ( $wgEnableUserEmail && $user->isAllowed( 
'sendemail' ) ) {
@@ -1389,6 +1409,10 @@
                $user = $form->getModifiedUser();
                $result = true;
 
+               if ( !$user->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' 
) ) {
+                       return Status::newFatal( 'mypreferencesprotected' );
+               }
+
                // Filter input
                foreach ( array_keys( $formData ) as $name ) {
                        if ( isset( self::$saveFilters[$name] ) ) {
@@ -1397,40 +1421,36 @@
                        }
                }
 
-               // Stuff that shouldn't be saved as a preference.
-               $saveBlacklist = array(
-                       'realname',
-                       'emailaddress',
-               );
-
                // Fortunately, the realname field is MUCH simpler
                if ( !in_array( 'realname', $wgHiddenPrefs ) ) {
                        $realName = $formData['realname'];
                        $user->setRealName( $realName );
                }
 
-               foreach ( $saveBlacklist as $b ) {
-                       unset( $formData[$b] );
+               if ( $user->isAllowed( 'editmyoptions' ) ) {
+                       foreach ( self::$saveBlacklist as $b ) {
+                               unset( $formData[$b] );
+                       }
+
+                       # If users have saved a value for a preference which 
has subsequently been disabled
+                       # via $wgHiddenPrefs, we don't want to destroy that 
setting in case the preference
+                       # is subsequently re-enabled
+                       # TODO: maintenance script to actually delete these
+                       foreach ( $wgHiddenPrefs as $pref ) {
+                               # If the user has not set a non-default value 
here, the default will be returned
+                               # and subsequently discarded
+                               $formData[$pref] = $user->getOption( $pref, 
null, true );
+                       }
+
+                       // Keep old preferences from interfering due to 
back-compat code, etc.
+                       $user->resetOptions( 'unused', $form->getContext() );
+
+                       foreach ( $formData as $key => $value ) {
+                               $user->setOption( $key, $value );
+                       }
+
+                       $user->saveSettings();
                }
-
-               # If users have saved a value for a preference which has 
subsequently been disabled
-               # via $wgHiddenPrefs, we don't want to destroy that setting in 
case the preference
-               # is subsequently re-enabled
-               # TODO: maintenance script to actually delete these
-               foreach ( $wgHiddenPrefs as $pref ) {
-                       # If the user has not set a non-default value here, the 
default will be returned
-                       # and subsequently discarded
-                       $formData[$pref] = $user->getOption( $pref, null, true 
);
-               }
-
-               // Keep old preferences from interfering due to back-compat 
code, etc.
-               $user->resetOptions( 'unused', $form->getContext() );
-
-               foreach ( $formData as $key => $value ) {
-                       $user->setOption( $key, $value );
-               }
-
-               $user->saveSettings();
 
                $wgAuth->updateExternalDB( $user );
 
@@ -1554,13 +1574,19 @@
         * @return String
         */
        function getButtons() {
+               if ( !$this->getModifiedUser()->isAllowedAny( 
'editmyprivateinfo', 'editmyoptions' ) ) {
+                       return '';
+               }
+
                $html = parent::getButtons();
 
-               $t = SpecialPage::getTitleFor( 'Preferences', 'reset' );
+               if ( $this->getModifiedUser()->isAllowed( 'editmyoptions' ) ) {
+                       $t = SpecialPage::getTitleFor( 'Preferences', 'reset' );
 
-               $html .= "\n" . Linker::link( $t, $this->msg( 'restoreprefs' 
)->escaped() );
+                       $html .= "\n" . Linker::link( $t, $this->msg( 
'restoreprefs' )->escaped() );
 
-               $html = Xml::tags( 'div', array( 'class' => 'mw-prefs-buttons' 
), $html );
+                       $html = Xml::tags( 'div', array( 'class' => 
'mw-prefs-buttons' ), $html );
+               }
 
                return $html;
        }
diff --git a/includes/User.php b/includes/User.php
index 8ce44a8..3103f36 100644
--- a/includes/User.php
+++ b/includes/User.php
@@ -124,6 +124,8 @@
                'edit',
                'editinterface',
                'editprotected',
+               'editmyoptions',
+               'editmyprivateinfo',
                'editmyusercss',
                'editmyuserjs',
                'editmywatchlist',
@@ -167,6 +169,7 @@
                'upload_by_url',
                'userrights',
                'userrights-interwiki',
+               'viewmyprivateinfo',
                'viewmywatchlist',
                'writeapi',
        );
@@ -1785,6 +1788,10 @@
         * @return bool True if the user has new messages
         */
        public function getNewtalk() {
+               if ( !$this->isAllowed( 'viewmyprivateinfo' ) ) {
+                       return false;
+               }
+
                $this->load();
 
                // Load the newtalk status if it is unloaded (mNewtalk=-1)
@@ -1827,6 +1834,10 @@
         * @return Array
         */
        public function getNewMessageLinks() {
+               if ( !$this->isAllowed( 'viewmyprivateinfo' ) ) {
+                       return array();
+               }
+
                $talks = array();
                if ( !wfRunHooks( 'UserRetrieveNewTalks', array( &$this, 
&$talks ) ) ) {
                        return $talks;
@@ -1938,7 +1949,9 @@
         * @param $curRev Revision new, as yet unseen revision of the user talk 
page. Ignored if null or !$val.
         */
        public function setNewtalk( $val, $curRev = null ) {
-               if ( wfReadOnly() ) {
+               // Not editmyprivateinfo! People will not want to have to grant
+               // editmyprivateinfo just to reset the indicator.
+               if ( wfReadOnly() || !$this->isAllowed( 'viewmyprivateinfo' ) ) 
{
                        return;
                }
 
@@ -2179,6 +2192,18 @@
         * @return string User's email address
         */
        public function getEmail() {
+               if ( !$this->isAllowed( 'viewmyprivateinfo' ) ) {
+                       return '';
+               }
+               return $this->getEmailInternal();
+       }
+
+       /**
+        * Get the user's e-mail address, without permission checks
+        * @since 1.22
+        * @return string User's email address
+        */
+       public function getEmailInternal() {
                $this->load();
                wfRunHooks( 'UserGetEmail', array( $this, &$this->mEmail ) );
                return $this->mEmail;
@@ -2189,6 +2214,18 @@
         * @return string TS_MW timestamp
         */
        public function getEmailAuthenticationTimestamp() {
+               if ( !$this->isAllowed( 'viewmyprivateinfo' ) ) {
+                       return null;
+               }
+               return $this->getEmailAuthenticationTimestampInternal();
+       }
+
+       /**
+        * Get the timestamp of the user's e-mail authentication, without 
permission checks
+        * @since 1.22
+        * @return string TS_MW timestamp
+        */
+       public function getEmailAuthenticationTimestampInternal() {
                $this->load();
                wfRunHooks( 'UserGetEmailAuthenticationTimestamp', array( 
$this, &$this->mEmailAuthenticated ) );
                return $this->mEmailAuthenticated;
@@ -2222,6 +2259,10 @@
                        return Status::newFatal( 'emaildisabled' );
                }
 
+               if ( !$this->isAllowed( 'editmyprivateinfo' ) ) {
+                       return Status::newFatal( 'myprivateinfoprotected' );
+               }
+
                $oldaddr = $this->getEmail();
                if ( $str === $oldaddr ) {
                        return Status::newGood( true );
@@ -2249,6 +2290,18 @@
         * @return string User's real name
         */
        public function getRealName() {
+               if ( !$this->isAllowed( 'viewmyprivateinfo' ) ) {
+                       return '';
+               }
+               return $this->getRealNameInternal();
+       }
+
+       /**
+        * Get the user's real name, without permission checks
+        * @since 1.22
+        * @return string User's real name
+        */
+       public function getRealNameInternal() {
                if ( !$this->isItemLoaded( 'realname' ) ) {
                        $this->load();
                }
@@ -2261,8 +2314,10 @@
         * @param string $str New real name
         */
        public function setRealName( $str ) {
-               $this->load();
-               $this->mRealName = $str;
+               if ( $this->isAllowed( 'editmyprivateinfo' ) ) {
+                       $this->load();
+                       $this->mRealName = $str;
+               }
        }
 
        /**
@@ -2485,6 +2540,10 @@
                $resetKinds = array( 'registered', 'registered-multiselect', 
'registered-checkmatrix', 'unused' ),
                IContextSource $context = null
        ) {
+               if ( !$this->isAllowed( 'editmyoptions' ) ) {
+                       return;
+               }
+
                $this->load();
                $defaultOptions = self::getDefaultOptions();
 
@@ -3761,6 +3820,10 @@
         * @return bool
         */
        public function confirmEmail() {
+               if ( !$this->isAllowed( 'editmyprivateinfo' ) ) {
+                       return false;
+               }
+
                // Check if it's already confirmed, so we don't touch the 
database
                // and fire the ConfirmEmailComplete hook on redundant 
confirmations.
                if ( !$this->isEmailConfirmed() ) {
@@ -3778,6 +3841,10 @@
         * @return bool Returns true
         */
        function invalidateEmail() {
+               if ( !$this->isAllowed( 'editmyprivateinfo' ) ) {
+                       return false;
+               }
+
                $this->load();
                $this->mEmailToken = null;
                $this->mEmailTokenExpires = null;
@@ -3791,6 +3858,10 @@
         * @param string $timestamp TS_MW timestamp
         */
        function setEmailAuthenticationTimestamp( $timestamp ) {
+               if ( !$this->isAllowed( 'editmyprivateinfo' ) ) {
+                       return;
+               }
+
                $this->load();
                $this->mEmailAuthenticated = $timestamp;
                wfRunHooks( 'UserSetEmailAuthenticationTimestamp', array( 
$this, &$this->mEmailAuthenticated ) );
@@ -4478,6 +4549,10 @@
         * @todo document
         */
        protected function saveOptions() {
+               if ( !$this->isAllowed( 'editmyoptions' ) ) {
+                       return false;
+               }
+
                $this->loadOptions();
 
                // Not using getOptions(), to keep hidden preferences in 
database
diff --git a/includes/UserMailer.php b/includes/UserMailer.php
index e48070a..540ceef 100644
--- a/includes/UserMailer.php
+++ b/includes/UserMailer.php
@@ -37,9 +37,9 @@
         */
        function __construct( $address, $name = null, $realName = null ) {
                if ( is_object( $address ) && $address instanceof User ) {
-                       $this->address = $address->getEmail();
+                       $this->address = $address->getEmailInternal();
                        $this->name = $address->getName();
-                       $this->realName = $address->getRealName();
+                       $this->realName = $address->getRealNameInternal();
                } else {
                        $this->address = strval( $address );
                        $this->name = strval( $name );
@@ -737,7 +737,7 @@
                        $keys['$PAGEEDITOR_EMAIL'] = wfMessage( 'noemailtitle' 
)->inContentLanguage()->text();
 
                } else {
-                       $keys['$PAGEEDITOR'] = $wgEnotifUseRealName ? 
$this->editor->getRealName() : $this->editor->getName();
+                       $keys['$PAGEEDITOR'] = $wgEnotifUseRealName ? 
$this->editor->getRealNameInternal() : $this->editor->getName();
                        $emailPage = SpecialPage::getSafeTitleFor( 'Emailuser', 
$this->editor->getName() );
                        $keys['$PAGEEDITOR_EMAIL'] = 
$emailPage->getCanonicalURL();
                }
@@ -765,7 +765,7 @@
                # global configuration level.
                $adminAddress = new MailAddress( $wgPasswordSender, 
$wgPasswordSenderName );
                if ( $wgEnotifRevealEditorAddress
-                       && ( $this->editor->getEmail() != '' )
+                       && ( $this->editor->getEmailInternal() != '' )
                        && $this->editor->getOption( 'enotifrevealaddr' ) )
                {
                        $editorAddress = new MailAddress( $this->editor );
@@ -835,7 +835,7 @@
                        array( '$WATCHINGUSERNAME',
                                '$PAGEEDITDATE',
                                '$PAGEEDITTIME' ),
-                       array( $wgEnotifUseRealName ? 
$watchingUser->getRealName() : $watchingUser->getName(),
+                       array( $wgEnotifUseRealName ? 
$watchingUser->getRealNameInternal() : $watchingUser->getName(),
                                $wgContLang->userDate( $this->timestamp, 
$watchingUser ),
                                $wgContLang->userTime( $this->timestamp, 
$watchingUser ) ),
                        $this->body );
diff --git a/includes/actions/WatchAction.php b/includes/actions/WatchAction.php
index 8382eaf..6b05b92 100644
--- a/includes/actions/WatchAction.php
+++ b/includes/actions/WatchAction.php
@@ -95,7 +95,7 @@
         * @param User $user User who is watching/unwatching
         */
        public static function doWatchOrUnwatch( $watch, Title $title, User 
$user ) {
-               if ( $user->isLoggedIn() && $user->getWatchedItem( $title, true 
)->isWatched() != $watch ) {
+               if ( $user->isLoggedIn() && $user->isWatched( $title, 
WatchedItem::IGNORE_USER_RIGHTS ) != $watch ) {
                        // If the user doesn't have 'editmywatchlist', we still 
want to
                        // allow them to add but not remove items via edits and 
such.
                        if ( $watch ) {
diff --git a/includes/api/ApiOptions.php b/includes/api/ApiOptions.php
index 8c996a2..720025f 100644
--- a/includes/api/ApiOptions.php
+++ b/includes/api/ApiOptions.php
@@ -42,6 +42,10 @@
                        $this->dieUsage( 'Anonymous users cannot change 
preferences', 'notloggedin' );
                }
 
+               if ( !$user->isAllowed( 'editmyoptions' ) ) {
+                       $this->dieUsage( 'You don\'t have permission to edit 
your options', 'permissiondenied' );
+               }
+
                $params = $this->extractRequestParams();
                $changed = false;
 
diff --git a/includes/api/ApiQueryUserInfo.php 
b/includes/api/ApiQueryUserInfo.php
index 8e65b40..989521a 100644
--- a/includes/api/ApiQueryUserInfo.php
+++ b/includes/api/ApiQueryUserInfo.php
@@ -104,7 +104,8 @@
                }
 
                if ( isset( $this->prop['preferencestoken'] ) &&
-                       is_null( $this->getMain()->getRequest()->getVal( 
'callback' ) )
+                       is_null( $this->getMain()->getRequest()->getVal( 
'callback' ) ) &&
+                       $user->isAllowed( 'editmyoptions' )
                ) {
                        $vals['preferencestoken'] = $user->getEditToken( '', 
$this->getMain()->getRequest() );
                }
diff --git a/includes/specials/SpecialChangeEmail.php 
b/includes/specials/SpecialChangeEmail.php
index 9e435fb..8b7dd49 100644
--- a/includes/specials/SpecialChangeEmail.php
+++ b/includes/specials/SpecialChangeEmail.php
@@ -41,7 +41,7 @@
        protected $mNewEmail;
 
        public function __construct() {
-               parent::__construct( 'ChangeEmail' );
+               parent::__construct( 'ChangeEmail', 'editmyprivateinfo' );
        }
 
        /**
diff --git a/includes/specials/SpecialChangePassword.php 
b/includes/specials/SpecialChangePassword.php
index b53a46a..81999f6 100644
--- a/includes/specials/SpecialChangePassword.php
+++ b/includes/specials/SpecialChangePassword.php
@@ -31,7 +31,7 @@
        protected $mUserName, $mOldpass, $mNewpass, $mRetype, $mDomain;
 
        public function __construct() {
-               parent::__construct( 'ChangePassword' );
+               parent::__construct( 'ChangePassword', 'editmyprivateinfo' );
        }
 
        /**
diff --git a/includes/specials/SpecialConfirmemail.php 
b/includes/specials/SpecialConfirmemail.php
index 47808d1..0c3f0b1 100644
--- a/includes/specials/SpecialConfirmemail.php
+++ b/includes/specials/SpecialConfirmemail.php
@@ -31,7 +31,7 @@
  */
 class EmailConfirmation extends UnlistedSpecialPage {
        public function __construct() {
-               parent::__construct( 'Confirmemail' );
+               parent::__construct( 'Confirmemail', 'editmyprivateinfo' );
        }
 
        /**
@@ -149,7 +149,7 @@
  */
 class EmailInvalidation extends UnlistedSpecialPage {
        public function __construct() {
-               parent::__construct( 'Invalidateemail' );
+               parent::__construct( 'Invalidateemail', 'editmyprivateinfo' );
        }
 
        function execute( $code ) {
diff --git a/includes/specials/SpecialPasswordReset.php 
b/includes/specials/SpecialPasswordReset.php
index 3b67554..69c4056 100644
--- a/includes/specials/SpecialPasswordReset.php
+++ b/includes/specials/SpecialPasswordReset.php
@@ -43,7 +43,7 @@
        private $result;
 
        public function __construct() {
-               parent::__construct( 'PasswordReset' );
+               parent::__construct( 'PasswordReset', 'editmyprivateinfo' );
        }
 
        public function userCanExecute( User $user ) {
diff --git a/includes/specials/SpecialPreferences.php 
b/includes/specials/SpecialPreferences.php
index bb6bc95..fe91ada 100644
--- a/includes/specials/SpecialPreferences.php
+++ b/includes/specials/SpecialPreferences.php
@@ -69,6 +69,10 @@
        }
 
        private function showResetForm() {
+               if ( !$this->getUser()->isAllowed( 'editmyoptions' ) ) {
+                       throw new PermissionsError( 'editmyoptions' );
+               }
+
                $this->getOutput()->addWikiMsg( 'prefs-reset-intro' );
 
                $htmlForm = new HTMLForm( array(), $this->getContext(), 
'prefs-restore' );
@@ -82,6 +86,10 @@
        }
 
        public function submitReset( $formData ) {
+               if ( !$this->getUser()->isAllowed( 'editmyoptions' ) ) {
+                       throw new PermissionsError( 'editmyoptions' );
+               }
+
                $user = $this->getUser();
                $user->resetOptions( 'all' );
                $user->saveSettings();
diff --git a/includes/specials/SpecialWatchlist.php 
b/includes/specials/SpecialWatchlist.php
index 428c470..9741329 100644
--- a/includes/specials/SpecialWatchlist.php
+++ b/includes/specials/SpecialWatchlist.php
@@ -56,14 +56,16 @@
 
                // Add feed links
                $wlToken = $user->getOption( 'watchlisttoken' );
-               if ( !$wlToken ) {
+               if ( !$wlToken && $user->isAllowed( 'editmyoptions' ) ) {
                        $wlToken = MWCryptRand::generateHex( 40 );
                        $user->setOption( 'watchlisttoken', $wlToken );
                        $user->saveSettings();
                }
 
-               $this->addFeedLinks( array( 'action' => 'feedwatchlist', 
'allrev' => 'allrev',
-                                                       'wlowner' => 
$user->getName(), 'wltoken' => $wlToken ) );
+               if ( $wlToken ) {
+                       $this->addFeedLinks( array( 'action' => 
'feedwatchlist', 'allrev' => 'allrev',
+                                                               'wlowner' => 
$user->getName(), 'wltoken' => $wlToken ) );
+               }
 
                $this->setHeaders();
                $this->outputHeader();
diff --git a/languages/messages/MessagesEn.php 
b/languages/messages/MessagesEn.php
index f03e2ee..ec502ca 100644
--- a/languages/messages/MessagesEn.php
+++ b/languages/messages/MessagesEn.php
@@ -1072,6 +1072,8 @@
 'customjsprotected'             => "You do not have permission to edit this 
JavaScript page because it contains another user's personal settings.",
 'mycustomcssprotected'          => "You do not have permission to edit this 
CSS page.",
 'mycustomjsprotected'           => "You do not have permission to edit this 
JavaScript page.",
+'myprivateinfoprotected'        => "You do not have permission to edit your 
private information.",
+'mypreferencesprotected'        => "You do not have permission to edit your 
preferences.",
 'ns-specialprotected'           => 'Special pages cannot be edited.',
 'titleprotected'                => 'This title has been protected from 
creation by [[User:$1|$1]].
 The reason given is "\'\'$2\'\'".',
@@ -2098,6 +2100,9 @@
 'right-editmyuserjs'          => "Edit your own user JavaScript files",
 'right-viewmywatchlist'       => "View your own watchlist",
 'right-editmywatchlist'       => "Edit your own watchlist. Note some actions 
will still add pages even without this right.",
+'right-viewmyprivateinfo'     => "View your own private data (e.g. email 
address, real name)",
+'right-editmyprivateinfo'     => "Edit your own private data (e.g. email 
address, real name)",
+'right-editmyoptions'         => "Edit your own preferences",
 'right-rollback'              => 'Quickly rollback the edits of the last user 
who edited a particular page',
 'right-markbotedits'          => 'Mark rolled-back edits as bot edits',
 'right-noratelimit'           => 'Not be affected by rate limits',
diff --git a/languages/messages/MessagesQqq.php 
b/languages/messages/MessagesQqq.php
index fe09960..15eb944 100644
--- a/languages/messages/MessagesQqq.php
+++ b/languages/messages/MessagesQqq.php
@@ -1027,6 +1027,8 @@
 'customjsprotected' => 'Used as error message.',
 'mycustomcssprotected' => 'Used as error message.',
 'mycustomjsprotected' => 'Used as error message.',
+'myprivateinfoprotected' => 'Used as error message.',
+'mypreferencesprotected' => 'Used as error message.',
 'ns-specialprotected' => 'Error message displayed when trying to edit a page 
in the Special namespace',
 'titleprotected' => 'Use $1 for GENDER.',
 'filereadonlyerror' => 'Parameters:
@@ -2909,6 +2911,9 @@
 'right-editmyuserjs' => '{{doc-right|editmyuserjs}}',
 'right-viewmywatchlist' => '{{doc-right|viewmywatchlist}}',
 'right-editmywatchlist' => '{{doc-right|editmywatchlist}}',
+'right-viewmyprivateinfo' => '{{doc-right|viewmyprivateinfo}}',
+'right-editmyprivateinfo' => '{{doc-right|editmyprivateinfo}}',
+'right-editmyoptions' => '{{doc-right|editmyoptions}}',
 'right-rollback' => '{{doc-right|rollback}}
 {{Identical|Rollback}}',
 'right-markbotedits' => '{{doc-right|markbotedits}}
diff --git a/maintenance/dictionary/mediawiki.dic 
b/maintenance/dictionary/mediawiki.dic
index f73dfc1..656dc33 100644
--- a/maintenance/dictionary/mediawiki.dic
+++ b/maintenance/dictionary/mediawiki.dic
@@ -1284,6 +1284,8 @@
 editintro
 edititis
 editlink
+editmyoptions
+editmyprivateinfo
 editmyusercss
 editmyuserjs
 editmywatchlist
@@ -4343,6 +4345,7 @@
 viewcount
 viewdeleted
 viewhelppage
+viewmyprivateinfo
 viewmywatchlist
 viewprevnext
 viewsource
diff --git a/maintenance/language/messages.inc 
b/maintenance/language/messages.inc
index be9273c..82f9619 100644
--- a/maintenance/language/messages.inc
+++ b/maintenance/language/messages.inc
@@ -426,6 +426,8 @@
                'customjsprotected',
                'mycustomcssprotected',
                'mycustomjsprotected',
+               'myprivateinfoprotected',
+               'mypreferencesprotected',
                'ns-specialprotected',
                'titleprotected',
                'filereadonlyerror',
@@ -1225,6 +1227,9 @@
                'right-editmyuserjs',
                'right-viewmywatchlist',
                'right-editmywatchlist',
+               'right-viewmyprivateinfo',
+               'right-editmyprivateinfo',
+               'right-editmyoptions',
                'right-rollback',
                'right-markbotedits',
                'right-noratelimit',

-- 
To view, visit https://gerrit.wikimedia.org/r/67876
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I3f03dd010020e8d43cc2d3bca7b3ef7196d1c548
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Anomie <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to