MediaWiki REL1_34
SpecialImport.php
Go to the documentation of this file.
1<?php
29
36 private $sourceName = false;
37 private $interwiki = false;
38 private $subproject;
40 private $mapping = 'default';
41 private $namespace;
42 private $rootpage = '';
43 private $frompage = '';
44 private $logcomment = false;
45 private $history = true;
46 private $includeTemplates = false;
51
52 public function __construct() {
53 parent::__construct( 'Import', 'import' );
54 }
55
56 public function doesWrites() {
57 return true;
58 }
59
66 function execute( $par ) {
68
69 $this->setHeaders();
70 $this->outputHeader();
71
72 $this->namespace = $this->getConfig()->get( 'ImportTargetNamespace' );
73
74 $this->getOutput()->addModules( 'mediawiki.special.import' );
75
76 $this->importSources = $this->getConfig()->get( 'ImportSources' );
77 Hooks::run( 'ImportSources', [ &$this->importSources ] );
78
79 $user = $this->getUser();
80 if ( !MediaWikiServices::getInstance()
82 ->userHasAnyRight( $user, 'import', 'importupload' )
83 ) {
84 throw new PermissionsError( 'import' );
85 }
86
87 # @todo Allow Title::getUserPermissionsErrors() to take an array
88 # @todo FIXME: Title::checkSpecialsAndNSPermissions() has a very weird expectation of what
89 # getUserPermissionsErrors() might actually be used for, hence the 'ns-specialprotected'
90 $errors = wfMergeErrorArrays(
91 $this->getPageTitle()->getUserPermissionsErrors(
92 'import', $user, PermissionManager::RIGOR_FULL,
93 [ 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' ]
94 ),
95 $this->getPageTitle()->getUserPermissionsErrors(
96 'importupload', $user, PermissionManager::RIGOR_FULL,
97 [ 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' ]
98 )
99 );
100
101 if ( $errors ) {
102 throw new PermissionsError( 'import', $errors );
103 }
104
105 $this->checkReadOnly();
106
107 $request = $this->getRequest();
108 if ( $request->wasPosted() && $request->getVal( 'action' ) == 'submit' ) {
109 $this->doImport();
110 }
111 $this->showForm();
112 }
113
117 private function doImport() {
118 $isUpload = false;
119 $request = $this->getRequest();
120 $this->sourceName = $request->getVal( "source" );
121 $this->assignKnownUsers = $request->getCheck( 'assignKnownUsers' );
122
123 $this->logcomment = $request->getText( 'log-comment' );
124 $this->pageLinkDepth = $this->getConfig()->get( 'ExportMaxLinkDepth' ) == 0
125 ? 0
126 : $request->getIntOrNull( 'pagelink-depth' );
127
128 $this->mapping = $request->getVal( 'mapping' );
129 if ( $this->mapping === 'namespace' ) {
130 $this->namespace = $request->getIntOrNull( 'namespace' );
131 } elseif ( $this->mapping === 'subpage' ) {
132 $this->rootpage = $request->getText( 'rootpage' );
133 } else {
134 $this->mapping = 'default';
135 }
136
137 $user = $this->getUser();
138 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
139 if ( !$user->matchEditToken( $request->getVal( 'editToken' ) ) ) {
140 $source = Status::newFatal( 'import-token-mismatch' );
141 } elseif ( $this->sourceName === 'upload' ) {
142 $isUpload = true;
143 $this->usernamePrefix = $this->fullInterwikiPrefix = $request->getVal( 'usernamePrefix' );
144 if ( $permissionManager->userHasRight( $user, 'importupload' ) ) {
146 } else {
147 throw new PermissionsError( 'importupload' );
148 }
149 } elseif ( $this->sourceName === 'interwiki' ) {
150 if ( !$permissionManager->userHasRight( $user, 'import' ) ) {
151 throw new PermissionsError( 'import' );
152 }
153 $this->interwiki = $this->fullInterwikiPrefix = $request->getVal( 'interwiki' );
154 // does this interwiki have subprojects?
155 $hasSubprojects = array_key_exists( $this->interwiki, $this->importSources );
156 if ( !$hasSubprojects && !in_array( $this->interwiki, $this->importSources ) ) {
157 $source = Status::newFatal( "import-invalid-interwiki" );
158 } else {
159 if ( $hasSubprojects ) {
160 $this->subproject = $request->getVal( 'subproject' );
161 $this->fullInterwikiPrefix .= ':' . $request->getVal( 'subproject' );
162 }
163 if ( $hasSubprojects &&
164 !in_array( $this->subproject, $this->importSources[$this->interwiki] )
165 ) {
166 $source = Status::newFatal( "import-invalid-interwiki" );
167 } else {
168 $this->history = $request->getCheck( 'interwikiHistory' );
169 $this->frompage = $request->getText( "frompage" );
170 $this->includeTemplates = $request->getCheck( 'interwikiTemplates' );
172 $this->fullInterwikiPrefix,
173 $this->frompage,
174 $this->history,
175 $this->includeTemplates,
176 $this->pageLinkDepth );
177 }
178 }
179 } else {
180 $source = Status::newFatal( "importunknownsource" );
181 }
182
183 if ( (string)$this->fullInterwikiPrefix === '' ) {
184 $source->fatal( 'importnoprefix' );
185 }
186
187 $out = $this->getOutput();
188 if ( !$source->isGood() ) {
189 $out->wrapWikiTextAsInterface( 'error',
190 $this->msg( 'importfailed', $source->getWikiText( false, false, $this->getLanguage() ) )
191 ->plain()
192 );
193 } else {
194 $importer = new WikiImporter( $source->value, $this->getConfig() );
195 if ( !is_null( $this->namespace ) ) {
196 $importer->setTargetNamespace( $this->namespace );
197 } elseif ( !is_null( $this->rootpage ) ) {
198 $statusRootPage = $importer->setTargetRootPage( $this->rootpage );
199 if ( !$statusRootPage->isGood() ) {
200 $out->wrapWikiMsg(
201 "<div class=\"error\">\n$1\n</div>",
202 [
203 'import-options-wrong',
204 $statusRootPage->getWikiText( false, false, $this->getLanguage() ),
205 count( $statusRootPage->getErrorsArray() )
206 ]
207 );
208
209 return;
210 }
211 }
212 $importer->setUsernamePrefix( $this->fullInterwikiPrefix, $this->assignKnownUsers );
213
214 $out->addWikiMsg( "importstart" );
215
216 $reporter = new ImportReporter(
217 $importer,
218 $isUpload,
219 $this->fullInterwikiPrefix,
220 $this->logcomment
221 );
222 $reporter->setContext( $this->getContext() );
223 $exception = false;
224
225 $reporter->open();
226 try {
227 $importer->doImport();
228 } catch ( Exception $e ) {
229 $exception = $e;
230 }
231 $result = $reporter->close();
232
233 if ( $exception ) {
234 # No source or XML parse error
235 $out->wrapWikiMsg(
236 "<div class=\"error\">\n$1\n</div>",
237 [ 'importfailed', $exception->getMessage() ]
238 );
239 } elseif ( !$result->isGood() ) {
240 # Zero revisions
241 $out->wrapWikiMsg(
242 "<div class=\"error\">\n$1\n</div>",
243 [ 'importfailed', $result->getWikiText( false, false, $this->getLanguage() ) ]
244 );
245 } else {
246 # Success!
247 $out->addWikiMsg( 'importsuccess' );
248 }
249 $out->addHTML( '<hr />' );
250 }
251 }
252
253 private function getMappingFormPart( $sourceName ) {
254 $isSameSourceAsBefore = ( $this->sourceName === $sourceName );
255 $defaultNamespace = $this->getConfig()->get( 'ImportTargetNamespace' );
256 return "<tr>
257 <td>
258 </td>
259 <td class='mw-input'>" .
260 Xml::radioLabel(
261 $this->msg( 'import-mapping-default' )->text(),
262 'mapping',
263 'default',
264 // mw-import-mapping-interwiki-default, mw-import-mapping-upload-default
265 "mw-import-mapping-$sourceName-default",
266 ( $isSameSourceAsBefore ?
267 ( $this->mapping === 'default' ) :
268 is_null( $defaultNamespace ) )
269 ) .
270 "</td>
271 </tr>
272 <tr>
273 <td>
274 </td>
275 <td class='mw-input'>" .
276 Xml::radioLabel(
277 $this->msg( 'import-mapping-namespace' )->text(),
278 'mapping',
279 'namespace',
280 // mw-import-mapping-interwiki-namespace, mw-import-mapping-upload-namespace
281 "mw-import-mapping-$sourceName-namespace",
282 ( $isSameSourceAsBefore ?
283 ( $this->mapping === 'namespace' ) :
284 !is_null( $defaultNamespace ) )
285 ) . ' ' .
286 Html::namespaceSelector(
287 [
288 'selected' => ( $isSameSourceAsBefore ?
289 $this->namespace :
290 ( $defaultNamespace || '' ) ),
291 'in-user-lang' => true,
292 ], [
293 'name' => "namespace",
294 // mw-import-namespace-interwiki, mw-import-namespace-upload
295 'id' => "mw-import-namespace-$sourceName",
296 'class' => 'namespaceselector',
297 ]
298 ) .
299 "</td>
300 </tr>
301 <tr>
302 <td>
303 </td>
304 <td class='mw-input'>" .
305 Xml::radioLabel(
306 $this->msg( 'import-mapping-subpage' )->text(),
307 'mapping',
308 'subpage',
309 // mw-import-mapping-interwiki-subpage, mw-import-mapping-upload-subpage
310 "mw-import-mapping-$sourceName-subpage",
311 ( $isSameSourceAsBefore ? ( $this->mapping === 'subpage' ) : '' )
312 ) . ' ' .
313 Xml::input( 'rootpage', 50,
314 ( $isSameSourceAsBefore ? $this->rootpage : '' ),
315 [
316 // Should be "mw-import-rootpage-...", but we keep this inaccurate
317 // ID for legacy reasons
318 // mw-interwiki-rootpage-interwiki, mw-interwiki-rootpage-upload
319 'id' => "mw-interwiki-rootpage-$sourceName",
320 'type' => 'text'
321 ]
322 ) . ' ' .
323 "</td>
324 </tr>";
325 }
326
327 private function showForm() {
328 $action = $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] );
329 $user = $this->getUser();
330 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
331 $out = $this->getOutput();
332 $this->addHelpLink( 'https://meta.wikimedia.org/wiki/Special:MyLanguage/Help:Import', true );
333
334 if ( $permissionManager->userHasRight( $user, 'importupload' ) ) {
335 $mappingSelection = $this->getMappingFormPart( 'upload' );
336 $out->addHTML(
337 Xml::fieldset( $this->msg( 'import-upload' )->text() ) .
338 Xml::openElement(
339 'form',
340 [
341 'enctype' => 'multipart/form-data',
342 'method' => 'post',
343 'action' => $action,
344 'id' => 'mw-import-upload-form'
345 ]
346 ) .
347 $this->msg( 'importtext' )->parseAsBlock() .
348 Html::hidden( 'action', 'submit' ) .
349 Html::hidden( 'source', 'upload' ) .
350 Xml::openElement( 'table', [ 'id' => 'mw-import-table-upload' ] ) .
351 "<tr>
352 <td class='mw-label'>" .
353 Xml::label( $this->msg( 'import-upload-filename' )->text(), 'xmlimport' ) .
354 "</td>
355 <td class='mw-input'>" .
356 Html::input( 'xmlimport', '', 'file', [ 'id' => 'xmlimport' ] ) . ' ' .
357 "</td>
358 </tr>
359 <tr>
360 <td class='mw-label'>" .
361 Xml::label( $this->msg( 'import-upload-username-prefix' )->text(),
362 'mw-import-usernamePrefix' ) .
363 "</td>
364 <td class='mw-input'>" .
365 Xml::input( 'usernamePrefix', 50,
366 $this->usernamePrefix,
367 [ 'id' => 'usernamePrefix', 'type' => 'text' ] ) . ' ' .
368 "</td>
369 </tr>
370 <tr>
371 <td></td>
372 <td class='mw-input'>" .
373 Xml::checkLabel(
374 $this->msg( 'import-assign-known-users' )->text(),
375 'assignKnownUsers',
376 'assignKnownUsers',
377 $this->assignKnownUsers
378 ) .
379 "</td>
380 </tr>
381 <tr>
382 <td class='mw-label'>" .
383 Xml::label( $this->msg( 'import-comment' )->text(), 'mw-import-comment' ) .
384 "</td>
385 <td class='mw-input'>" .
386 Xml::input( 'log-comment', 50,
387 ( $this->sourceName === 'upload' ? $this->logcomment : '' ),
388 [ 'id' => 'mw-import-comment', 'type' => 'text' ] ) . ' ' .
389 "</td>
390 </tr>
391 $mappingSelection
392 <tr>
393 <td></td>
394 <td class='mw-submit'>" .
395 Xml::submitButton( $this->msg( 'uploadbtn' )->text() ) .
396 "</td>
397 </tr>" .
398 Xml::closeElement( 'table' ) .
399 Html::hidden( 'editToken', $user->getEditToken() ) .
400 Xml::closeElement( 'form' ) .
401 Xml::closeElement( 'fieldset' )
402 );
403 } elseif ( empty( $this->importSources ) ) {
404 $out->addWikiMsg( 'importnosources' );
405 }
406
407 if ( $permissionManager->userHasRight( $user, 'import' ) && !empty( $this->importSources ) ) {
408 # Show input field for import depth only if $wgExportMaxLinkDepth > 0
409 $importDepth = '';
410 if ( $this->getConfig()->get( 'ExportMaxLinkDepth' ) > 0 ) {
411 $importDepth = "<tr>
412 <td class='mw-label'>" .
413 $this->msg( 'export-pagelinks' )->parse() .
414 "</td>
415 <td class='mw-input'>" .
416 Xml::input( 'pagelink-depth', 3, 0 ) .
417 "</td>
418 </tr>";
419 }
420 $mappingSelection = $this->getMappingFormPart( 'interwiki' );
421
422 $out->addHTML(
423 Xml::fieldset( $this->msg( 'importinterwiki' )->text() ) .
424 Xml::openElement(
425 'form',
426 [
427 'method' => 'post',
428 'action' => $action,
429 'id' => 'mw-import-interwiki-form'
430 ]
431 ) .
432 $this->msg( 'import-interwiki-text' )->parseAsBlock() .
433 Html::hidden( 'action', 'submit' ) .
434 Html::hidden( 'source', 'interwiki' ) .
435 Html::hidden( 'editToken', $user->getEditToken() ) .
436 Xml::openElement( 'table', [ 'id' => 'mw-import-table-interwiki' ] ) .
437 "<tr>
438 <td class='mw-label'>" .
439 Xml::label( $this->msg( 'import-interwiki-sourcewiki' )->text(), 'interwiki' ) .
440 "</td>
441 <td class='mw-input'>" .
442 Xml::openElement(
443 'select',
444 [ 'name' => 'interwiki', 'id' => 'interwiki' ]
445 )
446 );
447
448 $needSubprojectField = false;
449 foreach ( $this->importSources as $key => $value ) {
450 if ( is_int( $key ) ) {
451 $key = $value;
452 } elseif ( $value !== $key ) {
453 $needSubprojectField = true;
454 }
455
456 $attribs = [
457 'value' => $key,
458 ];
459 if ( is_array( $value ) ) {
460 $attribs['data-subprojects'] = implode( ' ', $value );
461 }
462 if ( $this->interwiki === $key ) {
463 $attribs['selected'] = 'selected';
464 }
465 $out->addHTML( Html::element( 'option', $attribs, $key ) );
466 }
467
468 $out->addHTML(
469 Xml::closeElement( 'select' )
470 );
471
472 if ( $needSubprojectField ) {
473 $out->addHTML(
474 Xml::openElement(
475 'select',
476 [ 'name' => 'subproject', 'id' => 'subproject' ]
477 )
478 );
479
480 $subprojectsToAdd = [];
481 foreach ( $this->importSources as $key => $value ) {
482 if ( is_array( $value ) ) {
483 $subprojectsToAdd = array_merge( $subprojectsToAdd, $value );
484 }
485 }
486 $subprojectsToAdd = array_unique( $subprojectsToAdd );
487 sort( $subprojectsToAdd );
488 foreach ( $subprojectsToAdd as $subproject ) {
489 $out->addHTML( Xml::option( $subproject, $subproject, $this->subproject === $subproject ) );
490 }
491
492 $out->addHTML(
493 Xml::closeElement( 'select' )
494 );
495 }
496
497 $out->addHTML(
498 "</td>
499 </tr>
500 <tr>
501 <td class='mw-label'>" .
502 Xml::label( $this->msg( 'import-interwiki-sourcepage' )->text(), 'frompage' ) .
503 "</td>
504 <td class='mw-input'>" .
505 Xml::input( 'frompage', 50, $this->frompage, [ 'id' => 'frompage' ] ) .
506 "</td>
507 </tr>
508 <tr>
509 <td>
510 </td>
511 <td class='mw-input'>" .
512 Xml::checkLabel(
513 $this->msg( 'import-interwiki-history' )->text(),
514 'interwikiHistory',
515 'interwikiHistory',
516 $this->history
517 ) .
518 "</td>
519 </tr>
520 <tr>
521 <td>
522 </td>
523 <td class='mw-input'>" .
524 Xml::checkLabel(
525 $this->msg( 'import-interwiki-templates' )->text(),
526 'interwikiTemplates',
527 'interwikiTemplates',
528 $this->includeTemplates
529 ) .
530 "</td>
531 </tr>
532 <tr>
533 <td></td>
534 <td class='mw-input'>" .
535 Xml::checkLabel(
536 $this->msg( 'import-assign-known-users' )->text(),
537 'assignKnownUsers',
538 'interwikiAssignKnownUsers',
539 $this->assignKnownUsers
540 ) .
541 "</td>
542 </tr>
543 $importDepth
544 <tr>
545 <td class='mw-label'>" .
546 Xml::label( $this->msg( 'import-comment' )->text(), 'mw-interwiki-comment' ) .
547 "</td>
548 <td class='mw-input'>" .
549 Xml::input( 'log-comment', 50,
550 ( $this->sourceName === 'interwiki' ? $this->logcomment : '' ),
551 [ 'id' => 'mw-interwiki-comment', 'type' => 'text' ] ) . ' ' .
552 "</td>
553 </tr>
554 $mappingSelection
555 <tr>
556 <td>
557 </td>
558 <td class='mw-submit'>" .
559 Xml::submitButton(
560 $this->msg( 'import-interwiki-submit' )->text(),
562 ) .
563 "</td>
564 </tr>" .
565 Xml::closeElement( 'table' ) .
566 Xml::closeElement( 'form' ) .
567 Xml::closeElement( 'fieldset' )
568 );
569 }
570 }
571
572 protected function getGroupName() {
573 return 'pagetools';
574 }
575}
getPermissionManager()
wfMergeErrorArrays(... $args)
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
Reporting callback.
static newFromInterwiki( $interwiki, $page, $history=false, $templates=false, $pageLinkDepth=0)
static newFromUpload( $fieldname="xmlimport")
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition Linker.php:2195
MediaWikiServices is the service locator for the application scope of MediaWiki.
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.
doesWrites()
Indicates whether this special page may perform database writes.
getMappingFormPart( $sourceName)
execute( $par)
Execute.
doImport()
Do the actual import.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
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.
XML file reader for the page data importer.
$source