23 namespace MediaWiki\Specials;
44 use Wikimedia\RequestTimeout\TimeoutException;
77 private const MAX_SERVE_BYTES = 1048576;
89 parent::__construct(
'UploadStash',
'upload' );
91 $this->httpRequestFactory = $httpRequestFactory;
92 $this->urlUtils = $urlUtils;
109 $this->stash = $this->localRepo->getUploadStash( $this->
getUser() );
112 if ( $subPage ===
null || $subPage ===
'' ) {
113 $this->showUploads();
131 $params = $this->parseKey( $key );
132 if ( $params[
'type'] ===
'thumb' ) {
133 $this->outputThumbFromStash( $params[
'file'], $params[
'params'] );
135 $this->outputLocalFile( $params[
'file'] );
140 $message = $e->getMessage();
141 }
catch ( Exception $e ) {
143 $message = $e->getMessage();
158 private function parseKey( $key ) {
159 $type = strtok( $key,
'/' );
161 if ( $type !==
'file' && $type !==
'thumb' ) {
163 $this->
msg(
'uploadstash-bad-path-unknown-type', $type )
166 $fileName = strtok(
'/' );
167 $thumbPart = strtok(
'/' );
168 $file = $this->stash->getFile( $fileName );
169 if ( $type ===
'thumb' ) {
170 $srcNamePos = strrpos( $thumbPart, $fileName );
171 if ( $srcNamePos ===
false || $srcNamePos < 1 ) {
173 $this->
msg(
'uploadstash-bad-path-unrecognized-thumb-name' )
176 $paramString = substr( $thumbPart, 0, $srcNamePos - 1 );
178 $handler =
$file->getHandler();
180 $params = $handler->parseParamString( $paramString );
182 return [
'file' =>
$file,
'type' => $type,
'params' => $params ];
185 $this->
msg(
'uploadstash-bad-path-no-handler', $file->getMimeType(),
$file->getPath() )
190 return [
'file' =>
$file,
'type' => $type ];
199 private function outputThumbFromStash(
$file, $params ) {
205 if (
$file->getRepo()->getThumbProxyUrl()
208 $this->outputRemoteScaledThumb(
$file, $params );
210 $this->outputLocallyScaledThumb(
$file, $params );
221 private function outputLocallyScaledThumb(
$file, $params ) {
226 if ( !$thumbnailImage ) {
228 $this->
msg(
'uploadstash-file-not-found-no-thumb' )
233 if ( !$thumbnailImage->getStoragePath() ) {
235 $this->
msg(
'uploadstash-file-not-found-no-local-path' )
242 $this->stash->repo, $thumbnailImage->getStoragePath(),
false );
244 $this->outputLocalFile( $thumbFile );
264 private function outputRemoteScaledThumb(
$file, $params ) {
268 $scalerThumbName =
$file->generateThumbName(
$file->getName(), $params );
272 $thumbProxyUrl =
$file->getRepo()->getThumbProxyUrl();
273 if ( strlen( $thumbProxyUrl ) ) {
274 $scalerThumbUrl = $thumbProxyUrl .
'temp/' .
$file->getUrlRel() .
275 '/' . rawurlencode( $scalerThumbName );
276 $secret =
$file->getRepo()->getThumbProxySecret();
283 if ( preg_match(
'/^\/\//', $scalerBaseUrl ) ) {
287 $scalerBaseUrl = $this->urlUtils->expand( $scalerBaseUrl,
PROTO_CANONICAL );
290 $scalerThumbUrl = $scalerBaseUrl .
'/' .
$file->getUrlRel() .
291 '/' . rawurlencode( $scalerThumbName );
301 $req = $this->httpRequestFactory->create( $scalerThumbUrl, $httpOptions, __METHOD__ );
304 if ( strlen( $secret ) ) {
305 $req->setHeader(
'X-Swift-Secret', $secret );
308 $status = $req->execute();
309 if ( !$status->isOK() ) {
310 $errors = $status->getErrorsArray();
313 'uploadstash-file-not-found-no-remote-thumb',
314 print_r( $errors, 1 ),
319 $contentType = $req->getResponseHeader(
"content-type" );
320 if ( !$contentType ) {
322 $this->
msg(
'uploadstash-file-not-found-missing-content-type' )
326 $this->outputContents( $req->getContent(), $contentType );
337 private function outputLocalFile(
File $file ) {
338 if (
$file->getSize() > self::MAX_SERVE_BYTES ) {
340 $this->
msg(
'uploadstash-file-too-large', self::MAX_SERVE_BYTES )
344 $file->getRepo()->streamFileWithStatus(
$file->getPath(),
345 [
'Content-Transfer-Encoding: binary',
346 'Expires: Sun, 17-Jan-2038 19:14:07 GMT' ]
357 private function outputContents(
$content, $contentType ) {
359 if ( $size > self::MAX_SERVE_BYTES ) {
361 $this->
msg(
'uploadstash-file-too-large', self::MAX_SERVE_BYTES )
366 self::outputFileHeaders( $contentType, $size );
379 private static function outputFileHeaders( $contentType, $size ) {
380 header(
"Content-Type: $contentType",
true );
381 header(
'Content-Transfer-Encoding: binary',
true );
382 header(
'Expires: Sun, 17-Jan-2038 19:14:07 GMT',
true );
384 header(
'Cache-Control: private' );
385 header(
"Content-Length: $size",
true );
392 private function showUploads() {
406 ], $this->
getContext(),
'clearStashedUploads' );
408 $form->setSubmitDestructive();
409 $form->setSubmitCallback(
function ( $formData, $form ) {
410 if ( isset( $formData[
'Clear'] ) ) {
411 wfDebug(
'stash has: ' . print_r( $this->stash->listFiles(),
true ) );
413 if ( !$this->stash->clear() ) {
420 $form->setSubmitTextMsg(
'uploadstash-clear' );
422 $form->prepareForm();
423 $formResult = $form->tryAuthorizedSubmit();
427 $refreshHtml = $linkRenderer->makeKnownLink(
429 $this->
msg(
'uploadstash-refresh' )->text()
431 $files = $this->stash->listFiles();
432 if ( $files && count( $files ) ) {
434 $fileListItemsHtml =
'';
435 foreach ( $files as
$file ) {
436 $itemHtml = $linkRenderer->makeKnownLink(
441 $fileObj = $this->stash->getFile(
$file );
442 $thumb = $fileObj->generateThumbName(
$file, [
'width' => 220 ] );
444 $this->
msg(
'word-separator' )->escaped() .
445 $this->
msg(
'parentheses' )->rawParams(
446 $linkRenderer->makeKnownLink(
447 $this->getPageTitle(
"thumb/$file/$thumb" ),
448 $this->msg(
'uploadstash-thumbnail' )->text()
451 }
catch ( TimeoutException $e ) {
453 }
catch ( Exception $e ) {
459 $form->displayForm( $formResult );
475 class_alias( SpecialUploadStash::class,
'SpecialUploadStash' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfResetOutputBuffers( $resetGzipEncoding=true)
Clear away any user-level output buffers, discarding contents.
Implements some public methods and some protected utility functions which are required by multiple ch...
const RENDER_NOW
Force rendering in the current process.
Show an error that looks like an HTTP server error.
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Handler class for MWExceptions.
static logException(Throwable $e, $catcher=self::CAUGHT_BY_OTHER, $extraData=[])
Log a throwable to the exception log (if enabled).
A class containing constants representing the names of configuration variables.
const UploadStashScalerBaseUrl
Name constant for the UploadStashScalerBaseUrl setting, for use with Config::get()
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getUser()
Shortcut to get the User executing this instance.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
getPageTitle( $subpage=false)
Get a self-referential title object.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Shortcut to construct a special page which is unlisted by default.
Prioritized list of file repositories.
getLocalRepo()
Get the local repository, i.e.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
static newGood( $value=null)
Factory function for good results.
File without associated database record.
UploadStash is intended to accomplish a few things:
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.