Merge "Revert "Adding sanity check to Title::isRedirect().""
[lhc/web/wiklou.git] / includes / specials / SpecialEditWatchlist.php
index 7efa2b7..3d43831 100644 (file)
@@ -3,10 +3,34 @@
  * @defgroup Watchlist Users watchlist handling
  */
 
+/**
+ * Implements Special:EditWatchlist
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup SpecialPage
+ * @ingroup Watchlist
+ */
+
 /**
  * Provides the UI through which users can perform editing
  * operations on their watchlist
  *
+ * @ingroup SpecialPage
  * @ingroup Watchlist
  * @author Rob Church <robchur@gmail.com>
  */
@@ -23,6 +47,8 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
 
        protected $toc;
 
+       private $badItems = array();
+
        public function __construct(){
                parent::__construct( 'EditWatchlist' );
        }
@@ -215,8 +241,9 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                $dbr = wfGetDB( DB_MASTER );
                $res = $dbr->select(
                        'watchlist',
-                       '*',
                        array(
+                               'wl_namespace', 'wl_title'
+                       ), array(
                                'wl_user' => $this->getUser()->getId(),
                        ),
                        __METHOD__
@@ -224,11 +251,15 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                if( $res->numRows() > 0 ) {
                        foreach ( $res as $row ) {
                                $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
-                               if( $title instanceof Title && !$title->isTalkPage() )
+                               if ( $this->checkTitle( $title, $row->wl_namespace, $row->wl_title )
+                                       && !$title->isTalkPage()
+                               ) {
                                        $list[] = $title->getPrefixedText();
+                               }
                        }
                        $res->free();
                }
+               $this->cleanupWatchlist();
                return $list;
        }
 
@@ -247,7 +278,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                        array( 'wl_namespace',  'wl_title' ),
                        array( 'wl_user' => $this->getUser()->getId() ),
                        __METHOD__,
-                       array( 'ORDER BY' => 'wl_namespace, wl_title' )
+                       array( 'ORDER BY' => array( 'wl_namespace', 'wl_title' ) )
                );
 
                $lb = new LinkBatch();
@@ -262,6 +293,62 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                return $titles;
        }
 
+       /**
+        * Validates watchlist entry
+        *
+        * @param Title $title
+        * @param int $namespace
+        * @param String $dbKey
+        * @return bool: Whether this item is valid
+        */
+       private function checkTitle( $title, $namespace, $dbKey ) {
+               if ( $title
+                       && ( $title->isExternal()
+                               || $title->getNamespace() < 0
+                       )
+               ) {
+                       $title = false; // unrecoverable
+               }
+               if ( !$title
+                       || $title->getNamespace() != $namespace
+                       || $title->getDBkey() != $dbKey
+               ) {
+                       $this->badItems[] = array( $title, $namespace, $dbKey );
+               }
+               return (bool)$title;
+       }
+
+       /**
+        * Attempts to clean up broken items
+        */
+       private function cleanupWatchlist() {
+               if( !count( $this->badItems ) ) {
+                       return; //nothing to do
+               }
+               $dbw = wfGetDB( DB_MASTER );
+               $user = $this->getUser();
+               foreach ( $this->badItems as $row ) {
+                       list( $title, $namespace, $dbKey ) = $row;
+                       wfDebug( "User {$user->getName()} has broken watchlist item ns($namespace):$dbKey, "
+                               . ( $title ? 'cleaning up' : 'deleting' ) . ".\n"
+                       );
+
+                       $dbw->delete( 'watchlist',
+                               array(
+                                       'wl_user' => $user->getId(),
+                                       'wl_namespace' => $namespace,
+                                       'wl_title' => $dbKey,
+                               ),
+                               __METHOD__
+                       );
+
+                       // Can't just do an UPDATE instead of DELETE/INSERT due to unique index
+                       if ( $title ) {
+                               $user->addWatch( $title );
+                       }
+               }
+       }
+
        /**
         * Remove all titles from a user's watchlist
         */
@@ -351,7 +438,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
 
                foreach( $data as $titles ) {
                        $this->unwatchTitles( $titles );
-                       $removed += $titles;
+                       $removed = array_merge( $removed, $titles );
                }
 
                if( count( $removed ) > 0 ) {
@@ -375,30 +462,25 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                $fields = array();
                $count = 0;
 
-               $haveInvalidNamespaces = false;
                foreach( $this->getWatchlistInfo() as $namespace => $pages ){
-                       if ( $namespace < 0 ) {
-                               $haveInvalidNamespaces = true;
-                               continue;
+                       if ( $namespace >= 0 ) {
+                               $fields['TitlesNs'.$namespace] = array(
+                                       'class' => 'EditWatchlistCheckboxSeriesField',
+                                       'options' => array(),
+                                       'section' => "ns$namespace",
+                               );
                        }
 
-                       $fields['TitlesNs'.$namespace] = array(
-                               'class' => 'EditWatchlistCheckboxSeriesField',
-                               'options' => array(),
-                               'section' => "ns$namespace",
-                       );
-
                        foreach( array_keys( $pages ) as $dbkey ){
                                $title = Title::makeTitleSafe( $namespace, $dbkey );
-                               $text = $this->buildRemoveLine( $title );
-                               $fields['TitlesNs'.$namespace]['options'][$text] = $title->getEscapedText();
-                               $count++;
+                               if ( $this->checkTitle( $title, $namespace, $dbkey ) ) {
+                                       $text = $this->buildRemoveLine( $title );
+                                       $fields['TitlesNs'.$namespace]['options'][$text] = $title->getEscapedText();
+                                       $count++;
+                               }
                        }
                }
-               if ( $haveInvalidNamespaces ) {
-                       wfDebug( "User {$this->getContext()->getUser()->getId()} has invalid watchlist entries, cleaning up...\n" );
-                       $this->getContext()->getUser()->cleanupWatchlist();
-               }
+               $this->cleanupWatchlist();
 
                if ( count( $fields ) > 1 && $count > 30 ) {
                        $this->toc = Linker::tocIndent();