Add user rights 'viewmywatchlist', 'editmywatchlist'
authorBrad Jorsch <bjorsch@wikimedia.org>
Thu, 13 Jun 2013 18:02:55 +0000 (14:02 -0400)
committerBrad Jorsch <bjorsch@wikimedia.org>
Wed, 26 Jun 2013 14:20:40 +0000 (10:20 -0400)
These are needed for OAuth grants.

Note that, even if 'editmywatchlist' is not granted, various actions
will still allow for adding but not removing of pages.

Change-Id: Ie33446a228dd6ed0114730935c1bf65667f5ce01

26 files changed:
RELEASE-NOTES-1.22
includes/Article.php
includes/DefaultSettings.php
includes/EditPage.php
includes/FileDeleteForm.php
includes/ProtectionForm.php
includes/SkinTemplate.php
includes/Title.php
includes/User.php
includes/WatchedItem.php
includes/actions/WatchAction.php
includes/api/ApiBase.php
includes/api/ApiQueryInfo.php
includes/api/ApiSetNotificationTimestamp.php
includes/api/ApiWatch.php
includes/specials/SpecialBlock.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialWatchlist.php
includes/upload/UploadBase.php
languages/messages/MessagesEn.php
languages/messages/MessagesQqq.php
maintenance/dictionary/mediawiki.dic
maintenance/language/messages.inc

index 997dbc6..499dbc8 100644 (file)
@@ -28,9 +28,9 @@ production.
 * $wgLogAutopatrol added to allow disabling logging of autopatrol edits in the logging table.
   default for $wgLogAutopatrol is true.
 * The 'edit' right no longer allows for editing a user's own CSS and JS.
-* New rights 'editmyusercss' and 'editmyuserjs' restrict actions that were
-  formerly allowed by default. They have been added to the default for
-  $wgGroupPermissions['*'].
+* New rights 'editmyusercss', 'editmyuserjs', 'viewmywatchlist',
+  and 'editmywatchlist' 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.
@@ -108,6 +108,8 @@ production.
   for extensions such as OAuth:
 ** editmyusercss controls whether a user may edit their own CSS subpages.
 ** 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.
 * Add new hook AbortTalkPageEmailNotification, this will be used to determine
   whether to send the regular talk page email notification
 * (bug 46513) Vector: Add the collapsibleTabs script from the Vector extension.
index ba922a4..732b1c2 100644 (file)
@@ -1546,13 +1546,7 @@ class Article implements Page {
 
                        $this->doDelete( $reason, $suppress );
 
-                       if ( $user->isLoggedIn() && $request->getCheck( 'wpWatch' ) != $user->isWatched( $title ) ) {
-                               if ( $request->getCheck( 'wpWatch' ) ) {
-                                       WatchAction::doWatch( $title, $user );
-                               } else {
-                                       WatchAction::doUnwatch( $title, $user );
-                               }
-                       }
+                       WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
 
                        return;
                }
index 65f2477..31659f3 100644 (file)
@@ -3873,6 +3873,8 @@ $wgGroupPermissions['*']['createtalk'] = true;
 $wgGroupPermissions['*']['writeapi'] = true;
 $wgGroupPermissions['*']['editmyusercss'] = true;
 $wgGroupPermissions['*']['editmyuserjs'] = true;
+$wgGroupPermissions['*']['viewmywatchlist'] = true;
+$wgGroupPermissions['*']['editmywatchlist'] = true;
 #$wgGroupPermissions['*']['patrolmarks'] = false; // let anons see what was patrolled
 
 // Implicit group for all logged-in accounts
index 27f4556..c84daa6 100644 (file)
@@ -1743,7 +1743,9 @@ class EditPage {
        protected function updateWatchlist() {
                global $wgUser;
 
-               if ( $wgUser->isLoggedIn() && $this->watchthis != $wgUser->isWatched( $this->mTitle ) ) {
+               if ( $wgUser->isLoggedIn()
+                       && $this->watchthis != $wgUser->isWatched( $this->mTitle, WatchedItem::IGNORE_USER_RIGHTS )
+               ) {
                        $fname = __METHOD__;
                        $title = $this->mTitle;
                        $watch = $this->watchthis;
@@ -1752,11 +1754,7 @@ class EditPage {
                        $dbw = wfGetDB( DB_MASTER );
                        $dbw->onTransactionIdle( function() use ( $dbw, $title, $watch, $wgUser, $fname ) {
                                $dbw->begin( $fname );
-                               if ( $watch ) {
-                                       WatchAction::doWatch( $title, $wgUser );
-                               } else {
-                                       WatchAction::doUnwatch( $title, $wgUser );
-                               }
+                               WatchAction::doWatchOrUnwatch( $watch, $title, $wgUser );
                                $dbw->commit( $fname );
                        } );
                }
index 9fc70eb..65d82b8 100644 (file)
@@ -120,13 +120,7 @@ class FileDeleteForm {
                                // file, otherwise go back to the description page
                                $wgOut->addReturnTo( $this->oldimage ? $this->title : Title::newMainPage() );
 
-                               if ( $wgUser->isLoggedIn() && $wgRequest->getCheck( 'wpWatch' ) != $wgUser->isWatched( $this->title ) ) {
-                                       if ( $wgRequest->getCheck( 'wpWatch' ) ) {
-                                               WatchAction::doWatch( $this->title, $wgUser );
-                                       } else {
-                                               WatchAction::doUnwatch( $this->title, $wgUser );
-                                       }
-                               }
+                               WatchAction::doWatchOrUnwatch( $wgRequest->getCheck( 'wpWatch' ), $this->title, $wgUser );
                        }
                        return;
                }
index 6f444ee..2f6b65d 100644 (file)
@@ -330,13 +330,8 @@ class ProtectionForm {
                        return false;
                }
 
-               if ( $wgUser->isLoggedIn() && $wgRequest->getCheck( 'mwProtectWatch' ) != $wgUser->isWatched( $this->mTitle ) ) {
-                       if ( $wgRequest->getCheck( 'mwProtectWatch' ) ) {
-                               WatchAction::doWatch( $this->mTitle, $wgUser );
-                       } else {
-                               WatchAction::doUnwatch( $this->mTitle, $wgUser );
-                       }
-               }
+               WatchAction::doWatchOrUnwatch( $wgRequest->getCheck( 'mwProtectWatch' ), $this->mTitle, $wgUser );
+
                return true;
        }
 
index fa90954..18f410a 100644 (file)
@@ -613,12 +613,15 @@ class SkinTemplate extends Skin {
                                'href' => $href,
                                'active' => ( $href == $pageurl )
                        );
-                       $href = self::makeSpecialUrl( 'Watchlist' );
-                       $personal_urls['watchlist'] = array(
-                               'text' => $this->msg( 'mywatchlist' )->text(),
-                               'href' => $href,
-                               'active' => ( $href == $pageurl )
-                       );
+
+                       if ( $this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
+                               $href = self::makeSpecialUrl( 'Watchlist' );
+                               $personal_urls['watchlist'] = array(
+                                       'text' => $this->msg( 'mywatchlist' )->text(),
+                                       'href' => $href,
+                                       'active' => ( $href == $pageurl )
+                               );
+                       }
 
                        # We need to do an explicit check for Special:Contributions, as we
                        # have to match both the title, and the target, which could come
@@ -992,7 +995,7 @@ class SkinTemplate extends Skin {
                                wfProfileOut( __METHOD__ . '-live' );
 
                                // Checks if the user is logged in
-                               if ( $this->loggedin ) {
+                               if ( $this->loggedin && $user->isAllowedAll( 'viewmywatchlist', 'editmywatchlist' ) ) {
                                        /**
                                         * The following actions use messages which, if made particular to
                                         * the any specific skins, would break the Ajax code which makes this
index 9d42b2f..56c2ed4 100644 (file)
@@ -4531,7 +4531,7 @@ class Title {
                if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
                        return $this->mNotificationTimestamp[$uid];
                }
-               if ( !$uid || !$wgShowUpdatedMarker ) {
+               if ( !$uid || !$wgShowUpdatedMarker || !$user->isAllowed( 'viewmywatchlist' ) ) {
                        return $this->mNotificationTimestamp[$uid] = false;
                }
                // Don't cache too much!
index b6d2377..685fe96 100644 (file)
@@ -126,6 +126,7 @@ class User {
                'editprotected',
                'editmyusercss',
                'editmyuserjs',
+               'editmywatchlist',
                'editusercssjs', #deprecated
                'editusercss',
                'edituserjs',
@@ -166,6 +167,7 @@ class User {
                'upload_by_url',
                'userrights',
                'userrights-interwiki',
+               'viewmywatchlist',
                'writeapi',
        );
        /**
@@ -2860,11 +2862,14 @@ class User {
        /**
         * Get a WatchedItem for this user and $title.
         *
+        * @since 1.22 $checkRights parameter added
         * @param $title Title
+        * @param $checkRights int Whether to check 'viewmywatchlist'/'editmywatchlist' rights.
+        *     Pass WatchedItem::CHECK_USER_RIGHTS or WatchedItem::IGNORE_USER_RIGHTS.
         * @return WatchedItem
         */
-       public function getWatchedItem( $title ) {
-               $key = $title->getNamespace() . ':' . $title->getDBkey();
+       public function getWatchedItem( $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
+               $key = $checkRights . ':' . $title->getNamespace() . ':' . $title->getDBkey();
 
                if ( isset( $this->mWatchedItems[$key] ) ) {
                        return $this->mWatchedItems[$key];
@@ -2874,34 +2879,43 @@ class User {
                        $this->mWatchedItems = array();
                }
 
-               $this->mWatchedItems[$key] = WatchedItem::fromUserTitle( $this, $title );
+               $this->mWatchedItems[$key] = WatchedItem::fromUserTitle( $this, $title, $checkRights );
                return $this->mWatchedItems[$key];
        }
 
        /**
         * Check the watched status of an article.
+        * @since 1.22 $checkRights parameter added
         * @param $title Title of the article to look at
+        * @param $checkRights int Whether to check 'viewmywatchlist'/'editmywatchlist' rights.
+        *     Pass WatchedItem::CHECK_USER_RIGHTS or WatchedItem::IGNORE_USER_RIGHTS.
         * @return bool
         */
-       public function isWatched( $title ) {
-               return $this->getWatchedItem( $title )->isWatched();
+       public function isWatched( $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
+               return $this->getWatchedItem( $title, $checkRights )->isWatched();
        }
 
        /**
         * Watch an article.
+        * @since 1.22 $checkRights parameter added
         * @param $title Title of the article to look at
+        * @param $checkRights int Whether to check 'viewmywatchlist'/'editmywatchlist' rights.
+        *     Pass WatchedItem::CHECK_USER_RIGHTS or WatchedItem::IGNORE_USER_RIGHTS.
         */
-       public function addWatch( $title ) {
-               $this->getWatchedItem( $title )->addWatch();
+       public function addWatch( $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
+               $this->getWatchedItem( $title, $checkRights )->addWatch();
                $this->invalidateCache();
        }
 
        /**
         * Stop watching an article.
+        * @since 1.22 $checkRights parameter added
         * @param $title Title of the article to look at
+        * @param $checkRights int Whether to check 'viewmywatchlist'/'editmywatchlist' rights.
+        *     Pass WatchedItem::CHECK_USER_RIGHTS or WatchedItem::IGNORE_USER_RIGHTS.
         */
-       public function removeWatch( $title ) {
-               $this->getWatchedItem( $title )->removeWatch();
+       public function removeWatch( $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
+               $this->getWatchedItem( $title, $checkRights )->removeWatch();
                $this->invalidateCache();
        }
 
@@ -2909,6 +2923,7 @@ class User {
         * Clear the user's notification timestamp for the given title.
         * If e-notif e-mails are on, they will receive notification mails on
         * the next change of the page if it's watched etc.
+        * @note If the user doesn't have 'editmywatchlist', this will do nothing.
         * @param $title Title of the article to look at
         */
        public function clearNotification( &$title ) {
@@ -2919,6 +2934,11 @@ class User {
                        return;
                }
 
+               // Do nothing if not allowed to edit the watchlist
+               if ( !$this->isAllowed( 'editmywatchlist' ) ) {
+                       return;
+               }
+
                if ( $title->getNamespace() == NS_USER_TALK &&
                        $title->getText() == $this->getName() ) {
                        if ( !wfRunHooks( 'UserClearNewTalkNotification', array( &$this ) ) ) {
@@ -2954,12 +2974,18 @@ class User {
         * Resets all of the given user's page-change notification timestamps.
         * If e-notif e-mails are on, they will receive notification mails on
         * the next change of any watched page.
+        * @note If the user doesn't have 'editmywatchlist', this will do nothing.
         */
        public function clearAllNotifications() {
                if ( wfReadOnly() ) {
                        return;
                }
 
+               // Do nothing if not allowed to edit the watchlist
+               if ( !$this->isAllowed( 'editmywatchlist' ) ) {
+                       return;
+               }
+
                global $wgUseEnotif, $wgShowUpdatedMarker;
                if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
                        $this->setNewtalk( false );
@@ -4587,4 +4613,26 @@ class User {
                        'user_editcount',
                );
        }
+
+       /**
+        * Factory function for fatal permission-denied errors
+        *
+        * @since 1.22
+        * @param string $permission User right required
+        * @return Status
+        */
+       static function newFatalPermissionDeniedStatus( $permission ) {
+               global $wgLang;
+
+               $groups = array_map(
+                       array( 'User', 'makeGroupLinkWiki' ),
+                       User::getGroupsWithPermission( $permission )
+               );
+
+               if ( $groups ) {
+                       return Status::newFatal( 'badaccess-groups', $wgLang->commaList( $groups ), count( $groups ) );
+               } else {
+                       return Status::newFatal( 'badaccess-group0' );
+               }
+       }
 }
index 45aa822..1e07e7c 100644 (file)
  * @ingroup Watchlist
  */
 class WatchedItem {
-       var $mTitle, $mUser;
+       /**
+        * Constant to specify that user rights 'editmywatchlist' and
+        * 'viewmywatchlist' should not be checked.
+        * @since 1.22
+        */
+       const IGNORE_USER_RIGHTS = 0;
+
+       /**
+        * Constant to specify that user rights 'editmywatchlist' and
+        * 'viewmywatchlist' should be checked.
+        * @since 1.22
+        */
+       const CHECK_USER_RIGHTS = 1;
+
+       var $mTitle, $mUser, $mCheckRights;
        private $loaded = false, $watched, $timestamp;
 
        /**
         * Create a WatchedItem object with the given user and title
+        * @since 1.22 $checkRights parameter added
         * @param $user User: the user to use for (un)watching
         * @param $title Title: the title we're going to (un)watch
+        * @param $checkRights int: Whether to check the 'viewmywatchlist' and 'editmywatchlist' rights.
+        *     Pass either WatchedItem::IGNORE_USER_RIGHTS or WatchedItem::CHECK_USER_RIGHTS.
         * @return WatchedItem object
         */
-       public static function fromUserTitle( $user, $title ) {
+       public static function fromUserTitle( $user, $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
                $wl = new WatchedItem;
                $wl->mUser = $user;
                $wl->mTitle = $title;
+               $wl->mCheckRights = $checkRights;
 
                return $wl;
        }
@@ -110,11 +128,23 @@ class WatchedItem {
                }
        }
 
+       /**
+        * Check permissions
+        * @param $what string: 'viewmywatchlist' or 'editmywatchlist'
+        */
+       private function isAllowed( $what ) {
+               return !$this->mCheckRights || $this->mUser->isAllowed( $what );
+       }
+
        /**
         * Is mTitle being watched by mUser?
         * @return bool
         */
        public function isWatched() {
+               if ( !$this->isAllowed( 'viewmywatchlist' ) ) {
+                       return false;
+               }
+
                $this->load();
                return $this->watched;
        }
@@ -126,6 +156,10 @@ class WatchedItem {
         *         the wl_notificationtimestamp field otherwise
         */
        public function getNotificationTimestamp() {
+               if ( !$this->isAllowed( 'viewmywatchlist' ) ) {
+                       return false;
+               }
+
                $this->load();
                if ( $this->watched ) {
                        return $this->timestamp;
@@ -142,7 +176,7 @@ class WatchedItem {
         */
        public function resetNotificationTimestamp( $force = '' ) {
                // Only loggedin user can have a watchlist
-               if ( wfReadOnly() || $this->mUser->isAnon() ) {
+               if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
                        return;
                }
 
@@ -170,7 +204,7 @@ class WatchedItem {
                wfProfileIn( __METHOD__ );
 
                // Only loggedin user can have a watchlist
-               if ( wfReadOnly() || $this->mUser->isAnon() ) {
+               if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
                        wfProfileOut( __METHOD__ );
                        return false;
                }
@@ -210,7 +244,7 @@ class WatchedItem {
                wfProfileIn( __METHOD__ );
 
                // Only loggedin user can have a watchlist
-               if ( wfReadOnly() || $this->mUser->isAnon() ) {
+               if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
                        wfProfileOut( __METHOD__ );
                        return false;
                }
index b1c4811..929c1b5 100644 (file)
@@ -87,20 +87,46 @@ class WatchAction extends FormAction {
                return parent::checkCanExecute( $user );
        }
 
+       /**
+        * Watch or unwatch a page
+        * @since 1.22
+        * @param bool $watch Whether to watch or unwatch the page
+        * @param Title $title Page to watch/unwatch
+        * @param User $user User who is watching/unwatching
+        * @return Status
+        */
+       public static function doWatchOrUnwatch( $watch, Title $title, User $user ) {
+               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 ) {
+                               return self::doWatch( $title, $user, WatchedItem::IGNORE_USER_RIGHTS );
+                       } else {
+                               return self::doUnwatch( $title, $user );
+                       }
+               }
+               return Status::newGood();
+       }
+
        /**
         * Watch a page
-        * @since 1.22 Returns Status object
+        * @since 1.22 Returns Status, $checkRights parameter added
         * @param Title $title Page to watch/unwatch
         * @param User $user User who is watching/unwatching
+        * @param int $checkRights Passed through to $user->addWatch()
         * @return Status
         */
-       public static function doWatch( Title $title, User $user ) {
+       public static function doWatch( Title $title, User $user, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
+               if ( $checkRights !== WatchedItem::IGNORE_USER_RIGHTS && !$user->isAllowed( 'editmywatchlist' ) ) {
+                       return User::newFatalPermissionDeniedStatus( 'editmywatchlist' );
+               }
+
                $page = WikiPage::factory( $title );
 
                $status = Status::newFatal( 'hookaborted' );
                if ( wfRunHooks( 'WatchArticle', array( &$user, &$page, &$status ) ) ) {
                        $status = Status::newGood();
-                       $user->addWatch( $title );
+                       $user->addWatch( $title, $checkRights );
                        wfRunHooks( 'WatchArticleComplete', array( &$user, &$page ) );
                }
                return $status;
@@ -114,6 +140,10 @@ class WatchAction extends FormAction {
         * @return Status
         */
        public static function doUnwatch( Title $title, User $user ) {
+               if ( !$user->isAllowed( 'editmywatchlist' ) ) {
+                       return User::newFatalPermissionDeniedStatus( 'editmywatchlist' );
+               }
+
                $page = WikiPage::factory( $title );
 
                $status = Status::newFatal( 'hookaborted' );
index e6e784f..8f5185a 100644 (file)
@@ -823,7 +823,7 @@ abstract class ApiBase extends ContextSource {
         */
        protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) {
 
-               $userWatching = $this->getUser()->isWatched( $titleObj );
+               $userWatching = $this->getUser()->isWatched( $titleObj, WatchedItem::IGNORE_USER_RIGHTS );
 
                switch ( $watchlist ) {
                        case 'watch':
@@ -865,12 +865,7 @@ abstract class ApiBase extends ContextSource {
                        return;
                }
 
-               $user = $this->getUser();
-               if ( $value ) {
-                       WatchAction::doWatch( $titleObj, $user );
-               } else {
-                       WatchAction::doUnwatch( $titleObj, $user );
-               }
+               WatchAction::doWatchOrUnwatch( $value, $titleObj, $this->getUser() );
        }
 
        /**
@@ -1583,6 +1578,9 @@ abstract class ApiBase extends ContextSource {
                        if ( !$this->getUser()->isLoggedIn() ) {
                                $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' );
                        }
+                       if ( !$this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
+                               $this->dieUsage( 'You don\'t have permission to view your watchlist', 'permissiondenied' );
+                       }
                        $user = $this->getUser();
                }
                return $user;
index 5f8c497..017684e 100644 (file)
@@ -669,7 +669,9 @@ class ApiQueryInfo extends ApiQueryBase {
        private function getWatchedInfo() {
                $user = $this->getUser();
 
-               if ( $user->isAnon() || count( $this->everything ) == 0 ) {
+               if ( $user->isAnon() || count( $this->everything ) == 0
+                       || !$user->isAllowed( 'viewmywatchlist' )
+               ) {
                        return;
                }
 
index 53affbd..53a68fd 100644 (file)
@@ -39,6 +39,9 @@ class ApiSetNotificationTimestamp extends ApiBase {
                if ( $user->isAnon() ) {
                        $this->dieUsage( 'Anonymous users cannot use watchlist change notifications', 'notloggedin' );
                }
+               if ( !$user->isAllowed( 'editmywatchlist' ) ) {
+                       $this->dieUsage( 'You don\'t have permission to edit your watchlist', 'permissiondenied' );
+               }
 
                $params = $this->extractRequestParams();
                $this->requireMaxOneParameter( $params, 'timestamp', 'torevid', 'newerthanrevid' );
index e001be3..c7d636a 100644 (file)
@@ -36,6 +36,9 @@ class ApiWatch extends ApiBase {
                if ( !$user->isLoggedIn() ) {
                        $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' );
                }
+               if ( !$user->isAllowed( 'editmywatchlist' ) ) {
+                       $this->dieUsage( 'You don\'t have permission to edit your watchlist', 'permissiondenied' );
+               }
 
                $params = $this->extractRequestParams();
                $title = Title::newFromText( $params['title'] );
index 5a2ad62..6ba009a 100644 (file)
@@ -753,7 +753,7 @@ class SpecialBlock extends FormSpecialPage {
 
                # Can't watch a rangeblock
                if ( $type != Block::TYPE_RANGE && $data['Watch'] ) {
-                       $performer->addWatch( Title::makeTitle( NS_USER, $target ) );
+                       WatchAction::doWatch( Title::makeTitle( NS_USER, $target ), $performer, WatchedItem::IGNORE_USER_RIGHTS );
                }
 
                # Block constructor sanitizes certain block options on insert
index f297039..b6005de 100644 (file)
@@ -49,7 +49,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
        private $badItems = array();
 
        public function __construct() {
-               parent::__construct( 'EditWatchlist' );
+               parent::__construct( 'EditWatchlist', 'editmywatchlist' );
        }
 
        /**
index 2ba3c06..5923570 100644 (file)
@@ -690,13 +690,8 @@ class MovePageForm extends UnlistedSpecialPage {
                }
 
                # Deal with watches (we don't watch subpages)
-               if ( $this->watch && $user->isLoggedIn() ) {
-                       $user->addWatch( $ot );
-                       $user->addWatch( $nt );
-               } else {
-                       $user->removeWatch( $ot );
-                       $user->removeWatch( $nt );
-               }
+               WatchAction::doWatchOrUnwatch( $this->watch, $ot, $user );
+               WatchAction::doWatchOrUnwatch( $this->watch, $nt, $user );
 
                # Re-clear the file redirect cache, which may have been polluted by
                # parsing in messages above. See CR r56745.
index 6a35aa0..0546bb7 100644 (file)
@@ -393,7 +393,7 @@ class SpecialRecentChanges extends IncludableSpecialPage {
 
                $fields = RecentChange::selectFields();
                // JOIN on watchlist for users
-               if ( $uid ) {
+               if ( $uid && $this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
                        $tables[] = 'watchlist';
                        $fields[] = 'wl_user';
                        $fields[] = 'wl_notificationtimestamp';
index 4773c4a..33f1c4c 100644 (file)
@@ -99,7 +99,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges {
 
                // left join with watchlist table to highlight watched rows
                $uid = $this->getUser()->getId();
-               if ( $uid ) {
+               if ( $uid && $this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
                        $tables[] = 'watchlist';
                        $select[] = 'wl_user';
                        $join_conds['watchlist'] = array( 'LEFT JOIN', array(
index f5e3660..8340465 100644 (file)
@@ -26,8 +26,8 @@ class SpecialWatchlist extends SpecialPage {
        /**
         * Constructor
         */
-       public function __construct( $page = 'Watchlist' ) {
-               parent::__construct( $page );
+       public function __construct( $page = 'Watchlist', $restriction = 'viewmywatchlist' ) {
+               parent::__construct( $page, $restriction );
        }
 
        /**
@@ -54,6 +54,9 @@ class SpecialWatchlist extends SpecialPage {
                        return;
                }
 
+               // Check permissions
+               $this->checkPermissions();
+
                // Add feed links
                $wlToken = $user->getOption( 'watchlisttoken' );
                if ( !$wlToken ) {
index 2ed20c5..36e4252 100644 (file)
@@ -688,7 +688,7 @@ abstract class UploadBase {
 
                if ( $status->isGood() ) {
                        if ( $watch ) {
-                               $user->addWatch( $this->getLocalFile()->getTitle() );
+                               WatchAction::doWatch( $this->getLocalFile()->getTitle(), $user, WatchedItem::IGNORE_USER_RIGHTS );
                        }
                        wfRunHooks( 'UploadComplete', array( &$this ) );
                }
index b94b2e7..7a1b87d 100644 (file)
@@ -2095,6 +2095,8 @@ Your email address is not revealed when other users contact you.',
 'right-edituserjs'            => "Edit other users' JavaScript files",
 'right-editmyusercss'         => 'Edit your own user CSS files',
 '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-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',
@@ -2156,6 +2158,8 @@ Your email address is not revealed when other users contact you.',
 'action-userrights-interwiki' => 'edit user rights of users on other wikis',
 'action-siteadmin'            => 'lock or unlock the database',
 'action-sendemail'            => 'send emails',
+'action-viewmywatchlist'      => 'view your watchlist',
+'action-editmywatchlist'      => 'edit your watchlist',
 
 # Recent changes
 'nchanges'                          => '$1 {{PLURAL:$1|change|changes}}',
index 3deede4..6c09c1d 100644 (file)
@@ -2914,6 +2914,8 @@ See also {{msg-mw|Right-editusercss}}',
 'right-editmyuserjs' => '{{doc-right|editmyuserjs}}
 
 See also {{msg-mw|Right-edituserjs}}',
+'right-viewmywatchlist' => '{{doc-right|viewmywatchlist}}',
+'right-editmywatchlist' => '{{doc-right|editmywatchlist}}',
 'right-rollback' => '{{doc-right|rollback}}
 {{Identical|Rollback}}',
 'right-markbotedits' => '{{doc-right|markbotedits}}
@@ -2986,6 +2988,8 @@ This action allows editing of all of the "user rights", not just the rights of t
 'action-userrights-interwiki' => '{{Doc-action|userrights-interwiki}}',
 'action-siteadmin' => '{{Doc-action|siteadmin}}',
 'action-sendemail' => '{{doc-action|sendemail}}',
+'action-editmywatchlist' => '{{doc-action|editmywatchlist}}',
+'action-viewmywatchlist' => '{{doc-action|viewmywatchlist}}',
 
 # Recent changes
 'nchanges' => 'Appears on the [[Special:RecentChanges]] special page in brackets after pages having more than one change on that date. $1 is the number of changes on that day.',
index 663012f..f73dfc1 100644 (file)
@@ -1286,6 +1286,7 @@ edititis
 editlink
 editmyusercss
 editmyuserjs
+editmywatchlist
 editnotice
 editnotsupported
 editondblclick
@@ -4342,6 +4343,7 @@ view
 viewcount
 viewdeleted
 viewhelppage
+viewmywatchlist
 viewprevnext
 viewsource
 viewsourcelink
index 28c3e81..4236191 100644 (file)
@@ -1222,6 +1222,8 @@ $wgMessageStructure = array(
                'right-edituserjs',
                'right-editmyusercss',
                'right-editmyuserjs',
+               'right-viewmywatchlist',
+               'right-editmywatchlist',
                'right-rollback',
                'right-markbotedits',
                'right-noratelimit',
@@ -1283,6 +1285,8 @@ $wgMessageStructure = array(
                'action-userrights-interwiki',
                'action-siteadmin',
                'action-sendemail',
+               'action-editmywatchlist',
+               'action-viewmywatchlist',
        ),
        'recentchanges' => array(
                'nchanges',