MediaWiki  master
userOptions.php
Go to the documentation of this file.
1 <?php
30 
31 require_once __DIR__ . '/Maintenance.php';
32 
37 
38  public function __construct() {
39  parent::__construct();
40 
41  $this->addDescription( 'Pass through all users and change one of their options.
42 The new option is NOT validated.' );
43 
44  $this->addOption( 'list', 'List available user options and their default value' );
45  $this->addOption( 'usage', 'Report all options statistics or just one if you specify it' );
46  $this->addOption( 'old', 'The value to look for', false, true );
47  $this->addOption( 'new', 'New value to update users with', false, true );
48  $this->addOption( 'fromuserid', 'Start from this user ID when changing options',
49  false, true );
50  $this->addOption( 'touserid', 'Do not go beyond this user ID when changing options',
51  false, true );
52  $this->addOption( 'nowarn', 'Hides the 5 seconds warning' );
53  $this->addOption( 'dry', 'Do not save user settings back to database' );
54  $this->addArg( 'option name', 'Name of the option to change or provide statistics about', false );
55  $this->setBatchSize( 100 );
56  }
57 
61  public function execute() {
62  if ( $this->hasOption( 'list' ) ) {
63  $this->listAvailableOptions();
64  } elseif ( $this->hasOption( 'usage' ) ) {
65  $this->showUsageStats();
66  } elseif ( $this->hasOption( 'old' )
67  && $this->hasOption( 'new' )
68  && $this->hasArg( 0 )
69  ) {
70  $this->updateOptions();
71  } else {
72  $this->maybeHelp( true );
73  }
74  }
75 
79  private function listAvailableOptions() {
80  $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
81  $def = $userOptionsLookup->getDefaultOptions();
82  ksort( $def );
83  $maxOpt = 0;
84  foreach ( $def as $opt => $value ) {
85  $maxOpt = max( $maxOpt, strlen( $opt ) );
86  }
87  foreach ( $def as $opt => $value ) {
88  $this->output( sprintf( "%-{$maxOpt}s: %s\n", $opt, $value ) );
89  }
90  }
91 
95  private function showUsageStats() {
96  $option = $this->getArg( 0 );
97 
98  $ret = [];
99  $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
100  $defaultOptions = $userOptionsLookup->getDefaultOptions();
101 
102  // We list user by user_id from one of the replica DBs
103  $dbr = wfGetDB( DB_REPLICA );
104  $result = $dbr->select( 'user',
105  [ 'user_id' ],
106  [],
107  __METHOD__
108  );
109 
110  foreach ( $result as $id ) {
111  $user = User::newFromId( $id->user_id );
112 
113  // Get the options and update stats
114  if ( $option ) {
115  if ( !array_key_exists( $option, $defaultOptions ) ) {
116  $this->fatalError( "Invalid user option. Use --list to see valid choices\n" );
117  }
118 
119  $userValue = $user->getOption( $option );
120  if ( $userValue <> $defaultOptions[$option] ) {
121  $ret[$option][$userValue] = ( $ret[$option][$userValue] ?? 0 ) + 1;
122  }
123  } else {
124  foreach ( $defaultOptions as $name => $defaultValue ) {
125  $userValue = $user->getOption( $name );
126  if ( $userValue != $defaultValue ) {
127  $ret[$name][$userValue] = ( $ret[$name][$userValue] ?? 0 ) + 1;
128  }
129  }
130  }
131  }
132 
133  foreach ( $ret as $optionName => $usageStats ) {
134  $this->output( "Usage for <$optionName> (default: '{$defaultOptions[$optionName]}'):\n" );
135  foreach ( $usageStats as $value => $count ) {
136  $this->output( " $count user(s): '$value'\n" );
137  }
138  print "\n";
139  }
140  }
141 
145  private function updateOptions() {
146  $dryRun = $this->hasOption( 'dry' );
147  $settingWord = $dryRun ? 'Would set' : 'Setting';
148  $option = $this->getArg( 0 );
149  $from = $this->getOption( 'old' );
150  $to = $this->getOption( 'new' );
151 
152  if ( !$dryRun ) {
153  $this->warn( $option, $from, $to );
154  }
155 
156  $userOptionsManager = MediaWikiServices::getInstance()->getUserOptionsManager();
157  $dbr = wfGetDB( DB_REPLICA );
158  // The fromuserid parameter is inclusive, but iterating is easier with an exclusive
159  // range so convert it.
160  $fromUserId = (int)$this->getOption( 'fromuserid', 1 ) - 1;
161  $toUserId = (int)$this->getOption( 'touserid', 0 ) ?: null;
162  $queryBuilderTemplate = new SelectQueryBuilder( $dbr );
163  $queryBuilderTemplate
164  ->table( 'user' )
165  ->join( 'user_properties', null, [
166  'user_id = up_user',
167  'up_property' => $option,
168  ] )
169  ->fields( [ 'user_id', 'user_name' ] )
170  // up_value is unindexed so this can be slow, but should be acceptable in a script
171  ->where( [ 'up_value' => $from ] )
172  // need to order by ID so we can use ID ranges for query continuation
173  // also needed for the fromuserid / touserid parameters to work
174  ->orderBy( 'user_id', SelectQueryBuilder::SORT_ASC )
175  ->limit( $this->getBatchSize() )
176  ->caller( __METHOD__ );
177  if ( $toUserId ) {
178  $queryBuilderTemplate->andWhere( "user_id <= $toUserId " );
179  }
180 
181  do {
182  $queryBuilder = clone $queryBuilderTemplate;
183  $queryBuilder->andWhere( "user_id > $fromUserId" );
184  $result = $queryBuilder->fetchResultSet();
185  foreach ( $result as $row ) {
186  $this->output( "$settingWord {$option} for {$row->user_name} from '{$from}' to '{$to}'\n" );
187  $user = UserIdentityValue::newRegistered( $row->user_id, $row->user_name );
188  if ( !$dryRun ) {
189  $userOptionsManager->setOption( $user, $option, $to );
190  $userOptionsManager->saveOptions( $user );
191  }
192  $fromUserId = (int)$row->user_id;
193  }
194  $this->waitForReplication();
195  } while ( $result->numRows() );
196  }
197 
205  private function warn( $option, $from, $to ) {
206  if ( $this->hasOption( 'nowarn' ) ) {
207  return;
208  }
209 
210  $this->output( <<<WARN
211 The script is about to change the options for ALL USERS in the database.
212 Users with option <$option> = '$from' will be made to use '$to'.
213 
214 Abort with control-c in the next five seconds....
215 WARN
216  );
217  $this->countDown( 5 );
218  }
219 }
220 
221 $maintClass = UserOptionsMaintenance::class;
222 require_once RUN_MAINTENANCE_IF_MAIN;
MediaWiki\User\UserIdentityValue
Value object representing a user's identity.
Definition: UserIdentityValue.php:35
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:647
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:191
Maintenance\maybeHelp
maybeHelp( $force=false)
Maybe show the help.
Definition: Maintenance.php:1009
Maintenance\fatalError
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
Definition: Maintenance.php:489
Maintenance\addDescription
addDescription( $text)
Set the description text.
Definition: Maintenance.php:329
UserOptionsMaintenance\__construct
__construct()
Default constructor.
Definition: userOptions.php:38
Maintenance\hasArg
hasArg( $argId=0)
Does a given argument exist?
Definition: Maintenance.php:338
Maintenance
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:59
$dbr
$dbr
Definition: testCompression.php:54
$maintClass
$maintClass
Definition: userOptions.php:221
UserOptionsMaintenance
Definition: userOptions.php:36
UserOptionsMaintenance\execute
execute()
Do the actual work.
Definition: userOptions.php:61
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2225
Maintenance\addOption
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
Definition: Maintenance.php:249
Wikimedia\Rdbms\SelectQueryBuilder
Definition: SelectQueryBuilder.php:11
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Maintenance\countDown
countDown( $seconds)
Count down from $seconds to zero on the terminal, with a one-second pause between showing each number...
Definition: Maintenance.php:1452
Maintenance\waitForReplication
waitForReplication()
Wait for replica DBs to catch up.
Definition: Maintenance.php:1419
UserOptionsMaintenance\showUsageStats
showUsageStats()
List options usage.
Definition: userOptions.php:95
UserOptionsMaintenance\updateOptions
updateOptions()
Change our users options.
Definition: userOptions.php:145
UserOptionsMaintenance\listAvailableOptions
listAvailableOptions()
List default options and their value.
Definition: userOptions.php:79
$userOptionsLookup
UserOptionsLookup $userOptionsLookup
Definition: ApiWatchlistTrait.php:33
Maintenance\getOption
getOption( $name, $default=null)
Get an option, or return the default.
Definition: Maintenance.php:286
Maintenance\addArg
addArg( $arg, $description, $required=true)
Add some args that are needed.
Definition: Maintenance.php:300
Maintenance\getBatchSize
getBatchSize()
Returns batch size.
Definition: Maintenance.php:368
Maintenance\output
output( $out, $channel=null)
Throw some output to the user.
Definition: Maintenance.php:435
Maintenance\hasOption
hasOption( $name)
Checks to see if a particular option was set.
Definition: Maintenance.php:271
Maintenance\getArg
getArg( $argId=0, $default=null)
Get an argument.
Definition: Maintenance.php:353
UserOptionsMaintenance\warn
warn( $option, $from, $to)
The warning message and countdown.
Definition: userOptions.php:205
Maintenance\setBatchSize
setBatchSize( $s=0)
Definition: Maintenance.php:375