Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
MigratePreferences
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 4
132
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 doDBUpdates
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
30
 updateUser
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 getUpdateKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace LoginNotify\Maintenance;
4
5use BatchRowIterator;
6use LoggedUpdateMaintenance;
7use MediaWiki\MediaWikiServices;
8use MediaWiki\User\User;
9use MediaWiki\WikiMap\WikiMap;
10use RecursiveIteratorIterator;
11
12$IP = getenv( 'MW_INSTALL_PATH' );
13if ( $IP === false ) {
14    $IP = __DIR__ . '/../../..';
15}
16
17require_once "$IP/maintenance/Maintenance.php";
18
19/**
20 * Cleans up old preference values
21 */
22class MigratePreferences extends LoggedUpdateMaintenance {
23
24    // Previously, these constants were used by Hooks to force different per-user defaults
25    private const OPTIONS_FAKE_TRUTH = 2;
26    private const OPTIONS_FAKE_FALSE = 'fake-false';
27
28    /** @var bool[] */
29    private static $mapping = [
30        self::OPTIONS_FAKE_FALSE => false,
31        self::OPTIONS_FAKE_TRUTH => true,
32    ];
33
34    public function __construct() {
35        parent::__construct();
36        $this->addDescription( 'Cleans up old-style preferences used by LoginNotify' );
37        $this->setBatchSize( 500 );
38    }
39
40    /**
41     * Do the actual work. All child classes will need to implement this.
42     * Return true to log the update as done or false (usually on failure).
43     * @return bool
44     */
45    protected function doDBUpdates() {
46        $dbr = $this->getDB( DB_REPLICA, 'vslow' );
47        $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
48
49        $iterator = new BatchRowIterator( $dbr,
50            [ 'user_properties', 'user' ],
51            [ 'up_user', 'up_property' ],
52            $this->getBatchSize()
53        );
54        $iterator->addConditions( [
55            'user_id=up_user',
56            'up_property' => [
57                'echo-subscriptions-web-login-fail',
58                'echo-subscriptions-web-login-success',
59                'echo-subscriptions-email-login-fail',
60                'echo-subscriptions-email-login-success',
61            ],
62            'up_value' => [
63                self::OPTIONS_FAKE_TRUTH,
64                self::OPTIONS_FAKE_FALSE,
65            ],
66        ] );
67        $iterator->setFetchColumns( [ '*' ] );
68        $iterator->setCaller( __METHOD__ );
69
70        $lastRow = (object)[ 'user_id' => 0 ];
71        $optionsToUpdate = [];
72        $rows = 0;
73        $total = 0;
74        $iterator = new RecursiveIteratorIterator( $iterator );
75        foreach ( $iterator as $row ) {
76            $userId = $row->user_id;
77            $option = $row->up_property;
78            $value = $row->up_value;
79
80            if ( $userId != $lastRow->user_id ) {
81                $rows += $this->updateUser( $lastRow, $optionsToUpdate );
82                if ( $rows >= $this->getBatchSize() ) {
83                    $this->output( "  Updated {$rows} rows up to user ID {$lastRow->user_id}\n" );
84                    $lbFactory->waitForReplication( [ 'wiki' => WikiMap::getCurrentWikiId() ] );
85                    $total += $rows;
86                    $rows = 0;
87                }
88            }
89            if ( isset( self::$mapping[ $value ] ) ) {
90                $optionsToUpdate[$option] = self::$mapping[ $value ];
91            }
92            $lastRow = $row;
93        }
94
95        $total += $this->updateUser( $lastRow, $optionsToUpdate );
96
97        $this->output( "{$total} rows updated.\n" );
98
99        return true;
100    }
101
102    /**
103     * Update one user's preferences
104     *
105     * @param \stdClass $userRow Row from the user table
106     * @param array &$options Associative array of preference => value
107     * @return int Number of options updated
108     */
109    private function updateUser( $userRow, array &$options ) {
110        if ( $userRow->user_id && $options ) {
111            $user = User::newFromRow( $userRow );
112            $userOptionsManager = MediaWikiServices::getInstance()->getUserOptionsManager();
113            foreach ( $options as $option => $value ) {
114                $userOptionsManager->setOption( $user, $option, $value );
115            }
116            $userOptionsManager->saveOptions( $user );
117        }
118        $count = count( $options );
119        $options = [];
120        return $count;
121    }
122
123    /**
124     * Get the update key name to go in the update log table
125     * @return string
126     */
127    protected function getUpdateKey() {
128        return 'LoginNotify::migratePreferences';
129    }
130}
131
132$maintClass = MigratePreferences::class;
133require_once RUN_MAINTENANCE_IF_MAIN;