Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 125 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
MigrateCampaigns | |
0.00% |
0 / 119 |
|
0.00% |
0 / 7 |
650 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getConfigFromDB | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
42 | |||
explodeStringToArray | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
trimArray | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
30 | |||
ensureDefaultLicense | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
30 | |||
getConfigForJSON | |
0.00% |
0 / 50 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | /** |
3 | * Fixes timestamp corruption caused by one or more webservers temporarily |
4 | * being set to the wrong time. |
5 | * The time offset must be known and consistent. Start and end times |
6 | * (in 14-character format) restrict the search, and must bracket the damage. |
7 | * There must be a majority of good timestamps in the search period. |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; either version 2 of the License, or |
12 | * (at your option) any later version. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License along |
20 | * with this program; if not, write to the Free Software Foundation, Inc., |
21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
22 | * http://www.gnu.org/copyleft/gpl.html |
23 | * |
24 | * @file |
25 | * @ingroup Maintenance |
26 | * @author Yuvi Panda <yuvipanda@gmail.com> |
27 | */ |
28 | |
29 | $IP = getenv( 'MW_INSTALL_PATH' ); |
30 | if ( $IP === false ) { |
31 | $IP = __DIR__ . '/../../..'; |
32 | } |
33 | require_once "$IP/maintenance/Maintenance.php"; |
34 | |
35 | use MediaWiki\Extension\UploadWizard\CampaignContent; |
36 | use MediaWiki\Title\Title; |
37 | |
38 | /** |
39 | * Maintenance script to migrate campaigns from older, database table |
40 | * to newer page based storage |
41 | * |
42 | * @ingroup Maintenance |
43 | */ |
44 | class MigrateCampaigns extends Maintenance { |
45 | |
46 | /** |
47 | * @var \Wikimedia\Rdbms\IDatabase |
48 | */ |
49 | private $dbr = null; |
50 | |
51 | public function __construct() { |
52 | parent::__construct(); |
53 | |
54 | $this->requireExtension( 'Upload Wizard' ); |
55 | $this->addDescription( "Migrate UploadCampaigns from database storage to pages" ); |
56 | $this->addOption( 'user', 'The user to perform the migration as', false, true, 'u' ); |
57 | } |
58 | |
59 | private const OLD_KEY_DEFAULTS = [ |
60 | 'headerLabelPage' => '', |
61 | 'thanksLabelPage' => '', |
62 | |
63 | 'defaultLicenseType' => 'choice', |
64 | 'ownWorkOption' => 'choice', |
65 | 'licensesOwnWork' => '', |
66 | 'defaultOwnWorkLicense' => '', |
67 | |
68 | 'skipTutorial' => false, |
69 | 'tutorialTemplate' => 'Licensing_tutorial_$1.svg', |
70 | 'tutorialWidth' => 720, |
71 | 'tutorialHelpdeskCoords' => '27, 1319, 691, 1384', |
72 | |
73 | 'autoWikiText' => '', |
74 | 'autoCategories' => '', |
75 | |
76 | 'defaultDescription' => '', |
77 | 'defaultLat' => '', |
78 | 'defaultLon' => '', |
79 | 'defaultAlt' => '', |
80 | 'defaultCategories' => '', |
81 | |
82 | 'idField' => '', |
83 | 'idFieldLabel' => '', |
84 | 'idFieldLabelPage' => '', |
85 | 'idFieldMaxLength' => 25, |
86 | 'idFieldInitialValue' => '', |
87 | |
88 | 'idField2' => '', |
89 | 'idField2Label' => '', |
90 | 'idField2LabelPage' => '', |
91 | 'idField2MaxLength' => 25, |
92 | 'idField2InitialValue' => '' |
93 | ]; |
94 | |
95 | private const OLD_NUMBER_CONFIGS = [ |
96 | 'idFieldMaxLength', |
97 | 'idField2MaxLength', |
98 | 'tutorialWidth', |
99 | 'defaultLat', |
100 | 'defaultLon', |
101 | 'defaultAlt' |
102 | ]; |
103 | |
104 | /** |
105 | * @param int|string $id |
106 | * @return array |
107 | */ |
108 | private function getConfigFromDB( $id ) { |
109 | $config = []; |
110 | |
111 | $confProps = $this->dbr->newSelectQueryBuilder() |
112 | ->select( [ 'cc_property', 'cc_value' ] ) |
113 | ->from( 'uw_campaign_conf' ) |
114 | ->where( [ 'cc_campaign_id' => $id ] ) |
115 | ->caller( __METHOD__ ) |
116 | ->fetchResultSet(); |
117 | |
118 | foreach ( $confProps as $confProp ) { |
119 | if ( in_array( $confProp->cc_property, self::OLD_NUMBER_CONFIGS ) ) { |
120 | $config[$confProp->cc_property] = intval( $confProp->cc_value ); |
121 | } else { |
122 | $config[$confProp->cc_property] = $confProp->cc_value; |
123 | } |
124 | } |
125 | |
126 | $mergedConfig = []; |
127 | |
128 | foreach ( self::OLD_KEY_DEFAULTS as $key => $default ) { |
129 | if ( array_key_exists( $key, $config ) && $config[$key] !== $default ) { |
130 | $mergedConfig[$key] = $config[$key]; |
131 | } else { |
132 | $mergedConfig[$key] = null; |
133 | } |
134 | } |
135 | |
136 | return $mergedConfig; |
137 | } |
138 | |
139 | /** |
140 | * @param string $string |
141 | * @return string[] |
142 | */ |
143 | private function explodeStringToArray( $string ) { |
144 | $parts = explode( '|', $string ); |
145 | $array = []; |
146 | |
147 | foreach ( $parts as $part ) { |
148 | $part = trim( $part ); |
149 | |
150 | if ( $part !== '' ) { |
151 | $array[] = $part; |
152 | } |
153 | } |
154 | return $array; |
155 | } |
156 | |
157 | /** |
158 | * @param array $array |
159 | * @return array |
160 | */ |
161 | private function trimArray( $array ) { |
162 | $newArray = []; |
163 | foreach ( $array as $key => $value ) { |
164 | if ( is_array( $value ) ) { |
165 | $trimmedValue = $this->trimArray( $value ); |
166 | if ( $trimmedValue !== [] ) { |
167 | $newArray[$key] = $trimmedValue; |
168 | } |
169 | } elseif ( $value !== null ) { |
170 | $newArray[$key] = $value; |
171 | } |
172 | } |
173 | return $newArray; |
174 | } |
175 | |
176 | /** |
177 | * Ensure that the default license, if set, is the first |
178 | * |
179 | * @param array $licenses |
180 | * @param string $default |
181 | * @return array |
182 | */ |
183 | private function ensureDefaultLicense( $licenses, $default ) { |
184 | if ( count( $licenses ) === 1 || ( $default === null || trim( $default ) === '' ) ) { |
185 | return $licenses; |
186 | } |
187 | if ( in_array( $default, $licenses ) ) { |
188 | array_splice( $licenses, array_search( $default, $licenses ), 1 ); |
189 | } |
190 | array_unshift( $licenses, $default ); |
191 | // FIXME: No return value |
192 | } |
193 | |
194 | /** |
195 | * @param stdClass $campaign |
196 | * @param array $oldConfig |
197 | * @return array |
198 | */ |
199 | private function getConfigForJSON( $campaign, $oldConfig ) { |
200 | $config = [ |
201 | 'enabled' => $campaign->campaign_enabled === '1', |
202 | 'display' => [ |
203 | 'headerLabelPage' => $oldConfig['headerLabelPage'], |
204 | 'thanksLabelPage' => $oldConfig['thanksLabelPage'] |
205 | ], |
206 | 'defaults' => [ |
207 | 'description' => $oldConfig['defaultDescription'], |
208 | 'lat' => $oldConfig['defaultLat'], |
209 | 'lon' => $oldConfig['defaultLon'], |
210 | 'categories' => $this->explodeStringToArray( $oldConfig['defaultCategories'] ) |
211 | ], |
212 | 'autoAdd' => [ |
213 | 'wikitext' => $oldConfig['autoWikiText'], |
214 | 'categories' => $this->explodeStringToArray( $oldConfig['autoCategories'] ) |
215 | ], |
216 | "licensing" => [ |
217 | 'defaultType' => $oldConfig['defaultLicenseType'], |
218 | 'ownWorkDefault' => $oldConfig['ownWorkOption'], |
219 | 'ownWork' => [ |
220 | 'licenses' => $this->ensureDefaultLicense( |
221 | $this->explodeStringToArray( $oldConfig['licensesOwnWork'] ), |
222 | $oldConfig['defaultOwnWorkLicense'] |
223 | ) |
224 | ] |
225 | ], |
226 | 'fields' => [ |
227 | [ |
228 | 'wikitext' => $oldConfig['idField'], |
229 | 'label' => $oldConfig['idFieldLabel'], |
230 | # Migrated even though this is a nop. |
231 | # People will have to migrate this manually |
232 | 'labelPage' => $oldConfig['idFieldLabelPage'], |
233 | 'maxLength' => $oldConfig['idFieldMaxLength'], |
234 | 'initialValue' => $oldConfig['idFieldInitialValue'] |
235 | ], |
236 | [ |
237 | 'wikitext' => $oldConfig['idField2'], |
238 | 'label' => $oldConfig['idField2Label'], |
239 | 'labelPage' => $oldConfig['idField2LabelPage'], |
240 | 'maxLength' => $oldConfig['idField2MaxLength'], |
241 | 'initialValue' => $oldConfig['idField2InitialValue'] |
242 | ] |
243 | ], |
244 | 'tutorial' => [ |
245 | 'skip' => (bool)$oldConfig['skipTutorial'], |
246 | 'template' => $oldConfig['tutorialTemplate'], |
247 | 'helpdeskCoords' => $oldConfig['tutorialHelpdeskCoords'], |
248 | 'width' => $oldConfig['tutorialWidth'] |
249 | ] |
250 | ]; |
251 | |
252 | return $this->trimArray( $config ); |
253 | } |
254 | |
255 | public function execute() { |
256 | $services = $this->getServiceContainer(); |
257 | |
258 | $username = $this->getOption( 'user', 'Maintenance script' ); |
259 | |
260 | $this->dbr = $services->getDBLoadBalancerFactory()->getPrimaryDatabase(); |
261 | $campaigns = $this->dbr->newSelectQueryBuilder() |
262 | ->select( '*' ) |
263 | ->from( 'uw_campaigns' ) |
264 | ->caller( __METHOD__ ) |
265 | ->fetchResultSet(); |
266 | |
267 | if ( !$campaigns->numRows() ) { |
268 | $this->output( "Nothing to migrate.\n" ); |
269 | return; |
270 | } |
271 | |
272 | $user = $services->getUserFactory()->newFromName( $username ); |
273 | if ( !$user ) { |
274 | $this->fatalError( 'invalid username.' ); |
275 | } |
276 | $wikiPageFactory = $services->getWikiPageFactory(); |
277 | foreach ( $campaigns as $campaign ) { |
278 | $oldConfig = $this->getConfigFromDB( $campaign->campaign_id ); |
279 | $newConfig = $this->getConfigForJSON( $campaign, $oldConfig ); |
280 | |
281 | $title = Title::makeTitleSafe( NS_CAMPAIGN, $campaign->campaign_name ); |
282 | $page = $wikiPageFactory->newFromTitle( $title ); |
283 | |
284 | $content = new CampaignContent( json_encode( $newConfig ) ); |
285 | $page->doUserEditContent( |
286 | $content, |
287 | $user, |
288 | "Migrating from old campaign tables" |
289 | ); |
290 | $this->output( "Migrated {$campaign->campaign_name}\n" ); |
291 | } |
292 | } |
293 | } |
294 | |
295 | $maintClass = MigrateCampaigns::class; |
296 | require_once RUN_MAINTENANCE_IF_MAIN; |