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