MediaWiki REL1_27
ApiUpload.php
Go to the documentation of this file.
1<?php
30class ApiUpload extends ApiBase {
32 protected $mUpload = null;
33
34 protected $mParams;
35
36 public function execute() {
37 // Check whether upload is enabled
38 if ( !UploadBase::isEnabled() ) {
39 $this->dieUsageMsg( 'uploaddisabled' );
40 }
41
42 $user = $this->getUser();
43
44 // Parameter handling
45 $this->mParams = $this->extractRequestParams();
46 $request = $this->getMain()->getRequest();
47 // Check if async mode is actually supported (jobs done in cli mode)
48 $this->mParams['async'] = ( $this->mParams['async'] &&
49 $this->getConfig()->get( 'EnableAsyncUploads' ) );
50 // Add the uploaded file to the params array
51 $this->mParams['file'] = $request->getFileName( 'file' );
52 $this->mParams['chunk'] = $request->getFileName( 'chunk' );
53
54 // Copy the session key to the file key, for backward compatibility.
55 if ( !$this->mParams['filekey'] && $this->mParams['sessionkey'] ) {
56 $this->mParams['filekey'] = $this->mParams['sessionkey'];
57 }
58
59 // Select an upload module
60 try {
61 if ( !$this->selectUploadModule() ) {
62 return; // not a true upload, but a status request or similar
63 } elseif ( !isset( $this->mUpload ) ) {
64 $this->dieUsage( 'No upload module set', 'nomodule' );
65 }
66 } catch ( UploadStashException $e ) { // XXX: don't spam exception log
67 $this->handleStashException( $e );
68 }
69
70 // First check permission to upload
71 $this->checkPermissions( $user );
72
73 // Fetch the file (usually a no-op)
75 $status = $this->mUpload->fetchFile();
76 if ( !$status->isGood() ) {
77 $errors = $status->getErrorsArray();
78 $error = array_shift( $errors[0] );
79 $this->dieUsage( 'Error fetching file from remote source', $error, 0, $errors[0] );
80 }
81
82 // Check if the uploaded file is sane
83 if ( $this->mParams['chunk'] ) {
85 if ( $this->mParams['filesize'] > $maxSize ) {
86 $this->dieUsage( 'The file you submitted was too large', 'file-too-large' );
87 }
88 if ( !$this->mUpload->getTitle() ) {
89 $this->dieUsage( 'Invalid file title supplied', 'internal-error' );
90 }
91 } elseif ( $this->mParams['async'] && $this->mParams['filekey'] ) {
92 // defer verification to background process
93 } else {
94 wfDebug( __METHOD__ . " about to verify\n" );
95 $this->verifyUpload();
96 }
97
98 // Check if the user has the rights to modify or overwrite the requested title
99 // (This check is irrelevant if stashing is already requested, since the errors
100 // can always be fixed by changing the title)
101 if ( !$this->mParams['stash'] ) {
102 $permErrors = $this->mUpload->verifyTitlePermissions( $user );
103 if ( $permErrors !== true ) {
104 $this->dieRecoverableError( $permErrors[0], 'filename' );
105 }
106 }
107
108 // Get the result based on the current upload context:
109 try {
110 $result = $this->getContextResult();
111 if ( $result['result'] === 'Success' ) {
112 $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() );
113 }
114 } catch ( UploadStashException $e ) { // XXX: don't spam exception log
115 $this->handleStashException( $e );
116 }
117
118 $this->getResult()->addValue( null, $this->getModuleName(), $result );
119
120 // Cleanup any temporary mess
121 $this->mUpload->cleanupTempFile();
122 }
123
128 private function getContextResult() {
129 $warnings = $this->getApiWarnings();
130 if ( $warnings && !$this->mParams['ignorewarnings'] ) {
131 // Get warnings formatted in result array format
132 return $this->getWarningsResult( $warnings );
133 } elseif ( $this->mParams['chunk'] ) {
134 // Add chunk, and get result
135 return $this->getChunkResult( $warnings );
136 } elseif ( $this->mParams['stash'] ) {
137 // Stash the file and get stash result
138 return $this->getStashResult( $warnings );
139 }
140
141 // Check throttle after we've handled warnings
142 if ( UploadBase::isThrottled( $this->getUser() )
143 ) {
144 $this->dieUsageMsg( 'actionthrottledtext' );
145 }
146
147 // This is the most common case -- a normal upload with no warnings
148 // performUpload will return a formatted properly for the API with status
149 return $this->performUpload( $warnings );
150 }
151
157 private function getStashResult( $warnings ) {
158 $result = [];
159 // Some uploads can request they be stashed, so as not to publish them immediately.
160 // In this case, a failure to stash ought to be fatal
161 try {
162 $result['result'] = 'Success';
163 $result['filekey'] = $this->performStash();
164 $result['sessionkey'] = $result['filekey']; // backwards compatibility
165 if ( $warnings && count( $warnings ) > 0 ) {
166 $result['warnings'] = $warnings;
167 }
168 } catch ( UploadStashException $e ) {
169 $this->handleStashException( $e );
170 } catch ( Exception $e ) {
171 $this->dieUsage( $e->getMessage(), 'stashfailed' );
172 }
173
174 return $result;
175 }
176
182 private function getWarningsResult( $warnings ) {
183 $result = [];
184 $result['result'] = 'Warning';
185 $result['warnings'] = $warnings;
186 // in case the warnings can be fixed with some further user action, let's stash this upload
187 // and return a key they can use to restart it
188 try {
189 $result['filekey'] = $this->performStash();
190 $result['sessionkey'] = $result['filekey']; // backwards compatibility
191 } catch ( Exception $e ) {
192 $result['warnings']['stashfailed'] = $e->getMessage();
193 }
194
195 return $result;
196 }
197
203 private function getChunkResult( $warnings ) {
204 $result = [];
205
206 if ( $warnings && count( $warnings ) > 0 ) {
207 $result['warnings'] = $warnings;
208 }
209
210 $request = $this->getMain()->getRequest();
211 $chunkPath = $request->getFileTempname( 'chunk' );
212 $chunkSize = $request->getUpload( 'chunk' )->getSize();
213 $totalSoFar = $this->mParams['offset'] + $chunkSize;
214 $minChunkSize = $this->getConfig()->get( 'MinUploadChunkSize' );
215
216 // Sanity check sizing
217 if ( $totalSoFar > $this->mParams['filesize'] ) {
218 $this->dieUsage(
219 'Offset plus current chunk is greater than claimed file size', 'invalid-chunk'
220 );
221 }
222
223 // Enforce minimum chunk size
224 if ( $totalSoFar != $this->mParams['filesize'] && $chunkSize < $minChunkSize ) {
225 $this->dieUsage(
226 "Minimum chunk size is $minChunkSize bytes for non-final chunks", 'chunk-too-small'
227 );
228 }
229
230 if ( $this->mParams['offset'] == 0 ) {
231 try {
232 $filekey = $this->performStash();
233 } catch ( UploadStashException $e ) {
234 $this->handleStashException( $e );
235 } catch ( Exception $e ) {
236 // FIXME: Error handling here is wrong/different from rest of this
237 $this->dieUsage( $e->getMessage(), 'stashfailed' );
238 }
239 } else {
240 $filekey = $this->mParams['filekey'];
241
242 // Don't allow further uploads to an already-completed session
243 $progress = UploadBase::getSessionStatus( $this->getUser(), $filekey );
244 if ( !$progress ) {
245 // Probably can't get here, but check anyway just in case
246 $this->dieUsage( 'No chunked upload session with this key', 'stashfailed' );
247 } elseif ( $progress['result'] !== 'Continue' || $progress['stage'] !== 'uploading' ) {
248 $this->dieUsage(
249 'Chunked upload is already completed, check status for details', 'stashfailed'
250 );
251 }
252
253 $status = $this->mUpload->addChunk(
254 $chunkPath, $chunkSize, $this->mParams['offset'] );
255 if ( !$status->isGood() ) {
256 $extradata = [
257 'offset' => $this->mUpload->getOffset(),
258 ];
259
260 $this->dieUsage( $status->getWikiText( false, false, 'en' ), 'stashfailed', 0, $extradata );
261 }
262 }
263
264 // Check we added the last chunk:
265 if ( $totalSoFar == $this->mParams['filesize'] ) {
266 if ( $this->mParams['async'] ) {
268 $this->getUser(),
269 $filekey,
270 [ 'result' => 'Poll',
271 'stage' => 'queued', 'status' => Status::newGood() ]
272 );
274 Title::makeTitle( NS_FILE, $filekey ),
275 [
276 'filename' => $this->mParams['filename'],
277 'filekey' => $filekey,
278 'session' => $this->getContext()->exportSession()
279 ]
280 ) );
281 $result['result'] = 'Poll';
282 $result['stage'] = 'queued';
283 } else {
284 $status = $this->mUpload->concatenateChunks();
285 if ( !$status->isGood() ) {
287 $this->getUser(),
288 $filekey,
289 [ 'result' => 'Failure', 'stage' => 'assembling', 'status' => $status ]
290 );
291 $this->dieUsage( $status->getWikiText( false, false, 'en' ), 'stashfailed' );
292 }
293
294 // The fully concatenated file has a new filekey. So remove
295 // the old filekey and fetch the new one.
296 UploadBase::setSessionStatus( $this->getUser(), $filekey, false );
297 $this->mUpload->stash->removeFile( $filekey );
298 $filekey = $this->mUpload->getLocalFile()->getFileKey();
299
300 $result['result'] = 'Success';
301 }
302 } else {
304 $this->getUser(),
305 $filekey,
306 [
307 'result' => 'Continue',
308 'stage' => 'uploading',
309 'offset' => $totalSoFar,
310 'status' => Status::newGood(),
311 ]
312 );
313 $result['result'] = 'Continue';
314 $result['offset'] = $totalSoFar;
315 }
316
317 $result['filekey'] = $filekey;
318
319 return $result;
320 }
321
328 private function performStash() {
329 try {
330 $stashFile = $this->mUpload->stashFile( $this->getUser() );
331
332 if ( !$stashFile ) {
333 throw new MWException( 'Invalid stashed file' );
334 }
335 $fileKey = $stashFile->getFileKey();
336 } catch ( Exception $e ) {
337 $message = 'Stashing temporary file failed: ' . get_class( $e ) . ' ' . $e->getMessage();
338 wfDebug( __METHOD__ . ' ' . $message . "\n" );
339 $className = get_class( $e );
340 throw new $className( $message );
341 }
342
343 return $fileKey;
344 }
345
355 private function dieRecoverableError( $error, $parameter, $data = [] ) {
356 try {
357 $data['filekey'] = $this->performStash();
358 $data['sessionkey'] = $data['filekey'];
359 } catch ( Exception $e ) {
360 $data['stashfailed'] = $e->getMessage();
361 }
362 $data['invalidparameter'] = $parameter;
363
364 $parsed = $this->parseMsg( $error );
365 if ( isset( $parsed['data'] ) ) {
366 $data = array_merge( $data, $parsed['data'] );
367 }
368
369 $this->dieUsage( $parsed['info'], $parsed['code'], 0, $data );
370 }
371
379 protected function selectUploadModule() {
380 $request = $this->getMain()->getRequest();
381
382 // chunk or one and only one of the following parameters is needed
383 if ( !$this->mParams['chunk'] ) {
384 $this->requireOnlyOneParameter( $this->mParams,
385 'filekey', 'file', 'url' );
386 }
387
388 // Status report for "upload to stash"/"upload from stash"
389 if ( $this->mParams['filekey'] && $this->mParams['checkstatus'] ) {
390 $progress = UploadBase::getSessionStatus( $this->getUser(), $this->mParams['filekey'] );
391 if ( !$progress ) {
392 $this->dieUsage( 'No result in status data', 'missingresult' );
393 } elseif ( !$progress['status']->isGood() ) {
394 $this->dieUsage( $progress['status']->getWikiText( false, false, 'en' ), 'stashfailed' );
395 }
396 if ( isset( $progress['status']->value['verification'] ) ) {
397 $this->checkVerification( $progress['status']->value['verification'] );
398 }
399 unset( $progress['status'] ); // remove Status object
400 $this->getResult()->addValue( null, $this->getModuleName(), $progress );
401
402 return false;
403 }
404
405 // The following modules all require the filename parameter to be set
406 if ( is_null( $this->mParams['filename'] ) ) {
407 $this->dieUsageMsg( [ 'missingparam', 'filename' ] );
408 }
409
410 if ( $this->mParams['chunk'] ) {
411 // Chunk upload
412 $this->mUpload = new UploadFromChunks();
413 if ( isset( $this->mParams['filekey'] ) ) {
414 if ( $this->mParams['offset'] === 0 ) {
415 $this->dieUsage( 'Cannot supply a filekey when offset is 0', 'badparams' );
416 }
417
418 // handle new chunk
419 $this->mUpload->continueChunks(
420 $this->mParams['filename'],
421 $this->mParams['filekey'],
422 $request->getUpload( 'chunk' )
423 );
424 } else {
425 if ( $this->mParams['offset'] !== 0 ) {
426 $this->dieUsage( 'Must supply a filekey when offset is non-zero', 'badparams' );
427 }
428
429 // handle first chunk
430 $this->mUpload->initialize(
431 $this->mParams['filename'],
432 $request->getUpload( 'chunk' )
433 );
434 }
435 } elseif ( isset( $this->mParams['filekey'] ) ) {
436 // Upload stashed in a previous request
437 if ( !UploadFromStash::isValidKey( $this->mParams['filekey'] ) ) {
438 $this->dieUsageMsg( 'invalid-file-key' );
439 }
440
441 $this->mUpload = new UploadFromStash( $this->getUser() );
442 // This will not download the temp file in initialize() in async mode.
443 // We still have enough information to call checkWarnings() and such.
444 $this->mUpload->initialize(
445 $this->mParams['filekey'], $this->mParams['filename'], !$this->mParams['async']
446 );
447 } elseif ( isset( $this->mParams['file'] ) ) {
448 $this->mUpload = new UploadFromFile();
449 $this->mUpload->initialize(
450 $this->mParams['filename'],
451 $request->getUpload( 'file' )
452 );
453 } elseif ( isset( $this->mParams['url'] ) ) {
454 // Make sure upload by URL is enabled:
455 if ( !UploadFromUrl::isEnabled() ) {
456 $this->dieUsageMsg( 'copyuploaddisabled' );
457 }
458
459 if ( !UploadFromUrl::isAllowedHost( $this->mParams['url'] ) ) {
460 $this->dieUsageMsg( 'copyuploadbaddomain' );
461 }
462
463 if ( !UploadFromUrl::isAllowedUrl( $this->mParams['url'] ) ) {
464 $this->dieUsageMsg( 'copyuploadbadurl' );
465 }
466
467 $this->mUpload = new UploadFromUrl;
468 $this->mUpload->initialize( $this->mParams['filename'],
469 $this->mParams['url'] );
470 }
471
472 return true;
473 }
474
480 protected function checkPermissions( $user ) {
481 // Check whether the user has the appropriate permissions to upload anyway
482 $permission = $this->mUpload->isAllowed( $user );
483
484 if ( $permission !== true ) {
485 if ( !$user->isLoggedIn() ) {
486 $this->dieUsageMsg( [ 'mustbeloggedin', 'upload' ] );
487 }
488
489 $this->dieUsageMsg( 'badaccess-groups' );
490 }
491
492 // Check blocks
493 if ( $user->isBlocked() ) {
494 $this->dieBlocked( $user->getBlock() );
495 }
496
497 // Global blocks
498 if ( $user->isBlockedGlobally() ) {
499 $this->dieBlocked( $user->getGlobalBlock() );
500 }
501 }
502
506 protected function verifyUpload() {
507 $verification = $this->mUpload->verifyUpload();
508 if ( $verification['status'] === UploadBase::OK ) {
509 return;
510 }
511
512 $this->checkVerification( $verification );
513 }
514
519 protected function checkVerification( array $verification ) {
520 // @todo Move them to ApiBase's message map
521 switch ( $verification['status'] ) {
522 // Recoverable errors
524 $this->dieRecoverableError( 'filename-tooshort', 'filename' );
525 break;
527 $this->dieRecoverableError( 'illegal-filename', 'filename',
528 [ 'filename' => $verification['filtered'] ] );
529 break;
531 $this->dieRecoverableError( 'filename-toolong', 'filename' );
532 break;
534 $this->dieRecoverableError( 'filetype-missing', 'filename' );
535 break;
537 $this->dieRecoverableError( 'windows-nonascii-filename', 'filename' );
538 break;
539
540 // Unrecoverable errors
542 $this->dieUsage( 'The file you submitted was empty', 'empty-file' );
543 break;
545 $this->dieUsage( 'The file you submitted was too large', 'file-too-large' );
546 break;
547
549 $extradata = [
550 'filetype' => $verification['finalExt'],
551 'allowed' => array_values( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) )
552 ];
553 ApiResult::setIndexedTagName( $extradata['allowed'], 'ext' );
554
555 $msg = 'Filetype not permitted: ';
556 if ( isset( $verification['blacklistedExt'] ) ) {
557 $msg .= implode( ', ', $verification['blacklistedExt'] );
558 $extradata['blacklisted'] = array_values( $verification['blacklistedExt'] );
559 ApiResult::setIndexedTagName( $extradata['blacklisted'], 'ext' );
560 } else {
561 $msg .= $verification['finalExt'];
562 }
563 $this->dieUsage( $msg, 'filetype-banned', 0, $extradata );
564 break;
566 $params = $verification['details'];
567 $key = array_shift( $params );
568 $msg = $this->msg( $key, $params )->inLanguage( 'en' )->useDatabase( false )->text();
569 ApiResult::setIndexedTagName( $verification['details'], 'detail' );
570 $this->dieUsage( "This file did not pass file verification: $msg", 'verification-error',
571 0, [ 'details' => $verification['details'] ] );
572 break;
574 if ( is_array( $verification['error'] ) ) {
575 $params = $verification['error'];
576 } elseif ( $verification['error'] !== '' ) {
577 $params = [ $verification['error'] ];
578 } else {
579 $params = [ 'hookaborted' ];
580 }
581 $key = array_shift( $params );
582 $msg = $this->msg( $key, $params )->inLanguage( 'en' )->useDatabase( false )->text();
583 $this->dieUsage( $msg, 'hookaborted', 0, [ 'details' => $verification['error'] ] );
584 break;
585 default:
586 $this->dieUsage( 'An unknown error occurred', 'unknown-error',
587 0, [ 'details' => [ 'code' => $verification['status'] ] ] );
588 break;
589 }
590 }
591
599 protected function getApiWarnings() {
600 $warnings = $this->mUpload->checkWarnings();
601
602 return $this->transformWarnings( $warnings );
603 }
604
605 protected function transformWarnings( $warnings ) {
606 if ( $warnings ) {
607 // Add indices
608 ApiResult::setIndexedTagName( $warnings, 'warning' );
609
610 if ( isset( $warnings['duplicate'] ) ) {
611 $dupes = [];
613 foreach ( $warnings['duplicate'] as $dupe ) {
614 $dupes[] = $dupe->getName();
615 }
616 ApiResult::setIndexedTagName( $dupes, 'duplicate' );
617 $warnings['duplicate'] = $dupes;
618 }
619
620 if ( isset( $warnings['exists'] ) ) {
621 $warning = $warnings['exists'];
622 unset( $warnings['exists'] );
624 $localFile = isset( $warning['normalizedFile'] )
625 ? $warning['normalizedFile']
626 : $warning['file'];
627 $warnings[$warning['warning']] = $localFile->getName();
628 }
629 }
630
631 return $warnings;
632 }
633
638 protected function handleStashException( $e ) {
639 $exceptionType = get_class( $e );
640
641 switch ( $exceptionType ) {
642 case 'UploadStashFileNotFoundException':
643 $this->dieUsage(
644 'Could not find the file in the stash: ' . $e->getMessage(),
645 'stashedfilenotfound'
646 );
647 break;
648 case 'UploadStashBadPathException':
649 $this->dieUsage(
650 'File key of improper format or otherwise invalid: ' . $e->getMessage(),
651 'stashpathinvalid'
652 );
653 break;
654 case 'UploadStashFileException':
655 $this->dieUsage(
656 'Could not store upload in the stash: ' . $e->getMessage(),
657 'stashfilestorage'
658 );
659 break;
660 case 'UploadStashZeroLengthFileException':
661 $this->dieUsage(
662 'File is of zero length, and could not be stored in the stash: ' .
663 $e->getMessage(),
664 'stashzerolength'
665 );
666 break;
667 case 'UploadStashNotLoggedInException':
668 $this->dieUsage( 'Not logged in: ' . $e->getMessage(), 'stashnotloggedin' );
669 break;
670 case 'UploadStashWrongOwnerException':
671 $this->dieUsage( 'Wrong owner: ' . $e->getMessage(), 'stashwrongowner' );
672 break;
673 case 'UploadStashNoSuchKeyException':
674 $this->dieUsage( 'No such filekey: ' . $e->getMessage(), 'stashnosuchfilekey' );
675 break;
676 default:
677 $this->dieUsage( $exceptionType . ': ' . $e->getMessage(), 'stasherror' );
678 break;
679 }
680 }
681
689 protected function performUpload( $warnings ) {
690 // Use comment as initial page text by default
691 if ( is_null( $this->mParams['text'] ) ) {
692 $this->mParams['text'] = $this->mParams['comment'];
693 }
694
696 $file = $this->mUpload->getLocalFile();
697
698 // For preferences mode, we want to watch if 'watchdefault' is set,
699 // or if the *file* doesn't exist, and either 'watchuploads' or
700 // 'watchcreations' is set. But getWatchlistValue()'s automatic
701 // handling checks if the *title* exists or not, so we need to check
702 // all three preferences manually.
703 $watch = $this->getWatchlistValue(
704 $this->mParams['watchlist'], $file->getTitle(), 'watchdefault'
705 );
706
707 if ( !$watch && $this->mParams['watchlist'] == 'preferences' && !$file->exists() ) {
708 $watch = (
709 $this->getWatchlistValue( 'preferences', $file->getTitle(), 'watchuploads' ) ||
710 $this->getWatchlistValue( 'preferences', $file->getTitle(), 'watchcreations' )
711 );
712 }
713
714 // Deprecated parameters
715 if ( $this->mParams['watch'] ) {
716 $watch = true;
717 }
718
719 if ( $this->mParams['tags'] ) {
720 $status = ChangeTags::canAddTagsAccompanyingChange( $this->mParams['tags'], $this->getUser() );
721 if ( !$status->isOK() ) {
722 $this->dieStatus( $status );
723 }
724 }
725
726 // No errors, no warnings: do the upload
727 if ( $this->mParams['async'] ) {
728 $progress = UploadBase::getSessionStatus( $this->getUser(), $this->mParams['filekey'] );
729 if ( $progress && $progress['result'] === 'Poll' ) {
730 $this->dieUsage( 'Upload from stash already in progress.', 'publishfailed' );
731 }
733 $this->getUser(),
734 $this->mParams['filekey'],
735 [ 'result' => 'Poll', 'stage' => 'queued', 'status' => Status::newGood() ]
736 );
738 Title::makeTitle( NS_FILE, $this->mParams['filename'] ),
739 [
740 'filename' => $this->mParams['filename'],
741 'filekey' => $this->mParams['filekey'],
742 'comment' => $this->mParams['comment'],
743 'tags' => $this->mParams['tags'],
744 'text' => $this->mParams['text'],
745 'watch' => $watch,
746 'session' => $this->getContext()->exportSession()
747 ]
748 ) );
749 $result['result'] = 'Poll';
750 $result['stage'] = 'queued';
751 } else {
753 $status = $this->mUpload->performUpload( $this->mParams['comment'],
754 $this->mParams['text'], $watch, $this->getUser(), $this->mParams['tags'] );
755
756 if ( !$status->isGood() ) {
757 $error = $status->getErrorsArray();
758 ApiResult::setIndexedTagName( $error, 'error' );
759 $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error );
760 }
761 $result['result'] = 'Success';
762 }
763
764 $result['filename'] = $file->getName();
765 if ( $warnings && count( $warnings ) > 0 ) {
766 $result['warnings'] = $warnings;
767 }
768
769 return $result;
770 }
771
772 public function mustBePosted() {
773 return true;
774 }
775
776 public function isWriteMode() {
777 return true;
778 }
779
780 public function getAllowedParams() {
781 $params = [
782 'filename' => [
783 ApiBase::PARAM_TYPE => 'string',
784 ],
785 'comment' => [
787 ],
788 'tags' => [
789 ApiBase::PARAM_TYPE => 'tags',
791 ],
792 'text' => [
793 ApiBase::PARAM_TYPE => 'text',
794 ],
795 'watch' => [
796 ApiBase::PARAM_DFLT => false,
798 ],
799 'watchlist' => [
800 ApiBase::PARAM_DFLT => 'preferences',
802 'watch',
803 'preferences',
804 'nochange'
805 ],
806 ],
807 'ignorewarnings' => false,
808 'file' => [
809 ApiBase::PARAM_TYPE => 'upload',
810 ],
811 'url' => null,
812 'filekey' => null,
813 'sessionkey' => [
815 ],
816 'stash' => false,
817
818 'filesize' => [
819 ApiBase::PARAM_TYPE => 'integer',
822 ],
823 'offset' => [
824 ApiBase::PARAM_TYPE => 'integer',
826 ],
827 'chunk' => [
828 ApiBase::PARAM_TYPE => 'upload',
829 ],
830
831 'async' => false,
832 'checkstatus' => false,
833 ];
834
835 return $params;
836 }
837
838 public function needsToken() {
839 return 'csrf';
840 }
841
842 protected function getExamplesMessages() {
843 return [
844 'action=upload&filename=Wiki.png' .
845 '&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png&token=123ABC'
846 => 'apihelp-upload-example-url',
847 'action=upload&filename=Wiki.png&filekey=filekey&ignorewarnings=1&token=123ABC'
848 => 'apihelp-upload-example-filekey',
849 ];
850 }
851
852 public function getHelpUrls() {
853 return 'https://www.mediawiki.org/wiki/API:Upload';
854 }
855}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:39
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition ApiBase.php:106
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition ApiBase.php:91
getMain()
Get the main module.
Definition ApiBase.php:480
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition ApiBase.php:88
parseMsg( $error)
Return the error message related to a certain array.
Definition ApiBase.php:2194
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition ApiBase.php:50
extractRequestParams( $parseLimit=true)
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:685
dieUsageMsg( $error)
Output the error message related to a certain array.
Definition ApiBase.php:2144
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition ApiBase.php:100
getResult()
Get the result object.
Definition ApiBase.php:584
getWatchlistValue( $watchlist, $titleObj, $userOption=null)
Return true if we're to watch the page, false if not, null if no change.
Definition ApiBase.php:877
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:464
dieStatus( $status)
Throw a UsageException based on the errors in the Status object.
Definition ApiBase.php:1615
dieBlocked(Block $block)
Throw a UsageException, which will (if uncaught) call the main module's error handler and die with an...
Definition ApiBase.php:1543
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:1526
requireOnlyOneParameter( $params, $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:721
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition ApiBase.php:53
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition ApiUpload.php:36
checkPermissions( $user)
Checks that the user has permissions to perform this upload.
verifyUpload()
Performs file verification, dies on error.
UploadBase UploadFromChunks $mUpload
Definition ApiUpload.php:32
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
transformWarnings( $warnings)
handleStashException( $e)
Handles a stash exception, giving a useful error to the user.
getHelpUrls()
Return links to more detailed help pages about the module.
isWriteMode()
Indicates whether this module requires write mode.
getWarningsResult( $warnings)
Get Warnings Result.
getContextResult()
Get an upload result based on upload context.
getChunkResult( $warnings)
Get the result of a chunk upload.
getExamplesMessages()
Returns usage examples for this module.
selectUploadModule()
Select an upload module and set it to mUpload.
getStashResult( $warnings)
Get Stash Result, throws an exception if the file could not be stashed.
needsToken()
Returns the token type this module requires in order to execute.
performUpload( $warnings)
Perform the actual upload.
dieRecoverableError( $error, $parameter, $data=[])
Throw an error that the user can recover from by providing a better value for $parameter.
performStash()
Stash the file and return the file key Also re-raises exceptions with slightly more informative messa...
checkVerification(array $verification)
Performs file verification, dies on error.
mustBePosted()
Indicates whether this module must be called with a POST request.
getApiWarnings()
Check warnings.
Assemble the segments of a chunked upload.
static canAddTagsAccompanyingChange(array $tags, User $user=null)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
getUser()
Get the User object.
getRequest()
Get the WebRequest object.
getConfig()
Get the Config object.
msg()
Get a Message object with context set Parameters are the same as wfMessage()
exportSession()
Export the resolved user IP, HTTP headers, user ID, and session ID.
getContext()
Get the base IContextSource object.
static singleton( $wiki=false)
MediaWiki exception.
Upload a file from the upload stash into the local file repo.
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition Title.php:524
UploadBase and subclasses are the backend of MediaWiki's file uploads.
const EMPTY_FILE
static getSessionStatus(User $user, $statusKey)
Get the current status of a chunked upload (used for polling)
const FILETYPE_MISSING
static isEnabled()
Returns true if uploads are enabled.
const HOOK_ABORTED
const VERIFICATION_ERROR
const WINDOWS_NONASCII_FILENAME
const FILETYPE_BADTYPE
static getMaxUploadSize( $forType=null)
Get the MediaWiki maximum uploaded file size for given type of upload, based on $wgMaxUploadSize.
const FILE_TOO_LARGE
static isThrottled( $user)
Returns true if the user has surpassed the upload rate limit, false otherwise.
const ILLEGAL_FILENAME
const MIN_LENGTH_PARTNAME
const FILENAME_TOO_LONG
static setSessionStatus(User $user, $statusKey, $value)
Set the current status of a chunked upload (used for polling)
Implements uploading from chunks.
Implements regular file uploads.
Implements uploading from previously stored file.
static isValidKey( $key)
Implements uploading from a HTTP resource.
initialize( $name, $url)
Entry point for API upload.
static isAllowedHost( $url)
Checks whether the URL is for an allowed host The domains in the whitelist can include wildcard chara...
static isAllowedUrl( $url)
Checks whether the URL is not allowed.
static isEnabled()
Checks if the upload from URL feature is enabled.
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
const NS_FILE
Definition Defines.php:76
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition hooks.txt:1007
the array() calling protocol came about after MediaWiki 1.4rc1.
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 local account $user
Definition hooks.txt:249
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. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. '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 '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 '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 '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. '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 IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() '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 Sanitizer::validateEmail(), 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. '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) '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 '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. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. '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:1799
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2530
returning false will NOT prevent logging $e
Definition hooks.txt:1940
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
$params