From: Aryeh Gregor Date: Fri, 24 Jul 2009 01:22:06 +0000 (+0000) Subject: Add opt-in RSS feed for watchlist X-Git-Tag: 1.31.0-rc.0~40755 X-Git-Url: http://git.cyclocoop.org/clavettes/images/siteon3.jpg?a=commitdiff_plain;h=7b66b147387fa4e1fe58a88815765aaa622575c7;p=lhc%2Fweb%2Fwiklou.git Add opt-in RSS feed for watchlist Authentication is via a token entered in preferences, if not blank. If you set a token in your preferences, the following sort of link will generate the RSS feed: api.php?action=feedwatchlist&list=watchlist&wluser=Simetrical&wltoken=91c1ef18279f9c24ccf67a79e899ae4d2a3201bc I haven't actually added the tag to Special:Watchlist, since I've done enough coding for one night. Someone else can feel free to do that (otherwise people might get kind of confused :) ). An auto-generated random token is suggested to the user on the pref page so that they don't have to be too creative. Pref help text is rather underemphasized in the default style, though. It would be worth considering making this opt-out instead of opt-in, but that would require some voodoo magic to get the default prefs to work right (since we'd need a different value for each user). We might set the default to some function of user id + secret site-specific value to avoid having to store the values in the database. Since the feature is implemented via the API, it only works if the API is enabled. Some API people might want to review my code for sanity. Bug: 471 --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 24b342afa2..950508ceb2 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -160,6 +160,7 @@ this. Was used when mwEmbed was going to be an extension. when the Go button is pressed in addition to the main namespace. * (bug 19900) The "listgrouprights-key" message is now wrapped in a div with class "mw-listgrouprights-key" +* (bug 471) Allow RSS feeds for watchlist, using an opt-in security token === Bug fixes in 1.16 === diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php index 9cf212ed82..53cf698b16 100644 --- a/includes/HTMLForm.php +++ b/includes/HTMLForm.php @@ -462,20 +462,23 @@ abstract class HTMLFormField { $html = Xml::tags( 'tr', array( 'class' => "mw-htmlform-field-$fieldType" ), $html ) . "\n"; - // Help text + $helptext = null; if ( isset( $this->mParams['help-message'] ) ) { $msg = $this->mParams['help-message']; - - $text = wfMsgExt( $msg, 'parseinline' ); - - if( !wfEmptyMsg( $msg, $text ) ) { - $row = Xml::tags( 'td', array( 'colspan' => 2, 'class' => 'htmlform-tip' ), - $text ); - - $row = Xml::tags( 'tr', null, $row ); - - $html .= "$row\n"; + $helptext = wfMsgExt( $msg, 'parseinline' ); + if ( wfEmptyMsg( $msg, $helptext ) ) { + # Never mind + $helptext = null; } + } elseif ( isset( $this->mParams['help'] ) ) { + $helptext = $this->mParams['help']; + } + + if ( !is_null( $helptext ) ) { + $row = Xml::tags( 'td', array( 'colspan' => 2, 'class' => 'htmlform-tip' ), + $helptext ); + $row = Xml::tags( 'tr', null, $row ); + $html .= "$row\n"; } return $html; diff --git a/includes/Preferences.php b/includes/Preferences.php index 27284616e9..80e4ebd4bd 100644 --- a/includes/Preferences.php +++ b/includes/Preferences.php @@ -746,7 +746,7 @@ class Preferences { } static function watchlistPreferences( $user, &$defaultPreferences ) { - global $wgUseRCPatrol; + global $wgUseRCPatrol, $wgEnableAPI; ## Watchlist ##################################### $defaultPreferences['watchlistdays'] = array( @@ -800,6 +800,17 @@ class Preferences { 'section' => 'watchlist/advancedwatchlist', 'label-message' => 'tog-watchlisthideliu', ); + if ( $wgEnableAPI ) { + # Some random gibberish as a proposed default + $hash = sha1( mt_rand() . microtime( true ) ); + $defaultPreferences['watchlisttoken'] = + array( + 'type' => 'text', + 'section' => 'watchlist/advancedwatchlist', + 'label-message' => 'prefs-watchlist-token', + 'help' => wfMsgHtml( 'prefs-help-watchlist-token', $hash ) + ); + } if ( $wgUseRCPatrol ) { $defaultPreferences['watchlisthidepatrolled'] = diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php index 5a3cfdb6f5..a26184c833 100644 --- a/includes/api/ApiFeedWatchlist.php +++ b/includes/api/ApiFeedWatchlist.php @@ -75,6 +75,11 @@ class ApiFeedWatchlist extends ApiBase { 'wllimit' => (50 > $wgFeedLimit) ? $wgFeedLimit : 50 ); + if (!is_null($params['wluser'])) + $fauxReqArr['wluser'] = $params['wluser']; + if (!is_null($params['wltoken'])) + $fauxReqArr['wltoken'] = $params['wltoken']; + // Check for 'allrev' parameter, and if found, show all revisions to each page on wl. if ( ! is_null ( $params['allrev'] ) ) $fauxReqArr['wlallrev'] = ''; @@ -152,7 +157,13 @@ class ApiFeedWatchlist extends ApiBase { ApiBase :: PARAM_MIN => 1, ApiBase :: PARAM_MAX => 72, ), - 'allrev' => null + 'allrev' => null, + 'wluser' => array ( + ApiBase :: PARAM_TYPE => 'user' + ), + 'wltoken' => array ( + ApiBase :: PARAM_TYPE => 'string' + ) ); } @@ -160,7 +171,9 @@ class ApiFeedWatchlist extends ApiBase { return array ( 'feedformat' => 'The format of the feed', 'hours' => 'List pages modified within this many hours from now', - 'allrev' => 'Include multiple revisions of the same page within given timeframe.' + 'allrev' => 'Include multiple revisions of the same page within given timeframe.', + 'wluser' => "The user whose watchlist you want (must be accompanied by wltoken if it's not you)", + 'wltoken' => 'Security token that requested user set in their preferences' ); } diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php index 3d731758c2..6d317aa09e 100644 --- a/includes/api/ApiQueryWatchlist.php +++ b/includes/api/ApiQueryWatchlist.php @@ -56,11 +56,20 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $this->selectNamedDB('watchlist', DB_SLAVE, 'watchlist'); - if (!$wgUser->isLoggedIn()) - $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin'); - $params = $this->extractRequestParams(); + if (!is_null($params['user']) && !is_null($params['token'])) { + $user = User::newFromName($params['user']); + $token = $user->getOption('watchlisttoken'); + if ($token == '' || $token != $params['token']) { + $this->dieUsage('Incorrect watchlist token provided', 'bad_wltoken'); + } + } elseif (!$wgUser->isLoggedIn()) { + $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin'); + } else { + $user = $wgUser; + } + if (!is_null($params['prop']) && is_null($resultPageSet)) { $prop = array_flip($params['prop']); @@ -122,7 +131,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'recentchanges' )); - $userId = $wgUser->getId(); + $userId = $user->getId(); $this->addWhere(array ( 'wl_namespace = rc_namespace', 'wl_title = rc_title', @@ -147,7 +156,8 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show'); } - // Check permissions + // Check permissions. FIXME: should this check $user instead of + // $wgUser? global $wgUser; if((isset($show['patrolled']) || isset($show['!patrolled'])) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied'); @@ -162,13 +172,17 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $this->addWhereIf('rc_patrolled = 0', isset($show['!patrolled'])); $this->addWhereIf('rc_patrolled != 0', isset($show['patrolled'])); } - - if(!is_null($params['user']) && !is_null($params['excludeuser'])) - $this->dieUsage('user and excludeuser cannot be used together', 'user-excludeuser'); - if(!is_null($params['user'])) - $this->addWhereFld('rc_user_text', $params['user']); - if(!is_null($params['excludeuser'])) - $this->addWhere('rc_user_text != ' . $this->getDB()->addQuotes($params['excludeuser'])); + + # Ignore extra user conditions if we're using token mode, since the + # user was already manually specified. + if(is_null($params['user']) || is_null($params['token'])) { + if(!is_null($params['user']) && !is_null($params['excludeuser'])) + $this->dieUsage('user and excludeuser cannot be used together', 'user-excludeuser'); + if(!is_null($params['user'])) + $this->addWhereFld('rc_user_text', $params['user']); + if(!is_null($params['excludeuser'])) + $this->addWhere('rc_user_text != ' . $this->getDB()->addQuotes($params['excludeuser'])); + } # This is an index optimization for mysql, as done in the Special:Watchlist page @@ -321,6 +335,9 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'patrolled', '!patrolled', ) + ), + 'token' => array ( + ApiBase :: PARAM_TYPE => 'string' ) ); } @@ -339,7 +356,8 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'show' => array ( 'Show only items that meet this criteria.', 'For example, to see only minor edits done by logged-in users, set show=minor|!anon' - ) + ), + 'token' => "Give a security token (settable in preferences) to allow access to another user's watchlist" ); } diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index 06448bd55a..7b6821ab4e 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -1700,6 +1700,7 @@ Note that their indexes of {{SITENAME}} content may be out of date.', 'prefs-watchlist-days-max' => '(maximum 7 days)', 'prefs-watchlist-edits' => 'Maximum number of changes to show in expanded watchlist:', 'prefs-watchlist-edits-max' => '(maximum number: 1000)', +'prefs-watchlist-token' => 'Watchlist token', 'prefs-misc' => 'Misc', 'prefs-resetpass' => 'Change password', 'prefs-email' => 'E-mail options', @@ -1720,6 +1721,9 @@ Note that their indexes of {{SITENAME}} content may be out of date.', 'recentchangesdays-max' => '(maximum $1 {{PLURAL:$1|day|days}})', 'recentchangescount' => 'Number of edits to show by default:', 'prefs-help-recentchangescount' => 'This includes recent changes, page histories, and logs.', +'prefs-help-watchlist-token' => "Filling in this field with a secret key will generate an RSS feed for your watchlist. +Anyone who knows the key in this field will be able to read your watchlist, so choose a secure value. +Here's a randomly-generated value you can use: $1", 'savedprefs' => 'Your preferences have been saved.', 'timezonelegend' => 'Time zone:', 'localtime' => 'Local time:', diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc index 244bcd0436..f8d32df928 100644 --- a/maintenance/language/messages.inc +++ b/maintenance/language/messages.inc @@ -898,6 +898,7 @@ $wgMessageStructure = array( 'prefs-watchlist-days-max', 'prefs-watchlist-edits', 'prefs-watchlist-edits-max', + 'prefs-watchlist-token', 'prefs-misc', // continue checking if used from here on (r49916) 'prefs-resetpass', 'prefs-email', @@ -918,6 +919,7 @@ $wgMessageStructure = array( 'recentchangesdays-max', 'recentchangescount', 'prefs-help-recentchangescount', + 'prefs-help-watchlist-token', 'savedprefs', 'timezonelegend', 'localtime',