http://www.mediawiki.org/wiki/Special:Code/MediaWiki/88236

Revision: 88236
Author:   dale
Date:     2011-05-16 14:23:42 +0000 (Mon, 16 May 2011)
Log Message:
-----------
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 ) 

Added Paths:
-----------
    trunk/extensions/ResumableUpload/
    trunk/extensions/ResumableUpload/.buildpath
    trunk/extensions/ResumableUpload/.project
    trunk/extensions/ResumableUpload/ApiResumableUpload.php
    trunk/extensions/ResumableUpload/README
    trunk/extensions/ResumableUpload/ResumableUpload.i18n.php
    trunk/extensions/ResumableUpload/ResumableUpload.php
    trunk/extensions/ResumableUpload/ResumableUploadHandler.php

Removed Paths:
-------------
    trunk/extensions/FirefoggChunkedUpload/

Added: trunk/extensions/ResumableUpload/.buildpath
===================================================================
--- trunk/extensions/ResumableUpload/.buildpath                         (rev 0)
+++ trunk/extensions/ResumableUpload/.buildpath 2011-05-16 14:23:42 UTC (rev 
88236)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<buildpath>
+       <buildpathentry kind="src" path=""/>
+       <buildpathentry kind="con" path="org.eclipse.php.core.LANGUAGE"/>
+</buildpath>

Added: trunk/extensions/ResumableUpload/.project
===================================================================
--- trunk/extensions/ResumableUpload/.project                           (rev 0)
+++ trunk/extensions/ResumableUpload/.project   2011-05-16 14:23:42 UTC (rev 
88236)
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>ResumableUpload</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       
<name>org.eclipse.wst.validation.validationbuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.dltk.core.scriptbuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.php.core.PHPNature</nature>
+               <nature>org.eclipse.wst.jsdt.core.jsNature</nature>
+       </natures>
+</projectDescription>


Property changes on: trunk/extensions/ResumableUpload/.project
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/extensions/ResumableUpload/ApiResumableUpload.php
===================================================================
--- trunk/extensions/ResumableUpload/ApiResumableUpload.php                     
        (rev 0)
+++ trunk/extensions/ResumableUpload/ApiResumableUpload.php     2011-05-16 
14:23:42 UTC (rev 88236)
@@ -0,0 +1,241 @@
+<?php
+if ( !defined( 'MEDIAWIKI' ) ) die();
+/**
+ * @copyright Copyright © 2010 Mark A. Hershberger <[email protected]>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 
2.0 or later
+ */
+
+class ApiResumableUpload extends ApiUpload {
+       protected $mUpload = null, $mParams = null;
+
+       public function execute() {
+               global $wgUser;
+
+               // Check whether upload is enabled
+               if ( !UploadBase::isEnabled() ) {
+                       $this->dieUsageMsg( array( 'uploaddisabled' ) );
+               }
+
+               $this->mParams = $this->extractRequestParams();
+
+               $this->validateParams( $this->mParams );
+
+               $request = $this->getMain()->getRequest();
+               $this->mUpload = new ResumableUploadHandler;
+
+               $status = $this->mUpload->initialize(
+                       $request->getVal( 'done', null ),
+                       $request->getVal( 'filename', null ),
+                       $request->getVal( 'chunksession', null ),
+                       $request->getFileTempName( 'chunk' ),
+                       $request->getFileSize( 'chunk' ),
+                       $request->getSessionData( 
UploadBase::getSessionKeyname() )
+               );
+
+               if ( $status !== true ) {
+                       $this->dieUsage(  $status, 'chunk-init-error' );
+               }
+
+               $ret = $this->performUpload( );
+
+               if(is_array($ret)) {
+                       foreach($ret as $key => $val) {
+                               $this->getResult()->addValue(null, $key, $val);
+                       }
+               } else {
+                       $this->dieUsage($ret, 'error');
+               }
+       }
+
+       public function getUpload() { 
+               return $this->mUpload; 
+       }
+
+       public function performUploadInit($comment, $pageText, $watchlist, 
$user) {
+               // Verify the initial upload request
+               $this->verifyUploadInit();
+               
+               $session = $this->mUpload->setupChunkSession( $comment, 
$pageText, $watchlist );
+               return array('uploadUrl' =>
+                       wfExpandUrl( wfScript( 'api' ) ) . "?" .
+                       wfArrayToCGI( array(
+                               'action' => 'resumableupload',
+                               'token' => $user->editToken(),
+                               'format' => 'json',
+                               'chunksession' => $session,
+                               'filename' => $this->mUpload->getDesiredName(),
+                       ) ) );
+       }
+       
+       /**
+        * Check the upload 
+        */
+       public function verifyUploadInit(){
+
+               // Check for valid name: 
+               $check = $this->mUpload->validateName();
+               if( $check !== true ) {
+                       return $this->getVerificationError( $check );
+               }
+               
+               // Check proposed file size
+               $maxSize = $this->getMaxUploadSize( '*' );
+               if( $this->mFileSize > $maxSize ) {
+                       // We have to return an array here instead of 
getVerificationError so that we can include
+                       // the max size info. 
+                       return array(
+                               'status' => self::FILE_TOO_LARGE,
+                               'max' => $maxSize,
+                       );
+               }
+               
+               return true;
+       }
+
+       public function performUploadChunk() {
+               $this->mUpload->setupChunkSession();
+               $status = $this->mUpload->appendChunk();
+               if ( !$status->isOK() ) {
+                       $this->dieUsage($status->getWikiText(), 'error');
+               }
+               return array( 'result' => 1, 'filesize' => 
$this->mUpload->getFileSize() );
+       }
+
+       public function performUploadDone( $user ) {
+               $this->mUpload->finalizeFile();
+               $status = parent::performUpload( $this->comment, 
$this->pageText, $this->watchlist, $user );
+
+               if ( $status['result'] !== 'Success' ) {
+                       return $status;
+               }
+               $file = $this->mUpload->getLocalFile();
+               return array('result' => 1, 'done' => 1, 'resultUrl' =>  
wfExpandUrl( $file->getDescriptionUrl() ) );
+       }
+
+       /**
+        * Handle a chunk of the upload.  
+        * @see UploadBase::performUpload
+        */
+       public function performUpload( ) {
+               wfDebug( "\n\n\performUpload(chunked): comment: " . 
$this->comment .
+                                ' pageText: ' . $this->pageText . ' watch: ' . 
$this->watchlist );
+               $ret = "unknown error";
+
+               global $wgUser;
+               switch( $this->mUpload->getChunkMode() ){
+                       case ResumableUploadHandler::INIT:
+                               return $this->performUploadInit($this->comment, 
$this->pageText, $this->watchlist, $wgUser);
+                               break;
+                       case  ResumableUploadHandler::CHUNK:
+                               return $this->performUploadChunk();
+                               break;
+                       case ResumableUploadHandler::DONE:
+                               return $this->performUploadDone($wgUser);;
+                               break;
+               }
+               return $ret;
+       }
+
+       public function mustBePosted() {
+               return true;
+       }
+
+       public function isWriteMode() {
+               return true;
+       }
+
+       protected function validateParams( $params ) {
+               $required = array();
+               // Check required params for each upload mode:
+               switch( $this->mUpload->getChunkMode() ){
+                       case ResumableUploadHandler::INIT:
+                               $required[] = 'filename';
+                               $required[] = 'comment';
+                               $required[] = 'token';
+                               $required[] = 'filesize';
+                               break;
+                       case  ResumableUploadHandler::CHUNK:
+                               $required[] = 'byteoffset';
+                               $required[] = 'chunksession';
+                               // The actual file payload:
+                               $required[] = 'chunk';
+                               break;
+                       case ResumableUploadHandler::DONE:
+                               $required[] = 'chunksession';
+                               break;
+               }
+               foreach( $required as $arg ) {
+                       if ( !isset( $params[$arg] ) ) {
+                               $this->dieUsageMsg( array( 'missingparam', $arg 
) );
+                       }
+               }
+       }
+
+       public function getAllowedParams() {
+               return array(
+                       'filename' => null,
+                       'token' => null,
+                       'comment' => null,
+                       'ignorewarnings' => false,
+                       'chunksession' => null,
+                       'chunk' => null,
+                       'byteoffset' => null,
+                       'done' => false,
+                       'watchlist' => array(
+                               ApiBase::PARAM_DFLT => 'preferences',
+                               ApiBase::PARAM_TYPE => array(
+                                       'watch',
+                                       'unwatch',
+                                       'preferences',
+                                       'nochange'
+                               ),
+                       ),
+               );
+       }
+
+       public function getParamDescription() {         
+               return array(
+                       'filename' => 'Target filename',
+                       'filesize' => 'The total size of the file being 
uploaded',
+                       'token' => 'Edit token. You can get one of these 
through prop=info',
+                       'comment' => 'Upload comment',
+                       'watchlist' => 'Unconditionally add or remove the page 
from your watchlist, use preferences or do not change watch',
+                       'ignorewarnings' => 'Ignore any warnings',
+                       'chunksession' => 'The session key, established on the 
first contact during the chunked upload',
+                       'chunk' => 'The data in this chunk of a chunked upload',
+                       'byteoffset' => 'The byte offset range of the uploaded 
chunk, relative to the complete file',
+                       'done' => 'Set to 1 on the last chunk of a chunked 
upload',
+               
+                       'sessionkey' => 'Session key that identifies a previous 
upload that was stashed temporarily.',
+                       'stash' => 'If set, the server will not add the file to 
the repository and stash it temporarily.',
+               );
+       }
+
+       public function getDescription() {
+               return array(
+                       'Upload a file in chunks'
+               );
+       }
+
+       public function getPossibleErrors() {
+               return array_merge(
+                       parent::getPossibleErrors(),
+                       array(
+                               array( 'missingparam' ),
+                               array( 'chunk-init-error' ),
+                               array( 'code' => 'chunk-init-error', 'info' => 
'Insufficient information for initialization.' ),
+                               array( 'code' => 'chunked-error', 'info' => 
'There was a problem initializing the chunked upload.' ),
+                       )
+               );
+       }
+
+       public function getExamples() {
+               return array(
+                       'api.php?action=resumableupload&filename=Wiki.png',
+               );
+       }
+
+       public function getVersion() {
+               return __CLASS__ . ': $Id: ApiResumableUpload.php 83770 
2011-03-12 18:09:59Z reedy $';
+       }
+}

Added: trunk/extensions/ResumableUpload/README
===================================================================
--- trunk/extensions/ResumableUpload/README                             (rev 0)
+++ trunk/extensions/ResumableUpload/README     2011-05-16 14:23:42 UTC (rev 
88236)
@@ -0,0 +1,2 @@
+Adds Resumable upload support to MediaWiki upload api based on Googles 
resumable upload protocol:
+http://code.google.com/apis/gdata/docs/resumable_upload.html
\ No newline at end of file

Added: trunk/extensions/ResumableUpload/ResumableUpload.i18n.php
===================================================================
--- trunk/extensions/ResumableUpload/ResumableUpload.i18n.php                   
        (rev 0)
+++ trunk/extensions/ResumableUpload/ResumableUpload.i18n.php   2011-05-16 
14:23:42 UTC (rev 88236)
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Internationalisation file for extension Resumable Uploading.
+ *
+ * @file
+ * @ingroup Extensions
+ */
+
+$messages = array();
+
+$messages['en'] = array(
+       'resumableupload-desc' => "Resumable Uploading support",
+);

Added: trunk/extensions/ResumableUpload/ResumableUpload.php
===================================================================
--- trunk/extensions/ResumableUpload/ResumableUpload.php                        
        (rev 0)
+++ trunk/extensions/ResumableUpload/ResumableUpload.php        2011-05-16 
14:23:42 UTC (rev 88236)
@@ -0,0 +1,22 @@
+<?php
+if ( !defined( 'MEDIAWIKI' ) ) die();
+/**
+ * @copyright Copyright © 2010 Mark A. Hershberger <[email protected]>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 
2.0 or later
+ */
+
+$wgExtensionCredits['other'][] = array(
+       'path' => __FILE__,
+       'name' => 'Resumable Upload',
+       'url' => 'http://www.mediawiki.org/wiki/Extension:ResumableUpload',
+       'author' => array( 'Mark A. Hershberger', 'Michael Dale' ),
+       'descriptionmsg' => 'resumableupload-desc',
+);
+
+$dir = dirname( __FILE__ ) . '/';
+$wgExtensionMessagesFiles['ResumableUpload'] = $dir . 
'ResumableUpload.i18n.php';
+$wgAutoloadClasses['ApiResumableUpload'] = $dir . 'ApiResumableUpload.php';
+$wgAutoloadClasses['ResumableUploadHandler'] = $dir . 
'ResumableUploadHandler.php';
+
+$wgAPIModules['resumableupload'] = 'ApiResumableUpload';
+

Added: trunk/extensions/ResumableUpload/ResumableUploadHandler.php
===================================================================
--- trunk/extensions/ResumableUpload/ResumableUploadHandler.php                 
        (rev 0)
+++ trunk/extensions/ResumableUpload/ResumableUploadHandler.php 2011-05-16 
14:23:42 UTC (rev 88236)
@@ -0,0 +1,166 @@
+<?php
+if ( !defined( 'MEDIAWIKI' ) ) die();
+/**
+ * @copyright Copyright © 2010 Mark A. Hershberger <[email protected]>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 
2.0 or later
+ */
+
+class ResumableUploadHandler extends UploadBase {
+       const INIT = 1;
+       const CHUNK = 2;
+       const DONE = 3;
+
+       protected $chunkMode; // INIT, CHUNK, DONE
+       protected $sessionKey;
+       protected $comment;
+       protected $repoPath;
+       protected $pageText;
+       protected $watchlist;
+
+       public $status;
+ 
+       public function initializeFromRequest(&$request) {}
+       public function getChunkMode() {return $this->chunkMode;}
+       public function getDesiredName() {return $this->mDesiredDestName;}
+
+       /**
+        * Set session information for chunked uploads and allocate a unique 
key.
+        * @param $comment string
+        * @param $pageText string
+        * @param $watchlist bodolean
+        *
+        * @returns string the session key for this chunked upload
+        */
+       public function setupChunkSession( $comment, $pageText, $watchlist ) {
+               if ( !isset( $this->sessionKey ) ) {
+                       $this->sessionKey = $this->getSessionKey();
+               }
+               foreach ( array( 'mFilteredName', 'repoPath', 'mFileSize', 
'mDesiredDestName' )
+                               as $key ) {
+                       if ( isset( $this->$key ) ) {
+                               
$_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey][$key] = $this->$key;
+                       }
+               }
+               if ( isset( $comment ) ) {
+                       
$_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['commment'] = 
$comment;
+               }
+               if ( isset( $pageText ) ) {
+                       
$_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['pageText'] = 
$pageText;
+               }
+               if ( isset( $watchlist ) ) {
+                       
$_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['watchlist'] = 
$watchlist;
+               }
+               
$_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['version'] = 
UploadBase::SESSION_VERSION;
+
+               return $this->sessionKey;
+       }
+
+       /**
+        * Initialize a request
+        * @param $done boolean Set if this is the last chunk
+        * @param $filename string The desired filename, set only on first 
request.
+        * @param $sessionKey string The chunksession parameter
+        * @param $path string The path to the temp file containing this chunk
+        * @param $chunkSize integer The size of this chunk
+        * @param $sessionData array sessiondata
+        *
+        * @return mixed True if there was no error, otherwise an error 
description suitable for passing to dieUsage()
+        */
+       public function initialize( $done, $filename, $sessionKey, $path, 
$chunkSize, $sessionData ) {
+               if( $filename ) $this->mDesiredDestName = $filename;
+               $this->mTempPath = $path;
+
+               if ( $sessionKey !== null ) {
+                       $status = $this->initFromSessionKey( $sessionKey, 
$sessionData, $chunkSize );
+                       if( $status !== true ) {
+                               return $status;
+                       }
+
+                       if ( $done ) {
+                               $this->chunkMode = self::DONE;
+                       } else {
+                               $this->mTempPath = $path;
+                               $this->chunkMode = self::CHUNK;
+                       }
+               } else {
+                       // session key not set, init the chunk upload system:
+                       $this->chunkMode = self::INIT;
+               }
+
+               if ( $this->mDesiredDestName === null ) {
+                       return 'Insufficient information for initialization.';
+               }
+
+               return true;
+       }
+
+       /**
+        * Initialize a continuation of a chunked upload from a session key
+        * @param $sessionKey string
+        * @param $request WebRequest
+        * @param $fileSize int Size of this chunk
+        *
+        * @returns void
+        */
+       protected function initFromSessionKey( $sessionKey, $sessionData, 
$fileSize ) {
+               // testing against null because we don't want to cause obscure
+               // bugs when $sessionKey is full of "0"
+               $this->sessionKey = $sessionKey;
+
+               if ( isset( $sessionData[$this->sessionKey]['version'] )
+                       && $sessionData[$this->sessionKey]['version'] == 
UploadBase::SESSION_VERSION )
+               {
+                       foreach ( array( 'comment', 'pageText', 'watchlist', 
'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
+                                       as $key ) {
+                               if ( isset( 
$sessionData[$this->sessionKey][$key] ) ) {
+                                       $this->$key = 
$sessionData[$this->sessionKey][$key];
+                               }
+                       }
+
+                       $this->mFileSize += $fileSize;
+               } else {
+                       return 'Not a valid session key';
+               }
+
+               return true;
+       }
+
+       /**
+        * Append a chunk to the temporary file.
+        *
+        * @return void
+        */
+       public function appendChunk() {
+               global $wgMaxUploadSize;
+
+               if ( !$this->repoPath ) {
+                       $this->status = $this->saveTempUploadedFile( 
$this->mDesiredDestName, $this->mTempPath );
+
+                       if ( $this->status->isOK() ) {
+                               $this->repoPath = $this->status->value;
+                               
$_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['repoPath'] = 
$this->repoPath;
+                       }
+                       return $this->status;
+               }
+               
+               if ( $this->getRealPath( $this->repoPath ) ) {
+                       $this->status = $this->appendToUploadFile( 
$this->repoPath, $this->mTempPath );
+
+                       if ( $this->mFileSize > $wgMaxUploadSize )
+                               $this->status = Status::newFatal( 
'largefileserver' );
+
+               } else {
+                       $this->status = Status::newFatal( 'filenotfound', 
$this->repoPath );
+               }
+               return $this->status;
+       }
+
+       /**
+        * Append the final chunk and ready file for parent::performUpload()
+        * @return void
+        */
+       public function finalizeFile() {
+               $this->appendChunk();
+               $this->mTempPath = $this->getRealPath( $this->repoPath );
+       }
+}
\ No newline at end of file


_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to