From: Brad Jorsch Date: Thu, 13 Jun 2013 18:02:55 +0000 (-0400) Subject: Add user rights 'viewmywatchlist', 'editmywatchlist' X-Git-Tag: 1.31.0-rc.0~19334^2 X-Git-Url: https://git.cyclocoop.org/%7B%24admin_url%7Dcompta/operations/modifier.php?a=commitdiff_plain;h=18062eb3b08905340da38d34586c10fdd2e60631;p=lhc%2Fweb%2Fwiklou.git Add user rights 'viewmywatchlist', 'editmywatchlist' 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 --- diff --git a/RELEASE-NOTES-1.22 b/RELEASE-NOTES-1.22 index 997dbc6c21..499dbc836c 100644 --- a/RELEASE-NOTES-1.22 +++ b/RELEASE-NOTES-1.22 @@ -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. diff --git a/includes/Article.php b/includes/Article.php index ba922a46ab..732b1c20c9 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -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; } diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 65f2477685..31659f3295 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -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 diff --git a/includes/EditPage.php b/includes/EditPage.php index 27f4556bf7..c84daa67ea 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -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 ); } ); } diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php index 9fc70ebe02..65d82b8782 100644 --- a/includes/FileDeleteForm.php +++ b/includes/FileDeleteForm.php @@ -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; } diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php index 6f444ee3d1..2f6b65df4d 100644 --- a/includes/ProtectionForm.php +++ b/includes/ProtectionForm.php @@ -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; } diff --git a/includes/SkinTemplate.php b/includes/SkinTemplate.php index fa9095454c..18f410a35c 100644 --- a/includes/SkinTemplate.php +++ b/includes/SkinTemplate.php @@ -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 diff --git a/includes/Title.php b/includes/Title.php index 9d42b2f894..56c2ed4be9 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -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! diff --git a/includes/User.php b/includes/User.php index b6d2377c7c..685fe9613f 100644 --- a/includes/User.php +++ b/includes/User.php @@ -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' ); + } + } } diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php index 45aa82266e..1e07e7c795 100644 --- a/includes/WatchedItem.php +++ b/includes/WatchedItem.php @@ -27,19 +27,37 @@ * @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; } diff --git a/includes/actions/WatchAction.php b/includes/actions/WatchAction.php index b1c48119b6..929c1b5f12 100644 --- a/includes/actions/WatchAction.php +++ b/includes/actions/WatchAction.php @@ -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' ); diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index e6e784f2c8..8f5185a415 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -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; diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php index 5f8c49784a..017684edd3 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -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; } diff --git a/includes/api/ApiSetNotificationTimestamp.php b/includes/api/ApiSetNotificationTimestamp.php index 53affbdff5..53a68fde84 100644 --- a/includes/api/ApiSetNotificationTimestamp.php +++ b/includes/api/ApiSetNotificationTimestamp.php @@ -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' ); diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php index e001be3d04..c7d636a1bc 100644 --- a/includes/api/ApiWatch.php +++ b/includes/api/ApiWatch.php @@ -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'] ); diff --git a/includes/specials/SpecialBlock.php b/includes/specials/SpecialBlock.php index 5a2ad62944..6ba009ae61 100644 --- a/includes/specials/SpecialBlock.php +++ b/includes/specials/SpecialBlock.php @@ -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 diff --git a/includes/specials/SpecialEditWatchlist.php b/includes/specials/SpecialEditWatchlist.php index f297039ea6..b6005de4e5 100644 --- a/includes/specials/SpecialEditWatchlist.php +++ b/includes/specials/SpecialEditWatchlist.php @@ -49,7 +49,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { private $badItems = array(); public function __construct() { - parent::__construct( 'EditWatchlist' ); + parent::__construct( 'EditWatchlist', 'editmywatchlist' ); } /** diff --git a/includes/specials/SpecialMovepage.php b/includes/specials/SpecialMovepage.php index 2ba3c0660f..59235700ba 100644 --- a/includes/specials/SpecialMovepage.php +++ b/includes/specials/SpecialMovepage.php @@ -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. diff --git a/includes/specials/SpecialRecentchanges.php b/includes/specials/SpecialRecentchanges.php index 6a35aa0dcc..0546bb783c 100644 --- a/includes/specials/SpecialRecentchanges.php +++ b/includes/specials/SpecialRecentchanges.php @@ -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'; diff --git a/includes/specials/SpecialRecentchangeslinked.php b/includes/specials/SpecialRecentchangeslinked.php index 4773c4a327..33f1c4c03f 100644 --- a/includes/specials/SpecialRecentchangeslinked.php +++ b/includes/specials/SpecialRecentchangeslinked.php @@ -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( diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php index f5e3660ff7..8340465710 100644 --- a/includes/specials/SpecialWatchlist.php +++ b/includes/specials/SpecialWatchlist.php @@ -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 ) { diff --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php index 2ed20c5603..36e425202c 100644 --- a/includes/upload/UploadBase.php +++ b/includes/upload/UploadBase.php @@ -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 ) ); } diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index b94b2e70dd..7a1b87d02e 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -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}}', diff --git a/languages/messages/MessagesQqq.php b/languages/messages/MessagesQqq.php index 3deede4bbc..6c09c1da5e 100644 --- a/languages/messages/MessagesQqq.php +++ b/languages/messages/MessagesQqq.php @@ -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.', diff --git a/maintenance/dictionary/mediawiki.dic b/maintenance/dictionary/mediawiki.dic index 663012fc9d..f73dfc1dea 100644 --- a/maintenance/dictionary/mediawiki.dic +++ b/maintenance/dictionary/mediawiki.dic @@ -1286,6 +1286,7 @@ edititis editlink editmyusercss editmyuserjs +editmywatchlist editnotice editnotsupported editondblclick @@ -4342,6 +4343,7 @@ view viewcount viewdeleted viewhelppage +viewmywatchlist viewprevnext viewsource viewsourcelink diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc index 28c3e81c0f..4236191a03 100644 --- a/maintenance/language/messages.inc +++ b/maintenance/language/messages.inc @@ -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',