Index: t3lib/class.t3lib_div.php =================================================================== --- t3lib/class.t3lib_div.php (revision 5312) +++ t3lib/class.t3lib_div.php (working copy) @@ -3799,6 +3799,8 @@ $bInfo['BROWSER']= 'msie'; } elseif (strpos($useragent, 'Mozilla') !== false) { $bInfo['BROWSER']='net'; + } elseif (strpos($useragent, 'Flash') !== false) { + $bInfo['BROWSER'] = 'flash'; } if ($bInfo['BROWSER']) { // Browser version Index: t3lib/class.t3lib_userauth.php =================================================================== --- t3lib/class.t3lib_userauth.php (revision 5312) +++ t3lib/class.t3lib_userauth.php (working copy) @@ -216,6 +216,10 @@ $this->hash_length = t3lib_div::intInRange($this->hash_length,6,32); $this->svConfig = $TYPO3_CONF_VARS['SVCONF']['auth']; + // if we have a flash client, take the ID from the GP + if (!$id && $GLOBALS['CLIENT']['BROWSER'] == 'flash') { + $id = t3lib_div::_GP($this->name); + } // If fallback to get mode.... if (!$id && $this->getFallBack && $this->get_name) { @@ -845,12 +849,19 @@ /** * This returns the where-clause needed to lock a user to a hash integer - * + * if the client is flash (e.g. from a flash application inside TYPO3 that does a server request) + * then don't evaluate with the hashLockClause, as the client /browser is included in this hash + * and thus, the flash request would be rejected + * * @return string * @access private */ function hashLockClause() { - $wherePart = 'AND '.$this->session_table.'.ses_hashlock='.intval($this->hashLockClause_getHashInt()); + if ($GLOBALS['CLIENT']['BROWSER'] == 'flash') { + $wherePart = ''; + } else { + $wherePart = 'AND ' . $this->session_table . '.ses_hashlock = ' . intval($this->hashLockClause_getHashInt()); + } return $wherePart; } Index: typo3/file_list.php =================================================================== --- typo3/file_list.php (revision 5312) +++ typo3/file_list.php (working copy) @@ -54,6 +54,7 @@ require ('init.php'); require ('template.php'); $LANG->includeLLFile('EXT:lang/locallang_mod_file_list.xml'); +$LANG->includeLLFile('EXT:lang/locallang_misc.xml'); require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php'); require_once (PATH_t3lib.'class.t3lib_extfilefunc.php'); require_once (PATH_t3lib.'class.t3lib_recordlist.php'); @@ -168,6 +169,7 @@ $this->doc = t3lib_div::makeInstance('template'); $this->doc->backPath = $BACK_PATH; $this->doc->setModuleTemplate('templates/file_list.html'); + $this->doc->loadJavascriptLib('contrib/prototype/prototype.js'); // Validating the input "id" (the path, directory!) and checking it against the mounts of the user. $this->id = $this->basicFF->is_directory($this->id); @@ -175,7 +177,70 @@ // There there was access to this file path, continue, make the list if ($access) { + // include the initialization for the flash uploader + if ($GLOBALS['BE_USER']->uc['enableFlashUploader']) { + $uploadURL = t3lib_div::dirname(t3lib_div::getIndpEnv('SCRIPT_NAME')) . '/ajax.php'; + $uploadFileSizeLimit = $GLOBALS['TYPO3_CONF_VARS']['BE']['maxFileSize']; + $uploadFileTypes = t3lib_div::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']); + if (count($uploadFileTypes)) { + $uploadFileTypes = implode(';*.', $uploadFileTypes); + $uploadFileTypes = '*.' . $uploadFileTypes; + } else { + $uploadFileTypes = '*.*'; + } + + $this->doc->JScodeArray['flashUploader'] = ' + document.observe("dom:loaded", function() { + + // monitor the button + $("button-upload").observe("click", initFlashUploader); + + function initFlashUploader(event) { + // set the page specific options for the flashUploader + var flashUploadOptions = { + uploadURL: "' . $uploadURL . '", + uploadFileSizeLimit: "' . $uploadFileSizeLimit . '", + uploadFileTypes: "' . $allowedFileExtensions . '", + uploadFilePostName: "upload_1", + uploadPostParams: { + "file[upload][1][target]": "' . $this->id . '", + "file[upload][1][data]": 1, + "file[upload][1][charset]": "utf-8", + "ajaxID": "TYPO3_tcefile::process" + } + }; + + // get the flashUploaderWindow instance from the parent frame + var flashUploader = top.TYPO3.FileUploadWindow.getInstance(flashUploadOptions); + // add an additional function inside the container to show the checkbox option + var infoComponent = new top.Ext.Panel({ + autoEl: { tag: "div"}, + height: "auto", + id: "t3-upload-window-infopanel-addition", + html: \'\', + style: "text-align: center; line-height: 24px;" + }); + flashUploader.add(infoComponent); + + // do a reload of this frame once all uploads are done + flashUploader.on("totalcomplete", function() { + window.location.reload(); + }); + + // this is the callback function that delivers the additional post parameter to the flash application + top.setFlashPostOptionOverwriteExistingFiles = function(checkbox) { + var uploader = top.TYPO3.getInstance("FileUploadWindow"); + if (uploader.isVisible()) { + uploader.swf.addPostParam("overwriteExistingFiles", (checkbox.checked == true ? 1 : 0)); + } + }; + + event.stop(); + }; + }); + '; + } // Create filelisting object $this->filelist = t3lib_div::makeInstance('fileList'); $this->filelist->backPath = $BACK_PATH; @@ -347,7 +412,7 @@ // upload button $theIcon = 'backPath,'gfx/upload.gif','width="18" height="16"').' title="'.$GLOBALS['LANG']->makeEntities($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.upload',1)).'" alt="" />'; - $buttons['upload'] = ''.$theIcon.''; + $buttons['upload'] = ''.$theIcon.''; $theIcon = 'backPath,'gfx/new_file.gif','width="18" height="16"').' title="'.$GLOBALS['LANG']->makeEntities($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.new',1)).'" alt="" />'; $buttons['new'] = ''.$theIcon.''; Index: typo3/js/common.js =================================================================== --- typo3/js/common.js (revision 5312) +++ typo3/js/common.js (working copy) @@ -75,3 +75,16 @@ } } } + +// common storage and global object, could later hold more information about the current user etc. +var TYPO3 = { + // store instances that only should be running once + _instances: {}, + getInstance: function(className) { + return TYPO3._instances[className] || false; + }, + addInstance: function(className, instance) { + TYPO3._instances[className] = instance; + return instance; + } +}; Index: typo3/js/flashupload.js =================================================================== --- typo3/js/flashupload.js (revision 0) +++ typo3/js/flashupload.js (revision 0) @@ -0,0 +1,550 @@ +/** + * Javascript functions regarding the inclusion + * of a TYPO3-customized version of SWFupload + * + * For proper use see file_list.php or the doc_core_api manual + * + * (c) 2009 Benjamin Mack + * All rights reserved + * + * This script is part of TYPO3 by + * Kasper Skaarhoj + * + * Released under GNU/GPL (see license file in tslib/) + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This copyright notice MUST APPEAR in all copies of this script + */ +Ext.onReady(function() { + + /** + * This class is a derivative of Ext.Window, with some special + * constructor to set some default values and a method to make + * sure that it's only instantiated once (see TYPO3.FileUploadWindow.getInstance()) + * + * It allows an info area (can be overriden if set with the same ID) + * and additional multiple components (possible to add via the add() method of Ext.Container) + * + * Additionally, there are several events fired in the class that are specific for the upload handling + * that can be used in all use-cases + */ + TYPO3.FileUploadWindow = Ext.extend(Ext.Window, { + completedUploads: 0, // number of successfully completed uploads in this current instance, could be useful for some applications + activeUploads: {}, // holds all TYPO3.FileUpload instances currently uploading or in queue + swf: null, // holds the SWFUpload instance + swfDefaultConfig: { // includes all default options the SWFUpload needs + fileUploadWindow: null, // internal reference to the FileUploadWindow object for calling the event handlers + + flash_url: TS.PATH_typo3 + "contrib/swfupload/swfupload.swf", // url to the swfupload flash file, should be absolute + file_size_limit: '20.480', // you can either set a number which is in KB or set a unit (like 2 B, KB, MB, GB) + file_queue_limit: 0, // maximum files that are queued + file_upload_limit: 0, // maximum files that can be uploaded with one instance + upload_url: TS.PATH_typo3 + "ajax.php", // the destination URL that handles the upload + file_post_name: "upload_1", // Name of the $_FILES key available in PHP (e.g. $_FILES['upload_1']) + file_types: "*.*", // separate multiple file types with a semicolon. e.g. ".jpg;.jpeg" + file_types_description: "All Files", + assume_success_timeout: 5, // The number of seconds SWFUpload should wait for Flash to detect the server's response after the file has finished uploading. This setting allows you to work around the Flash Player bugs where long running server side scripts causes Flash to ignore the server response or the Mac Flash Player bug that ignores server responses with no content. + post_params: { // additional parameters later available via $_POST + ajaxID: 'typo3_tcefile::process' + }, + + // SWFupload options that should not be changed, as our FileUploadWindow object is handling all of this + button_placeholder_id: "t3-file-upload-window-button-selectfiles-placeholder", + button_window_mode: SWFUpload.WINDOW_MODE.TRANSPARENT, + button_action: SWFUpload.BUTTON_ACTION.SELECT_FILES, + button_disabled: false, + button_width: 80, + button_height: 20, + button_cursor: SWFUpload.CURSOR.HAND, + // internal, SWFupload event handlers, please use the ones found in the "fileUploadWindow" + file_dialog_complete_handler: function(numFilesSelected, numFilesQueued, numFilesInQueue) { + this.fileUploadWindow.uploadSelectFiles(numFilesSelected, numFilesQueued, numFilesInQueue); + }, + file_queued_handler: function(fileObj) { + this.fileUploadWindow.uploadQueued(fileObj); + }, + file_queue_error_handler: function(fileObj, errorCode, message) { + this.fileUploadWindow.uploadQueuedError(fileObj, errorCode, message); + }, + upload_start_handler: function(fileObj) { + this.fileUploadWindow.uploadStart(fileObj); + }, + upload_progress_handler: function(fileObj, bytesComplete, bytesTotal) { + this.fileUploadWindow.uploadProgress(fileObj, bytesComplete, bytesTotal); + }, + upload_error_handler: function(fileObj, errorCode, message) { + this.fileUploadWindow.uploadError(fileObj, errorCode, message); + }, + upload_success_handler: function(fileObj, serverData, responseReceived) { + this.fileUploadWindow.uploadSuccess(fileObj, serverData, responseReceived); + }, + upload_complete_handler: function(fileObj) { + this.fileUploadWindow.uploadComplete(fileObj); + }, + // callback introduced by the swf queue plugin + queue_complete_handler: function() { + this.fileUploadWindow.totalComplete(); + } + }, + + // component constructor + // private + initComponent: function() { + + var initialConfig = { + layout: 'anchor', + width: 350, + height: 'auto', + center: true, + modal: true, + tools: [], + id: 't3-upload-window', + closeAction: 'hide', + title: String.format(TYPO3.LLL.fileUpload.windowTitle), + shadow: true, + buttons: [{ + id: 't3-file-upload-window-button-selectfiles', + text: String.format(TYPO3.LLL.fileUpload.buttonSelectFiles) + }, { + id: 't3-file-upload-window-button-cancel', + text: String.format(TYPO3.LLL.fileUpload.buttonCancelUpload), + handler: function() { + if (this.swf) { + this.swf.cancelUpload(); + } + }, + scope: this + }, { + id: 't3-file-upload-window-button-close', + text: String.format(TYPO3.LLL.fileUpload.buttonCloseWindow), + handler: function() { + this.cleanup(); + this.hide(); + }, + scope: this + } + ] + }; + // set the default options, if not set yet by the application from outside + Ext.applyIf(this, initialConfig); + + // set default options that cannot be overriden from outside + var staticConfig = { + closable: false, + buttonAlign: 'center' + }; + Ext.apply(this, staticConfig); + + TYPO3.FileUploadWindow.superclass.initComponent.call(this); + this.addEvents( + /** + * @event uploadSelectFiles + * Fires after the "select files" dialog has been closed + * @param {TYPO3.FileUploadWindow} this + * @param {Number} numFilesSelected Total number of files selected + * @param {Number} numFilesQueued Total number of files that are queued + * @param {Number} numFilesInQueue Number of files in the queue right now + */ + 'uploadSelectFiles', + /** + * @event uploadQueued + * Fires after one file has been put in the queue. + * @param {TYPO3.FileUploadWindow} this + * @param {TYPO3.FileUpload} fileObj the instance of the file upload + */ + 'uploadQueued', + /** + * @event uploadQueuedError + * Fires after one file was tried to put in the queue but failed because it did not fit all requirements + * @param {TYPO3.FileUploadWindow} this + * @param {TYPO3.FileUpload} fileObj the instance of the file upload + * @param {Number} errorCode the internal SWFupload error code, see swfupload.js for details + * @param {String} message the default (english) message that comes with the error code + */ + 'uploadQueuedError', + /** + * @event uploadStart + * Fires right after the upload of a file was started + * @param {TYPO3.FileUploadWindow} this + * @param {TYPO3.FileUpload} fileObj the instance of the file upload + */ + 'uploadStart', + /** + * @event uploadProgress + * Fires multiple times during the upload process of one file, used to give a feedback to the user + * @param {TYPO3.FileUploadWindow} this + * @param {TYPO3.FileUpload} fileObj the instance of the file upload + */ + 'uploadProgress', + /** + * @event uploadError + * Fires if an error happens while uploading, if the upload was canceled or stopped + * @param {TYPO3.FileUploadWindow} this + * @param {TYPO3.FileUpload} fileObj the instance of the file upload + * @param {Number} errorCode the internal SWFupload error code, see swfupload.js for details + * @param {String} message the default (english) message that comes with the error code + */ + 'uploadError', + /** + * @event uploadProgress + * Fires after a file was successfully uploaded + * @param {TYPO3.FileUploadWindow} this + * @param {TYPO3.FileUpload} fileObj the instance of the file upload + * @param {String} serverData the data that gets returned from the server (the HTTP response) + * @param {String} responseReceived Due to some bugs in the Flash Player the server response may not be acknowledged and no uploadSuccess event is fired by Flash. In this case the assume_success_timeout setting is checked to see if enough time has passed to fire uploadSuccess anyway. In this case the received response parameter will be false. + */ + 'uploadSuccess', + /** + * @event uploadComplete + * Fires after the upload is complete and SWFUpload is ready to start the next file + * @param {TYPO3.FileUploadWindow} this + * @param {TYPO3.FileUpload} fileObj the instance of the file upload + */ + 'uploadComplete', + /** + * @event uploadComplete + * Fires after all files in the queue have been uploaded + * @param {TYPO3.FileUploadWindow} this + */ + 'totalComplete' + ); + + this.setupWindow(); + }, + + + /** + * private, called once the window is rendered() + * instantiates the SWFUpload instance, which in turn replaces a placeholder ID (needed for Flash 10) + * with the flash movie + */ + setupFlash: function() { + this.completedUploads = 0; // reset the completed uploads + var swfConfig = this.getFlashConfig(); + + // add a placeholder div next to the button itself, so it can be detected and replaced by the flash plugin + if (!Ext.fly(swfConfig.button_placeholder_id)) { + var button = Ext.DomQuery.selectNode('#t3-file-upload-window-button-selectfiles button'); + Ext.DomHelper.insertBefore(button, '
'); + } + this.swf = new SWFUpload(swfConfig); + this.swf.fileUploadWindow = this; + + // add some info to the dialog + // you can replace this + var infoComponent = this.getComponent('t3-upload-window-infopanel'); + if (!infoComponent) { + var txt = '
'; + var maxFileSize = (swfConfig.file_size_limit.toString().indexOf('B') > 0 ? swfConfig.file_size_limit : swfConfig.file_size_limit + ' KB'); + txt += String.format(TYPO3.LLL.fileUpload.infoComponentMaxFileSize, maxFileSize) + '
'; + txt += (swfConfig.file_upload_limit ? String.format(TYPO3.LLL.fileUpload.infoComponentFileUploadLimit, swfConfig.file_upload_limit) + '
' : ''); + txt += (swfConfig.file_types != '*.*' ? String.format(TYPO3.LLL.fileUpload.infoComponentFileTypeLimit, swfConfig.file_types) + '
' : ''); + txt += '
'; + infoComponent = new Ext.Panel({ + autoEl: { tag: 'div'}, + height: 'auto', + id: 't3-upload-window-infopanel', + html: txt, + style: 'text-align: center; line-height: 24px;' + }); + this.insert(0, infoComponent); + } + }, + + /** + * the configuration variables that can be set from outside (via uploadUrl etc) are set directly in the FileUploadWindow instance + * here we merge these variables from the outside with the default configuration set in the swfDefaultConfig + * and return a new config object that SWFupload can use and manipulate + * We decided to use camelCase parameters and parameters prepended with "upload" so it is + * better to understand than the existing SWFupload parameters + */ + getFlashConfig: function() { + var swfConfig = {}; + Ext.apply(swfConfig, this.swfDefaultConfig); + swfConfig.upload_url = Ext.value(this.uploadURL, this.swfDefaultConfig.upload_url); + swfConfig.file_size_limit = Ext.value(this.uploadFileSizeLimit, this.swfDefaultConfig.file_size_limit); + swfConfig.file_queue_limit = Ext.value(this.uploadMaxFilesQueued, this.swfDefaultConfig.file_queue_limit); + swfConfig.file_upload_limit = Ext.value(this.uploadMaxFiles, this.swfDefaultConfig.file_upload_limit); + swfConfig.file_post_name = Ext.value(this.uploadFileParam, this.swfDefaultConfig.file_post_name); + swfConfig.file_types = Ext.value(this.uploadFileTypes, this.swfDefaultConfig.file_types); + swfConfig.file_types_description = Ext.value(this.uploadFileTypesDescription, this.swfDefaultConfig.file_types_description); + swfConfig.post_params = Ext.value(this.uploadPostParams, this.swfDefaultConfig.post_params); + return swfConfig; + }, + + /** + * sets up the visual information before the window is rendered, it adds some default event handlers + * (they have to be re-set all the time, because all of them are purged in the cleanup function) + */ + setupWindow: function() { + this.on('add', function() { this.doLayout(); }.bind(this)); + this.on('remove', function() { this.doLayout(); }.bind(this)); + this.on('hide', function() { this.cleanup(); }.bind(this)); + this.on('show', function() { + this.setupFlash(); + this.doLayout(); + }.bind(this)); + + // show the window, and disable the cancel button (only gets enabled when files are selected) + this.show(); + this.buttons[1].disable(); + }, + + /** + * is used when the window is closed, then the flash movie is removed and the existing listeners + * are removed, all components are removed and the window is then hidden + */ + cleanup: function() { + this.buttons[1].disable(); + if (this.swf) { + this.swf.cancelUpload(); + this.swf.destroy(); + } + this.swf = null; + this.purgeListeners(); + this.removeAll(true); + }, + + // the following functions are event proxies that take the SWF-style events to the extJS handling + // they also provide basic functionality for the file upload process + // private + uploadSelectFiles: function(numFilesSelected, numFilesQueued, numFilesInQueue) { + if (numFilesSelected > 0) { + this.buttons[1].enable(); + } + this.swf.startUpload(); + this.fireEvent('uploadSelectFiles', this, [numFilesSelected, numFilesQueued, numFilesInQueue]); + }, + + // private + uploadQueued: function(fileObj) { + this.activeUploads[fileObj.id] = new TYPO3.FileUpload({file: fileObj}); + this.fireEvent('uploadQueued', this, [this.activeUploads[fileObj.id]]); + }, + + // private + uploadQueuedError: function(fileObj, errorCode, message) { + if (!this.activeUploads[fileObj.id]) { + this.activeUploads[fileObj.id] = new TYPO3.FileUpload({file: fileObj}); + } + this.activeUploads[fileObj.id].queueError(errorCode, message); + this.fireEvent('uploadQueuedError', this, [this.activeUploads[fileObj.id], errorCode, message]); + delete this.activeUploads[fileObj.id]; + }, + + // private + uploadStart: function(fileObj) { + this.activeUploads[fileObj.id].start(); + this.fireEvent('uploadStart', this, [this.activeUploads[fileObj.id]]); + }, + + // private + uploadProgress: function(fileObj, bytesComplete, bytesTotal) { + this.activeUploads[fileObj.id].update(bytesComplete, bytesTotal); + this.fireEvent('uploadProgress', this, [this.activeUploads[fileObj.id], bytesComplete, bytesTotal]); + }, + + // private + uploadError: function(fileObj, errorCode, message) { + this.activeUploads[fileObj.id].error(errorCode, message); + this.fireEvent('uploadError', this, [this.activeUploads[fileObj.id], errorCode, message]); + delete this.activeUploads[fileObj.id]; + }, + + // private + uploadSuccess: function(fileObj, serverData, responseReceived) { + this.completedUploads++; + this.activeUploads[fileObj.id].success(); + this.fireEvent('uploadSuccess', this, [this.activeUploads[fileObj.id], serverData, responseReceived]); + }, + + // private + uploadComplete: function(fileObj) { + this.fireEvent('uploadComplete', this, [this.activeUploads[fileObj.id]]); + delete this.activeUploads[fileObj.id]; + }, + + // private + totalComplete: function() { + if (this.completedUploads > 0) { + this.fireEvent('totalComplete', this); + } + this.cleanup(); + this.hide(); + }, + + /** + * Removes all components from this container. + * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. + * Defaults to the value of this Container's {@link #autoDestroy} config. + * @return {Array} Array of the destroyed components + * + * This function is copied from the Container object of extJS, because it was documented but not correctly implement + */ + removeAll: function(autoDestroy) { + var item, items = []; + while((item = this.items.last())) { + items.unshift(this.remove(item, autoDestroy)); + } + return items; + } + }); + + // This function should be used to fetch a FileUploadWindow instance, to make sure + // that only one instance is currently running + TYPO3.FileUploadWindow.getInstance = function(config) { + var instance = TYPO3.getInstance('FileUploadWindow'); + if (instance) { + if (!instance.swf) { + Ext.apply(instance, config); + instance.setupWindow(); + instance.show(); + } else { + // TODO, maybe we can raise an exception + alert(String.format(TYPO3.LLL.fileUpload.processRunning)); + } + } else { + instance = new TYPO3.FileUploadWindow(config); + TYPO3.addInstance('FileUploadWindow', instance); + } + return instance; + } + + + + /** + * This class includes one instance of an upload + * and is used mainly to display the progress bar Component + */ + TYPO3.FileUpload = Ext.extend(Ext.Component, { + progressBar: null, // Ext.Component progress bar instance + parent: null, // reference to the TYPO3.FileUploadWindow + file: null, // reference to the SWFupload.File object + + // creates the Ext component, used when a file is queued + initComponent: function() { + TYPO3.FileUpload.superclass.initComponent.call(this); + this.parent = TYPO3.getInstance('FileUploadWindow'); + this.progressBar = new Ext.ProgressBar({ + id: 'myprogress-' + this.file.id, + text: String.format(TYPO3.LLL.fileUpload.uploadWait, this.file.name), + height: 'auto', + disabled: true + }); + this.parent.add(this.progressBar); + }, + + // enables the progress bar and sets the text + start: function() { + this.progressBar.enable(); + this.progressBar.updateText(String.format(TYPO3.LLL.fileUpload.uploadStarting, this.file.name)); + }, + + // updates the progress bar (+ text) to the current progress + update: function(bytesComplete, bytesTotal) { + var percent = (bytesComplete / bytesTotal); + var text = String.format(TYPO3.LLL.fileUpload.uploadProgress, Math.round(100*percent, 1), this.file.name); + this.progressBar.updateProgress(percent, text); + }, + + // simply updates the text in the bar + updateText: function(text) { + this.progressBar.updateText(text); + }, + + // is called if there is an error after queuing a file + // get details for the error code from here: SWFUpload.QUEUE_ERROR (see contrib/swfupload/swfupload.js) + // can be further extended to color the progress bar in red colors + // then calls the cleanup function + queueError: function(errorCode, message) { + var txt = message; + switch (errorCode) { + case SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED: + txt = String.format(TYPO3.LLL.fileUpload.errorQueueLimitExceeded); + break; + case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT: + txt = String.format(TYPO3.LLL.fileUpload.errorQueueFileSizeLimit, this.file.name); + break; + case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE: + txt = String.format(TYPO3.LLL.fileUpload.errorQueueZeroByteFile, this.file.name); + break; + case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE: + txt = String.format(TYPO3.LLL.fileUpload.errorQueueInvalidFiletype, this.file.name); + break; + } + this.updateText(txt); + this.cleanup(10000); + }, + + // is called if there is an error while uploading + // get the error code from here: SWFUpload.UPLOAD_ERROR (see contrib/swfupload/swfupload.js) + // can be further extended to color the progress bar in red colors + // then calls the cleanup function + error: function(errorCode, message) { + var txt = message; + switch (errorCode) { + case SWFUpload.UPLOAD_ERROR.HTTP_ERROR: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadHttp); + break; + case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadMissingUrl); + break; + case SWFUpload.UPLOAD_ERROR.IO_ERROR: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadIO); + break; + case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadSecurityError, message); + break; + case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadLimit); + break; + case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadFailed); + break; + case SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadFileIDNotFound); + break; + case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadFileValidation); + break; + case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadFileCancelled, this.file.name); + break; + case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED: + txt = String.format(TYPO3.LLL.fileUpload.errorUploadFileStopped, this.file.name); + break; + } + this.updateText(txt); + this.cleanup(10000); + }, + + // updates the progressbar to show the full 100% and a text that everything went fine + // then calls the cleanup function + success: function() { + this.progressBar.updateProgress(1, String.format(TYPO3.LLL.fileUpload.uploadSuccess, this.file.name)); + this.cleanup(); + }, + + // cleanup function that calls the destroy method right now after a specified delay + cleanup: function(delay) { + var cleanup = new Ext.util.DelayedTask(this.destroy, this); + cleanup.delay((delay ? delay : 2000)); + }, + + // internal function to remove the progress Bar and trigger an remove() event after that + // in the + destroy: function() { + if (this.progressBar && this.progressBar.id && Ext.get(this.progressBar.id)) { + Ext.get(this.progressBar.id).fadeOut({ + callback: function() { + this.parent.remove(this.progressBar); + }.bind(this) + }); + } + } + }); + +}); Index: typo3/backend.php =================================================================== --- typo3/backend.php (revision 5312) +++ typo3/backend.php (working copy) @@ -96,6 +96,10 @@ 'contrib/scriptaculous/scriptaculous.js?load=builder,effects,controls,dragdrop', 'contrib/extjs/adapter/prototype/ext-prototype-adapter.js', 'contrib/extjs/ext-all.js', + 'contrib/swfupload/swfupload.js', + 'contrib/swfupload/plugins/swfupload.swfobject.js', + 'contrib/swfupload/plugins/swfupload.cookies.js', + 'contrib/swfupload/plugins/swfupload.queue.js', 'md5.js', 'js/backend.js', 'js/common.js', @@ -103,6 +107,7 @@ 'js/toolbarmanager.js', 'js/modulemenu.js', 'js/iecompatibility.js', + 'js/flashupload.js', '../t3lib/jsfunc.evalfield.js' ); @@ -409,6 +414,40 @@ var TS = new typoSetup(); /** + * Language Labels + */ + TYPO3.LLL = { + fileUpload: { + windowTitle: "File upload progress", + buttonSelectFiles: "Select Files", + buttonCancelUpload: "Cancel Upload", + buttonCloseWindow: "Close", + infoComponentMaxFileSize: "You can upload files with a maximum size of {0}.", + infoComponentFileUploadLimit: "You can upload a total of {0}.", + infoComponentFileTypeLimit: "You can upload the following file types {0}.", + processRunning: "Another process is already uploading", + uploadWait: "Waiting to start upload of {0}", + uploadStarting: "Starting upload of {0}", + uploadProgress: "{0}% of {1} uploaded", + uploadSuccess: "{0} was successfully uploaded!", + errorQueueLimitExceeded: "Too many files selected", + errorQueueFileSizeLimit: "File {0} is too big", + errorQueueZeroByteFile: "File {0} is empty", + errorQueueInvalidFiletype: "Filetype not allowed for {0}", + errorUploadHttp: "Too many files selected", + errorUploadMissingUrl: "Internal error: No Upload URL set", + errorUploadIO: "Internal error: Problems while reading/writing the file", + errorUploadSecurityError: "Internal error: {0}", + errorUploadLimit: "Upload limit exceeded", + errorUploadFailed: "Upload failed", + errorUploadFileIDNotFound: "Internal error: File ID not found", + errorUploadFileValidation: "Internal error while validating the file", + errorUploadFileCancelled: "Upload of {0} cancelled", + errorUploadStopped: "Upload of {0} stopped" + } + }; + + /** * Functions for session-expiry detection: */ function busy() { // Index: typo3/css/backend-style.css =================================================================== --- typo3/css/backend-style.css (revision 5312) +++ typo3/css/backend-style.css (working copy) @@ -309,7 +309,15 @@ padding: 1px; } +/* ----- File Upload Window ----- */ +.swfupload { + display: block; + position: absolute; + width: 60px; + height: 17px; + z-index: 2000; +} Index: typo3/sysext/setup/mod/locallang.xml =================================================================== --- typo3/sysext/setup/mod/locallang.xml (revision 5312) +++ typo3/sysext/setup/mod/locallang.xml (working copy) @@ -78,6 +78,7 @@ + Index: typo3/sysext/setup/mod/index.php =================================================================== --- typo3/sysext/setup/mod/index.php (revision 5312) +++ typo3/sysext/setup/mod/index.php (working copy) @@ -161,6 +161,7 @@ $BE_USER->uc['edit_wideDocument'] = $d['edit_wideDocument']; if ($GLOBALS['TYPO3_CONF_VARS']['BE']['RTEenabled']) { $BE_USER->uc['edit_RTE'] = $d['edit_RTE']; } $BE_USER->uc['edit_docModuleUpload'] = $d['edit_docModuleUpload']; + $BE_USER->uc['enableFlashUploader'] = $d['enableFlashUploader']; $BE_USER->uc['edit_showFieldHelp'] = $d['edit_showFieldHelp']; $BE_USER->uc['disableCMlayers'] = $d['disableCMlayers']; @@ -456,6 +457,10 @@ 'type' => 'check', 'form' => 'uc['edit_docModuleUpload']?' checked="checked"':'').' />' ), + 'enableFlashUploader' => array( + 'type' => 'check', + 'form' => 'uc['enableFlashUploader']?' checked="checked"':'').' />' + ), 'disableCMlayers' => array( 'type' => 'check', 'form' => 'uc['disableCMlayers']?' checked="checked"':'').' />'