diff --git a/config/defaults.inc.php b/config/defaults.inc.php
index fb7b9a542..b60a0d04d 100644
--- a/config/defaults.inc.php
+++ b/config/defaults.inc.php
@@ -796,11 +796,6 @@ $config['max_pagesize'] = 200;
// Minimal value of user's 'refresh_interval' setting (in seconds)
$config['min_refresh_interval'] = 60;
-// Enables files upload indicator. Requires APC installed and enabled apc.rfc1867 option.
-// By default refresh time is set to 1 second. You can set this value to true
-// or any integer value indicating number of seconds.
-$config['upload_progress'] = false;
-
// Specifies for how many seconds the Undo button will be available
// after object delete action. Currently used with supporting address book sources.
// Setting it to 0, disables the feature.
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index e94d9019b..f9d925eb2 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -2030,76 +2030,12 @@ class rcmail extends rcube
/**
* File upload progress handler.
+ *
+ * @deprecated We're using HTML5 upload progress
*/
public function upload_progress()
{
- $params = array(
- 'action' => $this->action,
- 'name' => rcube_utils::get_input_value('_progress', rcube_utils::INPUT_GET),
- );
-
- if (function_exists('uploadprogress_get_info')) {
- $status = uploadprogress_get_info($params['name']);
-
- if (!empty($status)) {
- $params['current'] = $status['bytes_uploaded'];
- $params['total'] = $status['bytes_total'];
- }
- }
-
- if (!isset($status) && filter_var(ini_get('apc.rfc1867'), FILTER_VALIDATE_BOOLEAN)
- && ini_get('apc.rfc1867_name')
- ) {
- $prefix = ini_get('apc.rfc1867_prefix');
- $status = apc_fetch($prefix . $params['name']);
-
- if (!empty($status)) {
- $params['current'] = $status['current'];
- $params['total'] = $status['total'];
- }
- }
-
- if (!isset($status) && filter_var(ini_get('session.upload_progress.enabled'), FILTER_VALIDATE_BOOLEAN)
- && ini_get('session.upload_progress.name')
- ) {
- $key = ini_get('session.upload_progress.prefix') . $params['name'];
-
- $params['total'] = $_SESSION[$key]['content_length'];
- $params['current'] = $_SESSION[$key]['bytes_processed'];
- }
-
- if (!empty($params['total'])) {
- $total = $this->show_bytes($params['total'], $unit);
- switch ($unit) {
- case 'GB':
- $gb = $params['current']/1073741824;
- $current = sprintf($gb >= 10 ? "%d" : "%.1f", $gb);
- break;
- case 'MB':
- $mb = $params['current']/1048576;
- $current = sprintf($mb >= 10 ? "%d" : "%.1f", $mb);
- break;
- case 'KB':
- $current = round($params['current']/1024);
- break;
- case 'B':
- default:
- $current = $params['current'];
- break;
- }
-
- $params['percent'] = round($params['current']/$params['total']*100);
- $params['text'] = $this->gettext(array(
- 'name' => 'uploadprogress',
- 'vars' => array(
- 'percent' => $params['percent'] . '%',
- 'current' => $current,
- 'total' => $total
- )
- ));
- }
-
- $this->output->command('upload_progress_update', $params);
+ // NOOP
$this->output->send();
}
@@ -2112,24 +2048,6 @@ class rcmail extends rcube
*/
public function upload_init($max_size = null)
{
- // Enable upload progress bar
- if ($seconds = $this->config->get('upload_progress')) {
- if (function_exists('uploadprogress_get_info')) {
- $field_name = 'UPLOAD_IDENTIFIER';
- }
- if (!$field_name && filter_var(ini_get('apc.rfc1867'), FILTER_VALIDATE_BOOLEAN)) {
- $field_name = ini_get('apc.rfc1867_name');
- }
- if (!$field_name && filter_var(ini_get('session.upload_progress.enabled'), FILTER_VALIDATE_BOOLEAN)) {
- $field_name = ini_get('session.upload_progress.name');
- }
-
- if ($field_name) {
- $this->output->set_env('upload_progress_name', $field_name);
- $this->output->set_env('upload_progress_time', (int) $seconds);
- }
- }
-
// find max filesize value
$max_filesize = rcube_utils::max_upload_size();
if ($max_size && $max_size < $max_filesize) {
@@ -2147,6 +2065,8 @@ class rcmail extends rcube
'name' => 'filecounterror', 'vars' => array('count' => $max_filecount))));
}
+ $this->output->add_label('uploadprogress', 'GB', 'MB', 'KB', 'B');
+
return $max_filesize_txt;
}
diff --git a/program/js/app.js b/program/js/app.js
index 19958cd6c..57726a026 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -50,6 +50,7 @@ function rcube_webmail()
this.menu_buttons = {};
this.entity_selectors = [];
this.image_style = {};
+ this.uploads = {};
// webmail client settings
this.dblclick_time = 500;
@@ -684,7 +685,7 @@ function rcube_webmail()
}
// activate html5 file drop feature (if browser supports it and if configured)
- if (this.gui_objects.filedrop && this.env.filedrop && ((window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary) || window.FormData)) {
+ if (this.gui_objects.filedrop && this.env.filedrop && window.FormData) {
$(document.body).on('dragover dragleave drop', function(e) { return ref.document_drag_hover(e, e.type == 'dragover'); });
$(this.gui_objects.filedrop).addClass('droptarget')
.on('dragover dragleave', function(e) { return ref.file_drag_hover(e, e.type == 'dragover'); })
@@ -1393,8 +1394,6 @@ function rcube_webmail()
var form = props || this.gui_objects.importform,
importlock = this.set_busy(true, 'importwait');
- $('[name="_unlock"]', form).val(importlock);
-
if (!(flag = this.upload_file(form, 'import', importlock))) {
this.set_busy(false, null, importlock);
if (flag !== false)
@@ -5314,77 +5313,21 @@ function rcube_webmail()
// upload (attachment) file
this.upload_file = function(form, action, lock)
{
- if (!form)
- return;
-
- // count files and size on capable browser
- var size = 0, numfiles = 0;
-
- $.each($(form).get(0).elements || [], function() {
- if (this.type != 'file')
- return;
-
- var i, files = this.files ? this.files.length : (this.value ? 1 : 0);
-
- // check file size
- if (this.files) {
- for (i=0; i < files; i++)
- size += this.files[i].size;
- }
-
- numfiles += files;
- });
-
- // create hidden iframe and post upload form
- if (numfiles) {
- if (this.env.max_filesize && this.env.filesizeerror && size > this.env.max_filesize) {
- this.display_message(this.env.filesizeerror, 'error');
- return false;
- }
-
- if (this.env.max_filecount && this.env.filecounterror && numfiles > this.env.max_filecount) {
- this.display_message(this.env.filecounterror, 'error');
- return false;
- }
-
- var frame_name = this.async_upload_form(form, action || 'upload', function(e) {
- var d, content = '';
- try {
- if (this.contentDocument) {
- d = this.contentDocument;
- } else if (this.contentWindow) {
- d = this.contentWindow.document;
- }
- content = d.childNodes[1].innerHTML;
- } catch (err) {}
-
- if (!content.match(/add2attachment/) && (!bw.opera || (ref.env.uploadframe && ref.env.uploadframe == e.data.ts))) {
- if (!content.match(/display_message/))
- ref.display_message('fileuploaderror', 'error');
- ref.remove_from_attachment_list(e.data.ts);
-
- if (lock)
- ref.set_busy(false, null, lock);
+ if (form) {
+ var fname, files = [];
+ $('input', form).each(function() {
+ if (this.files) {
+ fname = this.name;
+ for (var i=0; i < this.files.length; i++)
+ files.push(this.files[i]);
}
- // Opera hack: handle double onload
- if (bw.opera)
- ref.env.uploadframe = e.data.ts;
});
- // display upload indicator and cancel button
- var content = '' + this.get_label('uploading' + (numfiles > 1 ? 'many' : '')) + '',
- ts = frame_name.replace(/^rcmupload/, '');
-
- this.add2attachment_list(ts, { name:'', html:content, classname:'uploading', frame:frame_name, complete:false });
-
- // upload progress support
- if (this.env.upload_progress_time) {
- this.upload_progress_start('upload', ts);
- }
-
- // set reference to the form object
- this.gui_objects.attachmentform = form;
- return true;
+ return this.file_upload(files, {_id: this.env.compose_id || ''}, {
+ name: fname,
+ action: action,
+ lock: lock
+ });
}
};
@@ -5408,12 +5351,15 @@ function rcube_webmail()
var label, indicator, li = $('
');
+ if (!att.complete && att.html.indexOf('<') < 0)
+ att.html = '' + att.html + '';
+
if (!att.complete && this.env.loadingicon)
att.html = '
' + att.html;
- if (!att.complete && att.frame) {
+ if (!att.complete) {
label = this.get_label('cancel');
- att.html = ''
+ att.html = ''
+ (this.env.cancelicon ? '
' : '' + label + '') + '' + att.html;
}
@@ -5453,35 +5399,16 @@ function rcube_webmail()
return false;
};
- this.cancel_attachment_upload = function(name, frame_name)
+ this.cancel_attachment_upload = function(name)
{
- if (!name || !frame_name)
+ if (!name || !this.uploads[name])
return false;
this.remove_from_attachment_list(name);
- $("iframe[name='"+frame_name+"']").remove();
+ this.uploads[name].abort();
return false;
};
- this.upload_progress_start = function(action, name)
- {
- setTimeout(function() { ref.http_request(action, {_progress: name}); },
- this.env.upload_progress_time * 1000);
- };
-
- this.upload_progress_update = function(param)
- {
- var elem = $('#'+param.name + ' > span');
-
- if (!elem.length || !param.text)
- return;
-
- elem.text(param.text);
-
- if (!param.done)
- this.upload_progress_start(param.action, param.name);
- };
-
// rename uploaded attachment (in compose)
this.rename_attachment = function(id)
{
@@ -9069,7 +8996,7 @@ function rcube_webmail()
return;
if (response.unlock)
- this.set_busy(false);
+ this.set_busy(false, null, response.unlock);
this.triggerEvent('responsebefore', {response: response});
this.triggerEvent('responsebefore'+response.action, {response: response});
@@ -9472,19 +9399,6 @@ function rcube_webmail()
frame_name = 'rcmupload' + ts,
frame = this.dummy_iframe(frame_name);
- // upload progress support
- if (this.env.upload_progress_name) {
- var fname = this.env.upload_progress_name,
- field = $('input[name='+fname+']', form);
-
- if (!field.length) {
- field = $('').attr({type: 'hidden', name: fname});
- field.prependTo(form);
- }
-
- field.val(ts);
- }
-
// handle upload errors by parsing iframe content in onload
frame.on('load', {ts:ts}, onload);
@@ -9532,19 +9446,14 @@ function rcube_webmail()
this.file_drag_hover(e, false);
// prepare multipart form data composition
- var uri, size = 0, numfiles = 0,
+ var uri,
files = e.target.files || e.dataTransfer.files,
- formdata = window.FormData ? new FormData() : null,
- fieldname = (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'),
- boundary = '------multipartformboundary' + (new Date).getTime(),
- dashdash = '--', crlf = '\r\n',
- multipart = dashdash + boundary + crlf,
args = {_id: this.env.compose_id || this.env.cid || '', _remote: 1, _from: this.env.action};
if (!files || !files.length) {
// Roundcube attachment, pass its uri to the backend and attach
if (uri = e.dataTransfer.getData('roundcube-uri')) {
- var ts = new Date().getTime(),
+ var ts = 'upload' + new Date().getTime(),
// jQuery way to escape filename (#1490530)
content = $('').text(e.dataTransfer.getData('roundcube-name') || this.get_label('attaching')).html();
@@ -9557,110 +9466,126 @@ function rcube_webmail()
this.http_post(this.env.filedrop.action || 'upload', args);
}
+
return;
}
- // inline function to submit the files to the server
- var submit_data = function() {
- if (ref.env.max_filesize && ref.env.filesizeerror && size > ref.env.max_filesize) {
- ref.display_message(ref.env.filesizeerror, 'error');
- return;
+ this.file_upload(files, args, {
+ name: (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'),
+ single: this.env.filedrop.single,
+ filter: this.env.filedrop.filter,
+ action: ref.env.filedrop.action
+ });
+ };
+
+ // Files upload using ajax
+ this.file_upload = function(files, post_args, props)
+ {
+ if (!window.FormData || !files || !files.length)
+ return false;
+
+ var f, i, fname, size = 0, numfiles = 0,
+ formdata = new FormData(),
+ fieldname = props.name || '_file[]',
+ limit = props.single ? 1 : files.length;
+ args = $.extend({_remote: 1, _from: this.env.action}, post_args || {});
+
+ // add files to form data
+ for (i=0; numfiles < limit && (f = files[i]); i++) {
+ // filter by file type if requested
+ if (props.filter && !f.type.match(new RegExp(props.filter))) {
+ // TODO: show message to user
+ continue;
}
- if (ref.env.max_filecount && ref.env.filecounterror && numfiles > ref.env.max_filecount) {
- ref.display_message(ref.env.filecounterror, 'error');
- return;
+ formdata.append(fieldname, f);
+ size += f.size;
+ fname = f.name;
+ numfiles++;
+ }
+
+ if (numfiles) {
+ if (this.env.max_filesize && this.env.filesizeerror && size > this.env.max_filesize) {
+ this.display_message(this.env.filesizeerror, 'error');
+ return false;
}
- var multiple = files.length > 1,
- ts = new Date().getTime(),
+ if (this.env.max_filecount && this.env.filecounterror && numfiles > this.env.max_filecount) {
+ this.display_message(this.env.filecounterror, 'error');
+ return false;
+ }
+
+ var ts = 'upload' + new Date().getTime(),
+ label = numfiles > 1 ? this.get_label('uploadingmany') : fname,
// jQuery way to escape filename (#1490530)
- content = $('').text(multiple ? ref.get_label('uploadingmany') : files[0].name).html();
+ content = $('').text(label).html();
// add to attachments list
- if (!ref.add2attachment_list(ts, { name:'', html:content, classname:'uploading', complete:false }))
- ref.file_upload_id = ref.set_busy(true, 'uploading');
-
- // complete multipart content and post request
- multipart += dashdash + boundary + dashdash + crlf;
+ if (!this.add2attachment_list(ts, {name: '', html: content, classname: 'uploading', complete: false}) && !props.lock)
+ props.lock = this.file_upload_id = this.set_busy(true, 'uploading');
args._uploadid = ts;
+ args._unlock = props.lock;
- $.ajax({
+ this.uploads[ts] = $.ajax({
type: 'POST',
dataType: 'json',
- url: ref.url(ref.env.filedrop.action || 'upload', args),
- contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
+ url: this.url(props.action || 'upload', args),
+ contentType: false,
processData: false,
timeout: 0, // disable default timeout set in ajaxSetup()
- data: formdata || multipart,
- headers: {'X-Roundcube-Request': ref.env.request_token},
- xhr: function() { var xhr = jQuery.ajaxSettings.xhr(); if (!formdata && xhr.sendAsBinary) xhr.send = xhr.sendAsBinary; return xhr; },
- success: function(data){ ref.http_response(data); },
- error: function(o, status, err) { ref.http_error(o, status, err, null, 'attachment'); }
+ data: formdata,
+ headers: {'X-Roundcube-Request': this.env.request_token},
+ xhr: function() {
+ var xhr = $.ajaxSettings.xhr();
+ if (xhr.upload && ref.labels.uploadprogress) {
+ xhr.upload.onprogress = function(e) {
+ var msg = ref.file_upload_msg(e.loaded, e.total);
+ if (msg) {
+ $('#' + ts).find('.uploading').text(msg);
+ }
+ };
+ }
+ return xhr;
+ },
+ success: function(data) {
+ delete ref.uploads[ts];
+ ref.http_response(data);
+ },
+ error: function(o, status, err) {
+ delete ref.uploads[ts];
+ ref.remove_from_attachment_list(ts);
+ ref.http_error(o, status, err, props.lock, 'attachment');
+ }
});
- };
+ }
- // get contents of all dropped files
- var last = this.env.filedrop.single ? 0 : files.length - 1;
- for (var j=0, i=0, f; j <= last && (f = files[i]); i++) {
- if (!f.name) f.name = f.fileName;
- if (!f.size) f.size = f.fileSize;
- if (!f.type) f.type = 'application/octet-stream';
+ return true;
+ };
- // file name contains non-ASCII characters, do UTF8-binary string conversion.
- if (!formdata && /[^\x20-\x7E]/.test(f.name))
- f.name_bin = unescape(encodeURIComponent(f.name));
+ this.file_upload_msg = function(current, total)
+ {
+ if (total && current < total) {
+ var percent = Math.round(current/total * 100),
+ label = ref.get_label('uploadprogress');
- // filter by file type if requested
- if (this.env.filedrop.filter && !f.type.match(new RegExp(this.env.filedrop.filter))) {
- // TODO: show message to user
- continue;
+ if (total >= 1073741824) {
+ total = parseFloat(total/1073741824).toFixed(1) + ' ' . this.get_label('GB');
+ current = parseFloat(current/1073741824).toFixed(1);
}
-
- size += f.size;
- numfiles++;
-
- // do it the easy way with FormData (FF 4+, Chrome 5+, Safari 5+)
- if (formdata) {
- formdata.append(fieldname, f);
- if (j == last)
- return submit_data();
- }
- // use FileReader supporetd by Firefox 3.6
- else if (window.FileReader) {
- var reader = new FileReader();
-
- // closure to pass file properties to async callback function
- reader.onload = (function(file, j) {
- return function(e) {
- multipart += 'Content-Disposition: form-data; name="' + fieldname + '"';
- multipart += '; filename="' + (f.name_bin || file.name) + '"' + crlf;
- multipart += 'Content-Length: ' + file.size + crlf;
- multipart += 'Content-Type: ' + file.type + crlf + crlf;
- multipart += reader.result + crlf;
- multipart += dashdash + boundary + crlf;
-
- if (j == last) // we're done, submit the data
- return submit_data();
- }
- })(f,j);
- reader.readAsBinaryString(f);
+ else if (total >= 1048576) {
+ total = parseFloat(total/1048576).toFixed(1) + ' ' + this.get_label('MB');
+ current = parseFloat(current/1048576).toFixed(1);
}
- // Firefox 3
- else if (f.getAsBinary) {
- multipart += 'Content-Disposition: form-data; name="' + fieldname + '"';
- multipart += '; filename="' + (f.name_bin || f.name) + '"' + crlf;
- multipart += 'Content-Length: ' + f.size + crlf;
- multipart += 'Content-Type: ' + f.type + crlf + crlf;
- multipart += f.getAsBinary() + crlf;
- multipart += dashdash + boundary +crlf;
-
- if (j == last)
- return submit_data();
+ else if (total >= 1024) {
+ total = parseInt(total/1024) + ' ' + this.get_label('KB');
+ current = parseInt(current/1024);
+ }
+ else {
+ total = total + ' ' + this.get_label('B');
}
- j++;
+ return label.replace('$percent', percent + '%').replace('$current', current).replace('$total', total);
}
};
diff --git a/program/js/editor.js b/program/js/editor.js
index 91123defd..191196eb6 100644
--- a/program/js/editor.js
+++ b/program/js/editor.js
@@ -729,7 +729,7 @@ function rcube_text_editor(config, id)
});
// enable drag-n-drop area
- if ((window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary) || window.FormData) {
+ if (window.FormData) {
if (!rcmail.env.filedrop) {
rcmail.env.filedrop = {};
}
diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc
index 2297cc0a8..e5a07217a 100644
--- a/program/steps/mail/attachments.inc
+++ b/program/steps/mail/attachments.inc
@@ -19,11 +19,6 @@
+-----------------------------------------------------------------------+
*/
-// Upload progress update
-if (!empty($_GET['_progress'])) {
- $RCMAIL->upload_progress();
-}
-
$COMPOSE_ID = rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC);
$COMPOSE = null;
diff --git a/program/steps/settings/upload.inc b/program/steps/settings/upload.inc
index 5e91f1d20..01ee433e3 100644
--- a/program/steps/settings/upload.inc
+++ b/program/steps/settings/upload.inc
@@ -19,11 +19,6 @@
+-----------------------------------------------------------------------+
*/
-// Upload progress update
-if (!empty($_GET['_progress'])) {
- $RCMAIL->upload_progress();
-}
-
$from = rcube_utils::get_input_value('_from', rcube_utils::INPUT_GET);
$type = preg_replace('/(add|edit)-/', '', $from);