r64419 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r64418‎ | r64419 | r64420 >
Date:03:36, 31 March 2010
Author:mah
Status:deferred
Tags:
Comment:
* Select final namespace for FirefoggChunkedUpload extension.
* Passes old tests + a couple of new ones.
* TODO: Actually test against Firefogg.
* TODO: backport to 1.16 — I think this will mostly mean cutting-n-pasting code from the upload api
Modified paths:
  • /trunk/extensions/FirefoggChunkedUpload (added) (history)
  • /trunk/extensions/FirefoggChunkedUpload/ApiFirefoggChunkedUpload.php (added) (history)
  • /trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUpload.i18n.php (added) (history)
  • /trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUpload.php (added) (history)
  • /trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUploadHandler.php (added) (history)
  • /trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUploading.i18n.php (deleted) (history)
  • /trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUploading.php (deleted) (history)
  • /trunk/extensions/FirefoggChunkedUpload/tests (added) (history)
  • /trunk/extensions/FirefoggChunkedUpload/tests/UploadFromChunksTest.php (added) (history)
  • /trunk/extensions/FirefoggChunkedUpload/tests/bootstrap.php (added) (history)
  • /trunk/extensions/FirefoggChunkedUpload/tests/phpunit.xml (added) (history)
  • /trunk/extensions/FirefoggChunkedUploading (deleted) (history)

Diff [purge]

Index: trunk/extensions/FirefoggChunkedUpload/ApiFirefoggChunkedUpload.php
@@ -0,0 +1,193 @@
 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+ /* public function __construct( $main, $action ) { */
 11+ /* parent::__construct( $main, $action ); */
 12+ /* } */
 13+
 14+ public function execute() {
 15+ global $wgUser;
 16+
 17+ // Check whether upload is enabled
 18+ if ( !UploadBase::isEnabled() ) {
 19+ $this->dieUsageMsg( array( 'uploaddisabled' ) );
 20+ }
 21+
 22+ $this->mParams = $this->extractRequestParams();
 23+
 24+ $this->validateParams( $this->mParams );
 25+
 26+ $request = $this->getMain()->getRequest();
 27+ $this->mUpload = new FirefoggChunkedUploadHandler;
 28+
 29+ $status = $this->mUpload->initialize(
 30+ $request->getVal( 'done', null ),
 31+ $request->getVal( 'filename', null ),
 32+ $request->getVal( 'chunksession', null ),
 33+ $request->getFileTempName( 'chunk' ),
 34+ $request->getFileSize( 'chunk' ),
 35+ $request->getSessionData( UploadBase::getSessionKeyname() )
 36+ );
 37+
 38+ if ( $status !== true ) {
 39+ $this->dieUsage( $status, 'chunk-init-error' );
 40+ }
 41+
 42+ $ret = $this->performUpload( );
 43+
 44+ if(is_array($ret)) {
 45+ foreach($ret as $key => $val) {
 46+ $this->getResult()->addValue(null, $key, $val);
 47+ }
 48+ } else {
 49+ $this->dieUsage($ret, 'error');
 50+ }
 51+ }
 52+
 53+ public function getUpload() { return $this->mUpload; }
 54+
 55+ public function performUploadInit($comment, $pageText, $watch, $user) {
 56+ $check = $this->mUpload->validateNameAndOverwrite();
 57+ if( $check !== true ) {
 58+ $this->getVerificationError( $check );
 59+ }
 60+
 61+ return array('uploadUrl' =>
 62+ wfExpandUrl( wfScript( 'api' ) ) . "?" .
 63+ wfArrayToCGI( array(
 64+ 'action' => 'firefoggupload',
 65+ 'token' => $user->editToken(),
 66+ 'format' => 'json',
 67+ 'filename' => $this->mDesiredDestName,
 68+ 'chunksession' => $this->mUpload->setupChunkSession( $comment, $pageText, $watch ) ) ) );
 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->watch, $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->watch );
 100+ $ret = "unknown error";
 101+
 102+ global $wgUser;
 103+ if ( $this->mUpload->getChunkMode() == FirefoggChunkedUploadHandler::INIT ) {
 104+ $ret = $this->performUploadInit($this->comment, $this->pageText, $this->watch, $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($user);
 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+ } else {
 128+ $required[] = 'filename';
 129+ $required[] = 'comment';
 130+ $required[] = 'watch';
 131+ $required[] = 'ignorewarnings';
 132+ }
 133+
 134+ foreach( $required as $arg ) {
 135+ if ( !isset( $params[$arg] ) ) {
 136+ $this->dieUsageMsg( array( 'missingparam', $arg ) );
 137+ }
 138+ }
 139+ }
 140+
 141+ public function getAllowedParams() {
 142+ return array(
 143+ 'filename' => null,
 144+ 'token' => null,
 145+ 'comment' => null,
 146+ 'watch' => false,
 147+ 'ignorewarnings' => false,
 148+ 'chunksession' => null,
 149+ 'chunk' => null,
 150+ 'done' => false,
 151+ );
 152+ }
 153+
 154+ public function getParamDescription() {
 155+ return array(
 156+ 'filename' => 'Target filename',
 157+ 'token' => 'Edit token. You can get one of these through prop=info',
 158+ 'comment' => 'Upload comment',
 159+ 'watch' => 'Watch the page',
 160+ 'ignorewarnings' => 'Ignore any warnings',
 161+ 'chunksession' => 'The session key, established on the first contact during the chunked upload',
 162+ 'chunk' => 'The data in this chunk of a chunked upload',
 163+ 'done' => 'Set to 1 on the last chunk of a chunked upload',
 164+ );
 165+ }
 166+
 167+ public function getDescription() {
 168+ return array(
 169+ 'Upload a file in chunks using the protocol documented at http://firefogg.org/dev/chunk_post.html'
 170+ );
 171+ }
 172+
 173+ public function getPossibleErrors() {
 174+ return array_merge(
 175+ parent::getPossibleErrors(),
 176+ array(
 177+ array( 'missingparam' ),
 178+ array( 'chunk-init-error' ),
 179+ array( 'code' => 'chunk-init-error', 'info' => 'Insufficient information for initialization.' ),
 180+ array( 'code' => 'chunked-error', 'info' => 'There was a problem initializing the chunked upload.' ),
 181+ )
 182+ );
 183+ }
 184+
 185+ public function getExamples() {
 186+ return array(
 187+ 'api.php?action=firefoggupload&filename=Wiki.png',
 188+ );
 189+ }
 190+
 191+ public function getVersion() {
 192+ return __CLASS__ . ': $Id$';
 193+ }
 194+}
Property changes on: trunk/extensions/FirefoggChunkedUpload/ApiFirefoggChunkedUpload.php
___________________________________________________________________
Added: svn:eol-syle
1195 + native
Index: trunk/extensions/FirefoggChunkedUpload/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/FirefoggChunkedUpload/tests/bootstrap.php
___________________________________________________________________
Added: svn:eol-syle
129 + native
Index: trunk/extensions/FirefoggChunkedUpload/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/FirefoggChunkedUpload/tests/phpunit.xml
___________________________________________________________________
Added: svn:eol-syle
121 + native
Index: trunk/extensions/FirefoggChunkedUpload/tests/UploadFromChunksTest.php
@@ -0,0 +1,361 @@
 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;
 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+
 88+ $this->assertArrayHasKey( "login", $data[0] );
 89+ $this->assertArrayHasKey( "result", $data[0]['login'] );
 90+ $this->assertEquals( "Success", $data[0]['login']['result'] );
 91+ $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
 92+
 93+ return $data;
 94+ }
 95+
 96+ /**
 97+ * @depends testLogin
 98+ */
 99+ function testSetupChunkBannedFileType( $data ) {
 100+ global $wgUser;
 101+ $wgUser = User::newFromName( self::$userName );
 102+ $wgUser->load();
 103+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 104+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 105+ $exception = false;
 106+
 107+ try {
 108+ $this->doApiRequest( array(
 109+ 'action' => 'firefoggupload',
 110+ 'comment' => 'test',
 111+ 'watch' => true,
 112+ 'filename' => 'tmp.txt',
 113+ 'token' => $token ), $data );
 114+ } catch ( UsageException $e ) {
 115+ $exception = true;
 116+ $this->assertEquals( "This type of file is banned", $e->getMessage() );
 117+ }
 118+
 119+ $this->assertTrue( $exception, "Got exception" );
 120+ }
 121+
 122+ /**
 123+ * @depends testLogin
 124+ */
 125+ function testSetupChunkSession( $data ) {
 126+ global $wgUser;
 127+ $wgUser = User::newFromName( self::$userName );
 128+ $wgUser->load();
 129+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 130+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 131+
 132+ $data = $this->doApiRequest( array(
 133+ 'action' => 'firefoggupload',
 134+ 'comment' => 'test',
 135+ 'watch' => true,
 136+ 'filename' => 'TestPic.png',
 137+ 'token' => $token ), $data );
 138+
 139+ $this->assertArrayHasKey( 'uploadUrl', $data[0] );
 140+ $this->assertRegexp( '/action=firefoggupload/', $data[0]['uploadUrl'] );
 141+ $this->assertRegexp( '/chunksession=/', $data[0]['uploadUrl'] );
 142+ $this->assertRegexp( '/token=/', $data[0]['uploadUrl'] );
 143+
 144+ return $data;
 145+ }
 146+
 147+ /**
 148+ * @depends testLogin
 149+ */
 150+ function testInvalidSessionKey( $data ) {
 151+ global $wgUser;
 152+ $wgUser = User::newFromName( self::$userName );
 153+ $wgUser->load();
 154+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 155+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 156+ $exception = false;
 157+
 158+ try {
 159+ $this->doApiRequest( array(
 160+ 'action' => 'firefoggupload',
 161+ 'enablechunks' => true,
 162+ 'token' => $token,
 163+ 'chunksession' => 'bogus' ), $data );
 164+ } catch ( UsageException $e ) {
 165+ $exception = true;
 166+ $this->assertEquals( "Not a valid session key", $e->getMessage() );
 167+ }
 168+
 169+ $this->assertTrue( $exception, "Got exception" );
 170+ }
 171+
 172+ function testPerformUploadInitError() {
 173+ global $wgUser;
 174+ $wgUser = User::newFromId( 1 );
 175+
 176+ $req = new FauxRequest(
 177+ array(
 178+ 'action' => 'firefoggupload',
 179+ 'sessionkey' => '1',
 180+ 'filename' => 'test.png',
 181+ ) );
 182+ $module = new ApiMain( $req, true );
 183+ $gotException = false;
 184+ try {
 185+ $module->execute();
 186+ } catch ( UsageException $e ) {
 187+ $this->assertEquals( "The token parameter must be set", $e->getMessage() );
 188+ $gotException = true;
 189+ }
 190+
 191+ $this->assertTrue( $gotException );
 192+ }
 193+
 194+
 195+ /**
 196+ * @depends testLogin
 197+ */
 198+ function testSetupChunkForBannedContent( $data ) {
 199+ global $wgUser;
 200+ $wgUser = User::newFromName( self::$userName );
 201+ $wgUser->load();
 202+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 203+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 204+ $exception = false;
 205+
 206+ $this->makeChunk( "123" );
 207+ $data = $this->doApiRequest( array(
 208+ 'action' => 'firefoggupload',
 209+ 'comment' => 'test',
 210+ 'watch' => true,
 211+ 'filename' => 'tmp.png',
 212+ 'token' => $token ), $data );
 213+ return $data;
 214+ }
 215+
 216+ /**
 217+ * @depends testSetupChunkForBannedContent
 218+ */
 219+ function testChunkUploadBannedContent ( $data ) {
 220+ global $wgUser;
 221+ $wgUser = User::newFromName( self::$userName );
 222+ $wgUser->load();
 223+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 224+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 225+ $exception = false;
 226+ $url = $data[0]['uploadUrl'];
 227+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 228+ $params['done'] = true;
 229+
 230+ $this->makeChunk( "123" );
 231+ $gotException = false;
 232+ try {
 233+ $data = $this->doApiRequest( $params, $data );
 234+ } catch ( UsageException $e ) {
 235+ $this->assertEquals( "This file did not pass file verification",
 236+ $e->getMessage() );
 237+ $gotException = true;
 238+ }
 239+ $this->cleanChunk();
 240+ $this->assertTrue( $gotException );
 241+ }
 242+
 243+ /**
 244+ * @depends testLogin
 245+ */
 246+ function testUploadChunkDoneGood( $data ) {
 247+ global $wgUser, $wgVerifyMimeType;
 248+ $wgVerifyMimeType = false;
 249+
 250+ $this->markTestIncomplete("Not working yet ... fails every other time b/c we're not dealing with a temporary db");
 251+
 252+ DeleteArchivedFilesImplementation::doDelete(new nullClass, true);
 253+ DeleteArchivedRevisionsImplementation::doDelete(new nullClass);
 254+
 255+ $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID();
 256+ $oldFile = Article::newFromID( $id );
 257+ if ( $oldFile ) {
 258+ $oldFile->doDeleteArticle();
 259+ $oldFile->doPurge();
 260+ }
 261+
 262+ $oldFile = wfFindFile( "Twar.png" );
 263+ if ( $oldFile ) {
 264+ $oldFile->delete();
 265+ }
 266+ $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID();
 267+ $this->assertEquals(0, $id);
 268+
 269+ $oldFile = Article::newFromID( $id );
 270+ $this->assertEquals(null, $oldFile);
 271+
 272+ $wgUser = User::newFromName( self::$userName );
 273+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 274+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 275+ $data = $this->doApiRequest( array(
 276+ 'action' => 'firefoggupload',
 277+ 'comment' => 'test',
 278+ 'watch' => true,
 279+ 'filename' => 'twar.png',
 280+ 'token' => $token ), $data );
 281+
 282+ $url = $data[0]['uploadUrl'];
 283+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 284+ $size = 0;
 285+ for ( $i = 0; $i < 5; $i++ ) {
 286+ $this->makeChunk( "123" );
 287+ $size += $_FILES['chunk']['size'];
 288+
 289+ $data = $this->doApiRequest( $params, $data );
 290+ $this->assertArrayHasKey( "result", $data[0] );
 291+ $this->assertTrue( (bool)$data[0]["result"] );
 292+
 293+ $this->assertArrayHasKey( "filesize", $data[0] );
 294+ $this->assertEquals( $size, $data[0]['filesize'] );
 295+
 296+ $this->cleanChunk();
 297+ }
 298+
 299+ $params['done'] = true;
 300+
 301+ $this->makeChunk( "456" );
 302+ $data = $this->doApiRequest( $params, $data );
 303+
 304+ $this->cleanChunk();
 305+ $this->assertArrayHasKey( 'result', $data[0] );
 306+
 307+ $this->assertEquals( 1, $data[0]['result'] );
 308+
 309+ $this->assertArrayHasKey( 'done', $data[0] );
 310+ $this->assertEquals( 1, $data[0]['done'] );
 311+
 312+ $this->assertArrayHasKey( 'resultUrl', $data[0] );
 313+ $this->assertRegExp( '/File:Twar.png/', $data[0]['resultUrl'] );
 314+ }
 315+
 316+ /**
 317+ * @depends testLogin
 318+ */
 319+ function testUploadChunkDoneDuplicate( $data ) {
 320+ global $wgUser, $wgVerifyMimeType;
 321+
 322+ $this->markTestIncomplete("Not working yet");
 323+
 324+ $wgVerifyMimeType = false;
 325+ $wgUser = User::newFromName( self::$userName );
 326+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 327+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 328+ $data = $this->doApiRequest( array(
 329+ 'filename' => 'twar.png',
 330+ 'action' => 'firefoggupload',
 331+ 'token' => $token ), $data );
 332+
 333+ $url = $data[0]['uploadUrl'];
 334+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 335+ $size = 0;
 336+ $gotException = false;
 337+ for ( $i = 0; $i < 30; $i++ ) {
 338+ $this->makeChunk( "123" );
 339+ $size += $_FILES['chunk']['size'];
 340+ try {
 341+ $data = $this->doApiRequest( $params, $data );
 342+ } catch (UsageException $e) {
 343+ $arr = $e->getMessageArray();
 344+ $this->assertArrayHasKey( "code", $arr );
 345+ $this->assertEquals( "internal-error", $arr['code'] );
 346+
 347+ $this->assertEquals( "fileexistserror", $arr[0][0] );
 348+ $gotException = true;
 349+ }
 350+ }
 351+ $this->cleanChunk();
 352+ $this->assertTrue($gotException);
 353+ }
 354+
 355+ function testCleanup() {
 356+ $dbw = wfGetDB( DB_MASTER );
 357+ $dbw->begin();
 358+ $dbw->delete("image", array('img_user_text' => self::$userName ));
 359+ $dbw->commit();
 360+ $this->assertTrue(true);
 361+ }
 362+}
Property changes on: trunk/extensions/FirefoggChunkedUpload/tests/UploadFromChunksTest.php
___________________________________________________________________
Added: svn:eol-syle
1363 + native
Index: trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUpload.i18n.php
@@ -0,0 +1,13 @@
 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' => 'Extension to implement Firefogg\'s Chunked Uploading protocol',
 14+);
Property changes on: trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUpload.i18n.php
___________________________________________________________________
Added: svn:eol-style
115 + native
Added: svn:eol-syle
216 + native
Index: trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUploadHandler.php
@@ -0,0 +1,164 @@
 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 $watch;
 20+
 21+ public $status;
 22+
 23+ public function initializeFromRequest(&$request) {}
 24+ public function getChunkMode() {return $this->chunkMode;}
 25+
 26+ /**
 27+ * Set session information for chunked uploads and allocate a unique key.
 28+ * @param $comment string
 29+ * @param $pageText string
 30+ * @param $watch bodolean
 31+ *
 32+ * @returns string the session key for this chunked upload
 33+ */
 34+ public function setupChunkSession( $comment, $pageText, $watch ) {
 35+ if ( !isset( $this->sessionKey ) ) {
 36+ $this->sessionKey = $this->getSessionKey();
 37+ }
 38+ foreach ( array( 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
 39+ as $key ) {
 40+ if ( isset( $this->$key ) ) {
 41+ $_SESSION[self::SESSION_KEYNAME][$this->sessionKey][$key] = $this->$key;
 42+ }
 43+ }
 44+ if ( isset( $comment ) ) {
 45+ $_SESSION[self::SESSION_KEYNAME][$this->sessionKey]['commment'] = $comment;
 46+ }
 47+ if ( isset( $pageText ) ) {
 48+ $_SESSION[self::SESSION_KEYNAME][$this->sessionKey]['pageText'] = $pageText;
 49+ }
 50+ if ( isset( $watch ) ) {
 51+ $_SESSION[self::SESSION_KEYNAME][$this->sessionKey]['watch'] = $watch;
 52+ }
 53+ $_SESSION[self::SESSION_KEYNAME][$this->sessionKey]['version'] = self::SESSION_VERSION;
 54+
 55+ return $this->sessionKey;
 56+ }
 57+
 58+ /**
 59+ * Initialize a request
 60+ * @param $done boolean Set if this is the last chunk
 61+ * @param $filename string The desired filename, set only on first request.
 62+ * @param $sessionKey string The chunksession parameter
 63+ * @param $path string The path to the temp file containing this chunk
 64+ * @param $chunkSize integer The size of this chunk
 65+ * @param $sessionData array sessiondata
 66+ *
 67+ * @return mixed True if there was no error, otherwise an error description suitable for passing to dieUsage()
 68+ */
 69+ public function initialize( $done, $filename, $sessionKey, $path, $chunkSize, $sessionData ) {
 70+ if( $filename ) $this->mDesiredDestName = $filename;
 71+ $this->mTempPath = $path;
 72+
 73+ if ( $sessionKey !== null ) {
 74+ $status = $this->initFromSessionKey( $sessionKey, $sessionData, $chunkSize );
 75+ if( $status !== true ) {
 76+ return $status;
 77+ }
 78+
 79+ if ( $done ) {
 80+ $this->chunkMode = self::DONE;
 81+ } else {
 82+ $this->mTempPath = $path;
 83+ $this->chunkMode = self::CHUNK;
 84+ }
 85+ } else {
 86+ // session key not set, init the chunk upload system:
 87+ $this->chunkMode = self::INIT;
 88+ }
 89+
 90+ if ( $this->mDesiredDestName === null ) {
 91+ return 'Insufficient information for initialization.';
 92+ }
 93+
 94+ return true;
 95+ }
 96+
 97+ /**
 98+ * Initialize a continuation of a chunked upload from a session key
 99+ * @param $sessionKey string
 100+ * @param $request WebRequest
 101+ * @param $fileSize int Size of this chunk
 102+ *
 103+ * @returns void
 104+ */
 105+ protected function initFromSessionKey( $sessionKey, $sessionData, $fileSize ) {
 106+ // testing against null because we don't want to cause obscure
 107+ // bugs when $sessionKey is full of "0"
 108+ $this->sessionKey = $sessionKey;
 109+
 110+ if ( isset( $sessionData[$this->sessionKey]['version'] )
 111+ && $sessionData[$this->sessionKey]['version'] == self::SESSION_VERSION )
 112+ {
 113+ foreach ( array( 'comment', 'pageText', 'watch', 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
 114+ as $key ) {
 115+ if ( isset( $sessionData[$this->sessionKey][$key] ) ) {
 116+ $this->$key = $sessionData[$this->sessionKey][$key];
 117+ }
 118+ }
 119+
 120+ $this->mFileSize += $fileSize;
 121+ } else {
 122+ return 'Not a valid session key';
 123+ }
 124+
 125+ return true;
 126+ }
 127+
 128+ /**
 129+ * Append a chunk to the temporary file.
 130+ *
 131+ * @return void
 132+ */
 133+ public function appendChunk() {
 134+ global $wgMaxUploadSize;
 135+
 136+ if ( !$this->repoPath ) {
 137+ $this->status = $this->saveTempUploadedFile( $this->mDesiredDestName, $this->mTempPath );
 138+
 139+ if ( $this->status->isOK() ) {
 140+ $this->repoPath = $this->status->value;
 141+ $_SESSION[self::SESSION_KEYNAME][$this->sessionKey]['repoPath'] = $this->repoPath;
 142+ }
 143+ return $this->status;
 144+ }
 145+ if ( $this->getRealPath( $this->repoPath ) ) {
 146+ $this->status = $this->appendToUploadFile( $this->repoPath, $this->mTempPath );
 147+
 148+ if ( $this->mFileSize > $wgMaxUploadSize )
 149+ $this->status = Status::newFatal( 'largefileserver' );
 150+
 151+ } else {
 152+ $this->status = Status::newFatal( 'filenotfound', $this->repoPath );
 153+ }
 154+ return $this->status;
 155+ }
 156+
 157+ /**
 158+ * Append the final chunk and ready file for parent::performUpload()
 159+ * @return void
 160+ */
 161+ public function finalizeFile() {
 162+ $this->appendChunk();
 163+ $this->mTempPath = $this->getRealPath( $this->repoPath );
 164+ }
 165+}
\ No newline at end of file
Property changes on: trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUploadHandler.php
___________________________________________________________________
Added: svn:eol-syle
1166 + native
Index: trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUpload.php
@@ -0,0 +1,18 @@
 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['specialpage'][] = 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+
 18+$dir = dirname( __FILE__ ) . '/';
 19+$wgExtensionMessagesFiles['FirefoggChunkedUploading'] = $dir . 'FirefoggChunkedUpload.i18n.php';
Property changes on: trunk/extensions/FirefoggChunkedUpload/FirefoggChunkedUpload.php
___________________________________________________________________
Added: svn:eol-style
120 + native
Added: svn:eol-syle
221 + native

Status & tagging log