Cap 'days' param in RC/Watchlist at $wgRCMaxAge
[lhc/web/wiklou.git] / includes / specials / SpecialRecentchanges.php
index c9c2475..f0c2bc4 100644 (file)
@@ -315,7 +315,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $opts = parent::getDefaultOptions();
                $user = $this->getUser();
 
-               $opts->add( 'days', $user->getIntOption( 'rcdays' ) );
+               $opts->add( 'days', $user->getIntOption( 'rcdays' ), FormOptions::FLOAT );
                $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
                $opts->add( 'from', '' );
 
@@ -359,7 +359,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) {
                                $opts['limit'] = $m[1];
                        }
-                       if ( preg_match( '/^days=(\d+)$/', $bit, $m ) ) {
+                       if ( preg_match( '/^days=(\d+(?:\.\d+)?)$/', $bit, $m ) ) {
                                $opts['days'] = $m[1];
                        }
                        if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
@@ -373,6 +373,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
 
        public function validateOptions( FormOptions $opts ) {
                $opts->validateIntBounds( 'limit', 0, 5000 );
+               $opts->validateBounds( 'days', 0, $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
                parent::validateOptions( $opts );
        }
 
@@ -387,8 +388,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        $query_options, $join_conds, $opts );
 
                // Calculate cutoff
-               $cutoff_unixtime = time() - ( $opts['days'] * 86400 );
-               $cutoff_unixtime = $cutoff_unixtime - ( $cutoff_unixtime % 86400 );
+               $cutoff_unixtime = time() - $opts['days'] * 3600 * 24;
                $cutoff = $dbr->timestamp( $cutoff_unixtime );
 
                $fromValid = preg_match( '/^[0-9]{14}$/', $opts['from'] );
@@ -430,13 +430,14 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $fields[] = 'page_latest';
                $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
 
+               $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
                ChangeTags::modifyDisplayQuery(
                        $tables,
                        $fields,
                        $conds,
                        $join_conds,
                        $query_options,
-                       $opts['tagfilter']
+                       $tagFilter
                );
 
                if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
@@ -449,13 +450,24 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        return false;
                }
 
+               $orderByAndLimit = [
+                       'ORDER BY' => 'rc_timestamp DESC',
+                       'LIMIT' => $opts['limit']
+               ];
+               if ( in_array( 'DISTINCT', $query_options ) ) {
+                       // ChangeTags::modifyDisplayQuery() adds DISTINCT when filtering on multiple tags.
+                       // In order to prevent DISTINCT from causing query performance problems,
+                       // we have to GROUP BY the primary key. This in turn requires us to add
+                       // the primary key to the end of the ORDER BY, and the old ORDER BY to the
+                       // start of the GROUP BY
+                       $orderByAndLimit['ORDER BY'] = 'rc_timestamp DESC, rc_id DESC';
+                       $orderByAndLimit['GROUP BY'] = 'rc_timestamp, rc_id';
+               }
                // array_merge() is used intentionally here so that hooks can, should
                // they so desire, override the ORDER BY / LIMIT condition(s); prior to
                // MediaWiki 1.26 this used to use the plus operator instead, which meant
                // that extensions weren't able to change these conditions
-               $query_options = array_merge( [
-                       'ORDER BY' => 'rc_timestamp DESC',
-                       'LIMIT' => $opts['limit'] ], $query_options );
+               $query_options = array_merge( $orderByAndLimit, $query_options );
                $rows = $dbr->select(
                        $tables,
                        $fields,