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