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