MediaWiki  1.23.1
ApiUpload.php
Go to the documentation of this file.
1 <?php
30 class ApiUpload extends ApiBase {
32  protected $mUpload = null;
33 
34  protected $mParams;
35 
36  public function execute() {
37  global $wgEnableAsyncUploads;
38 
39  // Check whether upload is enabled
40  if ( !UploadBase::isEnabled() ) {
41  $this->dieUsageMsg( 'uploaddisabled' );
42  }
43 
44  $user = $this->getUser();
45 
46  // Parameter handling
47  $this->mParams = $this->extractRequestParams();
48  $request = $this->getMain()->getRequest();
49  // Check if async mode is actually supported (jobs done in cli mode)
50  $this->mParams['async'] = ( $this->mParams['async'] && $wgEnableAsyncUploads );
51  // Add the uploaded file to the params array
52  $this->mParams['file'] = $request->getFileName( 'file' );
53  $this->mParams['chunk'] = $request->getFileName( 'chunk' );
54 
55  // Copy the session key to the file key, for backward compatibility.
56  if ( !$this->mParams['filekey'] && $this->mParams['sessionkey'] ) {
57  $this->mParams['filekey'] = $this->mParams['sessionkey'];
58  }
59 
60  // Select an upload module
61  try {
62  if ( !$this->selectUploadModule() ) {
63  return; // not a true upload, but a status request or similar
64  } elseif ( !isset( $this->mUpload ) ) {
65  $this->dieUsage( 'No upload module set', 'nomodule' );
66  }
67  } catch ( UploadStashException $e ) { // XXX: don't spam exception log
68  $this->dieUsage( get_class( $e ) . ": " . $e->getMessage(), 'stasherror' );
69  }
70 
71  // First check permission to upload
72  $this->checkPermissions( $user );
73 
74  // Fetch the file (usually a no-op)
76  $status = $this->mUpload->fetchFile();
77  if ( !$status->isGood() ) {
78  $errors = $status->getErrorsArray();
79  $error = array_shift( $errors[0] );
80  $this->dieUsage( 'Error fetching file from remote source', $error, 0, $errors[0] );
81  }
82 
83  // Check if the uploaded file is sane
84  if ( $this->mParams['chunk'] ) {
85  $maxSize = $this->mUpload->getMaxUploadSize();
86  if ( $this->mParams['filesize'] > $maxSize ) {
87  $this->dieUsage( 'The file you submitted was too large', 'file-too-large' );
88  }
89  if ( !$this->mUpload->getTitle() ) {
90  $this->dieUsage( 'Invalid file title supplied', 'internal-error' );
91  }
92  } elseif ( $this->mParams['async'] && $this->mParams['filekey'] ) {
93  // defer verification to background process
94  } else {
95  wfDebug( __METHOD__ . " about to verify\n" );
96  $this->verifyUpload();
97  }
98 
99  // Check if the user has the rights to modify or overwrite the requested title
100  // (This check is irrelevant if stashing is already requested, since the errors
101  // can always be fixed by changing the title)
102  if ( !$this->mParams['stash'] ) {
103  $permErrors = $this->mUpload->verifyTitlePermissions( $user );
104  if ( $permErrors !== true ) {
105  $this->dieRecoverableError( $permErrors[0], 'filename' );
106  }
107  }
108 
109  // Get the result based on the current upload context:
110  try {
111  $result = $this->getContextResult();
112  if ( $result['result'] === 'Success' ) {
113  $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() );
114  }
115  } catch ( UploadStashException $e ) { // XXX: don't spam exception log
116  $this->dieUsage( get_class( $e ) . ": " . $e->getMessage(), 'stasherror' );
117  }
118 
119  $this->getResult()->addValue( null, $this->getModuleName(), $result );
120 
121  // Cleanup any temporary mess
122  $this->mUpload->cleanupTempFile();
123  }
124 
129  private function getContextResult() {
130  $warnings = $this->getApiWarnings();
131  if ( $warnings && !$this->mParams['ignorewarnings'] ) {
132  // Get warnings formatted in result array format
133  return $this->getWarningsResult( $warnings );
134  } elseif ( $this->mParams['chunk'] ) {
135  // Add chunk, and get result
136  return $this->getChunkResult( $warnings );
137  } elseif ( $this->mParams['stash'] ) {
138  // Stash the file and get stash result
139  return $this->getStashResult( $warnings );
140  }
141 
142  // This is the most common case -- a normal upload with no warnings
143  // performUpload will return a formatted properly for the API with status
144  return $this->performUpload( $warnings );
145  }
146 
152  private function getStashResult( $warnings ) {
153  $result = array();
154  // Some uploads can request they be stashed, so as not to publish them immediately.
155  // In this case, a failure to stash ought to be fatal
156  try {
157  $result['result'] = 'Success';
158  $result['filekey'] = $this->performStash();
159  $result['sessionkey'] = $result['filekey']; // backwards compatibility
160  if ( $warnings && count( $warnings ) > 0 ) {
161  $result['warnings'] = $warnings;
162  }
163  } catch ( MWException $e ) {
164  $this->dieUsage( $e->getMessage(), 'stashfailed' );
165  }
166 
167  return $result;
168  }
169 
175  private function getWarningsResult( $warnings ) {
176  $result = array();
177  $result['result'] = 'Warning';
178  $result['warnings'] = $warnings;
179  // in case the warnings can be fixed with some further user action, let's stash this upload
180  // and return a key they can use to restart it
181  try {
182  $result['filekey'] = $this->performStash();
183  $result['sessionkey'] = $result['filekey']; // backwards compatibility
184  } catch ( MWException $e ) {
185  $result['warnings']['stashfailed'] = $e->getMessage();
186  }
187 
188  return $result;
189  }
190 
196  private function getChunkResult( $warnings ) {
197  $result = array();
198 
199  $result['result'] = 'Continue';
200  if ( $warnings && count( $warnings ) > 0 ) {
201  $result['warnings'] = $warnings;
202  }
203  $request = $this->getMain()->getRequest();
204  $chunkPath = $request->getFileTempname( 'chunk' );
205  $chunkSize = $request->getUpload( 'chunk' )->getSize();
206  if ( $this->mParams['offset'] == 0 ) {
207  try {
208  $filekey = $this->performStash();
209  } catch ( MWException $e ) {
210  // FIXME: Error handling here is wrong/different from rest of this
211  $this->dieUsage( $e->getMessage(), 'stashfailed' );
212  }
213  } else {
214  $filekey = $this->mParams['filekey'];
216  $status = $this->mUpload->addChunk(
217  $chunkPath, $chunkSize, $this->mParams['offset'] );
218  if ( !$status->isGood() ) {
219  $this->dieUsage( $status->getWikiText(), 'stashfailed' );
220 
221  return array();
222  }
223  }
224 
225  // Check we added the last chunk:
226  if ( $this->mParams['offset'] + $chunkSize == $this->mParams['filesize'] ) {
227  if ( $this->mParams['async'] ) {
228  $progress = UploadBase::getSessionStatus( $filekey );
229  if ( $progress && $progress['result'] === 'Poll' ) {
230  $this->dieUsage( "Chunk assembly already in progress.", 'stashfailed' );
231  }
233  $filekey,
234  array( 'result' => 'Poll',
235  'stage' => 'queued', 'status' => Status::newGood() )
236  );
238  Title::makeTitle( NS_FILE, $filekey ),
239  array(
240  'filename' => $this->mParams['filename'],
241  'filekey' => $filekey,
242  'session' => $this->getContext()->exportSession()
243  )
244  ) );
245  if ( $ok ) {
246  $result['result'] = 'Poll';
247  } else {
248  UploadBase::setSessionStatus( $filekey, false );
249  $this->dieUsage(
250  "Failed to start AssembleUploadChunks.php", 'stashfailed' );
251  }
252  } else {
253  $status = $this->mUpload->concatenateChunks();
254  if ( !$status->isGood() ) {
255  $this->dieUsage( $status->getWikiText(), 'stashfailed' );
256 
257  return array();
258  }
259 
260  // The fully concatenated file has a new filekey. So remove
261  // the old filekey and fetch the new one.
262  $this->mUpload->stash->removeFile( $filekey );
263  $filekey = $this->mUpload->getLocalFile()->getFileKey();
264 
265  $result['result'] = 'Success';
266  }
267  }
268  $result['filekey'] = $filekey;
269  $result['offset'] = $this->mParams['offset'] + $chunkSize;
270 
271  return $result;
272  }
273 
280  private function performStash() {
281  try {
282  $stashFile = $this->mUpload->stashFile();
283 
284  if ( !$stashFile ) {
285  throw new MWException( 'Invalid stashed file' );
286  }
287  $fileKey = $stashFile->getFileKey();
288  } catch ( MWException $e ) {
289  $message = 'Stashing temporary file failed: ' . get_class( $e ) . ' ' . $e->getMessage();
290  wfDebug( __METHOD__ . ' ' . $message . "\n" );
291  throw new MWException( $message );
292  }
293 
294  return $fileKey;
295  }
296 
306  private function dieRecoverableError( $error, $parameter, $data = array() ) {
307  try {
308  $data['filekey'] = $this->performStash();
309  $data['sessionkey'] = $data['filekey'];
310  } catch ( MWException $e ) {
311  $data['stashfailed'] = $e->getMessage();
312  }
313  $data['invalidparameter'] = $parameter;
314 
315  $parsed = $this->parseMsg( $error );
316  $this->dieUsage( $parsed['info'], $parsed['code'], 0, $data );
317  }
318 
326  protected function selectUploadModule() {
327  $request = $this->getMain()->getRequest();
328 
329  // chunk or one and only one of the following parameters is needed
330  if ( !$this->mParams['chunk'] ) {
331  $this->requireOnlyOneParameter( $this->mParams,
332  'filekey', 'file', 'url', 'statuskey' );
333  }
334 
335  // Status report for "upload to stash"/"upload from stash"
336  if ( $this->mParams['filekey'] && $this->mParams['checkstatus'] ) {
337  $progress = UploadBase::getSessionStatus( $this->mParams['filekey'] );
338  if ( !$progress ) {
339  $this->dieUsage( 'No result in status data', 'missingresult' );
340  } elseif ( !$progress['status']->isGood() ) {
341  $this->dieUsage( $progress['status']->getWikiText(), 'stashfailed' );
342  }
343  if ( isset( $progress['status']->value['verification'] ) ) {
344  $this->checkVerification( $progress['status']->value['verification'] );
345  }
346  unset( $progress['status'] ); // remove Status object
347  $this->getResult()->addValue( null, $this->getModuleName(), $progress );
348 
349  return false;
350  }
351 
352  if ( $this->mParams['statuskey'] ) {
353  $this->checkAsyncDownloadEnabled();
354 
355  // Status request for an async upload
356  $sessionData = UploadFromUrlJob::getSessionData( $this->mParams['statuskey'] );
357  if ( !isset( $sessionData['result'] ) ) {
358  $this->dieUsage( 'No result in session data', 'missingresult' );
359  }
360  if ( $sessionData['result'] == 'Warning' ) {
361  $sessionData['warnings'] = $this->transformWarnings( $sessionData['warnings'] );
362  $sessionData['sessionkey'] = $this->mParams['statuskey'];
363  }
364  $this->getResult()->addValue( null, $this->getModuleName(), $sessionData );
365 
366  return false;
367  }
368 
369  // The following modules all require the filename parameter to be set
370  if ( is_null( $this->mParams['filename'] ) ) {
371  $this->dieUsageMsg( array( 'missingparam', 'filename' ) );
372  }
373 
374  if ( $this->mParams['chunk'] ) {
375  // Chunk upload
376  $this->mUpload = new UploadFromChunks();
377  if ( isset( $this->mParams['filekey'] ) ) {
378  // handle new chunk
379  $this->mUpload->continueChunks(
380  $this->mParams['filename'],
381  $this->mParams['filekey'],
382  $request->getUpload( 'chunk' )
383  );
384  } else {
385  // handle first chunk
386  $this->mUpload->initialize(
387  $this->mParams['filename'],
388  $request->getUpload( 'chunk' )
389  );
390  }
391  } elseif ( isset( $this->mParams['filekey'] ) ) {
392  // Upload stashed in a previous request
393  if ( !UploadFromStash::isValidKey( $this->mParams['filekey'] ) ) {
394  $this->dieUsageMsg( 'invalid-file-key' );
395  }
396 
397  $this->mUpload = new UploadFromStash( $this->getUser() );
398  // This will not download the temp file in initialize() in async mode.
399  // We still have enough information to call checkWarnings() and such.
400  $this->mUpload->initialize(
401  $this->mParams['filekey'], $this->mParams['filename'], !$this->mParams['async']
402  );
403  } elseif ( isset( $this->mParams['file'] ) ) {
404  $this->mUpload = new UploadFromFile();
405  $this->mUpload->initialize(
406  $this->mParams['filename'],
407  $request->getUpload( 'file' )
408  );
409  } elseif ( isset( $this->mParams['url'] ) ) {
410  // Make sure upload by URL is enabled:
411  if ( !UploadFromUrl::isEnabled() ) {
412  $this->dieUsageMsg( 'copyuploaddisabled' );
413  }
414 
415  if ( !UploadFromUrl::isAllowedHost( $this->mParams['url'] ) ) {
416  $this->dieUsageMsg( 'copyuploadbaddomain' );
417  }
418 
419  if ( !UploadFromUrl::isAllowedUrl( $this->mParams['url'] ) ) {
420  $this->dieUsageMsg( 'copyuploadbadurl' );
421  }
422 
423  $async = false;
424  if ( $this->mParams['asyncdownload'] ) {
425  $this->checkAsyncDownloadEnabled();
426 
427  if ( $this->mParams['leavemessage'] && !$this->mParams['ignorewarnings'] ) {
428  $this->dieUsage( 'Using leavemessage without ignorewarnings is not supported',
429  'missing-ignorewarnings' );
430  }
431 
432  if ( $this->mParams['leavemessage'] ) {
433  $async = 'async-leavemessage';
434  } else {
435  $async = 'async';
436  }
437  }
438  $this->mUpload = new UploadFromUrl;
439  $this->mUpload->initialize( $this->mParams['filename'],
440  $this->mParams['url'], $async );
441  }
442 
443  return true;
444  }
445 
451  protected function checkPermissions( $user ) {
452  // Check whether the user has the appropriate permissions to upload anyway
453  $permission = $this->mUpload->isAllowed( $user );
454 
455  if ( $permission !== true ) {
456  if ( !$user->isLoggedIn() ) {
457  $this->dieUsageMsg( array( 'mustbeloggedin', 'upload' ) );
458  }
459 
460  $this->dieUsageMsg( 'badaccess-groups' );
461  }
462  }
463 
467  protected function verifyUpload() {
468  $verification = $this->mUpload->verifyUpload();
469  if ( $verification['status'] === UploadBase::OK ) {
470  return;
471  }
472 
473  $this->checkVerification( $verification );
474  }
475 
479  protected function checkVerification( array $verification ) {
481 
482  // @todo Move them to ApiBase's message map
483  switch ( $verification['status'] ) {
484  // Recoverable errors
486  $this->dieRecoverableError( 'filename-tooshort', 'filename' );
487  break;
489  $this->dieRecoverableError( 'illegal-filename', 'filename',
490  array( 'filename' => $verification['filtered'] ) );
491  break;
493  $this->dieRecoverableError( 'filename-toolong', 'filename' );
494  break;
496  $this->dieRecoverableError( 'filetype-missing', 'filename' );
497  break;
499  $this->dieRecoverableError( 'windows-nonascii-filename', 'filename' );
500  break;
501 
502  // Unrecoverable errors
504  $this->dieUsage( 'The file you submitted was empty', 'empty-file' );
505  break;
507  $this->dieUsage( 'The file you submitted was too large', 'file-too-large' );
508  break;
509 
511  $extradata = array(
512  'filetype' => $verification['finalExt'],
513  'allowed' => array_values( array_unique( $wgFileExtensions ) )
514  );
515  $this->getResult()->setIndexedTagName( $extradata['allowed'], 'ext' );
516 
517  $msg = "Filetype not permitted: ";
518  if ( isset( $verification['blacklistedExt'] ) ) {
519  $msg .= join( ', ', $verification['blacklistedExt'] );
520  $extradata['blacklisted'] = array_values( $verification['blacklistedExt'] );
521  $this->getResult()->setIndexedTagName( $extradata['blacklisted'], 'ext' );
522  } else {
523  $msg .= $verification['finalExt'];
524  }
525  $this->dieUsage( $msg, 'filetype-banned', 0, $extradata );
526  break;
528  $this->getResult()->setIndexedTagName( $verification['details'], 'detail' );
529  $this->dieUsage( 'This file did not pass file verification', 'verification-error',
530  0, array( 'details' => $verification['details'] ) );
531  break;
533  $this->dieUsage( "The modification you tried to make was aborted by an extension hook",
534  'hookaborted', 0, array( 'error' => $verification['error'] ) );
535  break;
536  default:
537  $this->dieUsage( 'An unknown error occurred', 'unknown-error',
538  0, array( 'code' => $verification['status'] ) );
539  break;
540  }
541  }
542 
550  protected function getApiWarnings() {
551  $warnings = $this->mUpload->checkWarnings();
552 
553  return $this->transformWarnings( $warnings );
554  }
555 
556  protected function transformWarnings( $warnings ) {
557  if ( $warnings ) {
558  // Add indices
559  $result = $this->getResult();
560  $result->setIndexedTagName( $warnings, 'warning' );
561 
562  if ( isset( $warnings['duplicate'] ) ) {
563  $dupes = array();
564  foreach ( $warnings['duplicate'] as $dupe ) {
565  $dupes[] = $dupe->getName();
566  }
567  $result->setIndexedTagName( $dupes, 'duplicate' );
568  $warnings['duplicate'] = $dupes;
569  }
570 
571  if ( isset( $warnings['exists'] ) ) {
572  $warning = $warnings['exists'];
573  unset( $warnings['exists'] );
574  $localFile = isset( $warning['normalizedFile'] )
575  ? $warning['normalizedFile']
576  : $warning['file'];
577  $warnings[$warning['warning']] = $localFile->getName();
578  }
579  }
580 
581  return $warnings;
582  }
583 
591  protected function performUpload( $warnings ) {
592  // Use comment as initial page text by default
593  if ( is_null( $this->mParams['text'] ) ) {
594  $this->mParams['text'] = $this->mParams['comment'];
595  }
596 
598  $file = $this->mUpload->getLocalFile();
599 
600  // For preferences mode, we want to watch if 'watchdefault' is set or
601  // if the *file* doesn't exist and 'watchcreations' is set. But
602  // getWatchlistValue()'s automatic handling checks if the *title*
603  // exists or not, so we need to check both prefs manually.
604  $watch = $this->getWatchlistValue(
605  $this->mParams['watchlist'], $file->getTitle(), 'watchdefault'
606  );
607  if ( !$watch && $this->mParams['watchlist'] == 'preferences' && !$file->exists() ) {
608  $watch = $this->getWatchlistValue(
609  $this->mParams['watchlist'], $file->getTitle(), 'watchcreations'
610  );
611  }
612 
613  // Deprecated parameters
614  if ( $this->mParams['watch'] ) {
615  $watch = true;
616  }
617 
618  // No errors, no warnings: do the upload
619  if ( $this->mParams['async'] ) {
620  $progress = UploadBase::getSessionStatus( $this->mParams['filekey'] );
621  if ( $progress && $progress['result'] === 'Poll' ) {
622  $this->dieUsage( "Upload from stash already in progress.", 'publishfailed' );
623  }
625  $this->mParams['filekey'],
626  array( 'result' => 'Poll', 'stage' => 'queued', 'status' => Status::newGood() )
627  );
629  Title::makeTitle( NS_FILE, $this->mParams['filename'] ),
630  array(
631  'filename' => $this->mParams['filename'],
632  'filekey' => $this->mParams['filekey'],
633  'comment' => $this->mParams['comment'],
634  'text' => $this->mParams['text'],
635  'watch' => $watch,
636  'session' => $this->getContext()->exportSession()
637  )
638  ) );
639  if ( $ok ) {
640  $result['result'] = 'Poll';
641  } else {
642  UploadBase::setSessionStatus( $this->mParams['filekey'], false );
643  $this->dieUsage(
644  "Failed to start PublishStashedFile.php", 'publishfailed' );
645  }
646  } else {
648  $status = $this->mUpload->performUpload( $this->mParams['comment'],
649  $this->mParams['text'], $watch, $this->getUser() );
650 
651  if ( !$status->isGood() ) {
652  $error = $status->getErrorsArray();
653 
654  if ( count( $error ) == 1 && $error[0][0] == 'async' ) {
655  // The upload can not be performed right now, because the user
656  // requested so
657  return array(
658  'result' => 'Queued',
659  'statuskey' => $error[0][1],
660  );
661  }
662 
663  $this->getResult()->setIndexedTagName( $error, 'error' );
664  $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error );
665  }
666  $result['result'] = 'Success';
667  }
668 
669  $result['filename'] = $file->getName();
670  if ( $warnings && count( $warnings ) > 0 ) {
671  $result['warnings'] = $warnings;
672  }
673 
674  return $result;
675  }
676 
680  protected function checkAsyncDownloadEnabled() {
681  global $wgAllowAsyncCopyUploads;
682  if ( !$wgAllowAsyncCopyUploads ) {
683  $this->dieUsage( 'Asynchronous copy uploads disabled', 'asynccopyuploaddisabled' );
684  }
685  }
686 
687  public function mustBePosted() {
688  return true;
689  }
690 
691  public function isWriteMode() {
692  return true;
693  }
694 
695  public function getAllowedParams() {
696  $params = array(
697  'filename' => array(
698  ApiBase::PARAM_TYPE => 'string',
699  ),
700  'comment' => array(
701  ApiBase::PARAM_DFLT => ''
702  ),
703  'text' => null,
704  'token' => array(
705  ApiBase::PARAM_TYPE => 'string',
707  ),
708  'watch' => array(
709  ApiBase::PARAM_DFLT => false,
711  ),
712  'watchlist' => array(
713  ApiBase::PARAM_DFLT => 'preferences',
715  'watch',
716  'preferences',
717  'nochange'
718  ),
719  ),
720  'ignorewarnings' => false,
721  'file' => array(
722  ApiBase::PARAM_TYPE => 'upload',
723  ),
724  'url' => null,
725  'filekey' => null,
726  'sessionkey' => array(
727  ApiBase::PARAM_DFLT => null,
729  ),
730  'stash' => false,
731 
732  'filesize' => null,
733  'offset' => null,
734  'chunk' => array(
735  ApiBase::PARAM_TYPE => 'upload',
736  ),
737 
738  'async' => false,
739  'asyncdownload' => false,
740  'leavemessage' => false,
741  'statuskey' => null,
742  'checkstatus' => false,
743  );
744 
745  return $params;
746  }
747 
748  public function getParamDescription() {
749  $params = array(
750  'filename' => 'Target filename',
751  'token' => 'Edit token. You can get one of these through prop=info',
752  'comment' => 'Upload comment. Also used as the initial page text for new ' .
753  'files if "text" is not specified',
754  'text' => 'Initial page text for new files',
755  'watch' => 'Watch the page',
756  'watchlist' => 'Unconditionally add or remove the page from your watchlist, ' .
757  'use preferences or do not change watch',
758  'ignorewarnings' => 'Ignore any warnings',
759  'file' => 'File contents',
760  'url' => 'URL to fetch the file from',
761  'filekey' => 'Key that identifies a previous upload that was stashed temporarily.',
762  'sessionkey' => 'Same as filekey, maintained for backward compatibility.',
763  'stash' => 'If set, the server will not add the file to the repository ' .
764  'and stash it temporarily.',
765 
766  'chunk' => 'Chunk contents',
767  'offset' => 'Offset of chunk in bytes',
768  'filesize' => 'Filesize of entire upload',
769 
770  'async' => 'Make potentially large file operations asynchronous when possible',
771  'asyncdownload' => 'Make fetching a URL asynchronous',
772  'leavemessage' => 'If asyncdownload is used, leave a message on the user talk page if finished',
773  'statuskey' => 'Fetch the upload status for this file key (upload by URL)',
774  'checkstatus' => 'Only fetch the upload status for the given file key',
775  );
776 
777  return $params;
778  }
779 
780  public function getResultProperties() {
781  return array(
782  '' => array(
783  'result' => array(
785  'Success',
786  'Warning',
787  'Continue',
788  'Queued'
789  ),
790  ),
791  'filekey' => array(
792  ApiBase::PROP_TYPE => 'string',
793  ApiBase::PROP_NULLABLE => true
794  ),
795  'sessionkey' => array(
796  ApiBase::PROP_TYPE => 'string',
797  ApiBase::PROP_NULLABLE => true
798  ),
799  'offset' => array(
800  ApiBase::PROP_TYPE => 'integer',
801  ApiBase::PROP_NULLABLE => true
802  ),
803  'statuskey' => array(
804  ApiBase::PROP_TYPE => 'string',
805  ApiBase::PROP_NULLABLE => true
806  ),
807  'filename' => array(
808  ApiBase::PROP_TYPE => 'string',
809  ApiBase::PROP_NULLABLE => true
810  )
811  )
812  );
813  }
814 
815  public function getDescription() {
816  return array(
817  'Upload a file, or get the status of pending uploads. Several methods are available:',
818  ' * Upload file contents directly, using the "file" parameter',
819  ' * Have the MediaWiki server fetch a file from a URL, using the "url" parameter',
820  ' * Complete an earlier upload that failed due to warnings, using the "filekey" parameter',
821  'Note that the HTTP POST must be done as a file upload (i.e. using multipart/form-data) when',
822  'sending the "file". Also you must get and send an edit token before doing any upload stuff.'
823  );
824  }
825 
826  public function getPossibleErrors() {
827  return array_merge( parent::getPossibleErrors(),
828  $this->getRequireOnlyOneParameterErrorMessages( array( 'filekey', 'file', 'url', 'statuskey' ) ),
829  array(
830  array( 'uploaddisabled' ),
831  array( 'invalid-file-key' ),
832  array( 'uploaddisabled' ),
833  array( 'mustbeloggedin', 'upload' ),
834  array( 'badaccess-groups' ),
835  array( 'code' => 'fetchfileerror', 'info' => '' ),
836  array( 'code' => 'nomodule', 'info' => 'No upload module set' ),
837  array( 'code' => 'empty-file', 'info' => 'The file you submitted was empty' ),
838  array( 'code' => 'filetype-missing', 'info' => 'The file is missing an extension' ),
839  array( 'code' => 'filename-tooshort', 'info' => 'The filename is too short' ),
840  array( 'code' => 'overwrite', 'info' => 'Overwriting an existing file is not allowed' ),
841  array( 'code' => 'stashfailed', 'info' => 'Stashing temporary file failed' ),
842  array( 'code' => 'publishfailed', 'info' => 'Publishing of stashed file failed' ),
843  array( 'code' => 'internal-error', 'info' => 'An internal error occurred' ),
844  array( 'code' => 'asynccopyuploaddisabled', 'info' => 'Asynchronous copy uploads disabled' ),
845  array( 'code' => 'stasherror', 'info' => 'An upload stash error occurred' ),
846  array( 'fileexists-forbidden' ),
847  array( 'fileexists-shared-forbidden' ),
848  )
849  );
850  }
851 
852  public function needsToken() {
853  return true;
854  }
855 
856  public function getTokenSalt() {
857  return '';
858  }
859 
860  public function getExamples() {
861  return array(
862  'api.php?action=upload&filename=Wiki.png' .
863  '&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png'
864  => 'Upload from a URL',
865  'api.php?action=upload&filename=Wiki.png&filekey=filekey&ignorewarnings=1'
866  => 'Complete an upload that failed due to warnings',
867  );
868  }
869 
870  public function getHelpUrls() {
871  return 'https://www.mediawiki.org/wiki/API:Upload';
872  }
873 }
UploadBase
Definition: UploadBase.php:38
ApiUpload\getChunkResult
getChunkResult( $warnings)
Get the result of a chunk upload.
Definition: ApiUpload.php:195
ApiUpload\selectUploadModule
selectUploadModule()
Select an upload module and set it to mUpload.
Definition: ApiUpload.php:325
Title\makeTitle
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:398
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. $title:Title object for the current page $request:WebRequest $ignoreRedirect:boolean to skip redirect check $target:Title/string of redirect target $article:Article object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) $article:article(object) being checked 'IsTrustedProxy':Override the result of wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of User::isValidEmailAddr(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetMagic':DEPRECATED, use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1528
ContextSource\getContext
getContext()
Get the RequestContext object.
Definition: ContextSource.php:40
UploadBase\VERIFICATION_ERROR
const VERIFICATION_ERROR
Definition: UploadBase.php:57
ApiUpload\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiUpload.php:690
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
Definition: ApiBase.php:62
ApiUpload\getStashResult
getStashResult( $warnings)
Get Stash Result, throws an exception if the file could not be stashed.
Definition: ApiUpload.php:151
UploadBase\FILE_TOO_LARGE
const FILE_TOO_LARGE
Definition: UploadBase.php:62
UploadBase\MIN_LENGTH_PARTNAME
const MIN_LENGTH_PARTNAME
Definition: UploadBase.php:52
ApiBase\parseMsg
parseMsg( $error)
Return the error message related to a certain array.
Definition: ApiBase.php:1978
ApiUpload\getPossibleErrors
getPossibleErrors()
Returns a list of all possible errors returned by the module.
Definition: ApiUpload.php:825
ApiUpload\verifyUpload
verifyUpload()
Performs file verification, dies on error.
Definition: ApiUpload.php:466
ApiBase\dieUsageMsg
dieUsageMsg( $error)
Output the error message related to a certain array.
Definition: ApiBase.php:1929
UploadBase\isEnabled
static isEnabled()
Returns true if uploads are enabled.
Definition: UploadBase.php:98
ApiBase\PARAM_TYPE
const PARAM_TYPE
Definition: ApiBase.php:50
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:205
ApiUpload\getWarningsResult
getWarningsResult( $warnings)
Get Warnings Result.
Definition: ApiUpload.php:174
Status\newGood
static newGood( $value=null)
Factory function for good results.
Definition: Status.php:77
NS_FILE
const NS_FILE
Definition: Defines.php:85
$params
$params
Definition: styleTest.css.php:40
ApiUpload\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiUpload.php:35
ApiUpload\getExamples
getExamples()
Returns usage examples for this module.
Definition: ApiUpload.php:859
ApiUpload\getApiWarnings
getApiWarnings()
Check warnings.
Definition: ApiUpload.php:549
ApiUpload\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiUpload.php:694
UploadFromStash
Implements uploading from previously stored file.
Definition: UploadFromStash.php:30
UploadBase\HOOK_ABORTED
const HOOK_ABORTED
Definition: UploadBase.php:61
ApiUpload\getTokenSalt
getTokenSalt()
Returns the token salt if there is one, '' if the module doesn't require a salt, else false if the mo...
Definition: ApiUpload.php:855
UploadBase\OK
const OK
Definition: UploadBase.php:50
ApiUpload\$mParams
$mParams
Definition: ApiUpload.php:33
ContextSource\getUser
getUser()
Get the User object.
Definition: ContextSource.php:132
UploadBase\getSessionStatus
static getSessionStatus( $statusKey)
Get the current status of a chunked upload (used for polling).
Definition: UploadBase.php:1739
UploadBase\EMPTY_FILE
const EMPTY_FILE
Definition: UploadBase.php:51
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:42
UploadFromUrlJob\getSessionData
static & getSessionData( $key)
Definition: UploadFromUrlJob.php:178
PublishStashedFileJob
Upload a file from the upload stash into the local file repo.
Definition: PublishStashedFileJob.php:29
UploadFromUrl\initialize
initialize( $name, $url, $async=false)
Entry point for API upload.
Definition: UploadFromUrl.php:134
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
Definition: ApiBase.php:60
MWException
MediaWiki exception.
Definition: MWException.php:26
UploadFromUrl
Implements uploading from a HTTP resource.
Definition: UploadFromUrl.php:31
ApiUpload\dieRecoverableError
dieRecoverableError( $error, $parameter, $data=array())
Throw an error that the user can recover from by providing a better value for $parameter.
Definition: ApiUpload.php:305
UploadFromUrl\isEnabled
static isEnabled()
Checks if the upload from URL feature is enabled.
Definition: UploadFromUrl.php:59
UploadBase\WINDOWS_NONASCII_FILENAME
const WINDOWS_NONASCII_FILENAME
Definition: UploadBase.php:63
ApiUpload\getContextResult
getContextResult()
Get an upload result based on upload context.
Definition: ApiUpload.php:128
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
ApiUpload
Definition: ApiUpload.php:30
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
$wgFileExtensions
if(! $wgHtml5Version && $wgAllowRdfaAttributes) $wgFileExtensions
Definition: Setup.php:362
ApiBase\getRequireOnlyOneParameterErrorMessages
getRequireOnlyOneParameterErrorMessages( $params)
Generates the possible errors requireOnlyOneParameter() can die with.
Definition: ApiBase.php:748
ApiBase\PROP_TYPE
const PROP_TYPE
Definition: ApiBase.php:74
ApiUpload\getParamDescription
getParamDescription()
Returns an array of parameter descriptions.
Definition: ApiUpload.php:747
ApiBase\getWatchlistValue
getWatchlistValue( $watchlist, $titleObj, $userOption=null)
Return true if we're to watch the page, false if not, null if no change.
Definition: ApiBase.php:913
ApiUpload\needsToken
needsToken()
Returns whether this module requires a token to execute It is used to show possible errors in action=...
Definition: ApiUpload.php:851
$ok
$ok
Definition: UtfNormalTest.php:71
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:933
ApiBase\extractRequestParams
extractRequestParams( $parseLimit=true)
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:687
UploadFromStash\isValidKey
static isValidKey( $key)
Definition: UploadFromStash.php:74
UploadFromUrl\isAllowedUrl
static isAllowedUrl( $url)
Checks whether the URL is not allowed.
Definition: UploadFromUrl.php:115
ApiUpload\getResultProperties
getResultProperties()
Returns possible properties in the result, grouped by the value of the prop parameter that shows them...
Definition: ApiUpload.php:779
ApiBase\dieUsage
dieUsage( $description, $errorCode, $httpRespCode=0, $extradata=null)
Throw a UsageException, which will (if uncaught) call the main module's error handler and die with an...
Definition: ApiBase.php:1363
AssembleUploadChunksJob
Assemble the segments of a chunked upload.
Definition: AssembleUploadChunksJob.php:29
ApiBase\requireOnlyOneParameter
requireOnlyOneParameter( $params)
Die if none or more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:722
ApiUpload\performStash
performStash()
Stash the file and return the file key Also re-raises exceptions with slightly more informative messa...
Definition: ApiUpload.php:279
ApiUpload\getHelpUrls
getHelpUrls()
Definition: ApiUpload.php:869
ApiBase\PROP_NULLABLE
const PROP_NULLABLE
Definition: ApiBase.php:76
ApiUpload\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition: ApiUpload.php:686
ApiUpload\performUpload
performUpload( $warnings)
Perform the actual upload.
Definition: ApiUpload.php:590
UploadBase\FILENAME_TOO_LONG
const FILENAME_TOO_LONG
Definition: UploadBase.php:64
UploadFromUrl\isAllowedHost
static isAllowedHost( $url)
Checks whether the URL is for an allowed host The domains in the whitelist can include wildcard chara...
Definition: UploadFromUrl.php:72
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
UploadBase\ILLEGAL_FILENAME
const ILLEGAL_FILENAME
Definition: UploadBase.php:53
$file
if(PHP_SAPI !='cli') $file
Definition: UtfNormalTest2.php:30
UploadBase\setSessionStatus
static setSessionStatus( $statusKey, $value)
Set the current status of a chunked upload (used for polling).
Definition: UploadBase.php:1752
ApiUpload\$mUpload
UploadBase $mUpload
Definition: ApiUpload.php:31
ContextSource\exportSession
exportSession()
Export the resolved user IP, HTTP headers, user ID, and session ID.
Definition: ContextSource.php:188
JobQueueGroup\singleton
static singleton( $wiki=false)
Definition: JobQueueGroup.php:61
ApiBase\PARAM_DFLT
const PARAM_DFLT
Definition: ApiBase.php:46
ApiUpload\checkPermissions
checkPermissions( $user)
Checks that the user has permissions to perform this upload.
Definition: ApiUpload.php:450
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:148
ApiBase\getMain
getMain()
Get the main module.
Definition: ApiBase.php:188
ApiUpload\checkAsyncDownloadEnabled
checkAsyncDownloadEnabled()
Checks if asynchronous copy uploads are enabled and throws an error if they are not.
Definition: ApiUpload.php:679
UploadFromChunks
Implements uploading from chunks.
Definition: UploadFromChunks.php:30
ApiUpload\transformWarnings
transformWarnings( $warnings)
Definition: ApiUpload.php:555
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2573
$e
if( $useReadline) $e
Definition: eval.php:66
UploadFromFile
Implements regular file uploads.
Definition: UploadFromFile.php:30
ApiUpload\getDescription
getDescription()
Returns the description string for this module.
Definition: ApiUpload.php:814
UploadStashException
Definition: UploadStash.php:679
ApiUpload\checkVerification
checkVerification(array $verification)
Performs file verification, dies on error.
Definition: ApiUpload.php:478
UploadBase\FILETYPE_MISSING
const FILETYPE_MISSING
Definition: UploadBase.php:55
UploadBase\FILETYPE_BADTYPE
const FILETYPE_BADTYPE
Definition: UploadBase.php:56