Merge "Rename autonym for 'no' from 'norsk bokmål' to 'norsk'"
[lhc/web/wiklou.git] / includes / specials / SpecialWatchlist.php
index 85ac2de..9e01d2d 100644 (file)
@@ -22,6 +22,8 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\ResultWrapper;
+use Wikimedia\Rdbms\IDatabase;
 
 /**
  * A special page that lists last changes made to the wiki,
@@ -32,6 +34,8 @@ use MediaWiki\MediaWikiServices;
 class SpecialWatchlist extends ChangesListSpecialPage {
        public function __construct( $page = 'Watchlist', $restriction = 'viewmywatchlist' ) {
                parent::__construct( $page, $restriction );
+
+               $this->maxDays = $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 );
        }
 
        public function doesWrites() {
@@ -79,6 +83,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                if ( ( $config->get( 'EnotifWatchlist' ) || $config->get( 'ShowUpdatedMarker' ) )
                        && $request->getVal( 'reset' )
                        && $request->wasPosted()
+                       && $user->matchEditToken( $request->getVal( 'token' ) )
                ) {
                        $user->clearAllNotifications();
                        $output->redirect( $this->getPageTitle()->getFullURL( $opts->getChangedValues() ) );
@@ -103,6 +108,58 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                ];
        }
 
+       /**
+        * @inheritdoc
+        */
+       protected function transformFilterDefinition( array $filterDefinition ) {
+               if ( isset( $filterDefinition['showHideSuffix'] ) ) {
+                       $filterDefinition['showHide'] = 'wl' . $filterDefinition['showHideSuffix'];
+               }
+
+               return $filterDefinition;
+       }
+
+       /**
+        * @inheritdoc
+        */
+       protected function registerFilters() {
+               parent::registerFilters();
+
+               $user = $this->getUser();
+
+               $significance = $this->getFilterGroup( 'significance' );
+               $hideMinor = $significance->getFilter( 'hideminor' );
+               $hideMinor->setDefault( $user->getBoolOption( 'watchlisthideminor' ) );
+
+               $automated = $this->getFilterGroup( 'automated' );
+               $hideBots = $automated->getFilter( 'hidebots' );
+               $hideBots->setDefault( $user->getBoolOption( 'watchlisthidebots' ) );
+
+               $registration = $this->getFilterGroup( 'registration' );
+               $hideAnons = $registration->getFilter( 'hideanons' );
+               $hideAnons->setDefault( $user->getBoolOption( 'watchlisthideanons' ) );
+               $hideLiu = $registration->getFilter( 'hideliu' );
+               $hideLiu->setDefault( $user->getBoolOption( 'watchlisthideliu' ) );
+
+               $reviewStatus = $this->getFilterGroup( 'reviewStatus' );
+               if ( $reviewStatus !== null ) {
+                       // Conditional on feature being available and rights
+                       $hidePatrolled = $reviewStatus->getFilter( 'hidepatrolled' );
+                       $hidePatrolled->setDefault( $user->getBoolOption( 'watchlisthidepatrolled' ) );
+               }
+
+               $authorship = $this->getFilterGroup( 'authorship' );
+               $hideMyself = $authorship->getFilter( 'hidemyself' );
+               $hideMyself->setDefault( $user->getBoolOption( 'watchlisthideown' ) );
+
+               $changeType = $this->getFilterGroup( 'changeType' );
+               $hideCategorization = $changeType->getFilter( 'hidecategorization' );
+               if ( $hideCategorization !== null ) {
+                       // Conditional on feature being available
+                       $hideCategorization->setDefault( $user->getBoolOption( 'watchlisthidecategorization' ) );
+               }
+       }
+
        /**
         * Get a FormOptions object containing the default options
         *
@@ -114,22 +171,15 @@ class SpecialWatchlist extends ChangesListSpecialPage {
 
                $opts->add( 'days', $user->getOption( 'watchlistdays' ), FormOptions::FLOAT );
                $opts->add( 'extended', $user->getBoolOption( 'extendwatchlist' ) );
-               if ( $this->getRequest()->getVal( 'action' ) == 'submit' ) {
-                       // The user has submitted the form, so we dont need the default values
-                       return $opts;
-               }
-
-               $opts->add( 'hideminor', $user->getBoolOption( 'watchlisthideminor' ) );
-               $opts->add( 'hidebots', $user->getBoolOption( 'watchlisthidebots' ) );
-               $opts->add( 'hideanons', $user->getBoolOption( 'watchlisthideanons' ) );
-               $opts->add( 'hideliu', $user->getBoolOption( 'watchlisthideliu' ) );
-               $opts->add( 'hidepatrolled', $user->getBoolOption( 'watchlisthidepatrolled' ) );
-               $opts->add( 'hidemyself', $user->getBoolOption( 'watchlisthideown' ) );
-               $opts->add( 'hidecategorization', $user->getBoolOption( 'watchlisthidecategorization' ) );
 
                return $opts;
        }
 
+       public function validateOptions( FormOptions $opts ) {
+               $opts->validateBounds( 'days', 0, $this->maxDays );
+               parent::validateOptions( $opts );
+       }
+
        /**
         * Get all custom filters
         *
@@ -171,6 +221,26 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                        }
                }
 
+               if ( $this->getRequest()->getVal( 'action' ) == 'submit' ) {
+                       $allBooleansFalse = [];
+
+                       // If the user submitted the form, start with a baseline of "all
+                       // booleans are false", then change the ones they checked.  This
+                       // means we ignore the defaults.
+
+                       // This is how we handle the fact that HTML forms don't submit
+                       // unchecked boxes.
+                       foreach ( $this->filterGroups as $filterGroup ) {
+                               if ( $filterGroup instanceof ChangesListBooleanFilterGroup ) {
+                                       foreach ( $filterGroup->getFilters() as $filter ) {
+                                               $allBooleansFalse[$filter->getName()] = false;
+                                       }
+                               }
+                       }
+
+                       $params = $params + $allBooleansFalse;
+               }
+
                // Not the prettiest way to achieve this… FormOptions internally depends on data sanitization
                // methods defined on WebRequest and removing this dependency would cause some code duplication.
                $request = new DerivativeRequest( $this->getRequest(), $params );
@@ -180,38 +250,33 @@ class SpecialWatchlist extends ChangesListSpecialPage {
        }
 
        /**
-        * Return an array of conditions depending of options set in $opts
-        *
-        * @param FormOptions $opts
-        * @return array
+        * @inheritdoc
         */
-       public function buildMainQueryConds( FormOptions $opts ) {
+       protected function buildQuery( &$tables, &$fields, &$conds, &$query_options,
+               &$join_conds, FormOptions $opts
+       ) {
                $dbr = $this->getDB();
-               $conds = parent::buildMainQueryConds( $opts );
+               parent::buildQuery( $tables, $fields, $conds, $query_options, $join_conds,
+                       $opts );
 
                // Calculate cutoff
                if ( $opts['days'] > 0 ) {
                        $conds[] = 'rc_timestamp > ' .
-                               $dbr->addQuotes( $dbr->timestamp( time() - intval( $opts['days'] * 86400 ) ) );
+                               $dbr->addQuotes( $dbr->timestamp( time() - $opts['days'] * 3600 * 24 ) );
                }
-
-               return $conds;
        }
 
        /**
-        * Process the query
-        *
-        * @param array $conds
-        * @param FormOptions $opts
-        * @return bool|ResultWrapper Result or false (for Recentchangeslinked only)
+        * @inheritdoc
         */
-       public function doMainQuery( $conds, $opts ) {
+       protected function doMainQuery( $tables, $fields, $conds, $query_options,
+               $join_conds, FormOptions $opts
+       ) {
                $dbr = $this->getDB();
                $user = $this->getUser();
 
                # Toggle watchlist content (all recent edits or just the latest)
                if ( $opts['extended'] ) {
-                       $limitWatchlist = $user->getIntOption( 'wllimit' );
                        $usePage = false;
                } else {
                        # Top log Ids for a page are not stored
@@ -226,30 +291,33 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                                        LIST_OR
                                );
                        }
-                       $limitWatchlist = 0;
                        $usePage = true;
                }
 
-               $tables = [ 'recentchanges', 'watchlist' ];
-               $fields = RecentChange::selectFields();
-               $query_options = [ 'ORDER BY' => 'rc_timestamp DESC' ];
-               $join_conds = [
-                       'watchlist' => [
-                               'INNER JOIN',
-                               [
-                                       'wl_user' => $user->getId(),
-                                       'wl_namespace=rc_namespace',
-                                       'wl_title=rc_title'
+               $tables = array_merge( [ 'recentchanges', 'watchlist' ], $tables );
+               $fields = array_merge( RecentChange::selectFields(), $fields );
+
+               $query_options = array_merge( [
+                       'ORDER BY' => 'rc_timestamp DESC',
+                       'LIMIT' => $user->getIntOption( 'wllimit' )
+               ], $query_options );
+               $join_conds = array_merge(
+                       [
+                               'watchlist' => [
+                                       'INNER JOIN',
+                                       [
+                                               'wl_user' => $user->getId(),
+                                               'wl_namespace=rc_namespace',
+                                               'wl_title=rc_title'
+                                       ],
                                ],
                        ],
-               ];
+                       $join_conds
+               );
 
                if ( $this->getConfig()->get( 'ShowUpdatedMarker' ) ) {
                        $fields[] = 'wl_notificationtimestamp';
                }
-               if ( $limitWatchlist ) {
-                       $query_options['LIMIT'] = $limitWatchlist;
-               }
 
                $rollbacker = $user->isAllowed( 'rollback' );
                if ( $usePage || $rollbacker ) {
@@ -360,7 +428,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
 
                $dbr->dataSeek( $rows, 0 );
 
-               $list = ChangesList::newFromContext( $this->getContext() );
+               $list = ChangesList::newFromContext( $this->getContext(), $this->filterGroups );
                $list->setWatchlistDivs();
                $list->initChangesListRows( $rows );
                $dbr->dataSeek( $rows, 0 );
@@ -436,7 +504,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                if ( $opts['days'] > 0 ) {
                        $days = $opts['days'];
                } else {
-                       $days = $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 );
+                       $days = $this->maxDays;
                }
                $timestamp = wfTimestampNow();
                $wlInfo = $this->msg( 'wlnote' )->numParams( $numRows, round( $days * 24 ) )->params(
@@ -447,31 +515,23 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                $cutofflinks = $this->msg( 'wlshowtime' ) . ' ' . $this->cutoffselector( $opts );
 
                # Spit out some control panel links
-               $filters = [
-                       'hideminor' => 'wlshowhideminor',
-                       'hidebots' => 'wlshowhidebots',
-                       'hideanons' => 'wlshowhideanons',
-                       'hideliu' => 'wlshowhideliu',
-                       'hidemyself' => 'wlshowhidemine',
-                       'hidepatrolled' => 'wlshowhidepatr'
-               ];
-
-               if ( $this->getConfig()->get( 'RCWatchCategoryMembership' ) ) {
-                       $filters['hidecategorization'] = 'wlshowhidecategorization';
-               }
-
-               foreach ( $this->getRenderableCustomFilters( $this->getCustomFilters() ) as $key => $params ) {
-                       $filters[$key] = $params['msg'];
-               }
-
-               // Disable some if needed
-               if ( !$user->useRCPatrol() ) {
-                       unset( $filters['hidepatrolled'] );
-               }
-
                $links = [];
-               foreach ( $filters as $name => $msg ) {
-                       $links[] = $this->showHideCheck( $nondefaults, $msg, $name, $opts[$name] );
+               $context = $this->getContext();
+               $namesOfDisplayedFilters = [];
+               foreach ( $this->getFilterGroups() as $groupName => $group ) {
+                       if ( !$group->isPerGroupRequestParameter() ) {
+                               foreach ( $group->getFilters() as $filterName => $filter ) {
+                                       if ( $filter->displaysOnUnstructuredUi( $this ) ) {
+                                               $namesOfDisplayedFilters[] = $filterName;
+                                               $links[] = $this->showHideCheck(
+                                                       $nondefaults,
+                                                       $filter->getShowHide(),
+                                                       $filterName,
+                                                       $opts[$filterName]
+                                               );
+                                       }
+                               }
+                       }
                }
 
                $hiddenFields = $nondefaults;
@@ -480,8 +540,8 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                unset( $hiddenFields['invert'] );
                unset( $hiddenFields['associated'] );
                unset( $hiddenFields['days'] );
-               foreach ( $filters as $key => $value ) {
-                       unset( $hiddenFields[$key] );
+               foreach ( $namesOfDisplayedFilters as $filterName ) {
+                       unset( $hiddenFields[$filterName] );
                }
 
                # Create output
@@ -544,7 +604,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                        $days[] = $userWatchlistOption;
                }
 
-               $maxDays = (string)( $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
+               $maxDays = (string)$this->maxDays;
                // add the maximum possible value, if it isn't available already
                if ( !in_array( $maxDays, $days ) ) {
                        $days[] = $maxDays;
@@ -606,6 +666,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                                'id' => 'mw-watchlist-resetbutton' ] ) . "\n" .
                        Xml::submitButton( $this->msg( 'enotif_reset' )->text(),
                                [ 'name' => 'mw-watchlist-reset-submit' ] ) . "\n" .
+                       Html::hidden( 'token', $user->getEditToken() ) . "\n" .
                        Html::hidden( 'reset', 'all' ) . "\n";
                        foreach ( $nondefaults as $key => $value ) {
                                $form .= Html::hidden( $key, $value ) . "\n";