Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 163 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
LoadPreDefinedObject | |
0.00% |
0 / 157 |
|
0.00% |
0 / 3 |
2352 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 76 |
|
0.00% |
0 / 1 |
992 | |||
makeEdit | |
0.00% |
0 / 48 |
|
0.00% |
0 / 1 |
272 |
1 | <?php |
2 | |
3 | /** |
4 | * WikiLambda loadPreDefinedObject maintenance script |
5 | * |
6 | * Loads specified pre-defined Object, range of Objects, or all pre-defined Objects into the database. |
7 | * |
8 | * @file |
9 | * @ingroup Extensions |
10 | * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt |
11 | * @license MIT |
12 | */ |
13 | |
14 | use MediaWiki\Extension\WikiLambda\ZObjectStore; |
15 | use MediaWiki\Logger\LoggerFactory; |
16 | use MediaWiki\MediaWikiServices; |
17 | use MediaWiki\Title\Title; |
18 | use MediaWiki\Title\TitleFactory; |
19 | |
20 | $IP = getenv( 'MW_INSTALL_PATH' ); |
21 | if ( $IP === false ) { |
22 | $IP = __DIR__ . '/../../..'; |
23 | } |
24 | require_once "$IP/maintenance/Maintenance.php"; |
25 | |
26 | class LoadPreDefinedObject extends Maintenance { |
27 | |
28 | /** |
29 | * @inheritDoc |
30 | */ |
31 | public function __construct() { |
32 | parent::__construct(); |
33 | $this->requireExtension( 'WikiLambda' ); |
34 | $this->addDescription( 'Loads a specified pre-defined Object into the database' ); |
35 | |
36 | $this->addOption( |
37 | 'zid', |
38 | 'Which ZID to load', |
39 | false, |
40 | true |
41 | ); |
42 | |
43 | $this->addOption( |
44 | 'from', |
45 | 'Which range of ZIDs to load from', |
46 | false, |
47 | true |
48 | ); |
49 | |
50 | $this->addOption( |
51 | 'to', |
52 | 'Which range of ZIDs to load to', |
53 | false, |
54 | true |
55 | ); |
56 | |
57 | $this->addOption( |
58 | 'all', |
59 | 'Load for all pre-defined ZIDs (Z1 – Z9999)', |
60 | false, |
61 | false |
62 | ); |
63 | |
64 | $this->addOption( |
65 | 'force', |
66 | 'Forces the load even if the Object already exists (clears the Object)', |
67 | false, |
68 | false |
69 | ); |
70 | |
71 | // TODO (T335418): Add a feature to reload the 'content' but retain the user-written/-extended |
72 | // labels, aliases, and short descriptions, for use e.g. when |
73 | } |
74 | |
75 | /** |
76 | * @inheritDoc |
77 | */ |
78 | public function execute() { |
79 | // Construct the ZObjectStore, because ServiceWiring hasn't run |
80 | $services = MediaWikiServices::getInstance(); |
81 | $titleFactory = $services->getTitleFactory(); |
82 | $zObjectStore = new ZObjectStore( |
83 | $services->getDBLoadBalancerFactory(), |
84 | $services->getTitleFactory(), |
85 | $services->getWikiPageFactory(), |
86 | $services->getRevisionStore(), |
87 | $services->getUserGroupManager(), |
88 | LoggerFactory::getInstance( 'WikiLambda' ) |
89 | ); |
90 | |
91 | $from = $this->getOption( 'from' ); |
92 | $to = $this->getOption( 'to' ); |
93 | $all = $this->getOption( 'all' ); |
94 | $zid = $this->getOption( 'zid' ); |
95 | $force = $this->getOption( 'force' ) || false; |
96 | |
97 | if ( $all ) { |
98 | // --all option overrides any --from and --to passed as arguments |
99 | if ( (bool)$from || (bool)$to ) { |
100 | $this->fatalError( 'The flag "--all" should not be used along with "--for" and "--to"' . "\n" ); |
101 | } |
102 | |
103 | $from = 1; |
104 | $to = 9999; |
105 | } elseif ( $zid ) { |
106 | if ( !is_numeric( $zid ) || $zid < 1 || $zid > 9999 ) { |
107 | $this->fatalError( 'The flag "--zid" must be a number between 1 and 9999' . "\n" ); |
108 | } |
109 | |
110 | // --zid option overrides any --from and --to passed as arguments |
111 | if ( (bool)$from || (bool)$to ) { |
112 | $this->fatalError( 'The flag "--zid" should not be used with "--for" or "--to"' . "\n" ); |
113 | } |
114 | |
115 | // --zid option also overrides --all if present |
116 | $from = $zid; |
117 | $to = $zid; |
118 | } else { |
119 | // If no --all and no --zid are entered, then --from and --to are mandatory |
120 | if ( (bool)$from xor (bool)$to ) { |
121 | $this->fatalError( 'The flag "--from" must be used with the flag "--to" to set a range' . "\n" ); |
122 | } |
123 | |
124 | if ( !is_numeric( $from ) || $from < 1 || $from > 9999 ) { |
125 | $this->fatalError( 'The flag "--from" must be a number between 1 and 9999' . "\n" ); |
126 | } |
127 | |
128 | if ( !is_numeric( $to ) || $to < 1 || $to > 9999 ) { |
129 | $this->fatalError( 'The flag "--to" must be a number between 1 and 9999' . "\n" ); |
130 | } |
131 | |
132 | if ( $from > $to ) { |
133 | $this->fatalError( 'The flag "--from" must be lower than the flag "--to"' . "\n" ); |
134 | } |
135 | } |
136 | |
137 | // Base path: |
138 | $path = dirname( __DIR__ ) . '/function-schemata/data/definitions/'; |
139 | |
140 | // Get dependencies file |
141 | $dependenciesFile = file_get_contents( $path . 'dependencies.json' ); |
142 | if ( $dependenciesFile === false ) { |
143 | $this->fatalError( |
144 | 'Could not load dependencies file from function-schemata sub-repository of the WikiLambda extension.' |
145 | . ' Have you initiated & fetched it? Try `git submodule update --init --recursive`.' |
146 | ); |
147 | } |
148 | $dependencies = json_decode( $dependenciesFile, true ); |
149 | $tracker = []; |
150 | |
151 | // Get data files |
152 | $initialDataToLoadListing = array_filter( |
153 | scandir( $path ), |
154 | static function ( $key ) use ( $from, $to ) { |
155 | if ( preg_match( '/^Z(\d+)\.json$/', $key, $match ) ) { |
156 | if ( $match[1] >= $from && $match[1] <= $to ) { |
157 | return true; |
158 | } |
159 | } |
160 | return false; |
161 | } |
162 | ); |
163 | |
164 | // Naturally sort, so Z2 gets created before Z12 etc. |
165 | natsort( $initialDataToLoadListing ); |
166 | |
167 | $success = 0; |
168 | $failure = 0; |
169 | $skipped = 0; |
170 | foreach ( $initialDataToLoadListing as $filename ) { |
171 | |
172 | $zid = substr( $filename, 0, -5 ); |
173 | |
174 | switch ( $this->makeEdit( $zid, $path, $titleFactory, $zObjectStore, $force, $dependencies, $tracker ) ) { |
175 | case 1: |
176 | $success++; |
177 | break; |
178 | |
179 | case -1: |
180 | $failure++; |
181 | break; |
182 | |
183 | case 0: |
184 | $skipped++; |
185 | break; |
186 | |
187 | default: |
188 | throw new RuntimeException( 'Unrecognised return value!' ); |
189 | } |
190 | } |
191 | |
192 | if ( $success > 0 ) { |
193 | $this->output( $success . ' objects creates or updates successes.' . "\n" ); |
194 | } |
195 | |
196 | if ( $skipped > 0 ) { |
197 | $this->output( $skipped . ' objects creates or updates skipped.' . "\n" ); |
198 | } |
199 | |
200 | if ( $failure > 0 ) { |
201 | $this->fatalError( $failure . ' objects creates or updates failed.' . "\n" ); |
202 | } |
203 | } |
204 | |
205 | // phpcs:disable MediaWiki.Commenting.FunctionComment.MissingDocumentationPrivate |
206 | private function makeEdit( |
207 | string $zid, |
208 | string $path, |
209 | TitleFactory $titleFactory, |
210 | ZObjectStore $zObjectStore, |
211 | bool $force, |
212 | array $dependencies, |
213 | array &$tracker |
214 | ) { |
215 | $data = file_get_contents( $path . $zid . '.json' ); |
216 | |
217 | if ( !$data ) { |
218 | $this->error( 'The ZObject "' . $zid . '" was not found in the definitions folder.' ); |
219 | return -1; |
220 | } |
221 | |
222 | $title = $titleFactory->newFromText( $zid, NS_MAIN ); |
223 | if ( !( $title instanceof Title ) ) { |
224 | $this->error( 'The ZObject title "' . $zid . '" could not be loaded somehow; invalid name?' ); |
225 | return -1; |
226 | } |
227 | |
228 | if ( !$title->exists() ) { |
229 | $creating = true; |
230 | } else { |
231 | if ( !$force ) { |
232 | $this->error( 'The ZObject "' . $zid . '" already exists and --force was not passed.' ); |
233 | return 0; |
234 | } |
235 | $creating = false; |
236 | } |
237 | |
238 | $deps = $dependencies[ $zid ]; |
239 | foreach ( $deps as $dep ) { |
240 | // Avoid circular dependencies |
241 | if ( !in_array( $dep, $tracker ) ) { |
242 | array_push( $tracker, $dep ); |
243 | // Don't force dependencies if they are already inserted, set to false |
244 | $depSuccess = $this->makeEdit( |
245 | $dep, $path, $titleFactory, $zObjectStore, false, $dependencies, $tracker ); |
246 | switch ( $depSuccess ) { |
247 | case 1: |
248 | $this->output( "Dependency: $dep successfully inserted.\n" ); |
249 | break; |
250 | case -1: |
251 | $this->output( "Dependency: $dep failed.\n" ); |
252 | break; |
253 | case 0: |
254 | $this->output( "Dependency: $dep skipped.\n" ); |
255 | break; |
256 | default: |
257 | throw new RuntimeException( 'Unrecognised return value!' ); |
258 | } |
259 | } |
260 | } |
261 | |
262 | $editSummary = wfMessage( |
263 | $creating |
264 | ? 'wikilambda-bootstrapcreationeditsummary' |
265 | : 'wikilambda-bootstrapupdatingeditsummary' |
266 | )->inLanguage( 'en' )->text(); |
267 | |
268 | // And we create or update the ZObject |
269 | $response = $zObjectStore->updateZObjectAsSystemUser( |
270 | /* String zid */ $zid, |
271 | /* String content */ $data, |
272 | /* Edit summary */ $editSummary, |
273 | /* Update flags */ $creating ? EDIT_NEW : EDIT_UPDATE |
274 | ); |
275 | |
276 | if ( $response->isOK() ) { |
277 | $this->output( ( $creating ? 'Created ' : 'Updated ' ) . '"' . $zid . '".' . "\n" ); |
278 | return 1; |
279 | } else { |
280 | $this->error( "Problem " . ( $creating ? 'creating' : 'updating' ) . ' "' . $zid . "\":\n" ); |
281 | $this->error( $response->getErrors() ); |
282 | $this->error( "\n" ); |
283 | return -1; |
284 | } |
285 | } |
286 | } |
287 | |
288 | $maintClass = LoadPreDefinedObject::class; |
289 | require_once RUN_MAINTENANCE_IF_MAIN; |