r53703 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r53702‎ | r53703 | r53704 >
Date:01:22, 24 July 2009
Author:simetrical
Status:resolved (Comments)
Tags:
Comment:
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 <link> 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
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/HTMLForm.php (modified) (history)
  • /trunk/phase3/includes/Preferences.php (modified) (history)
  • /trunk/phase3/includes/api/ApiFeedWatchlist.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryWatchlist.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/maintenance/language/messages.inc (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/language/messages.inc
@@ -898,6 +898,7 @@
899899 'prefs-watchlist-days-max',
900900 'prefs-watchlist-edits',
901901 'prefs-watchlist-edits-max',
 902+ 'prefs-watchlist-token',
902903 'prefs-misc', // continue checking if used from here on (r49916)
903904 'prefs-resetpass',
904905 'prefs-email',
@@ -918,6 +919,7 @@
919920 'recentchangesdays-max',
920921 'recentchangescount',
921922 'prefs-help-recentchangescount',
 923+ 'prefs-help-watchlist-token',
922924 'savedprefs',
923925 'timezonelegend',
924926 'localtime',
Index: trunk/phase3/RELEASE-NOTES
@@ -160,6 +160,7 @@
161161 when the Go button is pressed in addition to the main namespace.
162162 * (bug 19900) The "listgrouprights-key" message is now wrapped in a div with
163163 class "mw-listgrouprights-key"
 164+* (bug 471) Allow RSS feeds for watchlist, using an opt-in security token
164165
165166 === Bug fixes in 1.16 ===
166167
Index: trunk/phase3/includes/HTMLForm.php
@@ -462,22 +462,25 @@
463463 $html = Xml::tags( 'tr', array( 'class' => "mw-htmlform-field-$fieldType" ),
464464 $html ) . "\n";
465465
466 - // Help text
 466+ $helptext = null;
467467 if ( isset( $this->mParams['help-message'] ) ) {
468468 $msg = $this->mParams['help-message'];
469 -
470 - $text = wfMsgExt( $msg, 'parseinline' );
471 -
472 - if( !wfEmptyMsg( $msg, $text ) ) {
473 - $row = Xml::tags( 'td', array( 'colspan' => 2, 'class' => 'htmlform-tip' ),
474 - $text );
475 -
476 - $row = Xml::tags( 'tr', null, $row );
477 -
478 - $html .= "$row\n";
 469+ $helptext = wfMsgExt( $msg, 'parseinline' );
 470+ if ( wfEmptyMsg( $msg, $helptext ) ) {
 471+ # Never mind
 472+ $helptext = null;
479473 }
 474+ } elseif ( isset( $this->mParams['help'] ) ) {
 475+ $helptext = $this->mParams['help'];
480476 }
481477
 478+ if ( !is_null( $helptext ) ) {
 479+ $row = Xml::tags( 'td', array( 'colspan' => 2, 'class' => 'htmlform-tip' ),
 480+ $helptext );
 481+ $row = Xml::tags( 'tr', null, $row );
 482+ $html .= "$row\n";
 483+ }
 484+
482485 return $html;
483486 }
484487
Index: trunk/phase3/includes/api/ApiQueryWatchlist.php
@@ -56,11 +56,20 @@
5757
5858 $this->selectNamedDB('watchlist', DB_SLAVE, 'watchlist');
5959
60 - if (!$wgUser->isLoggedIn())
 60+ $params = $this->extractRequestParams();
 61+
 62+ if (!is_null($params['user']) && !is_null($params['token'])) {
 63+ $user = User::newFromName($params['user']);
 64+ $token = $user->getOption('watchlisttoken');
 65+ if ($token == '' || $token != $params['token']) {
 66+ $this->dieUsage('Incorrect watchlist token provided', 'bad_wltoken');
 67+ }
 68+ } elseif (!$wgUser->isLoggedIn()) {
6169 $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin');
 70+ } else {
 71+ $user = $wgUser;
 72+ }
6273
63 - $params = $this->extractRequestParams();
64 -
6574 if (!is_null($params['prop']) && is_null($resultPageSet)) {
6675
6776 $prop = array_flip($params['prop']);
@@ -122,7 +131,7 @@
123132 'recentchanges'
124133 ));
125134
126 - $userId = $wgUser->getId();
 135+ $userId = $user->getId();
127136 $this->addWhere(array (
128137 'wl_namespace = rc_namespace',
129138 'wl_title = rc_title',
@@ -147,7 +156,8 @@
148157 $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show');
149158 }
150159
151 - // Check permissions
 160+ // Check permissions. FIXME: should this check $user instead of
 161+ // $wgUser?
152162 global $wgUser;
153163 if((isset($show['patrolled']) || isset($show['!patrolled'])) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol())
154164 $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied');
@@ -162,15 +172,19 @@
163173 $this->addWhereIf('rc_patrolled = 0', isset($show['!patrolled']));
164174 $this->addWhereIf('rc_patrolled != 0', isset($show['patrolled']));
165175 }
166 -
167 - if(!is_null($params['user']) && !is_null($params['excludeuser']))
168 - $this->dieUsage('user and excludeuser cannot be used together', 'user-excludeuser');
169 - if(!is_null($params['user']))
170 - $this->addWhereFld('rc_user_text', $params['user']);
171 - if(!is_null($params['excludeuser']))
172 - $this->addWhere('rc_user_text != ' . $this->getDB()->addQuotes($params['excludeuser']));
173176
 177+ # Ignore extra user conditions if we're using token mode, since the
 178+ # user was already manually specified.
 179+ if(is_null($params['user']) || is_null($params['token'])) {
 180+ if(!is_null($params['user']) && !is_null($params['excludeuser']))
 181+ $this->dieUsage('user and excludeuser cannot be used together', 'user-excludeuser');
 182+ if(!is_null($params['user']))
 183+ $this->addWhereFld('rc_user_text', $params['user']);
 184+ if(!is_null($params['excludeuser']))
 185+ $this->addWhere('rc_user_text != ' . $this->getDB()->addQuotes($params['excludeuser']));
 186+ }
174187
 188+
175189 # This is an index optimization for mysql, as done in the Special:Watchlist page
176190 $this->addWhereIf("rc_timestamp > ''", !isset ($params['start']) && !isset ($params['end']) && $wgDBtype == 'mysql');
177191
@@ -321,6 +335,9 @@
322336 'patrolled',
323337 '!patrolled',
324338 )
 339+ ),
 340+ 'token' => array (
 341+ ApiBase :: PARAM_TYPE => 'string'
325342 )
326343 );
327344 }
@@ -339,7 +356,8 @@
340357 'show' => array (
341358 'Show only items that meet this criteria.',
342359 'For example, to see only minor edits done by logged-in users, set show=minor|!anon'
343 - )
 360+ ),
 361+ 'token' => "Give a security token (settable in preferences) to allow access to another user's watchlist"
344362 );
345363 }
346364
Index: trunk/phase3/includes/api/ApiFeedWatchlist.php
@@ -75,6 +75,11 @@
7676 'wllimit' => (50 > $wgFeedLimit) ? $wgFeedLimit : 50
7777 );
7878
 79+ if (!is_null($params['wluser']))
 80+ $fauxReqArr['wluser'] = $params['wluser'];
 81+ if (!is_null($params['wltoken']))
 82+ $fauxReqArr['wltoken'] = $params['wltoken'];
 83+
7984 // Check for 'allrev' parameter, and if found, show all revisions to each page on wl.
8085 if ( ! is_null ( $params['allrev'] ) ) $fauxReqArr['wlallrev'] = '';
8186
@@ -152,7 +157,13 @@
153158 ApiBase :: PARAM_MIN => 1,
154159 ApiBase :: PARAM_MAX => 72,
155160 ),
156 - 'allrev' => null
 161+ 'allrev' => null,
 162+ 'wluser' => array (
 163+ ApiBase :: PARAM_TYPE => 'user'
 164+ ),
 165+ 'wltoken' => array (
 166+ ApiBase :: PARAM_TYPE => 'string'
 167+ )
157168 );
158169 }
159170
@@ -160,7 +171,9 @@
161172 return array (
162173 'feedformat' => 'The format of the feed',
163174 'hours' => 'List pages modified within this many hours from now',
164 - 'allrev' => 'Include multiple revisions of the same page within given timeframe.'
 175+ 'allrev' => 'Include multiple revisions of the same page within given timeframe.',
 176+ 'wluser' => "The user whose watchlist you want (must be accompanied by wltoken if it's not you)",
 177+ 'wltoken' => 'Security token that requested user set in their preferences'
165178 );
166179 }
167180
Index: trunk/phase3/includes/Preferences.php
@@ -746,7 +746,7 @@
747747 }
748748
749749 static function watchlistPreferences( $user, &$defaultPreferences ) {
750 - global $wgUseRCPatrol;
 750+ global $wgUseRCPatrol, $wgEnableAPI;
751751 ## Watchlist #####################################
752752 $defaultPreferences['watchlistdays'] =
753753 array(
@@ -800,6 +800,17 @@
801801 'section' => 'watchlist/advancedwatchlist',
802802 'label-message' => 'tog-watchlisthideliu',
803803 );
 804+ if ( $wgEnableAPI ) {
 805+ # Some random gibberish as a proposed default
 806+ $hash = sha1( mt_rand() . microtime( true ) );
 807+ $defaultPreferences['watchlisttoken'] =
 808+ array(
 809+ 'type' => 'text',
 810+ 'section' => 'watchlist/advancedwatchlist',
 811+ 'label-message' => 'prefs-watchlist-token',
 812+ 'help' => wfMsgHtml( 'prefs-help-watchlist-token', $hash )
 813+ );
 814+ }
804815
805816 if ( $wgUseRCPatrol ) {
806817 $defaultPreferences['watchlisthidepatrolled'] =
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -1700,6 +1700,7 @@
17011701 'prefs-watchlist-days-max' => '(maximum 7 days)',
17021702 'prefs-watchlist-edits' => 'Maximum number of changes to show in expanded watchlist:',
17031703 'prefs-watchlist-edits-max' => '(maximum number: 1000)',
 1704+'prefs-watchlist-token' => 'Watchlist token',
17041705 'prefs-misc' => 'Misc',
17051706 'prefs-resetpass' => 'Change password',
17061707 'prefs-email' => 'E-mail options',
@@ -1720,6 +1721,9 @@
17211722 'recentchangesdays-max' => '(maximum $1 {{PLURAL:$1|day|days}})',
17221723 'recentchangescount' => 'Number of edits to show by default:',
17231724 'prefs-help-recentchangescount' => 'This includes recent changes, page histories, and logs.',
 1725+'prefs-help-watchlist-token' => "Filling in this field with a secret key will generate an RSS feed for your watchlist.
 1726+Anyone who knows the key in this field will be able to read your watchlist, so choose a secure value.
 1727+Here's a randomly-generated value you can use: $1",
17241728 'savedprefs' => 'Your preferences have been saved.',
17251729 'timezonelegend' => 'Time zone:',
17261730 'localtime' => 'Local time:',

Follow-up revisions

RevisionCommit summaryAuthorDate
r53782Don't overload wluser parameter, use new wlowner...simetrical17:04, 26 July 2009

Comments

#Comment by Catrope (talk | contribs)   10:40, 25 July 2009

You're using the user parameter in list=watchlist for two different purposes: one for the token-based auth, and one for filtering by user name. Please add a new parameter for the former.

#Comment by Simetrical (talk | contribs)   01:57, 26 July 2009

I noticed that, but is it really a problem? If you do the token-based auth, you're automatically filtering by the username anyway, since you retrieve the user id based on the user name.

#Comment by Catrope (talk | contribs)   09:35, 26 July 2009

You don't get what wluser does: it filters the contributions by user name. For instance, if I request list=watchlist&wluser=Simetrical , I would only get pages on my watchlist that were last changed by you (or, if I used &wlallrev , only changes to pages on my watchlist made by you).

#Comment by Simetrical (talk | contribs)   13:49, 26 July 2009

Ah, okay, that's totally borked, then. I'll change it.

#Comment by Simetrical (talk | contribs)   17:04, 26 July 2009

Should be fixed in r53782.

Status & tagging log