MediaWiki  master
UploadForm.php
Go to the documentation of this file.
1 <?php
24 use Wikimedia\RequestTimeout\TimeoutException;
25 
29 class UploadForm extends HTMLForm {
30  protected $mWatch;
31  protected $mForReUpload;
32  protected $mSessionKey;
34  protected $mDestWarningAck;
35  protected $mDestFile;
36 
37  protected $mComment;
39  protected $mTextTop;
41  protected $mTextAfterSummary;
42 
43  protected $mSourceIds;
44 
45  protected $mMaxFileSize = [];
46 
48  protected $mMaxUploadSize = [];
49 
51  private $localRepo;
52 
55 
57  private $nsInfo;
58 
67  public function __construct(
68  array $options = [],
69  IContextSource $context = null,
70  LinkRenderer $linkRenderer = null,
71  LocalRepo $localRepo = null,
73  NamespaceInfo $nsInfo = null
74  ) {
75  if ( $context instanceof IContextSource ) {
76  $this->setContext( $context );
77  }
78 
79  $services = MediaWikiServices::getInstance();
80  if ( !$linkRenderer ) {
81  $linkRenderer = $services->getLinkRenderer();
82  }
83  if ( !$localRepo ) {
84  $localRepo = $services->getRepoGroup()->getLocalRepo();
85  }
86  if ( !$contentLanguage ) {
87  $contentLanguage = $services->getContentLanguage();
88  }
89  if ( !$nsInfo ) {
90  $nsInfo = $services->getNamespaceInfo();
91  }
92  $this->localRepo = $localRepo;
93  $this->contentLanguage = $contentLanguage;
94  $this->nsInfo = $nsInfo;
95 
96  $this->mWatch = !empty( $options['watch'] );
97  $this->mForReUpload = !empty( $options['forreupload'] );
98  $this->mSessionKey = $options['sessionkey'] ?? '';
99  $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] );
100  $this->mDestWarningAck = !empty( $options['destwarningack'] );
101  $this->mDestFile = $options['destfile'] ?? '';
102 
103  $this->mComment = $options['description'] ?? '';
104 
105  $this->mTextTop = $options['texttop'] ?? '';
106 
107  $this->mTextAfterSummary = $options['textaftersummary'] ?? '';
108 
109  $sourceDescriptor = $this->getSourceSection();
110  $descriptor = $sourceDescriptor
111  + $this->getDescriptionSection()
112  + $this->getOptionsSection();
113 
114  $this->getHookRunner()->onUploadFormInitDescriptor( $descriptor );
115  parent::__construct( $descriptor, $this->getContext(), 'upload' );
116 
117  # Add a link to edit MediaWiki:Licenses
118  if ( $this->getAuthority()->isAllowed( 'editinterface' ) ) {
119  $this->getOutput()->addModuleStyles( 'mediawiki.special' );
120  $licensesLink = $linkRenderer->makeKnownLink(
121  $this->msg( 'licenses' )->inContentLanguage()->getTitle(),
122  $this->msg( 'licenses-edit' )->text(),
123  [],
124  [ 'action' => 'edit' ]
125  );
126  $editLicenses = '<p class="mw-upload-editlicenses">' . $licensesLink . '</p>';
127  $this->addFooterText( $editLicenses, 'description' );
128  }
129 
130  # Set some form properties
131  $this->setSubmitTextMsg( 'uploadbtn' );
132  $this->setSubmitName( 'wpUpload' );
133  # Used message keys: 'accesskey-upload', 'tooltip-upload'
134  $this->setSubmitTooltip( 'upload' );
135  $this->setId( 'mw-upload-form' );
136 
137  # Build a list of IDs for javascript insertion
138  $this->mSourceIds = [];
139  foreach ( $sourceDescriptor as $field ) {
140  if ( !empty( $field['id'] ) ) {
141  $this->mSourceIds[] = $field['id'];
142  }
143  }
144  }
145 
152  protected function getSourceSection() {
153  if ( $this->mSessionKey ) {
154  return [
155  'SessionKey' => [
156  'type' => 'hidden',
157  'default' => $this->mSessionKey,
158  ],
159  'SourceType' => [
160  'type' => 'hidden',
161  'default' => 'Stash',
162  ],
163  ];
164  }
165 
166  $canUploadByUrl = UploadFromUrl::isEnabled()
167  && ( UploadFromUrl::isAllowed( $this->getUser() ) === true )
168  && $this->getConfig()->get( MainConfigNames::CopyUploadsFromSpecialUpload );
169  $radio = $canUploadByUrl;
170  $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) );
171 
172  $descriptor = [];
173  if ( $this->mTextTop ) {
174  $descriptor['UploadFormTextTop'] = [
175  // @phan-suppress-next-line SecurityCheck-XSS mTextTop is raw html
176  'type' => 'info',
177  'section' => 'source',
178  'default' => $this->mTextTop,
179  'raw' => true,
180  ];
181  }
182 
183  $this->mMaxUploadSize['file'] = min(
186  );
187 
188  $help = $this->msg( 'upload-maxfilesize' )->sizeParams( $this->mMaxUploadSize['file'] )->parse();
189 
190  // If the user can also upload by URL, there are 2 different file size limits.
191  // This extra message helps stress which limit corresponds to what.
192  if ( $canUploadByUrl ) {
193  $help .= $this->msg( 'word-separator' )->escaped();
194  $help .= $this->msg( 'upload_source_file' )->parse();
195  }
196 
197  $descriptor['UploadFile'] = [
198  'class' => UploadSourceField::class,
199  'section' => 'source',
200  'type' => 'file',
201  'id' => 'wpUploadFile',
202  'radio-id' => 'wpSourceTypeFile',
203  'label-message' => 'sourcefilename',
204  'upload-type' => 'File',
205  'radio' => &$radio,
206  'help' => $help,
207  'checked' => $selectedSourceType == 'file',
208  ];
209 
210  if ( $canUploadByUrl ) {
211  $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' );
212  $descriptor['UploadFileURL'] = [
213  'class' => UploadSourceField::class,
214  'section' => 'source',
215  'id' => 'wpUploadFileURL',
216  'radio-id' => 'wpSourceTypeurl',
217  'label-message' => 'sourceurl',
218  'upload-type' => 'url',
219  'radio' => &$radio,
220  'help' => $this->msg( 'upload-maxfilesize' )->sizeParams( $this->mMaxUploadSize['url'] )->parse() .
221  $this->msg( 'word-separator' )->escaped() .
222  $this->msg( 'upload_source_url' )->parse(),
223  'checked' => $selectedSourceType == 'url',
224  ];
225  }
226  $this->getHookRunner()->onUploadFormSourceDescriptors(
227  $descriptor, $radio, $selectedSourceType );
228 
229  $descriptor['Extensions'] = [
230  'type' => 'info',
231  'section' => 'source',
232  'default' => $this->getExtensionsMessage(),
233  'raw' => true,
234  ];
235 
236  return $descriptor;
237  }
238 
244  protected function getExtensionsMessage() {
245  # Print a list of allowed file extensions, if so configured. We ignore
246  # MIME type here, it's incomprehensible to most people and too long.
247  $config = $this->getConfig();
248 
249  if ( $config->get( MainConfigNames::CheckFileExtensions ) ) {
250  $fileExtensions = array_unique( $config->get( MainConfigNames::FileExtensions ) );
251  if ( $config->get( MainConfigNames::StrictFileExtensions ) ) {
252  # Everything not permitted is banned
253  $extensionsList =
254  '<div id="mw-upload-permitted">' .
255  $this->msg( 'upload-permitted' )
256  ->params( $this->getLanguage()->commaList( $fileExtensions ) )
257  ->numParams( count( $fileExtensions ) )
258  ->parseAsBlock() .
259  "</div>\n";
260  } else {
261  # We have to list both preferred and prohibited
262  $prohibitedExtensions =
263  array_unique( $config->get( MainConfigNames::ProhibitedFileExtensions ) );
264  $extensionsList =
265  '<div id="mw-upload-preferred">' .
266  $this->msg( 'upload-preferred' )
267  ->params( $this->getLanguage()->commaList( $fileExtensions ) )
268  ->numParams( count( $fileExtensions ) )
269  ->parseAsBlock() .
270  "</div>\n" .
271  '<div id="mw-upload-prohibited">' .
272  $this->msg( 'upload-prohibited' )
273  ->params( $this->getLanguage()->commaList( $prohibitedExtensions ) )
274  ->numParams( count( $prohibitedExtensions ) )
275  ->parseAsBlock() .
276  "</div>\n";
277  }
278  } else {
279  # Everything is permitted.
280  $extensionsList = '';
281  }
282 
283  return $extensionsList;
284  }
285 
292  protected function getDescriptionSection() {
293  $config = $this->getConfig();
294  if ( $this->mSessionKey ) {
295  $stash = $this->localRepo->getUploadStash( $this->getUser() );
296  try {
297  $file = $stash->getFile( $this->mSessionKey );
298  } catch ( TimeoutException $e ) {
299  throw $e;
300  } catch ( Exception $e ) {
301  $file = null;
302  }
303  if ( $file ) {
304  $mto = $file->transform( [ 'width' => 120 ] );
305  if ( $mto ) {
306  $this->addHeaderText(
307  '<div class="thumb t' .
308  $this->contentLanguage->alignEnd() . '">' .
309  Html::element( 'img', [
310  'src' => $mto->getUrl(),
311  'class' => 'thumbimage',
312  ] ) . '</div>', 'description' );
313  }
314  }
315  }
316 
317  $descriptor = [
318  'DestFile' => [
319  'type' => 'text',
320  'section' => 'description',
321  'id' => 'wpDestFile',
322  'label-message' => 'destfilename',
323  'size' => 60,
324  'default' => $this->mDestFile,
325  # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm
326  'nodata' => strval( $this->mDestFile ) !== '',
327  ],
328  'UploadDescription' => [
329  'type' => $this->mForReUpload
330  ? 'text'
331  : 'textarea',
332  'section' => 'description',
333  'id' => 'wpUploadDescription',
334  'label-message' => $this->mForReUpload
335  ? 'filereuploadsummary'
336  : 'fileuploadsummary',
337  'default' => $this->mComment,
338  ]
339  ];
340  if ( $this->mTextAfterSummary ) {
341  $descriptor['UploadFormTextAfterSummary'] = [
342  // @phan-suppress-next-line SecurityCheck-XSS mTextAfterSummary is raw html
343  'type' => 'info',
344  'section' => 'description',
345  'default' => $this->mTextAfterSummary,
346  'raw' => true,
347  ];
348  }
349 
350  $descriptor += [
351  'EditTools' => [
352  'type' => 'edittools',
353  'section' => 'description',
354  'message' => 'edittools-upload',
355  ]
356  ];
357 
358  if ( $this->mForReUpload ) {
359  $descriptor['DestFile']['readonly'] = true;
360  $descriptor['UploadDescription']['size'] = 60;
361  } else {
362  $descriptor['License'] = [
363  'type' => 'select',
364  'class' => Licenses::class,
365  'section' => 'description',
366  'id' => 'wpLicense',
367  'label-message' => 'license',
368  ];
369  $descriptor['UploadDescription']['rows'] = 8;
370  }
371 
372  if ( $config->get( MainConfigNames::UseCopyrightUpload ) ) {
373  $descriptor['UploadCopyStatus'] = [
374  'type' => 'text',
375  'section' => 'description',
376  'id' => 'wpUploadCopyStatus',
377  'label-message' => 'filestatus',
378  ];
379  $descriptor['UploadSource'] = [
380  'type' => 'text',
381  'section' => 'description',
382  'id' => 'wpUploadSource',
383  'label-message' => 'filesource',
384  ];
385  }
386 
387  return $descriptor;
388  }
389 
396  protected function getOptionsSection() {
397  $user = $this->getUser();
398  if ( $user->isRegistered() ) {
399  $descriptor = [
400  'Watchthis' => [
401  'type' => 'check',
402  'id' => 'wpWatchthis',
403  'label-message' => 'watchthisupload',
404  'section' => 'options',
405  'default' => $this->mWatch,
406  ]
407  ];
408  }
409  if ( !$this->mHideIgnoreWarning ) {
410  $descriptor['IgnoreWarning'] = [
411  'type' => 'check',
412  'id' => 'wpIgnoreWarning',
413  'label-message' => 'ignorewarnings',
414  'section' => 'options',
415  ];
416  }
417 
418  $descriptor['DestFileWarningAck'] = [
419  'type' => 'hidden',
420  'id' => 'wpDestFileWarningAck',
421  'default' => $this->mDestWarningAck ? '1' : '',
422  ];
423 
424  if ( $this->mForReUpload ) {
425  $descriptor['ForReUpload'] = [
426  'type' => 'hidden',
427  'id' => 'wpForReUpload',
428  'default' => '1',
429  ];
430  }
431 
432  return $descriptor;
433  }
434 
439  public function show() {
440  $this->addUploadJS();
441  return parent::show();
442  }
443 
447  protected function addUploadJS() {
448  $config = $this->getConfig();
449 
450  $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize();
451 
452  $scriptVars = [
453  'wgAjaxLicensePreview' => $config->get( MainConfigNames::AjaxLicensePreview ),
454  'wgUploadAutoFill' => !$this->mForReUpload &&
455  // If we received mDestFile from the request, don't autofill
456  // the wpDestFile textbox
457  $this->mDestFile === '',
458  'wgUploadSourceIds' => $this->mSourceIds,
459  'wgCheckFileExtensions' => $config->get( MainConfigNames::CheckFileExtensions ),
460  'wgStrictFileExtensions' => $config->get( MainConfigNames::StrictFileExtensions ),
461  'wgFileExtensions' =>
462  array_values( array_unique( $config->get( MainConfigNames::FileExtensions ) ) ),
463  'wgMaxUploadSize' => $this->mMaxUploadSize,
464  'wgFileCanRotate' => SpecialUpload::rotationEnabled(),
465  ];
466 
467  $out = $this->getOutput();
468  $out->addJsConfigVars( $scriptVars );
469 
470  $out->addModules( [
471  'mediawiki.special.upload', // Extras for thumbnail and license preview.
472  ] );
473  }
474 
480  public function trySubmit() {
481  return false;
482  }
483 }
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
IContextSource $context
getContext()
Get the base IContextSource object.
setContext(IContextSource $context)
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:150
setSubmitName( $name)
Definition: HTMLForm.php:1600
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
Definition: HTMLForm.php:1578
setId( $id)
Definition: HTMLForm.php:1728
addFooterText( $msg, $section=null)
Add footer text, inside the form.
Definition: HTMLForm.php:1019
addHeaderText( $msg, $section=null)
Add HTML to the header, inside the form.
Definition: HTMLForm.php:923
setSubmitTooltip( $name)
Definition: HTMLForm.php:1611
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:236
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:45
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition: LocalRepo.php:39
Class that generates HTML anchor link elements for pages.
A class containing constants representing the names of configuration variables.
MediaWikiServices is the service locator for the application scope of MediaWiki.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
static rotationEnabled()
Should we rotate images in the preview on Special:Upload.
static getMaxUploadSize( $forType=null)
Get MediaWiki's maximum uploaded file size for given type of upload, based on $wgMaxUploadSize.
static getMaxPhpUploadSize()
Get the PHP maximum uploaded file size, based on ini settings.
Sub class of HTMLForm that provides the form section of SpecialUpload.
Definition: UploadForm.php:29
string $mTextAfterSummary
raw html
Definition: UploadForm.php:41
Language $contentLanguage
Definition: UploadForm.php:54
show()
Add the upload JS and show the form.
Definition: UploadForm.php:439
NamespaceInfo $nsInfo
Definition: UploadForm.php:57
trySubmit()
Empty function; submission is handled elsewhere.
Definition: UploadForm.php:480
LocalRepo $localRepo
Definition: UploadForm.php:51
getDescriptionSection()
Get the descriptor of the fieldset that contains the file description input.
Definition: UploadForm.php:292
getOptionsSection()
Get the descriptor of the fieldset that contains the upload options, such as "watch this file".
Definition: UploadForm.php:396
addUploadJS()
Add upload JS to the OutputPage.
Definition: UploadForm.php:447
__construct(array $options=[], IContextSource $context=null, LinkRenderer $linkRenderer=null, LocalRepo $localRepo=null, Language $contentLanguage=null, NamespaceInfo $nsInfo=null)
Definition: UploadForm.php:67
getSourceSection()
Get the descriptor of the fieldset that contains the file source selection.
Definition: UploadForm.php:152
string $mTextTop
raw html
Definition: UploadForm.php:39
getExtensionsMessage()
Get the messages indicating which extensions are preferred and prohibited.
Definition: UploadForm.php:244
array $mMaxUploadSize
Definition: UploadForm.php:48
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.
$help
Definition: mcc.php:32
return true
Definition: router.php:90
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42