r88241 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r88240‎ | r88241 | r88242 >
Date:15:09, 16 May 2011
Author:dale
Status:deferred (Comments)
Tags:
Comment:
fixed r88236 commit, renamed FirefoggChunkedUpload r79293 to ResumableUpload, the rest of the changes are forthcoming
Modified paths:
  • /trunk/extensions/ResumableUpload (added) (history)

Diff [purge]

Index: trunk/extensions/ResumableUpload/ApiFirefoggChunkedUpload.php
@@ -0,0 +1,200 @@
 2+<?php
 3+if ( !defined( 'MEDIAWIKI' ) ) die();
 4+/**
 5+ * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
 6+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 7+ */
 8+
 9+class ApiFirefoggChunkedUpload extends ApiUpload {
 10+ private $mUpload = null, $mParams = null;
 11+
 12+ public function execute() {
 13+ global $wgUser;
 14+
 15+ // Check whether upload is enabled
 16+ if ( !UploadBase::isEnabled() ) {
 17+ $this->dieUsageMsg( array( 'uploaddisabled' ) );
 18+ }
 19+
 20+ $this->mParams = $this->extractRequestParams();
 21+
 22+ $this->validateParams( $this->mParams );
 23+
 24+ $request = $this->getMain()->getRequest();
 25+ $this->mUpload = new FirefoggChunkedUploadHandler;
 26+
 27+ $status = $this->mUpload->initialize(
 28+ $request->getVal( 'done', null ),
 29+ $request->getVal( 'filename', null ),
 30+ $request->getVal( 'chunksession', null ),
 31+ $request->getFileTempName( 'chunk' ),
 32+ $request->getFileSize( 'chunk' ),
 33+ $request->getSessionData( UploadBase::getSessionKeyname() )
 34+ );
 35+
 36+ if ( $status !== true ) {
 37+ $this->dieUsage( $status, 'chunk-init-error' );
 38+ }
 39+
 40+ $ret = $this->performUpload( );
 41+
 42+ if(is_array($ret)) {
 43+ foreach($ret as $key => $val) {
 44+ $this->getResult()->addValue(null, $key, $val);
 45+ }
 46+ } else {
 47+ $this->dieUsage($ret, 'error');
 48+ }
 49+ }
 50+
 51+ public function getUpload() { return $this->mUpload; }
 52+
 53+ public function performUploadInit($comment, $pageText, $watchlist, $user) {
 54+ $check = $this->mUpload->validateNameAndOverwrite();
 55+ if( $check !== true ) {
 56+ $this->getVerificationError( $check );
 57+ }
 58+
 59+ $session = $this->mUpload->setupChunkSession( $comment, $pageText, $watchlist );
 60+ return array('uploadUrl' =>
 61+ wfExpandUrl( wfScript( 'api' ) ) . "?" .
 62+ wfArrayToCGI( array(
 63+ 'action' => 'firefoggupload',
 64+ 'token' => $user->editToken(),
 65+ 'format' => 'json',
 66+ 'chunksession' => $session,
 67+ 'filename' => $this->mUpload->getDesiredName(),
 68+ ) ) );
 69+ }
 70+
 71+ public function performUploadChunk() {
 72+ $this->mUpload->setupChunkSession();
 73+ $status = $this->mUpload->appendChunk();
 74+ if ( !$status->isOK() ) {
 75+ $this->dieUsage($status->getWikiText(), 'error');
 76+ }
 77+ return array('result' => 1, 'filesize' => $this->mUpload->getFileSize() );
 78+ }
 79+
 80+ public function performUploadDone($user) {
 81+ $this->mUpload->finalizeFile();
 82+ $status = parent::performUpload( $this->comment, $this->pageText, $this->watchlist, $user );
 83+
 84+ if ( $status['result'] !== 'Success' ) {
 85+ return $status;
 86+ }
 87+ $file = $this->mUpload->getLocalFile();
 88+ return array('result' => 1, 'done' => 1, 'resultUrl' => wfExpandUrl( $file->getDescriptionUrl() ) );
 89+ }
 90+
 91+ /**
 92+ * Handle a chunk of the upload. Overrides the parent method
 93+ * because Chunked Uploading clients (i.e. Firefogg) require
 94+ * specific API responses.
 95+ * @see UploadBase::performUpload
 96+ */
 97+ public function performUpload( ) {
 98+ wfDebug( "\n\n\performUpload(chunked): comment: " . $this->comment .
 99+ ' pageText: ' . $this->pageText . ' watch: ' . $this->watchlist );
 100+ $ret = "unknown error";
 101+
 102+ global $wgUser;
 103+ if ( $this->mUpload->getChunkMode() == FirefoggChunkedUploadHandler::INIT ) {
 104+ $ret = $this->performUploadInit($this->comment, $this->pageText, $this->watchlist, $wgUser);
 105+ } else if ( $this->mUpload->getChunkMode() == FirefoggChunkedUploadHandler::CHUNK ) {
 106+ $ret = $this->performUploadChunk();
 107+ } else if ( $this->mUpload->getChunkMode() == FirefoggChunkedUploadHandler::DONE ) {
 108+ $ret = $this->performUploadDone($wgUser);
 109+ }
 110+
 111+ return $ret;
 112+ }
 113+
 114+ public function mustBePosted() {
 115+ return true;
 116+ }
 117+
 118+ public function isWriteMode() {
 119+ return true;
 120+ }
 121+
 122+ protected function validateParams( $params ) {
 123+ if( $params['done'] ) {
 124+ $required[] = 'chunksession';
 125+ }
 126+ if( $params['chunksession'] === null ) {
 127+ $required[] = 'filename';
 128+ $required[] = 'comment';
 129+ $required[] = 'watchlist';
 130+ $required[] = 'ignorewarnings';
 131+ }
 132+
 133+ foreach( $required as $arg ) {
 134+ if ( !isset( $params[$arg] ) ) {
 135+ $this->dieUsageMsg( array( 'missingparam', $arg ) );
 136+ }
 137+ }
 138+ }
 139+
 140+ public function getAllowedParams() {
 141+ return array(
 142+ 'filename' => null,
 143+ 'token' => null,
 144+ 'comment' => null,
 145+ 'ignorewarnings' => false,
 146+ 'chunksession' => null,
 147+ 'chunk' => null,
 148+ 'done' => false,
 149+ 'watchlist' => array(
 150+ ApiBase::PARAM_DFLT => 'preferences',
 151+ ApiBase::PARAM_TYPE => array(
 152+ 'watch',
 153+ 'unwatch',
 154+ 'preferences',
 155+ 'nochange'
 156+ ),
 157+ ),
 158+ );
 159+ }
 160+
 161+ public function getParamDescription() {
 162+ return array(
 163+ 'filename' => 'Target filename',
 164+ 'token' => 'Edit token. You can get one of these through prop=info',
 165+ 'comment' => 'Upload comment',
 166+ 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
 167+ 'ignorewarnings' => 'Ignore any warnings',
 168+ 'chunksession' => 'The session key, established on the first contact during the chunked upload',
 169+ 'chunk' => 'The data in this chunk of a chunked upload',
 170+ 'done' => 'Set to 1 on the last chunk of a chunked upload',
 171+ );
 172+ }
 173+
 174+ public function getDescription() {
 175+ return array(
 176+ 'Upload a file in chunks using the protocol documented at http://firefogg.org/dev/chunk_post.html'
 177+ );
 178+ }
 179+
 180+ public function getPossibleErrors() {
 181+ return array_merge(
 182+ parent::getPossibleErrors(),
 183+ array(
 184+ array( 'missingparam' ),
 185+ array( 'chunk-init-error' ),
 186+ array( 'code' => 'chunk-init-error', 'info' => 'Insufficient information for initialization.' ),
 187+ array( 'code' => 'chunked-error', 'info' => 'There was a problem initializing the chunked upload.' ),
 188+ )
 189+ );
 190+ }
 191+
 192+ public function getExamples() {
 193+ return array(
 194+ 'api.php?action=firefoggupload&filename=Wiki.png',
 195+ );
 196+ }
 197+
 198+ public function getVersion() {
 199+ return __CLASS__ . ': $Id$';
 200+ }
 201+}
Property changes on: trunk/extensions/ResumableUpload/ApiFirefoggChunkedUpload.php
___________________________________________________________________
Added: Keywords
1202 + Id
Added: svn:eol-style
2203 + native
Added: svn:eol-syle
3204 + native
Index: trunk/extensions/ResumableUpload/tests/bootstrap.php
@@ -0,0 +1,27 @@
 2+<?php
 3+
 4+/**
 5+ * Set up the MediaWiki environment when running tests with "phpunit" command
 6+ *
 7+ * Warning: this file is not included from global scope!
 8+ * @file
 9+ */
 10+
 11+global $wgCommandLineMode, $IP, $optionsWithArgs;
 12+$IP = dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) . "/mw-svn";
 13+define( 'MW_PHPUNIT_TEST', true );
 14+
 15+require_once( "$IP/maintenance/commandLine.inc" );
 16+
 17+if( !version_compare(PHPUnit_Runner_Version::id(), "3.4.1", ">") ) {
 18+ echo <<<EOF
 19+************************************************************
 20+
 21+These tests run best with version PHPUnit 3.4.2 or better.
 22+Earlier versions may show failures because earlier versions
 23+of PHPUnit do not properly implement dependencies.
 24+
 25+************************************************************
 26+
 27+EOF;
 28+}
\ No newline at end of file
Property changes on: trunk/extensions/ResumableUpload/tests/bootstrap.php
___________________________________________________________________
Added: svn:eol-style
129 + native
Added: svn:eol-syle
230 + native
Index: trunk/extensions/ResumableUpload/tests/phpunit.xml
@@ -0,0 +1,19 @@
 2+<!-- See http://www.phpunit.de/manual/3.3/en/appendixes.configuration.html -->
 3+<phpunit bootstrap="./bootstrap.php"
 4+ colors="false"
 5+ backupGlobals="false"
 6+ convertErrorsToExceptions="true"
 7+ convertNoticesToExceptions="true"
 8+ convertWarningsToExceptions="true"
 9+ stopOnFailure="false">
 10+ <testsuite name="MediaWiki Test Suite">
 11+ <!-- <directory>.</directory> -->
 12+ <file>UploadFromChunksTest.php</file>
 13+ </testsuite>
 14+ <groups>
 15+ <exclude>
 16+ <group>Broken</group>
 17+ <group>Stub</group>
 18+ </exclude>
 19+ </groups>
 20+</phpunit>
\ No newline at end of file
Property changes on: trunk/extensions/ResumableUpload/tests/phpunit.xml
___________________________________________________________________
Added: svn:eol-style
121 + native
Added: svn:eol-syle
222 + native
Index: trunk/extensions/ResumableUpload/tests/UploadFromChunksTest.php
@@ -0,0 +1,371 @@
 2+<?php
 3+
 4+global $IP;
 5+require_once( "$IP/maintenance/tests/ApiSetup.php" );
 6+require_once( "$IP/maintenance/deleteArchivedFiles.inc" );
 7+require_once( "$IP/maintenance/deleteArchivedRevisions.inc" );
 8+require_once( dirname( dirname( __FILE__ ) ) . '/FirefoggChunkedUpload.php' );
 9+
 10+class nullClass {
 11+ public function handleOutput(){}
 12+ public function purgeRedundantText(){}
 13+}
 14+
 15+class UploadFromChunksTest extends ApiSetup {
 16+
 17+ // a base64 encoded image;
 18+ private $pngimg = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAASUExURf///8zMzJmZmWZmZjMzMwAAAPOPemkAAAM1SURBVHjaYmBgYGBkYQUBFkYWFiCPCchixQAMCCZAACF0MAMVM4K4TFh0IGsBCCAkOxhYmBnAAKaHhZkZmxaAAGJgYIbpYGBihGgBWsTMzMwE4jIhaWGAYoAAYmCECDExYAcwGxkg5oNIgABigDqLARdgZmGB2wICrKwAAcSA3xKgIxlZ0PwCEEAMBCxhgHoWSQtAADFAAxgfYEJ1GEAAQbQw4tUCsocBYQVAADEgu4uRkREeUCwszEwwLhOKLQABhNDCBA4aSDgwwhIAJKqYUPwCEEAMUK/AUwnc9aywJMCI7DAgAAggBohZ8JTBhGIJzCoWZL8ABBCYidAB8RUjWppkYUG2BSCAGMDqEMZiswUtXgACiAHsFYixTMywGGLGpgUWYgABxAA2mQkWCMyMqFoYmdD8ACQAAogBHJHMrCxg1cyIiICmCkYWDFsAAgiihYmZCewFFpR0BfI3LLch+QUggBiQ0iQjEyMDmh54qCBlUIAAYsCRJsElADQvgWKTlRGeKwECiAF3XgGmMEYQYADZzcoA9z5AAMG9RQCAtEC9DxBADFiyFyMjVi0wABBAWLQwQdIiuhYGWJIACCBg+KKUJ9BoBRdS2LQALQMIIGDQIEmwAO1kYcVWHCDZAhBAqFqYmOAxj2YNtAwDAYAAYmDEiBYWzHKKkRERYiwAAYSphZEZwxZGZiZQVEJTJkAAMTCyokc7M5oORlC5wcoEjxeAAAJqQXU0UB6W5WFmABMtEzMi1wEEEFAbE0YyAUuzMMEsYQalMkQSBQggUDmNPU3C9IA4LCxI+QUggEBiKOU8yExgqccCL3chnkPKlQABhGo6ejHBDKmdUHMlQAAhhQvQaGZGkBIkjcAMywLmI+VKgABCSowsTJhZkhlWXiBpAQggYBqBZl9GVOdBcz0LZqEEEEAMqLULMBLg1THWog9IAwQQA0qiZcRW5aPbAhBADCg1El4tMAAQQAxoiZYZXnTh1AIQQAzo2QlYpDDjcBgrxGEAAcSAJTthswmiBUwDBBC2GpkZJTaRvQ+mAQKIAUuuxdZWQvILQABBmSxMjBj5EpcWgACCMoFOYYSpZyHQHgMIMACt2hmoVEikCQAAAABJRU5ErkJggg==";
 19+
 20+ function setUp() {
 21+ global $wgEnableUploads, $wgLocalFileRepo;
 22+
 23+ $wgEnableUploads = true;
 24+ parent::setup();
 25+ $wgLocalFileRepo = array(
 26+ 'class' => 'LocalRepo',
 27+ 'name' => 'local',
 28+ 'directory' => 'test-repo',
 29+ 'url' => 'http://example.com/images',
 30+ 'hashLevels' => 2,
 31+ 'transformVia404' => false,
 32+ );
 33+
 34+ ini_set( 'log_errors', 1 );
 35+ ini_set( 'error_reporting', 1 );
 36+ ini_set( 'display_errors', 1 );
 37+ }
 38+
 39+ function makeChunk( $content ) {
 40+ $file = tempnam( wfTempDir(), "" );
 41+ $fh = fopen( $file, "wb" );
 42+ if ( $fh == false ) {
 43+ $this->markTestIncomplete( "Couldn't open $file!\n" );
 44+ return;
 45+ }
 46+ fwrite( $fh, $content );
 47+ fclose( $fh );
 48+
 49+ $_FILES['chunk']['tmp_name'] = $file;
 50+ $_FILES['chunk']['size'] = 3;
 51+ $_FILES['chunk']['error'] = null;
 52+ $_FILES['chunk']['name'] = "test.txt";
 53+ }
 54+
 55+ function cleanChunk() {
 56+ if ( file_exists( $_FILES['chunk']['tmp_name'] ) )
 57+ unlink( $_FILES['chunk']['tmp_name'] );
 58+ }
 59+
 60+ function doApiRequest( $params, $data = null ) {
 61+ $session = isset( $data[2] ) ? $data[2] : array();
 62+ $_SESSION = $session;
 63+
 64+ $req = new FauxRequest( $params, true, $session );
 65+ $module = new ApiMain( $req, true );
 66+ $module->execute();
 67+
 68+ return array( $module->getResultData(), $req, $_SESSION );
 69+ }
 70+
 71+ /* function testGetTitle() { */
 72+ /* $filename = tempnam( wfTempDir(), "" ); */
 73+ /* $c = new ApiFirefoggChunkedUpload; */
 74+ /* $c->initialize( false, "temp.txt", null, $filename, 0, null ); */
 75+ /* $this->assertEquals( null, $c->getUpload()->getTitle() ); */
 76+
 77+ /* $c = new ApiFirefoggChunkedUpload; */
 78+ /* $c->initialize( false, "Temp.png", null, $filename, 0, null ); */
 79+ /* $this->assertEquals( Title::makeTitleSafe( NS_FILE, "Temp.png" ), $c->getUpload()->getTitle() ); */
 80+ /* } */
 81+
 82+ function testLogin() {
 83+ $data = $this->doApiRequest( array(
 84+ 'action' => 'login',
 85+ 'lgname' => self::$userName,
 86+ 'lgpassword' => self::$passWord ) );
 87+ $this->assertArrayHasKey( "login", $data[0] );
 88+ $this->assertArrayHasKey( "result", $data[0]['login'] );
 89+ $this->assertEquals( "NeedToken", $data[0]['login']['result'] );
 90+ $token = $data[0]['login']['token'];
 91+
 92+ $data = $this->doApiRequest( array(
 93+ 'action' => 'login',
 94+ "lgtoken" => $token,
 95+ "lgname" => self::$userName,
 96+ "lgpassword" => self::$passWord ) );
 97+
 98+ $this->assertArrayHasKey( "login", $data[0] );
 99+ $this->assertArrayHasKey( "result", $data[0]['login'] );
 100+ $this->assertEquals( "Success", $data[0]['login']['result'] );
 101+ $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
 102+
 103+ return $data;
 104+ }
 105+
 106+ /**
 107+ * @depends testLogin
 108+ */
 109+ function testSetupChunkBannedFileType( $data ) {
 110+ global $wgUser;
 111+ $wgUser = User::newFromName( self::$userName );
 112+ $wgUser->load();
 113+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 114+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 115+ $exception = false;
 116+
 117+ try {
 118+ $this->doApiRequest( array(
 119+ 'action' => 'firefoggupload',
 120+ 'comment' => 'test',
 121+ 'watchlist' => 'watch',
 122+ 'filename' => 'tmp.txt',
 123+ 'token' => $token ), $data );
 124+ } catch ( UsageException $e ) {
 125+ $exception = true;
 126+ $this->assertEquals( "This type of file is banned", $e->getMessage() );
 127+ }
 128+
 129+ $this->assertTrue( $exception, "Got exception" );
 130+ }
 131+
 132+ /**
 133+ * @depends testLogin
 134+ */
 135+ function testSetupChunkSession( $data ) {
 136+ global $wgUser;
 137+ $wgUser = User::newFromName( self::$userName );
 138+ $wgUser->load();
 139+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 140+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 141+
 142+ $data = $this->doApiRequest( array(
 143+ 'action' => 'firefoggupload',
 144+ 'comment' => 'test',
 145+ 'watchlist' => 'watch',
 146+ 'filename' => 'TestPic.png',
 147+ 'token' => $token ), $data );
 148+
 149+ $this->assertArrayHasKey( 'uploadUrl', $data[0] );
 150+ $this->assertRegexp( '/action=firefoggupload/', $data[0]['uploadUrl'] );
 151+ $this->assertRegexp( '/chunksession=/', $data[0]['uploadUrl'] );
 152+ $this->assertRegexp( '/token=/', $data[0]['uploadUrl'] );
 153+
 154+ return $data;
 155+ }
 156+
 157+ /**
 158+ * @depends testLogin
 159+ */
 160+ function testInvalidSessionKey( $data ) {
 161+ global $wgUser;
 162+ $wgUser = User::newFromName( self::$userName );
 163+ $wgUser->load();
 164+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 165+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 166+ $exception = false;
 167+
 168+ try {
 169+ $this->doApiRequest( array(
 170+ 'action' => 'firefoggupload',
 171+ 'enablechunks' => true,
 172+ 'token' => $token,
 173+ 'chunksession' => 'bogus' ), $data );
 174+ } catch ( UsageException $e ) {
 175+ $exception = true;
 176+ $this->assertEquals( "Not a valid session key", $e->getMessage() );
 177+ }
 178+
 179+ $this->assertTrue( $exception, "Got exception" );
 180+ }
 181+
 182+ function testPerformUploadInitError() {
 183+ global $wgUser;
 184+ $wgUser = User::newFromId( 1 );
 185+
 186+ $req = new FauxRequest(
 187+ array(
 188+ 'action' => 'firefoggupload',
 189+ 'sessionkey' => '1',
 190+ 'filename' => 'test.png',
 191+ ) );
 192+ $module = new ApiMain( $req, true );
 193+ $gotException = false;
 194+ try {
 195+ $module->execute();
 196+ } catch ( UsageException $e ) {
 197+ $this->assertEquals( "The token parameter must be set", $e->getMessage() );
 198+ $gotException = true;
 199+ }
 200+
 201+ $this->assertTrue( $gotException );
 202+ }
 203+
 204+
 205+ /**
 206+ * @depends testLogin
 207+ */
 208+ function testSetupChunkForBannedContent( $data ) {
 209+ global $wgUser;
 210+ $wgUser = User::newFromName( self::$userName );
 211+ $wgUser->load();
 212+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 213+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 214+ $exception = false;
 215+
 216+ $this->makeChunk( "123" );
 217+ $data = $this->doApiRequest( array(
 218+ 'action' => 'firefoggupload',
 219+ 'comment' => 'test',
 220+ 'watchlist' => 'watch',
 221+ 'filename' => 'tmp.png',
 222+ 'token' => $token ), $data );
 223+ return $data;
 224+ }
 225+
 226+ /**
 227+ * @depends testSetupChunkForBannedContent
 228+ */
 229+ function testChunkUploadBannedContent ( $data ) {
 230+ global $wgUser;
 231+ $wgUser = User::newFromName( self::$userName );
 232+ $wgUser->load();
 233+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 234+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 235+ $exception = false;
 236+ $url = $data[0]['uploadUrl'];
 237+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 238+ $params['done'] = true;
 239+
 240+ $this->makeChunk( "123" );
 241+ $gotException = false;
 242+ try {
 243+ $data = $this->doApiRequest( $params, $data );
 244+ } catch ( UsageException $e ) {
 245+ $this->assertEquals( "This file did not pass file verification",
 246+ $e->getMessage() );
 247+ $gotException = true;
 248+ }
 249+ $this->cleanChunk();
 250+ $this->assertTrue( $gotException );
 251+ }
 252+
 253+ /**
 254+ * @depends testLogin
 255+ */
 256+ function testUploadChunkDoneGood( $data ) {
 257+ global $wgUser, $wgVerifyMimeType;
 258+ $wgVerifyMimeType = false;
 259+
 260+ $this->markTestIncomplete("Not working yet ... fails every other time b/c we're not dealing with a temporary db");
 261+
 262+ DeleteArchivedFilesImplementation::doDelete(new nullClass, true);
 263+ DeleteArchivedRevisionsImplementation::doDelete(new nullClass);
 264+
 265+ $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID();
 266+ $oldFile = Article::newFromID( $id );
 267+ if ( $oldFile ) {
 268+ $oldFile->doDeleteArticle();
 269+ $oldFile->doPurge();
 270+ }
 271+
 272+ $oldFile = wfFindFile( "Twar.png" );
 273+ if ( $oldFile ) {
 274+ $oldFile->delete();
 275+ }
 276+ $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID();
 277+ $this->assertEquals(0, $id);
 278+
 279+ $oldFile = Article::newFromID( $id );
 280+ $this->assertEquals(null, $oldFile);
 281+
 282+ $wgUser = User::newFromName( self::$userName );
 283+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 284+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 285+ $data = $this->doApiRequest( array(
 286+ 'action' => 'firefoggupload',
 287+ 'comment' => 'test',
 288+ 'watchlist' => 'watch',
 289+ 'filename' => 'twar.png',
 290+ 'token' => $token ), $data );
 291+
 292+ $url = $data[0]['uploadUrl'];
 293+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 294+ $size = 0;
 295+ for ( $i = 0; $i < 5; $i++ ) {
 296+ $this->makeChunk( "123" );
 297+ $size += $_FILES['chunk']['size'];
 298+
 299+ $data = $this->doApiRequest( $params, $data );
 300+ $this->assertArrayHasKey( "result", $data[0] );
 301+ $this->assertTrue( (bool)$data[0]["result"] );
 302+
 303+ $this->assertArrayHasKey( "filesize", $data[0] );
 304+ $this->assertEquals( $size, $data[0]['filesize'] );
 305+
 306+ $this->cleanChunk();
 307+ }
 308+
 309+ $params['done'] = true;
 310+
 311+ $this->makeChunk( "456" );
 312+ $data = $this->doApiRequest( $params, $data );
 313+
 314+ $this->cleanChunk();
 315+ $this->assertArrayHasKey( 'result', $data[0] );
 316+
 317+ $this->assertEquals( 1, $data[0]['result'] );
 318+
 319+ $this->assertArrayHasKey( 'done', $data[0] );
 320+ $this->assertEquals( 1, $data[0]['done'] );
 321+
 322+ $this->assertArrayHasKey( 'resultUrl', $data[0] );
 323+ $this->assertRegExp( '/File:Twar.png/', $data[0]['resultUrl'] );
 324+ }
 325+
 326+ /**
 327+ * @depends testLogin
 328+ */
 329+ function testUploadChunkDoneDuplicate( $data ) {
 330+ global $wgUser, $wgVerifyMimeType;
 331+
 332+ $this->markTestIncomplete("Not working yet");
 333+
 334+ $wgVerifyMimeType = false;
 335+ $wgUser = User::newFromName( self::$userName );
 336+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 337+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 338+ $data = $this->doApiRequest( array(
 339+ 'filename' => 'twar.png',
 340+ 'action' => 'firefoggupload',
 341+ 'token' => $token ), $data );
 342+
 343+ $url = $data[0]['uploadUrl'];
 344+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 345+ $size = 0;
 346+ $gotException = false;
 347+ for ( $i = 0; $i < 30; $i++ ) {
 348+ $this->makeChunk( "123" );
 349+ $size += $_FILES['chunk']['size'];
 350+ try {
 351+ $data = $this->doApiRequest( $params, $data );
 352+ } catch (UsageException $e) {
 353+ $arr = $e->getMessageArray();
 354+ $this->assertArrayHasKey( "code", $arr );
 355+ $this->assertEquals( "internal-error", $arr['code'] );
 356+
 357+ $this->assertEquals( "fileexistserror", $arr[0][0] );
 358+ $gotException = true;
 359+ }
 360+ }
 361+ $this->cleanChunk();
 362+ $this->assertTrue($gotException);
 363+ }
 364+
 365+ function testCleanup() {
 366+ $dbw = wfGetDB( DB_MASTER );
 367+ $dbw->begin();
 368+ $dbw->delete("image", array('img_user_text' => self::$userName ));
 369+ $dbw->commit();
 370+ $this->assertTrue(true);
 371+ }
 372+}
Property changes on: trunk/extensions/ResumableUpload/tests/UploadFromChunksTest.php
___________________________________________________________________
Added: svn:eol-style
1373 + native
Added: svn:eol-syle
2374 + native
Index: trunk/extensions/ResumableUpload/FirefoggChunkedUpload.i18n.php
@@ -0,0 +1,313 @@
 2+<?php
 3+/**
 4+ * Internationalisation file for extension FirefoggChunkedUploading.
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$messages = array();
 11+
 12+$messages['en'] = array(
 13+ 'firefoggcu-desc' => "Firefogg's Chunked Uploading protocol",
 14+);
 15+
 16+/** Message documentation (Message documentation)
 17+ * @author Umherirrender
 18+ */
 19+$messages['qqq'] = array(
 20+ 'firefoggcu-desc' => '{{desc}}',
 21+);
 22+
 23+/** Belarusian (Taraškievica orthography) (Беларуская (тарашкевіца))
 24+ * @author Wizardist
 25+ */
 26+$messages['be-tarask'] = array(
 27+ 'firefoggcu-desc' => 'Парцыённы пратакол загрузкі Firefogg',
 28+);
 29+
 30+/** Breton (Brezhoneg)
 31+ * @author Fulup
 32+ */
 33+$messages['br'] = array(
 34+ 'firefoggcu-desc' => 'Protokol pellgargañ a-dammoù Firefogg',
 35+);
 36+
 37+/** Bosnian (Bosanski)
 38+ * @author CERminator
 39+ */
 40+$messages['bs'] = array(
 41+ 'firefoggcu-desc' => 'Firefogg-ov protokol otpremanja',
 42+);
 43+
 44+/** Catalan (Català)
 45+ * @author Paucabot
 46+ */
 47+$messages['ca'] = array(
 48+ 'firefoggcu-desc' => 'Protocol de Càrrega Fragmentada de Firefogg',
 49+);
 50+
 51+/** Welsh (Cymraeg)
 52+ * @author Lloffiwr
 53+ * @author Xxglennxx
 54+ */
 55+$messages['cy'] = array(
 56+ 'firefoggcu-desc' => "Protocol Firefogg i Uwchlwytho'n Dalpiau",
 57+);
 58+
 59+/** German (Deutsch)
 60+ * @author Purodha
 61+ * @author Umherirrender
 62+ */
 63+$messages['de'] = array(
 64+ 'firefoggcu-desc' => 'Etappenweises Protokoll zum Hochladen für Firefogg',
 65+);
 66+
 67+/** Lower Sorbian (Dolnoserbski)
 68+ * @author Michawiki
 69+ */
 70+$messages['dsb'] = array(
 71+ 'firefoggcu-desc' => 'Protokol Firefogg za nagraśe pó porcijach',
 72+);
 73+
 74+/** Greek (Ελληνικά)
 75+ * @author Περίεργος
 76+ */
 77+$messages['el'] = array(
 78+ 'firefoggcu-desc' => 'πρωτόκολλο Κατατεμνόμενης Φόρτωσης της Firefogg',
 79+);
 80+
 81+/** Spanish (Español)
 82+ * @author Pertile
 83+ */
 84+$messages['es'] = array(
 85+ 'firefoggcu-desc' => 'Protocolo de carga por trozos de Firefogg',
 86+);
 87+
 88+/** Basque (Euskara)
 89+ * @author An13sa
 90+ */
 91+$messages['eu'] = array(
 92+ 'firefoggcu-desc' => 'Firefoggen Chunked igoera protokoloa',
 93+);
 94+
 95+/** Finnish (Suomi)
 96+ * @author Nike
 97+ */
 98+$messages['fi'] = array(
 99+ 'firefoggcu-desc' => 'Firefoggin pilkottu latausprotokolla',
 100+);
 101+
 102+/** French (Français)
 103+ * @author Peter17
 104+ */
 105+$messages['fr'] = array(
 106+ 'firefoggcu-desc' => 'Protocole de téléversement par morceaux de Firefogg',
 107+);
 108+
 109+/** Galician (Galego)
 110+ * @author Toliño
 111+ */
 112+$messages['gl'] = array(
 113+ 'firefoggcu-desc' => 'Protocolo de carga por anacos do Firefogg',
 114+);
 115+
 116+/** Swiss German (Alemannisch)
 117+ * @author Als-Holder
 118+ */
 119+$messages['gsw'] = array(
 120+ 'firefoggcu-desc' => 'Stuckwys-Uffeladigs-Protokoll vu Firefogg',
 121+);
 122+
 123+/** Hebrew (עברית)
 124+ * @author YaronSh
 125+ */
 126+$messages['he'] = array(
 127+ 'firefoggcu-desc' => 'פרוטוקול השליחה בחלקים של Firefogg',
 128+);
 129+
 130+/** Upper Sorbian (Hornjoserbsce)
 131+ * @author Michawiki
 132+ */
 133+$messages['hsb'] = array(
 134+ 'firefoggcu-desc' => 'Protokol Firefogg za nahraće po porcijach',
 135+);
 136+
 137+/** Hungarian (Magyar)
 138+ * @author Glanthor Reviol
 139+ */
 140+$messages['hu'] = array(
 141+ 'firefoggcu-desc' => 'A Firefogg daraboló feltöltési protokollja',
 142+);
 143+
 144+/** Interlingua (Interlingua)
 145+ * @author McDutchie
 146+ */
 147+$messages['ia'] = array(
 148+ 'firefoggcu-desc' => 'Protocollo de incargamento in portiones de Firefogg',
 149+);
 150+
 151+/** Indonesian (Bahasa Indonesia)
 152+ * @author Iwan Novirion
 153+ */
 154+$messages['id'] = array(
 155+ 'firefoggcu-desc' => 'Protokol mengunggah berkas Firefogg',
 156+);
 157+
 158+/** Italian (Italiano)
 159+ * @author Civvì
 160+ */
 161+$messages['it'] = array(
 162+ 'firefoggcu-desc' => 'Protocollo di caricamento di Firefogg Chunked',
 163+);
 164+
 165+/** Japanese (日本語)
 166+ * @author Aotake
 167+ */
 168+$messages['ja'] = array(
 169+ 'firefoggcu-desc' => 'Firefogg 一括アップロードプロトコル',
 170+);
 171+
 172+/** Colognian (Ripoarisch)
 173+ * @author Purodha
 174+ */
 175+$messages['ksh'] = array(
 176+ 'firefoggcu-desc' => 'Dem Firefogg sing Protokoll zum Huhlaade en Etappe.',
 177+);
 178+
 179+/** Luxembourgish (Lëtzebuergesch)
 180+ * @author Robby
 181+ */
 182+$messages['lb'] = array(
 183+ 'firefoggcu-desc' => 'Eropluedprotokoll vu Firefogg dee mat eenzelne Stecker schafft',
 184+);
 185+
 186+/** Macedonian (Македонски)
 187+ * @author Bjankuloski06
 188+ */
 189+$messages['mk'] = array(
 190+ 'firefoggcu-desc' => 'Firefogg-овиот протокол на подигање дел по дел',
 191+);
 192+
 193+/** Dutch (Nederlands)
 194+ * @author Siebrand
 195+ */
 196+$messages['nl'] = array(
 197+ 'firefoggcu-desc' => 'Firefogg-protocol voor uploaden in delen (Chunked Uploading)',
 198+);
 199+
 200+/** Norwegian (bokmål)‬ (‪Norsk (bokmål)‬)
 201+ * @author Nghtwlkr
 202+ */
 203+$messages['no'] = array(
 204+ 'firefoggcu-desc' => 'Firefoggs bulkopplastingsprotokoll',
 205+);
 206+
 207+/** Occitan (Occitan)
 208+ * @author Cedric31
 209+ */
 210+$messages['oc'] = array(
 211+ 'firefoggcu-desc' => 'Protocòl de mandadís per tròces de Firefogg',
 212+);
 213+
 214+/** Polish (Polski)
 215+ * @author Sp5uhe
 216+ */
 217+$messages['pl'] = array(
 218+ 'firefoggcu-desc' => 'Protokół przesyłania kawałkami Firefogg',
 219+);
 220+
 221+/** Piedmontese (Piemontèis)
 222+ * @author Dragonòt
 223+ */
 224+$messages['pms'] = array(
 225+ 'firefoggcu-desc' => 'Protocòl Firefogg Chunked Upload',
 226+);
 227+
 228+/** Portuguese (Português)
 229+ * @author Hamilton Abreu
 230+ */
 231+$messages['pt'] = array(
 232+ 'firefoggcu-desc' => 'Protocolo de Chunked Uploading do Firefogg',
 233+);
 234+
 235+/** Brazilian Portuguese (Português do Brasil)
 236+ * @author Giro720
 237+ */
 238+$messages['pt-br'] = array(
 239+ 'firefoggcu-desc' => 'Protocolo de Chunked Uploading do Firefogg',
 240+);
 241+
 242+/** Tarandíne (Tarandíne)
 243+ * @author Joetaras
 244+ */
 245+$messages['roa-tara'] = array(
 246+ 'firefoggcu-desc' => 'Protocolle de carecamende Chunked Firefogg',
 247+);
 248+
 249+/** Russian (Русский)
 250+ * @author Александр Сигачёв
 251+ */
 252+$messages['ru'] = array(
 253+ 'firefoggcu-desc' => 'Порционный протокол загрузки Firefogg',
 254+);
 255+
 256+/** Serbian Cyrillic ekavian (Српски (ћирилица))
 257+ * @author Charmed94
 258+ */
 259+$messages['sr-ec'] = array(
 260+ 'firefoggcu-desc' => 'Firefogg-ов протокол отпремања',
 261+);
 262+
 263+/** Serbian Latin ekavian (Srpski (latinica)) */
 264+$messages['sr-el'] = array(
 265+ 'firefoggcu-desc' => 'Firefogg-ov protokol otpremanja',
 266+);
 267+
 268+/** Swedish (Svenska)
 269+ * @author Ainali
 270+ */
 271+$messages['sv'] = array(
 272+ 'firefoggcu-desc' => 'Firefoggs segmenterade uppladdningskontroll',
 273+);
 274+
 275+/** Telugu (తెలుగు)
 276+ * @author Ravichandra
 277+ */
 278+$messages['te'] = array(
 279+ 'firefoggcu-desc' => 'ఫైర్‌ఫాగ్ విడిభాగాలుగా ఎక్కించే నియమావళి',
 280+);
 281+
 282+/** Tagalog (Tagalog)
 283+ * @author AnakngAraw
 284+ */
 285+$messages['tl'] = array(
 286+ 'firefoggcu-desc' => 'Pamamaraan sa Matipak na Pagkakargang Paitaas ng Firefogg',
 287+);
 288+
 289+/** Turkish (Türkçe)
 290+ * @author Joseph
 291+ */
 292+$messages['tr'] = array(
 293+ 'firefoggcu-desc' => "Firefogg'un Toplu Yükleme iletişim kuralı",
 294+);
 295+
 296+/** Ukrainian (Українська)
 297+ * @author Ytsukeng Fyvaprol
 298+ */
 299+$messages['uk'] = array(
 300+ 'firefoggcu-desc' => 'Порційний протокол завантаження Firefogg',
 301+);
 302+
 303+/** Simplified Chinese (‪中文(简体)‬) */
 304+$messages['zh-hans'] = array(
 305+ 'firefoggcu-desc' => 'Firefogg的分块上传协议',
 306+);
 307+
 308+/** Traditional Chinese (‪中文(繁體)‬)
 309+ * @author Frankou
 310+ */
 311+$messages['zh-hant'] = array(
 312+ 'firefoggcu-desc' => 'Firefogg的分塊上傳協議',
 313+);
 314+
Property changes on: trunk/extensions/ResumableUpload/FirefoggChunkedUpload.i18n.php
___________________________________________________________________
Added: svn:eol-style
1315 + native
Added: svn:eol-syle
2316 + native
Index: trunk/extensions/ResumableUpload/FirefoggChunkedUploadHandler.php
@@ -0,0 +1,165 @@
 2+<?php
 3+if ( !defined( 'MEDIAWIKI' ) ) die();
 4+/**
 5+ * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
 6+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 7+ */
 8+
 9+class FirefoggChunkedUploadHandler extends UploadBase {
 10+ const INIT = 1;
 11+ const CHUNK = 2;
 12+ const DONE = 3;
 13+
 14+ protected $chunkMode; // INIT, CHUNK, DONE
 15+ protected $sessionKey;
 16+ protected $comment;
 17+ protected $repoPath;
 18+ protected $pageText;
 19+ protected $watchlist;
 20+
 21+ public $status;
 22+
 23+ public function initializeFromRequest(&$request) {}
 24+ public function getChunkMode() {return $this->chunkMode;}
 25+ public function getDesiredName() {return $this->mDesiredDestName;}
 26+
 27+ /**
 28+ * Set session information for chunked uploads and allocate a unique key.
 29+ * @param $comment string
 30+ * @param $pageText string
 31+ * @param $watchlist bodolean
 32+ *
 33+ * @returns string the session key for this chunked upload
 34+ */
 35+ public function setupChunkSession( $comment, $pageText, $watchlist ) {
 36+ if ( !isset( $this->sessionKey ) ) {
 37+ $this->sessionKey = $this->getSessionKey();
 38+ }
 39+ foreach ( array( 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
 40+ as $key ) {
 41+ if ( isset( $this->$key ) ) {
 42+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey][$key] = $this->$key;
 43+ }
 44+ }
 45+ if ( isset( $comment ) ) {
 46+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['commment'] = $comment;
 47+ }
 48+ if ( isset( $pageText ) ) {
 49+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['pageText'] = $pageText;
 50+ }
 51+ if ( isset( $watchlist ) ) {
 52+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['watchlist'] = $watchlist;
 53+ }
 54+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['version'] = UploadBase::SESSION_VERSION;
 55+
 56+ return $this->sessionKey;
 57+ }
 58+
 59+ /**
 60+ * Initialize a request
 61+ * @param $done boolean Set if this is the last chunk
 62+ * @param $filename string The desired filename, set only on first request.
 63+ * @param $sessionKey string The chunksession parameter
 64+ * @param $path string The path to the temp file containing this chunk
 65+ * @param $chunkSize integer The size of this chunk
 66+ * @param $sessionData array sessiondata
 67+ *
 68+ * @return mixed True if there was no error, otherwise an error description suitable for passing to dieUsage()
 69+ */
 70+ public function initialize( $done, $filename, $sessionKey, $path, $chunkSize, $sessionData ) {
 71+ if( $filename ) $this->mDesiredDestName = $filename;
 72+ $this->mTempPath = $path;
 73+
 74+ if ( $sessionKey !== null ) {
 75+ $status = $this->initFromSessionKey( $sessionKey, $sessionData, $chunkSize );
 76+ if( $status !== true ) {
 77+ return $status;
 78+ }
 79+
 80+ if ( $done ) {
 81+ $this->chunkMode = self::DONE;
 82+ } else {
 83+ $this->mTempPath = $path;
 84+ $this->chunkMode = self::CHUNK;
 85+ }
 86+ } else {
 87+ // session key not set, init the chunk upload system:
 88+ $this->chunkMode = self::INIT;
 89+ }
 90+
 91+ if ( $this->mDesiredDestName === null ) {
 92+ return 'Insufficient information for initialization.';
 93+ }
 94+
 95+ return true;
 96+ }
 97+
 98+ /**
 99+ * Initialize a continuation of a chunked upload from a session key
 100+ * @param $sessionKey string
 101+ * @param $request WebRequest
 102+ * @param $fileSize int Size of this chunk
 103+ *
 104+ * @returns void
 105+ */
 106+ protected function initFromSessionKey( $sessionKey, $sessionData, $fileSize ) {
 107+ // testing against null because we don't want to cause obscure
 108+ // bugs when $sessionKey is full of "0"
 109+ $this->sessionKey = $sessionKey;
 110+
 111+ if ( isset( $sessionData[$this->sessionKey]['version'] )
 112+ && $sessionData[$this->sessionKey]['version'] == UploadBase::SESSION_VERSION )
 113+ {
 114+ foreach ( array( 'comment', 'pageText', 'watchlist', 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
 115+ as $key ) {
 116+ if ( isset( $sessionData[$this->sessionKey][$key] ) ) {
 117+ $this->$key = $sessionData[$this->sessionKey][$key];
 118+ }
 119+ }
 120+
 121+ $this->mFileSize += $fileSize;
 122+ } else {
 123+ return 'Not a valid session key';
 124+ }
 125+
 126+ return true;
 127+ }
 128+
 129+ /**
 130+ * Append a chunk to the temporary file.
 131+ *
 132+ * @return void
 133+ */
 134+ public function appendChunk() {
 135+ global $wgMaxUploadSize;
 136+
 137+ if ( !$this->repoPath ) {
 138+ $this->status = $this->saveTempUploadedFile( $this->mDesiredDestName, $this->mTempPath );
 139+
 140+ if ( $this->status->isOK() ) {
 141+ $this->repoPath = $this->status->value;
 142+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['repoPath'] = $this->repoPath;
 143+ }
 144+ return $this->status;
 145+ }
 146+ if ( $this->getRealPath( $this->repoPath ) ) {
 147+ $this->status = $this->appendToUploadFile( $this->repoPath, $this->mTempPath );
 148+
 149+ if ( $this->mFileSize > $wgMaxUploadSize )
 150+ $this->status = Status::newFatal( 'largefileserver' );
 151+
 152+ } else {
 153+ $this->status = Status::newFatal( 'filenotfound', $this->repoPath );
 154+ }
 155+ return $this->status;
 156+ }
 157+
 158+ /**
 159+ * Append the final chunk and ready file for parent::performUpload()
 160+ * @return void
 161+ */
 162+ public function finalizeFile() {
 163+ $this->appendChunk();
 164+ $this->mTempPath = $this->getRealPath( $this->repoPath );
 165+ }
 166+}
\ No newline at end of file
Property changes on: trunk/extensions/ResumableUpload/FirefoggChunkedUploadHandler.php
___________________________________________________________________
Added: svn:eol-style
1167 + native
Added: svn:eol-syle
2168 + native
Index: trunk/extensions/ResumableUpload/FirefoggChunkedUpload.php
@@ -0,0 +1,21 @@
 2+<?php
 3+if ( !defined( 'MEDIAWIKI' ) ) die();
 4+/**
 5+ * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
 6+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 7+ */
 8+
 9+$wgExtensionCredits['other'][] = array(
 10+ 'path' => __FILE__,
 11+ 'name' => 'FirefoggChunkedUpload',
 12+ 'url' => 'http://www.mediawiki.org/wiki/Extension:FirefoggChunkedUpload',
 13+ 'author' => array( 'Mark A. Hershberger' ),
 14+ 'descriptionmsg' => 'firefoggcu-desc',
 15+);
 16+
 17+$dir = dirname( __FILE__ ) . '/';
 18+$wgExtensionMessagesFiles['FirefoggChunkedUpload'] = $dir . 'FirefoggChunkedUpload.i18n.php';
 19+$wgAutoloadClasses['ApiFirefoggChunkedUpload'] = $dir . 'ApiFirefoggChunkedUpload.php';
 20+$wgAutoloadClasses['FirefoggChunkedUploadHandler'] = $dir . 'FirefoggChunkedUploadHandler.php';
 21+
 22+$wgAPIModules['firefoggupload'] = 'ApiFirefoggChunkedUpload';
Property changes on: trunk/extensions/ResumableUpload/FirefoggChunkedUpload.php
___________________________________________________________________
Added: svn:eol-style
123 + native
Added: svn:eol-syle
224 + native

Follow-up revisions

RevisionCommit summaryAuthorDate
r88248Followup r88241: Tweak message, update Translatewiki registrationraymond17:23, 16 May 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r79293Properly qualify UPLOAD_SESSIONreedy21:07, 30 December 2010
r88236Renamed FirefoggChunkedUpload to ResumableUpload...dale14:23, 16 May 2011

Comments

#Comment by Reedy (talk | contribs)   15:12, 16 May 2011

Why not use the highest version in trunk?

You're missing i18n additions, as well as register_globals vulnerabilities fixes and svn property fixes

#Comment by Mdale (talk | contribs)   15:28, 16 May 2011

All those files were essentially renamed, all the il8n additions were lost because of a different extension name. The register_globals vulnerabilities was both part of the update and what Tim had already committed. r79293 seemed to be the last substantial commit directly associated with Chunk uploading. Previously people have complained when I used head the commit right before it was deleted, since it had "nothing to do" with the given path. But yes your right I should have used a more recent version.

Status & tagging log