MediaWiki fundraising/REL1_35
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;
48
53
57 private $permManager;
58
59 public function __construct() {
60 parent::__construct( 'Import', 'import' );
61 $this->permManager = MediaWikiServices::getInstance()->getPermissionManager();
62 }
63
64 public function doesWrites() {
65 return true;
66 }
67
74 public function execute( $par ) {
76
77 $this->setHeaders();
78 $this->outputHeader();
79
80 $this->namespace = $this->getConfig()->get( 'ImportTargetNamespace' );
81
82 $this->getOutput()->addModules( 'mediawiki.special.import' );
83
84 $this->importSources = $this->getConfig()->get( 'ImportSources' );
85 // Avoid phan error by checking the type
86 if ( !is_array( $this->importSources ) ) {
87 throw new UnexpectedValueException( '$wgImportSources must be an array' );
88 }
89 $this->getHookRunner()->onImportSources( $this->importSources );
90
91 $user = $this->getUser();
92 if ( !$this->permManager->userHasAnyRight( $user, 'import', 'importupload' ) ) {
93 throw new PermissionsError( 'import' );
94 }
95
96 # @todo Allow PermissionManager::getPermissionErrors() to take an array
97 $errors = wfMergeErrorArrays(
98 $this->permManager->getPermissionErrors(
99 'import', $user, $this->getPageTitle(),
100 PermissionManager::RIGOR_FULL,
101 [ 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' ]
102 ),
103 $this->permManager->getPermissionErrors(
104 'importupload', $user, $this->getPageTitle(),
105 PermissionManager::RIGOR_FULL,
106 [ 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' ]
107 )
108 );
109
110 if ( $errors ) {
111 throw new PermissionsError( 'import', $errors );
112 }
113
114 $this->checkReadOnly();
115
116 $request = $this->getRequest();
117 if ( $request->wasPosted() && $request->getVal( 'action' ) == 'submit' ) {
118 $this->doImport();
119 }
120 $this->showForm();
121 }
122
126 private function doImport() {
127 $isUpload = false;
128 $request = $this->getRequest();
129 $this->sourceName = $request->getVal( "source" );
130 $this->assignKnownUsers = $request->getCheck( 'assignKnownUsers' );
131
132 $this->logcomment = $request->getText( 'log-comment' );
133 $this->pageLinkDepth = $this->getConfig()->get( 'ExportMaxLinkDepth' ) == 0
134 ? 0
135 : $request->getIntOrNull( 'pagelink-depth' );
136
137 $this->mapping = $request->getVal( 'mapping' );
138 if ( $this->mapping === 'namespace' ) {
139 $this->namespace = $request->getIntOrNull( 'namespace' );
140 } elseif ( $this->mapping === 'subpage' ) {
141 $this->rootpage = $request->getText( 'rootpage' );
142 } else {
143 $this->mapping = 'default';
144 }
145
146 $user = $this->getUser();
147 if ( !$user->matchEditToken( $request->getVal( 'editToken' ) ) ) {
148 $source = Status::newFatal( 'import-token-mismatch' );
149 } elseif ( $this->sourceName === 'upload' ) {
150 $isUpload = true;
151 $this->usernamePrefix = $this->fullInterwikiPrefix = $request->getVal( 'usernamePrefix' );
152 if ( $this->permManager->userHasRight( $user, 'importupload' ) ) {
154 } else {
155 throw new PermissionsError( 'importupload' );
156 }
157 } elseif ( $this->sourceName === 'interwiki' ) {
158 if ( !$this->permManager->userHasRight( $user, 'import' ) ) {
159 throw new PermissionsError( 'import' );
160 }
161 $this->interwiki = $this->fullInterwikiPrefix = $request->getVal( 'interwiki' );
162 // does this interwiki have subprojects?
163 $hasSubprojects = array_key_exists( $this->interwiki, $this->importSources );
164 if ( !$hasSubprojects && !in_array( $this->interwiki, $this->importSources ) ) {
165 $source = Status::newFatal( "import-invalid-interwiki" );
166 } else {
167 if ( $hasSubprojects ) {
168 $this->subproject = $request->getVal( 'subproject' );
169 $this->fullInterwikiPrefix .= ':' . $request->getVal( 'subproject' );
170 }
171 if ( $hasSubprojects &&
172 !in_array( $this->subproject, $this->importSources[$this->interwiki] )
173 ) {
174 $source = Status::newFatal( "import-invalid-interwiki" );
175 } else {
176 $this->history = $request->getCheck( 'interwikiHistory' );
177 $this->frompage = $request->getText( "frompage" );
178 $this->includeTemplates = $request->getCheck( 'interwikiTemplates' );
180 $this->fullInterwikiPrefix,
181 $this->frompage,
182 $this->history,
183 $this->includeTemplates,
184 $this->pageLinkDepth );
185 }
186 }
187 } else {
188 $source = Status::newFatal( "importunknownsource" );
189 }
190
191 if ( (string)$this->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 = new WikiImporter( $source->value, $this->getConfig() );
203 if ( $this->namespace !== null ) {
204 $importer->setTargetNamespace( $this->namespace );
205 } elseif ( $this->rootpage !== null ) {
206 $statusRootPage = $importer->setTargetRootPage( $this->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( $this->fullInterwikiPrefix, $this->assignKnownUsers );
221
222 $out->addWikiMsg( "importstart" );
223
224 $reporter = new ImportReporter(
225 $importer,
226 $isUpload,
227 $this->fullInterwikiPrefix,
228 $this->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 $isSameSourceAsBefore = ( $this->sourceName === $sourceName );
263 $defaultNamespace = $this->getConfig()->get( 'ImportTargetNamespace' );
264 return "<tr>
265 <td>
266 </td>
267 <td class='mw-input'>" .
268 Xml::radioLabel(
269 $this->msg( 'import-mapping-default' )->text(),
270 'mapping',
271 'default',
272 // mw-import-mapping-interwiki-default, mw-import-mapping-upload-default
273 "mw-import-mapping-$sourceName-default",
274 ( $isSameSourceAsBefore ?
275 ( $this->mapping === 'default' ) :
276 $defaultNamespace === null )
277 ) .
278 "</td>
279 </tr>
280 <tr>
281 <td>
282 </td>
283 <td class='mw-input'>" .
284 Xml::radioLabel(
285 $this->msg( 'import-mapping-namespace' )->text(),
286 'mapping',
287 'namespace',
288 // mw-import-mapping-interwiki-namespace, mw-import-mapping-upload-namespace
289 "mw-import-mapping-$sourceName-namespace",
290 ( $isSameSourceAsBefore ?
291 ( $this->mapping === 'namespace' ) :
292 $defaultNamespace !== null )
293 ) . ' ' .
294 Html::namespaceSelector(
295 [
296 'selected' => ( $isSameSourceAsBefore ?
297 $this->namespace :
298 ( $defaultNamespace || '' ) ),
299 'in-user-lang' => true,
300 ], [
301 'name' => "namespace",
302 // mw-import-namespace-interwiki, mw-import-namespace-upload
303 'id' => "mw-import-namespace-$sourceName",
304 'class' => 'namespaceselector',
305 ]
306 ) .
307 "</td>
308 </tr>
309 <tr>
310 <td>
311 </td>
312 <td class='mw-input'>" .
313 Xml::radioLabel(
314 $this->msg( 'import-mapping-subpage' )->text(),
315 'mapping',
316 'subpage',
317 // mw-import-mapping-interwiki-subpage, mw-import-mapping-upload-subpage
318 "mw-import-mapping-$sourceName-subpage",
319 ( $isSameSourceAsBefore ? ( $this->mapping === 'subpage' ) : '' )
320 ) . ' ' .
321 Xml::input( 'rootpage', 50,
322 ( $isSameSourceAsBefore ? $this->rootpage : '' ),
323 [
324 // Should be "mw-import-rootpage-...", but we keep this inaccurate
325 // ID for legacy reasons
326 // mw-interwiki-rootpage-interwiki, mw-interwiki-rootpage-upload
327 'id' => "mw-interwiki-rootpage-$sourceName",
328 'type' => 'text'
329 ]
330 ) . ' ' .
331 "</td>
332 </tr>";
333 }
334
335 private function showForm() {
336 $action = $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] );
337 $user = $this->getUser();
338 $out = $this->getOutput();
339 $this->addHelpLink( 'https://meta.wikimedia.org/wiki/Special:MyLanguage/Help:Import', true );
340
341 if ( $this->permManager->userHasRight( $user, 'importupload' ) ) {
342 $mappingSelection = $this->getMappingFormPart( 'upload' );
343 $out->addHTML(
344 Xml::fieldset( $this->msg( 'import-upload' )->text() ) .
345 Xml::openElement(
346 'form',
347 [
348 'enctype' => 'multipart/form-data',
349 'method' => 'post',
350 'action' => $action,
351 'id' => 'mw-import-upload-form'
352 ]
353 ) .
354 $this->msg( 'importtext' )->parseAsBlock() .
355 Html::hidden( 'action', 'submit' ) .
356 Html::hidden( 'source', 'upload' ) .
357 Xml::openElement( 'table', [ 'id' => 'mw-import-table-upload' ] ) .
358 "<tr>
359 <td class='mw-label'>" .
360 Xml::label( $this->msg( 'import-upload-filename' )->text(), 'xmlimport' ) .
361 "</td>
362 <td class='mw-input'>" .
363 Html::input( 'xmlimport', '', 'file', [ 'id' => 'xmlimport' ] ) . ' ' .
364 "</td>
365 </tr>
366 <tr>
367 <td class='mw-label'>" .
368 Xml::label( $this->msg( 'import-upload-username-prefix' )->text(),
369 'mw-import-usernamePrefix' ) .
370 "</td>
371 <td class='mw-input'>" .
372 Xml::input( 'usernamePrefix', 50,
373 $this->usernamePrefix,
374 [ 'id' => 'usernamePrefix', 'type' => 'text' ] ) . ' ' .
375 "</td>
376 </tr>
377 <tr>
378 <td></td>
379 <td class='mw-input'>" .
380 Xml::checkLabel(
381 $this->msg( 'import-assign-known-users' )->text(),
382 'assignKnownUsers',
383 'assignKnownUsers',
384 $this->assignKnownUsers
385 ) .
386 "</td>
387 </tr>
388 <tr>
389 <td class='mw-label'>" .
390 Xml::label( $this->msg( 'import-comment' )->text(), 'mw-import-comment' ) .
391 "</td>
392 <td class='mw-input'>" .
393 Xml::input( 'log-comment', 50,
394 ( $this->sourceName === 'upload' ? $this->logcomment : '' ),
395 [ 'id' => 'mw-import-comment', 'type' => 'text' ] ) . ' ' .
396 "</td>
397 </tr>
398 $mappingSelection
399 <tr>
400 <td></td>
401 <td class='mw-submit'>" .
402 Xml::submitButton( $this->msg( 'uploadbtn' )->text() ) .
403 "</td>
404 </tr>" .
405 Xml::closeElement( 'table' ) .
406 Html::hidden( 'editToken', $user->getEditToken() ) .
407 Xml::closeElement( 'form' ) .
408 Xml::closeElement( 'fieldset' )
409 );
410 } elseif ( empty( $this->importSources ) ) {
411 $out->addWikiMsg( 'importnosources' );
412 }
413
414 if ( $this->permManager->userHasRight( $user, 'import' ) && !empty( $this->importSources ) ) {
415 # Show input field for import depth only if $wgExportMaxLinkDepth > 0
416 $importDepth = '';
417 if ( $this->getConfig()->get( 'ExportMaxLinkDepth' ) > 0 ) {
418 $importDepth = "<tr>
419 <td class='mw-label'>" .
420 $this->msg( 'export-pagelinks' )->parse() .
421 "</td>
422 <td class='mw-input'>" .
423 Xml::input( 'pagelink-depth', 3, 0 ) .
424 "</td>
425 </tr>";
426 }
427 $mappingSelection = $this->getMappingFormPart( 'interwiki' );
428
429 $out->addHTML(
430 Xml::fieldset( $this->msg( 'importinterwiki' )->text() ) .
431 Xml::openElement(
432 'form',
433 [
434 'method' => 'post',
435 'action' => $action,
436 'id' => 'mw-import-interwiki-form'
437 ]
438 ) .
439 $this->msg( 'import-interwiki-text' )->parseAsBlock() .
440 Html::hidden( 'action', 'submit' ) .
441 Html::hidden( 'source', 'interwiki' ) .
442 Html::hidden( 'editToken', $user->getEditToken() ) .
443 Xml::openElement( 'table', [ 'id' => 'mw-import-table-interwiki' ] ) .
444 "<tr>
445 <td class='mw-label'>" .
446 Xml::label( $this->msg( 'import-interwiki-sourcewiki' )->text(), 'interwiki' ) .
447 "</td>
448 <td class='mw-input'>" .
449 Xml::openElement(
450 'select',
451 [ 'name' => 'interwiki', 'id' => 'interwiki' ]
452 )
453 );
454
455 $needSubprojectField = false;
456 foreach ( $this->importSources as $key => $value ) {
457 if ( is_int( $key ) ) {
458 $key = $value;
459 } elseif ( $value !== $key ) {
460 $needSubprojectField = true;
461 }
462
463 $attribs = [
464 'value' => $key,
465 ];
466 if ( is_array( $value ) ) {
467 $attribs['data-subprojects'] = implode( ' ', $value );
468 }
469 if ( $this->interwiki === $key ) {
470 $attribs['selected'] = 'selected';
471 }
472 $out->addHTML( Html::element( 'option', $attribs, $key ) );
473 }
474
475 $out->addHTML(
476 Xml::closeElement( 'select' )
477 );
478
479 if ( $needSubprojectField ) {
480 $out->addHTML(
481 Xml::openElement(
482 'select',
483 [ 'name' => 'subproject', 'id' => 'subproject' ]
484 )
485 );
486
487 $subprojectsToAdd = [];
488 foreach ( $this->importSources as $key => $value ) {
489 if ( is_array( $value ) ) {
490 $subprojectsToAdd = array_merge( $subprojectsToAdd, $value );
491 }
492 }
493 $subprojectsToAdd = array_unique( $subprojectsToAdd );
494 sort( $subprojectsToAdd );
495 foreach ( $subprojectsToAdd as $subproject ) {
496 $out->addHTML( Xml::option( $subproject, $subproject, $this->subproject === $subproject ) );
497 }
498
499 $out->addHTML(
500 Xml::closeElement( 'select' )
501 );
502 }
503
504 $out->addHTML(
505 "</td>
506 </tr>
507 <tr>
508 <td class='mw-label'>" .
509 Xml::label( $this->msg( 'import-interwiki-sourcepage' )->text(), 'frompage' ) .
510 "</td>
511 <td class='mw-input'>" .
512 Xml::input( 'frompage', 50, $this->frompage, [ 'id' => 'frompage' ] ) .
513 "</td>
514 </tr>
515 <tr>
516 <td>
517 </td>
518 <td class='mw-input'>" .
519 Xml::checkLabel(
520 $this->msg( 'import-interwiki-history' )->text(),
521 'interwikiHistory',
522 'interwikiHistory',
523 $this->history
524 ) .
525 "</td>
526 </tr>
527 <tr>
528 <td>
529 </td>
530 <td class='mw-input'>" .
531 Xml::checkLabel(
532 $this->msg( 'import-interwiki-templates' )->text(),
533 'interwikiTemplates',
534 'interwikiTemplates',
535 $this->includeTemplates
536 ) .
537 "</td>
538 </tr>
539 <tr>
540 <td></td>
541 <td class='mw-input'>" .
542 Xml::checkLabel(
543 $this->msg( 'import-assign-known-users' )->text(),
544 'assignKnownUsers',
545 'interwikiAssignKnownUsers',
546 $this->assignKnownUsers
547 ) .
548 "</td>
549 </tr>
550 $importDepth
551 <tr>
552 <td class='mw-label'>" .
553 Xml::label( $this->msg( 'import-comment' )->text(), 'mw-interwiki-comment' ) .
554 "</td>
555 <td class='mw-input'>" .
556 Xml::input( 'log-comment', 50,
557 ( $this->sourceName === 'interwiki' ? $this->logcomment : '' ),
558 [ 'id' => 'mw-interwiki-comment', 'type' => 'text' ] ) . ' ' .
559 "</td>
560 </tr>
561 $mappingSelection
562 <tr>
563 <td>
564 </td>
565 <td class='mw-submit'>" .
566 Xml::submitButton(
567 $this->msg( 'import-interwiki-submit' )->text(),
569 ) .
570 "</td>
571 </tr>" .
572 Xml::closeElement( 'table' ) .
573 Xml::closeElement( 'form' ) .
574 Xml::closeElement( 'fieldset' )
575 );
576 }
577 }
578
579 protected function getGroupName() {
580 return 'pagetools';
581 }
582}
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")
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition Linker.php:2294
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)
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...
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