43 $this->db = $database;
51 private function out( $str ) {
52 call_user_func( $this->outputCallback, $str );
61 $info = $this->db->indexInfo(
'user',
'user_name', __METHOD__ );
63 $this->
out(
"WARNING: doesn't seem to have user_name index at all!\n" );
68 # Confusingly, 'Non_unique' is 0 for *unique* indexes,
69 # and 1 for *non-unique* indexes. Pass the crack, MySQL,
70 # it's obviously some good stuff!
71 return ( $info[0]->Non_unique == 0 );
105 echo
wfWikiID() .
" already has a unique index on its user table.\n";
112 $this->
out(
"Checking for duplicate accounts...\n" );
114 $count = count( $dupes );
116 $this->
out(
"Found $count accounts with duplicate records on " .
wfWikiID() .
".\n" );
118 $this->reassigned = 0;
120 foreach ( $dupes as $name ) {
121 $this->
examine( $name, $doDelete );
128 if ( $this->reassigned > 0 ) {
130 $this->
out(
"$this->reassigned duplicate accounts had edits "
131 .
"reassigned to a canonical record id.\n" );
133 $this->
out(
"$this->reassigned duplicate accounts need to have edits reassigned.\n" );
137 if ( $this->trimmed > 0 ) {
139 $this->
out(
"$this->trimmed duplicate user records were deleted from "
142 $this->
out(
"$this->trimmed duplicate user accounts were found on "
143 .
wfWikiID() .
" which can be removed safely.\n" );
147 if ( $this->failed > 0 ) {
148 $this->
out(
"Something terribly awry; $this->failed duplicate accounts were not removed.\n" );
153 if ( $this->trimmed == 0 || $doDelete ) {
154 $this->
out(
"It is now safe to apply the unique index on user_name.\n" );
158 $this->
out(
"Run this script again with the --fix option to automatically delete them.\n" );
169 $set = [
'user',
'revision' ];
170 $names = array_map( [ $this,
'lockTable' ], $set );
171 $tables = implode(
',', $names );
173 $this->db->query(
"LOCK TABLES $tables", __METHOD__ );
177 return $this->db->tableName( $table ) .
' WRITE';
184 $this->db->query(
"UNLOCK TABLES", __METHOD__ );
193 $user = $this->db->tableName(
'user' );
194 $result = $this->db->query(
195 "SELECT user_name,COUNT(*) AS n
198 HAVING n > 1", __METHOD__ );
201 foreach ( $result as $row ) {
202 $list[] = $row->user_name;
217 $result = $this->db->select(
'user',
219 [
'user_name' => $name ],
222 $firstRow = $this->db->fetchObject( $result );
223 $firstId = $firstRow->user_id;
224 $this->
out(
"Record that will be used for '$name' is user_id=$firstId\n" );
226 foreach ( $result as $row ) {
227 $dupeId = $row->user_id;
228 $this->
out(
"... dupe id $dupeId: " );
232 $this->
out(
"has $edits edits! " );
236 if ( $newEdits == 0 ) {
237 $this->
out(
"confirmed cleaned. " );
240 $this->
out(
"WARNING! $newEdits remaining edits for $dupeId; NOT deleting user.\n" );
244 $this->
out(
"(will need to reassign edits on fix)" );
247 $this->
out(
"ok, no edits. " );
266 return intval( $this->db->selectField(
269 [
'rev_user' => $userid ],
279 $this->
out(
'reassigning... ' );
280 $this->db->update(
'revision',
281 [
'rev_user' => $to ],
282 [
'rev_user' => $from ],
284 $this->
out(
"ok. " );
293 $this->
out(
"deleting..." );
294 $this->db->delete(
'user', [
'user_id' => $userid ], __METHOD__ );
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Look for duplicate user table entries and optionally prune them.
lock()
We don't want anybody to mess with our stuff...
editCount( $userid)
Count the number of edits attributed to this user.
__construct(&$database, $outputCallback)
getDupes()
Grab usernames for which multiple records are present in the database.
examine( $name, $doDelete)
Examine user records for the given name.
trimAccount( $userid)
Remove a user account line.
out( $str)
Output some text via the output callback provided.
checkDupes( $doDelete=false)
Checks the database for duplicate user account records in preparation for application of a unique ind...
clearDupes()
Checks the database for duplicate user account records and remove them in preparation for application...
reassignEdits( $from, $to)
hasUniqueIndex()
Check if this database's user table has already had a unique user_name index applied.
this hook is for auditing only RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables