Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
60.80% covered (warning)
60.80%
76 / 125
55.56% covered (warning)
55.56%
5 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiImport
61.29% covered (warning)
61.29%
76 / 124
55.56% covered (warning)
55.56%
5 / 9
82.20
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
53.09% covered (warning)
53.09%
43 / 81
0.00% covered (danger)
0.00%
0 / 1
56.27
 getAllowedImportSources
55.56% covered (warning)
55.56%
5 / 9
0.00% covered (danger)
0.00%
0 / 1
5.40
 mustBePosted
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isWriteMode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllowedParams
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
1
 needsToken
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2009 Roan Kattouw <roan.kattouw@gmail.com>
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Api;
10
11use Exception;
12use MediaWiki\ChangeTags\ChangeTags;
13use MediaWiki\Import\ImportStreamSource;
14use MediaWiki\Import\WikiImporterFactory;
15use MediaWiki\MainConfigNames;
16use MediaWiki\Permissions\PermissionManager;
17use Wikimedia\ParamValidator\ParamValidator;
18
19/**
20 * API module that imports an XML file like Special:Import does
21 *
22 * @ingroup API
23 */
24class ApiImport extends ApiBase {
25
26    public function __construct(
27        ApiMain $main,
28        string $action,
29        private readonly WikiImporterFactory $wikiImporterFactory,
30        private readonly PermissionManager $permissionManager,
31    ) {
32        parent::__construct( $main, $action );
33    }
34
35    public function execute() {
36        $this->useTransactionalTimeLimit();
37        $params = $this->extractRequestParams();
38
39        $this->requireMaxOneParameter( $params, 'namespace', 'rootpage' );
40
41        $isUpload = false;
42        if ( isset( $params['interwikisource'] ) ) {
43            if ( !$this->getAuthority()->isAllowed( 'import' ) ) {
44                $this->dieWithError( 'apierror-cantimport' );
45            }
46            if ( !isset( $params['interwikipage'] ) ) {
47                $this->dieWithError( [ 'apierror-missingparam', 'interwikipage' ] );
48            }
49            $source = ImportStreamSource::newFromInterwiki(
50                $params['interwikisource'],
51                $params['interwikipage'],
52                $params['fullhistory'],
53                $params['templates']
54            );
55            $usernamePrefix = $params['interwikisource'];
56        } else {
57            $isUpload = true;
58            if ( !$this->getAuthority()->isAllowed( 'importupload' ) ) {
59                $this->dieWithError( 'apierror-cantimport-upload' );
60            }
61            $source = ImportStreamSource::newFromUpload( 'xml' );
62            $usernamePrefix = (string)$params['interwikiprefix'];
63            if ( $usernamePrefix === '' ) {
64                $encParamName = $this->encodeParamName( 'interwikiprefix' );
65                $this->dieWithError( [ 'apierror-missingparam', $encParamName ] );
66            }
67        }
68        if ( !$source->isOK() ) {
69            $this->dieStatus( $source );
70        }
71
72        // Check if user can add the log entry tags which were requested
73        if ( $params['tags'] ) {
74            $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $this->getAuthority() );
75            if ( !$ableToTag->isOK() ) {
76                $this->dieStatus( $ableToTag );
77            }
78        }
79
80        $importer = $this->wikiImporterFactory->getWikiImporter( $source->value, $this->getAuthority() );
81        if ( isset( $params['namespace'] ) ) {
82            $importer->setTargetNamespace( $params['namespace'] );
83        } elseif ( isset( $params['rootpage'] ) ) {
84            $statusRootPage = $importer->setTargetRootPage( $params['rootpage'] );
85            if ( !$statusRootPage->isGood() ) {
86                $this->dieStatus( $statusRootPage );
87            }
88        }
89        $importer->setUsernamePrefix( $usernamePrefix, $params['assignknownusers'] );
90
91        // T432713#12228661 - temporarily add noratelimit for import
92        $scopedNoRateLimit = $this->permissionManager->addTemporaryUserRights(
93            $this->getUser(), 'noratelimit'
94        );
95
96        $importer->setAuthorizeOnly( true );
97        $importer->doImport();
98        if ( $importer->getReauthOperation() !== null ) {
99            $this->dieWithError(
100                [ 'badaccess-cannotreauthenticate', $importer->getReauthOperation() ],
101                'reauthenticate'
102            );
103        }
104
105        // Re-open the source; the pre-pass consumed the stream
106        $source = $isUpload
107            ? ImportStreamSource::newFromUpload( 'xml' )
108            : ImportStreamSource::newFromInterwiki(
109                $params['interwikisource'],
110                $params['interwikipage'],
111                $params['fullhistory'],
112                $params['templates']
113            );
114        if ( !$source->isOK() ) {
115            $this->dieStatus( $source );
116        }
117        $importer = $this->wikiImporterFactory->getWikiImporter( $source->value, $this->getAuthority() );
118        if ( isset( $params['namespace'] ) ) {
119            $importer->setTargetNamespace( $params['namespace'] );
120        } elseif ( isset( $params['rootpage'] ) ) {
121            $importer->setTargetRootPage( $params['rootpage'] );
122        }
123        $importer->setUsernamePrefix( $usernamePrefix, $params['assignknownusers'] );
124        $importer->setAuthorizeOnly( false );
125
126        $reporter = new ApiImportReporter(
127            $importer,
128            $isUpload,
129            $params['interwikisource'],
130            $params['summary'],
131            $this
132        );
133        if ( $params['tags'] ) {
134            $reporter->setChangeTags( $params['tags'] );
135        }
136
137        try {
138            $importer->doImport();
139        } catch ( Exception $e ) {
140            $this->dieWithException( $e, [ 'wrap' => 'apierror-import-unknownerror' ] );
141        }
142
143        $resultData = $reporter->getData();
144        $result = $this->getResult();
145        ApiResult::setIndexedTagName( $resultData, 'page' );
146        $result->addValue( null, $this->getModuleName(), $resultData );
147    }
148
149    /**
150     * Returns a list of interwiki prefixes corresponding to each defined import
151     * source.
152     *
153     * @return array
154     * @since 1.27
155     */
156    public function getAllowedImportSources() {
157        $importSources = $this->getConfig()->get( MainConfigNames::ImportSources );
158        $this->getHookRunner()->onImportSources( $importSources );
159
160        $result = [];
161        foreach ( $importSources as $key => $value ) {
162            if ( is_int( $key ) ) {
163                $result[] = $value;
164            } else {
165                foreach ( $value as $subproject ) {
166                    $result[] = "$key:$subproject";
167                }
168            }
169        }
170        return $result;
171    }
172
173    /** @inheritDoc */
174    public function mustBePosted() {
175        return true;
176    }
177
178    /** @inheritDoc */
179    public function isWriteMode() {
180        return true;
181    }
182
183    /** @inheritDoc */
184    public function getAllowedParams() {
185        return [
186            'summary' => null,
187            'xml' => [
188                ParamValidator::PARAM_TYPE => 'upload',
189            ],
190            'interwikiprefix' => [
191                ParamValidator::PARAM_TYPE => 'string',
192            ],
193            'interwikisource' => [
194                ParamValidator::PARAM_TYPE => $this->getAllowedImportSources(),
195            ],
196            'interwikipage' => null,
197            'fullhistory' => false,
198            'templates' => false,
199            'namespace' => [
200                ParamValidator::PARAM_TYPE => 'namespace'
201            ],
202            'assignknownusers' => false,
203            'rootpage' => null,
204            'tags' => [
205                ParamValidator::PARAM_TYPE => 'tags',
206                ParamValidator::PARAM_ISMULTI => true,
207            ],
208        ];
209    }
210
211    /** @inheritDoc */
212    public function needsToken() {
213        return 'csrf';
214    }
215
216    /** @inheritDoc */
217    protected function getExamplesMessages() {
218        return [
219            'action=import&interwikisource=meta&interwikipage=Help:ParserFunctions&' .
220                'namespace=100&fullhistory=&token=123ABC'
221                => 'apihelp-import-example-import',
222        ];
223    }
224
225    /** @inheritDoc */
226    public function getHelpUrls() {
227        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Import';
228    }
229}
230
231/** @deprecated class alias since 1.43 */
232class_alias( ApiImport::class, 'ApiImport' );