Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
3.01% |
5 / 166 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
SpecialUploadWizard | |
3.01% |
5 / 166 |
|
0.00% |
0 / 9 |
2239.51 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 44 |
|
0.00% |
0 / 1 |
156 | |||
handleCampaign | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
42 | |||
displayError | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
addJsVars | |
0.00% |
0 / 59 |
|
0.00% |
0 / 1 |
240 | |||
isUploadAllowed | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
isUserUploadAllowed | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
getWizardHtml | |
0.00% |
0 / 34 |
|
0.00% |
0 / 1 |
72 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\UploadWizard\Specials; |
4 | |
5 | use BitmapHandler; |
6 | use ChangeTags; |
7 | use DerivativeContext; |
8 | use LogicException; |
9 | use MediaWiki\Extension\UploadWizard\Campaign; |
10 | use MediaWiki\Extension\UploadWizard\Config; |
11 | use MediaWiki\Extension\UploadWizard\Hooks; |
12 | use MediaWiki\Extension\UploadWizard\Tutorial; |
13 | use MediaWiki\Html\Html; |
14 | use MediaWiki\Request\WebRequest; |
15 | use MediaWiki\SpecialPage\SpecialPage; |
16 | use MediaWiki\Title\Title; |
17 | use MediaWiki\User\Options\UserOptionsLookup; |
18 | use MediaWiki\User\User; |
19 | use PermissionsError; |
20 | use UploadBase; |
21 | use UploadFromUrl; |
22 | use UserBlockedError; |
23 | |
24 | /** |
25 | * Special:UploadWizard |
26 | * |
27 | * Easy to use multi-file upload page. |
28 | * |
29 | * @file |
30 | * @ingroup SpecialPage |
31 | * @ingroup Upload |
32 | */ |
33 | |
34 | class SpecialUploadWizard extends SpecialPage { |
35 | /** |
36 | * The name of the upload wizard campaign, or null when none is specified. |
37 | * |
38 | * @since 1.2 |
39 | * @var string|null |
40 | */ |
41 | protected $campaign = null; |
42 | |
43 | /** @var UserOptionsLookup */ |
44 | private $userOptionsLookup; |
45 | |
46 | /** |
47 | * @param UserOptionsLookup $userOptionsLookup |
48 | * @param WebRequest|null $request the request (usually wgRequest) |
49 | * @param string|null $par everything in the URL after Special:UploadWizard. |
50 | * Not sure what we can use it for |
51 | */ |
52 | public function __construct( UserOptionsLookup $userOptionsLookup, $request = null, $par = null ) { |
53 | $this->userOptionsLookup = $userOptionsLookup; |
54 | parent::__construct( 'UploadWizard', 'upload' ); |
55 | } |
56 | |
57 | /** |
58 | * Replaces default execute method |
59 | * Checks whether uploading enabled, user permissions okay, |
60 | * @param string|null $subPage subpage, e.g. the "foo" in Special:UploadWizard/foo. |
61 | */ |
62 | public function execute( $subPage ) { |
63 | // side effects: if we can't upload, will print error page to wgOut |
64 | // and return false |
65 | if ( !( $this->isUploadAllowed() && $this->isUserUploadAllowed( $this->getUser() ) ) ) { |
66 | return; |
67 | } |
68 | |
69 | $this->setHeaders(); |
70 | $this->outputHeader(); |
71 | |
72 | $req = $this->getRequest(); |
73 | |
74 | $urlArgs = [ 'caption', 'description', 'lat', 'lon', 'alt' ]; |
75 | |
76 | $urlDefaults = []; |
77 | foreach ( $urlArgs as $arg ) { |
78 | $value = $req->getText( $arg ); |
79 | if ( $value ) { |
80 | $urlDefaults[$arg] = $value; |
81 | } |
82 | } |
83 | |
84 | $categories = $req->getText( 'categories' ); |
85 | if ( $categories ) { |
86 | $urlDefaults['categories'] = explode( '|', $categories ); |
87 | } |
88 | |
89 | $urlDefaults['objref'] = $req->getText( 'objref' ) ?: ''; |
90 | $urlDefaults['updateList'] = $req->getText( 'updateList' ) ?: ''; |
91 | |
92 | Config::setUrlSetting( 'defaults', $urlDefaults ); |
93 | |
94 | $fields = $req->getArray( 'fields' ); |
95 | $fieldDefaults = []; |
96 | |
97 | # Support id and id2 for field0 and field1 |
98 | # Legacy support for old URL structure. They override fields[] |
99 | if ( $req->getText( 'id' ) ) { |
100 | $fields[0] = $req->getText( 'id' ); |
101 | } |
102 | |
103 | if ( $req->getText( 'id2' ) ) { |
104 | $fields[1] = $req->getText( 'id2' ); |
105 | } |
106 | |
107 | if ( $fields ) { |
108 | foreach ( $fields as $index => $value ) { |
109 | $fieldDefaults[$index]['initialValue'] = $value; |
110 | } |
111 | } |
112 | |
113 | Config::setUrlSetting( 'fields', $fieldDefaults ); |
114 | |
115 | $this->handleCampaign(); |
116 | |
117 | $out = $this->getOutput(); |
118 | |
119 | // fallback for non-JS |
120 | $out->addHTML( '<div class="mwe-upwiz-unavailable">' ); |
121 | $out->addHTML( |
122 | Html::errorBox( |
123 | $this->msg( 'mwe-upwiz-unavailable' )->parse() |
124 | ) |
125 | ); |
126 | // create a simple form for non-JS fallback, which targets the old Special:Upload page. |
127 | // at some point, if we completely subsume its functionality, change that to point here again, |
128 | // but then we'll need to process non-JS uploads in the same way Special:Upload does. |
129 | $derivativeContext = new DerivativeContext( $this->getContext() ); |
130 | $derivativeContext->setTitle( SpecialPage::getTitleFor( 'Upload' ) ); |
131 | $simpleForm = new UploadWizardSimpleForm( [], $derivativeContext, $this->getLinkRenderer() ); |
132 | $simpleForm->show(); |
133 | $out->addHTML( '</div>' ); |
134 | |
135 | // global javascript variables |
136 | $this->addJsVars( $subPage ); |
137 | |
138 | // dependencies (css, js) |
139 | $out->addModules( 'ext.uploadWizard.page' ); |
140 | // load spinner styles early |
141 | $out->addModuleStyles( [ 'ext.uploadWizard.page.styles', 'jquery.spinner.styles' ] ); |
142 | |
143 | // where the uploadwizard will go |
144 | // TODO import more from UploadWizard's createInterface call. |
145 | $out->addHTML( $this->getWizardHtml() ); |
146 | } |
147 | |
148 | /** |
149 | * Handles the campaign parameter. |
150 | * |
151 | * @since 1.2 |
152 | */ |
153 | protected function handleCampaign() { |
154 | $campaignName = $this->getRequest()->getVal( 'campaign' ); |
155 | if ( $campaignName === null ) { |
156 | $campaignName = Config::getSetting( 'defaultCampaign' ); |
157 | } |
158 | |
159 | if ( $campaignName !== null && $campaignName !== '' ) { |
160 | $campaign = Campaign::newFromName( $campaignName ); |
161 | |
162 | if ( $campaign === false ) { |
163 | $this->displayError( $this->msg( 'mwe-upwiz-error-nosuchcampaign', $campaignName )->text() ); |
164 | } elseif ( $campaign->getIsEnabled() ) { |
165 | $this->campaign = $campaignName; |
166 | } else { |
167 | $this->displayError( $this->msg( 'mwe-upwiz-error-campaigndisabled', $campaignName )->text() ); |
168 | } |
169 | } |
170 | } |
171 | |
172 | /** |
173 | * Display an error message. |
174 | * |
175 | * @since 1.2 |
176 | * |
177 | * @param string $message |
178 | */ |
179 | protected function displayError( $message ) { |
180 | $this->getOutput()->addHTML( |
181 | Html::errorBox( |
182 | $message |
183 | ) |
184 | ); |
185 | } |
186 | |
187 | /** |
188 | * Adds some global variables for our use, as well as initializes the UploadWizard |
189 | * |
190 | * TODO once bug https://bugzilla.wikimedia.org/show_bug.cgi?id=26901 |
191 | * is fixed we should package configuration with the upload wizard instead of |
192 | * in uploadWizard output page. |
193 | * |
194 | * @param string $subPage subpage, e.g. the "foo" in Special:UploadWizard/foo |
195 | */ |
196 | public function addJsVars( $subPage ) { |
197 | $config = Config::getConfig( $this->campaign ); |
198 | |
199 | if ( array_key_exists( 'trackingCategory', $config ) ) { |
200 | if ( array_key_exists( 'campaign', $config['trackingCategory'] ) ) { |
201 | if ( $this->campaign !== null ) { |
202 | $config['trackingCategory']['campaign'] = str_replace( |
203 | '$1', |
204 | $this->campaign, |
205 | $config['trackingCategory']['campaign'] |
206 | ); |
207 | } else { |
208 | unset( $config['trackingCategory']['campaign'] ); |
209 | } |
210 | } |
211 | } |
212 | // UploadFromUrl parameter set to true only if the user is allowed to upload a file |
213 | // from a URL which we need to check in our Javascript implementation. |
214 | if ( UploadFromUrl::isEnabled() && UploadFromUrl::isAllowed( $this->getUser() ) === true ) { |
215 | $config['UploadFromUrl'] = true; |
216 | } else { |
217 | $config['UploadFromUrl'] = false; |
218 | } |
219 | |
220 | // Get the user's default license. This will usually be 'default', but |
221 | // can be a specific license like 'ownwork-cc-zero'. |
222 | $userDefaultLicense = $this->userOptionsLookup->getOption( $this->getUser(), 'upwiz_deflicense' ); |
223 | |
224 | if ( $userDefaultLicense !== 'default' ) { |
225 | [ $userLicenseType, $userDefaultLicense ] = explode( '-', $userDefaultLicense, 2 ); |
226 | |
227 | // Determine if the user's default license is valid for this campaign |
228 | switch ( $config['licensing']['ownWorkDefault'] ) { |
229 | case "own": |
230 | $defaultInAllowedLicenses = in_array( |
231 | $userDefaultLicense, $config['licensing']['ownWork']['licenses'] |
232 | ); |
233 | break; |
234 | case "notown": |
235 | $defaultInAllowedLicenses = in_array( |
236 | $userDefaultLicense, Config::getThirdPartyLicenses() |
237 | ); |
238 | break; |
239 | case "choice": |
240 | $defaultInAllowedLicenses = ( in_array( |
241 | $userDefaultLicense, $config['licensing']['ownWork']['licenses'] |
242 | ) || |
243 | in_array( $userDefaultLicense, Config::getThirdPartyLicenses() ) ); |
244 | break; |
245 | default: |
246 | throw new LogicException( 'Bad ownWorkDefault config' ); |
247 | } |
248 | |
249 | if ( $defaultInAllowedLicenses ) { |
250 | if ( $userLicenseType === 'ownwork' ) { |
251 | $userLicenseGroup = 'ownWork'; |
252 | } else { |
253 | $userLicenseGroup = 'thirdParty'; |
254 | } |
255 | $config['licensing'][$userLicenseGroup]['defaults'] = [ $userDefaultLicense ]; |
256 | $config['licensing']['defaultType'] = $userLicenseType; |
257 | |
258 | if ( $userDefaultLicense === 'custom' ) { |
259 | $config['licenses']['custom']['defaultText'] = |
260 | $this->userOptionsLookup->getOption( $this->getUser(), 'upwiz_deflicense_custom' ); |
261 | } |
262 | } |
263 | } |
264 | |
265 | // add an 'uploadwizard' tag, but only if it'll be allowed |
266 | Hooks::addListDefinedTags( $tags ); |
267 | $status = ChangeTags::canAddTagsAccompanyingChange( $tags, $this->getUser() ); |
268 | $config['CanAddTags'] = $status->isOK(); |
269 | |
270 | // Upload comment should be localized with respect to the wiki's language |
271 | $config['uploadComment'] = [ |
272 | 'ownWork' => $this->msg( 'mwe-upwiz-upload-comment-own-work' ) |
273 | ->inContentLanguage()->plain(), |
274 | 'thirdParty' => $this->msg( 'mwe-upwiz-upload-comment-third-party' ) |
275 | ->inContentLanguage()->plain() |
276 | ]; |
277 | |
278 | $bitmapHandler = new BitmapHandler(); |
279 | $this->getOutput()->addJsConfigVars( |
280 | [ |
281 | 'UploadWizardConfig' => $config, |
282 | 'wgFileCanRotate' => $bitmapHandler->canRotate(), |
283 | ] |
284 | ); |
285 | } |
286 | |
287 | /** |
288 | * Check if anyone can upload (or if other sitewide config prevents this) |
289 | * Side effect: will print error page to wgOut if cannot upload. |
290 | * @return bool true if can upload |
291 | */ |
292 | private function isUploadAllowed() { |
293 | // Check uploading enabled |
294 | if ( !UploadBase::isEnabled() ) { |
295 | $this->getOutput()->showErrorPage( 'uploaddisabled', 'uploaddisabledtext' ); |
296 | return false; |
297 | } |
298 | |
299 | // Check whether we actually want to allow changing stuff |
300 | $this->checkReadOnly(); |
301 | |
302 | // we got all the way here, so it must be okay to upload |
303 | return true; |
304 | } |
305 | |
306 | /** |
307 | * Check if the user can upload |
308 | * Side effect: will print error page to wgOut if cannot upload. |
309 | * @param User $user |
310 | * @throws PermissionsError |
311 | * @throws UserBlockedError |
312 | * @return bool true if can upload |
313 | */ |
314 | private function isUserUploadAllowed( User $user ) { |
315 | // Check permissions |
316 | $permissionRequired = UploadBase::isAllowed( $user ); |
317 | if ( $permissionRequired !== true ) { |
318 | throw new PermissionsError( $permissionRequired ); |
319 | } |
320 | |
321 | // Check blocks |
322 | if ( $user->isBlockedFromUpload() ) { |
323 | // If the user is blocked from uploading then there is a block |
324 | // @phan-suppress-next-line PhanTypeMismatchArgumentNullable |
325 | throw new UserBlockedError( $user->getBlock() ); |
326 | } |
327 | |
328 | // we got all the way here, so it must be okay to upload |
329 | return true; |
330 | } |
331 | |
332 | /** |
333 | * Return the basic HTML structure for the entire page |
334 | * Will be enhanced by the javascript to actually do stuff |
335 | * @return string html |
336 | */ |
337 | protected function getWizardHtml() { |
338 | $config = Config::getConfig( $this->campaign ); |
339 | |
340 | if ( array_key_exists( |
341 | 'display', $config ) && array_key_exists( 'headerLabel', $config['display'] ) |
342 | ) { |
343 | $this->getOutput()->addHTML( $config['display']['headerLabel'] ); |
344 | } |
345 | |
346 | if ( array_key_exists( 'fallbackToAltUploadForm', $config ) |
347 | && array_key_exists( 'altUploadForm', $config ) |
348 | && $config['altUploadForm'] != '' |
349 | && $config[ 'fallbackToAltUploadForm' ] |
350 | ) { |
351 | $linkHtml = ''; |
352 | $altUploadForm = Title::newFromText( $config[ 'altUploadForm' ] ); |
353 | if ( $altUploadForm instanceof Title ) { |
354 | $linkHtml = Html::rawElement( 'p', [ 'style' => 'text-align: center;' ], |
355 | Html::element( 'a', [ 'href' => $altUploadForm->getLocalURL() ], |
356 | $config['altUploadForm'] |
357 | ) |
358 | ); |
359 | } |
360 | |
361 | return Html::rawElement( |
362 | 'div', |
363 | [], |
364 | Html::element( |
365 | 'p', |
366 | [ 'style' => 'text-align: center' ], |
367 | $this->msg( 'mwe-upwiz-extension-disabled' )->text() |
368 | ) . $linkHtml |
369 | ); |
370 | } |
371 | |
372 | // always load the html: even if the tutorial is skipped, users can |
373 | // still move back to view it |
374 | $tutorialHtml = Tutorial::getHtml( $this->campaign ); |
375 | |
376 | // TODO move this into UploadWizard.js or some other javascript resource so the upload wizard |
377 | // can be dynamically included ( for example the add media wizard ) |
378 | return '<div id="upload-wizard" class="upload-section">' . |
379 | '<div id="mwe-upwiz-tutorial-html" style="display:none;">' . |
380 | $tutorialHtml . |
381 | '</div>' . |
382 | '<div class="mwe-first-spinner">' . |
383 | new \MediaWiki\Widget\SpinnerWidget( [ 'size' => 'large' ] ) . |
384 | '</div>' . |
385 | '</div>'; |
386 | } |
387 | |
388 | /** |
389 | * @inheritDoc |
390 | */ |
391 | protected function getGroupName() { |
392 | return 'media'; |
393 | } |
394 | } |