MediaWiki REL1_37
SpecialImport.php
Go to the documentation of this file.
1<?php
28
37
39 private $permManager;
40
43
48 public function __construct(
51 ) {
52 parent::__construct( 'Import', 'import' );
53
54 $this->permManager = $permManager;
55 $this->wikiImporterFactory = $wikiImporterFactory;
56 }
57
58 public function doesWrites() {
59 return true;
60 }
61
68 public function execute( $par ) {
70
71 $this->setHeaders();
72 $this->outputHeader();
73
74 $this->getOutput()->addModules( 'mediawiki.special.import' );
75 $this->getOutput()->addModuleStyles( 'mediawiki.special.import.styles.ooui' );
76
77 $this->importSources = $this->getConfig()->get( 'ImportSources' );
78 // Avoid phan error by checking the type
79 if ( !is_array( $this->importSources ) ) {
80 throw new UnexpectedValueException( '$wgImportSources must be an array' );
81 }
82 $this->getHookRunner()->onImportSources( $this->importSources );
83
84 $user = $this->getUser();
85 if ( !$this->permManager->userHasAnyRight( $user, 'import', 'importupload' ) ) {
86 throw new PermissionsError( 'import' );
87 }
88
89 # @todo Allow PermissionManager::getPermissionErrors() to take an array
90 $errors = wfMergeErrorArrays(
91 $this->permManager->getPermissionErrors(
92 'import', $user, $this->getPageTitle(),
93 PermissionManager::RIGOR_FULL,
94 [ 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' ]
95 ),
96 $this->permManager->getPermissionErrors(
97 'importupload', $user, $this->getPageTitle(),
98 PermissionManager::RIGOR_FULL,
99 [ 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' ]
100 )
101 );
102
103 if ( $errors ) {
104 throw new PermissionsError( 'import', $errors );
105 }
106
107 $this->checkReadOnly();
108
109 $request = $this->getRequest();
110 if ( $request->wasPosted() && $request->getRawVal( 'action' ) == 'submit' ) {
111 $this->doImport();
112 }
113 $this->showForm();
114 }
115
119 private function doImport() {
120 $isUpload = false;
121 $request = $this->getRequest();
122 $sourceName = $request->getVal( 'source' );
123 $assignKnownUsers = $request->getCheck( 'assignKnownUsers' );
124
125 $logcomment = $request->getText( 'log-comment' );
126 $pageLinkDepth = $this->getConfig()->get( 'ExportMaxLinkDepth' ) == 0
127 ? 0
128 : $request->getIntOrNull( 'pagelink-depth' );
129
130 $rootpage = '';
131 $mapping = $request->getVal( 'mapping' );
132 $namespace = $this->getConfig()->get( 'ImportTargetNamespace' );
133 if ( $mapping === 'namespace' ) {
134 $namespace = $request->getIntOrNull( 'namespace' );
135 } elseif ( $mapping === 'subpage' ) {
136 $rootpage = $request->getText( 'rootpage' );
137 }
138
139 $user = $this->getUser();
140
141 $fullInterwikiPrefix = null;
142 if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) {
143 $source = Status::newFatal( 'import-token-mismatch' );
144 } elseif ( $sourceName === 'upload' ) {
145 $isUpload = true;
146 $fullInterwikiPrefix = $request->getVal( 'usernamePrefix' );
147 if ( $this->permManager->userHasRight( $user, 'importupload' ) ) {
149 } else {
150 throw new PermissionsError( 'importupload' );
151 }
152 } elseif ( $sourceName === 'interwiki' ) {
153 if ( !$this->permManager->userHasRight( $user, 'import' ) ) {
154 throw new PermissionsError( 'import' );
155 }
156 $interwiki = $fullInterwikiPrefix = $request->getVal( 'interwiki' );
157 // does this interwiki have subprojects?
158 $hasSubprojects = array_key_exists( $interwiki, $this->importSources );
159 if ( !$hasSubprojects && !in_array( $interwiki, $this->importSources ) ) {
160 $source = Status::newFatal( "import-invalid-interwiki" );
161 } else {
162 $subproject = null;
163 if ( $hasSubprojects ) {
164 $subproject = $request->getVal( 'subproject' );
165 // Trim "project::" prefix added for JS
166 if ( strpos( $subproject, $interwiki . '::' ) === 0 ) {
167 $subproject = substr( $subproject, strlen( $interwiki . '::' ) );
168 }
169 $fullInterwikiPrefix .= ':' . $subproject;
170 }
171 if ( $hasSubprojects &&
172 !in_array( $subproject, $this->importSources[$interwiki] )
173 ) {
174 $source = Status::newFatal( 'import-invalid-interwiki' );
175 } else {
176 $history = $request->getCheck( 'interwikiHistory' );
177 $frompage = $request->getText( 'frompage' );
178 $includeTemplates = $request->getCheck( 'interwikiTemplates' );
180 $fullInterwikiPrefix,
181 $frompage,
182 $history,
183 $includeTemplates,
184 $pageLinkDepth );
185 }
186 }
187 } else {
188 $source = Status::newFatal( "importunknownsource" );
189 }
190
191 if ( (string)$fullInterwikiPrefix === '' ) {
192 $source->fatal( 'importnoprefix' );
193 }
194
195 $out = $this->getOutput();
196 if ( !$source->isGood() ) {
197 $out->wrapWikiTextAsInterface( 'error',
198 $this->msg( 'importfailed', $source->getWikiText( false, false, $this->getLanguage() ) )
199 ->plain()
200 );
201 } else {
202 $importer = $this->wikiImporterFactory->getWikiImporter( $source->value );
203 if ( $namespace !== null ) {
204 $importer->setTargetNamespace( $namespace );
205 } elseif ( $rootpage !== null ) {
206 $statusRootPage = $importer->setTargetRootPage( $rootpage );
207 if ( !$statusRootPage->isGood() ) {
208 $out->wrapWikiMsg(
209 "<div class=\"error\">\n$1\n</div>",
210 [
211 'import-options-wrong',
212 $statusRootPage->getWikiText( false, false, $this->getLanguage() ),
213 count( $statusRootPage->getErrorsArray() )
214 ]
215 );
216
217 return;
218 }
219 }
220 $importer->setUsernamePrefix( $fullInterwikiPrefix, $assignKnownUsers );
221
222 $out->addWikiMsg( "importstart" );
223
224 $reporter = new ImportReporter(
225 $importer,
226 $isUpload,
227 $fullInterwikiPrefix,
228 $logcomment
229 );
230 $reporter->setContext( $this->getContext() );
231 $exception = false;
232
233 $reporter->open();
234 try {
235 $importer->doImport();
236 } catch ( Exception $e ) {
237 $exception = $e;
238 }
239 $result = $reporter->close();
240
241 if ( $exception ) {
242 # No source or XML parse error
243 $out->wrapWikiMsg(
244 "<div class=\"error\">\n$1\n</div>",
245 [ 'importfailed', $exception->getMessage() ]
246 );
247 } elseif ( !$result->isGood() ) {
248 # Zero revisions
249 $out->wrapWikiMsg(
250 "<div class=\"error\">\n$1\n</div>",
251 [ 'importfailed', $result->getWikiText( false, false, $this->getLanguage() ) ]
252 );
253 } else {
254 # Success!
255 $out->addWikiMsg( 'importsuccess' );
256 }
257 $out->addHTML( '<hr />' );
258 }
259 }
260
261 private function getMappingFormPart( $sourceName ) {
262 $defaultNamespace = $this->getConfig()->get( 'ImportTargetNamespace' );
263 return [
264 'mapping' => [
265 'type' => 'radio',
266 'name' => 'mapping',
267 // mw-import-mapping-interwiki, mw-import-mapping-upload
268 'id' => "mw-import-mapping-$sourceName",
269 'options-messages' => [
270 'import-mapping-default' => 'default',
271 'import-mapping-namespace' => 'namespace',
272 'import-mapping-subpage' => 'subpage'
273 ],
274 'default' => $defaultNamespace !== null ? 'namespace' : 'default'
275 ],
276 'namespace' => [
277 'type' => 'namespaceselect',
278 'name' => 'namespace',
279 // mw-import-namespace-interwiki, mw-import-namespace-upload
280 'id' => "mw-import-namespace-$sourceName",
281 'default' => $defaultNamespace ?: '',
282 'all' => null
283 ],
284 'rootpage' => [
285 'type' => 'text',
286 'name' => 'rootpage',
287 // Should be "mw-import-rootpage-...", but we keep this inaccurate
288 // ID for legacy reasons
289 // mw-interwiki-rootpage-interwiki, mw-interwiki-rootpage-upload
290 'id' => "mw-interwiki-rootpage-$sourceName",
291 ],
292 ];
293 }
294
295 private function showForm() {
296 $action = $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] );
297 $user = $this->getUser();
298 $out = $this->getOutput();
299 $this->addHelpLink( 'https://meta.wikimedia.org/wiki/Special:MyLanguage/Help:Import', true );
300
301 $interwikiFormDescriptor = [];
302 $uploadFormDescriptor = [];
303
304 if ( $this->permManager->userHasRight( $user, 'importupload' ) ) {
305 $mappingSelection = $this->getMappingFormPart( 'upload' );
306 $uploadFormDescriptor += [
307 'intro' => [
308 'type' => 'info',
309 'raw' => true,
310 'default' => $this->msg( 'importtext' )->parseAsBlock()
311 ],
312 'xmlimport' => [
313 'type' => 'file',
314 'name' => 'xmlimport',
315 'accept' => [ 'application/xml', 'text/xml' ],
316 'label-message' => 'import-upload-filename',
317 'required' => true,
318 ],
319 'usernamePrefix' => [
320 'type' => 'text',
321 'name' => 'usernamePrefix',
322 'label-message' => 'import-upload-username-prefix',
323 // TODO: Is this field required?
324 ],
325 'assignKnownUsers' => [
326 'type' => 'check',
327 'name' => 'assignKnownUsers',
328 'label-message' => 'import-assign-known-users'
329 ],
330 'log-comment' => [
331 'type' => 'text',
332 'name' => 'log-comment',
333 'label-message' => 'import-comment'
334 ],
335 'source' => [
336 'type' => 'hidden',
337 'name' => 'source',
338 'default' => 'upload',
339 'id' => '',
340 ],
341 ];
342
343 $uploadFormDescriptor += $mappingSelection;
344
345 $htmlForm = HTMLForm::factory( 'ooui', $uploadFormDescriptor, $this->getContext() );
346 $htmlForm->setAction( $action );
347 $htmlForm->setId( 'mw-import-upload-form' );
348 $htmlForm->setWrapperLegendMsg( 'import-upload' );
349 $htmlForm->setSubmitTextMsg( 'uploadbtn' );
350 $htmlForm->prepareForm()->displayForm( false );
351
352 } elseif ( empty( $this->importSources ) ) {
353 $out->addWikiMsg( 'importnosources' );
354 }
355
356 if ( $this->permManager->userHasRight( $user, 'import' ) && !empty( $this->importSources ) ) {
357
358 $projects = [];
359 $needSubprojectField = false;
360 foreach ( $this->importSources as $key => $value ) {
361 if ( is_int( $key ) ) {
362 $key = $value;
363 } elseif ( $value !== $key ) {
364 $needSubprojectField = true;
365 }
366
367 $projects[ $key ] = $key;
368 }
369
370 $interwikiFormDescriptor += [
371 'intro' => [
372 'type' => 'info',
373 'raw' => true,
374 'default' => $this->msg( 'import-interwiki-text' )->parseAsBlock()
375 ],
376 'interwiki' => [
377 'type' => 'select',
378 'name' => 'interwiki',
379 'label-message' => 'import-interwiki-sourcewiki',
380 'options' => $projects
381 ],
382 ];
383
384 if ( $needSubprojectField ) {
385 $subprojects = [];
386 foreach ( $this->importSources as $key => $value ) {
387 if ( is_array( $value ) ) {
388 foreach ( $value as $subproject ) {
389 $subprojects[ $subproject ] = $key . '::' . $subproject;
390 }
391 }
392 }
393
394 $interwikiFormDescriptor += [
395 'subproject' => [
396 'type' => 'select',
397 'name' => 'subproject',
398 'options' => $subprojects
399 ]
400 ];
401 }
402
403 $interwikiFormDescriptor += [
404 'frompage' => [
405 'type' => 'text',
406 'name' => 'frompage',
407 'label-message' => 'import-interwiki-sourcepage'
408 ],
409 'interwikiHistory' => [
410 'type' => 'check',
411 'name' => 'interwikiHistory',
412 'label-message' => 'import-interwiki-history'
413 ],
414 'interwikiTemplates' => [
415 'type' => 'check',
416 'name' => 'interwikiTemplates',
417 'label-message' => 'import-interwiki-templates'
418 ],
419 'assignKnownUsers' => [
420 'type' => 'check',
421 'name' => 'assignKnownUsers',
422 'label-message' => 'import-assign-known-users'
423 ],
424 ];
425
426 if ( $this->getConfig()->get( 'ExportMaxLinkDepth' ) > 0 ) {
427 $interwikiFormDescriptor += [
428 'pagelink-depth' => [
429 'type' => 'int',
430 'name' => 'pagelink-depth',
431 'label-message' => 'export-pagelinks',
432 'default' => 0
433 ]
434 ];
435 }
436
437 $interwikiFormDescriptor += [
438 'log-comment' => [
439 'type' => 'text',
440 'name' => 'log-comment',
441 'label-message' => 'import-comment'
442 ],
443 'source' => [
444 'type' => 'hidden',
445 'name' => 'source',
446 'default' => 'interwiki',
447 'id' => '',
448 ],
449 ];
450 $mappingSelection = $this->getMappingFormPart( 'interwiki' );
451
452 $interwikiFormDescriptor += $mappingSelection;
453
454 $htmlForm = HTMLForm::factory( 'ooui', $interwikiFormDescriptor, $this->getContext() );
455 $htmlForm->setAction( $action );
456 $htmlForm->setId( 'mw-import-interwiki-form' );
457 $htmlForm->setWrapperLegendMsg( 'importinterwiki' );
458 $htmlForm->setSubmitTextMsg( 'import-interwiki-submit' );
459 $htmlForm->prepareForm()->displayForm( false );
460 }
461 }
462
463 protected function getGroupName() {
464 return 'pagetools';
465 }
466}
wfMergeErrorArrays(... $args)
Merge arrays in the style of PermissionManager::getPermissionErrors, with duplicate removal e....
Reporting callback.
static newFromInterwiki( $interwiki, $page, $history=false, $templates=false, $pageLinkDepth=0)
static newFromUpload( $fieldname="xmlimport")
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Show an error when a user tries to do something they do not have the necessary permissions for.
MediaWiki page data importer.
WikiImporterFactory $wikiImporterFactory
doesWrites()
Indicates whether this special page may perform database writes.
getMappingFormPart( $sourceName)
PermissionManager $permManager
execute( $par)
Execute.
doImport()
Do the actual import.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
__construct(PermissionManager $permManager, WikiImporterFactory $wikiImporterFactory)
Parent class for all special pages.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getConfig()
Shortcut to get main config object.
getRequest()
Get the WebRequest being used for this instance.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getPageTitle( $subpage=false)
Get a self-referential title object.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Factory service for WikiImporter instances.
$source