r84718 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r84717‎ | r84718 | r84719 >
Date:23:28, 24 March 2011
Author:happy-melon
Status:ok (Comments)
Tags:brion 
Comment:
Move WatchlistEditor.php to /specials since inside it is essentially a complete special page. Make it subclass SpecialPage and do all the usual stuff. Rewrite it to use HTMLForm and other exciting things, and give it a good general clean up.
Modified paths:
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/SpecialPage.php (modified) (history)
  • /trunk/phase3/includes/WatchlistEditor.php (deleted) (history)
  • /trunk/phase3/includes/specials/SpecialEditWatchlist.php (added) (history)
  • /trunk/phase3/includes/specials/SpecialWatchlist.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/WatchlistEditor.php
@@ -1,542 +0,0 @@
2 -<?php
3 -
4 -/**
5 - * Provides the UI through which users can perform editing
6 - * operations on their watchlist
7 - *
8 - * @ingroup 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 $output OutputPage
25 - * @param $request WebRequest
26 - * @param $mode int
27 - */
28 - public function execute( $user, $output, $request, $mode ) {
29 - global $wgUser, $wgLang;
30 - if( wfReadOnly() ) {
31 - $output->readOnlyPage();
32 - return;
33 - }
34 - switch( $mode ) {
35 - case self::EDIT_CLEAR:
36 - // The "Clear" link scared people too much.
37 - // Pass on to the raw editor, from which it's very easy to clear.
38 - case self::EDIT_RAW:
39 - $output->setPageTitle( wfMsg( 'watchlistedit-raw-title' ) );
40 - if( $request->wasPosted() && $this->checkToken( $request, $wgUser ) ) {
41 - $wanted = $this->extractTitles( $request->getText( 'titles' ) );
42 - $current = $this->getWatchlist( $user );
43 - if( count( $wanted ) > 0 ) {
44 - $toWatch = array_diff( $wanted, $current );
45 - $toUnwatch = array_diff( $current, $wanted );
46 - $this->watchTitles( $toWatch, $user );
47 - $this->unwatchTitles( $toUnwatch, $user );
48 - $user->invalidateCache();
49 - if( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 )
50 - $output->addHTML( wfMsgExt( 'watchlistedit-raw-done', 'parse' ) );
51 - if( ( $count = count( $toWatch ) ) > 0 ) {
52 - $output->addHTML( wfMsgExt( 'watchlistedit-raw-added', 'parse',
53 - $wgLang->formatNum( $count ) ) );
54 - $this->showTitles( $toWatch, $output, $wgUser->getSkin() );
55 - }
56 - if( ( $count = count( $toUnwatch ) ) > 0 ) {
57 - $output->addHTML( wfMsgExt( 'watchlistedit-raw-removed', 'parse',
58 - $wgLang->formatNum( $count ) ) );
59 - $this->showTitles( $toUnwatch, $output, $wgUser->getSkin() );
60 - }
61 - } else {
62 - $this->clearWatchlist( $user );
63 - $user->invalidateCache();
64 - $output->addHTML( wfMsgExt( 'watchlistedit-raw-removed', 'parse',
65 - $wgLang->formatNum( count( $current ) ) ) );
66 - $this->showTitles( $current, $output, $wgUser->getSkin() );
67 - }
68 - }
69 - $this->showRawForm( $output, $user );
70 - break;
71 - case self::EDIT_NORMAL:
72 - $output->setPageTitle( wfMsg( 'watchlistedit-normal-title' ) );
73 - if( $request->wasPosted() && $this->checkToken( $request, $wgUser ) ) {
74 - $titles = $this->extractTitles( $request->getArray( 'titles' ) );
75 - $this->unwatchTitles( $titles, $user );
76 - $user->invalidateCache();
77 - $output->addHTML( wfMsgExt( 'watchlistedit-normal-done', 'parse',
78 - $wgLang->formatNum( count( $titles ) ) ) );
79 - $this->showTitles( $titles, $output, $wgUser->getSkin() );
80 - }
81 - $this->showNormalForm( $output, $user );
82 - }
83 - }
84 -
85 - /**
86 - * Check the edit token from a form submission
87 - *
88 - * @param $request WebRequest
89 - * @param $user User
90 - * @return bool
91 - */
92 - private function checkToken( $request, $user ) {
93 - return $user->matchEditToken( $request->getVal( 'token' ), 'watchlistedit' );
94 - }
95 -
96 - /**
97 - * Extract a list of titles from a blob of text, returning
98 - * (prefixed) strings; unwatchable titles are ignored
99 - *
100 - * @param $list mixed
101 - * @return array
102 - */
103 - private function extractTitles( $list ) {
104 - $titles = array();
105 - if( !is_array( $list ) ) {
106 - $list = explode( "\n", trim( $list ) );
107 - if( !is_array( $list ) ) {
108 - return array();
109 - }
110 - }
111 - foreach( $list as $text ) {
112 - $text = trim( $text );
113 - if( strlen( $text ) > 0 ) {
114 - $title = Title::newFromText( $text );
115 - if( $title instanceof Title && $title->isWatchable() ) {
116 - $titles[] = $title->getPrefixedText();
117 - }
118 - }
119 - }
120 - return array_unique( $titles );
121 - }
122 -
123 - /**
124 - * Print out a list of linked titles
125 - *
126 - * $titles can be an array of strings or Title objects; the former
127 - * is preferred, since Titles are very memory-heavy
128 - *
129 - * @param $titles An array of strings, or Title objects
130 - * @param $output OutputPage
131 - * @param $skin Skin
132 - */
133 - private function showTitles( $titles, $output, $skin ) {
134 - $talk = wfMsgHtml( 'talkpagelinktext' );
135 - // Do a batch existence check
136 - $batch = new LinkBatch();
137 - foreach( $titles as $title ) {
138 - if( !$title instanceof Title ) {
139 - $title = Title::newFromText( $title );
140 - }
141 - if( $title instanceof Title ) {
142 - $batch->addObj( $title );
143 - $batch->addObj( $title->getTalkPage() );
144 - }
145 - }
146 - $batch->execute();
147 - // Print out the list
148 - $output->addHTML( "<ul>\n" );
149 - foreach( $titles as $title ) {
150 - if( !$title instanceof Title ) {
151 - $title = Title::newFromText( $title );
152 - }
153 - if( $title instanceof Title ) {
154 - $output->addHTML( "<li>" . $skin->link( $title )
155 - . ' (' . $skin->link( $title->getTalkPage(), $talk ) . ")</li>\n" );
156 - }
157 - }
158 - $output->addHTML( "</ul>\n" );
159 - }
160 -
161 - /**
162 - * Count the number of titles on a user's watchlist, excluding talk pages
163 - *
164 - * @param $user User
165 - * @return int
166 - */
167 - private function countWatchlist( $user ) {
168 - $dbr = wfGetDB( DB_MASTER );
169 - $res = $dbr->select( 'watchlist', 'COUNT(*) AS count', array( 'wl_user' => $user->getId() ), __METHOD__ );
170 - $row = $dbr->fetchObject( $res );
171 - return ceil( $row->count / 2 ); // Paranoia
172 - }
173 -
174 - /**
175 - * Prepare a list of titles on a user's watchlist (excluding talk pages)
176 - * and return an array of (prefixed) strings
177 - *
178 - * @param $user User
179 - * @return array
180 - */
181 - private function getWatchlist( $user ) {
182 - $list = array();
183 - $dbr = wfGetDB( DB_MASTER );
184 - $res = $dbr->select(
185 - 'watchlist',
186 - '*',
187 - array(
188 - 'wl_user' => $user->getId(),
189 - ),
190 - __METHOD__
191 - );
192 - if( $res->numRows() > 0 ) {
193 - foreach ( $res as $row ) {
194 - $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
195 - if( $title instanceof Title && !$title->isTalkPage() )
196 - $list[] = $title->getPrefixedText();
197 - }
198 - $res->free();
199 - }
200 - return $list;
201 - }
202 -
203 - /**
204 - * Get a list of titles on a user's watchlist, excluding talk pages,
205 - * and return as a two-dimensional array with namespace, title and
206 - * redirect status
207 - *
208 - * @param $user User
209 - * @return array
210 - */
211 - private function getWatchlistInfo( $user ) {
212 - $titles = array();
213 - $dbr = wfGetDB( DB_MASTER );
214 -
215 - $res = $dbr->select(
216 - array( 'watchlist', 'page' ),
217 - array(
218 - 'wl_namespace',
219 - 'wl_title',
220 - 'page_id',
221 - 'page_len',
222 - 'page_is_redirect',
223 - 'page_latest'
224 - ),
225 - array( 'wl_user' => $user->getId() ),
226 - __METHOD__,
227 - array( 'ORDER BY' => 'wl_namespace, wl_title' ),
228 - array( 'page' => array(
229 - 'LEFT JOIN',
230 - 'wl_namespace = page_namespace AND wl_title = page_title'
231 - ) )
232 - );
233 -
234 - if( $res && $dbr->numRows( $res ) > 0 ) {
235 - $cache = LinkCache::singleton();
236 - foreach ( $res as $row ) {
237 - $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
238 - if( $title instanceof Title ) {
239 - // Update the link cache while we're at it
240 - if( $row->page_id ) {
241 - $cache->addGoodLinkObj( $row->page_id, $title, $row->page_len, $row->page_is_redirect, $row->page_latest );
242 - } else {
243 - $cache->addBadLinkObj( $title );
244 - }
245 - // Ignore non-talk
246 - if( !$title->isTalkPage() ) {
247 - $titles[$row->wl_namespace][$row->wl_title] = $row->page_is_redirect;
248 - }
249 - }
250 - }
251 - }
252 - return $titles;
253 - }
254 -
255 - /**
256 - * Show a message indicating the number of items on the user's watchlist,
257 - * and return this count for additional checking
258 - *
259 - * @param $output OutputPage
260 - * @param $user User
261 - * @return int
262 - */
263 - private function showItemCount( $output, $user ) {
264 - if( ( $count = $this->countWatchlist( $user ) ) > 0 ) {
265 - $output->addHTML( wfMsgExt( 'watchlistedit-numitems', 'parse',
266 - $GLOBALS['wgLang']->formatNum( $count ) ) );
267 - } else {
268 - $output->addHTML( wfMsgExt( 'watchlistedit-noitems', 'parse' ) );
269 - }
270 - return $count;
271 - }
272 -
273 - /**
274 - * Remove all titles from a user's watchlist
275 - *
276 - * @param $user User
277 - */
278 - private function clearWatchlist( $user ) {
279 - $dbw = wfGetDB( DB_MASTER );
280 - $dbw->delete( 'watchlist', array( 'wl_user' => $user->getId() ), __METHOD__ );
281 - }
282 -
283 - /**
284 - * Add a list of titles to a user's watchlist
285 - *
286 - * $titles can be an array of strings or Title objects; the former
287 - * is preferred, since Titles are very memory-heavy
288 - *
289 - * @param $titles An array of strings, or Title objects
290 - * @param $user User
291 - */
292 - private function watchTitles( $titles, $user ) {
293 - $dbw = wfGetDB( DB_MASTER );
294 - $rows = array();
295 - foreach( $titles as $title ) {
296 - if( !$title instanceof Title ) {
297 - $title = Title::newFromText( $title );
298 - }
299 - if( $title instanceof Title ) {
300 - $rows[] = array(
301 - 'wl_user' => $user->getId(),
302 - 'wl_namespace' => ( $title->getNamespace() & ~1 ),
303 - 'wl_title' => $title->getDBkey(),
304 - 'wl_notificationtimestamp' => null,
305 - );
306 - $rows[] = array(
307 - 'wl_user' => $user->getId(),
308 - 'wl_namespace' => ( $title->getNamespace() | 1 ),
309 - 'wl_title' => $title->getDBkey(),
310 - 'wl_notificationtimestamp' => null,
311 - );
312 - }
313 - }
314 - $dbw->insert( 'watchlist', $rows, __METHOD__, 'IGNORE' );
315 - }
316 -
317 - /**
318 - * Remove a list of titles from a user's watchlist
319 - *
320 - * $titles can be an array of strings or Title objects; the former
321 - * is preferred, since Titles are very memory-heavy
322 - *
323 - * @param $titles An array of strings, or Title objects
324 - * @param $user User
325 - */
326 - private function unwatchTitles( $titles, $user ) {
327 - $dbw = wfGetDB( DB_MASTER );
328 - foreach( $titles as $title ) {
329 - if( !$title instanceof Title ) {
330 - $title = Title::newFromText( $title );
331 - }
332 - if( $title instanceof Title ) {
333 - $dbw->delete(
334 - 'watchlist',
335 - array(
336 - 'wl_user' => $user->getId(),
337 - 'wl_namespace' => ( $title->getNamespace() & ~1 ),
338 - 'wl_title' => $title->getDBkey(),
339 - ),
340 - __METHOD__
341 - );
342 - $dbw->delete(
343 - 'watchlist',
344 - array(
345 - 'wl_user' => $user->getId(),
346 - 'wl_namespace' => ( $title->getNamespace() | 1 ),
347 - 'wl_title' => $title->getDBkey(),
348 - ),
349 - __METHOD__
350 - );
351 - $article = new Article($title);
352 - wfRunHooks('UnwatchArticleComplete',array(&$user,&$article));
353 - }
354 - }
355 - }
356 -
357 - /**
358 - * Show the standard watchlist editing form
359 - *
360 - * @param $output OutputPage
361 - * @param $user User
362 - */
363 - private function showNormalForm( $output, $user ) {
364 - global $wgUser;
365 - $count = $this->showItemCount( $output, $user );
366 - if( $count > 0 ) {
367 - $self = SpecialPage::getTitleFor( 'Watchlist' );
368 - $form = Xml::openElement( 'form', array( 'method' => 'post',
369 - 'action' => $self->getLocalUrl( array( 'action' => 'edit' ) ) ) );
370 - $form .= Html::hidden( 'token', $wgUser->editToken( 'watchlistedit' ) );
371 - $form .= "<fieldset>\n<legend>" . wfMsgHtml( 'watchlistedit-normal-legend' ) . "</legend>";
372 - $form .= wfMsgExt( 'watchlistedit-normal-explain', 'parse' );
373 - $form .= $this->buildRemoveList( $user, $wgUser->getSkin() );
374 - $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-normal-submit' ) ) . '</p>';
375 - $form .= '</fieldset></form>';
376 - $output->addHTML( $form );
377 - }
378 - }
379 -
380 - /**
381 - * Build the part of the standard watchlist editing form with the actual
382 - * title selection checkboxes and stuff. Also generates a table of
383 - * contents if there's more than one heading.
384 - *
385 - * @param $user User
386 - * @param $skin Skin (really, Linker)
387 - */
388 - private function buildRemoveList( $user, $skin ) {
389 - $list = "";
390 - $toc = $skin->tocIndent();
391 - $tocLength = 0;
392 - foreach( $this->getWatchlistInfo( $user ) as $namespace => $pages ) {
393 - $tocLength++;
394 - $heading = htmlspecialchars( $this->getNamespaceHeading( $namespace ) );
395 - $anchor = "editwatchlist-ns" . $namespace;
396 -
397 - $list .= $skin->makeHeadLine( 2, ">", $anchor, $heading, "" );
398 - $toc .= $skin->tocLine( $anchor, $heading, $tocLength, 1 ) . $skin->tocLineEnd();
399 -
400 - $list .= "<ul>\n";
401 - foreach( $pages as $dbkey => $redirect ) {
402 - $title = Title::makeTitleSafe( $namespace, $dbkey );
403 - $list .= $this->buildRemoveLine( $title, $redirect, $skin );
404 - }
405 - $list .= "</ul>\n";
406 - }
407 - // ISSUE: omit the TOC if the total number of titles is low?
408 - if( $tocLength > 1 ) {
409 - $list = $skin->tocList( $toc ) . $list;
410 - }
411 - return $list;
412 - }
413 -
414 - /**
415 - * Get the correct "heading" for a namespace
416 - *
417 - * @param $namespace int
418 - * @return string
419 - */
420 - private function getNamespaceHeading( $namespace ) {
421 - return $namespace == NS_MAIN
422 - ? wfMsgHtml( 'blanknamespace' )
423 - : htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( $namespace ) );
424 - }
425 -
426 - /**
427 - * Build a single list item containing a check box selecting a title
428 - * and a link to that title, with various additional bits
429 - *
430 - * @param $title Title
431 - * @param $redirect bool
432 - * @param $skin Skin
433 - * @return string
434 - */
435 - private function buildRemoveLine( $title, $redirect, $skin ) {
436 - global $wgLang;
437 -
438 - $link = $skin->link( $title );
439 - if( $redirect ) {
440 - $link = '<span class="watchlistredir">' . $link . '</span>';
441 - }
442 - $tools[] = $skin->link( $title->getTalkPage(), wfMsgHtml( 'talkpagelinktext' ) );
443 - if( $title->exists() ) {
444 - $tools[] = $skin->link(
445 - $title,
446 - wfMsgHtml( 'history_short' ),
447 - array(),
448 - array( 'action' => 'history' ),
449 - array( 'known', 'noclasses' )
450 - );
451 - }
452 - if( $title->getNamespace() == NS_USER && !$title->isSubpage() ) {
453 - $tools[] = $skin->link(
454 - SpecialPage::getTitleFor( 'Contributions', $title->getText() ),
455 - wfMsgHtml( 'contributions' ),
456 - array(),
457 - array(),
458 - array( 'known', 'noclasses' )
459 - );
460 - }
461 -
462 - wfRunHooks( 'WatchlistEditorBuildRemoveLine', array( &$tools, $title, $redirect, $skin ) );
463 -
464 - return "<li>"
465 - . Xml::check( 'titles[]', false, array( 'value' => $title->getPrefixedText() ) )
466 - . $link . " (" . $wgLang->pipeList( $tools ) . ")" . "</li>\n";
467 - }
468 -
469 - /**
470 - * Show a form for editing the watchlist in "raw" mode
471 - *
472 - * @param $output OutputPage
473 - * @param $user User
474 - */
475 - public function showRawForm( $output, $user ) {
476 - global $wgUser;
477 - $this->showItemCount( $output, $user );
478 - $self = SpecialPage::getTitleFor( 'Watchlist' );
479 - $form = Xml::openElement( 'form', array( 'method' => 'post',
480 - 'action' => $self->getLocalUrl( array( 'action' => 'raw' ) ) ) );
481 - $form .= Html::hidden( 'token', $wgUser->editToken( 'watchlistedit' ) );
482 - $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-raw-legend' ) . '</legend>';
483 - $form .= wfMsgExt( 'watchlistedit-raw-explain', 'parse' );
484 - $form .= Xml::label( wfMsg( 'watchlistedit-raw-titles' ), 'titles' );
485 - $form .= "<br />\n";
486 - $form .= Xml::openElement( 'textarea', array( 'id' => 'titles', 'name' => 'titles',
487 - 'rows' => $wgUser->getIntOption( 'rows' ), 'cols' => $wgUser->getIntOption( 'cols' ) ) );
488 - $titles = $this->getWatchlist( $user );
489 - foreach( $titles as $title ) {
490 - $form .= htmlspecialchars( $title ) . "\n";
491 - }
492 - $form .= '</textarea>';
493 - $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-raw-submit' ) ) . '</p>';
494 - $form .= '</fieldset></form>';
495 - $output->addHTML( $form );
496 - }
497 -
498 - /**
499 - * Determine whether we are editing the watchlist, and if so, what
500 - * kind of editing operation
501 - *
502 - * @param $request WebRequest
503 - * @param $par mixed
504 - * @return int
505 - */
506 - public static function getMode( $request, $par ) {
507 - $mode = strtolower( $request->getVal( 'action', $par ) );
508 - switch( $mode ) {
509 - case 'clear':
510 - return self::EDIT_CLEAR;
511 - case 'raw':
512 - return self::EDIT_RAW;
513 - case 'edit':
514 - return self::EDIT_NORMAL;
515 - default:
516 - return false;
517 - }
518 - }
519 -
520 - /**
521 - * Build a set of links for convenient navigation
522 - * between watchlist viewing and editing modes
523 - *
524 - * @param $skin Skin to use
525 - * @return string
526 - */
527 - public static function buildTools( $skin ) {
528 - global $wgLang;
529 -
530 - $tools = array();
531 - $modes = array( 'view' => false, 'edit' => 'edit', 'raw' => 'raw' );
532 - foreach( $modes as $mode => $subpage ) {
533 - // can use messages 'watchlisttools-view', 'watchlisttools-edit', 'watchlisttools-raw'
534 - $tools[] = $skin->linkKnown(
535 - SpecialPage::getTitleFor( 'Watchlist', $subpage ),
536 - wfMsgHtml( "watchlisttools-{$mode}" )
537 - );
538 - }
539 - return Html::rawElement( 'span',
540 - array( 'class' => 'mw-watchlist-toollinks' ),
541 - wfMsg( 'parentheses', $wgLang->pipeList( $tools ) ) );
542 - }
543 -}
Index: trunk/phase3/includes/AutoLoader.php
@@ -250,7 +250,7 @@
251251 'ViewCountUpdate' => 'includes/ViewCountUpdate.php',
252252 'WantedQueryPage' => 'includes/QueryPage.php',
253253 'WatchedItem' => 'includes/WatchedItem.php',
254 - 'WatchlistEditor' => 'includes/WatchlistEditor.php',
 254+ 'WatchlistEditor' => 'includes/specials/SpecialEditWatchlist.php',
255255 'WebRequest' => 'includes/WebRequest.php',
256256 'WebRequestUpload' => 'includes/WebRequest.php',
257257 'WebResponse' => 'includes/WebResponse.php',
@@ -655,6 +655,7 @@
656656 'SpecialCategories' => 'includes/specials/SpecialCategories.php',
657657 'SpecialComparePages' => 'includes/specials/SpecialComparePages.php',
658658 'SpecialDisableAccount' => 'includes/specials/SpecialDisableAccount.php',
 659+ 'SpecialEditWatchlist' => 'includes/specials/SpecialEditWatchlist.php',
659660 'SpecialExport' => 'includes/specials/SpecialExport.php',
660661 'SpecialFilepath' => 'includes/specials/SpecialFilepath.php',
661662 'SpecialImport' => 'includes/specials/SpecialImport.php',
Index: trunk/phase3/includes/specials/SpecialEditWatchlist.php
@@ -0,0 +1,603 @@
 2+<?php
 3+
 4+/**
 5+ * Provides the UI through which users can perform editing
 6+ * operations on their watchlist
 7+ *
 8+ * @ingroup Watchlist
 9+ * @author Rob Church <robchur@gmail.com>
 10+ */
 11+class SpecialEditWatchlist extends UnlistedSpecialPage {
 12+
 13+ /**
 14+ * Editing modes
 15+ */
 16+ const EDIT_CLEAR = 1;
 17+ const EDIT_RAW = 2;
 18+ const EDIT_NORMAL = 3;
 19+
 20+ protected $successMessage;
 21+
 22+ public function __construct(){
 23+ parent::__construct( 'EditWatchlist' );
 24+ }
 25+
 26+ /**
 27+ * Main execution point
 28+ *
 29+ * @param $user User
 30+ * @param $output OutputPage
 31+ * @param $request WebRequest
 32+ * @param $mode int
 33+ */
 34+ public function execute( $mode ) {
 35+ global $wgUser, $wgLang, $wgOut, $wgRequest;
 36+ if( wfReadOnly() ) {
 37+ $wgOut->readOnlyPage();
 38+ return;
 39+ }
 40+
 41+ # Anons don't get a watchlist
 42+ if( $wgUser->isAnon() ) {
 43+ $wgOut->setPageTitle( wfMsg( 'watchnologin' ) );
 44+ $llink = $wgUser->getSkin()->linkKnown(
 45+ SpecialPage::getTitleFor( 'Userlogin' ),
 46+ wfMsgHtml( 'loginreqlink' ),
 47+ array(),
 48+ array( 'returnto' => $this->getTitle()->getPrefixedText() )
 49+ );
 50+ $wgOut->addWikiMsgArray( 'watchlistanontext', array( $llink ), array( 'replaceafter' ) );
 51+ return;
 52+ }
 53+
 54+ $sub = wfMsgExt(
 55+ 'watchlistfor2',
 56+ array( 'parseinline', 'replaceafter' ),
 57+ $wgUser->getName(),
 58+ SpecialEditWatchlist::buildTools( $wgUser->getSkin() )
 59+ );
 60+ $wgOut->setSubtitle( $sub );
 61+
 62+ # B/C: $mode used to be waaay down the parameter list, and the first parameter
 63+ # was $wgUser
 64+ if( $mode instanceof User ){
 65+ $args = func_get_args();
 66+ if( count( $args >= 4 ) ){
 67+ $mode = $args[3];
 68+ }
 69+ }
 70+ $mode = self::getMode( $wgRequest, $mode );
 71+
 72+ switch( $mode ) {
 73+ case self::EDIT_CLEAR:
 74+ // The "Clear" link scared people too much.
 75+ // Pass on to the raw editor, from which it's very easy to clear.
 76+
 77+ case self::EDIT_RAW:
 78+ $wgOut->setPageTitle( wfMsg( 'watchlistedit-raw-title' ) );
 79+ $form = $this->getRawForm( $wgUser );
 80+ if( $form->show() ){
 81+ $wgOut->addHTML( $this->successMessage );
 82+ $wgOut->returnToMain();
 83+ }
 84+ break;
 85+
 86+ case self::EDIT_NORMAL:
 87+ default:
 88+ $wgOut->setPageTitle( wfMsg( 'watchlistedit-normal-title' ) );
 89+ $form = $this->getNormalForm( $wgUser );
 90+ if( $form->show() ){
 91+ $wgOut->addHTML( $this->successMessage );
 92+ $wgOut->returnToMain();
 93+ }
 94+ break;
 95+ }
 96+ }
 97+
 98+ /**
 99+ * Extract a list of titles from a blob of text, returning
 100+ * (prefixed) strings; unwatchable titles are ignored
 101+ *
 102+ * @param $list String
 103+ * @return array
 104+ */
 105+ private function extractTitles( $list ) {
 106+ $titles = array();
 107+ $list = explode( "\n", trim( $list ) );
 108+ if( !is_array( $list ) ) {
 109+ return array();
 110+ }
 111+ foreach( $list as $text ) {
 112+ $text = trim( $text );
 113+ if( strlen( $text ) > 0 ) {
 114+ $title = Title::newFromText( $text );
 115+ if( $title instanceof Title && $title->isWatchable() ) {
 116+ $titles[] = $title->getPrefixedText();
 117+ }
 118+ }
 119+ }
 120+ return array_unique( $titles );
 121+ }
 122+
 123+ public function submitRaw( $data ){
 124+ global $wgUser, $wgLang;
 125+ $wanted = $this->extractTitles( $data['Titles'] );
 126+ $current = $this->getWatchlist( $wgUser );
 127+
 128+ if( count( $wanted ) > 0 ) {
 129+ $toWatch = array_diff( $wanted, $current );
 130+ $toUnwatch = array_diff( $current, $wanted );
 131+ $this->watchTitles( $toWatch, $wgUser );
 132+ $this->unwatchTitles( $toUnwatch, $wgUser );
 133+ $wgUser->invalidateCache();
 134+
 135+ if( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 ){
 136+ $this->successMessage = wfMessage( 'watchlistedit-raw-done' )->parse();
 137+ } else {
 138+ return false;
 139+ }
 140+
 141+ if( count( $toWatch ) > 0 ) {
 142+ $this->successMessage .= wfMessage(
 143+ 'watchlistedit-raw-added',
 144+ $wgLang->formatNum( count( $toWatch ) )
 145+ );
 146+ $this->showTitles( $toWatch, $this->successMessage, $wgUser->getSkin() );
 147+ }
 148+
 149+ if( count( $toUnwatch ) > 0 ) {
 150+ $this->successMessage .= wfMessage(
 151+ 'watchlistedit-raw-removed',
 152+ $wgLang->formatNum( count( $toUnwatch ) )
 153+ );
 154+ $this->showTitles( $toUnwatch, $this->successMessage, $wgUser->getSkin() );
 155+ }
 156+ } else {
 157+ $this->clearWatchlist( $wgUser );
 158+ $wgUser->invalidateCache();
 159+ $this->successMessage .= wfMessage(
 160+ 'watchlistedit-raw-removed',
 161+ $wgLang->formatNum( count( $current ) )
 162+ );
 163+ $this->showTitles( $current, $this->successMessage, $wgUser->getSkin() );
 164+ }
 165+ return true;
 166+ }
 167+
 168+ /**
 169+ * Print out a list of linked titles
 170+ *
 171+ * $titles can be an array of strings or Title objects; the former
 172+ * is preferred, since Titles are very memory-heavy
 173+ *
 174+ * @param $titles array of strings, or Title objects
 175+ * @param $output String
 176+ * @param $skin Skin
 177+ */
 178+ private function showTitles( $titles, &$output, $skin ) {
 179+ $talk = wfMsgHtml( 'talkpagelinktext' );
 180+ // Do a batch existence check
 181+ $batch = new LinkBatch();
 182+ foreach( $titles as $title ) {
 183+ if( !$title instanceof Title ) {
 184+ $title = Title::newFromText( $title );
 185+ }
 186+ if( $title instanceof Title ) {
 187+ $batch->addObj( $title );
 188+ $batch->addObj( $title->getTalkPage() );
 189+ }
 190+ }
 191+ $batch->execute();
 192+ // Print out the list
 193+ $output .= "<ul>\n";
 194+ foreach( $titles as $title ) {
 195+ if( !$title instanceof Title ) {
 196+ $title = Title::newFromText( $title );
 197+ }
 198+ if( $title instanceof Title ) {
 199+ $output .= "<li>"
 200+ . $skin->link( $title )
 201+ . ' (' . $skin->link( $title->getTalkPage(), $talk )
 202+ . ")</li>\n";
 203+ }
 204+ }
 205+ $output .= "</ul>\n";
 206+ }
 207+
 208+ /**
 209+ * Count the number of titles on a user's watchlist, excluding talk pages
 210+ *
 211+ * @param $user User
 212+ * @return int
 213+ */
 214+ private function countWatchlist( $user ) {
 215+ $dbr = wfGetDB( DB_MASTER );
 216+ $res = $dbr->select( 'watchlist', 'COUNT(*) AS count', array( 'wl_user' => $user->getId() ), __METHOD__ );
 217+ $row = $dbr->fetchObject( $res );
 218+ return ceil( $row->count / 2 ); // Paranoia
 219+ }
 220+
 221+ /**
 222+ * Prepare a list of titles on a user's watchlist (excluding talk pages)
 223+ * and return an array of (prefixed) strings
 224+ *
 225+ * @param $user User
 226+ * @return array
 227+ */
 228+ private function getWatchlist( $user ) {
 229+ $list = array();
 230+ $dbr = wfGetDB( DB_MASTER );
 231+ $res = $dbr->select(
 232+ 'watchlist',
 233+ '*',
 234+ array(
 235+ 'wl_user' => $user->getId(),
 236+ ),
 237+ __METHOD__
 238+ );
 239+ if( $res->numRows() > 0 ) {
 240+ foreach ( $res as $row ) {
 241+ $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
 242+ if( $title instanceof Title && !$title->isTalkPage() )
 243+ $list[] = $title->getPrefixedText();
 244+ }
 245+ $res->free();
 246+ }
 247+ return $list;
 248+ }
 249+
 250+ /**
 251+ * Get a list of titles on a user's watchlist, excluding talk pages,
 252+ * and return as a two-dimensional array with namespace, title and
 253+ * redirect status
 254+ *
 255+ * @param $user User
 256+ * @return array
 257+ */
 258+ private function getWatchlistInfo( $user ) {
 259+ $titles = array();
 260+ $dbr = wfGetDB( DB_MASTER );
 261+
 262+ $res = $dbr->select(
 263+ array( 'watchlist', 'page' ),
 264+ array(
 265+ 'wl_namespace',
 266+ 'wl_title',
 267+ 'page_id',
 268+ 'page_len',
 269+ 'page_is_redirect',
 270+ 'page_latest'
 271+ ),
 272+ array( 'wl_user' => $user->getId() ),
 273+ __METHOD__,
 274+ array( 'ORDER BY' => 'wl_namespace, wl_title' ),
 275+ array( 'page' => array(
 276+ 'LEFT JOIN',
 277+ 'wl_namespace = page_namespace AND wl_title = page_title'
 278+ ) )
 279+ );
 280+
 281+ if( $res && $dbr->numRows( $res ) > 0 ) {
 282+ $cache = LinkCache::singleton();
 283+ foreach ( $res as $row ) {
 284+ $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
 285+ if( $title instanceof Title ) {
 286+ // Update the link cache while we're at it
 287+ if( $row->page_id ) {
 288+ $cache->addGoodLinkObj( $row->page_id, $title, $row->page_len, $row->page_is_redirect, $row->page_latest );
 289+ } else {
 290+ $cache->addBadLinkObj( $title );
 291+ }
 292+ // Ignore non-talk
 293+ if( !$title->isTalkPage() ) {
 294+ $titles[$row->wl_namespace][$row->wl_title] = $row->page_is_redirect;
 295+ }
 296+ }
 297+ }
 298+ }
 299+ return $titles;
 300+ }
 301+
 302+ /**
 303+ * Show a message indicating the number of items on the user's watchlist,
 304+ * and return this count for additional checking
 305+ *
 306+ * @param $output OutputPage
 307+ * @param $user User
 308+ * @return int
 309+ */
 310+ private function showItemCount( $output, $user ) {
 311+ if( ( $count = $this->countWatchlist( $user ) ) > 0 ) {
 312+ $output->addHTML( wfMsgExt( 'watchlistedit-numitems', 'parse',
 313+ $GLOBALS['wgLang']->formatNum( $count ) ) );
 314+ } else {
 315+ $output->addHTML( wfMsgExt( 'watchlistedit-noitems', 'parse' ) );
 316+ }
 317+ return $count;
 318+ }
 319+
 320+ /**
 321+ * Remove all titles from a user's watchlist
 322+ *
 323+ * @param $user User
 324+ */
 325+ private function clearWatchlist( $user ) {
 326+ $dbw = wfGetDB( DB_MASTER );
 327+ $dbw->delete(
 328+ 'watchlist',
 329+ array( 'wl_user' => $user->getId() ),
 330+ __METHOD__
 331+ );
 332+ }
 333+
 334+ /**
 335+ * Add a list of titles to a user's watchlist
 336+ *
 337+ * $titles can be an array of strings or Title objects; the former
 338+ * is preferred, since Titles are very memory-heavy
 339+ *
 340+ * @param $titles Array of strings, or Title objects
 341+ * @param $user User
 342+ */
 343+ private function watchTitles( $titles, $user ) {
 344+ $dbw = wfGetDB( DB_MASTER );
 345+ $rows = array();
 346+ foreach( $titles as $title ) {
 347+ if( !$title instanceof Title ) {
 348+ $title = Title::newFromText( $title );
 349+ }
 350+ if( $title instanceof Title ) {
 351+ $rows[] = array(
 352+ 'wl_user' => $user->getId(),
 353+ 'wl_namespace' => ( $title->getNamespace() & ~1 ),
 354+ 'wl_title' => $title->getDBkey(),
 355+ 'wl_notificationtimestamp' => null,
 356+ );
 357+ $rows[] = array(
 358+ 'wl_user' => $user->getId(),
 359+ 'wl_namespace' => ( $title->getNamespace() | 1 ),
 360+ 'wl_title' => $title->getDBkey(),
 361+ 'wl_notificationtimestamp' => null,
 362+ );
 363+ }
 364+ }
 365+ $dbw->insert( 'watchlist', $rows, __METHOD__, 'IGNORE' );
 366+ }
 367+
 368+ /**
 369+ * Remove a list of titles from a user's watchlist
 370+ *
 371+ * $titles can be an array of strings or Title objects; the former
 372+ * is preferred, since Titles are very memory-heavy
 373+ *
 374+ * @param $titles Array of strings, or Title objects
 375+ * @param $user User
 376+ */
 377+ private function unwatchTitles( $titles, $user ) {
 378+ $dbw = wfGetDB( DB_MASTER );
 379+ foreach( $titles as $title ) {
 380+ if( !$title instanceof Title ) {
 381+ $title = Title::newFromText( $title );
 382+ }
 383+ if( $title instanceof Title ) {
 384+ $dbw->delete(
 385+ 'watchlist',
 386+ array(
 387+ 'wl_user' => $user->getId(),
 388+ 'wl_namespace' => ( $title->getNamespace() & ~1 ),
 389+ 'wl_title' => $title->getDBkey(),
 390+ ),
 391+ __METHOD__
 392+ );
 393+ $dbw->delete(
 394+ 'watchlist',
 395+ array(
 396+ 'wl_user' => $user->getId(),
 397+ 'wl_namespace' => ( $title->getNamespace() | 1 ),
 398+ 'wl_title' => $title->getDBkey(),
 399+ ),
 400+ __METHOD__
 401+ );
 402+ $article = new Article($title);
 403+ wfRunHooks('UnwatchArticleComplete',array(&$user,&$article));
 404+ }
 405+ }
 406+ }
 407+
 408+ public function submitNormal( $data ) {
 409+ global $wgUser;
 410+ $removed = array();
 411+
 412+ foreach( $data as $titles ) {
 413+ $this->unwatchTitles( $titles, $wgUser );
 414+ $removed += $titles;
 415+ }
 416+
 417+ if( count( $removed ) > 0 ) {
 418+ global $wgLang;
 419+ $this->successMessage = wfMessage(
 420+ 'watchlistedit-normal-done',
 421+ $wgLang->formatNum( count( $removed ) )
 422+ );
 423+ $this->showTitles( $removed, $this->successMessage, $wgUser->getSkin() );
 424+ return true;
 425+ } else {
 426+ return false;
 427+ }
 428+ }
 429+
 430+ /**
 431+ * Get the standard watchlist editing form
 432+ *
 433+ * @param $user User
 434+ * @return HTMLForm
 435+ */
 436+ protected function getNormalForm( $user ){
 437+ global $wgContLang;
 438+ $skin = $user->getSkin();
 439+ $fields = array();
 440+
 441+ foreach( $this->getWatchlistInfo( $user ) as $namespace => $pages ){
 442+
 443+ $namespace == NS_MAIN
 444+ ? wfMsgHtml( 'blanknamespace' )
 445+ : htmlspecialchars( $wgContLang->getFormattedNsText( $namespace ) );
 446+
 447+ $fields['TitlesNs'.$namespace] = array(
 448+ 'type' => 'multiselect',
 449+ 'options' => array(),
 450+ 'section' => "ns$namespace",
 451+ );
 452+
 453+ foreach( $pages as $dbkey => $redirect ){
 454+ $title = Title::makeTitleSafe( $namespace, $dbkey );
 455+ $text = $this->buildRemoveLine( $title, $redirect, $skin );
 456+ $fields['TitlesNs'.$namespace]['options'][$text] = $title->getEscapedText();
 457+ }
 458+ }
 459+
 460+ $form = new EditWatchlistNormalHTMLForm( $fields );
 461+ $form->setTitle( $this->getTitle() );
 462+ $form->setSubmitText( wfMessage( 'watchlistedit-normal-submit' )->text() );
 463+ $form->setWrapperLegend( wfMessage( 'watchlistedit-normal-legend' )->text() );
 464+ $form->addHeaderText( wfMessage( 'watchlistedit-normal-explain' )->parse() );
 465+ $form->setSubmitCallback( array( $this, 'submitNormal' ) );
 466+ return $form;
 467+ }
 468+
 469+ /**
 470+ * Build the label for a checkbox, with a link to the title, and various additional bits
 471+ *
 472+ * @param $title Title
 473+ * @param $redirect bool
 474+ * @param $skin Skin
 475+ * @return string
 476+ */
 477+ private function buildRemoveLine( $title, $redirect, $skin ) {
 478+ global $wgLang;
 479+
 480+ $link = $skin->link( $title );
 481+ if( $redirect ) {
 482+ $link = '<span class="watchlistredir">' . $link . '</span>';
 483+ }
 484+ $tools[] = $skin->link( $title->getTalkPage(), wfMsgHtml( 'talkpagelinktext' ) );
 485+ if( $title->exists() ) {
 486+ $tools[] = $skin->link(
 487+ $title,
 488+ wfMsgHtml( 'history_short' ),
 489+ array(),
 490+ array( 'action' => 'history' ),
 491+ array( 'known', 'noclasses' )
 492+ );
 493+ }
 494+ if( $title->getNamespace() == NS_USER && !$title->isSubpage() ) {
 495+ $tools[] = $skin->link(
 496+ SpecialPage::getTitleFor( 'Contributions', $title->getText() ),
 497+ wfMsgHtml( 'contributions' ),
 498+ array(),
 499+ array(),
 500+ array( 'known', 'noclasses' )
 501+ );
 502+ }
 503+
 504+ wfRunHooks( 'WatchlistEditorBuildRemoveLine', array( &$tools, $title, $redirect, $skin ) );
 505+
 506+ return $link . " (" . $wgLang->pipeList( $tools ) . ")";
 507+ }
 508+
 509+ /**
 510+ * Get a form for editing the watchlist in "raw" mode
 511+ *
 512+ * @param $user User
 513+ * @return HTMLForm
 514+ */
 515+ protected function getRawForm( $user ){
 516+ $titles = implode( array_map( 'htmlspecialchars', $this->getWatchlist( $user ) ), "\n" );
 517+ $fields = array(
 518+ 'Titles' => array(
 519+ 'type' => 'textarea',
 520+ 'label-message' => 'watchlistedit-raw-titles',
 521+ 'default' => $titles,
 522+ ),
 523+ );
 524+ $form = new HTMLForm( $fields );
 525+ $form->setTitle( $this->getTitle( 'raw' ) );
 526+ $form->setSubmitText( wfMessage( 'watchlistedit-raw-submit' )->text() );
 527+ $form->setWrapperLegend( wfMessage( 'watchlistedit-raw-legend' )->text() );
 528+ $form->addHeaderText( wfMessage( 'watchlistedit-raw-explain' )->parse() );
 529+ $form->setSubmitCallback( array( $this, 'submitRaw' ) );
 530+ return $form;
 531+ }
 532+
 533+ /**
 534+ * Determine whether we are editing the watchlist, and if so, what
 535+ * kind of editing operation
 536+ *
 537+ * @param $request WebRequest
 538+ * @param $par mixed
 539+ * @return int
 540+ */
 541+ public static function getMode( $request, $par ) {
 542+ $mode = strtolower( $request->getVal( 'action', $par ) );
 543+ switch( $mode ) {
 544+ case 'clear':
 545+ case self::EDIT_CLEAR:
 546+ return self::EDIT_CLEAR;
 547+
 548+ case 'raw':
 549+ case self::EDIT_RAW:
 550+ return self::EDIT_RAW;
 551+
 552+ case 'edit':
 553+ case self::EDIT_NORMAL:
 554+ return self::EDIT_NORMAL;
 555+
 556+ default:
 557+ return false;
 558+ }
 559+ }
 560+
 561+ /**
 562+ * Build a set of links for convenient navigation
 563+ * between watchlist viewing and editing modes
 564+ *
 565+ * @param $skin Skin to use
 566+ * @return string
 567+ */
 568+ public static function buildTools( $skin ) {
 569+ global $wgLang;
 570+
 571+ $tools = array();
 572+ $modes = array(
 573+ 'view' => array( 'Watchlist', false ),
 574+ 'edit' => array( 'EditWatchlist', false ),
 575+ 'raw' => array( 'EditWatchlist', 'raw' ),
 576+ );
 577+ foreach( $modes as $mode => $arr ) {
 578+ // can use messages 'watchlisttools-view', 'watchlisttools-edit', 'watchlisttools-raw'
 579+ $tools[] = $skin->linkKnown(
 580+ SpecialPage::getTitleFor( $arr[0], $arr[1] ),
 581+ wfMsgHtml( "watchlisttools-{$mode}" )
 582+ );
 583+ }
 584+ return Html::rawElement( 'span',
 585+ array( 'class' => 'mw-watchlist-toollinks' ),
 586+ wfMsg( 'parentheses', $wgLang->pipeList( $tools ) ) );
 587+ }
 588+}
 589+
 590+# B/C since 1.18
 591+class WatchlistEditor extends SpecialEditWatchlist {}
 592+
 593+/**
 594+ * Extend HTMLForm purely so we can have a more sane way of getting the section headers
 595+ */
 596+class EditWatchlistNormalHTMLForm extends HTMLForm {
 597+ public function getLegend( $namespace ){
 598+ global $wgLang;
 599+ $namespace = substr( $namespace, 2 );
 600+ return $namespace == NS_MAIN
 601+ ? wfMsgHtml( 'blanknamespace' )
 602+ : htmlspecialchars( $wgLang->getFormattedNsText( $namespace ) );
 603+ }
 604+}
\ No newline at end of file
Property changes on: trunk/phase3/includes/specials/SpecialEditWatchlist.php
___________________________________________________________________
Added: svn:eol-style
1605 + native
Index: trunk/phase3/includes/specials/SpecialWatchlist.php
@@ -68,12 +68,28 @@
6969
7070 $wgOut->setPageTitle( wfMsg( 'watchlist' ) );
7171
72 - $sub = wfMsgExt( 'watchlistfor2', array( 'parseinline', 'replaceafter' ), $wgUser->getName(), WatchlistEditor::buildTools( $wgUser->getSkin() ) );
 72+ $sub = wfMsgExt(
 73+ 'watchlistfor2',
 74+ array( 'parseinline', 'replaceafter' ),
 75+ $wgUser->getName(),
 76+ SpecialEditWatchlist::buildTools( $wgUser->getSkin() )
 77+ );
7378 $wgOut->setSubtitle( $sub );
7479
75 - if( ( $mode = WatchlistEditor::getMode( $wgRequest, $par ) ) !== false ) {
76 - $editor = new WatchlistEditor();
77 - $editor->execute( $wgUser, $wgOut, $wgRequest, $mode );
 80+ if( ( $mode = SpecialEditWatchlist::getMode( $wgRequest, $par ) ) !== false ) {
 81+ # TODO: localise?
 82+ switch( $mode ){
 83+ case SpecialEditWatchlist::EDIT_CLEAR:
 84+ $mode = 'clear';
 85+ break;
 86+ case SpecialEditWatchlist::EDIT_RAW:
 87+ $mode = 'raw';
 88+ break;
 89+ default:
 90+ $mode = null;
 91+ }
 92+ $title = SpecialPage::getTitleFor( 'EditWatchlist', $mode );
 93+ $wgOut->redirect( $title->getLocalUrl() );
7894 return;
7995 }
8096
Index: trunk/phase3/includes/SpecialPage.php
@@ -150,6 +150,7 @@
151151 'Activeusers' => 'SpecialActiveUsers',
152152 'Userrights' => 'UserrightsPage',
153153 'DisableAccount' => 'SpecialDisableAccount',
 154+ 'EditWatchlist' => 'SpecialEditWatchlist',
154155
155156 # Recent changes and logs
156157 'Newimages' => array( 'IncludableSpecialPage', 'Newimages' ),
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -398,6 +398,7 @@
399399 'DisableAccount' => array( 'DisableAccount' ),
400400 'Disambiguations' => array( 'Disambiguations' ),
401401 'DoubleRedirects' => array( 'DoubleRedirects' ),
 402+ 'EditWatchlist' => array( 'EditWatchlist' ),
402403 'Emailuser' => array( 'EmailUser' ),
403404 'Export' => array( 'Export' ),
404405 'Fewestrevisions' => array( 'FewestRevisions' ),
@@ -4234,7 +4235,7 @@
42354236 'watchlistedit-normal-legend' => 'Remove titles from watchlist',
42364237 'watchlistedit-normal-explain' => 'Titles on your watchlist are shown below.
42374238 To remove a title, check the box next to it, and click "{{int:Watchlistedit-normal-submit}}".
4238 -You can also [[Special:Watchlist/raw|edit the raw list]].',
 4239+You can also [[Special:EditWatchlist/raw|edit the raw list]].',
42394240 'watchlistedit-normal-submit' => 'Remove titles',
42404241 'watchlistedit-normal-done' => '{{PLURAL:$1|1 title was|$1 titles were}} removed from your watchlist:',
42414242 'watchlistedit-raw-title' => 'Edit raw watchlist',
@@ -4242,7 +4243,7 @@
42434244 'watchlistedit-raw-explain' => 'Titles on your watchlist are shown below, and can be edited by adding to and removing from the list;
42444245 one title per line.
42454246 When finished, click "{{int:Watchlistedit-raw-submit}}".
4246 -You can also [[Special:Watchlist/edit|use the standard editor]].',
 4247+You can also [[Special:EditWatchlist|use the standard editor]].',
42474248 'watchlistedit-raw-titles' => 'Titles:',
42484249 'watchlistedit-raw-submit' => 'Update watchlist',
42494250 'watchlistedit-raw-done' => 'Your watchlist has been updated.',

Follow-up revisions

RevisionCommit summaryAuthorDate
r90351Follow-up r84718: updated enotif msgaaron17:53, 18 June 2011
r99126* (bug 31435) Fix raw watchlist edit regression from r84718....brion18:08, 6 October 2011
r102334(bug 31502) (follow-up r84718) Restore ToC to Special:EditWatchlist if there ...happy-melon22:11, 7 November 2011

Comments

#Comment by Duplicatebug (talk | contribs)   10:23, 18 May 2011

There is one more message to update: MediaWiki:enotif body. Thanks.

#Comment by Duplicatebug (talk | contribs)   10:30, 18 May 2011

It is possible to redirct earlier to Special:EditWatchlist? At the moment, anon does not get redirect. In my opinion it is better to redirect anon also, maybe at the first step, before $wlToken? Thanks.

#Comment by Brion VIBBER (talk | contribs)   18:05, 6 October 2011

Causes bug 31435 -- double-escaping in raw watchlist output.

Probably would have been easier to catch if this hadn't modified and moved code at the same time.

#Comment by Platonides (talk | contribs)   19:13, 7 October 2011

Bug 31502 blames this for removing the TOC

#Comment by Happy-melon (talk | contribs)   22:12, 7 November 2011

Fixed in r102334.

#Comment by Krenair (talk | contribs)   19:58, 1 April 2013

includes/specials/SpecialEditWatchlist.php line 66: See https://gerrit.wikimedia.org/r/#/c/56946/

Status & tagging log