r75905 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r75904‎ | r75905 | r75906 >
Date:03:32, 3 November 2010
Author:neilk
Status:ok
Tags:
Comment:
renaming the stashing library yet again, to UploadStash -- just simpler that way
Modified paths:
  • /branches/uploadwizard/extensions/UploadWizard/ApiQueryStashImageInfo.php (modified) (history)
  • /branches/uploadwizard/phase3/includes/AutoLoader.php (modified) (history)
  • /branches/uploadwizard/phase3/includes/SpecialPage.php (modified) (history)
  • /branches/uploadwizard/phase3/includes/specials/SpecialPrivateUploadStash.php (deleted) (history)
  • /branches/uploadwizard/phase3/includes/specials/SpecialUploadStash.php (added) (history)
  • /branches/uploadwizard/phase3/includes/upload/PrivateUploadStash.php (deleted) (history)
  • /branches/uploadwizard/phase3/includes/upload/UploadBase.php (modified) (history)
  • /branches/uploadwizard/phase3/includes/upload/UploadStash.php (added) (history)
  • /branches/uploadwizard/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /branches/uploadwizard/phase3/maintenance/tests/phpunit/includes/api/ApiUploadTest.php (modified) (history)

Diff [purge]

Index: branches/uploadwizard/phase3/maintenance/tests/phpunit/includes/api/ApiUploadTest.php
@@ -521,7 +521,7 @@
522522 $this->assertTrue( isset( $result['upload']['sessionkey'] ) );
523523 $sessionkey = $result['upload']['sessionkey'];
524524
525 - // it should be visible from Special:PrivateUploadStash
 525+ // it should be visible from Special:UploadStash
526526 // XXX ...but how to test this, with a fake WebRequest with the session?
527527
528528 // now we should try to release the file from stash
Index: branches/uploadwizard/phase3/includes/upload/PrivateUploadStash.php
@@ -1,406 +0,0 @@
2 -<?php
3 -/**
4 - * PrivateUploadStash is intended to accomplish a few things:
5 - * - enable applications to temporarily stash files without publishing them to the wiki.
6 - * - Several parts of MediaWiki do this in similar ways: UploadBase, UploadWizard, and FirefoggChunkedExtension
7 - * And there are several that reimplement stashing from scratch, in idiosyncratic ways. The idea is to unify them all here.
8 - * Mostly all of them are the same except for storing some custom fields, which we subsume into the data array.
9 - * - enable applications to find said files later, as long as the session or temp files haven't been purged.
10 - * - enable the uploading user (and *ONLY* the uploading user) to access said files, and thumbnails of said files, via a URL.
11 - * We accomplish this by making the session serve as a URL->file mapping, on the assumption that nobody else can access
12 - * the session, even the uploading user. See SpecialPrivateUploadStash, which implements a web interface to some files stored this way.
13 - *
14 - */
15 -class PrivateUploadStash {
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-]+$/';
21 -
22 - // repository that this uses to store temp files
23 - protected $repo;
24 -
25 - // array of initialized objects obtained from session (lazily initialized upon getFile())
26 - private $files = array();
27 -
28 - // the base URL for files in the stash
29 - private $baseUrl;
30 -
31 - // TODO: Once UploadBase starts using this, switch to use these constants rather than UploadBase::SESSION*
32 - // const SESSION_VERSION = 2;
33 - // const SESSION_KEYNAME = 'wsUploadData';
34 -
35 - /**
36 - * Represents the session which contains temporarily stored files.
37 - * Designed to be compatible with the session stashing code in UploadBase (should replace it eventually)
38 - * @param {FileRepo} $repo: optional -- repo in which to store files. Will choose LocalRepo if not supplied.
39 - */
40 - public function __construct( $repo = null ) {
41 -
42 - if ( is_null( $repo ) ) {
43 - $repo = RepoGroup::singleton()->getLocalRepo();
44 - }
45 -
46 - $this->repo = $repo;
47 -
48 - if ( ! isset( $_SESSION ) ) {
49 - throw new PrivateUploadStashNotAvailableException( 'no session variable' );
50 - }
51 -
52 - if ( !isset( $_SESSION[UploadBase::SESSION_KEYNAME] ) ) {
53 - $_SESSION[UploadBase::SESSION_KEYNAME] = array();
54 - }
55 -
56 - $this->baseUrl = SpecialPage::getTitleFor( 'PrivateUploadStash' )->getLocalURL();
57 - }
58 -
59 - /**
60 - * Get the base of URLs by which one can access the files
61 - * @return {String} url
62 - */
63 - public function getBaseUrl() {
64 - return $this->baseUrl;
65 - }
66 -
67 - /**
68 - * Get a file and its metadata from the stash.
69 - * May throw exception if session data cannot be parsed due to schema change, or key not found.
70 - * @param {Integer} $key: key
71 - * @throws PrivateUploadStashFileNotFoundException
72 - * @throws PrivateUploadStashBadVersionException
73 - * @return {PrivateUploadStashItem} null if no such item or item out of date, or the item
74 - */
75 - public function getFile( $key ) {
76 - if ( ! preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
77 - throw new PrivateUploadStashBadPathException( "key '$key' is not in a proper format" );
78 - }
79 -
80 - if ( !isset( $this->files[$key] ) ) {
81 - if ( !isset( $_SESSION[UploadBase::SESSION_KEYNAME][$key] ) ) {
82 - throw new PrivateUploadStashFileNotFoundException( "key '$key' not found in session" );
83 - }
84 -
85 - $data = $_SESSION[UploadBase::SESSION_KEYNAME][$key];
86 - // guards against PHP class changing while session data doesn't
87 - if ($data['version'] !== UploadBase::SESSION_VERSION ) {
88 - throw new PrivateUploadStashBadVersionException( $data['version'] . " does not match current version " . UploadBase::SESSION_VERSION );
89 - }
90 -
91 - // separate the stashData into the path, and then the rest of the data
92 - $path = $data['mTempPath'];
93 - unset( $data['mTempPath'] );
94 -
95 - $file = new PrivateUploadStashFile( $this, $this->repo, $path, $key, $data );
96 -
97 - $this->files[$key] = $file;
98 -
99 - }
100 - return $this->files[$key];
101 - }
102 -
103 - /**
104 - * Stash a file in a temp directory and record that we did this in the session, along with other metadata.
105 - * We store data in a flat key-val namespace because that's how UploadBase did it. This also means we have to
106 - * ensure that the key-val pairs in $data do not overwrite other required fields.
107 - *
108 - * @param {String} $path: path to file you want stashed
109 - * @param {Array} $data: optional, other data you want associated with the file. Do not use 'mTempPath', 'mFileProps', 'mFileSize', or 'version' as keys here
110 - * @param {String} $key: optional, unique key for this file in this session. Used for directory hashing when storing, otherwise not important
111 - * @throws PrivateUploadStashBadPathException
112 - * @throws PrivateUploadStashFileException
113 - * @return {null|PrivateUploadStashFile} file, or null on failure
114 - */
115 - public function stashFile( $path, $data = array(), $key = null ) {
116 - if ( ! file_exists( $path ) ) {
117 - throw new PrivateUploadStashBadPathException( "path '$path' doesn't exist" );
118 - }
119 - $fileProps = File::getPropsFromPath( $path );
120 -
121 - // If no key was supplied, use content hash. Also has the nice property of collapsing multiple identical files
122 - // uploaded this session, which could happen if uploads had failed.
123 - if ( is_null( $key ) ) {
124 - $key = $fileProps['sha1'];
125 - }
126 -
127 - if ( ! preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
128 - throw new PrivateUploadStashBadPathException( "key '$key' is not in a proper format" );
129 - }
130 -
131 - // if not already in a temporary area, put it there
132 - $status = $this->repo->storeTemp( basename( $path ), $path );
133 - if( ! $status->isOK() ) {
134 - // It is a convention in MediaWiki to only return one error per API exception, even if multiple errors
135 - // are available. We use reset() to pick the "first" thing that was wrong, preferring errors to warnings.
136 - // This is a bit lame, as we may have more info in the $status and we're throwing it away, but to fix it means
137 - // redesigning API errors significantly.
138 - // $status->value just contains the virtual URL (if anything) which is probably useless to the caller
139 - $error = reset( $status->getErrorsArray() );
140 - if ( ! count( $error ) ) {
141 - $error = reset( $status->getWarningsArray() );
142 - if ( ! count( $error ) ) {
143 - $error = array( 'unknown', 'no error recorded' );
144 - }
145 - }
146 - throw new PrivateUploadStashFileException( "error storing file in '$path': " . implode( '; ', $error ) );
147 - }
148 - $stashPath = $status->value;
149 -
150 - // required info we always store. Must trump any other application info in $data
151 - // 'mTempPath', 'mFileSize', and 'mFileProps' are arbitrary names
152 - // chosen for compatibility with UploadBase's way of doing this.
153 - $requiredData = array(
154 - 'mTempPath' => $stashPath,
155 - 'mFileSize' => $fileProps['size'],
156 - 'mFileProps' => $fileProps,
157 - 'version' => UploadBase::SESSION_VERSION
158 - );
159 -
160 - // now, merge required info and extra data into the session. (The extra data changes from application to application.
161 - // UploadWizard wants different things than say FirefoggChunkedUpload.)
162 - $_SESSION[UploadBase::SESSION_KEYNAME][$key] = array_merge( $data, $requiredData );
163 -
164 - return $this->getFile( $key );
165 - }
166 -
167 -}
168 -
169 -class PrivateUploadStashFile extends UnregisteredLocalFile {
170 - private $sessionStash;
171 - private $sessionKey;
172 - private $sessionData;
173 - private $urlName;
174 -
175 - /**
176 - * A LocalFile wrapper around a file that has been temporarily stashed, so we can do things like create thumbnails for it
177 - * Arguably UnregisteredLocalFile should be handling its own file repo but that class is a bit retarded currently
178 - * @param {PrivateUploadStash} $stash: PrivateUploadStash, useful for obtaining config, stashing transformed files
179 - * @param {FileRepo} $repo: repository where we should find the path
180 - * @param {String} $path: path to file
181 - * @param {String} $key: key to store the path and any stashed data under
182 - * @param {String} $data: any other data we want stored with this file
183 - * @throws PrivateUploadStashBadPathException
184 - * @throws PrivateUploadStashFileNotFoundException
185 - */
186 - public function __construct( $stash, $repo, $path, $key, $data ) {
187 - $this->sessionStash = $stash;
188 - $this->sessionKey = $key;
189 - $this->sessionData = $data;
190 -
191 - // resolve mwrepo:// urls
192 - if ( $repo->isVirtualUrl( $path ) ) {
193 - $path = $repo->resolveVirtualUrl( $path );
194 - }
195 -
196 - // check if path appears to be sane, no parent traversals, and is in this repo's temp zone.
197 - $repoTempPath = $repo->getZonePath( 'temp' );
198 - if ( ( ! $repo->validateFilename( $path ) ) ||
199 - ( strpos( $path, $repoTempPath ) !== 0 ) ) {
200 - throw new PrivateUploadStashBadPathException( "path '$path' is not valid or is not in repo temp area: '$repoTempPath'" );
201 - }
202 -
203 - // check if path exists! and is a plain file.
204 - if ( ! $repo->fileExists( $path, FileRepo::FILES_ONLY ) ) {
205 - throw new PrivateUploadStashFileNotFoundException( "cannot find path '$path'" );
206 - }
207 -
208 - parent::__construct( false, $repo, $path, false );
209 -
210 - // we will be initializing from some tmpnam files that don't have extensions.
211 - // most of MediaWiki assumes all uploaded files have good extensions. So, we fix this.
212 - $this->name = basename( $this->path );
213 - $this->setExtension();
214 -
215 - }
216 -
217 - /**
218 - * A method needed by the file transforming and scaling routines in File.php
219 - * We do not necessarily care about doing the description at this point
220 - * However, we also can't return the empty string, as the rest of MediaWiki demands this (and calls to imagemagick
221 - * convert require it to be there)
222 - * @return {String} dummy value
223 - */
224 - public function getDescriptionUrl() {
225 - return $this->getUrl();
226 - }
227 -
228 - /**
229 - * Find or guess extension -- ensuring that our extension matches our mime type.
230 - * Since these files are constructed from php tempnames they may not start off
231 - * with an extension.
232 - * This does not override getExtension() because things like getMimeType() already call getExtension(),
233 - * and that results in infinite recursion. So, we preemptively *set* the extension so getExtension() can find it.
234 - * For obvious reasons this should be called as early as possible, as part of initialization
235 - */
236 - public function setExtension() {
237 - // Does this have an extension?
238 - $n = strrpos( $this->path, '.' );
239 - $extension = null;
240 - if ( $n !== false ) {
241 - $extension = $n ? substr( $this->path, $n + 1 ) : '';
242 - } else {
243 - // If not, assume that it should be related to the mime type of the original file.
244 - //
245 - // This entire thing is backwards -- we *should* just create an extension based on
246 - // the mime type of the transformed file, *after* transformation. But File.php demands
247 - // to know the name of the transformed file before creating it.
248 - $mimeType = $this->getMimeType();
249 - $extensions = explode( ' ', MimeMagic::singleton()->getExtensionsForType( $mimeType ) );
250 - if ( count( $extensions ) ) {
251 - $extension = $extensions[0];
252 - }
253 - }
254 -
255 - if ( is_null( $extension ) ) {
256 - throw new PrivateUploadStashFileException( "extension '$extension' is null" );
257 - }
258 -
259 - $this->extension = parent::normalizeExtension( $extension );
260 - }
261 -
262 - /**
263 - * Get the path for the thumbnail (actually any transformation of this file)
264 - * The actual argument is the result of thumbName although we seem to have
265 - * buggy code elsewhere that expects a boolean 'suffix'
266 - *
267 - * @param {String|false} $thumbName: name of thumbnail (e.g. "120px-123456.jpg" ), or false to just get the path
268 - * @return {String} path thumbnail should take on filesystem, or containing directory if thumbname is false
269 - */
270 - public function getThumbPath( $thumbName = false ) {
271 - $path = dirname( $this->path );
272 - if ( $thumbName !== false ) {
273 - $path .= "/$thumbName";
274 - }
275 - return $path;
276 - }
277 -
278 - /**
279 - * Return the file/url base name of a thumbnail with the specified parameters
280 - *
281 - * @param {Array} $params: handler-specific parameters
282 - * @return {String|null} base name for URL, like '120px-12345.jpg', or null if there is no handler
283 - */
284 - function thumbName( $params ) {
285 - if ( !$this->getHandler() ) {
286 - return null;
287 - }
288 - $extension = $this->getExtension();
289 - list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( $extension, $this->getMimeType(), $params );
290 - $thumbName = $this->getHandler()->makeParamString( $params ) . '-' . $this->getUrlName();
291 - if ( $thumbExt != $extension ) {
292 - $thumbName .= ".$thumbExt";
293 - }
294 - return $thumbName;
295 - }
296 -
297 - /**
298 - * Get a URL to access the thumbnail
299 - * This is required because the model of how files work requires that
300 - * the thumbnail urls be predictable. However, in our model the URL is not based on the filename
301 - * (that's hidden in the session)
302 - *
303 - * @param {String} $thumbName: basename of thumbnail file -- however, we don't want to use the file exactly
304 - * @return {String} URL to access thumbnail, or URL with partial path
305 - */
306 - public function getThumbUrl( $thumbName = false ) {
307 - $path = $this->sessionStash->getBaseUrl();
308 - if ( $thumbName !== false ) {
309 - $path .= '/' . rawurlencode( $thumbName );
310 - }
311 - return $path;
312 - }
313 -
314 - /**
315 - * The basename for the URL, which we want to not be related to the filename.
316 - * Will also be used as the lookup key for a thumbnail file.
317 - * @return {String} base url name, like '120px-123456.jpg'
318 - */
319 - public function getUrlName() {
320 - if ( ! $this->urlName ) {
321 - $this->urlName = $this->sessionKey . '.' . $this->getExtension();
322 - }
323 - return $this->urlName;
324 - }
325 -
326 - /**
327 - * Return the URL of the file, if for some reason we wanted to download it
328 - * We tend not to do this for the original file, but we do want thumb icons
329 - * @return {String} url
330 - */
331 - public function getUrl() {
332 - if ( !isset( $this->url ) ) {
333 - $this->url = $this->sessionStash->getBaseUrl() . '/' . $this->getUrlName();
334 - }
335 - return $this->url;
336 - }
337 -
338 - /**
339 - * Parent classes use this method, for no obvious reason, to return the path (relative to wiki root, I assume).
340 - * But with this class, the URL is unrelated to the path.
341 - *
342 - * @return {String} url
343 - */
344 - public function getFullUrl() {
345 - return $this->getUrl();
346 - }
347 -
348 -
349 - /**
350 - * Getter for session key (the session-unique id by which this file's location & metadata is stored in the session)
351 - * @return {String} session key
352 - */
353 - public function getSessionKey() {
354 - return $this->sessionKey;
355 - }
356 -
357 - /**
358 - * Typically, transform() returns a ThumbnailImage, which you can think of as being the exact
359 - * equivalent of an HTML thumbnail on Wikipedia. So its URL is the full-size file, not the thumbnail's URL.
360 - *
361 - * Here we override transform() to stash the thumbnail file, and then
362 - * provide a way to get at the stashed thumbnail file to extract properties such as its URL
363 - *
364 - * @param {Array} $params: parameters suitable for File::transform()
365 - * @param {Bitmask} $flags: flags suitable for File::transform()
366 - * @return {ThumbnailImage} with additional File thumbnailFile property
367 - */
368 - public function transform( $params, $flags = 0 ) {
369 -
370 - // force it to get a thumbnail right away
371 - $flags |= self::RENDER_NOW;
372 -
373 - // returns a ThumbnailImage object containing the url and path. Note. NOT A FILE OBJECT.
374 - $thumb = parent::transform( $params, $flags );
375 - $key = $this->thumbName($params);
376 -
377 - // remove extension, so it's stored in the session under '120px-123456'
378 - // this makes it uniform with the other session key for the original, '123456'
379 - $n = strrpos( $key, '.' );
380 - if ( $n !== false ) {
381 - $key = substr( $key, 0, $n );
382 - }
383 -
384 - // stash the thumbnail File, and provide our caller with a way to get at its properties
385 - $stashedThumbFile = $this->sessionStash->stashFile( $thumb->path, array(), $key );
386 - $thumb->thumbnailFile = $stashedThumbFile;
387 -
388 - return $thumb;
389 -
390 - }
391 -
392 - /**
393 - * Remove the associated temporary file
394 - * @return {Status} success
395 - */
396 - public function remove() {
397 - return $this->repo->freeTemp( $this->path );
398 - }
399 -
400 -}
401 -
402 -class PrivateUploadStashNotAvailableException extends MWException {};
403 -class PrivateUploadStashFileNotFoundException extends MWException {};
404 -class PrivateUploadStashBadPathException extends MWException {};
405 -class PrivateUploadStashBadVersionException extends MWException {};
406 -class PrivateUploadStashFileException extends MWException {};
407 -
Index: branches/uploadwizard/phase3/includes/upload/UploadBase.php
@@ -600,7 +600,7 @@
601601 }
602602
603603 /**
604 - * NOTE: Probably should be deprecated in favor of PrivateUploadStash, but this is sometimes
 604+ * NOTE: Probably should be deprecated in favor of UploadStash, but this is sometimes
605605 * called outside that context.
606606 *
607607 * Stash a file in a temporary directory for later processing
@@ -632,7 +632,7 @@
633633 * @return {File}: stashed file
634634 */
635635 public function stashSessionFile( $key = null ) {
636 - $stash = new PrivateUploadStash();
 636+ $stash = new UploadStash();
637637 $data = array(
638638 'mFileProps' => $this->mFileProps
639639 );
Index: branches/uploadwizard/phase3/includes/upload/UploadStash.php
@@ -0,0 +1,406 @@
 2+<?php
 3+/**
 4+ * UploadStash is intended to accomplish a few things:
 5+ * - enable applications to temporarily stash files without publishing them to the wiki.
 6+ * - Several parts of MediaWiki do this in similar ways: UploadBase, UploadWizard, and FirefoggChunkedExtension
 7+ * And there are several that reimplement stashing from scratch, in idiosyncratic ways. The idea is to unify them all here.
 8+ * Mostly all of them are the same except for storing some custom fields, which we subsume into the data array.
 9+ * - enable applications to find said files later, as long as the session or temp files haven't been purged.
 10+ * - enable the uploading user (and *ONLY* the uploading user) to access said files, and thumbnails of said files, via a URL.
 11+ * We accomplish this by making the session serve as a URL->file mapping, on the assumption that nobody else can access
 12+ * the session, even the uploading user. See SpecialUploadStash, which implements a web interface to some files stored this way.
 13+ *
 14+ */
 15+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-]+$/';
 21+
 22+ // repository that this uses to store temp files
 23+ protected $repo;
 24+
 25+ // array of initialized objects obtained from session (lazily initialized upon getFile())
 26+ private $files = array();
 27+
 28+ // the base URL for files in the stash
 29+ private $baseUrl;
 30+
 31+ // TODO: Once UploadBase starts using this, switch to use these constants rather than UploadBase::SESSION*
 32+ // const SESSION_VERSION = 2;
 33+ // const SESSION_KEYNAME = 'wsUploadData';
 34+
 35+ /**
 36+ * Represents the session which contains temporarily stored files.
 37+ * Designed to be compatible with the session stashing code in UploadBase (should replace it eventually)
 38+ * @param {FileRepo} $repo: optional -- repo in which to store files. Will choose LocalRepo if not supplied.
 39+ */
 40+ public function __construct( $repo = null ) {
 41+
 42+ if ( is_null( $repo ) ) {
 43+ $repo = RepoGroup::singleton()->getLocalRepo();
 44+ }
 45+
 46+ $this->repo = $repo;
 47+
 48+ if ( ! isset( $_SESSION ) ) {
 49+ throw new UploadStashNotAvailableException( 'no session variable' );
 50+ }
 51+
 52+ if ( !isset( $_SESSION[UploadBase::SESSION_KEYNAME] ) ) {
 53+ $_SESSION[UploadBase::SESSION_KEYNAME] = array();
 54+ }
 55+
 56+ $this->baseUrl = SpecialPage::getTitleFor( 'UploadStash' )->getLocalURL();
 57+ }
 58+
 59+ /**
 60+ * Get the base of URLs by which one can access the files
 61+ * @return {String} url
 62+ */
 63+ public function getBaseUrl() {
 64+ return $this->baseUrl;
 65+ }
 66+
 67+ /**
 68+ * Get a file and its metadata from the stash.
 69+ * May throw exception if session data cannot be parsed due to schema change, or key not found.
 70+ * @param {Integer} $key: key
 71+ * @throws UploadStashFileNotFoundException
 72+ * @throws UploadStashBadVersionException
 73+ * @return {UploadStashItem} null if no such item or item out of date, or the item
 74+ */
 75+ public function getFile( $key ) {
 76+ if ( ! preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
 77+ throw new UploadStashBadPathException( "key '$key' is not in a proper format" );
 78+ }
 79+
 80+ if ( !isset( $this->files[$key] ) ) {
 81+ if ( !isset( $_SESSION[UploadBase::SESSION_KEYNAME][$key] ) ) {
 82+ throw new UploadStashFileNotFoundException( "key '$key' not found in session" );
 83+ }
 84+
 85+ $data = $_SESSION[UploadBase::SESSION_KEYNAME][$key];
 86+ // guards against PHP class changing while session data doesn't
 87+ if ($data['version'] !== UploadBase::SESSION_VERSION ) {
 88+ throw new UploadStashBadVersionException( $data['version'] . " does not match current version " . UploadBase::SESSION_VERSION );
 89+ }
 90+
 91+ // separate the stashData into the path, and then the rest of the data
 92+ $path = $data['mTempPath'];
 93+ unset( $data['mTempPath'] );
 94+
 95+ $file = new UploadStashFile( $this, $this->repo, $path, $key, $data );
 96+
 97+ $this->files[$key] = $file;
 98+
 99+ }
 100+ return $this->files[$key];
 101+ }
 102+
 103+ /**
 104+ * Stash a file in a temp directory and record that we did this in the session, along with other metadata.
 105+ * We store data in a flat key-val namespace because that's how UploadBase did it. This also means we have to
 106+ * ensure that the key-val pairs in $data do not overwrite other required fields.
 107+ *
 108+ * @param {String} $path: path to file you want stashed
 109+ * @param {Array} $data: optional, other data you want associated with the file. Do not use 'mTempPath', 'mFileProps', 'mFileSize', or 'version' as keys here
 110+ * @param {String} $key: optional, unique key for this file in this session. Used for directory hashing when storing, otherwise not important
 111+ * @throws UploadStashBadPathException
 112+ * @throws UploadStashFileException
 113+ * @return {null|UploadStashFile} file, or null on failure
 114+ */
 115+ public function stashFile( $path, $data = array(), $key = null ) {
 116+ if ( ! file_exists( $path ) ) {
 117+ throw new UploadStashBadPathException( "path '$path' doesn't exist" );
 118+ }
 119+ $fileProps = File::getPropsFromPath( $path );
 120+
 121+ // If no key was supplied, use content hash. Also has the nice property of collapsing multiple identical files
 122+ // uploaded this session, which could happen if uploads had failed.
 123+ if ( is_null( $key ) ) {
 124+ $key = $fileProps['sha1'];
 125+ }
 126+
 127+ if ( ! preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
 128+ throw new UploadStashBadPathException( "key '$key' is not in a proper format" );
 129+ }
 130+
 131+ // if not already in a temporary area, put it there
 132+ $status = $this->repo->storeTemp( basename( $path ), $path );
 133+ if( ! $status->isOK() ) {
 134+ // It is a convention in MediaWiki to only return one error per API exception, even if multiple errors
 135+ // are available. We use reset() to pick the "first" thing that was wrong, preferring errors to warnings.
 136+ // This is a bit lame, as we may have more info in the $status and we're throwing it away, but to fix it means
 137+ // redesigning API errors significantly.
 138+ // $status->value just contains the virtual URL (if anything) which is probably useless to the caller
 139+ $error = reset( $status->getErrorsArray() );
 140+ if ( ! count( $error ) ) {
 141+ $error = reset( $status->getWarningsArray() );
 142+ if ( ! count( $error ) ) {
 143+ $error = array( 'unknown', 'no error recorded' );
 144+ }
 145+ }
 146+ throw new UploadStashFileException( "error storing file in '$path': " . implode( '; ', $error ) );
 147+ }
 148+ $stashPath = $status->value;
 149+
 150+ // required info we always store. Must trump any other application info in $data
 151+ // 'mTempPath', 'mFileSize', and 'mFileProps' are arbitrary names
 152+ // chosen for compatibility with UploadBase's way of doing this.
 153+ $requiredData = array(
 154+ 'mTempPath' => $stashPath,
 155+ 'mFileSize' => $fileProps['size'],
 156+ 'mFileProps' => $fileProps,
 157+ 'version' => UploadBase::SESSION_VERSION
 158+ );
 159+
 160+ // now, merge required info and extra data into the session. (The extra data changes from application to application.
 161+ // UploadWizard wants different things than say FirefoggChunkedUpload.)
 162+ $_SESSION[UploadBase::SESSION_KEYNAME][$key] = array_merge( $data, $requiredData );
 163+
 164+ return $this->getFile( $key );
 165+ }
 166+
 167+}
 168+
 169+class UploadStashFile extends UnregisteredLocalFile {
 170+ private $sessionStash;
 171+ private $sessionKey;
 172+ private $sessionData;
 173+ private $urlName;
 174+
 175+ /**
 176+ * A LocalFile wrapper around a file that has been temporarily stashed, so we can do things like create thumbnails for it
 177+ * Arguably UnregisteredLocalFile should be handling its own file repo but that class is a bit retarded currently
 178+ * @param {UploadStash} $stash: UploadStash, useful for obtaining config, stashing transformed files
 179+ * @param {FileRepo} $repo: repository where we should find the path
 180+ * @param {String} $path: path to file
 181+ * @param {String} $key: key to store the path and any stashed data under
 182+ * @param {String} $data: any other data we want stored with this file
 183+ * @throws UploadStashBadPathException
 184+ * @throws UploadStashFileNotFoundException
 185+ */
 186+ public function __construct( $stash, $repo, $path, $key, $data ) {
 187+ $this->sessionStash = $stash;
 188+ $this->sessionKey = $key;
 189+ $this->sessionData = $data;
 190+
 191+ // resolve mwrepo:// urls
 192+ if ( $repo->isVirtualUrl( $path ) ) {
 193+ $path = $repo->resolveVirtualUrl( $path );
 194+ }
 195+
 196+ // check if path appears to be sane, no parent traversals, and is in this repo's temp zone.
 197+ $repoTempPath = $repo->getZonePath( 'temp' );
 198+ if ( ( ! $repo->validateFilename( $path ) ) ||
 199+ ( strpos( $path, $repoTempPath ) !== 0 ) ) {
 200+ throw new UploadStashBadPathException( "path '$path' is not valid or is not in repo temp area: '$repoTempPath'" );
 201+ }
 202+
 203+ // check if path exists! and is a plain file.
 204+ if ( ! $repo->fileExists( $path, FileRepo::FILES_ONLY ) ) {
 205+ throw new UploadStashFileNotFoundException( "cannot find path '$path'" );
 206+ }
 207+
 208+ parent::__construct( false, $repo, $path, false );
 209+
 210+ // we will be initializing from some tmpnam files that don't have extensions.
 211+ // most of MediaWiki assumes all uploaded files have good extensions. So, we fix this.
 212+ $this->name = basename( $this->path );
 213+ $this->setExtension();
 214+
 215+ }
 216+
 217+ /**
 218+ * A method needed by the file transforming and scaling routines in File.php
 219+ * We do not necessarily care about doing the description at this point
 220+ * However, we also can't return the empty string, as the rest of MediaWiki demands this (and calls to imagemagick
 221+ * convert require it to be there)
 222+ * @return {String} dummy value
 223+ */
 224+ public function getDescriptionUrl() {
 225+ return $this->getUrl();
 226+ }
 227+
 228+ /**
 229+ * Find or guess extension -- ensuring that our extension matches our mime type.
 230+ * Since these files are constructed from php tempnames they may not start off
 231+ * with an extension.
 232+ * This does not override getExtension() because things like getMimeType() already call getExtension(),
 233+ * and that results in infinite recursion. So, we preemptively *set* the extension so getExtension() can find it.
 234+ * For obvious reasons this should be called as early as possible, as part of initialization
 235+ */
 236+ public function setExtension() {
 237+ // Does this have an extension?
 238+ $n = strrpos( $this->path, '.' );
 239+ $extension = null;
 240+ if ( $n !== false ) {
 241+ $extension = $n ? substr( $this->path, $n + 1 ) : '';
 242+ } else {
 243+ // If not, assume that it should be related to the mime type of the original file.
 244+ //
 245+ // This entire thing is backwards -- we *should* just create an extension based on
 246+ // the mime type of the transformed file, *after* transformation. But File.php demands
 247+ // to know the name of the transformed file before creating it.
 248+ $mimeType = $this->getMimeType();
 249+ $extensions = explode( ' ', MimeMagic::singleton()->getExtensionsForType( $mimeType ) );
 250+ if ( count( $extensions ) ) {
 251+ $extension = $extensions[0];
 252+ }
 253+ }
 254+
 255+ if ( is_null( $extension ) ) {
 256+ throw new UploadStashFileException( "extension '$extension' is null" );
 257+ }
 258+
 259+ $this->extension = parent::normalizeExtension( $extension );
 260+ }
 261+
 262+ /**
 263+ * Get the path for the thumbnail (actually any transformation of this file)
 264+ * The actual argument is the result of thumbName although we seem to have
 265+ * buggy code elsewhere that expects a boolean 'suffix'
 266+ *
 267+ * @param {String|false} $thumbName: name of thumbnail (e.g. "120px-123456.jpg" ), or false to just get the path
 268+ * @return {String} path thumbnail should take on filesystem, or containing directory if thumbname is false
 269+ */
 270+ public function getThumbPath( $thumbName = false ) {
 271+ $path = dirname( $this->path );
 272+ if ( $thumbName !== false ) {
 273+ $path .= "/$thumbName";
 274+ }
 275+ return $path;
 276+ }
 277+
 278+ /**
 279+ * Return the file/url base name of a thumbnail with the specified parameters
 280+ *
 281+ * @param {Array} $params: handler-specific parameters
 282+ * @return {String|null} base name for URL, like '120px-12345.jpg', or null if there is no handler
 283+ */
 284+ function thumbName( $params ) {
 285+ if ( !$this->getHandler() ) {
 286+ return null;
 287+ }
 288+ $extension = $this->getExtension();
 289+ list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( $extension, $this->getMimeType(), $params );
 290+ $thumbName = $this->getHandler()->makeParamString( $params ) . '-' . $this->getUrlName();
 291+ if ( $thumbExt != $extension ) {
 292+ $thumbName .= ".$thumbExt";
 293+ }
 294+ return $thumbName;
 295+ }
 296+
 297+ /**
 298+ * Get a URL to access the thumbnail
 299+ * This is required because the model of how files work requires that
 300+ * the thumbnail urls be predictable. However, in our model the URL is not based on the filename
 301+ * (that's hidden in the session)
 302+ *
 303+ * @param {String} $thumbName: basename of thumbnail file -- however, we don't want to use the file exactly
 304+ * @return {String} URL to access thumbnail, or URL with partial path
 305+ */
 306+ public function getThumbUrl( $thumbName = false ) {
 307+ $path = $this->sessionStash->getBaseUrl();
 308+ if ( $thumbName !== false ) {
 309+ $path .= '/' . rawurlencode( $thumbName );
 310+ }
 311+ return $path;
 312+ }
 313+
 314+ /**
 315+ * The basename for the URL, which we want to not be related to the filename.
 316+ * Will also be used as the lookup key for a thumbnail file.
 317+ * @return {String} base url name, like '120px-123456.jpg'
 318+ */
 319+ public function getUrlName() {
 320+ if ( ! $this->urlName ) {
 321+ $this->urlName = $this->sessionKey . '.' . $this->getExtension();
 322+ }
 323+ return $this->urlName;
 324+ }
 325+
 326+ /**
 327+ * Return the URL of the file, if for some reason we wanted to download it
 328+ * We tend not to do this for the original file, but we do want thumb icons
 329+ * @return {String} url
 330+ */
 331+ public function getUrl() {
 332+ if ( !isset( $this->url ) ) {
 333+ $this->url = $this->sessionStash->getBaseUrl() . '/' . $this->getUrlName();
 334+ }
 335+ return $this->url;
 336+ }
 337+
 338+ /**
 339+ * Parent classes use this method, for no obvious reason, to return the path (relative to wiki root, I assume).
 340+ * But with this class, the URL is unrelated to the path.
 341+ *
 342+ * @return {String} url
 343+ */
 344+ public function getFullUrl() {
 345+ return $this->getUrl();
 346+ }
 347+
 348+
 349+ /**
 350+ * Getter for session key (the session-unique id by which this file's location & metadata is stored in the session)
 351+ * @return {String} session key
 352+ */
 353+ public function getSessionKey() {
 354+ return $this->sessionKey;
 355+ }
 356+
 357+ /**
 358+ * Typically, transform() returns a ThumbnailImage, which you can think of as being the exact
 359+ * equivalent of an HTML thumbnail on Wikipedia. So its URL is the full-size file, not the thumbnail's URL.
 360+ *
 361+ * Here we override transform() to stash the thumbnail file, and then
 362+ * provide a way to get at the stashed thumbnail file to extract properties such as its URL
 363+ *
 364+ * @param {Array} $params: parameters suitable for File::transform()
 365+ * @param {Bitmask} $flags: flags suitable for File::transform()
 366+ * @return {ThumbnailImage} with additional File thumbnailFile property
 367+ */
 368+ public function transform( $params, $flags = 0 ) {
 369+
 370+ // force it to get a thumbnail right away
 371+ $flags |= self::RENDER_NOW;
 372+
 373+ // returns a ThumbnailImage object containing the url and path. Note. NOT A FILE OBJECT.
 374+ $thumb = parent::transform( $params, $flags );
 375+ $key = $this->thumbName($params);
 376+
 377+ // remove extension, so it's stored in the session under '120px-123456'
 378+ // this makes it uniform with the other session key for the original, '123456'
 379+ $n = strrpos( $key, '.' );
 380+ if ( $n !== false ) {
 381+ $key = substr( $key, 0, $n );
 382+ }
 383+
 384+ // stash the thumbnail File, and provide our caller with a way to get at its properties
 385+ $stashedThumbFile = $this->sessionStash->stashFile( $thumb->path, array(), $key );
 386+ $thumb->thumbnailFile = $stashedThumbFile;
 387+
 388+ return $thumb;
 389+
 390+ }
 391+
 392+ /**
 393+ * Remove the associated temporary file
 394+ * @return {Status} success
 395+ */
 396+ public function remove() {
 397+ return $this->repo->freeTemp( $this->path );
 398+ }
 399+
 400+}
 401+
 402+class UploadStashNotAvailableException extends MWException {};
 403+class UploadStashFileNotFoundException extends MWException {};
 404+class UploadStashBadPathException extends MWException {};
 405+class UploadStashBadVersionException extends MWException {};
 406+class UploadStashFileException extends MWException {};
 407+
Property changes on: branches/uploadwizard/phase3/includes/upload/UploadStash.php
___________________________________________________________________
Added: svn:eol-style
1408 + native
Index: branches/uploadwizard/phase3/includes/AutoLoader.php
@@ -633,7 +633,7 @@
634634 'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php',
635635 'SpecialRecentchangeslinked' => 'includes/specials/SpecialRecentchangeslinked.php',
636636 'SpecialSearch' => 'includes/specials/SpecialSearch.php',
637 - 'SpecialPrivateUploadStash' => 'includes/specials/SpecialPrivateUploadStash.php',
 637+ 'SpecialUploadStash' => 'includes/specials/SpecialUploadStash.php',
638638 'SpecialSpecialpages' => 'includes/specials/SpecialSpecialpages.php',
639639 'SpecialStatistics' => 'includes/specials/SpecialStatistics.php',
640640 'SpecialTags' => 'includes/specials/SpecialTags.php',
@@ -669,7 +669,7 @@
670670 'UserloginTemplate' => 'includes/templates/Userlogin.php',
671671
672672 # includes/upload
673 - 'PrivateUploadStash' => 'includes/upload/PrivateUploadStash.php',
 673+ 'UploadStash' => 'includes/upload/UploadStash.php',
674674 'UploadBase' => 'includes/upload/UploadBase.php',
675675 'UploadFromStash' => 'includes/upload/UploadFromStash.php',
676676 'UploadFromFile' => 'includes/upload/UploadFromFile.php',
Index: branches/uploadwizard/phase3/includes/specials/SpecialPrivateUploadStash.php
@@ -1,140 +0,0 @@
2 -<?php
3 -/**
4 - * Special:PrivateUploadStash
5 - *
6 - * Web access for files temporarily stored by PrivateUploadStash.
7 - *
8 - * For example -- files that were uploaded with the UploadWizard extension are stored temporarily
9 - * before committing them to the db. But we want to see their thumbnails and get other information
10 - * about them.
11 - *
12 - * Since this is based on the user's session, in effect this creates a private temporary file area.
13 - * However, the URLs for the files cannot be shared.
14 - *
15 - * @file
16 - * @ingroup SpecialPage
17 - * @ingroup Upload
18 - */
19 -
20 -class SpecialPrivateUploadStash extends SpecialPage {
21 -
22 - static $HttpErrors = array( // FIXME: Use OutputPage::getStatusMessage() --RK
23 - 400 => 'Bad Request',
24 - 403 => 'Access Denied',
25 - 404 => 'File not found',
26 - 500 => 'Internal Server Error',
27 - );
28 -
29 - // PrivateUploadStash
30 - private $stash;
31 -
32 - // we should not be reading in really big files and serving them out
33 - private $maxServeFileSize = 262144; // 256K
34 -
35 - // $request is the request (usually wgRequest)
36 - // $subpage is everything in the URL after Special:PrivateUploadStash
37 - // FIXME: These parameters don't match SpecialPage::__construct()'s params at all, and are unused --RK
38 - public function __construct( $request = null, $subpage = null ) {
39 - parent::__construct( 'PrivateUploadStash', 'upload' );
40 - $this->stash = new PrivateUploadStash();
41 - }
42 -
43 - /**
44 - * If file available in stash, cats it out to the client as a simple HTTP response.
45 - * n.b. Most sanity checking done in PrivateUploadStashLocalFile, so this is straightforward.
46 - *
47 - * @param {String} $subPage: subpage, e.g. in http://example.com/wiki/Special:PrivateUploadStash/foo.jpg, the "foo.jpg" part
48 - * @return {Boolean} success
49 - */
50 - public function execute( $subPage ) {
51 - global $wgOut, $wgUser;
52 -
53 - if ( !$this->userCanExecute( $wgUser ) ) {
54 - $this->displayRestrictionError();
55 - return;
56 - }
57 -
58 - // prevent callers from doing standard HTML output -- we'll take it from here
59 - $wgOut->disable();
60 -
61 - try {
62 - $file = $this->getStashFile( $subPage );
63 - if ( $file->getSize() > $this->maxServeFileSize ) {
64 - throw new MWException( 'file size too large' );
65 - }
66 - $this->outputFile( $file );
67 - return true;
68 -
69 - } catch( PrivateUploadStashFileNotFoundException $e ) {
70 - $code = 404;
71 - } catch( PrivateUploadStashBadPathException $e ) {
72 - $code = 403;
73 - } catch( Exception $e ) {
74 - $code = 500;
75 - }
76 -
77 - wfHttpError( $code, self::$HttpErrors[$code], $e->getCode(), $e->getMessage() );
78 - return false;
79 - }
80 -
81 -
82 - /**
83 - * Convert the incoming url portion (subpage of Special page) into a stashed file, if available.
84 - * @param {String} $subPage
85 - * @return {File} file object
86 - * @throws MWException, PrivateUploadStashFileNotFoundException, PrivateUploadStashBadPathException
87 - */
88 - private function getStashFile( $subPage ) {
89 - // due to an implementation quirk (and trying to be compatible with older method)
90 - // the stash key doesn't have an extension
91 - $key = $subPage;
92 - $n = strrpos( $subPage, '.' );
93 - if ( $n !== false ) {
94 - $key = $n ? substr( $subPage, 0, $n ) : $subPage;
95 - }
96 -
97 - try {
98 - $file = $this->stash->getFile( $key );
99 - } catch ( PrivateUploadStashFileNotFoundException $e ) {
100 - // if we couldn't find it, and it looks like a thumbnail,
101 - // and it looks like we have the original, go ahead and generate it
102 - $matches = array();
103 - if ( ! preg_match( '/^(\d+)px-(.*)$/', $key, $matches ) ) {
104 - // that doesn't look like a thumbnail. re-raise exception
105 - throw $e;
106 - }
107 -
108 - list( $dummy, $width, $origKey ) = $matches;
109 -
110 - // do not trap exceptions, if key is in bad format, or file not found,
111 - // let exceptions propagate to caller.
112 - $origFile = $this->stash->getFile( $origKey );
113 -
114 - // ok we're here so the original must exist. Generate the thumbnail.
115 - // because the file is a PrivateUploadStashFile, this thumbnail will also be stashed,
116 - // and a thumbnailFile will be created in the thumbnailImage composite object
117 - $thumbnailImage = null;
118 - if ( !( $thumbnailImage = $origFile->getThumbnail( $width ) ) ) {
119 - throw new MWException( 'Could not obtain thumbnail' );
120 - }
121 - $file = $thumbnailImage->thumbnailFile;
122 - }
123 -
124 - return $file;
125 - }
126 -
127 - /**
128 - * Output HTTP response for file
129 - * Side effects, obviously, of echoing lots of stuff to stdout.
130 - * @param {File} file
131 - */
132 - private function outputFile( $file ) {
133 - header( 'Content-Type: ' . $file->getMimeType(), true );
134 - header( 'Content-Transfer-Encoding: binary', true );
135 - header( 'Expires: Sun, 17-Jan-2038 19:14:07 GMT', true );
136 - header( 'Pragma: public', true );
137 - header( 'Content-Length: ' . $file->getSize(), true ); // FIXME: PHP can handle Content-Length for you just fine --RK
138 - readfile( $file->getPath() );
139 - }
140 -}
141 -
Index: branches/uploadwizard/phase3/includes/specials/SpecialUploadStash.php
@@ -0,0 +1,140 @@
 2+<?php
 3+/**
 4+ * Special:UploadStash
 5+ *
 6+ * Web access for files temporarily stored by UploadStash.
 7+ *
 8+ * For example -- files that were uploaded with the UploadWizard extension are stored temporarily
 9+ * before committing them to the db. But we want to see their thumbnails and get other information
 10+ * about them.
 11+ *
 12+ * Since this is based on the user's session, in effect this creates a private temporary file area.
 13+ * However, the URLs for the files cannot be shared.
 14+ *
 15+ * @file
 16+ * @ingroup SpecialPage
 17+ * @ingroup Upload
 18+ */
 19+
 20+class SpecialUploadStash extends SpecialPage {
 21+
 22+ static $HttpErrors = array( // FIXME: Use OutputPage::getStatusMessage() --RK
 23+ 400 => 'Bad Request',
 24+ 403 => 'Access Denied',
 25+ 404 => 'File not found',
 26+ 500 => 'Internal Server Error',
 27+ );
 28+
 29+ // UploadStash
 30+ private $stash;
 31+
 32+ // we should not be reading in really big files and serving them out
 33+ private $maxServeFileSize = 262144; // 256K
 34+
 35+ // $request is the request (usually wgRequest)
 36+ // $subpage is everything in the URL after Special:UploadStash
 37+ // FIXME: These parameters don't match SpecialPage::__construct()'s params at all, and are unused --RK
 38+ public function __construct( $request = null, $subpage = null ) {
 39+ parent::__construct( 'UploadStash', 'upload' );
 40+ $this->stash = new UploadStash();
 41+ }
 42+
 43+ /**
 44+ * If file available in stash, cats it out to the client as a simple HTTP response.
 45+ * n.b. Most sanity checking done in UploadStashLocalFile, so this is straightforward.
 46+ *
 47+ * @param {String} $subPage: subpage, e.g. in http://example.com/wiki/Special:UploadStash/foo.jpg, the "foo.jpg" part
 48+ * @return {Boolean} success
 49+ */
 50+ public function execute( $subPage ) {
 51+ global $wgOut, $wgUser;
 52+
 53+ if ( !$this->userCanExecute( $wgUser ) ) {
 54+ $this->displayRestrictionError();
 55+ return;
 56+ }
 57+
 58+ // prevent callers from doing standard HTML output -- we'll take it from here
 59+ $wgOut->disable();
 60+
 61+ try {
 62+ $file = $this->getStashFile( $subPage );
 63+ if ( $file->getSize() > $this->maxServeFileSize ) {
 64+ throw new MWException( 'file size too large' );
 65+ }
 66+ $this->outputFile( $file );
 67+ return true;
 68+
 69+ } catch( UploadStashFileNotFoundException $e ) {
 70+ $code = 404;
 71+ } catch( UploadStashBadPathException $e ) {
 72+ $code = 403;
 73+ } catch( Exception $e ) {
 74+ $code = 500;
 75+ }
 76+
 77+ wfHttpError( $code, self::$HttpErrors[$code], $e->getCode(), $e->getMessage() );
 78+ return false;
 79+ }
 80+
 81+
 82+ /**
 83+ * Convert the incoming url portion (subpage of Special page) into a stashed file, if available.
 84+ * @param {String} $subPage
 85+ * @return {File} file object
 86+ * @throws MWException, UploadStashFileNotFoundException, UploadStashBadPathException
 87+ */
 88+ private function getStashFile( $subPage ) {
 89+ // due to an implementation quirk (and trying to be compatible with older method)
 90+ // the stash key doesn't have an extension
 91+ $key = $subPage;
 92+ $n = strrpos( $subPage, '.' );
 93+ if ( $n !== false ) {
 94+ $key = $n ? substr( $subPage, 0, $n ) : $subPage;
 95+ }
 96+
 97+ try {
 98+ $file = $this->stash->getFile( $key );
 99+ } catch ( UploadStashFileNotFoundException $e ) {
 100+ // if we couldn't find it, and it looks like a thumbnail,
 101+ // and it looks like we have the original, go ahead and generate it
 102+ $matches = array();
 103+ if ( ! preg_match( '/^(\d+)px-(.*)$/', $key, $matches ) ) {
 104+ // that doesn't look like a thumbnail. re-raise exception
 105+ throw $e;
 106+ }
 107+
 108+ list( $dummy, $width, $origKey ) = $matches;
 109+
 110+ // do not trap exceptions, if key is in bad format, or file not found,
 111+ // let exceptions propagate to caller.
 112+ $origFile = $this->stash->getFile( $origKey );
 113+
 114+ // ok we're here so the original must exist. Generate the thumbnail.
 115+ // because the file is a UploadStashFile, this thumbnail will also be stashed,
 116+ // and a thumbnailFile will be created in the thumbnailImage composite object
 117+ $thumbnailImage = null;
 118+ if ( !( $thumbnailImage = $origFile->getThumbnail( $width ) ) ) {
 119+ throw new MWException( 'Could not obtain thumbnail' );
 120+ }
 121+ $file = $thumbnailImage->thumbnailFile;
 122+ }
 123+
 124+ return $file;
 125+ }
 126+
 127+ /**
 128+ * Output HTTP response for file
 129+ * Side effects, obviously, of echoing lots of stuff to stdout.
 130+ * @param {File} file
 131+ */
 132+ private function outputFile( $file ) {
 133+ header( 'Content-Type: ' . $file->getMimeType(), true );
 134+ header( 'Content-Transfer-Encoding: binary', true );
 135+ header( 'Expires: Sun, 17-Jan-2038 19:14:07 GMT', true );
 136+ header( 'Pragma: public', true );
 137+ header( 'Content-Length: ' . $file->getSize(), true ); // FIXME: PHP can handle Content-Length for you just fine --RK
 138+ readfile( $file->getPath() );
 139+ }
 140+}
 141+
Property changes on: branches/uploadwizard/phase3/includes/specials/SpecialUploadStash.php
___________________________________________________________________
Added: svn:eol-style
1142 + native
Index: branches/uploadwizard/phase3/includes/SpecialPage.php
@@ -149,7 +149,7 @@
150150 'MIMEsearch' => array( 'SpecialPage', 'MIMEsearch' ),
151151 'FileDuplicateSearch' => array( 'SpecialPage', 'FileDuplicateSearch' ),
152152 'Upload' => 'SpecialUpload',
153 - 'PrivateUploadStash' => 'SpecialPrivateUploadStash',
 153+ 'UploadStash' => 'SpecialUploadStash',
154154
155155 # Wiki data and tools
156156 'Statistics' => 'SpecialStatistics',
Index: branches/uploadwizard/phase3/languages/messages/MessagesEn.php
@@ -460,7 +460,7 @@
461461 'RevisionMove' => array( 'RevisionMove' ),
462462 'ComparePages' => array( 'ComparePages' ),
463463 'Badtitle' => array( 'Badtitle' ),
464 - 'PrivateUploadStash' => array( 'PrivateUploadStash' ),
 464+ 'UploadStash' => array( 'UploadStash' ),
465465 );
466466
467467 /**
Index: branches/uploadwizard/extensions/UploadWizard/ApiQueryStashImageInfo.php
@@ -42,7 +42,7 @@
4343 $result = $this->getResult();
4444
4545 try {
46 - $stash = new PrivateUploadStash();
 46+ $stash = new UploadStash();
4747
4848 foreach ( $params['sessionkey'] as $sessionkey ) {
4949 $file = $stash->getFile( $sessionkey );
@@ -51,11 +51,11 @@
5252 $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), $modulePrefix );
5353 }
5454
55 - } catch ( PrivateUploadStashNotAvailableException $e ) {
 55+ } catch ( UploadStashNotAvailableException $e ) {
5656 $this->dieUsage( "Session not available: " . $e->getMessage(), "nosession" );
57 - } catch ( PrivateUploadStashFileNotFoundException $e ) {
 57+ } catch ( UploadStashFileNotFoundException $e ) {
5858 $this->dieUsage( "File not found: " . $e->getMessage(), "invalidsessiondata" );
59 - } catch ( PrivateUploadStashBadPathException $e ) {
 59+ } catch ( UploadStashBadPathException $e ) {
6060 $this->dieUsage( "Bad path: " . $e->getMessage(), "invalidsessiondata" );
6161 }
6262

Status & tagging log