r77451 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r77450‎ | r77451 | r77452 >
Date:02:45, 30 November 2010
Author:neilk
Status:ok (Comments)
Tags:
Comment:
Dual strategy thumbnailing -- locally for development and simpler wikis, or in the cluster for setups like the WMF's

When we did our test deploy, we found that we could not scale thumbnails locally in the cluster (and this was undesirable anyway).
So, we moved UploadStash thumbnailing to occur on URL invocation, which is more like the usual MediaWiki model anyway. So the customized transform()
moved from UploadStash to just being a special case of how SpecialUploadStash does things.

Note that the Special:UploadStash url masks how the thumbnail is obtained. Unlike the regular Commons wiki, the user never sees the cluster's URL for the thumbnail.
A web request, or an imageMagick call, is performed right there and then, and the results are catted out to the client.

For consistency, we did not use wfStreamfile since we were in some cases streaming from contents obtained over the MWhttpRequest, not just local files.
Modified paths:
  • /trunk/phase3/includes/specials/SpecialUploadStash.php (modified) (history)
  • /trunk/phase3/includes/upload/UploadStash.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/upload/UploadStash.php
@@ -12,14 +12,13 @@
1313 *
1414 */
1515 class UploadStash {
16 - // Format of the key for files -- has to be suitable as a filename itself in some cases.
17 - // This should encompass a sha1 content hash in hex (new style), or an integer (old style),
18 - // and also thumbnails with prepended strings like "120px-".
19 - // The file extension should not be part of the key.
20 - const KEY_FORMAT_REGEX = '/^[\w-]+$/';
2116
 17+ // Format of the key for files -- has to be suitable as a filename itself (e.g. ab12cd34ef.jpg)
 18+ const KEY_FORMAT_REGEX = '/^[\w-]+\.\w+$/';
 19+
2220 // repository that this uses to store temp files
23 - protected $repo;
 21+ // public because we sometimes need to get a LocalFile within the same repo.
 22+ public $repo;
2423
2524 // array of initialized objects obtained from session (lazily initialized upon getFile())
2625 private $files = array();
@@ -82,7 +81,9 @@
8382 unset( $data['mTempPath'] );
8483
8584 $file = new UploadStashFile( $this, $this->repo, $path, $key, $data );
86 -
 85+ if ( $file->getSize === 0 ) {
 86+ throw new UploadStashZeroLengthFileException( "File is zero length" );
 87+ }
8788 $this->files[$key] = $file;
8889
8990 }
@@ -108,18 +109,31 @@
109110 }
110111 $fileProps = File::getPropsFromPath( $path );
111112
 113+ // we will be initializing from some tmpnam files that don't have extensions.
 114+ // most of MediaWiki assumes all uploaded files have good extensions. So, we fix this.
 115+ $extension = self::getExtensionForPath( $path );
 116+ if ( ! preg_match( "/\\.\\Q$extension\\E$/", $path ) ) {
 117+ $pathWithGoodExtension = "$path.$extension";
 118+ if ( ! rename( $path, $pathWithGoodExtension ) ) {
 119+ throw new UploadStashFileException( "couldn't rename $path to have a better extension at $pathWithGoodExtension" );
 120+ }
 121+ $path = $pathWithGoodExtension;
 122+ }
 123+
112124 // If no key was supplied, use content hash. Also has the nice property of collapsing multiple identical files
113125 // uploaded this session, which could happen if uploads had failed.
114126 if ( is_null( $key ) ) {
115 - $key = $fileProps['sha1'];
 127+ $key = $fileProps['sha1'] . "." . $extension;
116128 }
117129
118130 if ( ! preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
119131 throw new UploadStashBadPathException( "key '$key' is not in a proper format" );
120132 }
121133
122 - // if not already in a temporary area, put it there
 134+
 135+ // if not already in a temporary area, put it there
123136 $status = $this->repo->storeTemp( basename( $path ), $path );
 137+
124138 if( ! $status->isOK() ) {
125139 // It is a convention in MediaWiki to only return one error per API exception, even if multiple errors
126140 // are available. We use reset() to pick the "first" thing that was wrong, preferring errors to warnings.
@@ -136,7 +150,7 @@
137151 throw new UploadStashFileException( "error storing file in '$path': " . implode( '; ', $error ) );
138152 }
139153 $stashPath = $status->value;
140 -
 154+
141155 // required info we always store. Must trump any other application info in $data
142156 // 'mTempPath', 'mFileSize', and 'mFileProps' are arbitrary names
143157 // chosen for compatibility with UploadBase's way of doing this.
@@ -149,11 +163,42 @@
150164
151165 // now, merge required info and extra data into the session. (The extra data changes from application to application.
152166 // UploadWizard wants different things than say FirefoggChunkedUpload.)
 167+ wfDebug( __METHOD__ . " storing under $key\n" );
153168 $_SESSION[UploadBase::SESSION_KEYNAME][$key] = array_merge( $data, $requiredData );
154169
155170 return $this->getFile( $key );
156171 }
157172
 173+ /**
 174+ * Find or guess extension -- ensuring that our extension matches our mime type.
 175+ * Since these files are constructed from php tempnames they may not start off
 176+ * with an extension.
 177+ * XXX this is somewhat redundant with the checks that ApiUpload.php does with incoming
 178+ * uploads versus the desired filename. Maybe we can get that passed to us...
 179+ */
 180+ public static function getExtensionForPath( $path ) {
 181+ // Does this have an extension?
 182+ $n = strrpos( $path, '.' );
 183+ $extension = null;
 184+ if ( $n !== false ) {
 185+ $extension = $n ? substr( $path, $n + 1 ) : '';
 186+ } else {
 187+ // If not, assume that it should be related to the mime type of the original file.
 188+ $magic = MimeMagic::singleton();
 189+ $mimeType = $magic->guessMimeType( $path );
 190+ $extensions = explode( ' ', MimeMagic::singleton()->getExtensionsForType( $mimeType ) );
 191+ if ( count( $extensions ) ) {
 192+ $extension = $extensions[0];
 193+ }
 194+ }
 195+
 196+ if ( is_null( $extension ) ) {
 197+ throw new UploadStashFileException( "extension is null" );
 198+ }
 199+
 200+ return File::normalizeExtension( $extension );
 201+ }
 202+
158203 }
159204
160205 class UploadStashFile extends UnregisteredLocalFile {
@@ -198,13 +243,11 @@
199244 throw new UploadStashFileNotFoundException( 'cannot find path, or not a plain file' );
200245 }
201246
 247+
 248+
202249 parent::__construct( false, $repo, $path, false );
203250
204 - // we will be initializing from some tmpnam files that don't have extensions.
205 - // most of MediaWiki assumes all uploaded files have good extensions. So, we fix this.
206251 $this->name = basename( $this->path );
207 - $this->setExtension();
208 -
209252 }
210253
211254 /**
@@ -220,40 +263,6 @@
221264 }
222265
223266 /**
224 - * Find or guess extension -- ensuring that our extension matches our mime type.
225 - * Since these files are constructed from php tempnames they may not start off
226 - * with an extension.
227 - * This does not override getExtension() because things like getMimeType() already call getExtension(),
228 - * and that results in infinite recursion. So, we preemptively *set* the extension so getExtension() can find it.
229 - * For obvious reasons this should be called as early as possible, as part of initialization
230 - */
231 - public function setExtension() {
232 - // Does this have an extension?
233 - $n = strrpos( $this->path, '.' );
234 - $extension = null;
235 - if ( $n !== false ) {
236 - $extension = $n ? substr( $this->path, $n + 1 ) : '';
237 - } else {
238 - // If not, assume that it should be related to the mime type of the original file.
239 - //
240 - // This entire thing is backwards -- we *should* just create an extension based on
241 - // the mime type of the transformed file, *after* transformation. But File.php demands
242 - // to know the name of the transformed file before creating it.
243 - $mimeType = $this->getMimeType();
244 - $extensions = explode( ' ', MimeMagic::singleton()->getExtensionsForType( $mimeType ) );
245 - if ( count( $extensions ) ) {
246 - $extension = $extensions[0];
247 - }
248 - }
249 -
250 - if ( is_null( $extension ) ) {
251 - throw new UploadStashFileException( "extension is null" );
252 - }
253 -
254 - $this->extension = parent::normalizeExtension( $extension );
255 - }
256 -
257 - /**
258267 * Get the path for the thumbnail (actually any transformation of this file)
259268 * The actual argument is the result of thumbName although we seem to have
260269 * buggy code elsewhere that expects a boolean 'suffix'
@@ -276,12 +285,27 @@
277286 * @return String: base name for URL, like '120px-12345.jpg', or null if there is no handler
278287 */
279288 function thumbName( $params ) {
 289+ return $this->getParamThumbName( $this->getUrlName(), $params );
 290+ }
 291+
 292+
 293+ /**
 294+ * Given the name of the original, i.e. Foo.jpg, and scaling parameters, returns filename with appropriate extension
 295+ * This is abstracted from getThumbName because we also use it to calculate the thumbname the file should have on
 296+ * remote image scalers
 297+ *
 298+ * @param String $urlName: A filename, like MyMovie.ogx
 299+ * @param Array $parameters: scaling parameters, like array( 'width' => '120' );
 300+ * @return String|null parameterized thumb name, like 120px-MyMovie.ogx.jpg, or null if no handler found
 301+ */
 302+ function getParamThumbName( $urlName, $params ) {
 303+ wfDebug( __METHOD__ . " getting for $urlName, " . print_r( $params, 1 ) . " \n" );
280304 if ( !$this->getHandler() ) {
281305 return null;
282306 }
283307 $extension = $this->getExtension();
284308 list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( $extension, $this->getMimeType(), $params );
285 - $thumbName = $this->getHandler()->makeParamString( $params ) . '-' . $this->getUrlName();
 309+ $thumbName = $this->getHandler()->makeParamString( $params ) . '-' . $urlName;
286310 if ( $thumbExt != $extension ) {
287311 $thumbName .= ".$thumbExt";
288312 }
@@ -308,6 +332,7 @@
309333 * @return String: URL to access thumbnail, or URL with partial path
310334 */
311335 public function getThumbUrl( $thumbName = false ) {
 336+ wfDebug( __METHOD__ . " getting for $thumbName \n" );
312337 return $this->getSpecialUrl( $thumbName );
313338 }
314339
@@ -319,7 +344,7 @@
320345 */
321346 public function getUrlName() {
322347 if ( ! $this->urlName ) {
323 - $this->urlName = $this->sessionKey . '.' . $this->getExtension();
 348+ $this->urlName = $this->sessionKey;
324349 }
325350 return $this->urlName;
326351 }
@@ -358,43 +383,6 @@
359384 }
360385
361386 /**
362 - * Typically, transform() returns a ThumbnailImage, which you can think of as being the exact
363 - * equivalent of an HTML thumbnail on Wikipedia. So its URL is the full-size file, not the thumbnail's URL.
364 - *
365 - * Here we override transform() to stash the thumbnail file, and then
366 - * provide a way to get at the stashed thumbnail file to extract properties such as its URL
367 - *
368 - * @param $params Array: parameters suitable for File::transform()
369 - * @param $flags Integer: bitmask, flags suitable for File::transform()
370 - * @return ThumbnailImage: with additional File thumbnailFile property
371 - */
372 - public function transform( $params, $flags = 0 ) {
373 -
374 - // force it to get a thumbnail right away
375 - $flags |= self::RENDER_NOW;
376 -
377 - // returns a ThumbnailImage object containing the url and path. Note. NOT A FILE OBJECT.
378 - $thumb = parent::transform( $params, $flags );
379 - wfDebug( "UploadStash: generating thumbnail\n" );
380 - wfDebug( print_r( $thumb, 1 ) );
381 - $key = $this->thumbName($params);
382 -
383 - // remove extension, so it's stored in the session under '120px-123456'
384 - // this makes it uniform with the other session key for the original, '123456'
385 - $n = strrpos( $key, '.' );
386 - if ( $n !== false ) {
387 - $key = substr( $key, 0, $n );
388 - }
389 -
390 - // stash the thumbnail File, and provide our caller with a way to get at its properties
391 - $stashedThumbFile = $this->sessionStash->stashFile( $thumb->getPath(), array(), $key );
392 - $thumb->thumbnailFile = $stashedThumbFile;
393 -
394 - return $thumb;
395 -
396 - }
397 -
398 - /**
399387 * Remove the associated temporary file
400388 * @return Status: success
401389 */
@@ -409,4 +397,5 @@
410398 class UploadStashBadPathException extends MWException {};
411399 class UploadStashBadVersionException extends MWException {};
412400 class UploadStashFileException extends MWException {};
 401+class UploadStashZeroLengthFileException extends MWException {};
413402
Index: trunk/phase3/includes/specials/SpecialUploadStash.php
@@ -66,24 +66,24 @@
6767 . 'use the URL of this page, with a slash and the key of the stashed file appended.';
6868 } else {
6969 try {
70 - $file = $this->getStashFile( $subPage );
71 - $size = $file->getSize();
72 - if ( $size === 0 ) {
73 - $code = 500;
74 - $message = 'File is zero length';
75 - } else if ( $size > self::MAX_SERVE_BYTES ) {
76 - $code = 500;
77 - $message = 'Cannot serve a file larger than ' . self::MAX_SERVE_BYTES . ' bytes';
 70+ if ( preg_match( '/^(\d+)px-(.*)$/', $subPage, $matches ) ) {
 71+ list( /* full match */, $width, $key ) = $matches;
 72+ return $this->outputThumbFromStash( $key, $width );
7873 } else {
79 - $this->outputFile( $file );
80 - return true;
 74+ return $this->outputFileFromStash( $subPage );
8175 }
8276 } catch( UploadStashFileNotFoundException $e ) {
8377 $code = 404;
8478 $message = $e->getMessage();
 79+ } catch( UploadStashZeroLengthFileException $e ) {
 80+ $code = 500;
 81+ $message = $e->getMessage();
8582 } catch( UploadStashBadPathException $e ) {
8683 $code = 500;
8784 $message = $e->getMessage();
 85+ } catch( SpecialUploadStashTooLargeException $e ) {
 86+ $code = 500;
 87+ $message = 'Cannot serve a file larger than ' . self::MAX_SERVE_BYTES . ' bytes. ' . $e->getMessage();
8888 } catch( Exception $e ) {
8989 $code = 500;
9090 $message = $e->getMessage();
@@ -93,66 +93,164 @@
9494 wfHttpError( $code, OutputPage::getStatusMessage( $code ), $message );
9595 return false;
9696 }
 97+
 98+ /**
 99+ * Get a file from stash and stream it out. Rely on parent to catch exceptions and transform them into HTTP
 100+ * @param String: $key - key of this file in the stash, which probably looks like a filename with extension.
 101+ * @throws ....?
 102+ * @return boolean
 103+ */
 104+ private function outputFileFromStash( $key ) {
 105+ $file = $this->stash->getFile( $key );
 106+ $this->outputLocalFile( $file );
 107+ return true;
 108+ }
97109
98110
99111 /**
100 - * Convert the incoming url portion (subpage of Special page) into a stashed file,
101 - * if available.
102 - *
103 - * @param $subPage String
104 - * @return File object
105 - * @throws MWException, UploadStashFileNotFoundException, UploadStashBadPathException
 112+ * Get a thumbnail for file, either generated locally or remotely, and stream it out
 113+ * @param String $key: key for the file in the stash
 114+ * @param int $width: width of desired thumbnail
 115+ * @return ??
 116+ */
 117+ private function outputThumbFromStash( $key, $width ) {
 118+
 119+ // this global, if it exists, points to a "scaler", as you might find in the Wikimedia Foundation cluster. See outputRemoteScaledThumb()
 120+ global $wgUploadStashScalerBaseUrl;
 121+
 122+ // let exceptions propagate to caller.
 123+ $file = $this->stash->getFile( $key );
 124+
 125+ // OK, we're here and no exception was thrown,
 126+ // so the original file must exist.
 127+
 128+ // let's get ready to transform the original -- these are standard
 129+ $params = array( 'width' => $width );
 130+ $flags = 0;
 131+
 132+ return $wgUploadStashScalerBaseUrl ? $this->outputRemoteScaledThumb( $file, $params, $flags )
 133+ : $this->outputLocallyScaledThumb( $file, $params, $flags );
 134+
 135+ }
 136+
 137+
 138+ /**
 139+ * Scale a file (probably with a locally installed imagemagick, or similar) and output it to STDOUT.
 140+ * @param $file: File object
 141+ * @param $params: scaling parameters ( e.g. array( width => '50' ) );
 142+ * @param $flags: scaling flags ( see File:: constants )
 143+ * @throws MWException
 144+ * @return boolean success
106145 */
107 - private function getStashFile( $subPage ) {
108 - // due to an implementation quirk (and trying to be compatible with older method)
109 - // the stash key doesn't have an extension
110 - $key = $subPage;
111 - $n = strrpos( $subPage, '.' );
112 - if ( $n !== false ) {
113 - $key = $n ? substr( $subPage, 0, $n ) : $subPage;
114 - }
 146+ private function outputLocallyScaledThumb( $params, $flags ) {
 147+ wfDebug( "UploadStash: SCALING locally!\n" );
115148
116 - try {
117 - $file = $this->stash->getFile( $key );
118 - } catch ( UploadStashFileNotFoundException $e ) {
119 - // if we couldn't find it, and it looks like a thumbnail,
120 - // and it looks like we have the original, go ahead and generate it
121 - $matches = array();
122 - if ( ! preg_match( '/^(\d+)px-(.*)$/', $key, $matches ) ) {
123 - // that doesn't look like a thumbnail. re-raise exception
124 - throw $e;
125 - }
 149+ // n.b. this is stupid, we insist on re-transforming the file every time we are invoked. We rely
 150+ // on HTTP caching to ensure this doesn't happen.
 151+
 152+ $flags |= File::RENDER_NOW;
126153
127 - list( , $width, $origKey ) = $matches;
 154+ $thumbnailImage = $file->transform( $params, $flags );
 155+ if ( !$thumbnailImage ) {
 156+ throw new MWException( 'Could not obtain thumbnail' );
 157+ }
128158
129 - // do not trap exceptions, if key is in bad format, or file not found,
130 - // let exceptions propagate to caller.
131 - $origFile = $this->stash->getFile( $origKey );
 159+ // we should have just generated it locally
 160+ if ( ! $thumbnailImage->getPath() ) {
 161+ throw new UploadStashFileNotFoundException( "no local path for scaled item" );
 162+ }
132163
133 - // ok we're here so the original must exist. Generate the thumbnail.
134 - // because the file is a UploadStashFile, this thumbnail will also be stashed,
135 - // and a thumbnailFile will be created in the thumbnailImage composite object
136 - $thumbnailImage = $origFile->transform( array( 'width' => $width ) );
137 - if ( !$thumbnailImage ) {
138 - throw new MWException( 'Could not obtain thumbnail' );
139 - }
140 - $file = $thumbnailImage->thumbnailFile;
 164+ // now we should construct a File, so we can get mime and other such info in a standard way
 165+ // n.b. mimetype may be different from original (ogx original -> jpeg thumb)
 166+ $thumbFile = new UnregisteredLocalFile( false, $this->stash->repo, $thumbnailImage->getPath(), false );
 167+ if ( ! $thumbFile ) {
 168+ throw new UploadStashFileNotFoundException( "couldn't create local file object for thumbnail" );
141169 }
142170
143 - return $file;
 171+ return $this->outputLocalFile( $thumbFile );
 172+
144173 }
 174+
 175+ /**
 176+ * Scale a file with a remote "scaler", as exists on the Wikimedia Foundation cluster, and output it to STDOUT.
 177+ * Note: unlike the usual thumbnail process, the web client never sees the cluster URL; we do the whole HTTP transaction to the scaler ourselves
 178+ * and cat the results out.
 179+ * Note: We rely on NFS to have propagated the file contents to the scaler. However, we do not rely on the thumbnail being created in NFS and then
 180+ * propagated back to our filesystem. Instead we take the results of the HTTP request instead.
 181+ * Note: no caching is being done here, although we are instructing the client to cache it forever.
 182+ * @param $file: File object
 183+ * @param $params: scaling parameters ( e.g. array( width => '50' ) );
 184+ * @param $flags: scaling flags ( see File:: constants )
 185+ * @throws MWException
 186+ * @return boolean success
 187+ */
 188+ private function outputRemoteScaledThumb( $file, $params, $flags ) {
 189+
 190+ // this global probably looks something like 'http://upload.wikimedia.org/wikipedia/test/thumb/temp'
 191+ // do not use trailing slash
 192+ global $wgUploadStashScalerBaseUrl;
145193
 194+ $scalerThumbName = $file->getParamThumbName( $file->name, $params );
 195+ $scalerThumbUrl = $wgUploadStashScalerBaseUrl . '/' . $file->getRel() . '/' . $scalerThumbName;
 196+ // make a CURL call to the scaler to create a thumbnail
 197+ wfDebug( "UploadStash: calling " . $scalerThumbUrl . " with curl \n" );
 198+ $req = MWHttpRequest::factory( $thumbScalerUrl );
 199+ $status = $req->execute();
 200+ if ( ! $status->isOK() ) {
 201+ throw new MWException( "Fetching thumbnail failed" );
 202+ }
 203+ $contentType = $req->getResponseHeader( "content-type" );
 204+ if ( ! $contentType ) {
 205+ throw new MWException( "Missing content-type header" );
 206+ }
 207+ return $this->outputFromContent( $req->getContent(), $contentType );
 208+ }
 209+
146210 /**
147211 * Output HTTP response for file
148 - * Side effects, obviously, of echoing lots of stuff to stdout.
 212+ * Side effect: writes HTTP response to STDOUT.
 213+ * XXX could use wfStreamfile (in includes/Streamfile.php), but for consistency with outputContents() doing it this way.
 214+ * XXX is mimeType really enough, or do we need encoding for full Content-Type header?
149215 *
150 - * @param $file File object
 216+ * @param $file File object with a local path (e.g. UnregisteredLocalFile, LocalFile. Oddly these don't share an ancestor!)
151217 */
152 - private function outputFile( $file ) {
153 - header( 'Content-Type: ' . $file->getMimeType(), true );
 218+ private function outputLocalFile( $file ) {
 219+ if ( $file->getSize() > self::MAX_SERVE_BYTES ) {
 220+ throw new SpecialUploadStashTooLargeException();
 221+ }
 222+ self::outputHeaders( $file->getMimeType(), $file->getSize() );
 223+ readfile( $file->getPath() );
 224+ }
 225+
 226+ /**
 227+ * Output HTTP response of raw content
 228+ * Side effect: writes HTTP response to STDOUT.
 229+ * @param String $content: content
 230+ * @param String $mimeType: mime type
 231+ */
 232+ private function outputContents( $content, $contentType ) {
 233+ $size = strlen( $content );
 234+ if ( $size > self::MAX_SERVE_BYTES ) {
 235+ throw new SpecialUploadStashTooLargeException();
 236+ }
 237+ self::outputHeaders( $contentType, $size );
 238+ print $content;
 239+ }
 240+
 241+ /**
 242+ * Output headers for streaming
 243+ * XXX unsure about encoding as binary; if we received from HTTP perhaps we should use that encoding, concatted with semicolon to mimeType as it usually is.
 244+ * Side effect: preps PHP to write headers to STDOUT.
 245+ * @param String $contentType : string suitable for content-type header
 246+ * @param String $size: length in bytes
 247+ */
 248+ private static function outputHeaders( $contentType, $size ) {
 249+ header( "Content-Type: $mimeType", true );
154250 header( 'Content-Transfer-Encoding: binary', true );
155251 header( 'Expires: Sun, 17-Jan-2038 19:14:07 GMT', true );
156 - header( 'Content-Length: ' . $file->getSize(), true );
157 - readfile( $file->getPath() );
 252+ header( "Content-Length: $size", true );
158253 }
 254+
159255 }
 256+
 257+class SpecialUploadStashTooLargeException extends MWException {};

Sign-offs

UserFlagDate
Bryaninspected14:49, 14 February 2011

Follow-up revisions

RevisionCommit summaryAuthorDate
r77459Merging in changes from trunk into the UploadStash library and the SpecialUpl...neilk05:36, 30 November 2010
r774631.16wmf4: Merge UploadStash fixes from trunk: r77451, r77453, r77454, r77455,...catrope09:52, 30 November 2010
r77493Fixes for r77451 per CR: add UploadStashZeroLengthFileException to AutoLoader...catrope18:37, 30 November 2010

Comments

#Comment by Platonides (talk | contribs)   18:07, 30 November 2010

Register globals issue: $wgUploadStashScalerBaseUrl is never defined.

Also, UploadStashZeroLengthFileException may not be available for SpecialUploadStash. It is not defined in the Autoloader.

About the change itself, I get a feeling of trying to do too much here.

#Comment by NeilK (talk | contribs)   18:39, 30 November 2010

Good points.

I'm a bit puzzled by your last remark though. Do you mean this is too much code for one change, or the entire concept of the feature is misguided?

Arguably this whole part of the UploadWizard project is a bit silly since in order to get a relatively minor feature we carved out an entire new aspect of MediaWiki (a "private" file space) and thus had to duplicate lots of functionality that it already did in slightly different ways. And here we even had to do it twice.

Anyway, I think I'm trying to do too much in a bug comment reply. I plan a post-mortem.

#Comment by Platonides (talk | contribs)   00:55, 1 December 2010

I would expect the transformation switching to be somewhere like a backend, with different classes for each way of thumbnailing. So this was a note of "doesn't seem right" at first glance.

However, you are right it can be considered a general defect of UploadWizard.

Still, I don't think that choosing based on $wgUploadStashScalerBaseUrl should be done by the Special page. I would go further down.

#Comment by Catrope (talk | contribs)   18:39, 30 November 2010

Good catch. Fixed both in r77493

#Comment by Bryan (talk | contribs)   13:02, 2 February 2011

Shouldn't $wgUploadStashScalerBaseUrl be part of the FileRepo config?

Otherwise can be marked ok.

#Comment by NeilK (talk | contribs)   19:29, 2 February 2011

I see that as more of a separate issue. The FileRepo governs how files are stored. How they are scaled is another issue entirely.

#Comment by Bryan (talk | contribs)   19:45, 2 February 2011

FileRepo also handles thumbnailing; or at least its config does... it has the transformVia404 key, which is basically the equivalent of $wgUploadStashScalerBaseUrl for normal files. As in my other comment, I prefer consistency over sanity :)

#Comment by Bryan (talk | contribs)   18:40, 7 February 2011

This is ok for deployment, but there are some outstanding stylistic issues that we are addressing on IRC.

#Comment by 😂 (talk | contribs)   17:39, 29 March 2011

This was pre-branch, untagging for 1.17

Status & tagging log