Move all watchlist editing into WatchlistEditor class, integrating "raw editing"...
authorRob Church <robchurch@users.mediawiki.org>
Wed, 4 Jul 2007 01:52:16 +0000 (01:52 +0000)
committerRob Church <robchurch@users.mediawiki.org>
Wed, 4 Jul 2007 01:52:16 +0000 (01:52 +0000)
RELEASE-NOTES
includes/AutoLoader.php
includes/SpecialWatchlist.php
includes/WatchlistEditor.php [new file with mode: 0644]
languages/messages/MessagesEn.php
maintenance/language/messages.inc

index b5343a2..e193f9b 100644 (file)
@@ -117,9 +117,11 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
 * New javascript for upload page that will show a warning if a file with the
   "destination filename" already exists.
 * Add 'editsection-brackets' message to allow localization (or removal) of the
-  brackets in the "[edit]" link for sections.
-* (bug 10437) Move texvc styling to shared.css.
-* (bug 10438) HTML TeX formulas should not wrap.
+  brackets in the "[edit]" link for sections
+* (bug 10437) Move texvc styling to shared.css
+* (bug 10438) HTML TeX formulas should not wrap
+* Introduce "raw editing" mode for the watchlist, to allow bulk additions,
+  removals, and convenient exporting of watchlist contents
 
 == Bugfixes since 1.10 ==
 
index fbd01a1..745c378 100644 (file)
@@ -252,6 +252,7 @@ function __autoload($className) {
                'ZhClient' => 'includes/ZhClient.php',
                'memcached' => 'includes/memcached-client.php',
                'EmaillingJob' => 'includes/JobQueue.php',
+               'WatchlistEditor' => 'includes/WatchlistEditor.php',
 
                # filerepo
                'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
index 07fc6b0..a921e98 100644 (file)
@@ -35,10 +35,17 @@ function wfSpecialWatchlist( $par ) {
                $wgOut->setSubtitle( wfMsgWikiHtml( 'watchlistfor', htmlspecialchars( $wgUser->getName() ) ) );
        }
 
-       if( wlHandleClear( $wgOut, $wgRequest, $par ) ) {
+       if( ( $mode = WatchlistEditor::getMode( $wgRequest, $par ) ) !== false ) {
+               $editor = new WatchlistEditor();
+               $editor->execute( $wgUser, $wgOut, $wgRequest, $mode );
                return;
        }
 
+       $uid = $wgUser->getId();
+       if( $wgEnotifWatchlist && $wgRequest->getVal( 'reset' ) && $wgRequest->wasPosted() ) {
+               $wgUser->clearAllNotifications( $uid );
+       }
+
        $defaults = array(
        /* float */ 'days' => floatval( $wgUser->getOption( 'watchlistdays' ) ), /* 3.0 or 0.5, watch further below */
        /* bool  */ 'hideOwn' => (int)$wgUser->getBoolOption( 'watchlisthideown' ),
@@ -72,37 +79,6 @@ function wfSpecialWatchlist( $par ) {
                $nameSpaceClause = '';
        }
 
-       # Watchlist editing
-       $action = $wgRequest->getVal( 'action' );
-       $remove = $wgRequest->getVal( 'remove' );
-       $id     = $wgRequest->getArray( 'id' );
-
-       $uid = $wgUser->getID();
-       if( $wgEnotifWatchlist && $wgRequest->getVal( 'reset' ) && $wgRequest->wasPosted() ) {
-               $wgUser->clearAllNotifications( $uid );
-       }
-
-        # Deleting items from watchlist
-       if(($action == 'submit') && isset($remove) && is_array($id)) {
-               $wgOut->addWikiText( wfMsg( 'removingchecked' ) );
-               $wgOut->addHTML( "<ul id=\"mw-unwatch-list\">\n" );
-               foreach($id as $one) {
-                       $t = Title::newFromURL( $one );
-                       if( !is_null( $t ) ) {
-                               $wl = WatchedItem::fromUserTitle( $wgUser, $t );
-                               if( $wl->removeWatch() === false ) {
-                                       $wgOut->addHTML( '<li class="mw-unwatch-failure">' . wfMsg( 'couldntremove', htmlspecialchars($one) ) . "</li>\n" );
-                               } else {
-                                       wfRunHooks('UnwatchArticle', array(&$wgUser, new Article($t)));
-                                       $wgOut->addHTML( '<li class="mw-unwatch-success">[[' . htmlspecialchars($one) . "]]</li>\n" );
-                               }
-                       } else {
-                               $wgOut->addHTML( '<li class="mw-unwatch-invalid">' . wfMsg( 'iteminvalidname', htmlspecialchars($one) ) . "</li>\n" );
-                       }
-               }
-               $wgOut->addHTML( "</ul>\n<p>" . wfMsg( 'wldone' ) . "</p>\n" );
-       }
-
        $dbr = wfGetDB( DB_SLAVE, 'watchlist' );
        list( $page, $watchlist, $recentchanges ) = $dbr->tableNamesN( 'page', 'watchlist', 'recentchanges' );
 
@@ -155,81 +131,6 @@ function wfSpecialWatchlist( $par ) {
                $npages = 40000 * $days;
        }
 
-       /* Edit watchlist form */
-       if($wgRequest->getBool('edit') || $par == 'edit' ) {
-               $wgOut->addWikiText( wfMsgExt( 'watchlistcontains', array( 'parseinline' ), $wgLang->formatNum( $nitems ) ) .
-                       "\n\n" . wfMsg( 'watcheditlist' ) );
-
-               $wgOut->addHTML( '<form action=\'' .
-                       $specialTitle->escapeLocalUrl( 'action=submit' ) .
-                       "' method='post'>\n" );
-
-#              Patch A2
-#              The following was proposed by KTurner 07.11.2004 to T.Gries
-#              $sql = "SELECT distinct (wl_namespace & ~1),wl_title FROM $watchlist WHERE wl_user=$uid";
-               $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";
-
-               $res = $dbr->query( $sql, $fname );
-
-               # Batch existence check
-               $linkBatch = new LinkBatch();
-               while( $row = $dbr->fetchObject( $res ) )
-                       $linkBatch->addObj( Title::makeTitleSafe( $row->wl_namespace, $row->wl_title ) );
-               $linkBatch->execute();
-
-               if( $dbr->numRows( $res ) > 0 )
-                       $dbr->dataSeek( $res, 0 ); # Let's do the time warp again!
-
-               $sk = $wgUser->getSkin();
-
-               $list = array();
-               while( $s = $dbr->fetchObject( $res ) ) {
-                       $list[$s->wl_namespace][$s->wl_title] = $s->page_is_redirect;
-               }
-
-               // TODO: Display a TOC
-               foreach($list as $ns => $titles) {
-                       if (Namespace::isTalk($ns))
-                               continue;
-                       if ($ns != NS_MAIN)
-                               $wgOut->addHTML( '<h2>' . $wgContLang->getFormattedNsText( $ns ) . '</h2>' );
-                       $wgOut->addHTML( '<ul>' );
-                       foreach( $titles as $title => $redir ) {
-                               $titleObj = Title::makeTitle( $ns, $title );
-                               if( is_null( $titleObj ) ) {
-                                       $wgOut->addHTML(
-                                               '<!-- bad title "' .
-                                               htmlspecialchars( $s->wl_title ) . '" in namespace ' . $s->wl_namespace . " -->\n"
-                                       );
-                               } else {
-                                       global $wgContLang;
-                                       $toolLinks = array();
-                                       $pageLink = $sk->makeLinkObj( $titleObj );
-                                       $toolLinks[] = $sk->makeLinkObj( $titleObj->getTalkPage(), $wgLang->getNsText( NS_TALK ) );
-                                       if( $titleObj->exists() )
-                                               $toolLinks[] = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml( 'history_short' ), 'action=history' );
-                                       $toolLinks = '(' . implode( ' | ', $toolLinks ) . ')';
-                                       $checkbox = '<input type="checkbox" name="id[]" value="' . htmlspecialchars( $titleObj->getPrefixedText() ) . '" /> ' . ( $wgContLang->isRTL() ? '&rlm;' : '&lrm;' );
-                                       if( $redir ) {
-                                               $spanopen = '<span class="watchlistredir">';
-                                               $spanclosed = '</span>';
-                                       } else {
-                                               $spanopen = $spanclosed = '';
-                                       }
-
-                                       $wgOut->addHTML( "<li>{$checkbox}{$spanopen}{$pageLink}{$spanclosed} {$toolLinks}</li>\n" );
-                               }
-                       }
-                       $wgOut->addHTML( '</ul>' );
-               }
-               $wgOut->addHTML(
-                       wfSubmitButton( wfMsg('removechecked'), array('name' => 'remove') ) .
-                       "\n</form>\n"
-               );
-
-               return;
-       }
-
        # If the watchlist is relatively short, it's simplest to zip
        # down its entirety and then sort the results.
 
@@ -463,54 +364,4 @@ function wlCountItems( &$user, $talk = true ) {
                $count = floor( $count / 2 );
 
        return( $count );
-}
-
-/**
- * Allow the user to clear their watchlist
- *
- * @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 wlHandleClear( &$out, &$request, $par ) {
-       global $wgLang;
-
-       # Check this function has something to do
-       if( $request->getText( 'action' ) == 'clear' || $par == 'clear' ) {
-               global $wgUser;
-               $out->setPageTitle( wfMsgHtml( 'clearwatchlist' ) );
-               $count = wlCountItems( $wgUser );
-               if( $count > 0 ) {
-                       # See if we're clearing or confirming
-                       if( $request->wasPosted() && $wgUser->matchEditToken( $request->getText( 'token' ), 'clearwatchlist' ) ) {
-                               # Clearing, so do it and report the result
-                               $dbw = wfGetDB( DB_MASTER );
-                               $dbw->delete( 'watchlist', array( 'wl_user' => $wgUser->mId ), 'wlHandleClear' );
-                               $out->addWikiText( wfMsgExt( 'watchlistcleardone', array( 'parsemag', 'escape'), $wgLang->formatNum( $count ) ) );
-                               $out->returnToMain();
-                       } else {
-                               # Confirming, so show a form
-                               $wlTitle = SpecialPage::getTitleFor( 'Watchlist' );
-                               $out->addHTML( wfElement( 'form', array( 'method' => 'post', 'action' => $wlTitle->getLocalUrl( 'action=clear' ) ), NULL ) );
-                               $out->addWikiText( wfMsgExt( 'watchlistcount', array( 'parsemag', 'escape'), $wgLang->formatNum( $count ) ) );
-                               $out->addWikiText( wfMsg( 'watchlistcleartext' ) );
-                               $out->addHTML(
-                                       wfHidden( 'token', $wgUser->editToken( 'clearwatchlist' ) ) .
-                                       wfElement( 'input', array( 'type' => 'submit', 'name' => 'submit', 'value' => wfMsgHtml( 'watchlistclearbutton' ) ), '' ) .
-                                       wfCloseElement( 'form' )
-                               );
-                       }
-                       return( true );
-               } else {
-                       # Nothing on the watchlist; nothing to do here
-                       $out->addWikiText( wfMsg( 'nowatchlist' ) );
-                       $out->returnToMain();
-                       return( true );
-               }
-       } else {
-               return( false );
-       }
-}
-
+}
\ No newline at end of file
diff --git a/includes/WatchlistEditor.php b/includes/WatchlistEditor.php
new file mode 100644 (file)
index 0000000..aec6f4d
--- /dev/null
@@ -0,0 +1,393 @@
+<?php
+
+/**
+ * Provides the UI through which users can perform editing
+ * operations on their watchlist
+ *
+ * @addtogroup Watchlist
+ * @author Rob Church <robchur@gmail.com>
+ */
+class WatchlistEditor {
+
+       /**
+        * Editing modes
+        */
+       const EDIT_CLEAR = 1;
+       const EDIT_RAW = 2;
+       const EDIT_NORMAL = 3;
+
+       /**
+        * Main execution point
+        *
+        * @param User $user
+        * @param OutputPage $output
+        * @param WebRequest $request
+        * @param int $mode
+        */
+       public function execute( $user, $output, $request, $mode ) {
+               if( wfReadOnly() ) {
+                       $output->readOnlyPage();
+                       return;
+               }
+               switch( $mode ) {
+                       case self::EDIT_CLEAR:
+                               $output->setPageTitle( wfMsg( 'watchlistedit-clear-title' ) );
+                               if( $request->wasPosted() && $this->checkToken( $request, $user ) ) {
+                                       $this->clearWatchlist( $user );
+                                       $user->invalidateCache();
+                                       $output->addHtml( wfMsgExt( 'watchlistedit-clear-done', 'parse' ) );
+                               } else {
+                                       $this->showClearForm( $output, $user );
+                               }
+                               break;
+                       case self::EDIT_RAW:
+                               $output->setPageTitle( wfMsg( 'watchlistedit-raw-title' ) );
+                               if( $request->wasPosted() && $this->checkToken( $request, $user ) ) {
+                                       $titles = $this->extractTitles( $request->getText( 'titles' ) );
+                                       $this->clearWatchlist( $user );
+                                       $this->watchTitles( $titles, $user );
+                                       $user->invalidateCache();
+                                       $output->addHtml( wfMsgExt( 'watchlistedit-raw-done', 'parse' ) );
+                               }
+                               $this->showRawForm( $output, $user );
+                               break;
+                       case self::EDIT_NORMAL:
+                               $output->setPageTitle( wfMsg( 'watchlistedit-normal-title' ) );
+                               if( $request->wasPosted() && $this->checkToken( $request, $user ) ) {
+                                       $titles = $this->extractTitles( $request->getArray( 'titles' ) );
+                                       $this->unwatchTitles( $titles, $user );
+                                       $user->invalidateCache();
+                                       $output->addHtml( wfMsgExt( 'watchlistedit-normal-done', 'parse',
+                                               $GLOBALS['wgLang']->formatNum( count( $titles ) ) ) );
+                                       $this->showTitles( $titles, $output, $user->getSkin() );
+                               }
+                               $this->showNormalForm( $output, $user );
+               }
+       }
+       
+       /**
+        * Check the edit token from a form submission
+        *
+        * @param WebRequest $request
+        * @param User $user
+        * @return bool
+        */
+       private function checkToken( $request, $user ) {
+               return $user->matchEditToken( $request->getVal( 'token' ), 'watchlistedit' );   
+       }
+       
+       /**
+        * Extract a list of titles from a text list; if we're given
+        * an array, convert each item into a Title
+        *
+        * @param mixed $list
+        * @return array
+        */
+       private function extractTitles( $list ) {
+               if( !is_array( $list ) ) {
+                       $list = explode( "\n", $list );
+                       if( !is_array( $list ) )
+                               return array();
+               }
+               for( $i = 0; $i < count( $list ); $i++ ) {
+                       $list[$i] = Title::newFromText( $list[$i] );
+                       if( !$list[$i] instanceof Title )
+                               unset( $list[$i] );
+               }
+               return $list;
+       }
+       
+       /**
+        * Print out a list of linked titles
+        *
+        * @param array $titles
+        * @param OutputPage $output
+        * @param Skin $skin
+        */
+       private function showTitles( $titles, $output, $skin ) {
+               $talk = htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( NS_TALK ) );
+               // Do a batch existence check           
+               $batch = new LinkBatch();
+               foreach( $titles as $title ) {
+                       $batch->addObj( $title );
+                       $batch->addObj( $title->getTalkPage() );
+               }
+               $batch->execute();
+               // Print out the list
+               $output->addHtml( "<ul>\n" );
+               foreach( $titles as $title )
+                       $output->addHtml( "<li>" . $skin->makeLinkObj( $title )
+                       . ' (' . $skin->makeLinkObj( $title->getTalkPage(), $talk ) . ")</li>\n" );
+               $output->addHtml( "</ul>\n" );
+       }
+       
+       /**
+        * Count the number of titles on a user's watchlist, excluding talk pages
+        *
+        * @param User $user
+        * @return int
+        */
+       private function countWatchlist( $user ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'watchlist', 'COUNT(*) AS count', array( 'wl_user' => $user->getId() ), __METHOD__ );
+               $row = $dbr->fetchObject( $res );
+               return ceil( $row->count / 2 ); // Paranoia
+       }
+       
+       /**
+        * Get a list of titles on a user's watchlist, excluding talk pages,
+        * and return as a two-dimensional array with namespace, title and
+        * redirect status
+        *
+        * @param User $user
+        * @return array
+        */
+       private function getWatchlist( $user ) {
+               $titles = array();
+               $dbr = wfGetDB( DB_SLAVE );
+               $uid = intval( $user->getId() );
+               list( $watchlist, $page ) = $dbr->tableNamesN( 'watchlist', 'page' );
+               $sql = "SELECT wl_namespace, wl_title, page_id, page_is_redirect
+                       FROM {$watchlist} LEFT JOIN {$page} ON ( wl_namespace = page_namespace
+                       AND wl_title = page_title ) WHERE wl_user = {$uid}";
+               $res = $dbr->query( $sql, __METHOD__ );
+               if( $res && $dbr->numRows( $res ) > 0 ) {
+                       $cache = LinkCache::singleton();
+                       while( $row = $dbr->fetchObject( $res ) ) {
+                               $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
+                               if( $title instanceof Title ) {
+                                       // Update the link cache while we're at it
+                                       if( $row->page_id ) {
+                                               $cache->addGoodLinkObj( $row->page_id, $title );
+                                       } else {
+                                               $cache->addBadLinkObj( $title );
+                                       }
+                                       // Ignore non-talk
+                                       if( !$title->isTalkPage() )
+                                               $titles[$row->wl_namespace][$row->wl_title] = $row->page_is_redirect;
+                               }
+                       }
+               }
+               return $titles;
+       }
+       
+       /**
+        * Show a message indicating the number of items on the user's watchlist,
+        * and return this count for additional checking
+        *
+        * @param OutputPage $output
+        * @param User $user
+        * @return int
+        */
+       private function showItemCount( $output, $user ) {
+               if( ( $count = $this->countWatchlist( $user ) ) > 0 ) {
+                       $output->addHtml( wfMsgExt( 'watchlistedit-numitems', 'parse',
+                               $GLOBALS['wgLang']->formatNum( $count ) ) );
+               } else {
+                       $output->addHtml( wfMsgExt( 'watchlistedit-noitems', 'parse' ) );
+               }
+               return $count;
+       }
+       
+       /**
+        * Remove all titles from a user's watchlist
+        *
+        * @param User $user
+        */
+       private function clearWatchlist( $user ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete( 'watchlist', array( 'wl_user' => $user->getId() ), __METHOD__ );
+       }
+
+       /**
+        * Add a list of titles to a user's watchlist
+        *
+        * @param array $titles
+        * @param User $user
+        */
+       private function watchTitles( $titles, $user ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $rows = array();
+               foreach( $titles as $title ) {
+                       $rows[] = array(
+                               'wl_user' => $user->getId(),
+                               'wl_namespace' => ( $title->getNamespace() & ~1 ),
+                               'wl_title' => $title->getDBkey(),
+                               'wl_notificationtimestamp' => null,
+                       );
+                       $rows[] = array(
+                               'wl_user' => $user->getId(),
+                               'wl_namespace' => ( $title->getNamespace() | 1 ),
+                               'wl_title' => $title->getDBkey(),
+                               'wl_notificationtimestamp' => null,
+                       );
+               }
+               $dbw->insert( 'watchlist', $rows, __METHOD__, 'IGNORE' );
+       }
+
+       /**
+        * Remove a list of titles from a user's watchlist
+        *
+        * @param array $titles
+        * @param User $user
+        */
+       private function unwatchTitles( $titles, $user ) {
+               $dbw = wfGetDB( DB_MASTER );
+               foreach( $titles as $title ) {
+                       $dbw->delete(
+                               'watchlist',
+                               array(
+                                       'wl_user' => $user->getId(),
+                                       'wl_namespace' => ( $title->getNamespace() & ~1 ),
+                                       'wl_title' => $title->getDBkey(),
+                               ),
+                               __METHOD__
+                       );
+                       $dbw->delete(
+                               'watchlist',
+                               array(
+                                       'wl_user' => $user->getId(),
+                                       'wl_namespace' => ( $title->getNamespace() | 1 ),
+                                       'wl_title' => $title->getDBkey(),
+                               ),
+                               __METHOD__
+                       );
+               }
+       }
+
+       /**
+        * Show a confirmation form for users wishing to clear their watchlist
+        *
+        * @param OutputPage $output
+        * @param User $user
+        */
+       private function showClearForm( $output, $user ) {
+               if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) {
+                       $self = SpecialPage::getTitleFor( 'Watchlist' );
+                       $form  = Xml::openElement( 'form', array( 'method' => 'post',
+                               'action' => $self->getLocalUrl( 'action=clear' ) ) );
+                       $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) );
+                       $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-clear-legend' ) . '</legend>';
+                       $form .= wfMsgExt( 'watchlistedit-clear-confirm', 'parse' );
+                       $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-clear-submit' ) ) . '</p>';
+                       $form .= '</fieldset></form>';
+                       $output->addHtml( $form );
+               }
+       }
+       
+       /**
+        * Show the standard watchlist editing form
+        *
+        * @param OutputPage $output
+        * @param User $user
+        */
+       private function showNormalForm( $output, $user ) {
+               if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) {
+                       $self = SpecialPage::getTitleFor( 'Watchlist' );
+                       $form  = Xml::openElement( 'form', array( 'method' => 'post',
+                               'action' => $self->getLocalUrl( 'action=edit' ) ) );
+                       $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) );
+                       $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-normal-legend' ) . '</legend>';
+                       $form .= wfMsgExt( 'watchlistedit-normal-explain', 'parse' );
+                       foreach( $this->getWatchlist( $user ) as $namespace => $pages ) {
+                               $form .= '<h2>' . $this->getNamespaceHeading( $namespace ) . '</h2>';
+                               $form .= '<ul>';
+                               foreach( $pages as $dbkey => $redirect ) {
+                                       $title = Title::makeTitleSafe( $namespace, $dbkey );
+                                       $form .= $this->buildRemoveLine( $title, $redirect, $user->getSkin() );
+                               }
+                               $form .= '</ul>';
+                       }
+                       $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-normal-submit' ) ) . '</p>';
+                       $form .= '</fieldset></form>';
+                       $output->addHtml( $form );
+               }
+       }
+       
+       /**
+        * Get the correct "heading" for a namespace
+        *
+        * @param int $namespace
+        * @return string
+        */
+       private function getNamespaceHeading( $namespace ) {
+               return $namespace == NS_MAIN
+                       ? wfMsgHtml( 'blanknamespace' )
+                       : htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( $namespace ) );
+       }
+       
+       /**
+        * Build a single list item containing a check box selecting a title
+        * and a link to that title, with various additional bits
+        *
+        * @param Title $title
+        * @param bool $redirect
+        * @param Skin $skin
+        * @return string
+        */
+       private function buildRemoveLine( $title, $redirect, $skin ) {
+               $link = $skin->makeLinkObj( $title );
+               if( $redirect )
+                       $link = '<span class="watchlistredir">' . $link . '</span>';
+               $tools[] = $skin->makeLinkObj( $title->getTalkPage(),
+                       htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( NS_TALK ) ) );
+               if( $title->exists() )
+                       $tools[] = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'history_short' ), 'action=history' );
+               return '<li>'
+                       . Xml::check( 'titles[]', false, array( 'value' => $title->getPrefixedText() ) )
+                       . $link . ' (' . implode( ' | ', $tools ) . ')' . '</li>';
+               }
+       
+       /**
+        * Show a form for editing the watchlist in "raw" mode
+        *
+        * @param OutputPage $output
+        * @param User $user
+        */
+       public function showRawForm( $output, $user ) {
+               $this->showItemCount( $output, $user );
+               $self = SpecialPage::getTitleFor( 'Watchlist' );
+               $form  = Xml::openElement( 'form', array( 'method' => 'post',
+                       'action' => $self->getLocalUrl( 'action=raw' ) ) );
+               $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) );
+               $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-raw-legend' ) . '</legend>';
+               $form .= wfMsgExt( 'watchlistedit-raw-explain', 'parse' );
+               $form .= Xml::label( wfMsg( 'watchlistedit-raw-titles' ), 'titles' );
+               $form .= Xml::openElement( 'textarea', array( 'id' => 'titles', 'name' => 'titles',
+                       'rows' => 6, 'cols' => 80 ) );
+               foreach( $this->getWatchlist( $user ) as $namespace => $pages ) {
+                       foreach( $pages as $dbkey => $redirect ) {
+                               $title = Title::makeTitleSafe( $namespace, $dbkey );
+                               if( $title instanceof Title )
+                                       $form .= htmlspecialchars( $title->getPrefixedText() ) . "\n";
+                       }
+               }
+               $form .= '</textarea>';
+               $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-raw-submit' ) ) . '</p>';
+               $form .= '</fieldset></form>';
+               $output->addHtml( $form );
+       }
+       
+       /**
+        * Determine whether we are editing the watchlist, and if so, what
+        * kind of editing operation
+        *
+        * @param WebRequest $request
+        * @param mixed $par
+        * @return int
+        */
+       public static function getMode( $request, $par ) {
+               $mode = strtolower( $request->getVal( 'action', $par ) );
+               switch( $mode ) {
+                       case 'clear':
+                               return self::EDIT_CLEAR;
+                       case 'raw':
+                               return self::EDIT_RAW;
+                       case 'edit':
+                               return self::EDIT_NORMAL;
+                       default:
+                               return false;
+               }
+       }
+
+}
\ No newline at end of file
index d4065af..8d402eb 100644 (file)
@@ -1692,10 +1692,6 @@ or has chosen not to receive e-mail from other users.',
 'nowatchlist'          => 'You have no items on your watchlist.',
 'watchlistanontext'    => 'Please $1 to view or edit items on your watchlist.',
 'watchlistcount'       => "'''You have {{PLURAL:$1|$1 item|$1 items}} on your watchlist, including talk pages.'''",
-'clearwatchlist'       => 'Clear watchlist',
-'watchlistcleartext'   => 'Are you sure you wish to remove them?',
-'watchlistclearbutton' => 'Clear watchlist',
-'watchlistcleardone'   => 'Your watchlist has been cleared. {{PLURAL:$1|$1 item was|$1 items were}} removed.',
 'watchnologin'         => 'Not logged in',
 'watchnologintext'     => 'You must be [[Special:Userlogin|logged in]] to modify your watchlist.',
 'addedwatch'           => 'Added to watchlist',
@@ -1720,13 +1716,7 @@ If you want to remove the page from your watchlist later, click \"Unwatch\" in t
 'wlheader-showupdated' => "* Pages which have been changed since you last visited them are shown in '''bold'''",
 'watchmethod-recent'   => 'checking recent edits for watched pages',
 'watchmethod-list'     => 'checking watched pages for recent edits',
-'removechecked'        => 'Remove checked items from watchlist',
 'watchlistcontains'    => 'Your watchlist contains $1 {{PLURAL:$1|page|pages}}.',
-'watcheditlist'        => "Here's an alphabetical list of your
-watched content pages. Check the boxes of pages you want to remove from your watchlist and click the 'remove checked' button
-at the bottom of the screen (deleting a content page also deletes the accompanying talk page and vice versa).",
-'removingchecked'      => 'Removing requested items from watchlist...',
-'couldntremove'        => "Couldn't remove item '$1'...",
 'iteminvalidname'      => "Problem with item '$1', invalid name...",
 'wlnote'               => "Below {{PLURAL:$1|is the last change|are the last '''$1''' changes}} in the last {{PLURAL:$2|hour|'''$2''' hours}}.",
 'wlshowlast'           => 'Show last $1 hours $2 days $3',
@@ -1737,7 +1727,6 @@ at the bottom of the screen (deleting a content page also deletes the accompanyi
 'watchlist-hide-own'   => 'Hide my edits',
 'watchlist-show-minor' => 'Show minor edits',
 'watchlist-hide-minor' => 'Hide minor edits',
-'wldone'               => 'Done.',
 
 # Displayed when you click the "watch" button and it's in the process of watching
 'watching'   => 'Watching...',
@@ -2896,4 +2885,29 @@ $1',
 'lag-warn-normal' => 'Changes newer than $1 seconds may not be shown in this list.',
 'lag-warn-high'   => 'Due to high database server lag, changes newer than $1 seconds may not be shown in this list.',
 
-);
+# Watchlist editor
+'watchlistedit-numitems' => 'Your watchlist contains $1 title(s), excluding talk pages.',
+'watchlistedit-noitems' => 'Your watchlist contains no titles.',
+'watchlistedit-clear-title' => 'Clear watchlist',
+'watchlistedit-clear-legend' => 'Clear watchlist',
+'watchlistedit-clear-confirm' => 'This will remove all titles from your watchlist. Are you sure you
+       want to do this? You can also [[Special:Watchlist/edit|remove individual titles]].',
+'watchlistedit-clear-submit' => 'Clear',
+'watchlistedit-clear-done' => 'Your watchlist has been cleared. All titles were removed.',
+'watchlistedit-normal-title' => 'Edit watchlist',
+'watchlistedit-normal-legend' => 'Remove titles from watchlist',
+'watchlistedit-normal-explain' => 'Titles on your watchlist are shown below. To remove a title, check
+       the box next to it, and click Remove Titles. You can also [[Special:Watchlist/raw|edit the raw list]],
+       or [[Special:Watchlist/clear|remove all titles]].',
+'watchlistedit-normal-submit' => 'Remove Titles',
+'watchlistedit-normal-done' => '$1 title(s) were removed from your watchlist:',
+'watchlistedit-raw-title' => 'Edit raw watchlist',
+'watchlistedit-raw-legend' => 'Edit raw watchlist',
+'watchlistedit-raw-explain' => 'Titles on your watchlist are shown below, and can be edited by
+       adding to and removing from the list; one title per line. When finished, click Update Watchlist.
+       You can also [[Special:Watchlist/edit|use the standard editor]].',
+'watchlistedit-raw-titles' => 'Titles:',
+'watchlistedit-raw-submit' => 'Update Watchlist',
+'watchlistedit-raw-done' => 'Your watchlist has been updated.',
+
+);
\ No newline at end of file
index c252b92..fa0ede3 100644 (file)
@@ -1080,10 +1080,6 @@ $wgMessageStructure = array(
                'nowatchlist',
                'watchlistanontext',
                'watchlistcount',
-               'clearwatchlist',
-               'watchlistcleartext',
-               'watchlistclearbutton',
-               'watchlistcleardone',
                'watchnologin',
                'watchnologintext',
                'addedwatch',
@@ -1101,11 +1097,7 @@ $wgMessageStructure = array(
                'wlheader-showupdated',
                'watchmethod-recent',
                'watchmethod-list',
-               'removechecked',
                'watchlistcontains',
-               'watcheditlist',
-               'removingchecked',
-               'couldntremove',
                'iteminvalidname',
                'wlnote',
                'wlshowlast',
@@ -1116,7 +1108,6 @@ $wgMessageStructure = array(
                'watchlist-hide-own',
                'watchlist-show-minor',
                'watchlist-hide-minor',
-               'wldone',
        ),
        'watching' => array(
                'watching',
@@ -2131,6 +2122,26 @@ $wgMessageStructure = array(
                'lag-warn-normal',
                'lag-warn-high',
        ),
+       'watchlisteditor' => array(
+               'watchlistedit-numitems',
+               'watchlistedit-noitems',
+               'watchlistedit-clear-title',
+               'watchlistedit-clear-legend',
+               'watchlistedit-clear-confirm',
+               'watchlistedit-clear-submit',
+               'watchlistedit-clear-done',
+               'watchlistedit-normal-title',
+               'watchlistedit-normal-legend',
+               'watchlistedit-normal-explain',
+               'watchlistedit-normal-submit',
+               'watchlistedit-normal-done',
+               'watchlistedit-raw-title',
+               'watchlistedit-raw-legend',
+               'watchlistedit-raw-explain',
+               'watchlistedit-raw-titles',
+               'watchlistedit-raw-submit',
+               'watchlistedit-raw-done',
+       ),      
 );
 /** Comments for each block */
 $wgBlockComments = array(
@@ -2297,6 +2308,7 @@ Variants for Chinese language",
        'sizeunits'           => 'Size units',
        'livepreview'         => 'Live preview',
        'lagwarning'          => 'Friendlier slave lag warnings',
+       'watchlisteditor' => 'Watchlist editor',
 );
 
 /** Short comments for standalone messages */