summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Machniak <alec@alec.pl>2013-11-08 12:05:43 (GMT)
committerAleksander Machniak <alec@alec.pl>2013-11-08 12:05:43 (GMT)
commit7ecb80d380346e0204828d568834019c49c598a0 (patch)
treecd0b34ace7c0bd81099a67433a3236f424be6009
parentb5fe149a3c903f13cb0a14000d4f1ba2f6edf94e (diff)
downloadkolab-chwala-7ecb80d380346e0204828d568834019c49c598a0.tar.gz
Add possibility to save (update) binary files (prepared for webodf editor)
-rw-r--r--lib/file_api.php49
-rw-r--r--public_html/js/files_api.js100
-rw-r--r--public_html/js/files_ui.js28
3 files changed, 160 insertions, 17 deletions
diff --git a/lib/file_api.php b/lib/file_api.php
index eaa4dc6..da6e6d3 100644
--- a/lib/file_api.php
+++ b/lib/file_api.php
@@ -335,14 +335,29 @@ class file_api
if (!isset($args['file']) || $args['file'] === '') {
throw new Exception("Missing file name", file_api::ERROR_CODE);
}
- if (!isset($args['content'])) {
+
+ // file content might be in a request argument or uploaded file
+ if (!empty($_FILES['content'])) {
+ $files = $this->upload('content');
+
+ if (count($files) != 1) {
+ throw new Exception("Missing file content", file_api::ERROR_CODE);
+ }
+
+ $file = array(
+ 'path' => $files[0]['path'],
+ 'type' => $files[0]['type'],
+ );
+ }
+ else if (!isset($args['content'])) {
throw new Exception("Missing file content", file_api::ERROR_CODE);
}
-
- $file = array(
- 'content' => $args['content'],
- 'type' => rcube_mime::file_content_type($args['content'], $args['file'], $args['content-type'], true),
- );
+ else {
+ $file = array(
+ 'content' => $args['content'],
+ 'type' => rcube_mime::file_content_type($args['content'], $args['file'], $args['content-type'], true),
+ );
+ }
$this->api->$request($args['file'], $file);
@@ -511,25 +526,33 @@ class file_api
/**
* File uploads handler
+ *
+ * @param string $fieldname Name of the for field ($_FILES index)
*/
- protected function upload()
+ protected function upload($fieldname = 'file')
{
$files = array();
- if (is_array($_FILES['file']['tmp_name'])) {
- foreach ($_FILES['file']['tmp_name'] as $i => $filepath) {
- if ($err = $_FILES['file']['error'][$i]) {
- if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
+ if (!empty($_FILES[$fieldname]['tmp_name'])) {
+ foreach ((array)$_FILES[$fieldname]['tmp_name'] as $i => $filepath) {
+ // looks strange? we support single and miltiple fields
+ $error = is_array($_FILES[$fieldname]['error']) ? $_FILES[$fieldname]['error'][$i] : $_FILES[$fieldname]['error'];
+
+ if ($error) {
+ if ($error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE) {
throw new Exception("Maximum file size exceeded", file_api::ERROR_CODE);
}
throw new Exception("File upload failed", file_api::ERROR_CODE);
}
+ $filename = is_array($_FILES[$fieldname]['name']) ? $_FILES[$fieldname]['name'][$i] : $_FILES[$fieldname]['name'];
+ $filetype = is_array($_FILES[$fieldname]['type']) ? $_FILES[$fieldname]['type'][$i] : $_FILES[$fieldname]['type'];
+
$files[] = array(
'path' => $filepath,
- 'name' => $_FILES['file']['name'][$i],
+ 'name' => $filename,
'size' => filesize($filepath),
- 'type' => rcube_mime::file_content_type($filepath, $_FILES['file']['name'][$i], $_FILES['file']['type']),
+ 'type' => rcube_mime::file_content_type($filepath, $filename, $filetype),
);
}
}
diff --git a/public_html/js/files_api.js b/public_html/js/files_api.js
index 721fc54..1c7a733 100644
--- a/public_html/js/files_api.js
+++ b/public_html/js/files_api.js
@@ -501,6 +501,106 @@ function files_api()
return (new Date(1970, 1, 1, 0, 0, s, 0)).toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1');
};
+
+ // file uploader
+ this.file_uploader = function(e, config)
+ {
+ if (!config)
+ config = this.env.filedrop;
+
+ // prepare multipart form data composition
+ var files = $.isArray(e) ? e : e.target.files || e.dataTransfer.files,
+ formdata = window.FormData ? new FormData() : null,
+ fieldname = (config.fieldname || '_file') + (config.single ? '' : '[]'),
+ boundary = '------multipartformboundary' + (new Date).getTime(),
+ dashdash = '--', crlf = '\r\n',
+ multipart = dashdash + boundary + crlf;
+
+ if (!files || !files.length)
+ return;
+
+ // inline function to submit the files to the server
+ var submit_data = function() {
+ var multiple = files.length > 1,
+ ts = new Date().getTime(),
+ url_params = $.extend({uploadid: ts}, config.params || {});
+
+ // complete multipart content and post request
+ multipart += dashdash + boundary + dashdash + crlf;
+
+ $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: ref.env.url + ref.url(config.action || 'upload', url_params),
+ contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
+ processData: false,
+ timeout: 0, // disable default timeout set in ajaxSetup()
+ data: formdata || multipart,
+ xhr: function() { var xhr = jQuery.ajaxSettings.xhr(); if (!formdata && xhr.sendAsBinary) xhr.send = xhr.sendAsBinary; return xhr; },
+ success: function(data) { config.response_handler ? ref[config.response_handler](data) : ref.response(data); },
+ error: function(o, status, err) { ref.http_error(o, status, err, null, 'attachment'); }
+ });
+ };
+
+ // get contents of all dropped files
+ var last = config.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';
+
+ // 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));
+
+ // 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);
+ }
+ // 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();
+ }
+
+ j++;
+ }
+ };
+
+ // check file uploading support
+ this.file_uploader_support = function()
+ {
+ return window.FormData || (window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary);
+ };
};
// Add escape() method to RegExp object
diff --git a/public_html/js/files_ui.js b/public_html/js/files_ui.js
index f4410e6..92d20d3 100644
--- a/public_html/js/files_ui.js
+++ b/public_html/js/files_ui.js
@@ -1550,13 +1550,33 @@ function files_ui()
if (!this.file_editor)
return;
- var content = this.file_editor.getContent();
+ // binary files like ODF need to be updated using FormData
+ if (this.file_editor.getContentCallback) {
+ if (!this.file_uploader_support())
+ return;
+
+ this.set_busy(true, 'saving');
+
+ this.file_editor.disable();
+ this.file_editor.getContentCallback(function(content, filename) {
+ ui.file_uploader([content], {
+ action: 'file_update',
+ params: {file: ui.env.file, info: 1},
+ response_handler: 'file_save_response',
+ fieldname: 'content',
+ single: true
+ });
+ });
+
+ return;
+ }
+
+ this.set_busy(true, 'saving');
this.file_editor.disable();
- // because we currently can edit only text file
- // and we do not expect them to be very big, we save
+ // we do not expect text files to be very big, we save
// file in a very simple way, no upload progress, etc.
- this.set_busy(true, 'saving');
+ var content = this.file_editor.getContent();
this.request('file_update', {file: this.env.file, content: content, info: 1}, 'file_save_response');
};