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