MediaWiki master
UploadForm.php
Go to the documentation of this file.
1<?php
32use Wikimedia\RequestTimeout\TimeoutException;
33
37class UploadForm extends HTMLForm {
39 protected $mWatch;
41 protected $mForReUpload;
43 protected $mSessionKey;
49 protected $mDestFile;
50
52 protected $mComment;
54 protected $mTextTop;
57
59 protected $mSourceIds;
60
62 protected $mMaxUploadSize = [];
63
64 private LocalRepo $localRepo;
65 private Language $contentLanguage;
66 private NamespaceInfo $nsInfo;
67 private HookRunner $hookRunner;
68
78 public function __construct(
79 array $options = [],
80 IContextSource $context = null,
81 LinkRenderer $linkRenderer = null,
82 LocalRepo $localRepo = null,
83 Language $contentLanguage = null,
84 NamespaceInfo $nsInfo = null,
85 HookContainer $hookContainer = null
86 ) {
87 if ( $context instanceof IContextSource ) {
88 $this->setContext( $context );
89 }
90
91 $services = MediaWikiServices::getInstance();
92 if ( !$linkRenderer ) {
93 $linkRenderer = $services->getLinkRenderer();
94 }
95 if ( !$localRepo ) {
96 $localRepo = $services->getRepoGroup()->getLocalRepo();
97 }
98 if ( !$contentLanguage ) {
99 $contentLanguage = $services->getContentLanguage();
100 }
101 if ( !$nsInfo ) {
102 $nsInfo = $services->getNamespaceInfo();
103 }
104 $this->localRepo = $localRepo;
105 $this->contentLanguage = $contentLanguage;
106 $this->nsInfo = $nsInfo;
107 $this->hookRunner = new HookRunner( $hookContainer ?? $services->getHookContainer() );
108
109 $this->mWatch = !empty( $options['watch'] );
110 $this->mForReUpload = !empty( $options['forreupload'] );
111 $this->mSessionKey = $options['sessionkey'] ?? '';
112 $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] );
113 $this->mDestWarningAck = !empty( $options['destwarningack'] );
114 $this->mDestFile = $options['destfile'] ?? '';
115
116 $this->mComment = $options['description'] ?? '';
117
118 $this->mTextTop = $options['texttop'] ?? '';
119
120 $this->mTextAfterSummary = $options['textaftersummary'] ?? '';
121
122 $sourceDescriptor = $this->getSourceSection();
123 $descriptor = $sourceDescriptor
124 + $this->getDescriptionSection()
125 + $this->getOptionsSection();
126
127 $this->hookRunner->onUploadFormInitDescriptor( $descriptor );
128 parent::__construct( $descriptor, $this->getContext(), 'upload' );
129
130 # Add a link to edit MediaWiki:Licenses
131 if ( $this->getAuthority()->isAllowed( 'editinterface' ) ) {
132 $this->getOutput()->addModuleStyles( 'mediawiki.special' );
133 $licensesLink = $linkRenderer->makeKnownLink(
134 $this->msg( 'licenses' )->inContentLanguage()->getTitle(),
135 $this->msg( 'licenses-edit' )->text(),
136 [],
137 [ 'action' => 'edit' ]
138 );
139 $editLicenses = '<p class="mw-upload-editlicenses">' . $licensesLink . '</p>';
140 $this->addFooterHtml( $editLicenses, 'description' );
141 }
142
143 # Set some form properties
144 $this->setSubmitTextMsg( 'uploadbtn' );
145 $this->setSubmitName( 'wpUpload' );
146 # Used message keys: 'accesskey-upload', 'tooltip-upload'
147 $this->setSubmitTooltip( 'upload' );
148 $this->setId( 'mw-upload-form' );
149
150 # Build a list of IDs for javascript insertion
151 $this->mSourceIds = [];
152 foreach ( $sourceDescriptor as $field ) {
153 if ( !empty( $field['id'] ) ) {
154 $this->mSourceIds[] = $field['id'];
155 }
156 }
157 }
158
165 protected function getSourceSection() {
166 if ( $this->mSessionKey ) {
167 return [
168 'SessionKey' => [
169 'type' => 'hidden',
170 'default' => $this->mSessionKey,
171 ],
172 'SourceType' => [
173 'type' => 'hidden',
174 'default' => 'Stash',
175 ],
176 ];
177 }
178
179 $canUploadByUrl = UploadFromUrl::isEnabled()
180 && ( UploadFromUrl::isAllowed( $this->getAuthority() ) === true )
181 && $this->getConfig()->get( MainConfigNames::CopyUploadsFromSpecialUpload );
182 $radio = $canUploadByUrl;
183 $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) );
184
185 $descriptor = [];
186 if ( $this->mTextTop ) {
187 $descriptor['UploadFormTextTop'] = [
188 'type' => 'info',
189 'section' => 'source',
190 'default' => $this->mTextTop,
191 'raw' => true,
192 ];
193 }
194
195 $this->mMaxUploadSize['file'] = min(
196 UploadBase::getMaxUploadSize( 'file' ),
197 UploadBase::getMaxPhpUploadSize()
198 );
199
200 $help = $this->msg( 'upload-maxfilesize' )->sizeParams( $this->mMaxUploadSize['file'] )->parse();
201
202 // If the user can also upload by URL, there are 2 different file size limits.
203 // This extra message helps stress which limit corresponds to what.
204 if ( $canUploadByUrl ) {
205 $help .= $this->msg( 'word-separator' )->escaped();
206 $help .= $this->msg( 'upload_source_file' )->parse();
207 }
208
209 $descriptor['UploadFile'] = [
210 'class' => UploadSourceField::class,
211 'section' => 'source',
212 'type' => 'file',
213 'id' => 'wpUploadFile',
214 'radio-id' => 'wpSourceTypeFile',
215 'label-message' => 'sourcefilename',
216 'upload-type' => 'File',
217 'radio' => &$radio,
218 'help' => $help,
219 'checked' => $selectedSourceType == 'file',
220 ];
221
222 if ( $canUploadByUrl ) {
223 $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' );
224 $descriptor['UploadFileURL'] = [
225 'class' => UploadSourceField::class,
226 'section' => 'source',
227 'id' => 'wpUploadFileURL',
228 'radio-id' => 'wpSourceTypeurl',
229 'label-message' => 'sourceurl',
230 'upload-type' => 'url',
231 'radio' => &$radio,
232 'help' => $this->msg( 'upload-maxfilesize' )->sizeParams( $this->mMaxUploadSize['url'] )->parse() .
233 $this->msg( 'word-separator' )->escaped() .
234 $this->msg( 'upload_source_url' )->parse(),
235 'checked' => $selectedSourceType == 'url',
236 ];
237 }
238 $this->hookRunner->onUploadFormSourceDescriptors(
239 $descriptor, $radio, $selectedSourceType );
240
241 $descriptor['Extensions'] = [
242 'type' => 'info',
243 'section' => 'source',
244 'default' => $this->getExtensionsMessage(),
245 'raw' => true,
246 ];
247
248 return $descriptor;
249 }
250
256 protected function getExtensionsMessage() {
257 # Print a list of allowed file extensions, if so configured. We ignore
258 # MIME type here, it's incomprehensible to most people and too long.
259 $config = $this->getConfig();
260
261 if ( $config->get( MainConfigNames::CheckFileExtensions ) ) {
262 $fileExtensions = array_unique( $config->get( MainConfigNames::FileExtensions ) );
263 if ( $config->get( MainConfigNames::StrictFileExtensions ) ) {
264 # Everything not permitted is banned
265 $extensionsList =
266 '<div id="mw-upload-permitted">' .
267 $this->msg( 'upload-permitted' )
268 ->params( $this->getLanguage()->commaList( $fileExtensions ) )
269 ->numParams( count( $fileExtensions ) )
270 ->parseAsBlock() .
271 "</div>\n";
272 } else {
273 # We have to list both preferred and prohibited
274 $prohibitedExtensions =
275 array_unique( $config->get( MainConfigNames::ProhibitedFileExtensions ) );
276 $extensionsList =
277 '<div id="mw-upload-preferred">' .
278 $this->msg( 'upload-preferred' )
279 ->params( $this->getLanguage()->commaList( $fileExtensions ) )
280 ->numParams( count( $fileExtensions ) )
281 ->parseAsBlock() .
282 "</div>\n" .
283 '<div id="mw-upload-prohibited">' .
284 $this->msg( 'upload-prohibited' )
285 ->params( $this->getLanguage()->commaList( $prohibitedExtensions ) )
286 ->numParams( count( $prohibitedExtensions ) )
287 ->parseAsBlock() .
288 "</div>\n";
289 }
290 } else {
291 # Everything is permitted.
292 $extensionsList = '';
293 }
294
295 return $extensionsList;
296 }
297
304 protected function getDescriptionSection() {
305 $config = $this->getConfig();
306 if ( $this->mSessionKey ) {
307 $stash = $this->localRepo->getUploadStash( $this->getUser() );
308 try {
309 $file = $stash->getFile( $this->mSessionKey );
310 } catch ( TimeoutException $e ) {
311 throw $e;
312 } catch ( Exception $e ) {
313 $file = null;
314 }
315 if ( $file ) {
316 $mto = $file->transform( [ 'width' => 120 ] );
317 if ( $mto ) {
318 $this->addHeaderHtml(
319 '<div class="thumb t' .
320 $this->contentLanguage->alignEnd() . '">' .
321 Html::element( 'img', [
322 'src' => $mto->getUrl(),
323 'class' => 'thumbimage',
324 ] ) . '</div>', 'description' );
325 }
326 }
327 }
328
329 $descriptor = [
330 'DestFile' => [
331 'type' => 'text',
332 'section' => 'description',
333 'id' => 'wpDestFile',
334 'label-message' => 'destfilename',
335 'size' => 60,
336 'default' => $this->mDestFile,
337 # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm
338 'nodata' => strval( $this->mDestFile ) !== '',
339 ],
340 'UploadDescription' => [
341 'type' => $this->mForReUpload
342 ? 'text'
343 : 'textarea',
344 'section' => 'description',
345 'id' => 'wpUploadDescription',
346 'label-message' => $this->mForReUpload
347 ? 'filereuploadsummary'
348 : 'fileuploadsummary',
349 'default' => $this->mComment,
350 ]
351 ];
352 if ( $this->mTextAfterSummary ) {
353 $descriptor['UploadFormTextAfterSummary'] = [
354 'type' => 'info',
355 'section' => 'description',
356 'default' => $this->mTextAfterSummary,
357 'raw' => true,
358 ];
359 }
360
361 $descriptor += [
362 'EditTools' => [
363 'type' => 'edittools',
364 'section' => 'description',
365 'message' => 'edittools-upload',
366 ]
367 ];
368
369 if ( $this->mForReUpload ) {
370 $descriptor['DestFile']['readonly'] = true;
371 $descriptor['UploadDescription']['size'] = 60;
372 } else {
373 $descriptor['License'] = [
374 'type' => 'select',
375 'class' => Licenses::class,
376 'section' => 'description',
377 'id' => 'wpLicense',
378 'label-message' => 'license',
379 ];
380 $descriptor['UploadDescription']['rows'] = 8;
381 }
382
383 if ( $config->get( MainConfigNames::UseCopyrightUpload ) ) {
384 $descriptor['UploadCopyStatus'] = [
385 'type' => 'text',
386 'section' => 'description',
387 'id' => 'wpUploadCopyStatus',
388 'label-message' => 'filestatus',
389 ];
390 $descriptor['UploadSource'] = [
391 'type' => 'text',
392 'section' => 'description',
393 'id' => 'wpUploadSource',
394 'label-message' => 'filesource',
395 ];
396 }
397
398 return $descriptor;
399 }
400
407 protected function getOptionsSection() {
408 $user = $this->getUser();
409 if ( $user->isRegistered() ) {
410 $descriptor = [
411 'Watchthis' => [
412 'type' => 'check',
413 'id' => 'wpWatchthis',
414 'label-message' => 'watchthisupload',
415 'section' => 'options',
416 'default' => $this->mWatch,
417 ]
418 ];
419 }
420 if ( !$this->mHideIgnoreWarning ) {
421 $descriptor['IgnoreWarning'] = [
422 'type' => 'check',
423 'id' => 'wpIgnoreWarning',
424 'label-message' => 'ignorewarnings',
425 'section' => 'options',
426 ];
427 }
428
429 $descriptor['DestFileWarningAck'] = [
430 'type' => 'hidden',
431 'id' => 'wpDestFileWarningAck',
432 'default' => $this->mDestWarningAck ? '1' : '',
433 ];
434
435 if ( $this->mForReUpload ) {
436 $descriptor['ForReUpload'] = [
437 'type' => 'hidden',
438 'id' => 'wpForReUpload',
439 'default' => '1',
440 ];
441 }
442
443 return $descriptor;
444 }
445
450 public function show() {
451 $this->addUploadJS();
452 return parent::show();
453 }
454
458 protected function addUploadJS() {
459 $config = $this->getConfig();
460
461 $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize();
462
463 $scriptVars = [
464 'wgAjaxLicensePreview' => $config->get( MainConfigNames::AjaxLicensePreview ),
465 'wgUploadAutoFill' => !$this->mForReUpload &&
466 // If we received mDestFile from the request, don't autofill
467 // the wpDestFile textbox
468 $this->mDestFile === '',
469 'wgUploadSourceIds' => $this->mSourceIds,
470 'wgCheckFileExtensions' => $config->get( MainConfigNames::CheckFileExtensions ),
471 'wgStrictFileExtensions' => $config->get( MainConfigNames::StrictFileExtensions ),
472 'wgFileExtensions' =>
473 array_values( array_unique( $config->get( MainConfigNames::FileExtensions ) ) ),
474 'wgMaxUploadSize' => $this->mMaxUploadSize,
475 'wgFileCanRotate' => SpecialUpload::rotationEnabled(),
476 ];
477
478 $out = $this->getOutput();
479 $out->addJsConfigVars( $scriptVars );
480
481 $out->addModules( [
482 'mediawiki.special.upload', // Extras for thumbnail and license preview.
483 ] );
484 }
485
491 public function trySubmit() {
492 return false;
493 }
494}
getUser()
getRequest()
getAuthority()
getContext()
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition LocalRepo.php:48
setContext(IContextSource $context)
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:208
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
addFooterHtml( $html, $section=null)
Add footer HTML, inside the form.
addHeaderHtml( $html, $section=null)
Add HTML to the header, inside the form.
Definition HTMLForm.php:949
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
Class that generates HTML for internal links.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Form for uploading media files.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Sub class of HTMLForm that provides the form section of SpecialUpload.
string $mTextAfterSummary
raw html
bool $mForReUpload
bool $mHideIgnoreWarning
show()
Add the upload JS and show the form.
trySubmit()
Empty function; submission is handled elsewhere.
getDescriptionSection()
Get the descriptor of the fieldset that contains the file description input.
getOptionsSection()
Get the descriptor of the fieldset that contains the upload options, such as "watch this file".
bool $mDestWarningAck
addUploadJS()
Add upload JS to the OutputPage.
string $mDestFile
__construct(array $options=[], IContextSource $context=null, LinkRenderer $linkRenderer=null, LocalRepo $localRepo=null, Language $contentLanguage=null, NamespaceInfo $nsInfo=null, HookContainer $hookContainer=null)
getSourceSection()
Get the descriptor of the fieldset that contains the file source selection.
string $mSessionKey
string $mComment
string[] $mSourceIds
string $mTextTop
raw html
getExtensionsMessage()
Get the messages indicating which extensions are preferred and prohibited.
array $mMaxUploadSize
static isAllowed(Authority $performer)
Checks if the user is allowed to use the upload-by-URL feature.
static isEnabled()
Checks if the upload from URL feature is enabled.
Interface for objects which can provide a MediaWiki context on request.