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