Merge "mediawiki.widgets: Remove use of bind() for lexical 'this' binding"
[lhc/web/wiklou.git] / maintenance / populateContentModel.php
1 <?php
2 /**
3 * Populate the page_content_model and {rev,ar}_content_{model,format} fields.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Maintenance
22 */
23
24 require_once __DIR__ . '/Maintenance.php';
25
26 /**
27 * Usage:
28 * populateContentModel.php --ns=1 --table=page
29 */
30 class PopulateContentModel extends Maintenance {
31 public function __construct() {
32 parent::__construct();
33 $this->addDescription( 'Populate the various content_* fields' );
34 $this->addOption( 'ns', 'Namespace to run in, or "all" for all namespaces', true, true );
35 $this->addOption( 'table', 'Table to run in', true, true );
36 $this->setBatchSize( 100 );
37 }
38
39 public function execute() {
40 $dbw = $this->getDB( DB_MASTER );
41 $ns = $this->getOption( 'ns' );
42 if ( !ctype_digit( $ns ) && $ns !== 'all' ) {
43 $this->error( 'Invalid namespace', 1 );
44 }
45 $ns = $ns === 'all' ? 'all' : (int)$ns;
46 $table = $this->getOption( 'table' );
47 switch ( $table ) {
48 case 'revision':
49 case 'archive':
50 $this->populateRevisionOrArchive( $dbw, $table, $ns );
51 break;
52 case 'page':
53 $this->populatePage( $dbw, $ns );
54 break;
55 default:
56 $this->error( "Invalid table name: $table", 1 );
57 }
58 }
59
60 private function updatePageRows( DatabaseBase $dbw, $pageIds, $model ) {
61 $count = count( $pageIds );
62 $this->output( "Setting $count rows to $model..." );
63 $dbw->update(
64 'page',
65 [ 'page_content_model' => $model ],
66 [ 'page_id' => $pageIds ],
67 __METHOD__
68 );
69 wfWaitForSlaves();
70 $this->output( "done.\n" );
71 }
72
73 protected function populatePage( DatabaseBase $dbw, $ns ) {
74 $toSave = [];
75 $lastId = 0;
76 $nsCondition = $ns === 'all' ? [] : [ 'page_namespace' => $ns ];
77 do {
78 $rows = $dbw->select(
79 'page',
80 [ 'page_namespace', 'page_title', 'page_id' ],
81 [
82 'page_content_model' => null,
83 'page_id > ' . $dbw->addQuotes( $lastId ),
84 ] + $nsCondition,
85 __METHOD__,
86 [ 'LIMIT' => $this->mBatchSize, 'ORDER BY' => 'page_id ASC' ]
87 );
88 $this->output( "Fetched {$rows->numRows()} rows.\n" );
89 foreach ( $rows as $row ) {
90 $title = Title::newFromRow( $row );
91 $model = ContentHandler::getDefaultModelFor( $title );
92 $toSave[$model][] = $row->page_id;
93 if ( count( $toSave[$model] ) >= $this->mBatchSize ) {
94 $this->updatePageRows( $dbw, $toSave[$model], $model );
95 unset( $toSave[$model] );
96 }
97 $lastId = $row->page_id;
98 }
99 } while ( $rows->numRows() >= $this->mBatchSize );
100 foreach ( $toSave as $model => $pages ) {
101 $this->updatePageRows( $dbw, $pages, $model );
102 }
103 }
104
105 private function updateRevisionOrArchiveRows( DatabaseBase $dbw, $ids, $model, $table ) {
106 $prefix = $table === 'archive' ? 'ar' : 'rev';
107 $model_column = "{$prefix}_content_model";
108 $format_column = "{$prefix}_content_format";
109 $key = "{$prefix}_id";
110
111 $count = count( $ids );
112 $format = ContentHandler::getForModelID( $model )->getDefaultFormat();
113 $this->output( "Setting $count rows to $model / $format..." );
114 $dbw->update(
115 $table,
116 [ $model_column => $model, $format_column => $format ],
117 [ $key => $ids ],
118 __METHOD__
119 );
120 $this->output( "done.\n" );
121 }
122
123 protected function populateRevisionOrArchive( DatabaseBase $dbw, $table, $ns ) {
124 $prefix = $table === 'archive' ? 'ar' : 'rev';
125 $model_column = "{$prefix}_content_model";
126 $format_column = "{$prefix}_content_format";
127 $key = "{$prefix}_id";
128 if ( $table === 'archive' ) {
129 $selectTables = 'archive';
130 $fields = [ 'ar_namespace', 'ar_title' ];
131 $join_conds = [];
132 $where = $ns === 'all' ? [] : [ 'ar_namespace' => $ns ];
133 } else { // revision
134 $selectTables = [ 'revision', 'page' ];
135 $fields = [ 'page_title', 'page_namespace' ];
136 $join_conds = [ 'page' => [ 'INNER JOIN', 'rev_page=page_id' ] ];
137 $where = $ns === 'all' ? [] : [ 'page_namespace' => $ns ];
138 }
139
140 $toSave = [];
141 $lastId = 0;
142 do {
143 $rows = $dbw->select(
144 $selectTables,
145 array_merge( $fields, [ $model_column, $format_column, $key ] ),
146 // @todo support populating format if model is already set
147 [
148 $model_column => null,
149 "$key > " . $dbw->addQuotes( $lastId ),
150 ] + $where,
151 __METHOD__,
152 [ 'LIMIT' => $this->mBatchSize, 'ORDER BY' => "$key ASC" ],
153 $join_conds
154 );
155 $this->output( "Fetched {$rows->numRows()} rows.\n" );
156 foreach ( $rows as $row ) {
157 if ( $table === 'archive' ) {
158 $title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
159 } else {
160 $title = Title::newFromRow( $row );
161 }
162 $lastId = $row->{$key};
163 try {
164 $handler = ContentHandler::getForTitle( $title );
165 } catch ( MWException $e ) {
166 $this->error( "Invalid content model for $title" );
167 continue;
168 }
169 $defaultModel = $handler->getModelID();
170 $defaultFormat = $handler->getDefaultFormat();
171 $dbModel = $row->{$model_column};
172 $dbFormat = $row->{$format_column};
173 $id = $row->{$key};
174 if ( $dbModel === null && $dbFormat === null ) {
175 // Set the defaults
176 $toSave[$defaultModel][] = $row->{$key};
177 } else { // $dbModel === null, $dbFormat set.
178 if ( $dbFormat === $defaultFormat ) {
179 $toSave[$defaultModel][] = $row->{$key};
180 } else { // non-default format, just update now
181 $this->output( "Updating model to match format for $table $id of $title... " );
182 $dbw->update(
183 $table,
184 [ $model_column => $defaultModel ],
185 [ $key => $id ],
186 __METHOD__
187 );
188 wfWaitForSlaves();
189 $this->output( "done.\n" );
190 continue;
191 }
192 }
193
194 if ( count( $toSave[$defaultModel] ) >= $this->mBatchSize ) {
195 $this->updateRevisionOrArchiveRows( $dbw, $toSave[$defaultModel], $defaultModel, $table );
196 unset( $toSave[$defaultModel] );
197 }
198 }
199 } while ( $rows->numRows() >= $this->mBatchSize );
200 foreach ( $toSave as $model => $ids ) {
201 $this->updateRevisionOrArchiveRows( $dbw, $ids, $model, $table );
202 }
203 }
204 }
205
206 $maintClass = 'PopulateContentModel';
207 require_once RUN_MAINTENANCE_IF_MAIN;