44 parent::__construct();
47 $this->
addOption(
'field',
'The name of a database field to process',
49 $this->
addOption(
'type',
'Which type of invalid actors to find or fix, '
50 .
'missing or broken (with empty actor_name which can\'t be associated '
51 .
'with an existing user).',
53 $this->
addOption(
'skip',
'A comma-separated list of actor IDs to skip.',
55 $this->
addOption(
'overwrite-with',
'Replace invalid actors with this user. '
56 .
'Typically, this would be "Unknown user", but it could be any reserved '
57 .
'system user (per $wgReservedUsernames) or locally registered user. '
58 .
'If not given, invalid actors will only be listed, not fixed. '
59 .
'You will be prompted for confirmation before data is written. ',
68 private function getTables() {
70 'ar_actor' => [
'archive',
'ar_actor',
'ar_id' ],
71 'img_actor' => [
'image',
'img_actor',
'img_name' ],
72 'oi_actor' => [
'oldimage',
'oi_actor',
'oi_archive_name' ],
73 'fa_actor' => [
'filearchive',
'fa_actor',
'fa_id' ],
74 'fr_actor' => [
'filerevision',
'fr_actor',
'fr_id' ],
75 'rc_actor' => [
'recentchanges',
'rc_actor',
'rc_id' ],
76 'log_actor' => [
'logging',
'log_actor',
'log_id' ],
77 'rev_actor' => [
'revision',
'rev_actor',
'rev_id' ],
78 'bl_by_actor' => [
'block',
'bl_by_actor',
'bl_id' ],
86 private function getTableInfo( $field ) {
87 $tables = $this->getTables();
88 return $tables[$field] ??
null;
100 private function getNewActorId() {
101 $name = $this->
getOption(
'overwrite-with' );
103 if ( $name ===
null ) {
107 $user = $this->userFactory->newFromName( $name );
110 $this->
fatalError(
"Not a valid user name: '$name'" );
113 if ( $user->isRegistered() ) {
114 $this->
output(
"Using existing user: '$user'\n" );
115 } elseif ( !$this->userNameUtils->isUsable( $user->getName() ) ) {
116 $this->
output(
"Using system user: '{$user->getName()}'\n" );
118 $this->
fatalError(
"Unknown user: '{$user->getName()}'" );
124 $actorId = $this->actorNormalization->acquireActorId( $user, $dbw );
126 $this->
fatalError(
"Failed to acquire an actor ID for user '$user'" );
129 $this->
output(
"Replacement actor ID is $actorId.\n" );
135 $this->userFactory = $services->getUserFactory();
136 $this->userNameUtils = $services->getUserNameUtils();
137 $this->actorNormalization = $services->getActorNormalization();
140 if ( !$this->getTableInfo( $field ) ) {
141 $this->
fatalError(
"Unknown field: $field.\n" );
144 $type = $this->
getOption(
'type',
'missing' );
145 if ( $type !==
'missing' && $type !==
'broken' ) {
146 $this->
fatalError(
"Unknown type: $type.\n" );
150 $overwrite = $this->getNewActorId();
152 $bad = $this->findBadActors( $field, $type, $skip );
154 if ( $bad && $overwrite ) {
156 $this->
output(
"Do you want to OVERWRITE the listed actor IDs?\n" );
157 $this->
output(
"Information about the invalid IDs will be lost!\n" );
159 $confirm = static::readconsole(
'Type "yes" to continue: ' );
161 if ( $confirm ===
'yes' ) {
162 $this->overwriteActorIDs( $field, array_keys( $bad ), $overwrite );
168 $this->
output(
"Done.\n" );
180 private function findBadActors( $field, $type, $skip ) {
181 [ $table, $actorField, $idField ] = $this->getTableInfo( $field );
182 $this->
output(
"Finding invalid actor IDs in $table.$actorField...\n" );
202 $queryBuilder = $dbr->newSelectQueryBuilder()
203 ->select( [ $actorField, $idField ] )
205 ->leftJoin(
'actor',
null, [
"$actorField = actor_id" ] )
206 ->where( $type ==
'missing' ? [
'actor_id' =>
null ] : [
'actor_name' =>
'' ] )
210 $queryBuilder->andWhere( $dbr->expr( $actorField,
'!=', $skip ) );
213 $res = $queryBuilder->caller( __METHOD__ )->fetchResultSet();
214 $count = $res->numRows();
219 $this->
output(
"\t\tID\tACTOR\n" );
222 foreach ( $res as $row ) {
223 $id = $row->$idField;
224 $actor = (int)( $row->$actorField );
227 $this->
output(
"\t\t$id\t$actor\n" );
230 $this->
output(
"\tFound $count invalid actor IDs.\n" );
233 $this->
output(
"\tBatch size reached, run again after fixing the current batch.\n" );
248 private function overwriteActorIDs( $field, array $ids,
int $overwrite ) {
249 [ $table, $actorField, $idField ] = $this->getTableInfo( $field );
251 $count = count( $ids );
252 $this->
output(
"OVERWRITING $count actor IDs in $table.$actorField with $overwrite...\n" );
256 $dbw->newUpdateQueryBuilder()
258 ->set( [ $actorField => $overwrite ] )
259 ->where( [ $idField => $ids ] )
260 ->caller( __METHOD__ )->execute();
262 $count = $dbw->affectedRows();
265 $this->
output(
"\tUpdated $count rows.\n" );
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.