MediaWiki  master
SpecialImport.php
Go to the documentation of this file.
1 <?php
28 
34 class SpecialImport extends SpecialPage {
36  private $importSources;
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 }
SpecialImport\showForm
showForm()
Definition: SpecialImport.php:295
SpecialImport
MediaWiki page data importer.
Definition: SpecialImport.php:34
SpecialPage\getPageTitle
getPageTitle( $subpage=false)
Get a self-referential title object.
Definition: SpecialPage.php:744
wfMergeErrorArrays
wfMergeErrorArrays(... $args)
Merge arrays in the style of PermissionManager::getPermissionErrors, with duplicate removal e....
Definition: GlobalFunctions.php:163
SpecialPage\msg
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
Definition: SpecialPage.php:912
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
SpecialImport\$wikiImporterFactory
WikiImporterFactory $wikiImporterFactory
Definition: SpecialImport.php:42
SpecialImport\$permManager
PermissionManager $permManager
Definition: SpecialImport.php:39
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:790
SpecialImport\$importSources
array $importSources
Definition: SpecialImport.php:36
SpecialImport\__construct
__construct(PermissionManager $permManager, WikiImporterFactory $wikiImporterFactory)
Definition: SpecialImport.php:48
SpecialPage\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: SpecialPage.php:1018
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:32
ImportReporter
Reporting callback.
Definition: ImportReporter.php:29
SpecialPage\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: SpecialPage.php:948
SpecialImport\execute
execute( $par)
Execute.
Definition: SpecialImport.php:68
SpecialImport\doImport
doImport()
Do the actual import.
Definition: SpecialImport.php:119
SpecialPage\getHookRunner
getHookRunner()
Definition: SpecialPage.php:1095
SpecialPage\getConfig
getConfig()
Shortcut to get main config object.
Definition: SpecialPage.php:878
SpecialImport\getGroupName
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Definition: SpecialImport.php:463
SpecialPage\setHeaders
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
Definition: SpecialPage.php:618
SpecialPage\getUser
getUser()
Shortcut to get the User executing this instance.
Definition: SpecialPage.php:800
SpecialImport\doesWrites
doesWrites()
Indicates whether this special page may perform database writes.
Definition: SpecialImport.php:58
SpecialPage\getContext
getContext()
Gets the context this SpecialPage is executed in.
Definition: SpecialPage.php:764
ImportStreamSource\newFromInterwiki
static newFromInterwiki( $interwiki, $page, $history=false, $templates=false, $pageLinkDepth=0)
Definition: ImportStreamSource.php:149
MediaWiki\Permissions\PermissionManager
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Definition: PermissionManager.php:53
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:43
SpecialPage\getRequest
getRequest()
Get the WebRequest being used for this instance.
Definition: SpecialPage.php:780
$source
$source
Definition: mwdoc-filter.php:34
SpecialPage\checkReadOnly
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
Definition: SpecialPage.php:371
WikiImporterFactory
Factory service for WikiImporter instances.
Definition: WikiImporterFactory.php:33
HTMLForm\factory
static factory( $displayFormat,... $arguments)
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:326
SpecialPage\outputHeader
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Definition: SpecialPage.php:709
ImportStreamSource\newFromUpload
static newFromUpload( $fieldname="xmlimport")
Definition: ImportStreamSource.php:75
SpecialImport\getMappingFormPart
getMappingFormPart( $sourceName)
Definition: SpecialImport.php:261