r23693 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r23692‎ | r23693 | r23694 >
Date:01:52, 4 July 2007
Author:robchurch
Status:old
Tags:
Comment:
Move all watchlist editing into WatchlistEditor class, integrating "raw editing" from ExportWatchlist extension, per Brion. Spiffed up the existing editor and "clear" interface; all three editors better integrated from a UI perspective.
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/SpecialWatchlist.php (modified) (history)
  • /trunk/phase3/includes/WatchlistEditor.php (added) (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
@@ -1080,10 +1080,6 @@
10811081 'nowatchlist',
10821082 'watchlistanontext',
10831083 'watchlistcount',
1084 - 'clearwatchlist',
1085 - 'watchlistcleartext',
1086 - 'watchlistclearbutton',
1087 - 'watchlistcleardone',
10881084 'watchnologin',
10891085 'watchnologintext',
10901086 'addedwatch',
@@ -1101,11 +1097,7 @@
11021098 'wlheader-showupdated',
11031099 'watchmethod-recent',
11041100 'watchmethod-list',
1105 - 'removechecked',
11061101 'watchlistcontains',
1107 - 'watcheditlist',
1108 - 'removingchecked',
1109 - 'couldntremove',
11101102 'iteminvalidname',
11111103 'wlnote',
11121104 'wlshowlast',
@@ -1116,7 +1108,6 @@
11171109 'watchlist-hide-own',
11181110 'watchlist-show-minor',
11191111 'watchlist-hide-minor',
1120 - 'wldone',
11211112 ),
11221113 'watching' => array(
11231114 'watching',
@@ -2131,6 +2122,26 @@
21322123 'lag-warn-normal',
21332124 'lag-warn-high',
21342125 ),
 2126+ 'watchlisteditor' => array(
 2127+ 'watchlistedit-numitems',
 2128+ 'watchlistedit-noitems',
 2129+ 'watchlistedit-clear-title',
 2130+ 'watchlistedit-clear-legend',
 2131+ 'watchlistedit-clear-confirm',
 2132+ 'watchlistedit-clear-submit',
 2133+ 'watchlistedit-clear-done',
 2134+ 'watchlistedit-normal-title',
 2135+ 'watchlistedit-normal-legend',
 2136+ 'watchlistedit-normal-explain',
 2137+ 'watchlistedit-normal-submit',
 2138+ 'watchlistedit-normal-done',
 2139+ 'watchlistedit-raw-title',
 2140+ 'watchlistedit-raw-legend',
 2141+ 'watchlistedit-raw-explain',
 2142+ 'watchlistedit-raw-titles',
 2143+ 'watchlistedit-raw-submit',
 2144+ 'watchlistedit-raw-done',
 2145+ ),
21352146 );
21362147 /** Comments for each block */
21372148 $wgBlockComments = array(
@@ -2297,6 +2308,7 @@
22982309 'sizeunits' => 'Size units',
22992310 'livepreview' => 'Live preview',
23002311 'lagwarning' => 'Friendlier slave lag warnings',
 2312+ 'watchlisteditor' => 'Watchlist editor',
23012313 );
23022314
23032315 /** Short comments for standalone messages */
Index: trunk/phase3/includes/WatchlistEditor.php
@@ -0,0 +1,393 @@
 2+<?php
 3+
 4+/**
 5+ * Provides the UI through which users can perform editing
 6+ * operations on their watchlist
 7+ *
 8+ * @addtogroup Watchlist
 9+ * @author Rob Church <robchur@gmail.com>
 10+ */
 11+class WatchlistEditor {
 12+
 13+ /**
 14+ * Editing modes
 15+ */
 16+ const EDIT_CLEAR = 1;
 17+ const EDIT_RAW = 2;
 18+ const EDIT_NORMAL = 3;
 19+
 20+ /**
 21+ * Main execution point
 22+ *
 23+ * @param User $user
 24+ * @param OutputPage $output
 25+ * @param WebRequest $request
 26+ * @param int $mode
 27+ */
 28+ public function execute( $user, $output, $request, $mode ) {
 29+ if( wfReadOnly() ) {
 30+ $output->readOnlyPage();
 31+ return;
 32+ }
 33+ switch( $mode ) {
 34+ case self::EDIT_CLEAR:
 35+ $output->setPageTitle( wfMsg( 'watchlistedit-clear-title' ) );
 36+ if( $request->wasPosted() && $this->checkToken( $request, $user ) ) {
 37+ $this->clearWatchlist( $user );
 38+ $user->invalidateCache();
 39+ $output->addHtml( wfMsgExt( 'watchlistedit-clear-done', 'parse' ) );
 40+ } else {
 41+ $this->showClearForm( $output, $user );
 42+ }
 43+ break;
 44+ case self::EDIT_RAW:
 45+ $output->setPageTitle( wfMsg( 'watchlistedit-raw-title' ) );
 46+ if( $request->wasPosted() && $this->checkToken( $request, $user ) ) {
 47+ $titles = $this->extractTitles( $request->getText( 'titles' ) );
 48+ $this->clearWatchlist( $user );
 49+ $this->watchTitles( $titles, $user );
 50+ $user->invalidateCache();
 51+ $output->addHtml( wfMsgExt( 'watchlistedit-raw-done', 'parse' ) );
 52+ }
 53+ $this->showRawForm( $output, $user );
 54+ break;
 55+ case self::EDIT_NORMAL:
 56+ $output->setPageTitle( wfMsg( 'watchlistedit-normal-title' ) );
 57+ if( $request->wasPosted() && $this->checkToken( $request, $user ) ) {
 58+ $titles = $this->extractTitles( $request->getArray( 'titles' ) );
 59+ $this->unwatchTitles( $titles, $user );
 60+ $user->invalidateCache();
 61+ $output->addHtml( wfMsgExt( 'watchlistedit-normal-done', 'parse',
 62+ $GLOBALS['wgLang']->formatNum( count( $titles ) ) ) );
 63+ $this->showTitles( $titles, $output, $user->getSkin() );
 64+ }
 65+ $this->showNormalForm( $output, $user );
 66+ }
 67+ }
 68+
 69+ /**
 70+ * Check the edit token from a form submission
 71+ *
 72+ * @param WebRequest $request
 73+ * @param User $user
 74+ * @return bool
 75+ */
 76+ private function checkToken( $request, $user ) {
 77+ return $user->matchEditToken( $request->getVal( 'token' ), 'watchlistedit' );
 78+ }
 79+
 80+ /**
 81+ * Extract a list of titles from a text list; if we're given
 82+ * an array, convert each item into a Title
 83+ *
 84+ * @param mixed $list
 85+ * @return array
 86+ */
 87+ private function extractTitles( $list ) {
 88+ if( !is_array( $list ) ) {
 89+ $list = explode( "\n", $list );
 90+ if( !is_array( $list ) )
 91+ return array();
 92+ }
 93+ for( $i = 0; $i < count( $list ); $i++ ) {
 94+ $list[$i] = Title::newFromText( $list[$i] );
 95+ if( !$list[$i] instanceof Title )
 96+ unset( $list[$i] );
 97+ }
 98+ return $list;
 99+ }
 100+
 101+ /**
 102+ * Print out a list of linked titles
 103+ *
 104+ * @param array $titles
 105+ * @param OutputPage $output
 106+ * @param Skin $skin
 107+ */
 108+ private function showTitles( $titles, $output, $skin ) {
 109+ $talk = htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( NS_TALK ) );
 110+ // Do a batch existence check
 111+ $batch = new LinkBatch();
 112+ foreach( $titles as $title ) {
 113+ $batch->addObj( $title );
 114+ $batch->addObj( $title->getTalkPage() );
 115+ }
 116+ $batch->execute();
 117+ // Print out the list
 118+ $output->addHtml( "<ul>\n" );
 119+ foreach( $titles as $title )
 120+ $output->addHtml( "<li>" . $skin->makeLinkObj( $title )
 121+ . ' (' . $skin->makeLinkObj( $title->getTalkPage(), $talk ) . ")</li>\n" );
 122+ $output->addHtml( "</ul>\n" );
 123+ }
 124+
 125+ /**
 126+ * Count the number of titles on a user's watchlist, excluding talk pages
 127+ *
 128+ * @param User $user
 129+ * @return int
 130+ */
 131+ private function countWatchlist( $user ) {
 132+ $dbr = wfGetDB( DB_SLAVE );
 133+ $res = $dbr->select( 'watchlist', 'COUNT(*) AS count', array( 'wl_user' => $user->getId() ), __METHOD__ );
 134+ $row = $dbr->fetchObject( $res );
 135+ return ceil( $row->count / 2 ); // Paranoia
 136+ }
 137+
 138+ /**
 139+ * Get a list of titles on a user's watchlist, excluding talk pages,
 140+ * and return as a two-dimensional array with namespace, title and
 141+ * redirect status
 142+ *
 143+ * @param User $user
 144+ * @return array
 145+ */
 146+ private function getWatchlist( $user ) {
 147+ $titles = array();
 148+ $dbr = wfGetDB( DB_SLAVE );
 149+ $uid = intval( $user->getId() );
 150+ list( $watchlist, $page ) = $dbr->tableNamesN( 'watchlist', 'page' );
 151+ $sql = "SELECT wl_namespace, wl_title, page_id, page_is_redirect
 152+ FROM {$watchlist} LEFT JOIN {$page} ON ( wl_namespace = page_namespace
 153+ AND wl_title = page_title ) WHERE wl_user = {$uid}";
 154+ $res = $dbr->query( $sql, __METHOD__ );
 155+ if( $res && $dbr->numRows( $res ) > 0 ) {
 156+ $cache = LinkCache::singleton();
 157+ while( $row = $dbr->fetchObject( $res ) ) {
 158+ $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
 159+ if( $title instanceof Title ) {
 160+ // Update the link cache while we're at it
 161+ if( $row->page_id ) {
 162+ $cache->addGoodLinkObj( $row->page_id, $title );
 163+ } else {
 164+ $cache->addBadLinkObj( $title );
 165+ }
 166+ // Ignore non-talk
 167+ if( !$title->isTalkPage() )
 168+ $titles[$row->wl_namespace][$row->wl_title] = $row->page_is_redirect;
 169+ }
 170+ }
 171+ }
 172+ return $titles;
 173+ }
 174+
 175+ /**
 176+ * Show a message indicating the number of items on the user's watchlist,
 177+ * and return this count for additional checking
 178+ *
 179+ * @param OutputPage $output
 180+ * @param User $user
 181+ * @return int
 182+ */
 183+ private function showItemCount( $output, $user ) {
 184+ if( ( $count = $this->countWatchlist( $user ) ) > 0 ) {
 185+ $output->addHtml( wfMsgExt( 'watchlistedit-numitems', 'parse',
 186+ $GLOBALS['wgLang']->formatNum( $count ) ) );
 187+ } else {
 188+ $output->addHtml( wfMsgExt( 'watchlistedit-noitems', 'parse' ) );
 189+ }
 190+ return $count;
 191+ }
 192+
 193+ /**
 194+ * Remove all titles from a user's watchlist
 195+ *
 196+ * @param User $user
 197+ */
 198+ private function clearWatchlist( $user ) {
 199+ $dbw = wfGetDB( DB_MASTER );
 200+ $dbw->delete( 'watchlist', array( 'wl_user' => $user->getId() ), __METHOD__ );
 201+ }
 202+
 203+ /**
 204+ * Add a list of titles to a user's watchlist
 205+ *
 206+ * @param array $titles
 207+ * @param User $user
 208+ */
 209+ private function watchTitles( $titles, $user ) {
 210+ $dbw = wfGetDB( DB_MASTER );
 211+ $rows = array();
 212+ foreach( $titles as $title ) {
 213+ $rows[] = array(
 214+ 'wl_user' => $user->getId(),
 215+ 'wl_namespace' => ( $title->getNamespace() & ~1 ),
 216+ 'wl_title' => $title->getDBkey(),
 217+ 'wl_notificationtimestamp' => null,
 218+ );
 219+ $rows[] = array(
 220+ 'wl_user' => $user->getId(),
 221+ 'wl_namespace' => ( $title->getNamespace() | 1 ),
 222+ 'wl_title' => $title->getDBkey(),
 223+ 'wl_notificationtimestamp' => null,
 224+ );
 225+ }
 226+ $dbw->insert( 'watchlist', $rows, __METHOD__, 'IGNORE' );
 227+ }
 228+
 229+ /**
 230+ * Remove a list of titles from a user's watchlist
 231+ *
 232+ * @param array $titles
 233+ * @param User $user
 234+ */
 235+ private function unwatchTitles( $titles, $user ) {
 236+ $dbw = wfGetDB( DB_MASTER );
 237+ foreach( $titles as $title ) {
 238+ $dbw->delete(
 239+ 'watchlist',
 240+ array(
 241+ 'wl_user' => $user->getId(),
 242+ 'wl_namespace' => ( $title->getNamespace() & ~1 ),
 243+ 'wl_title' => $title->getDBkey(),
 244+ ),
 245+ __METHOD__
 246+ );
 247+ $dbw->delete(
 248+ 'watchlist',
 249+ array(
 250+ 'wl_user' => $user->getId(),
 251+ 'wl_namespace' => ( $title->getNamespace() | 1 ),
 252+ 'wl_title' => $title->getDBkey(),
 253+ ),
 254+ __METHOD__
 255+ );
 256+ }
 257+ }
 258+
 259+ /**
 260+ * Show a confirmation form for users wishing to clear their watchlist
 261+ *
 262+ * @param OutputPage $output
 263+ * @param User $user
 264+ */
 265+ private function showClearForm( $output, $user ) {
 266+ if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) {
 267+ $self = SpecialPage::getTitleFor( 'Watchlist' );
 268+ $form = Xml::openElement( 'form', array( 'method' => 'post',
 269+ 'action' => $self->getLocalUrl( 'action=clear' ) ) );
 270+ $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) );
 271+ $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-clear-legend' ) . '</legend>';
 272+ $form .= wfMsgExt( 'watchlistedit-clear-confirm', 'parse' );
 273+ $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-clear-submit' ) ) . '</p>';
 274+ $form .= '</fieldset></form>';
 275+ $output->addHtml( $form );
 276+ }
 277+ }
 278+
 279+ /**
 280+ * Show the standard watchlist editing form
 281+ *
 282+ * @param OutputPage $output
 283+ * @param User $user
 284+ */
 285+ private function showNormalForm( $output, $user ) {
 286+ if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) {
 287+ $self = SpecialPage::getTitleFor( 'Watchlist' );
 288+ $form = Xml::openElement( 'form', array( 'method' => 'post',
 289+ 'action' => $self->getLocalUrl( 'action=edit' ) ) );
 290+ $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) );
 291+ $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-normal-legend' ) . '</legend>';
 292+ $form .= wfMsgExt( 'watchlistedit-normal-explain', 'parse' );
 293+ foreach( $this->getWatchlist( $user ) as $namespace => $pages ) {
 294+ $form .= '<h2>' . $this->getNamespaceHeading( $namespace ) . '</h2>';
 295+ $form .= '<ul>';
 296+ foreach( $pages as $dbkey => $redirect ) {
 297+ $title = Title::makeTitleSafe( $namespace, $dbkey );
 298+ $form .= $this->buildRemoveLine( $title, $redirect, $user->getSkin() );
 299+ }
 300+ $form .= '</ul>';
 301+ }
 302+ $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-normal-submit' ) ) . '</p>';
 303+ $form .= '</fieldset></form>';
 304+ $output->addHtml( $form );
 305+ }
 306+ }
 307+
 308+ /**
 309+ * Get the correct "heading" for a namespace
 310+ *
 311+ * @param int $namespace
 312+ * @return string
 313+ */
 314+ private function getNamespaceHeading( $namespace ) {
 315+ return $namespace == NS_MAIN
 316+ ? wfMsgHtml( 'blanknamespace' )
 317+ : htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( $namespace ) );
 318+ }
 319+
 320+ /**
 321+ * Build a single list item containing a check box selecting a title
 322+ * and a link to that title, with various additional bits
 323+ *
 324+ * @param Title $title
 325+ * @param bool $redirect
 326+ * @param Skin $skin
 327+ * @return string
 328+ */
 329+ private function buildRemoveLine( $title, $redirect, $skin ) {
 330+ $link = $skin->makeLinkObj( $title );
 331+ if( $redirect )
 332+ $link = '<span class="watchlistredir">' . $link . '</span>';
 333+ $tools[] = $skin->makeLinkObj( $title->getTalkPage(),
 334+ htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( NS_TALK ) ) );
 335+ if( $title->exists() )
 336+ $tools[] = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'history_short' ), 'action=history' );
 337+ return '<li>'
 338+ . Xml::check( 'titles[]', false, array( 'value' => $title->getPrefixedText() ) )
 339+ . $link . ' (' . implode( ' | ', $tools ) . ')' . '</li>';
 340+ }
 341+
 342+ /**
 343+ * Show a form for editing the watchlist in "raw" mode
 344+ *
 345+ * @param OutputPage $output
 346+ * @param User $user
 347+ */
 348+ public function showRawForm( $output, $user ) {
 349+ $this->showItemCount( $output, $user );
 350+ $self = SpecialPage::getTitleFor( 'Watchlist' );
 351+ $form = Xml::openElement( 'form', array( 'method' => 'post',
 352+ 'action' => $self->getLocalUrl( 'action=raw' ) ) );
 353+ $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) );
 354+ $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-raw-legend' ) . '</legend>';
 355+ $form .= wfMsgExt( 'watchlistedit-raw-explain', 'parse' );
 356+ $form .= Xml::label( wfMsg( 'watchlistedit-raw-titles' ), 'titles' );
 357+ $form .= Xml::openElement( 'textarea', array( 'id' => 'titles', 'name' => 'titles',
 358+ 'rows' => 6, 'cols' => 80 ) );
 359+ foreach( $this->getWatchlist( $user ) as $namespace => $pages ) {
 360+ foreach( $pages as $dbkey => $redirect ) {
 361+ $title = Title::makeTitleSafe( $namespace, $dbkey );
 362+ if( $title instanceof Title )
 363+ $form .= htmlspecialchars( $title->getPrefixedText() ) . "\n";
 364+ }
 365+ }
 366+ $form .= '</textarea>';
 367+ $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-raw-submit' ) ) . '</p>';
 368+ $form .= '</fieldset></form>';
 369+ $output->addHtml( $form );
 370+ }
 371+
 372+ /**
 373+ * Determine whether we are editing the watchlist, and if so, what
 374+ * kind of editing operation
 375+ *
 376+ * @param WebRequest $request
 377+ * @param mixed $par
 378+ * @return int
 379+ */
 380+ public static function getMode( $request, $par ) {
 381+ $mode = strtolower( $request->getVal( 'action', $par ) );
 382+ switch( $mode ) {
 383+ case 'clear':
 384+ return self::EDIT_CLEAR;
 385+ case 'raw':
 386+ return self::EDIT_RAW;
 387+ case 'edit':
 388+ return self::EDIT_NORMAL;
 389+ default:
 390+ return false;
 391+ }
 392+ }
 393+
 394+}
\ No newline at end of file
Property changes on: trunk/phase3/includes/WatchlistEditor.php
___________________________________________________________________
Added: svn:eol-style
1395 + native
Index: trunk/phase3/includes/AutoLoader.php
@@ -252,6 +252,7 @@
253253 'ZhClient' => 'includes/ZhClient.php',
254254 'memcached' => 'includes/memcached-client.php',
255255 'EmaillingJob' => 'includes/JobQueue.php',
 256+ 'WatchlistEditor' => 'includes/WatchlistEditor.php',
256257
257258 # filerepo
258259 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
Index: trunk/phase3/includes/SpecialWatchlist.php
@@ -35,10 +35,17 @@
3636 $wgOut->setSubtitle( wfMsgWikiHtml( 'watchlistfor', htmlspecialchars( $wgUser->getName() ) ) );
3737 }
3838
39 - if( wlHandleClear( $wgOut, $wgRequest, $par ) ) {
 39+ if( ( $mode = WatchlistEditor::getMode( $wgRequest, $par ) ) !== false ) {
 40+ $editor = new WatchlistEditor();
 41+ $editor->execute( $wgUser, $wgOut, $wgRequest, $mode );
4042 return;
4143 }
4244
 45+ $uid = $wgUser->getId();
 46+ if( $wgEnotifWatchlist && $wgRequest->getVal( 'reset' ) && $wgRequest->wasPosted() ) {
 47+ $wgUser->clearAllNotifications( $uid );
 48+ }
 49+
4350 $defaults = array(
4451 /* float */ 'days' => floatval( $wgUser->getOption( 'watchlistdays' ) ), /* 3.0 or 0.5, watch further below */
4552 /* bool */ 'hideOwn' => (int)$wgUser->getBoolOption( 'watchlisthideown' ),
@@ -72,37 +79,6 @@
7380 $nameSpaceClause = '';
7481 }
7582
76 - # Watchlist editing
77 - $action = $wgRequest->getVal( 'action' );
78 - $remove = $wgRequest->getVal( 'remove' );
79 - $id = $wgRequest->getArray( 'id' );
80 -
81 - $uid = $wgUser->getID();
82 - if( $wgEnotifWatchlist && $wgRequest->getVal( 'reset' ) && $wgRequest->wasPosted() ) {
83 - $wgUser->clearAllNotifications( $uid );
84 - }
85 -
86 - # Deleting items from watchlist
87 - if(($action == 'submit') && isset($remove) && is_array($id)) {
88 - $wgOut->addWikiText( wfMsg( 'removingchecked' ) );
89 - $wgOut->addHTML( "<ul id=\"mw-unwatch-list\">\n" );
90 - foreach($id as $one) {
91 - $t = Title::newFromURL( $one );
92 - if( !is_null( $t ) ) {
93 - $wl = WatchedItem::fromUserTitle( $wgUser, $t );
94 - if( $wl->removeWatch() === false ) {
95 - $wgOut->addHTML( '<li class="mw-unwatch-failure">' . wfMsg( 'couldntremove', htmlspecialchars($one) ) . "</li>\n" );
96 - } else {
97 - wfRunHooks('UnwatchArticle', array(&$wgUser, new Article($t)));
98 - $wgOut->addHTML( '<li class="mw-unwatch-success">[[' . htmlspecialchars($one) . "]]</li>\n" );
99 - }
100 - } else {
101 - $wgOut->addHTML( '<li class="mw-unwatch-invalid">' . wfMsg( 'iteminvalidname', htmlspecialchars($one) ) . "</li>\n" );
102 - }
103 - }
104 - $wgOut->addHTML( "</ul>\n<p>" . wfMsg( 'wldone' ) . "</p>\n" );
105 - }
106 -
10783 $dbr = wfGetDB( DB_SLAVE, 'watchlist' );
10884 list( $page, $watchlist, $recentchanges ) = $dbr->tableNamesN( 'page', 'watchlist', 'recentchanges' );
10985
@@ -155,81 +131,6 @@
156132 $npages = 40000 * $days;
157133 }
158134
159 - /* Edit watchlist form */
160 - if($wgRequest->getBool('edit') || $par == 'edit' ) {
161 - $wgOut->addWikiText( wfMsgExt( 'watchlistcontains', array( 'parseinline' ), $wgLang->formatNum( $nitems ) ) .
162 - "\n\n" . wfMsg( 'watcheditlist' ) );
163 -
164 - $wgOut->addHTML( '<form action=\'' .
165 - $specialTitle->escapeLocalUrl( 'action=submit' ) .
166 - "' method='post'>\n" );
167 -
168 -# Patch A2
169 -# The following was proposed by KTurner 07.11.2004 to T.Gries
170 -# $sql = "SELECT distinct (wl_namespace & ~1),wl_title FROM $watchlist WHERE wl_user=$uid";
171 - $sql = "SELECT wl_namespace, wl_title, page_is_redirect FROM $watchlist LEFT JOIN $page ON wl_namespace = page_namespace AND wl_title = page_title WHERE wl_user=$uid";
172 -
173 - $res = $dbr->query( $sql, $fname );
174 -
175 - # Batch existence check
176 - $linkBatch = new LinkBatch();
177 - while( $row = $dbr->fetchObject( $res ) )
178 - $linkBatch->addObj( Title::makeTitleSafe( $row->wl_namespace, $row->wl_title ) );
179 - $linkBatch->execute();
180 -
181 - if( $dbr->numRows( $res ) > 0 )
182 - $dbr->dataSeek( $res, 0 ); # Let's do the time warp again!
183 -
184 - $sk = $wgUser->getSkin();
185 -
186 - $list = array();
187 - while( $s = $dbr->fetchObject( $res ) ) {
188 - $list[$s->wl_namespace][$s->wl_title] = $s->page_is_redirect;
189 - }
190 -
191 - // TODO: Display a TOC
192 - foreach($list as $ns => $titles) {
193 - if (Namespace::isTalk($ns))
194 - continue;
195 - if ($ns != NS_MAIN)
196 - $wgOut->addHTML( '<h2>' . $wgContLang->getFormattedNsText( $ns ) . '</h2>' );
197 - $wgOut->addHTML( '<ul>' );
198 - foreach( $titles as $title => $redir ) {
199 - $titleObj = Title::makeTitle( $ns, $title );
200 - if( is_null( $titleObj ) ) {
201 - $wgOut->addHTML(
202 - '<!-- bad title "' .
203 - htmlspecialchars( $s->wl_title ) . '" in namespace ' . $s->wl_namespace . " -->\n"
204 - );
205 - } else {
206 - global $wgContLang;
207 - $toolLinks = array();
208 - $pageLink = $sk->makeLinkObj( $titleObj );
209 - $toolLinks[] = $sk->makeLinkObj( $titleObj->getTalkPage(), $wgLang->getNsText( NS_TALK ) );
210 - if( $titleObj->exists() )
211 - $toolLinks[] = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml( 'history_short' ), 'action=history' );
212 - $toolLinks = '(' . implode( ' | ', $toolLinks ) . ')';
213 - $checkbox = '<input type="checkbox" name="id[]" value="' . htmlspecialchars( $titleObj->getPrefixedText() ) . '" /> ' . ( $wgContLang->isRTL() ? '&rlm;' : '&lrm;' );
214 - if( $redir ) {
215 - $spanopen = '<span class="watchlistredir">';
216 - $spanclosed = '</span>';
217 - } else {
218 - $spanopen = $spanclosed = '';
219 - }
220 -
221 - $wgOut->addHTML( "<li>{$checkbox}{$spanopen}{$pageLink}{$spanclosed} {$toolLinks}</li>\n" );
222 - }
223 - }
224 - $wgOut->addHTML( '</ul>' );
225 - }
226 - $wgOut->addHTML(
227 - wfSubmitButton( wfMsg('removechecked'), array('name' => 'remove') ) .
228 - "\n</form>\n"
229 - );
230 -
231 - return;
232 - }
233 -
234135 # If the watchlist is relatively short, it's simplest to zip
235136 # down its entirety and then sort the results.
236137
@@ -463,54 +364,4 @@
464365 $count = floor( $count / 2 );
465366
466367 return( $count );
467 -}
468 -
469 -/**
470 - * Allow the user to clear their watchlist
471 - *
472 - * @param $out Output object
473 - * @param $request Request object
474 - * @param $par Parameters passed to the watchlist page
475 - * @return bool True if it's been taken care of; false indicates the watchlist
476 - * code needs to do something further
477 - */
478 -function wlHandleClear( &$out, &$request, $par ) {
479 - global $wgLang;
480 -
481 - # Check this function has something to do
482 - if( $request->getText( 'action' ) == 'clear' || $par == 'clear' ) {
483 - global $wgUser;
484 - $out->setPageTitle( wfMsgHtml( 'clearwatchlist' ) );
485 - $count = wlCountItems( $wgUser );
486 - if( $count > 0 ) {
487 - # See if we're clearing or confirming
488 - if( $request->wasPosted() && $wgUser->matchEditToken( $request->getText( 'token' ), 'clearwatchlist' ) ) {
489 - # Clearing, so do it and report the result
490 - $dbw = wfGetDB( DB_MASTER );
491 - $dbw->delete( 'watchlist', array( 'wl_user' => $wgUser->mId ), 'wlHandleClear' );
492 - $out->addWikiText( wfMsgExt( 'watchlistcleardone', array( 'parsemag', 'escape'), $wgLang->formatNum( $count ) ) );
493 - $out->returnToMain();
494 - } else {
495 - # Confirming, so show a form
496 - $wlTitle = SpecialPage::getTitleFor( 'Watchlist' );
497 - $out->addHTML( wfElement( 'form', array( 'method' => 'post', 'action' => $wlTitle->getLocalUrl( 'action=clear' ) ), NULL ) );
498 - $out->addWikiText( wfMsgExt( 'watchlistcount', array( 'parsemag', 'escape'), $wgLang->formatNum( $count ) ) );
499 - $out->addWikiText( wfMsg( 'watchlistcleartext' ) );
500 - $out->addHTML(
501 - wfHidden( 'token', $wgUser->editToken( 'clearwatchlist' ) ) .
502 - wfElement( 'input', array( 'type' => 'submit', 'name' => 'submit', 'value' => wfMsgHtml( 'watchlistclearbutton' ) ), '' ) .
503 - wfCloseElement( 'form' )
504 - );
505 - }
506 - return( true );
507 - } else {
508 - # Nothing on the watchlist; nothing to do here
509 - $out->addWikiText( wfMsg( 'nowatchlist' ) );
510 - $out->returnToMain();
511 - return( true );
512 - }
513 - } else {
514 - return( false );
515 - }
516 -}
517 -
 368+}
\ No newline at end of file
Index: trunk/phase3/RELEASE-NOTES
@@ -117,9 +117,11 @@
118118 * New javascript for upload page that will show a warning if a file with the
119119 "destination filename" already exists.
120120 * Add 'editsection-brackets' message to allow localization (or removal) of the
121 - brackets in the "[edit]" link for sections.
122 -* (bug 10437) Move texvc styling to shared.css.
123 -* (bug 10438) HTML TeX formulas should not wrap.
 121+ brackets in the "[edit]" link for sections
 122+* (bug 10437) Move texvc styling to shared.css
 123+* (bug 10438) HTML TeX formulas should not wrap
 124+* Introduce "raw editing" mode for the watchlist, to allow bulk additions,
 125+ removals, and convenient exporting of watchlist contents
124126
125127 == Bugfixes since 1.10 ==
126128
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -1692,10 +1692,6 @@
16931693 'nowatchlist' => 'You have no items on your watchlist.',
16941694 'watchlistanontext' => 'Please $1 to view or edit items on your watchlist.',
16951695 'watchlistcount' => "'''You have {{PLURAL:$1|$1 item|$1 items}} on your watchlist, including talk pages.'''",
1696 -'clearwatchlist' => 'Clear watchlist',
1697 -'watchlistcleartext' => 'Are you sure you wish to remove them?',
1698 -'watchlistclearbutton' => 'Clear watchlist',
1699 -'watchlistcleardone' => 'Your watchlist has been cleared. {{PLURAL:$1|$1 item was|$1 items were}} removed.',
17001696 'watchnologin' => 'Not logged in',
17011697 'watchnologintext' => 'You must be [[Special:Userlogin|logged in]] to modify your watchlist.',
17021698 'addedwatch' => 'Added to watchlist',
@@ -1720,13 +1716,7 @@
17211717 'wlheader-showupdated' => "* Pages which have been changed since you last visited them are shown in '''bold'''",
17221718 'watchmethod-recent' => 'checking recent edits for watched pages',
17231719 'watchmethod-list' => 'checking watched pages for recent edits',
1724 -'removechecked' => 'Remove checked items from watchlist',
17251720 'watchlistcontains' => 'Your watchlist contains $1 {{PLURAL:$1|page|pages}}.',
1726 -'watcheditlist' => "Here's an alphabetical list of your
1727 -watched content pages. Check the boxes of pages you want to remove from your watchlist and click the 'remove checked' button
1728 -at the bottom of the screen (deleting a content page also deletes the accompanying talk page and vice versa).",
1729 -'removingchecked' => 'Removing requested items from watchlist...',
1730 -'couldntremove' => "Couldn't remove item '$1'...",
17311721 'iteminvalidname' => "Problem with item '$1', invalid name...",
17321722 'wlnote' => "Below {{PLURAL:$1|is the last change|are the last '''$1''' changes}} in the last {{PLURAL:$2|hour|'''$2''' hours}}.",
17331723 'wlshowlast' => 'Show last $1 hours $2 days $3',
@@ -1737,7 +1727,6 @@
17381728 'watchlist-hide-own' => 'Hide my edits',
17391729 'watchlist-show-minor' => 'Show minor edits',
17401730 'watchlist-hide-minor' => 'Hide minor edits',
1741 -'wldone' => 'Done.',
17421731
17431732 # Displayed when you click the "watch" button and it's in the process of watching
17441733 'watching' => 'Watching...',
@@ -2896,4 +2885,29 @@
28972886 'lag-warn-normal' => 'Changes newer than $1 seconds may not be shown in this list.',
28982887 'lag-warn-high' => 'Due to high database server lag, changes newer than $1 seconds may not be shown in this list.',
28992888
2900 -);
 2889+# Watchlist editor
 2890+'watchlistedit-numitems' => 'Your watchlist contains $1 title(s), excluding talk pages.',
 2891+'watchlistedit-noitems' => 'Your watchlist contains no titles.',
 2892+'watchlistedit-clear-title' => 'Clear watchlist',
 2893+'watchlistedit-clear-legend' => 'Clear watchlist',
 2894+'watchlistedit-clear-confirm' => 'This will remove all titles from your watchlist. Are you sure you
 2895+ want to do this? You can also [[Special:Watchlist/edit|remove individual titles]].',
 2896+'watchlistedit-clear-submit' => 'Clear',
 2897+'watchlistedit-clear-done' => 'Your watchlist has been cleared. All titles were removed.',
 2898+'watchlistedit-normal-title' => 'Edit watchlist',
 2899+'watchlistedit-normal-legend' => 'Remove titles from watchlist',
 2900+'watchlistedit-normal-explain' => 'Titles on your watchlist are shown below. To remove a title, check
 2901+ the box next to it, and click Remove Titles. You can also [[Special:Watchlist/raw|edit the raw list]],
 2902+ or [[Special:Watchlist/clear|remove all titles]].',
 2903+'watchlistedit-normal-submit' => 'Remove Titles',
 2904+'watchlistedit-normal-done' => '$1 title(s) were removed from your watchlist:',
 2905+'watchlistedit-raw-title' => 'Edit raw watchlist',
 2906+'watchlistedit-raw-legend' => 'Edit raw watchlist',
 2907+'watchlistedit-raw-explain' => 'Titles on your watchlist are shown below, and can be edited by
 2908+ adding to and removing from the list; one title per line. When finished, click Update Watchlist.
 2909+ You can also [[Special:Watchlist/edit|use the standard editor]].',
 2910+'watchlistedit-raw-titles' => 'Titles:',
 2911+'watchlistedit-raw-submit' => 'Update Watchlist',
 2912+'watchlistedit-raw-done' => 'Your watchlist has been updated.',
 2913+
 2914+);
\ No newline at end of file

Follow-up revisions

RevisionCommit summaryAuthorDate
r23912Merged revisions 23662-23909 via svnmerge from...david18:11, 9 July 2007

Status & tagging log