Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 275 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
UploadForm | |
0.00% |
0 / 275 |
|
0.00% |
0 / 8 |
1560 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 48 |
|
0.00% |
0 / 1 |
90 | |||
getSourceSection | |
0.00% |
0 / 68 |
|
0.00% |
0 / 1 |
56 | |||
getExtensionsMessage | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
12 | |||
getDescriptionSection | |
0.00% |
0 / 79 |
|
0.00% |
0 / 1 |
132 | |||
getOptionsSection | |
0.00% |
0 / 30 |
|
0.00% |
0 / 1 |
30 | |||
show | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
addUploadJS | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
6 | |||
trySubmit | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | use MediaWiki\Context\IContextSource; |
22 | use MediaWiki\HookContainer\HookContainer; |
23 | use MediaWiki\HookContainer\HookRunner; |
24 | use MediaWiki\Html\Html; |
25 | use MediaWiki\HTMLForm\HTMLForm; |
26 | use MediaWiki\Linker\LinkRenderer; |
27 | use MediaWiki\MainConfigNames; |
28 | use MediaWiki\MediaWikiServices; |
29 | use MediaWiki\Specials\SpecialUpload; |
30 | use MediaWiki\Status\Status; |
31 | use MediaWiki\Title\NamespaceInfo; |
32 | use Wikimedia\RequestTimeout\TimeoutException; |
33 | |
34 | /** |
35 | * Sub class of HTMLForm that provides the form section of SpecialUpload |
36 | */ |
37 | class UploadForm extends HTMLForm { |
38 | protected $mWatch; |
39 | protected $mForReUpload; |
40 | protected $mSessionKey; |
41 | protected $mHideIgnoreWarning; |
42 | protected $mDestWarningAck; |
43 | protected $mDestFile; |
44 | |
45 | protected $mComment; |
46 | /** @var string raw html */ |
47 | protected $mTextTop; |
48 | /** @var string raw html */ |
49 | protected $mTextAfterSummary; |
50 | |
51 | protected $mSourceIds; |
52 | |
53 | protected $mMaxFileSize = []; |
54 | |
55 | /** @var array */ |
56 | protected $mMaxUploadSize = []; |
57 | |
58 | private LocalRepo $localRepo; |
59 | private Language $contentLanguage; |
60 | private NamespaceInfo $nsInfo; |
61 | private HookRunner $hookRunner; |
62 | |
63 | /** |
64 | * @param array $options |
65 | * @param IContextSource|null $context |
66 | * @param LinkRenderer|null $linkRenderer |
67 | * @param LocalRepo|null $localRepo |
68 | * @param Language|null $contentLanguage |
69 | * @param NamespaceInfo|null $nsInfo |
70 | * @param HookContainer|null $hookContainer |
71 | */ |
72 | public function __construct( |
73 | array $options = [], |
74 | IContextSource $context = null, |
75 | LinkRenderer $linkRenderer = null, |
76 | LocalRepo $localRepo = null, |
77 | Language $contentLanguage = null, |
78 | NamespaceInfo $nsInfo = null, |
79 | HookContainer $hookContainer = null |
80 | ) { |
81 | if ( $context instanceof IContextSource ) { |
82 | $this->setContext( $context ); |
83 | } |
84 | |
85 | $services = MediaWikiServices::getInstance(); |
86 | if ( !$linkRenderer ) { |
87 | $linkRenderer = $services->getLinkRenderer(); |
88 | } |
89 | if ( !$localRepo ) { |
90 | $localRepo = $services->getRepoGroup()->getLocalRepo(); |
91 | } |
92 | if ( !$contentLanguage ) { |
93 | $contentLanguage = $services->getContentLanguage(); |
94 | } |
95 | if ( !$nsInfo ) { |
96 | $nsInfo = $services->getNamespaceInfo(); |
97 | } |
98 | $this->localRepo = $localRepo; |
99 | $this->contentLanguage = $contentLanguage; |
100 | $this->nsInfo = $nsInfo; |
101 | $this->hookRunner = new HookRunner( $hookContainer ?? $services->getHookContainer() ); |
102 | |
103 | $this->mWatch = !empty( $options['watch'] ); |
104 | $this->mForReUpload = !empty( $options['forreupload'] ); |
105 | $this->mSessionKey = $options['sessionkey'] ?? ''; |
106 | $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] ); |
107 | $this->mDestWarningAck = !empty( $options['destwarningack'] ); |
108 | $this->mDestFile = $options['destfile'] ?? ''; |
109 | |
110 | $this->mComment = $options['description'] ?? ''; |
111 | |
112 | $this->mTextTop = $options['texttop'] ?? ''; |
113 | |
114 | $this->mTextAfterSummary = $options['textaftersummary'] ?? ''; |
115 | |
116 | $sourceDescriptor = $this->getSourceSection(); |
117 | $descriptor = $sourceDescriptor |
118 | + $this->getDescriptionSection() |
119 | + $this->getOptionsSection(); |
120 | |
121 | $this->hookRunner->onUploadFormInitDescriptor( $descriptor ); |
122 | parent::__construct( $descriptor, $this->getContext(), 'upload' ); |
123 | |
124 | # Add a link to edit MediaWiki:Licenses |
125 | if ( $this->getAuthority()->isAllowed( 'editinterface' ) ) { |
126 | $this->getOutput()->addModuleStyles( 'mediawiki.special' ); |
127 | $licensesLink = $linkRenderer->makeKnownLink( |
128 | $this->msg( 'licenses' )->inContentLanguage()->getTitle(), |
129 | $this->msg( 'licenses-edit' )->text(), |
130 | [], |
131 | [ 'action' => 'edit' ] |
132 | ); |
133 | $editLicenses = '<p class="mw-upload-editlicenses">' . $licensesLink . '</p>'; |
134 | $this->addFooterText( $editLicenses, 'description' ); |
135 | } |
136 | |
137 | # Set some form properties |
138 | $this->setSubmitTextMsg( 'uploadbtn' ); |
139 | $this->setSubmitName( 'wpUpload' ); |
140 | # Used message keys: 'accesskey-upload', 'tooltip-upload' |
141 | $this->setSubmitTooltip( 'upload' ); |
142 | $this->setId( 'mw-upload-form' ); |
143 | |
144 | # Build a list of IDs for javascript insertion |
145 | $this->mSourceIds = []; |
146 | foreach ( $sourceDescriptor as $field ) { |
147 | if ( !empty( $field['id'] ) ) { |
148 | $this->mSourceIds[] = $field['id']; |
149 | } |
150 | } |
151 | } |
152 | |
153 | /** |
154 | * Get the descriptor of the fieldset that contains the file source |
155 | * selection. The section is 'source' |
156 | * |
157 | * @return array Descriptor array |
158 | */ |
159 | protected function getSourceSection() { |
160 | if ( $this->mSessionKey ) { |
161 | return [ |
162 | 'SessionKey' => [ |
163 | 'type' => 'hidden', |
164 | 'default' => $this->mSessionKey, |
165 | ], |
166 | 'SourceType' => [ |
167 | 'type' => 'hidden', |
168 | 'default' => 'Stash', |
169 | ], |
170 | ]; |
171 | } |
172 | |
173 | $canUploadByUrl = UploadFromUrl::isEnabled() |
174 | && ( UploadFromUrl::isAllowed( $this->getAuthority() ) === true ) |
175 | && $this->getConfig()->get( MainConfigNames::CopyUploadsFromSpecialUpload ); |
176 | $radio = $canUploadByUrl; |
177 | $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); |
178 | |
179 | $descriptor = []; |
180 | if ( $this->mTextTop ) { |
181 | $descriptor['UploadFormTextTop'] = [ |
182 | 'type' => 'info', |
183 | 'section' => 'source', |
184 | 'default' => $this->mTextTop, |
185 | 'raw' => true, |
186 | ]; |
187 | } |
188 | |
189 | $this->mMaxUploadSize['file'] = min( |
190 | UploadBase::getMaxUploadSize( 'file' ), |
191 | UploadBase::getMaxPhpUploadSize() |
192 | ); |
193 | |
194 | $help = $this->msg( 'upload-maxfilesize' )->sizeParams( $this->mMaxUploadSize['file'] )->parse(); |
195 | |
196 | // If the user can also upload by URL, there are 2 different file size limits. |
197 | // This extra message helps stress which limit corresponds to what. |
198 | if ( $canUploadByUrl ) { |
199 | $help .= $this->msg( 'word-separator' )->escaped(); |
200 | $help .= $this->msg( 'upload_source_file' )->parse(); |
201 | } |
202 | |
203 | $descriptor['UploadFile'] = [ |
204 | 'class' => UploadSourceField::class, |
205 | 'section' => 'source', |
206 | 'type' => 'file', |
207 | 'id' => 'wpUploadFile', |
208 | 'radio-id' => 'wpSourceTypeFile', |
209 | 'label-message' => 'sourcefilename', |
210 | 'upload-type' => 'File', |
211 | 'radio' => &$radio, |
212 | 'help' => $help, |
213 | 'checked' => $selectedSourceType == 'file', |
214 | ]; |
215 | |
216 | if ( $canUploadByUrl ) { |
217 | $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' ); |
218 | $descriptor['UploadFileURL'] = [ |
219 | 'class' => UploadSourceField::class, |
220 | 'section' => 'source', |
221 | 'id' => 'wpUploadFileURL', |
222 | 'radio-id' => 'wpSourceTypeurl', |
223 | 'label-message' => 'sourceurl', |
224 | 'upload-type' => 'url', |
225 | 'radio' => &$radio, |
226 | 'help' => $this->msg( 'upload-maxfilesize' )->sizeParams( $this->mMaxUploadSize['url'] )->parse() . |
227 | $this->msg( 'word-separator' )->escaped() . |
228 | $this->msg( 'upload_source_url' )->parse(), |
229 | 'checked' => $selectedSourceType == 'url', |
230 | ]; |
231 | } |
232 | $this->hookRunner->onUploadFormSourceDescriptors( |
233 | $descriptor, $radio, $selectedSourceType ); |
234 | |
235 | $descriptor['Extensions'] = [ |
236 | 'type' => 'info', |
237 | 'section' => 'source', |
238 | 'default' => $this->getExtensionsMessage(), |
239 | 'raw' => true, |
240 | ]; |
241 | |
242 | return $descriptor; |
243 | } |
244 | |
245 | /** |
246 | * Get the messages indicating which extensions are preferred and prohibited. |
247 | * |
248 | * @return string HTML string containing the message |
249 | */ |
250 | protected function getExtensionsMessage() { |
251 | # Print a list of allowed file extensions, if so configured. We ignore |
252 | # MIME type here, it's incomprehensible to most people and too long. |
253 | $config = $this->getConfig(); |
254 | |
255 | if ( $config->get( MainConfigNames::CheckFileExtensions ) ) { |
256 | $fileExtensions = array_unique( $config->get( MainConfigNames::FileExtensions ) ); |
257 | if ( $config->get( MainConfigNames::StrictFileExtensions ) ) { |
258 | # Everything not permitted is banned |
259 | $extensionsList = |
260 | '<div id="mw-upload-permitted">' . |
261 | $this->msg( 'upload-permitted' ) |
262 | ->params( $this->getLanguage()->commaList( $fileExtensions ) ) |
263 | ->numParams( count( $fileExtensions ) ) |
264 | ->parseAsBlock() . |
265 | "</div>\n"; |
266 | } else { |
267 | # We have to list both preferred and prohibited |
268 | $prohibitedExtensions = |
269 | array_unique( $config->get( MainConfigNames::ProhibitedFileExtensions ) ); |
270 | $extensionsList = |
271 | '<div id="mw-upload-preferred">' . |
272 | $this->msg( 'upload-preferred' ) |
273 | ->params( $this->getLanguage()->commaList( $fileExtensions ) ) |
274 | ->numParams( count( $fileExtensions ) ) |
275 | ->parseAsBlock() . |
276 | "</div>\n" . |
277 | '<div id="mw-upload-prohibited">' . |
278 | $this->msg( 'upload-prohibited' ) |
279 | ->params( $this->getLanguage()->commaList( $prohibitedExtensions ) ) |
280 | ->numParams( count( $prohibitedExtensions ) ) |
281 | ->parseAsBlock() . |
282 | "</div>\n"; |
283 | } |
284 | } else { |
285 | # Everything is permitted. |
286 | $extensionsList = ''; |
287 | } |
288 | |
289 | return $extensionsList; |
290 | } |
291 | |
292 | /** |
293 | * Get the descriptor of the fieldset that contains the file description |
294 | * input. The section is 'description' |
295 | * |
296 | * @return array Descriptor array |
297 | */ |
298 | protected function getDescriptionSection() { |
299 | $config = $this->getConfig(); |
300 | if ( $this->mSessionKey ) { |
301 | $stash = $this->localRepo->getUploadStash( $this->getUser() ); |
302 | try { |
303 | $file = $stash->getFile( $this->mSessionKey ); |
304 | } catch ( TimeoutException $e ) { |
305 | throw $e; |
306 | } catch ( Exception $e ) { |
307 | $file = null; |
308 | } |
309 | if ( $file ) { |
310 | $mto = $file->transform( [ 'width' => 120 ] ); |
311 | if ( $mto ) { |
312 | $this->addHeaderText( |
313 | '<div class="thumb t' . |
314 | $this->contentLanguage->alignEnd() . '">' . |
315 | Html::element( 'img', [ |
316 | 'src' => $mto->getUrl(), |
317 | 'class' => 'thumbimage', |
318 | ] ) . '</div>', 'description' ); |
319 | } |
320 | } |
321 | } |
322 | |
323 | $descriptor = [ |
324 | 'DestFile' => [ |
325 | 'type' => 'text', |
326 | 'section' => 'description', |
327 | 'id' => 'wpDestFile', |
328 | 'label-message' => 'destfilename', |
329 | 'size' => 60, |
330 | 'default' => $this->mDestFile, |
331 | # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm |
332 | 'nodata' => strval( $this->mDestFile ) !== '', |
333 | ], |
334 | 'UploadDescription' => [ |
335 | 'type' => $this->mForReUpload |
336 | ? 'text' |
337 | : 'textarea', |
338 | 'section' => 'description', |
339 | 'id' => 'wpUploadDescription', |
340 | 'label-message' => $this->mForReUpload |
341 | ? 'filereuploadsummary' |
342 | : 'fileuploadsummary', |
343 | 'default' => $this->mComment, |
344 | ] |
345 | ]; |
346 | if ( $this->mTextAfterSummary ) { |
347 | $descriptor['UploadFormTextAfterSummary'] = [ |
348 | 'type' => 'info', |
349 | 'section' => 'description', |
350 | 'default' => $this->mTextAfterSummary, |
351 | 'raw' => true, |
352 | ]; |
353 | } |
354 | |
355 | $descriptor += [ |
356 | 'EditTools' => [ |
357 | 'type' => 'edittools', |
358 | 'section' => 'description', |
359 | 'message' => 'edittools-upload', |
360 | ] |
361 | ]; |
362 | |
363 | if ( $this->mForReUpload ) { |
364 | $descriptor['DestFile']['readonly'] = true; |
365 | $descriptor['UploadDescription']['size'] = 60; |
366 | } else { |
367 | $descriptor['License'] = [ |
368 | 'type' => 'select', |
369 | 'class' => Licenses::class, |
370 | 'section' => 'description', |
371 | 'id' => 'wpLicense', |
372 | 'label-message' => 'license', |
373 | ]; |
374 | $descriptor['UploadDescription']['rows'] = 8; |
375 | } |
376 | |
377 | if ( $config->get( MainConfigNames::UseCopyrightUpload ) ) { |
378 | $descriptor['UploadCopyStatus'] = [ |
379 | 'type' => 'text', |
380 | 'section' => 'description', |
381 | 'id' => 'wpUploadCopyStatus', |
382 | 'label-message' => 'filestatus', |
383 | ]; |
384 | $descriptor['UploadSource'] = [ |
385 | 'type' => 'text', |
386 | 'section' => 'description', |
387 | 'id' => 'wpUploadSource', |
388 | 'label-message' => 'filesource', |
389 | ]; |
390 | } |
391 | |
392 | return $descriptor; |
393 | } |
394 | |
395 | /** |
396 | * Get the descriptor of the fieldset that contains the upload options, |
397 | * such as "watch this file". The section is 'options' |
398 | * |
399 | * @return array Descriptor array |
400 | */ |
401 | protected function getOptionsSection() { |
402 | $user = $this->getUser(); |
403 | if ( $user->isRegistered() ) { |
404 | $descriptor = [ |
405 | 'Watchthis' => [ |
406 | 'type' => 'check', |
407 | 'id' => 'wpWatchthis', |
408 | 'label-message' => 'watchthisupload', |
409 | 'section' => 'options', |
410 | 'default' => $this->mWatch, |
411 | ] |
412 | ]; |
413 | } |
414 | if ( !$this->mHideIgnoreWarning ) { |
415 | $descriptor['IgnoreWarning'] = [ |
416 | 'type' => 'check', |
417 | 'id' => 'wpIgnoreWarning', |
418 | 'label-message' => 'ignorewarnings', |
419 | 'section' => 'options', |
420 | ]; |
421 | } |
422 | |
423 | $descriptor['DestFileWarningAck'] = [ |
424 | 'type' => 'hidden', |
425 | 'id' => 'wpDestFileWarningAck', |
426 | 'default' => $this->mDestWarningAck ? '1' : '', |
427 | ]; |
428 | |
429 | if ( $this->mForReUpload ) { |
430 | $descriptor['ForReUpload'] = [ |
431 | 'type' => 'hidden', |
432 | 'id' => 'wpForReUpload', |
433 | 'default' => '1', |
434 | ]; |
435 | } |
436 | |
437 | return $descriptor; |
438 | } |
439 | |
440 | /** |
441 | * Add the upload JS and show the form. |
442 | * @return bool|Status |
443 | */ |
444 | public function show() { |
445 | $this->addUploadJS(); |
446 | return parent::show(); |
447 | } |
448 | |
449 | /** |
450 | * Add upload JS to the OutputPage |
451 | */ |
452 | protected function addUploadJS() { |
453 | $config = $this->getConfig(); |
454 | |
455 | $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize(); |
456 | |
457 | $scriptVars = [ |
458 | 'wgAjaxLicensePreview' => $config->get( MainConfigNames::AjaxLicensePreview ), |
459 | 'wgUploadAutoFill' => !$this->mForReUpload && |
460 | // If we received mDestFile from the request, don't autofill |
461 | // the wpDestFile textbox |
462 | $this->mDestFile === '', |
463 | 'wgUploadSourceIds' => $this->mSourceIds, |
464 | 'wgCheckFileExtensions' => $config->get( MainConfigNames::CheckFileExtensions ), |
465 | 'wgStrictFileExtensions' => $config->get( MainConfigNames::StrictFileExtensions ), |
466 | 'wgFileExtensions' => |
467 | array_values( array_unique( $config->get( MainConfigNames::FileExtensions ) ) ), |
468 | 'wgMaxUploadSize' => $this->mMaxUploadSize, |
469 | 'wgFileCanRotate' => SpecialUpload::rotationEnabled(), |
470 | ]; |
471 | |
472 | $out = $this->getOutput(); |
473 | $out->addJsConfigVars( $scriptVars ); |
474 | |
475 | $out->addModules( [ |
476 | 'mediawiki.special.upload', // Extras for thumbnail and license preview. |
477 | ] ); |
478 | } |
479 | |
480 | /** |
481 | * Empty function; submission is handled elsewhere. |
482 | * |
483 | * @return bool False |
484 | */ |
485 | public function trySubmit() { |
486 | return false; |
487 | } |
488 | } |