]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - maintenance/rebuildrecentchanges.php
MediaWiki 1.17.1
[autoinstalls/mediawiki.git] / maintenance / rebuildrecentchanges.php
1 <?php
2 /**
3  * Rebuild link tracking tables from scratch.  This takes several
4  * hours, depending on the database size and server configuration.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  * http://www.gnu.org/copyleft/gpl.html
20  *
21  * @ingroup Maintenance
22  * @todo Document
23  */
24
25 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
26
27 class RebuildRecentchanges extends Maintenance {
28         public function __construct() {
29                 parent::__construct();
30                 $this->mDescription = "Rebuild recent changes";
31         }
32
33         public function execute() {
34                 $this->rebuildRecentChangesTablePass1();
35                 $this->rebuildRecentChangesTablePass2();
36                 $this->rebuildRecentChangesTablePass3();
37                 $this->rebuildRecentChangesTablePass4();
38                 $this->purgeFeeds();
39                 $this->output( "Done.\n" );
40         }
41
42         /**
43          * Rebuild pass 1
44          * DOCUMENT ME!
45          */
46         private function rebuildRecentChangesTablePass1() {
47                 $dbw = wfGetDB( DB_MASTER );
48
49                 $dbw->delete( 'recentchanges', '*' );
50
51                 $this->output( "Loading from page and revision tables...\n" );
52
53                 global $wgRCMaxAge;
54
55                 $this->output( '$wgRCMaxAge=' . $wgRCMaxAge );
56                 $days = $wgRCMaxAge / 24 / 3600;
57                 if ( intval( $days ) == $days ) {
58                                 $this->output( " (" . $days . " days)\n" );
59                 } else {
60                                 $this->output( " (approx. " .  intval( $days ) . " days)\n" );
61                 }
62
63                 $cutoff = time() - $wgRCMaxAge;
64                 $dbw->insertSelect( 'recentchanges', array( 'page', 'revision' ),
65                         array(
66                                 'rc_timestamp'  => 'rev_timestamp',
67                                 'rc_cur_time'   => 'rev_timestamp',
68                                 'rc_user'       => 'rev_user',
69                                 'rc_user_text'  => 'rev_user_text',
70                                 'rc_namespace'  => 'page_namespace',
71                                 'rc_title'      => 'page_title',
72                                 'rc_comment'    => 'rev_comment',
73                                 'rc_minor'      => 'rev_minor_edit',
74                                 'rc_bot'        => 0,
75                                 'rc_new'        => 'page_is_new',
76                                 'rc_cur_id'     => 'page_id',
77                                 'rc_this_oldid' => 'rev_id',
78                                 'rc_last_oldid' => 0, // is this ok?
79                                 'rc_type'       => $dbw->conditional( 'page_is_new != 0', RC_NEW, RC_EDIT ),
80                                 'rc_deleted'    => 'rev_deleted'
81                         ), array(
82                                 'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $cutoff ) ),
83                                 'rev_page=page_id'
84                         ), __METHOD__,
85                         array(), // INSERT options
86                         array( 'ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 5000 ) // SELECT options
87                 );
88         }
89
90         /**
91          * Rebuild pass 2
92          * DOCUMENT ME!
93          */
94         private function rebuildRecentChangesTablePass2() {
95                 $dbw = wfGetDB( DB_MASTER );
96                 list ( $recentchanges, $revision ) = $dbw->tableNamesN( 'recentchanges', 'revision' );
97
98                 $this->output( "Updating links and size differences...\n" );
99
100                 # Fill in the rc_last_oldid field, which points to the previous edit
101                 $sql = "SELECT rc_cur_id,rc_this_oldid,rc_timestamp FROM $recentchanges " .
102                   "ORDER BY rc_cur_id,rc_timestamp";
103                 $res = $dbw->query( $sql, DB_MASTER );
104
105                 $lastCurId = 0;
106                 $lastOldId = 0;
107                 foreach ( $res as $obj ) {
108                         $new = 0;
109                         if ( $obj->rc_cur_id != $lastCurId ) {
110                                 # Switch! Look up the previous last edit, if any
111                                 $lastCurId = intval( $obj->rc_cur_id );
112                                 $emit = $obj->rc_timestamp;
113                                 $sql2 = "SELECT rev_id,rev_len FROM $revision " .
114                                         "WHERE rev_page={$lastCurId} " .
115                                         "AND rev_timestamp<'{$emit}' ORDER BY rev_timestamp DESC";
116                                 $sql2 = $dbw->limitResult( $sql2, 1, false );
117                                 $res2 = $dbw->query( $sql2 );
118                                 $row = $dbw->fetchObject( $res2 );
119                                 if ( $row ) {
120                                         $lastOldId = intval( $row->rev_id );
121                                         # Grab the last text size if available
122                                         $lastSize = !is_null( $row->rev_len ) ? intval( $row->rev_len ) : 'NULL';
123                                 } else {
124                                         # No previous edit
125                                         $lastOldId = 0;
126                                         $lastSize = 'NULL';
127                                         $new = 1; // probably true
128                                 }
129                         }
130                         if ( $lastCurId == 0 ) {
131                                 $this->output( "Uhhh, something wrong? No curid\n" );
132                         } else {
133                                 # Grab the entry's text size
134                                 $size = $dbw->selectField( 'revision', 'rev_len', array( 'rev_id' => $obj->rc_this_oldid ) );
135
136                                 $dbw->update( 'recentchanges',
137                                         array(
138                                                 'rc_last_oldid' => $lastOldId,
139                                                 'rc_new'        => $new,
140                                                 'rc_type'       => $new,
141                                                 'rc_old_len'    => $lastSize,
142                                                 'rc_new_len'    => $size,
143                                         ), array(
144                                                 'rc_cur_id'     => $lastCurId,
145                                                 'rc_this_oldid' => $obj->rc_this_oldid,
146                                         ),
147                                         __METHOD__
148                                 );
149
150                                 $lastOldId = intval( $obj->rc_this_oldid );
151                                 $lastSize = $size;
152                         }
153                 }
154         }
155
156         /**
157          * Rebuild pass 3
158          * DOCUMENT ME!
159          */
160         private function rebuildRecentChangesTablePass3() {
161                 $dbw = wfGetDB( DB_MASTER );
162
163                 $this->output( "Loading from user, page, and logging tables...\n" );
164
165                 global $wgRCMaxAge, $wgLogTypes, $wgLogRestrictions;
166                 // Some logs don't go in RC. This should check for that
167                 $basicRCLogs = array_diff( $wgLogTypes, array_keys( $wgLogRestrictions ) );
168
169                 // Escape...blah blah
170                 $selectLogs = array();
171                 foreach ( $basicRCLogs as $logtype ) {
172                         $safetype = $dbw->strencode( $logtype );
173                         $selectLogs[] = "'$safetype'";
174                 }
175
176                 $cutoff = time() - $wgRCMaxAge;
177                 list( $logging, $page ) = $dbw->tableNamesN( 'logging', 'page' );
178                 $dbw->insertSelect( 'recentchanges', array( 'user', "$logging LEFT JOIN $page ON (log_namespace=page_namespace AND log_title=page_title)" ),
179                         array(
180                                 'rc_timestamp'  => 'log_timestamp',
181                                 'rc_cur_time'   => 'log_timestamp',
182                                 'rc_user'       => 'log_user',
183                                 'rc_user_text'  => 'user_name',
184                                 'rc_namespace'  => 'log_namespace',
185                                 'rc_title'      => 'log_title',
186                                 'rc_comment'    => 'log_comment',
187                                 'rc_minor'      => 0,
188                                 'rc_bot'        => 0,
189                                 'rc_patrolled'  => 1,
190                                 'rc_new'        => 0,
191                                 'rc_this_oldid' => 0,
192                                 'rc_last_oldid' => 0,
193                                 'rc_type'       => RC_LOG,
194                                 'rc_cur_id'     => $dbw->cascadingDeletes() ? 'page_id' : 'COALESCE(page_id, 0)',
195                                 'rc_log_type'   => 'log_type',
196                                 'rc_log_action' => 'log_action',
197                                 'rc_logid'      => 'log_id',
198                                 'rc_params'     => 'log_params',
199                                 'rc_deleted'    => 'log_deleted'
200                         ), array(
201                                 'log_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $cutoff ) ),
202                                 'log_user=user_id',
203                                 'log_type IN(' . implode( ',', $selectLogs ) . ')'
204                         ), __METHOD__,
205                         array(), // INSERT options
206                         array( 'ORDER BY' => 'log_timestamp DESC', 'LIMIT' => 5000 ) // SELECT options
207                 );
208         }
209
210         /**
211          * Rebuild pass 4
212          * DOCUMENT ME!
213          */
214         private function rebuildRecentChangesTablePass4() {
215                 global $wgGroupPermissions, $wgUseRCPatrol;
216
217                 $dbw = wfGetDB( DB_MASTER );
218
219                 list( $recentchanges, $usergroups, $user ) = $dbw->tableNamesN( 'recentchanges', 'user_groups', 'user' );
220
221                 $botgroups = $autopatrolgroups = array();
222                 foreach ( $wgGroupPermissions as $group => $rights ) {
223                         if ( isset( $rights['bot'] ) && $rights['bot'] ) {
224                                 $botgroups[] = $dbw->addQuotes( $group );
225                         }
226                         if ( $wgUseRCPatrol && isset( $rights['autopatrol'] ) && $rights['autopatrol'] ) {
227                                 $autopatrolgroups[] = $dbw->addQuotes( $group );
228                         }
229                 }
230                 # Flag our recent bot edits
231                 if ( !empty( $botgroups ) ) {
232                         $botwhere = implode( ',', $botgroups );
233                         $botusers = array();
234
235                         $this->output( "Flagging bot account edits...\n" );
236
237                         # Find all users that are bots
238                         $sql = "SELECT DISTINCT user_name FROM $usergroups, $user " .
239                                 "WHERE ug_group IN($botwhere) AND user_id = ug_user";
240                         $res = $dbw->query( $sql, DB_MASTER );
241
242                         foreach ( $res as $obj ) {
243                                 $botusers[] = $dbw->addQuotes( $obj->user_name );
244                         }
245                         # Fill in the rc_bot field
246                         if ( !empty( $botusers ) ) {
247                                 $botwhere = implode( ',', $botusers );
248                                 $sql2 = "UPDATE $recentchanges SET rc_bot=1 " .
249                                         "WHERE rc_user_text IN($botwhere)";
250                                 $dbw->query( $sql2 );
251                         }
252                 }
253                 global $wgMiserMode;
254                 # Flag our recent autopatrolled edits
255                 if ( !$wgMiserMode && !empty( $autopatrolgroups ) ) {
256                         $patrolwhere = implode( ',', $autopatrolgroups );
257                         $patrolusers = array();
258
259                         $this->output( "Flagging auto-patrolled edits...\n" );
260
261                         # Find all users in RC with autopatrol rights
262                         $sql = "SELECT DISTINCT user_name FROM $usergroups, $user " .
263                                 "WHERE ug_group IN($patrolwhere) AND user_id = ug_user";
264                         $res = $dbw->query( $sql, DB_MASTER );
265
266                         foreach ( $res as $obj ) {
267                                 $patrolusers[] = $dbw->addQuotes( $obj->user_name );
268                         }
269
270                         # Fill in the rc_patrolled field
271                         if ( !empty( $patrolusers ) ) {
272                                 $patrolwhere = implode( ',', $patrolusers );
273                                 $sql2 = "UPDATE $recentchanges SET rc_patrolled=1 " .
274                                         "WHERE rc_user_text IN($patrolwhere)";
275                                 $dbw->query( $sql2 );
276                         }
277                 }
278         }
279
280         /**
281          * Purge cached feeds in $messageMemc
282          */
283         private function purgeFeeds() {
284                 global $wgFeedClasses, $messageMemc;
285
286                 $this->output( "Deleting feed timestamps.\n" );
287
288                 foreach ( $wgFeedClasses as $feed => $className ) {
289                         $messageMemc->delete( wfMemcKey( 'rcfeed', $feed, 'timestamp' ) ); # Good enough for now.
290                 }
291         }
292
293 }
294
295 $maintClass = "RebuildRecentchanges";
296 require_once( RUN_MAINTENANCE_IF_MAIN );