Page MenuHomePhabricator

471-2.diff

Authored By
bzimport
Nov 21 2014, 7:08 PM
Size
16 KB
Referenced Files
None
Subscribers
None

471-2.diff

Index: maintenance/archives/patch-user_watchlist_token.sql
===================================================================
--- maintenance/archives/patch-user_watchlist_token.sql (revision 0)
+++ maintenance/archives/patch-user_watchlist_token.sql (revision 0)
@@ -0,0 +1,10 @@
+--
+-- New user field for syndicated watchlists
+-- Wil Mahan <wmahan at gmail dot com>, September 2006
+--
+
+ALTER TABLE /*$wgDBprefix*/user
+ -- Pseudorandomly generated token for watchlist feeds
+ -- Accounts contain NULL by default.
+ ADD user_watchlist_token CHAR(32) BINARY;
+
Index: maintenance/updaters.inc
===================================================================
--- maintenance/updaters.inc (revision 16398)
+++ maintenance/updaters.inc (working copy)
@@ -44,6 +44,7 @@
array( 'user', 'user_token', 'patch-user_token.sql' ),
array( 'user', 'user_email_token', 'patch-user_email_token.sql' ),
array( 'user', 'user_registration','patch-user_registration.sql' ),
+ array( 'user', 'user_watchlist_token','patch-user_watchlist_token.sql' ),
array( 'logging', 'log_params', 'patch-log_params.sql' ),
array( 'archive', 'ar_rev_id', 'patch-archive-rev_id.sql' ),
array( 'archive', 'ar_text_id', 'patch-archive-text_id.sql' ),
Index: maintenance/tables.sql
===================================================================
--- maintenance/tables.sql (revision 16398)
+++ maintenance/tables.sql (working copy)
@@ -110,6 +110,11 @@
-- Accounts predating this schema addition may contain NULL.
user_registration char(14) binary,
+ -- Initially NULL; when a user enables watchlist syndication,
+ -- this is set to a pseudorandomly generated token for
+ -- authorization
+ user_watchlist_token char(32) binary,
+
PRIMARY KEY user_id (user_id),
UNIQUE INDEX user_name (user_name),
INDEX (user_email_token)
Index: includes/User.php
===================================================================
--- includes/User.php (revision 16398)
+++ includes/User.php (working copy)
@@ -44,6 +44,7 @@
var $mTouched; //!<
var $mDatePreference; // !<
var $mVersion; //!< serialized version
+ var $mWatchlistToken; //!<
/**@}} */
/**
@@ -86,6 +87,7 @@
'numberheadings' => 0,
'uselivepreview' => 0,
'watchlistdays' => 3.0,
+ 'publicwatchlist' => 0,
);
static public $mToggles = array(
@@ -122,6 +124,7 @@
'forceeditsummary',
'watchlisthideown',
'watchlisthidebots',
+ 'publicwatchlist',
);
/** Constructor using User:loadDefaults() */
@@ -211,6 +214,7 @@
'mToken',
'mTouched',
'mVersion',
+'mWatchlistToken'
);
}
@@ -434,6 +438,7 @@
$this->mBlockedby = -1; # Unset
$this->setToken(); # Random
$this->mHash = false;
+ $this->mWatchlistToken = null;
if ( isset( $_COOKIE[$wgCookiePrefix.'LoggedOut'] ) ) {
$this->mTouched = wfTimestamp( TS_MW, $_COOKIE[$wgCookiePrefix.'LoggedOut'] );
@@ -828,7 +833,7 @@
$dbr =& wfGetDB( DB_SLAVE );
$s = $dbr->selectRow( 'user', array( 'user_name','user_password','user_newpassword','user_email',
'user_email_authenticated',
- 'user_real_name','user_options','user_touched', 'user_token', 'user_registration' ),
+ 'user_real_name','user_options','user_touched', 'user_token', 'user_registration', 'user_watchlist_token' ),
array( 'user_id' => $this->mId ), $fname );
if ( $s !== false ) {
@@ -842,6 +847,7 @@
$this->mTouched = wfTimestamp(TS_MW,$s->user_touched);
$this->mToken = $s->user_token;
$this->mRegistration = wfTimestampOrNull( TS_MW, $s->user_registration );
+ $this->mWatchlistToken = $s->user_watchlist_token;
$res = $dbr->select( 'user_groups',
array( 'ug_group' ),
@@ -1556,7 +1562,8 @@
'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
'user_options' => $this->encodeOptions(),
'user_touched' => $dbw->timestamp($this->mTouched),
- 'user_token' => $this->mToken
+ 'user_token' => $this->mToken,
+ 'user_watchlist_token' => $this->mWatchlistToken,
), array( /* WHERE */
'user_id' => $this->mId
), $fname
@@ -1997,6 +2004,31 @@
}
/**
+ * Generate, store, and return a new watchlist authentication
+ * token.
+ *
+ * @return string
+ * @public
+ */
+ function resetWatchlistToken() {
+ $fname = 'User::watchlistToken';
+ $this->mWatchlistToken = $this->generateToken( $this->mId );
+ $this->invalidateCache();
+ $this->saveSettings();
+ return $this->mWatchlistToken;
+ }
+
+ /**
+ * Return the watchlist authentication token for this user.
+ *
+ * @public
+ */
+ function getWatchlistToken() {
+ $this->loadFromDatabase();
+ return $this->mWatchlistToken;
+ }
+
+ /**
* @param array $groups list of groups
* @return array list of permission key names for given groups combined
* @static
Index: includes/Feed.php
===================================================================
--- includes/Feed.php (revision 16398)
+++ includes/Feed.php (working copy)
@@ -74,6 +74,29 @@
function getAuthor() { return $this->xmlEncode( $this->Author ); }
function getComments() { return $this->xmlEncode( $this->Comments ); }
/**#@-*/
+
+ /**
+ * Get the URL for a syndicated feed
+ *
+ * @static
+ * @param string $format The feed format, such as "rss" or "atom"
+ * @return The URL for this page's feed
+ */
+ function feedUrl( $format ) {
+ global $wgRequest, $wgTitle, $wgUser;
+
+ $isWatchlist = $wgTitle->equals( Title::makeTitle( NS_SPECIAL, 'Watchlist' ) );
+ if ($isWatchlist) {
+ $userName = wfUrlEncode( $wgUser->getTitlekey() );
+ $token = $wgUser->getWatchlistToken();
+ $limit = $wgUser->getOption( 'wllimit' );
+ $url = $wgTitle->getLocalUrl( "feed=$format&user=$userName&token=$token&limit=$limit" );
+ }
+ else {
+ $url = $wgRequest->escapeAppendQuery( "feed=$format" );
+ }
+ return $url;
+ }
}
/**
Index: includes/OutputPage.php
===================================================================
--- includes/OutputPage.php (revision 16398)
+++ includes/OutputPage.php (working copy)
@@ -1041,9 +1041,9 @@
}
if( $this->isSyndicated() ) {
# FIXME: centralize the mime-type and name information in Feed.php
- $link = $wgRequest->escapeAppendQuery( 'feed=rss' );
+ $link = FeedItem::feedUrl( 'rss' );
$ret .= "<link rel='alternate' type='application/rss+xml' title='RSS 2.0' href='$link' />\n";
- $link = $wgRequest->escapeAppendQuery( 'feed=atom' );
+ $link = FeedItem::feedUrl( 'atom' );
$ret .= "<link rel='alternate' type='application/atom+xml' title='Atom 1.0' href='$link' />\n";
}
Index: includes/SpecialWatchlist.php
===================================================================
--- includes/SpecialWatchlist.php (revision 16398)
+++ includes/SpecialWatchlist.php (working copy)
@@ -22,6 +22,16 @@
global $wgEnotifWatchlist;
$fname = 'wfSpecialWatchlist';
+ # Handle syndicated watchlists
+ if( wlHandleFeed( $wgOut, $wgRequest, $par ) ) {
+ return;
+ }
+
+ $syndicate = $wgUser->getOption( 'publicwatchlist' );
+ if( $syndicate ) {
+ $wgOut->setSyndicated( true );
+ }
+
$skin =& $wgUser->getSkin();
$specialTitle = Title::makeTitle( NS_SPECIAL, 'Watchlist' );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
@@ -512,4 +522,163 @@
return( false );
}
}
+
+/**
+ * Allow syndication of watchlists using "feed=..."
+ *
+ * @param $out Output object
+ * @param $request Request object
+ * @param $par Parameters passed to the watchlist page
+ * @return bool True if it's been taken care of; false indicates the watchlist
+ * code needs to do something further
+ */
+function wlHandleFeed( &$out, &$request, $par ) {
+ global $wgUser, $wgLang;
+ global $wgSyndicateWatchlists, $wgFeedClasses;
+ global $wgSitename, $wgShowUpdatedMarker;
+
+ $fname = 'wlHandleFeed';
+ if ( !$wgSyndicateWatchlists ) {
+ return false;
+ }
+
+ $feedFormat = $request->getVal( 'feed' );
+ if( is_null( $feedFormat ) || !isset( $wgFeedClasses[$feedFormat] ) ) {
+ return false;
+ }
+
+ $specialTitle = Title::makeTitle( NS_SPECIAL, 'Watchlist' );
+
+ $feedUserName = $request->getVal( 'user' );
+ if ( !is_null($feedUserName) ) {
+ $wlUser = User::newFromName( $feedUserName );
+ if( is_null( $wlUser ) ) {
+ # invalid user name for feed
+ $out->showErrorPage( 'noname', 'nosuchusershort' );
+ return true;
+ }
+
+ $public = $wlUser->getOption( 'publicwatchlist' );
+ if ( !$public ) {
+ $out->showErrorPage( 'badaccess', 'feedaccesdenied' );
+ return true;
+ }
+
+ $token = $request->getVal( 'token' );
+ if( !$token || $token !== $wlUser->getWatchlistToken() ) {
+ $out->showErrorPage( 'badaccess', 'feedacessdenied' );
+ return true;
+ }
+ }
+ else if ( !$wgUser->isAnon() ) {
+ # If no user is given, display feed for our own watchlist
+ $wlUser = $wgUser;
+ }
+ else {
+ # Display "not logged in" error page
+ $skin =& $wgUser->getSkin();
+ $out->setPageTitle( wfMsg( 'watchnologin' ) );
+ $llink = $skin->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Userlogin' ), wfMsgHtml( 'loginreqlink' ), 'returnto=' . $specialTitle->getPrefixedUrl() );
+ $out->addHtml( wfMsgWikiHtml( 'watchlistanontext', $llink ) );
+ return true;
+ }
+ $uid = $wlUser->getID();
+
+ # Set limit
+ $limit = intval( $wgUser->getOption( 'wllimit' ) );
+ if ( !$limit ) {
+ $limit = $wgUser->getDefaultOption('wllimit');
+ }
+ $limit = $request->getInt( 'limit', $limit );
+ global $wgFeedLimit;
+ if( $limit > $wgFeedLimit ) {
+ $limit = $wgFeedLimit;
+ }
+
+ $unseen = $request->getBool( 'unseen' );
+
+ # Force feeds to use the following settings
+ $cutoff = false;
+ $andLatest = '';
+ $andHideOwn = '';
+ $andHideBots = '';
+ $nameSpaceClause = '';
+ $limitWatchlist = "LIMIT $limit";
+ $npages = wfMsg( 'watchlistall1' );
+
+ $dbr =& wfGetDB( DB_SLAVE );
+ extract( $dbr->tableNames( 'page', 'revision', 'watchlist', 'recentchanges' ) );
+
+ # Get the number of pages found
+ $sql = "SELECT COUNT(*) AS n FROM $watchlist WHERE wl_user=$uid";
+ $res = $dbr->query( $sql, $fname );
+ $s = $dbr->fetchObject( $res );
+ $nitems = floor($s->n / 2);
+
+ $sql = "SELECT
+ rc_namespace AS page_namespace, rc_title AS page_title,
+ rc_comment AS rev_comment, rc_cur_id AS page_id,
+ rc_user AS rev_user, rc_user_text AS rev_user_text,
+ rc_timestamp AS rev_timestamp, rc_minor AS rev_minor_edit,
+ rc_this_oldid AS rev_id,
+ rc_last_oldid, rc_id, rc_patrolled,
+ rc_new AS page_is_new,wl_notificationtimestamp
+ FROM $watchlist,$recentchanges,$page
+ WHERE wl_user=$uid
+ AND wl_namespace=rc_namespace
+ AND wl_title=rc_title
+ AND rc_timestamp > '$cutoff'
+ AND rc_cur_id=page_id
+ $andLatest
+ $andHideOwn
+ $andHideBots
+ $nameSpaceClause
+ ORDER BY rc_timestamp DESC
+ $limitWatchlist";
+
+ $res = $dbr->query( $sql, $fname );
+
+ $feed = new $wgFeedClasses[$feedFormat]
+ (
+ $wgSitename . ' - ' . wfMsg( 'watchlist' ) . ' ' . wfMsgWikiHtml( 'watchlistfor', htmlspecialchars( $wlUser->getName() ) ),
+ wfMsgWikiHtml( "watchdetails",
+ $wgLang->formatNum( $nitems ),
+ $wgLang->formatNum( $npages ),
+ '',
+ $specialTitle->escapeLocalUrl( )
+ )
+ ,
+ $specialTitle->getFullUrl()
+ );
+ $feed->outHeader();
+
+ while ( $obj = $dbr->fetchObject( $res ) ) {
+ if ( $wgShowUpdatedMarker ) {
+ $updated = $obj->wl_notificationtimestamp;
+ } else {
+ $updated = true;
+ }
+ # With the "unseen" option, skip pages that weren't updated
+ if ( $unseen && !$updated ) {
+ continue;
+ }
+ $title = Title::makeTitle( $obj->page_namespace, $obj->page_title );
+ $talkpage = $title->getTalkPage();
+ $item = new FeedItem(
+ $title->getPrefixedText(),
+ htmlspecialchars( $obj->rev_comment ),
+ $title->getFullURL(),
+ $obj->rev_timestamp,
+ $obj->rev_user_text,
+ $talkpage->getFullURL()
+ );
+ $feed->outItem( $item );
+ }
+ $feed->outFooter();
+
+ $dbr->freeResult( $res );
+
+ return true;
+}
+
?>
Index: includes/SkinTemplate.php
===================================================================
--- includes/SkinTemplate.php (revision 16398)
+++ includes/SkinTemplate.php (working copy)
@@ -222,7 +222,7 @@
foreach( $wgFeedClasses as $format => $class ) {
$feeds[$format] = array(
'text' => $format,
- 'href' => $wgRequest->appendQuery( "feed=$format" )
+ 'href' => FeedItem::feedUrl( $format )
);
}
$tpl->setRef( 'feeds', $feeds );
Index: includes/SpecialPreferences.php
===================================================================
--- includes/SpecialPreferences.php (revision 16398)
+++ includes/SpecialPreferences.php (working copy)
@@ -28,6 +28,7 @@
var $mSearch, $mRecent, $mHourDiff, $mSearchLines, $mSearchChars, $mAction;
var $mReset, $mPosted, $mToggles, $mSearchNs, $mRealName, $mImageSize;
var $mUnderline, $mWatchlistEdits;
+ var $mResetWatchlistToken;
/**
* Constructor
@@ -66,6 +67,7 @@
$this->mSuccess = $request->getCheck( 'success' );
$this->mWatchlistDays = $request->getVal( 'wpWatchlistDays' );
$this->mWatchlistEdits = $request->getVal( 'wpWatchlistEdits' );
+ $this->mResetWatchlistToken = $request->getCheck( 'wpOpresetwatchlisttoken' );
$this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) &&
$this->mPosted &&
@@ -208,6 +210,7 @@
global $wgEnableUserEmail, $wgEnableEmail;
global $wgEmailAuthentication, $wgMinimalPasswordLength;
global $wgAuth;
+ global $wgSyndicateWatchlists;
if ( '' != $this->mNewpass && $wgAuth->allowPasswordChange() ) {
@@ -326,6 +329,13 @@
$wgUser->saveSettings();
}
}
+
+ if( $wgSyndicateWatchlists && $wgUser->getOption( 'publicwatchlist' ) ) {
+ if( is_null( $wgUser->getWatchlistToken() ) || $this->mResetWatchlistToken ) {
+ # generate a new watchlist token
+ $wgUser->resetWatchlistToken();
+ }
+ }
if( $needRedirect && $error === false ) {
$title =& Title::makeTitle( NS_SPECIAL, "Preferences" );
@@ -869,6 +879,29 @@
$wgOut->addHTML( $this->getToggles( array( 'watchlisthideown', 'watchlisthidebots', 'extendwatchlist' ) ) );
$wgOut->addHTML( wfInputLabel( wfMsg( 'prefs-watchlist-edits' ),
'wpWatchlistEdits', 'wpWatchlistEdits', 3, $this->mWatchlistEdits ) );
+ global $wgSyndicateWatchlists;
+ if( $wgSyndicateWatchlists ) {
+ $wgOut->addHTML( '<br /><br />' ); # Spacing
+ $wgOut->addHTML( $this->getToggles( array( 'publicwatchlist' ) ) );
+ if( $wgUser->getOption( 'publicwatchlist' ) ) {
+ $skin = $wgUser->getSkin();
+ $token = $wgUser->getWatchlistToken();
+ $userName = wfUrlencode( $wgUser->getTitlekey() );
+ $limit = $wgUser->getOption( 'wllimit' );
+ $specialTitle = Title::makeTitle( NS_SPECIAL, 'Watchlist' );
+ global $wgFeedClasses;
+ $s = '';
+ foreach( $wgFeedClasses as $format => $class ) {
+ $s .= $skin->makeKnownLinkObj( $specialTitle, $format, "feed=$format&user=$userName&token=$token&limit=$limit" ) . ' ';
+ }
+ $wgOut->addHTML( wfMsgWikiHtml( 'syndicatedwatchlistwarning', $s ) );
+ $tname = 'resetwatchlisttoken';
+ $ttext = wfMsg( 'resetwatchlisttoken' );
+ $wgOut->addHTML(
+ "<div class='toggle'><input type='checkbox' value='1' id=\"$tname\" name=\"wpOp$tname\" />" .
+ " <span class='toggletext'><label for=\"$tname\">$ttext</label></span></div>\n" );
+ }
+ }
$wgOut->addHTML( '</fieldset>' );
Index: includes/DefaultSettings.php
===================================================================
--- includes/DefaultSettings.php (revision 16398)
+++ includes/DefaultSettings.php (working copy)
@@ -1671,7 +1671,10 @@
* pages larger than this size. */
$wgFeedDiffCutoff = 32768;
+/** Allow users to share their watchlists in Atom or RSS feeds */
+$wgSyndicateWatchlists = true;
+
/**
* Additional namespaces. If the namespaces defined in Language.php and
* Namespace.php are insufficient, you can create new ones here, for example,
Index: includes/Skin.php
===================================================================
--- includes/Skin.php (revision 16398)
+++ includes/Skin.php (working copy)
@@ -752,7 +752,7 @@
$s = "<a href=\"$printurl\">" . wfMsg( 'printableversion' ) . '</a>';
if( $wgOut->isSyndicated() ) {
foreach( $wgFeedClasses as $format => $class ) {
- $feedurl = $wgRequest->escapeAppendQuery( "feed=$format" );
+ $feedurl = wfFeedUrl( $format );
$s .= " | <a href=\"$feedurl\">{$format}</a>";
}
}
Index: languages/MessagesEn.php
===================================================================
--- languages/MessagesEn.php (revision 16398)
+++ languages/MessagesEn.php (working copy)
@@ -364,7 +364,12 @@
'tog-forceeditsummary' => 'Prompt me when entering a blank edit summary',
'tog-watchlisthideown' => 'Hide my edits from the watchlist',
'tog-watchlisthidebots' => 'Hide bot edits from the watchlist',
+'tog-publicwatchlist' => 'Allow sharing my watchlist',
+'syndicatedwatchlistwarning' => "My watchlist feeds: $1 ('''''Note''': anyone who knows these URLs can see your watchlist.)''",
+'resetwatchlisttoken' => 'Reset my watchlist feed URLs now',
+'feedacessdenied' => 'You do not have permission to access this feed.',
+
'underline-always' => 'Always',
'underline-never' => 'Never',
'underline-default' => 'Browser default',

File Metadata

Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1187
Default Alt Text
471-2.diff (16 KB)

Event Timeline