r76797 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r76796‎ | r76797 | r76798 >
Date:14:47, 16 November 2010
Author:catrope
Status:deferred
Tags:
Comment:
uploadwizard-deployment: Merge recent revs from trunk: r75995, r76354, r76386, r76503, r76526, r76537, r76740, r76746, r76750, r76782, r76783, r76796
Modified paths:
  • /branches/uploadwizard-deployment/includes/AutoLoader.php (modified) (history)
  • /branches/uploadwizard-deployment/includes/api/ApiQuery.php (modified) (history)
  • /branches/uploadwizard-deployment/includes/api/ApiQueryStashImageInfo.php (added) (history)
  • /branches/uploadwizard-deployment/includes/specials/SpecialUploadStash.php (modified) (history)
  • /branches/uploadwizard-deployment/includes/upload/UploadBase.php (modified) (history)
  • /branches/uploadwizard-deployment/includes/upload/UploadStash.php (modified) (history)
  • /branches/uploadwizard-deployment/languages/messages/MessagesEn.php (modified) (history)

Diff [purge]

Index: branches/uploadwizard-deployment/includes/upload/UploadBase.php
@@ -412,9 +412,15 @@
413413 * @return mixed Status indicating the whether the upload succeeded.
414414 */
415415 public function performUpload( $comment, $pageText, $watch, $user ) {
416 - wfDebug( "\n\n\performUpload: sum:" . $comment . ' c: ' . $pageText . ' w:' . $watch );
417 - $status = $this->getLocalFile()->upload( $this->mTempPath, $comment, $pageText,
418 - File::DELETE_SOURCE, $this->mFileProps, false, $user );
 416+ $status = $this->getLocalFile()->upload(
 417+ $this->mTempPath,
 418+ $comment,
 419+ $pageText,
 420+ File::DELETE_SOURCE,
 421+ $this->mFileProps,
 422+ false,
 423+ $user
 424+ );
419425
420426 if( $status->isGood() && $watch ) {
421427 $user->addWatch( $this->getLocalFile()->getTitle() );
@@ -549,8 +555,7 @@
550556 'mFileProps' => $this->mFileProps
551557 );
552558 $file = $stash->stashFile( $this->mTempPath, $data, $key );
553 - // TODO should we change the "local file" here?
554 - // $this->mLocalFile = $file;
 559+ $this->mLocalFile = $file;
555560 return $file;
556561 }
557562
@@ -1120,7 +1125,15 @@
11211126 */
11221127 public function getImageInfo( $result ) {
11231128 $file = $this->getLocalFile();
1124 - $imParam = ApiQueryImageInfo::getPropertyNames();
1125 - return ApiQueryImageInfo::getInfo( $file, array_flip( $imParam ), $result );
 1129+ // TODO This cries out for refactoring. We really want to say $file->getAllInfo(); here.
 1130+ // Perhaps "info" methods should be moved into files, and the API should just wrap them in queries.
 1131+ if ( $file instanceof UploadStashFile ) {
 1132+ $imParam = ApiQueryStashImageInfo::getPropertyNames();
 1133+ $info = ApiQueryStashImageInfo::getInfo( $file, array_flip( $imParam ), $result );
 1134+ } else {
 1135+ $imParam = ApiQueryImageInfo::getPropertyNames();
 1136+ $info = ApiQueryImageInfo::getInfo( $file, array_flip( $imParam ), $result );
 1137+ }
 1138+ return $info;
11261139 }
11271140 }
Property changes on: branches/uploadwizard-deployment/includes/upload/UploadBase.php
___________________________________________________________________
Modified: svn:mergeinfo
11281141 Merged /trunk/phase3/includes/upload/UploadBase.php:r76354,76503,76526,76537,76540,76740,76746,76750,76782-76783,76796
Index: branches/uploadwizard-deployment/includes/upload/UploadStash.php
@@ -24,9 +24,6 @@
2525 // array of initialized objects obtained from session (lazily initialized upon getFile())
2626 private $files = array();
2727
28 - // the base URL for files in the stash
29 - private $baseUrl;
30 -
3128 // TODO: Once UploadBase starts using this, switch to use these constants rather than UploadBase::SESSION*
3229 // const SESSION_VERSION = 2;
3330 // const SESSION_KEYNAME = 'wsUploadData';
@@ -52,18 +49,9 @@
5350 $_SESSION[UploadBase::SESSION_KEYNAME] = array();
5451 }
5552
56 - $this->baseUrl = SpecialPage::getTitleFor( 'UploadStash' )->getLocalURL();
5753 }
5854
5955 /**
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 - /**
6856 * Get a file and its metadata from the stash.
6957 * May throw exception if session data cannot be parsed due to schema change, or key not found.
7058 * @param {Integer} $key: key
@@ -78,7 +66,7 @@
7967
8068 if ( !isset( $this->files[$key] ) ) {
8169 if ( !isset( $_SESSION[UploadBase::SESSION_KEYNAME][$key] ) ) {
82 - throw new UploadStashFileNotFoundException( "key '$key' not found in session" );
 70+ throw new UploadStashFileNotFoundException( "key '$key' not found in stash" );
8371 }
8472
8573 $data = $_SESSION[UploadBase::SESSION_KEYNAME][$key];
@@ -113,7 +101,7 @@
114102 */
115103 public function stashFile( $path, $data = array(), $key = null ) {
116104 if ( ! file_exists( $path ) ) {
117 - throw new UploadStashBadPathException( "path '$path' doesn't exist" );
 105+ throw new UploadStashBadPathException( "path doesn't exist" );
118106 }
119107 $fileProps = File::getPropsFromPath( $path );
120108
@@ -196,12 +184,12 @@
197185 $repoTempPath = $repo->getZonePath( 'temp' );
198186 if ( ( ! $repo->validateFilename( $path ) ) ||
199187 ( strpos( $path, $repoTempPath ) !== 0 ) ) {
200 - throw new UploadStashBadPathException( "path '$path' is not valid or is not in repo temp area: '$repoTempPath'" );
 188+ throw new UploadStashBadPathException( 'path is not valid' );
201189 }
202190
203191 // check if path exists! and is a plain file.
204192 if ( ! $repo->fileExists( $path, FileRepo::FILES_ONLY ) ) {
205 - throw new UploadStashFileNotFoundException( "cannot find path '$path'" );
 193+ throw new UploadStashFileNotFoundException( 'cannot find path, or not a plain file' );
206194 }
207195
208196 parent::__construct( false, $repo, $path, false );
@@ -252,7 +240,7 @@
253241 }
254242
255243 if ( is_null( $extension ) ) {
256 - throw new UploadStashFileException( "extension '$extension' is null" );
 244+ throw new UploadStashFileException( "extension is null" );
257245 }
258246
259247 $this->extension = parent::normalizeExtension( $extension );
@@ -293,6 +281,16 @@
294282 return $thumbName;
295283 }
296284
 285+ /**
 286+ * Helper function -- given a 'subpage', return the local URL e.g. /wiki/Special:UploadStash/subpage
 287+ * @param {String} $subPage
 288+ * @return {String} local URL for this subpage in the Special:UploadStash space.
 289+ */
 290+ private function getSpecialUrl( $subPage ) {
 291+ return SpecialPage::getTitleFor( 'UploadStash', $subPage )->getLocalURL();
 292+ }
 293+
 294+
297295 /**
298296 * Get a URL to access the thumbnail
299297 * This is required because the model of how files work requires that
@@ -303,11 +301,7 @@
304302 * @return {String} URL to access thumbnail, or URL with partial path
305303 */
306304 public function getThumbUrl( $thumbName = false ) {
307 - $path = $this->sessionStash->getBaseUrl();
308 - if ( $thumbName !== false ) {
309 - $path .= '/' . rawurlencode( $thumbName );
310 - }
311 - return $path;
 305+ return self::getSpecialUrl( $thumbName );
312306 }
313307
314308 /**
@@ -329,7 +323,7 @@
330324 */
331325 public function getUrl() {
332326 if ( !isset( $this->url ) ) {
333 - $this->url = $this->sessionStash->getBaseUrl() . '/' . $this->getUrlName();
 327+ $this->url = self::getSpecialUrl( $this->getUrlName() );
334328 }
335329 return $this->url;
336330 }
@@ -381,7 +375,7 @@
382376 }
383377
384378 // 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 );
 379+ $stashedThumbFile = $this->sessionStash->stashFile( $thumb->getPath(), array(), $key );
386380 $thumb->thumbnailFile = $stashedThumbFile;
387381
388382 return $thumb;
Property changes on: branches/uploadwizard-deployment/includes/upload/UploadStash.php
___________________________________________________________________
Modified: svn:mergeinfo
389383 Merged /trunk/phase3/includes/upload/UploadStash.php:r76354,76503,76526,76537,76540,76740,76746,76750,76782-76783,76796
Index: branches/uploadwizard-deployment/includes/api/ApiQuery.php
@@ -52,6 +52,7 @@
5353 'langlinks' => 'ApiQueryLangLinks',
5454 'images' => 'ApiQueryImages',
5555 'imageinfo' => 'ApiQueryImageInfo',
 56+ 'stashimageinfo' => 'ApiQueryStashImageInfo',
5657 'templates' => 'ApiQueryLinks',
5758 'categories' => 'ApiQueryCategories',
5859 'extlinks' => 'ApiQueryExternalLinks',
Index: branches/uploadwizard-deployment/includes/api/ApiQueryStashImageInfo.php
@@ -0,0 +1,152 @@
 2+<?php
 3+/**
 4+ * API for MediaWiki 1.16+
 5+ *
 6+ * This program is free software; you can redistribute it and/or modify
 7+ * it under the terms of the GNU General Public License as published by
 8+ * the Free Software Foundation; either version 2 of the License, or
 9+ * (at your option) any later version.
 10+ *
 11+ * This program is distributed in the hope that it will be useful,
 12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14+ * GNU General Public License for more details.
 15+ *
 16+ * You should have received a copy of the GNU General Public License along
 17+ * with this program; if not, write to the Free Software Foundation, Inc.,
 18+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 19+ * http://www.gnu.org/copyleft/gpl.html
 20+ *
 21+ * @file
 22+ */
 23+
 24+/**
 25+ * A query action to get image information from temporarily stashed files.
 26+ *
 27+ * @ingroup API
 28+ */
 29+class ApiQueryStashImageInfo extends ApiQueryImageInfo {
 30+
 31+ public function __construct( $query, $moduleName ) {
 32+ parent::__construct( $query, $moduleName, 'sii' );
 33+ }
 34+
 35+ public function execute() {
 36+ $params = $this->extractRequestParams();
 37+ $modulePrefix = $this->getModulePrefix();
 38+
 39+ $prop = array_flip( $params['prop'] );
 40+
 41+ $scale = $this->getScale( $params );
 42+
 43+ $result = $this->getResult();
 44+
 45+ try {
 46+ $stash = new UploadStash();
 47+
 48+ foreach ( $params['sessionkey'] as $sessionkey ) {
 49+ $file = $stash->getFile( $sessionkey );
 50+ $imageInfo = self::getInfo( $file, $prop, $result, $scale );
 51+ $result->addValue( array( 'query', $this->getModuleName() ), null, $imageInfo );
 52+ $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), $modulePrefix );
 53+ }
 54+
 55+ } catch ( UploadStashNotAvailableException $e ) {
 56+ $this->dieUsage( "Session not available: " . $e->getMessage(), "nosession" );
 57+ } catch ( UploadStashFileNotFoundException $e ) {
 58+ $this->dieUsage( "File not found: " . $e->getMessage(), "invalidsessiondata" );
 59+ } catch ( UploadStashBadPathException $e ) {
 60+ $this->dieUsage( "Bad path: " . $e->getMessage(), "invalidsessiondata" );
 61+ }
 62+
 63+ }
 64+
 65+ /**
 66+ * Returns all valid parameters to siiprop
 67+ */
 68+ public static function getPropertyNames() {
 69+ return array(
 70+ 'timestamp',
 71+ 'url',
 72+ 'size',
 73+ 'dimensions', // For backwards compatibility with Allimages
 74+ 'sha1',
 75+ 'mime',
 76+ 'thumbmime',
 77+ 'metadata',
 78+ 'bitdepth',
 79+ );
 80+ }
 81+
 82+
 83+ public function getAllowedParams() {
 84+ return array(
 85+ 'sessionkey' => array(
 86+ ApiBase::PARAM_ISMULTI => true,
 87+ ApiBase::PARAM_REQUIRED => true,
 88+ ApiBase::PARAM_DFLT => null
 89+ ),
 90+ 'prop' => array(
 91+ ApiBase::PARAM_ISMULTI => true,
 92+ ApiBase::PARAM_DFLT => 'timestamp|url',
 93+ ApiBase::PARAM_TYPE => self::getPropertyNames()
 94+ ),
 95+ 'urlwidth' => array(
 96+ ApiBase::PARAM_TYPE => 'integer',
 97+ ApiBase::PARAM_DFLT => -1
 98+ ),
 99+ 'urlheight' => array(
 100+ ApiBase::PARAM_TYPE => 'integer',
 101+ ApiBase::PARAM_DFLT => -1
 102+ )
 103+ );
 104+ }
 105+
 106+ /**
 107+ * Return the API documentation for the parameters.
 108+ * @return {Array} parameter documentation.
 109+ */
 110+ public function getParamDescription() {
 111+ $p = $this->getModulePrefix();
 112+ return array(
 113+ 'prop' => array(
 114+ 'What image information to get:',
 115+ ' timestamp - Adds timestamp for the uploaded version',
 116+ ' url - Gives URL to the image and the description page',
 117+ ' size - Adds the size of the image in bytes and the height and width',
 118+ ' dimensions - Alias for size',
 119+ ' sha1 - Adds sha1 hash for the image',
 120+ ' mime - Adds MIME of the image',
 121+ ' thumbmime - Adss MIME of the image thumbnail (requires url)',
 122+ ' metadata - Lists EXIF metadata for the version of the image',
 123+ ' bitdepth - Adds the bit depth of the version',
 124+ ),
 125+ 'sessionkey' => 'Session key that identifies a previous upload that was stashed temporarily.',
 126+ 'urlwidth' => "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
 127+ 'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth"
 128+ );
 129+ }
 130+
 131+ public function getDescription() {
 132+ return 'Returns image information for stashed images';
 133+ }
 134+
 135+ public function getPossibleErrors() {
 136+ return array_merge( parent::getPossibleErrors(), array(
 137+ array( 'code' => 'siiurlwidth', 'info' => 'siiurlheight cannot be used without iiurlwidth' ),
 138+ ) );
 139+ }
 140+
 141+ protected function getExamples() {
 142+ return array(
 143+ 'api.php?action=query&prop=stashimageinfo&siisessionkey=124sd34rsdf567',
 144+ 'api.php?action=query&prop=stashimageinfo&siisessionkey=b34edoe3|bceffd4&siiurlwidth=120&siiprop=url',
 145+ );
 146+ }
 147+
 148+ public function getVersion() {
 149+ return __CLASS__ . ': $Id$';
 150+ }
 151+
 152+}
 153+
Property changes on: branches/uploadwizard-deployment/includes/api/ApiQueryStashImageInfo.php
___________________________________________________________________
Added: svn:eol-style
1154 + native
Added: svn:keywords
2155 + Id
Index: branches/uploadwizard-deployment/includes/AutoLoader.php
@@ -321,6 +321,7 @@
322322 'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
323323 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
324324 'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
 325+ 'ApiQueryStashImageInfo' => 'includes/api/ApiQueryStashImageInfo.php',
325326 'ApiQueryTags' => 'includes/api/ApiQueryTags.php',
326327 'ApiQueryUserInfo' => 'includes/api/ApiQueryUserInfo.php',
327328 'ApiQueryUsers' => 'includes/api/ApiQueryUsers.php',
Property changes on: branches/uploadwizard-deployment/includes/AutoLoader.php
___________________________________________________________________
Modified: svn:mergeinfo
328329 Merged /trunk/phase3/includes/AutoLoader.php:r76503,76526,76537,76540,76740,76746,76750,76782-76783,76796
Index: branches/uploadwizard-deployment/includes/specials/SpecialUploadStash.php
@@ -16,27 +16,27 @@
1717 * @ingroup Upload
1818 */
1919
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 -
 20+class SpecialUploadStash extends UnlistedSpecialPage {
2921 // UploadStash
3022 private $stash;
3123
32 - // we should not be reading in really big files and serving them out
33 - private $maxServeFileSize = 262144; // 256K
 24+ // Since we are directly writing the file to STDOUT,
 25+ // we should not be reading in really big files and serving them out.
 26+ //
 27+ // We also don't want people using this as a file drop, even if they
 28+ // share credentials.
 29+ //
 30+ // This service is really for thumbnails and other such previews while
 31+ // uploading.
 32+ const MAX_SERVE_BYTES = 262144; // 256K
3433
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();
 34+ public function __construct( ) {
 35+ parent::__construct( 'UploadStash', 'upload' );
 36+ try {
 37+ $this->stash = new UploadStash( );
 38+ } catch (UploadStashNotAvailableException $e) {
 39+ return null;
 40+ }
4141 }
4242
4343 /**
@@ -44,7 +44,7 @@
4545 * n.b. Most sanity checking done in UploadStashLocalFile, so this is straightforward.
4646 *
4747 * @param {String} $subPage: subpage, e.g. in http://example.com/wiki/Special:UploadStash/foo.jpg, the "foo.jpg" part
48 - * @return {Boolean} success
 48+ * @return {Boolean} success
4949 */
5050 public function execute( $subPage ) {
5151 global $wgOut, $wgUser;
@@ -57,23 +57,43 @@
5858 // prevent callers from doing standard HTML output -- we'll take it from here
5959 $wgOut->disable();
6060
61 - try {
62 - $file = $this->getStashFile( $subPage );
63 - if ( $file->getSize() > $this->maxServeFileSize ) {
64 - throw new MWException( 'file size too large' );
 61+ $code = 500;
 62+ $message = 'Unknown error';
 63+
 64+ if ( !isset( $subPage ) || $subPage === '' ) {
 65+ // the user probably visited the page just to see what would happen, so explain it a bit.
 66+ $code = '400';
 67+ $message = "Missing key\n\n"
 68+ . 'This page provides access to temporarily stashed files for the user that '
 69+ . 'uploaded those files. See the upload API documentation. To access a stashed file, '
 70+ . 'use the URL of this page, with a slash and the key of the stashed file appended.';
 71+ } else {
 72+ try {
 73+ $file = $this->getStashFile( $subPage );
 74+ $size = $file->getSize();
 75+ if ( $size === 0 ) {
 76+ $code = 500;
 77+ $message = 'File is zero length';
 78+ } else if ( $size > self::MAX_SERVE_BYTES ) {
 79+ $code = 500;
 80+ $message = 'Cannot serve a file larger than ' . self::MAX_SERVE_BYTES . ' bytes';
 81+ } else {
 82+ $this->outputFile( $file );
 83+ return true;
 84+ }
 85+ } catch( UploadStashFileNotFoundException $e ) {
 86+ $code = 404;
 87+ $message = $e->getMessage();
 88+ } catch( UploadStashBadPathException $e ) {
 89+ $code = 500;
 90+ $message = $e->getMessage();
 91+ } catch( Exception $e ) {
 92+ $code = 500;
 93+ $message = $e->getMessage();
6594 }
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;
7595 }
7696
77 - wfHttpError( $code, self::$HttpErrors[$code], $e->getCode(), $e->getMessage() );
 97+ wfHttpError( $code, OutputPage::getStatusMessage( $code ), $message );
7898 return false;
7999 }
80100
@@ -89,8 +109,8 @@
90110 // the stash key doesn't have an extension
91111 $key = $subPage;
92112 $n = strrpos( $subPage, '.' );
93 - if ( $n !== false ) {
94 - $key = $n ? substr( $subPage, 0, $n ) : $subPage;
 113+ if ( $n !== false ) {
 114+ $key = $n ? substr( $subPage, 0, $n ) : $subPage;
95115 }
96116
97117 try {
@@ -119,7 +139,7 @@
120140 }
121141 $file = $thumbnailImage->thumbnailFile;
122142 }
123 -
 143+
124144 return $file;
125145 }
126146
@@ -127,14 +147,12 @@
128148 * Output HTTP response for file
129149 * Side effects, obviously, of echoing lots of stuff to stdout.
130150 * @param {File} file
131 - */
 151+ */
132152 private function outputFile( $file ) {
133153 header( 'Content-Type: ' . $file->getMimeType(), true );
134154 header( 'Content-Transfer-Encoding: binary', true );
135155 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
 156+ header( 'Content-Length: ' . $file->getSize(), true );
138157 readfile( $file->getPath() );
139158 }
140159 }
141 -
Property changes on: branches/uploadwizard-deployment/includes/specials/SpecialUploadStash.php
___________________________________________________________________
Modified: svn:mergeinfo
142160 Merged /trunk/phase3/includes/specials/SpecialUploadStash.php:r76386,76503,76526,76537,76540,76740,76746,76750,76782-76783,76796
Index: branches/uploadwizard-deployment/languages/messages/MessagesEn.php
@@ -373,6 +373,7 @@
374374 'Watchlist' => array( 'Watchlist' ),
375375 'Recentchanges' => array( 'RecentChanges' ),
376376 'Upload' => array( 'Upload' ),
 377+ 'UploadStash' => array( 'UploadStash' ),
377378 'Listfiles' => array( 'ListFiles', 'FileList', 'ImageList' ),
378379 'Newimages' => array( 'NewFiles', 'NewImages' ),
379380 'Listusers' => array( 'ListUsers', 'UserList' ),
Property changes on: branches/uploadwizard-deployment/languages/messages/MessagesEn.php
___________________________________________________________________
Modified: svn:mergeinfo
380381 Merged /trunk/phase3/languages/messages/MessagesEn.php:r75995

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r75995Follow-up r75906: Add new special page to $specialPageAliases too...raymond09:23, 4 November 2010
r76354used accessor as recommended in comments to r73555neilk23:35, 8 November 2010
r76386* Marked Special:UploadStash unlisted since accessing it only subpage param o...ialex12:56, 9 November 2010
r76503fixes Bug 25812mah04:50, 11 November 2010
r76526* Revert r76503...mah17:52, 11 November 2010
r76537followup r76526...mah19:47, 11 November 2010
r76740updated comments, error messages. Removing exposure of actual paths in error ...neilk23:49, 15 November 2010
r76746Followups mostly to r75906....neilk00:24, 16 November 2010
r76750removed URL hackery, now using SpecialPage::getTitleFor(). Followup to r75906neilk01:15, 16 November 2010
r76782Fixed bug#25784 (thumbnails of stashed files had wrong description URLs). ...neilk06:57, 16 November 2010
r76783fixed typoneilk07:09, 16 November 2010
r76796Followup r76782: is_a() -> instanceof (we dropped PHP 4 support over 3 years ...catrope14:23, 16 November 2010

Status & tagging log