Avoid arithmetics on localized number string ("0,04") in SpecialWatchlist
authorEdward Chernenko <edwardspec@gmail.com>
Sun, 1 Jul 2018 02:10:56 +0000 (05:10 +0300)
committerEdward Chernenko <edwardspec@gmail.com>
Tue, 10 Jul 2018 00:05:34 +0000 (00:05 +0000)
In SpecialWatchlist::cutoffselector(), values like 1/24 or 6/24 are cast
to string via strval(). However, in some locales (e.g. ru_RU.utf8) strval
will return a localized form of the number, e.g. "0,04" instead of "0.04".

This "0,04" is then used in arithmetic operations, where it's treated as 0,
resulting in "0 hours" being shown instead of "1 hour", "2 hours", etc.

Bug: T198501
Change-Id: Iaa4e6170b30a7bb9ce0f22d9d2cc4772b0faa3b8
(cherry picked from commit 6b240c699eb1e965dad3c51107566f1f27ed1887)

includes/specials/SpecialWatchlist.php

index dda1dac..55a7f03 100644 (file)
@@ -751,45 +751,36 @@ class SpecialWatchlist extends ChangesListSpecialPage {
        }
 
        function cutoffselector( $options ) {
-               // Cast everything to strings immediately, so that we know all of the values have the same
-               // precision, and can be compared with '==='. 2/24 has a few more decimal places than its
-               // default string representation, for example, and would confuse comparisons.
-
-               // Misleadingly, the 'days' option supports hours too.
-               $days = array_map( 'strval', [ 1 / 24, 2 / 24, 6 / 24, 12 / 24, 1, 3, 7 ] );
-
-               $userWatchlistOption = (string)$this->getUser()->getOption( 'watchlistdays' );
-               // add the user preference, if it isn't available already
-               if ( !in_array( $userWatchlistOption, $days ) && $userWatchlistOption !== '0' ) {
-                       $days[] = $userWatchlistOption;
-               }
-
-               $maxDays = (string)$this->maxDays;
-               // add the maximum possible value, if it isn't available already
-               if ( !in_array( $maxDays, $days ) ) {
-                       $days[] = $maxDays;
-               }
-
-               $selected = (string)$options['days'];
+               $selected = (float)$options['days'];
                if ( $selected <= 0 ) {
-                       $selected = $maxDays;
-               }
-
-               // add the currently selected value, if it isn't available already
-               if ( !in_array( $selected, $days ) ) {
-                       $days[] = $selected;
-               }
+                       $selected = $this->maxDays;
+               }
+
+               $selectedHours = round( $selected * 24 );
+
+               $hours = array_unique( array_filter( [
+                       1,
+                       2,
+                       6,
+                       12,
+                       24,
+                       72,
+                       168,
+                       24 * (float)$this->getUser()->getOption( 'watchlistdays', 0 ),
+                       24 * $this->maxDays,
+                       $selectedHours
+               ] ) );
+               asort( $hours );
 
-               $select = new XmlSelect( 'days', 'days', $selected );
+               $select = new XmlSelect( 'days', 'days', $selectedHours / 24 );
 
-               asort( $days );
-               foreach ( $days as $value ) {
-                       if ( $value < 1 ) {
-                               $name = $this->msg( 'hours' )->numParams( $value * 24 )->text();
+               foreach ( $hours as $value ) {
+                       if ( $value < 24 ) {
+                               $name = $this->msg( 'hours' )->numParams( $value )->text();
                        } else {
-                               $name = $this->msg( 'days' )->numParams( $value )->text();
+                               $name = $this->msg( 'days' )->numParams( $value / 24 )->text();
                        }
-                       $select->addOption( $name, $value );
+                       $select->addOption( $name, $value / 24 );
                }
 
                return $select->getHTML() . "\n<br />\n";