r88236 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r88235‎ | r88236 | r88237 >
Date:14:23, 16 May 2011
Author:dale
Status:reverted (Comments)
Tags:
Comment:
Renamed FirefoggChunkedUpload to ResumableUpload
Modified protocol to include total filesize in initial request, include byte offset on every chunk, and more strict parameter checks
( just an initial rename not yet working or tested )
Modified paths:
  • /trunk/extensions/FirefoggChunkedUpload (deleted) (history)
  • /trunk/extensions/ResumableUpload (added) (history)
  • /trunk/extensions/ResumableUpload/.buildpath (added) (history)
  • /trunk/extensions/ResumableUpload/.project (added) (history)
  • /trunk/extensions/ResumableUpload/ApiResumableUpload.php (added) (history)
  • /trunk/extensions/ResumableUpload/README (added) (history)
  • /trunk/extensions/ResumableUpload/ResumableUpload.i18n.php (added) (history)
  • /trunk/extensions/ResumableUpload/ResumableUpload.php (added) (history)
  • /trunk/extensions/ResumableUpload/ResumableUploadHandler.php (added) (history)

Diff [purge]

Index: trunk/extensions/ResumableUpload/.project
@@ -0,0 +1,39 @@
 2+<?xml version="1.0" encoding="UTF-8"?>
 3+<projectDescription>
 4+ <name>ResumableUpload</name>
 5+ <comment></comment>
 6+ <projects>
 7+ </projects>
 8+ <buildSpec>
 9+ <buildCommand>
 10+ <name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
 11+ <arguments>
 12+ </arguments>
 13+ </buildCommand>
 14+ <buildCommand>
 15+ <name>org.eclipse.wst.validation.validationbuilder</name>
 16+ <arguments>
 17+ </arguments>
 18+ </buildCommand>
 19+ <buildCommand>
 20+ <name>org.eclipse.dltk.core.scriptbuilder</name>
 21+ <arguments>
 22+ </arguments>
 23+ </buildCommand>
 24+ <buildCommand>
 25+ <name>org.eclipse.pde.ManifestBuilder</name>
 26+ <arguments>
 27+ </arguments>
 28+ </buildCommand>
 29+ <buildCommand>
 30+ <name>org.eclipse.pde.SchemaBuilder</name>
 31+ <arguments>
 32+ </arguments>
 33+ </buildCommand>
 34+ </buildSpec>
 35+ <natures>
 36+ <nature>org.eclipse.pde.PluginNature</nature>
 37+ <nature>org.eclipse.php.core.PHPNature</nature>
 38+ <nature>org.eclipse.wst.jsdt.core.jsNature</nature>
 39+ </natures>
 40+</projectDescription>
Property changes on: trunk/extensions/ResumableUpload/.project
___________________________________________________________________
Added: svn:mime-type
141 + text/plain
Index: trunk/extensions/ResumableUpload/ApiResumableUpload.php
@@ -0,0 +1,241 @@
 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 ApiResumableUpload extends ApiUpload {
 10+ protected $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 ResumableUploadHandler;
 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() {
 52+ return $this->mUpload;
 53+ }
 54+
 55+ public function performUploadInit($comment, $pageText, $watchlist, $user) {
 56+ // Verify the initial upload request
 57+ $this->verifyUploadInit();
 58+
 59+ $session = $this->mUpload->setupChunkSession( $comment, $pageText, $watchlist );
 60+ return array('uploadUrl' =>
 61+ wfExpandUrl( wfScript( 'api' ) ) . "?" .
 62+ wfArrayToCGI( array(
 63+ 'action' => 'resumableupload',
 64+ 'token' => $user->editToken(),
 65+ 'format' => 'json',
 66+ 'chunksession' => $session,
 67+ 'filename' => $this->mUpload->getDesiredName(),
 68+ ) ) );
 69+ }
 70+
 71+ /**
 72+ * Check the upload
 73+ */
 74+ public function verifyUploadInit(){
 75+
 76+ // Check for valid name:
 77+ $check = $this->mUpload->validateName();
 78+ if( $check !== true ) {
 79+ return $this->getVerificationError( $check );
 80+ }
 81+
 82+ // Check proposed file size
 83+ $maxSize = $this->getMaxUploadSize( '*' );
 84+ if( $this->mFileSize > $maxSize ) {
 85+ // We have to return an array here instead of getVerificationError so that we can include
 86+ // the max size info.
 87+ return array(
 88+ 'status' => self::FILE_TOO_LARGE,
 89+ 'max' => $maxSize,
 90+ );
 91+ }
 92+
 93+ return true;
 94+ }
 95+
 96+ public function performUploadChunk() {
 97+ $this->mUpload->setupChunkSession();
 98+ $status = $this->mUpload->appendChunk();
 99+ if ( !$status->isOK() ) {
 100+ $this->dieUsage($status->getWikiText(), 'error');
 101+ }
 102+ return array( 'result' => 1, 'filesize' => $this->mUpload->getFileSize() );
 103+ }
 104+
 105+ public function performUploadDone( $user ) {
 106+ $this->mUpload->finalizeFile();
 107+ $status = parent::performUpload( $this->comment, $this->pageText, $this->watchlist, $user );
 108+
 109+ if ( $status['result'] !== 'Success' ) {
 110+ return $status;
 111+ }
 112+ $file = $this->mUpload->getLocalFile();
 113+ return array('result' => 1, 'done' => 1, 'resultUrl' => wfExpandUrl( $file->getDescriptionUrl() ) );
 114+ }
 115+
 116+ /**
 117+ * Handle a chunk of the upload.
 118+ * @see UploadBase::performUpload
 119+ */
 120+ public function performUpload( ) {
 121+ wfDebug( "\n\n\performUpload(chunked): comment: " . $this->comment .
 122+ ' pageText: ' . $this->pageText . ' watch: ' . $this->watchlist );
 123+ $ret = "unknown error";
 124+
 125+ global $wgUser;
 126+ switch( $this->mUpload->getChunkMode() ){
 127+ case ResumableUploadHandler::INIT:
 128+ return $this->performUploadInit($this->comment, $this->pageText, $this->watchlist, $wgUser);
 129+ break;
 130+ case ResumableUploadHandler::CHUNK:
 131+ return $this->performUploadChunk();
 132+ break;
 133+ case ResumableUploadHandler::DONE:
 134+ return $this->performUploadDone($wgUser);;
 135+ break;
 136+ }
 137+ return $ret;
 138+ }
 139+
 140+ public function mustBePosted() {
 141+ return true;
 142+ }
 143+
 144+ public function isWriteMode() {
 145+ return true;
 146+ }
 147+
 148+ protected function validateParams( $params ) {
 149+ $required = array();
 150+ // Check required params for each upload mode:
 151+ switch( $this->mUpload->getChunkMode() ){
 152+ case ResumableUploadHandler::INIT:
 153+ $required[] = 'filename';
 154+ $required[] = 'comment';
 155+ $required[] = 'token';
 156+ $required[] = 'filesize';
 157+ break;
 158+ case ResumableUploadHandler::CHUNK:
 159+ $required[] = 'byteoffset';
 160+ $required[] = 'chunksession';
 161+ // The actual file payload:
 162+ $required[] = 'chunk';
 163+ break;
 164+ case ResumableUploadHandler::DONE:
 165+ $required[] = 'chunksession';
 166+ break;
 167+ }
 168+ foreach( $required as $arg ) {
 169+ if ( !isset( $params[$arg] ) ) {
 170+ $this->dieUsageMsg( array( 'missingparam', $arg ) );
 171+ }
 172+ }
 173+ }
 174+
 175+ public function getAllowedParams() {
 176+ return array(
 177+ 'filename' => null,
 178+ 'token' => null,
 179+ 'comment' => null,
 180+ 'ignorewarnings' => false,
 181+ 'chunksession' => null,
 182+ 'chunk' => null,
 183+ 'byteoffset' => null,
 184+ 'done' => false,
 185+ 'watchlist' => array(
 186+ ApiBase::PARAM_DFLT => 'preferences',
 187+ ApiBase::PARAM_TYPE => array(
 188+ 'watch',
 189+ 'unwatch',
 190+ 'preferences',
 191+ 'nochange'
 192+ ),
 193+ ),
 194+ );
 195+ }
 196+
 197+ public function getParamDescription() {
 198+ return array(
 199+ 'filename' => 'Target filename',
 200+ 'filesize' => 'The total size of the file being uploaded',
 201+ 'token' => 'Edit token. You can get one of these through prop=info',
 202+ 'comment' => 'Upload comment',
 203+ 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
 204+ 'ignorewarnings' => 'Ignore any warnings',
 205+ 'chunksession' => 'The session key, established on the first contact during the chunked upload',
 206+ 'chunk' => 'The data in this chunk of a chunked upload',
 207+ 'byteoffset' => 'The byte offset range of the uploaded chunk, relative to the complete file',
 208+ 'done' => 'Set to 1 on the last chunk of a chunked upload',
 209+
 210+ 'sessionkey' => 'Session key that identifies a previous upload that was stashed temporarily.',
 211+ 'stash' => 'If set, the server will not add the file to the repository and stash it temporarily.',
 212+ );
 213+ }
 214+
 215+ public function getDescription() {
 216+ return array(
 217+ 'Upload a file in chunks'
 218+ );
 219+ }
 220+
 221+ public function getPossibleErrors() {
 222+ return array_merge(
 223+ parent::getPossibleErrors(),
 224+ array(
 225+ array( 'missingparam' ),
 226+ array( 'chunk-init-error' ),
 227+ array( 'code' => 'chunk-init-error', 'info' => 'Insufficient information for initialization.' ),
 228+ array( 'code' => 'chunked-error', 'info' => 'There was a problem initializing the chunked upload.' ),
 229+ )
 230+ );
 231+ }
 232+
 233+ public function getExamples() {
 234+ return array(
 235+ 'api.php?action=resumableupload&filename=Wiki.png',
 236+ );
 237+ }
 238+
 239+ public function getVersion() {
 240+ return __CLASS__ . ': $Id: ApiResumableUpload.php 83770 2011-03-12 18:09:59Z reedy $';
 241+ }
 242+}
Index: trunk/extensions/ResumableUpload/ResumableUpload.i18n.php
@@ -0,0 +1,13 @@
 2+<?php
 3+/**
 4+ * Internationalisation file for extension Resumable Uploading.
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$messages = array();
 11+
 12+$messages['en'] = array(
 13+ 'resumableupload-desc' => "Resumable Uploading support",
 14+);
Index: trunk/extensions/ResumableUpload/ResumableUploadHandler.php
@@ -0,0 +1,166 @@
 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 ResumableUploadHandler 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+
 147+ if ( $this->getRealPath( $this->repoPath ) ) {
 148+ $this->status = $this->appendToUploadFile( $this->repoPath, $this->mTempPath );
 149+
 150+ if ( $this->mFileSize > $wgMaxUploadSize )
 151+ $this->status = Status::newFatal( 'largefileserver' );
 152+
 153+ } else {
 154+ $this->status = Status::newFatal( 'filenotfound', $this->repoPath );
 155+ }
 156+ return $this->status;
 157+ }
 158+
 159+ /**
 160+ * Append the final chunk and ready file for parent::performUpload()
 161+ * @return void
 162+ */
 163+ public function finalizeFile() {
 164+ $this->appendChunk();
 165+ $this->mTempPath = $this->getRealPath( $this->repoPath );
 166+ }
 167+}
\ No newline at end of file
Index: trunk/extensions/ResumableUpload/.buildpath
@@ -0,0 +1,5 @@
 2+<?xml version="1.0" encoding="UTF-8"?>
 3+<buildpath>
 4+ <buildpathentry kind="src" path=""/>
 5+ <buildpathentry kind="con" path="org.eclipse.php.core.LANGUAGE"/>
 6+</buildpath>
Index: trunk/extensions/ResumableUpload/ResumableUpload.php
@@ -0,0 +1,22 @@
 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' => 'Resumable Upload',
 12+ 'url' => 'http://www.mediawiki.org/wiki/Extension:ResumableUpload',
 13+ 'author' => array( 'Mark A. Hershberger', 'Michael Dale' ),
 14+ 'descriptionmsg' => 'resumableupload-desc',
 15+);
 16+
 17+$dir = dirname( __FILE__ ) . '/';
 18+$wgExtensionMessagesFiles['ResumableUpload'] = $dir . 'ResumableUpload.i18n.php';
 19+$wgAutoloadClasses['ApiResumableUpload'] = $dir . 'ApiResumableUpload.php';
 20+$wgAutoloadClasses['ResumableUploadHandler'] = $dir . 'ResumableUploadHandler.php';
 21+
 22+$wgAPIModules['resumableupload'] = 'ApiResumableUpload';
 23+
Index: trunk/extensions/ResumableUpload/README
@@ -0,0 +1,2 @@
 2+Adds Resumable upload support to MediaWiki upload api based on Googles resumable upload protocol:
 3+http://code.google.com/apis/gdata/docs/resumable_upload.html
\ No newline at end of file

Follow-up revisions

RevisionCommit summaryAuthorDate
r88238Removed ResumableUpload so that it can be svn renamed properly from FirefoggC...dale14:53, 16 May 2011
r88241fixed r88236 commit, renamed FirefoggChunkedUpload r79293 to ResumableUpload,...dale15:09, 16 May 2011
r88242update to ResumableUploadHandler names and api updates ( see r88236 )dale15:19, 16 May 2011

Comments

#Comment by Raymond (talk | contribs)   14:35, 16 May 2011

Please use SVN move/rename to keep the history.

Status & tagging log