MediaWiki master
UploadForm.php
Go to the documentation of this file.
1<?php
31use Wikimedia\RequestTimeout\TimeoutException;
32
36class UploadForm extends HTMLForm {
37 protected $mWatch;
38 protected $mForReUpload;
39 protected $mSessionKey;
42 protected $mDestFile;
43
44 protected $mComment;
46 protected $mTextTop;
49
50 protected $mSourceIds;
51
52 protected $mMaxFileSize = [];
53
55 protected $mMaxUploadSize = [];
56
57 private LocalRepo $localRepo;
58 private Language $contentLanguage;
59 private NamespaceInfo $nsInfo;
60 private HookRunner $hookRunner;
61
71 public function __construct(
72 array $options = [],
73 IContextSource $context = null,
74 LinkRenderer $linkRenderer = null,
75 LocalRepo $localRepo = null,
76 Language $contentLanguage = null,
77 NamespaceInfo $nsInfo = null,
78 HookContainer $hookContainer = null
79 ) {
80 if ( $context instanceof IContextSource ) {
81 $this->setContext( $context );
82 }
83
84 $services = MediaWikiServices::getInstance();
85 if ( !$linkRenderer ) {
86 $linkRenderer = $services->getLinkRenderer();
87 }
88 if ( !$localRepo ) {
89 $localRepo = $services->getRepoGroup()->getLocalRepo();
90 }
91 if ( !$contentLanguage ) {
92 $contentLanguage = $services->getContentLanguage();
93 }
94 if ( !$nsInfo ) {
95 $nsInfo = $services->getNamespaceInfo();
96 }
97 $this->localRepo = $localRepo;
98 $this->contentLanguage = $contentLanguage;
99 $this->nsInfo = $nsInfo;
100 $this->hookRunner = new HookRunner( $hookContainer ?? $services->getHookContainer() );
101
102 $this->mWatch = !empty( $options['watch'] );
103 $this->mForReUpload = !empty( $options['forreupload'] );
104 $this->mSessionKey = $options['sessionkey'] ?? '';
105 $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] );
106 $this->mDestWarningAck = !empty( $options['destwarningack'] );
107 $this->mDestFile = $options['destfile'] ?? '';
108
109 $this->mComment = $options['description'] ?? '';
110
111 $this->mTextTop = $options['texttop'] ?? '';
112
113 $this->mTextAfterSummary = $options['textaftersummary'] ?? '';
114
115 $sourceDescriptor = $this->getSourceSection();
116 $descriptor = $sourceDescriptor
117 + $this->getDescriptionSection()
118 + $this->getOptionsSection();
119
120 $this->hookRunner->onUploadFormInitDescriptor( $descriptor );
121 parent::__construct( $descriptor, $this->getContext(), 'upload' );
122
123 # Add a link to edit MediaWiki:Licenses
124 if ( $this->getAuthority()->isAllowed( 'editinterface' ) ) {
125 $this->getOutput()->addModuleStyles( 'mediawiki.special' );
126 $licensesLink = $linkRenderer->makeKnownLink(
127 $this->msg( 'licenses' )->inContentLanguage()->getTitle(),
128 $this->msg( 'licenses-edit' )->text(),
129 [],
130 [ 'action' => 'edit' ]
131 );
132 $editLicenses = '<p class="mw-upload-editlicenses">' . $licensesLink . '</p>';
133 $this->addFooterText( $editLicenses, 'description' );
134 }
135
136 # Set some form properties
137 $this->setSubmitTextMsg( 'uploadbtn' );
138 $this->setSubmitName( 'wpUpload' );
139 # Used message keys: 'accesskey-upload', 'tooltip-upload'
140 $this->setSubmitTooltip( 'upload' );
141 $this->setId( 'mw-upload-form' );
142
143 # Build a list of IDs for javascript insertion
144 $this->mSourceIds = [];
145 foreach ( $sourceDescriptor as $field ) {
146 if ( !empty( $field['id'] ) ) {
147 $this->mSourceIds[] = $field['id'];
148 }
149 }
150 }
151
158 protected function getSourceSection() {
159 if ( $this->mSessionKey ) {
160 return [
161 'SessionKey' => [
162 'type' => 'hidden',
163 'default' => $this->mSessionKey,
164 ],
165 'SourceType' => [
166 'type' => 'hidden',
167 'default' => 'Stash',
168 ],
169 ];
170 }
171
172 $canUploadByUrl = UploadFromUrl::isEnabled()
173 && ( UploadFromUrl::isAllowed( $this->getAuthority() ) === true )
174 && $this->getConfig()->get( MainConfigNames::CopyUploadsFromSpecialUpload );
175 $radio = $canUploadByUrl;
176 $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) );
177
178 $descriptor = [];
179 if ( $this->mTextTop ) {
180 $descriptor['UploadFormTextTop'] = [
181 'type' => 'info',
182 'section' => 'source',
183 'default' => $this->mTextTop,
184 'raw' => true,
185 ];
186 }
187
188 $this->mMaxUploadSize['file'] = min(
189 UploadBase::getMaxUploadSize( 'file' ),
190 UploadBase::getMaxPhpUploadSize()
191 );
192
193 $help = $this->msg( 'upload-maxfilesize' )->sizeParams( $this->mMaxUploadSize['file'] )->parse();
194
195 // If the user can also upload by URL, there are 2 different file size limits.
196 // This extra message helps stress which limit corresponds to what.
197 if ( $canUploadByUrl ) {
198 $help .= $this->msg( 'word-separator' )->escaped();
199 $help .= $this->msg( 'upload_source_file' )->parse();
200 }
201
202 $descriptor['UploadFile'] = [
203 'class' => UploadSourceField::class,
204 'section' => 'source',
205 'type' => 'file',
206 'id' => 'wpUploadFile',
207 'radio-id' => 'wpSourceTypeFile',
208 'label-message' => 'sourcefilename',
209 'upload-type' => 'File',
210 'radio' => &$radio,
211 'help' => $help,
212 'checked' => $selectedSourceType == 'file',
213 ];
214
215 if ( $canUploadByUrl ) {
216 $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' );
217 $descriptor['UploadFileURL'] = [
218 'class' => UploadSourceField::class,
219 'section' => 'source',
220 'id' => 'wpUploadFileURL',
221 'radio-id' => 'wpSourceTypeurl',
222 'label-message' => 'sourceurl',
223 'upload-type' => 'url',
224 'radio' => &$radio,
225 'help' => $this->msg( 'upload-maxfilesize' )->sizeParams( $this->mMaxUploadSize['url'] )->parse() .
226 $this->msg( 'word-separator' )->escaped() .
227 $this->msg( 'upload_source_url' )->parse(),
228 'checked' => $selectedSourceType == 'url',
229 ];
230 }
231 $this->hookRunner->onUploadFormSourceDescriptors(
232 $descriptor, $radio, $selectedSourceType );
233
234 $descriptor['Extensions'] = [
235 'type' => 'info',
236 'section' => 'source',
237 'default' => $this->getExtensionsMessage(),
238 'raw' => true,
239 ];
240
241 return $descriptor;
242 }
243
249 protected function getExtensionsMessage() {
250 # Print a list of allowed file extensions, if so configured. We ignore
251 # MIME type here, it's incomprehensible to most people and too long.
252 $config = $this->getConfig();
253
254 if ( $config->get( MainConfigNames::CheckFileExtensions ) ) {
255 $fileExtensions = array_unique( $config->get( MainConfigNames::FileExtensions ) );
256 if ( $config->get( MainConfigNames::StrictFileExtensions ) ) {
257 # Everything not permitted is banned
258 $extensionsList =
259 '<div id="mw-upload-permitted">' .
260 $this->msg( 'upload-permitted' )
261 ->params( $this->getLanguage()->commaList( $fileExtensions ) )
262 ->numParams( count( $fileExtensions ) )
263 ->parseAsBlock() .
264 "</div>\n";
265 } else {
266 # We have to list both preferred and prohibited
267 $prohibitedExtensions =
268 array_unique( $config->get( MainConfigNames::ProhibitedFileExtensions ) );
269 $extensionsList =
270 '<div id="mw-upload-preferred">' .
271 $this->msg( 'upload-preferred' )
272 ->params( $this->getLanguage()->commaList( $fileExtensions ) )
273 ->numParams( count( $fileExtensions ) )
274 ->parseAsBlock() .
275 "</div>\n" .
276 '<div id="mw-upload-prohibited">' .
277 $this->msg( 'upload-prohibited' )
278 ->params( $this->getLanguage()->commaList( $prohibitedExtensions ) )
279 ->numParams( count( $prohibitedExtensions ) )
280 ->parseAsBlock() .
281 "</div>\n";
282 }
283 } else {
284 # Everything is permitted.
285 $extensionsList = '';
286 }
287
288 return $extensionsList;
289 }
290
297 protected function getDescriptionSection() {
298 $config = $this->getConfig();
299 if ( $this->mSessionKey ) {
300 $stash = $this->localRepo->getUploadStash( $this->getUser() );
301 try {
302 $file = $stash->getFile( $this->mSessionKey );
303 } catch ( TimeoutException $e ) {
304 throw $e;
305 } catch ( Exception $e ) {
306 $file = null;
307 }
308 if ( $file ) {
309 $mto = $file->transform( [ 'width' => 120 ] );
310 if ( $mto ) {
311 $this->addHeaderText(
312 '<div class="thumb t' .
313 $this->contentLanguage->alignEnd() . '">' .
314 Html::element( 'img', [
315 'src' => $mto->getUrl(),
316 'class' => 'thumbimage',
317 ] ) . '</div>', 'description' );
318 }
319 }
320 }
321
322 $descriptor = [
323 'DestFile' => [
324 'type' => 'text',
325 'section' => 'description',
326 'id' => 'wpDestFile',
327 'label-message' => 'destfilename',
328 'size' => 60,
329 'default' => $this->mDestFile,
330 # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm
331 'nodata' => strval( $this->mDestFile ) !== '',
332 ],
333 'UploadDescription' => [
334 'type' => $this->mForReUpload
335 ? 'text'
336 : 'textarea',
337 'section' => 'description',
338 'id' => 'wpUploadDescription',
339 'label-message' => $this->mForReUpload
340 ? 'filereuploadsummary'
341 : 'fileuploadsummary',
342 'default' => $this->mComment,
343 ]
344 ];
345 if ( $this->mTextAfterSummary ) {
346 $descriptor['UploadFormTextAfterSummary'] = [
347 'type' => 'info',
348 'section' => 'description',
349 'default' => $this->mTextAfterSummary,
350 'raw' => true,
351 ];
352 }
353
354 $descriptor += [
355 'EditTools' => [
356 'type' => 'edittools',
357 'section' => 'description',
358 'message' => 'edittools-upload',
359 ]
360 ];
361
362 if ( $this->mForReUpload ) {
363 $descriptor['DestFile']['readonly'] = true;
364 $descriptor['UploadDescription']['size'] = 60;
365 } else {
366 $descriptor['License'] = [
367 'type' => 'select',
368 'class' => Licenses::class,
369 'section' => 'description',
370 'id' => 'wpLicense',
371 'label-message' => 'license',
372 ];
373 $descriptor['UploadDescription']['rows'] = 8;
374 }
375
376 if ( $config->get( MainConfigNames::UseCopyrightUpload ) ) {
377 $descriptor['UploadCopyStatus'] = [
378 'type' => 'text',
379 'section' => 'description',
380 'id' => 'wpUploadCopyStatus',
381 'label-message' => 'filestatus',
382 ];
383 $descriptor['UploadSource'] = [
384 'type' => 'text',
385 'section' => 'description',
386 'id' => 'wpUploadSource',
387 'label-message' => 'filesource',
388 ];
389 }
390
391 return $descriptor;
392 }
393
400 protected function getOptionsSection() {
401 $user = $this->getUser();
402 if ( $user->isRegistered() ) {
403 $descriptor = [
404 'Watchthis' => [
405 'type' => 'check',
406 'id' => 'wpWatchthis',
407 'label-message' => 'watchthisupload',
408 'section' => 'options',
409 'default' => $this->mWatch,
410 ]
411 ];
412 }
413 if ( !$this->mHideIgnoreWarning ) {
414 $descriptor['IgnoreWarning'] = [
415 'type' => 'check',
416 'id' => 'wpIgnoreWarning',
417 'label-message' => 'ignorewarnings',
418 'section' => 'options',
419 ];
420 }
421
422 $descriptor['DestFileWarningAck'] = [
423 'type' => 'hidden',
424 'id' => 'wpDestFileWarningAck',
425 'default' => $this->mDestWarningAck ? '1' : '',
426 ];
427
428 if ( $this->mForReUpload ) {
429 $descriptor['ForReUpload'] = [
430 'type' => 'hidden',
431 'id' => 'wpForReUpload',
432 'default' => '1',
433 ];
434 }
435
436 return $descriptor;
437 }
438
443 public function show() {
444 $this->addUploadJS();
445 return parent::show();
446 }
447
451 protected function addUploadJS() {
452 $config = $this->getConfig();
453
454 $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize();
455
456 $scriptVars = [
457 'wgAjaxLicensePreview' => $config->get( MainConfigNames::AjaxLicensePreview ),
458 'wgUploadAutoFill' => !$this->mForReUpload &&
459 // If we received mDestFile from the request, don't autofill
460 // the wpDestFile textbox
461 $this->mDestFile === '',
462 'wgUploadSourceIds' => $this->mSourceIds,
463 'wgCheckFileExtensions' => $config->get( MainConfigNames::CheckFileExtensions ),
464 'wgStrictFileExtensions' => $config->get( MainConfigNames::StrictFileExtensions ),
465 'wgFileExtensions' =>
466 array_values( array_unique( $config->get( MainConfigNames::FileExtensions ) ) ),
467 'wgMaxUploadSize' => $this->mMaxUploadSize,
468 'wgFileCanRotate' => SpecialUpload::rotationEnabled(),
469 ];
470
471 $out = $this->getOutput();
472 $out->addJsConfigVars( $scriptVars );
473
474 $out->addModules( [
475 'mediawiki.special.upload', // Extras for thumbnail and license preview.
476 ] );
477 }
478
484 public function trySubmit() {
485 return false;
486 }
487}
getUser()
getRequest()
getAuthority()
getContext()
Base class for language-specific code.
Definition Language.php:63
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition LocalRepo.php:49
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 handling uploads and special page.
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
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".
addUploadJS()
Add upload JS to the OutputPage.
__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 $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.