Project

General

Profile

Feature #20146 » flash_uploader_v10.patch

Administrator Admin, 2009-05-15 23:05

View differences:

NEWS.txt (working copy)
In the view showing a single table with many elements, a pagination helps to
get through them.
* Uploading files is now available with an optional Flash Uploader which shows a
nice widget with information on the upload progress. It makes it possible to select
multiple files at once. Enable the Flash Uploader by checking the appropriate checkbox
in the User Setup, reload the Backend and make sure that your browser has the
latest version of Flash (Flash v9+) installed.
* It is possible now to temporarily lock down the backend for system maintenance. Editors
will see an overlay with an message notifying them that the backend is locked. When the
lock is removed, editors can continue without having to re-login.
t3lib/class.t3lib_beuserauth.php (working copy)
'edit_showFieldHelp' => 'icon',
'edit_RTE' => '1',
'edit_docModuleUpload' => '1',
'enableFlashUploader' => '1',
'disableCMlayers' => 0,
'navFrameWidth' => '', // Default is 245 pixels
'navFrameResizable' => 0,
......
function veriCode() {
return substr(md5($this->id.$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']),0,10);
}
/**
* The session_id is used to find user in the database.
* Two tables are joined: The session-table with user_id of the session and the usertable with its primary key
* 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 DB result object or false on error
* @access private
*/
protected function fetchUserSessionFromDB() {
if ($GLOBALS['CLIENT']['BROWSER'] == 'flash') {
// if on the flash client, the veri code is valid, then the user session is fetched
// from the DB without the hashLock clause
if (t3lib_div::_GP('vC') == $this->veriCode()) {
$dbres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'*',
$this->session_table.','.$this->user_table,
$this->session_table.'.ses_id = '.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, $this->session_table).'
AND '.$this->session_table.'.ses_name = '.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->name, $this->session_table).'
AND '.$this->session_table.'.ses_userid = '.$this->user_table.'.'.$this->userid_column.'
'.$this->ipLockClause().'
'.$this->user_where_clause()
);
} else {
$dbres = false;
}
} else {
$dbres = parent::fetchUserSessionFromDB();
}
return $dbres;
}
}
t3lib/class.t3lib_div.php (working copy)
$bInfo['BROWSER']= 'msie';
} elseif (strpos($useragent, 'Mozilla') !== false) {
$bInfo['BROWSER']='net';
} elseif (strpos($useragent, 'Flash') !== false) {
$bInfo['BROWSER'] = 'flash';
}
if ($bInfo['BROWSER']) {
// Browser version
t3lib/class.t3lib_userauth.php (working copy)
$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) {
......
if ($this->writeDevLog) t3lib_div::devLog('Fetch session ses_id = '.$this->id, 't3lib_userAuth');
// The session_id is used to find user in the database. Two tables are joined: The session-table with user_id of the session and the usertable with its primary key
$dbres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'*',
$this->session_table.','.$this->user_table,
$this->session_table.'.ses_id = '.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, $this->session_table).'
AND '.$this->session_table.'.ses_name = '.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->name, $this->session_table).'
AND '.$this->session_table.'.ses_userid = '.$this->user_table.'.'.$this->userid_column.'
'.$this->ipLockClause().'
'.$this->hashLockClause().'
'.$this->user_where_clause()
);
// fetch the user session from the DB
$dbres = $this->fetchUserSessionFromDB();
if ($user = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($dbres)) {
if ($dbres && $user = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($dbres)) {
// A user was found
if (is_string($this->auth_timeout_field)) {
$timeout = intval($user[$this->auth_timeout_field]); // Get timeout-time from usertable
......
*************************/
/**
* The session_id is used to find user in the database.
* Two tables are joined: The session-table with user_id of the session and the usertable with its primary key
* @return DB result object or false on error
* @access private
*/
protected function fetchUserSessionFromDB() {
$dbres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'*',
$this->session_table.','.$this->user_table,
$this->session_table.'.ses_id = '.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, $this->session_table).'
AND '.$this->session_table.'.ses_name = '.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->name, $this->session_table).'
AND '.$this->session_table.'.ses_userid = '.$this->user_table.'.'.$this->userid_column.'
'.$this->ipLockClause().'
'.$this->hashLockClause().'
'.$this->user_where_clause()
);
return $dbres;
}
/**
* This returns the where-clause needed to select the user with respect flags like deleted, hidden, starttime, endtime
*
* @return string
typo3/alt_clickmenu.php (working copy)
// rename
if (!in_array('rename',$this->disabledItems)) $menuItems['rename']=$this->FILE_launch($path,'file_rename.php','rename','rename.gif');
// upload
if (!in_array('upload',$this->disabledItems) && is_dir($path)) $menuItems['upload']=$this->FILE_launch($path,'file_upload.php','upload','upload.gif',TRUE);
if (!in_array('upload',$this->disabledItems) && is_dir($path)) {
$menuItems['upload'] = $this->FILE_upload($path);
}
// new
if (!in_array('new',$this->disabledItems) && is_dir($path)) $menuItems['new']=$this->FILE_launch($path,'file_newfolder.php','new','new_file.gif');
// info
......
}
/**
* function for adding an upload entry to the $menuItems array
*
* @param string Path to the file/directory (target)
* @return array Item array, element in $menuItems
* @internal
*/
function FILE_upload($path) {
$script = 'file_upload.php';
$type = 'upload';
$image = 'upload.gif';
if ($GLOBALS['BE_USER']->uc['enableFlashUploader']) {
$loc='top.content'.(!$this->alwaysContentFrame?'.list_frame':'');
$editOnClick = 'if (top.TYPO3.FileUploadWindow.isFlashAvailable()) { initFlashUploader("' . rawurlencode($path) . '"); } else if(' . $loc . '){' . $loc . ".location.href=top.TS.PATH_typo3+'".$script.'?target=' . rawurlencode($path) . "';}";
return $this->linkItem(
$this->label($type),
$this->excludeIcon('<img'.t3lib_iconWorks::skinImg($this->PH_backPath,'gfx/'.$image,'width="12" height="12"').' alt="" />'),
$editOnClick.'return hideCM();'
);
} else {
return $this->FILE_launch($path, $script, $type, $image, true);
}
}
/**
* Returns element for copy or cut of files.
*
* @param string Path to the file/directory (target)
typo3/alt_file_navframe.php (working copy)
($this->currentSubScript?'top.currentSubScript=unescape("'.rawurlencode($this->currentSubScript).'");':'').'
function initFlashUploader(path) {
path = decodeURIComponent(path);
var flashUploadOptions = {
uploadURL: top.TS.PATH_typo3 + "ajax.php",
uploadFileSizeLimit: "' . t3lib_div::getMaxUploadFileSize() . '",
uploadFileTypes: {
allow: "' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace']['allow'] . '",
deny: "' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace']['deny'] . '"
},
uploadFilePostName: "upload_1",
uploadPostParams: {
"file[upload][1][target]": path,
"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",
bodyBorder: false,
border: false,
hideBorders: true,
cls: "t3-upload-window-infopanel",
id: "t3-upload-window-infopanel-addition",
html: \'<label for="overrideExistingFilesCheckbox"><input id="overrideExistingFilesCheckbox" type="checkbox" onclick="setFlashPostOptionOverwriteExistingFiles(this);" />\' + top.String.format(top.TYPO3.LLL.fileUpload.infoComponentOverrideFiles) + \'</label>\'
});
flashUploader.add(infoComponent);
// do a reload of this frame once all uploads are done
flashUploader.on("totalcomplete", function() {
jumpTo(path, "", "", "");
});
// 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));
}
};
}
// setting prefs for foldertree
Tree.ajaxID = "SC_alt_file_navframe::expandCollapse";
......
if (theUrl.indexOf("?") != -1) {
theUrl += "&id=" + id
} else {
theUrl += "?id=" + id
}
theUrl += "?id=" + id
}
top.fsMod.currentBank = bank;
if (top.condensedMode) {
typo3/backend.php (working copy)
'contrib/scriptaculous/scriptaculous.js?load=builder,effects,controls,dragdrop',
'contrib/extjs/adapter/ext/ext-base.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',
......
'js/toolbarmanager.js',
'js/modulemenu.js',
'js/iecompatibility.js',
'js/flashupload.js',
'../t3lib/jsfunc.evalfield.js'
);
......
this.PATH_typo3_enc = "'.rawurlencode($pathTYPO3).'";
this.username = "'.htmlspecialchars($GLOBALS['BE_USER']->user['username']).'";
this.uniqueID = "'.t3lib_div::shortMD5(uniqid('')).'";
this.veriCode = "' . $GLOBALS['BE_USER']->veriCode() . '";
this.navFrameWidth = 0;
this.securityLevel = "'.$this->loginSecurityLevel.'";
this.denyFileTypes = "' . PHP_EXTENSIONS_DEFAULT . '";
}
var TS = new typoSetup();
/**
* Language Labels
*/
TYPO3.LLL = {
fileUpload: {
windowTitle: "File Upload Progress",
buttonSelectFiles: "Select Files",
buttonCancelAll: "Cancel All Uploads",
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}.",
infoComponentOverrideFiles: "' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xml:overwriteExistingFiles', 1) . '",
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: "{0} is too big",
errorQueueZeroByteFile: "{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} canceled",
errorUploadStopped: "Upload of {0} stopped"
}
};
/**
* Functions for session-expiry detection:
*/
function busy() { //
typo3/css/backend-style.css (working copy)
padding: 1px;
}
/* ----- File Upload Window ----- */
.swfupload {
display: block;
position: absolute;
width: 80px;
height: 22px;
z-index: 2000;
}
#t3-upload-window-infopanel div div {
padding: 10px;
}
.t3-upload-window-infopanel div div {
padding: 0 10px 10px;
}
.t3-upload-window-infopanel input {
vertical-align: text-bottom;
margin-right: 3px;
}
.t3-upload-window-progressbar .x-progress-text div {
text-align: left;
text-indent: 6px;
}
......
typo3/file_list.php (working copy)
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');
......
$this->doc = t3lib_div::makeInstance('template');
$this->doc->backPath = $BACK_PATH;
$this->doc->setModuleTemplate('templates/file_list.html');
$this->doc->loadPrototype();
// Validating the input "id" (the path, directory!) and checking it against the mounts of the user.
$this->id = $this->basicFF->is_directory($this->id);
......
// 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']) {
$this->doc->JScodeArray['flashUploader'] = '
if (top.TYPO3.FileUploadWindow.isFlashAvailable()) {
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: top.TS.PATH_typo3 + "ajax.php",
uploadFileSizeLimit: "' . t3lib_div::getMaxUploadFileSize() . '",
uploadFileTypes: {
allow: "' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace']['allow'] . '",
deny: "' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace']['deny'] . '"
},
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",
bodyBorder: false,
border: false,
hideBorders: true,
cls: "t3-upload-window-infopanel",
id: "t3-upload-window-infopanel-addition",
html: \'<label for="overrideExistingFilesCheckbox"><input id="overrideExistingFilesCheckbox" type="checkbox" onclick="setFlashPostOptionOverwriteExistingFiles(this);" />\' + top.String.format(top.TYPO3.LLL.fileUpload.infoComponentOverrideFiles) + \'</label>\'
});
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;
......
// upload button
$theIcon = '<img'.t3lib_iconWorks::skinImg($this->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'] = '<a href="' . $BACK_PATH . 'file_upload.php?target=' . rawurlencode($this->id) . '&amp;returnUrl=' . rawurlencode($this->filelist->listURL()) . '">' . $theIcon . '</a>';
$buttons['upload'] = '<a href="' . $BACK_PATH . 'file_upload.php?target=' . rawurlencode($this->id) . '&amp;returnUrl=' . rawurlencode($this->filelist->listURL()) . '" id="button-upload">' . $theIcon . '</a>';
$theIcon = '<img'.t3lib_iconWorks::skinImg($this->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'] = '<a href="' . $BACK_PATH . 'file_newfolder.php?target=' . rawurlencode($this->id) . '&amp;returnUrl=' . rawurlencode($this->filelist->listURL()) . '">' . $theIcon . '</a>';
typo3/js/common.js (working copy)
}
}
}
// 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;
},
helpers: {
// creates an array by splitting a string into parts, taking a delimiter
split: function(str, delim) {
var res = new Array();
while (str.indexOf(delim) > 0) {
res.push(str.substr(0, str.indexOf(delim)));
str = str.substr(str.indexOf(delim) + delim.length);
}
res.push(allow);
return res;
}
}
};
typo3/js/flashupload.js (revision 0)
/**
* 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 <kasperYYYYY@typo3.com>
*
* 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
deniedFileTypes: '', // internal, local check to see if the uploading file is not allowed
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
minimum_flash_version: '9.0.28',
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: 22,
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: false,
hideBorders: true,
tbar: [{
id: 't3-file-upload-window-button-selectfiles',
text: String.format(TYPO3.LLL.fileUpload.buttonSelectFiles),
iconCls: 't3icon-ext-upload'
}, {
xtype: 'tbfill'
}, {
id: 't3-file-upload-window-button-cancel',
text: String.format(TYPO3.LLL.fileUpload.buttonCancelAll),
handler: function() {
this.cleanup();
this.hide();
},
scope: this,
iconCls: 't3icon-ext-cancel'
}
]
};
// 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,
resizable: false
};
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, '<div id="' + swfConfig.button_placeholder_id + '"></div>');
}
this.swf = new SWFUpload(swfConfig);
this.swf.fileUploadWindow = this;
// add some info to the dialog
// you can replace this by adding your own component with this ID in the constructor
if (!this.getComponent('t3-upload-window-infopanel')) {
var maxFileSize = (swfConfig.file_size_limit.toString().indexOf('B') > 0 ? swfConfig.file_size_limit : swfConfig.file_size_limit + ' KB');
var txt = String.format(TYPO3.LLL.fileUpload.infoComponentMaxFileSize, maxFileSize) + '<br/>';
if (swfConfig.file_upload_limit) {
txt += String.format(TYPO3.LLL.fileUpload.infoComponentFileUploadLimit, swfConfig.file_upload_limit) + '<br/>';
}
if (swfConfig.file_types != '*.*') {
txt += String.format(TYPO3.LLL.fileUpload.infoComponentFileTypeLimit, swfConfig.file_types);
}
this.insert(0, new Ext.Panel({
autoEl: { tag: 'div' },
height: 'auto',
id: 't3-upload-window-infopanel',
html: txt,
bodyBorder: false,
border: false
}));
}
},
/**
* 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.post_params = Ext.value(this.uploadPostParams, this.swfDefaultConfig.post_params);
// add the veriCode from the backend.php to verify the session with the flash client
swfConfig.post_params.vC = top.TS.veriCode;
swfConfig.file_types_description = Ext.value(this.uploadFileTypesDescription, this.swfDefaultConfig.file_types_description);
this.setFileTypeRestrictions(this.uploadFileTypes);
return swfConfig;
},
/**
* this function merges the values from the TYPO3 style (comma separated) to the format
* SWFupload needs it to be (*.jpg;*.gif) and also adds deny file patterns
*/
setFileTypeRestrictions: function(typo3FileTypes) {
if (typo3FileTypes.allow && typo3FileTypes.allow != '' && typo3FileTypes.allow != '*') {
var allowedFiles = TYPO3.helpers.split(typo3FileTypes.allow, ',');
this.swfDefaultConfig.file_types = '*.' + allowedFiles.join(';*.');
}
if (typo3FileTypes.deny && typo3FileTypes.deny != '') {
this.deniedFileTypes = typo3FileTypes.deny;
}
},
/**
* because swfupload does not include a way to explicitly deny certain files, we need to
* check the file type of every selected file before it gets uploaded
*/
fileTypeIsAllowed: function(filename) {
var ext = filename.substr(filename.lastIndexOf('.')+1);
if (ext) {
var denyTypes = this.deniedFileTypes;
denyTypes += (denyTypes.length ? ',' : '') + TS.denyFileTypes;
denyTypes = ',' + denyTypes + ',';
var reg = new RegExp(',' + ext + ',', 'i');
if (denyTypes.search(reg) == -1) {
return true;
}
}
return false;
},
/**
* 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();
},
/**
* 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() {
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) {
this.swf.startUpload();
this.fireEvent('uploadSelectFiles', this, [numFilesSelected, numFilesQueued, numFilesInQueue]);
},
// private
uploadQueued: function(fileObj) {
this.activeUploads[fileObj.id] = new TYPO3.FileUpload({file: fileObj});
if (!this.fileTypeIsAllowed(fileObj.name)) {
this.activeUploads[fileObj.id].error(SWFUpload.QUEUE_ERROR.INVALID_FILETYPE, '');
this.fireEvent('uploadError', this, [this.activeUploads[fileObj.id], SWFUpload.QUEUE_ERROR.INVALID_FILETYPE, '']);
this.swf.cancelUpload(fileObj.id, false);
} else {
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].error(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 function checks through the SWFObject if the required flash player is available
TYPO3.FileUploadWindow.isFlashAvailable = function() {
return swfobject.hasFlashPlayerVersion(TYPO3.FileUploadWindow.prototype.swfDefaultConfig.minimum_flash_version);
}
/**
* This class includes one instance of an upload
* and is used mainly to display the progress bar Component
*/
TYPO3.FileUpload = Ext.extend(Ext.Component, {
infoPanel: null, // Ext.Panel instance holding the progressBar and the cancel button
cancelButton: null, // Ext.BoxComponent instance, holding the X in there
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');
// the progress bar instance
this.progressBar = new Ext.ProgressBar({
id: 'flashupload-progress ' + this.file.id,
cls: 't3-upload-window-progressbar',
text: String.format(TYPO3.LLL.fileUpload.uploadWait, this.file.name),
width: '100%',
height: 21,
x: -1,
disabled: true
});
// the cancel button
this.cancelButton = new Ext.BoxComponent({
autoEl: {
tag: 'div',
'class': 't3icon-ext-cancel t3iconstyle-center'
},
hidden: true,
width: 22,
height: 18,
x: -100
});
// the panel that holds the progress bar and the button together
this.infoPanel = new Ext.Panel({
layout: 'absolute',
width: '100%',
hideBorders: true,
bodyBorder: false,
border: false,
items: [this.progressBar, this.cancelButton]
});
this.infoPanel.on('show', function() {
var h = this.progressBar.getBox().height;
this.infoPanel.setHeight(h);
// show the cancel button on hover
this.infoPanel.getEl().on('mouseover', function() {
this.cancelButton.show();
this.cancelButton.setPosition(this.progressBar.getBox().width - this.cancelButton.getBox().width, -1);
}, this);
// hide the cancel button on hover out
this.infoPanel.getEl().on('mouseout', function() {
this.cancelButton.hide();
}, this);
// if the cancel button is clicked, the download is canceled
this.cancelButton.getEl().on('click', function() {
if (this.parent.swf) {
this.parent.swf.cancelUpload(this.file.id);
}
this.cleanup(10);
}, this);
}, this);
this.parent.add(this.infoPanel);
this.infoPanel.show();
},
// 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 or while uploading
// get details for the error code from here: SWFUpload.QUEUE_ERROR or
// 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.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;
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 main window
destroy: function() {
if (this.infoPanel && this.infoPanel.id && Ext.get(this.infoPanel.id)) {
Ext.get(this.infoPanel.id).fadeOut({
callback: function() {
this.parent.remove(this.infoPanel);
}.bind(this)
});
}
}
});
});
typo3/stylesheet.css (working copy)
}
/*** Icons ***/
/* extJS icons */
.t3icon-ext-upload {
background: url('gfx/upload.gif') 3px 4px no-repeat !important;
}
.t3icon-ext-cancel {
background: url('gfx/icon_fatalerror.gif') 2px 3px no-repeat !important;
}
.t3iconstyle-center {
background-position: center center;
}
/* Visual debugging: */
/*
typo3/sysext/setup/locallang_csh_mod.xml (working copy)
<label index="option_edit_docModuleUpload.description">This option will enable an &quot;Upload&quot; box for each field in TYPO3 records where you can attach a file or image. Most likely you don't want to disable this since it is usually just a nice thing to have.</label>
<label index="option_edit_docModuleUpload.details">If you disable the Upload box there is still the TYPO3 Element Browser left for attaching files and images.</label>
<label index="_option_edit_docModuleUpload.image">EXT:setup/cshimages/setup12.png</label>
<label index="option_enableFlashUploader.alttitle">Enable Flash Uploader</label>
<label index="option_enableFlashUploader.description">This option will enable a flash-based uploader that allows you to select multiple files at once when uploading files. If you enable this option, make sure that your browser has the latest Flash plugin (Flash 9 or higher) installed in order to make this feature work.</label>
<label index="option_edit_showFieldHelp.alttitle">Context Sensitive Help mode</label>
<label index="option_edit_showFieldHelp.description">Defines the mode of Context Sensitive Help (CSH) in TYPO3. The default is that small help icons are shown everywhere a help item is available. You can click the icon and help will appear in a window for you.</label>
<label index="option_edit_showFieldHelp.details">Alternatively you can select that descriptions are shown inline. This is useful for people who are still learning how TYPO3 works and want to browse the backend with descriptions shown directly.
typo3/sysext/setup/mod/index.php (working copy)
$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'];
......
'type' => 'check',
'form' => '<input id="field_edit_docModuleUpload" type="checkbox" name="data[edit_docModuleUpload]"'.($BE_USER->uc['edit_docModuleUpload']?' checked="checked"':'').' />'
),
'enableFlashUploader' => array(
'type' => 'check',
'form' => '<input id="field_enableFlashUploader" type="checkbox" name="data[enableFlashUploader]"' . ($BE_USER->uc['enableFlashUploader'] ? ' checked="checked"' : '') . ' />'
),
'disableCMlayers' => array(
'type' => 'check',
'form' => '<input id="field_disableCMlayers" type="checkbox" name="data[disableCMlayers]"'.($BE_USER->uc['disableCMlayers']?' checked="checked"':'').' />'
typo3/sysext/setup/mod/locallang.xml (working copy)
<label index="edit_wideDocument">Wide document background</label>
<label index="edit_RTE">Enable Rich Text Editor (if available)</label>
<label index="edit_docModuleUpload">File upload directly in Doc-module</label>
<label index="enableFlashUploader">Enable Flash Uploader (requires Flash 9+)</label>
<label index="edit_functions">Edit</label>
<label index="disableCMlayers">Disable Popup Context Menus</label>
<label index="setToStandard">Reset all Values to default</label>
typo3/sysext/t3skin/stylesheets/stylesheet_post.css (working copy)
}
/* END PAGE HEADERS: */
/* Icons */
.t3icon-ext-upload {
background: url('../icons/gfx/upload.gif') 1px 2px no-repeat !important;
}
.t3icon-ext-cancel {
background: url('../icons/gfx/icon_fatalerror.gif') 1px 2px no-repeat !important;
}
(8-8/8)