62 private const MAX_SERVE_BYTES = 1_048_576;
70 parent::__construct(
'UploadStash',
'upload' );
72 $this->httpRequestFactory = $httpRequestFactory;
73 $this->urlUtils = $urlUtils;
74 $this->dbProvider = $dbProvider;
92 $this->stash = $this->localRepo->getUploadStash( $this->
getUser() );
95 if ( $subPage ===
null || $subPage ===
'' ) {
114 $params = $this->parseKey( $key );
115 if ( $params[
'type'] ===
'thumb' ) {
116 $this->outputThumbFromStash( $params[
'file'], $params[
'params'] );
118 $this->outputLocalFile( $params[
'file'] );
123 $message = $e->getMessage();
124 }
catch ( Exception $e ) {
126 $message = $e->getMessage();
141 private function parseKey( $key ) {
142 $type = strtok( $key,
'/' );
144 if ( $type !==
'file' && $type !==
'thumb' ) {
146 $this->
msg(
'uploadstash-bad-path-unknown-type', $type )
149 $fileName = strtok(
'/' );
150 $thumbPart = strtok(
'/' );
151 $file = $this->stash->getFile( $fileName );
152 if ( $type ===
'thumb' ) {
153 $srcNamePos = strrpos( $thumbPart, $fileName );
154 if ( $srcNamePos ===
false || $srcNamePos < 1 ) {
155 throw new UploadStashBadPathException(
156 $this->
msg(
'uploadstash-bad-path-unrecognized-thumb-name' )
159 $paramString = substr( $thumbPart, 0, $srcNamePos - 1 );
161 $handler = $file->getHandler();
163 $params = $handler->parseParamString( $paramString );
164 if ( $params ===
false ) {
169 return [
'file' => $file,
'type' => $type,
'params' =>
$params ];
171 throw new UploadStashBadPathException(
172 $this->
msg(
'uploadstash-bad-path-no-handler', $file->getMimeType(), $file->getPath() )
177 return [
'file' => $file,
'type' => $type ];
186 private function outputThumbFromStash( $file, $params ) {
192 if ( $file->getRepo()->getThumbProxyUrl()
195 $this->outputRemoteScaledThumb( $file, $params );
197 $this->outputLocallyScaledThumb( $file, $params );
207 private function outputLocallyScaledThumb( $file, $params ) {
211 $thumbnailImage = $file->transform( $params, File::RENDER_NOW );
212 if ( !$thumbnailImage ) {
213 throw new UploadStashFileNotFoundException(
214 $this->
msg(
'uploadstash-file-not-found-no-thumb' )
219 if ( !$thumbnailImage->getStoragePath() ) {
220 throw new UploadStashFileNotFoundException(
221 $this->
msg(
'uploadstash-file-not-found-no-local-path' )
227 $thumbFile =
new UnregisteredLocalFile(
false,
228 $this->stash->repo, $thumbnailImage->getStoragePath(),
false );
230 $this->outputLocalFile( $thumbFile );
249 private function outputRemoteScaledThumb( $file, $params ) {
253 $scalerThumbName = $file->generateThumbName( $file->getName(), $params );
257 $thumbProxyUrl = $file->getRepo()->getThumbProxyUrl();
258 if ( $thumbProxyUrl !==
null ) {
259 $scalerThumbUrl = $thumbProxyUrl .
'temp/' . $file->getUrlRel() .
260 '/' . rawurlencode( $scalerThumbName );
261 $secret = $file->getRepo()->getThumbProxySecret();
268 if ( preg_match(
'/^\/\//', $scalerBaseUrl ) ) {
272 $scalerBaseUrl = $this->urlUtils->expand( $scalerBaseUrl,
PROTO_CANONICAL );
275 $scalerThumbUrl = $scalerBaseUrl .
'/' . $file->getUrlRel() .
276 '/' . rawurlencode( $scalerThumbName );
286 $req = $this->httpRequestFactory->create( $scalerThumbUrl, $httpOptions, __METHOD__ );
289 if ( $secret !==
null ) {
290 $req->setHeader(
'X-Swift-Secret', $secret );
293 $status = $req->execute();
294 if ( !$status->isOK() ) {
295 throw new UploadStashFileNotFoundException(
297 'uploadstash-file-not-found-no-remote-thumb',
298 $status->getMessage(),
303 $contentType = $req->getResponseHeader(
"content-type" );
304 if ( !$contentType ) {
305 throw new UploadStashFileNotFoundException(
306 $this->
msg(
'uploadstash-file-not-found-missing-content-type' )
310 $this->outputContents( $req->getContent(), $contentType );
321 private function outputLocalFile( File $file ) {
322 if ( $file->getSize() > self::MAX_SERVE_BYTES ) {
324 $this->
msg(
'uploadstash-file-too-large', self::MAX_SERVE_BYTES )
328 $file->getRepo()->streamFileWithStatus( $file->getPath(),
329 [
'Content-Transfer-Encoding: binary',
330 'Expires: Sun, 17-Jan-2038 19:14:07 GMT' ]
341 private function outputContents( $content, $contentType ) {
342 $size = strlen( $content );
343 if ( $size > self::MAX_SERVE_BYTES ) {
345 $this->
msg(
'uploadstash-file-too-large', self::MAX_SERVE_BYTES )
350 self::outputFileHeaders( $contentType, $size );
363 private static function outputFileHeaders( $contentType, $size ) {
364 header(
"Content-Type: $contentType",
true );
365 header(
'Content-Transfer-Encoding: binary',
true );
366 header(
'Expires: Sun, 17-Jan-2038 19:14:07 GMT',
true );
368 header(
'Cache-Control: private' );
369 header(
"Content-Length: $size",
true );
376 private function showUploads() {
380 $this->
getOutput()->addModuleStyles(
'mediawiki.special' );
385 $form = HTMLForm::factory(
'ooui', [
391 ], $this->
getContext(),
'clearStashedUploads' );
393 $form->setSubmitDestructive();
394 $form->setSubmitCallback(
function ( $formData, $form ) {
395 if ( isset( $formData[
'Clear'] ) ) {
396 wfDebug(
'stash has: ' . print_r( $this->stash->listFiles(),
true ) );
398 if ( !$this->stash->clear() ) {
399 return Status::newFatal(
'uploadstash-errclear' );
403 return Status::newGood();
405 $form->setSubmitTextMsg(
'uploadstash-clear' );
407 $form->prepareForm();
408 $formResult = $form->tryAuthorizedSubmit();
412 $refreshHtml = $linkRenderer->makeKnownLink(
414 $this->
msg(
'uploadstash-refresh' )->text()
416 $pager =
new UploadStashPager(
423 if ( $pager->getNumRows() ) {
425 $this->
getOutput()->addParserOutputContent(
426 $pager->getFullOutput(),
427 ParserOptions::newFromContext( $this->getContext() )
429 $form->displayForm( $formResult );
430 $this->
getOutput()->addHTML( Html::rawElement(
'p', [], $refreshHtml ) );
432 $this->
getOutput()->addHTML( Html::rawElement(
'p', [],