From 9ede2c0b70185b4ce0faf30571014b324a9b6b22 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 7 Aug 2015 14:18:07 +0000 Subject: [PATCH] WIP image upload via drag into ckeditor 4.5 --- .../inc/class.etemplate_widget_vfs.inc.php | 52 ++++++++++- etemplate/js/et2_widget_file.js | 88 +++++++++---------- etemplate/js/et2_widget_htmlarea.js | 39 +++++--- resources/templates/default/edit.xet | 4 +- 4 files changed, 123 insertions(+), 60 deletions(-) diff --git a/etemplate/inc/class.etemplate_widget_vfs.inc.php b/etemplate/inc/class.etemplate_widget_vfs.inc.php index a88b267f2c..6780f88e72 100644 --- a/etemplate/inc/class.etemplate_widget_vfs.inc.php +++ b/etemplate/inc/class.etemplate_widget_vfs.inc.php @@ -77,6 +77,54 @@ class etemplate_widget_vfs extends etemplate_widget_file } } + /** + * Upload via dragging images into ckeditor + */ + public static function ajax_htmlarea_upload() + { + $request_id = str_replace(' ', '+', rawurldecode($_REQUEST['request_id'])); + $widget_id = $_REQUEST['widget_id']; + if(!self::$request = etemplate_request::read($request_id)) + { + $error = lang("Could not read session"); + } + elseif (!($template = etemplate_widget_template::instance(self::$request->template['name'], self::$request->template['template_set'], + self::$request->template['version'], self::$request->template['load_via']))) + { + // Can't use callback + $error = lang("Could not get template for file upload, callback skipped"); + } + /*elseif (!($widget = $template->getElementById($widget_id))) + { + $error = "Widget '$widget_id' not found!"; + }*/ + elseif (!isset($_FILES['upload'])) + { + $error = lang('No _FILES[upload] found!'); + } + else + { + $data = self::$request->content[$widget_id]; + $path = self::store_file($path = self::get_vfs_path($data['to_app'].':'.$data['to_id']).'/', $_FILES['upload']); + } + // switch regular JSON response handling off + egw_json_request::isJSONRequest(false); + + $file = array( + "uploaded" => (int)empty($error), + "fileName" => html::htmlspecialchars($_FILES['upload']['name']), + "url" => egw::link(egw_vfs::download_url($path)), + "error" => array( + "message" => $error, + ) + ); + + header('Content-Type: application/json; charset=utf-8'); + echo json_encode($file); + + common::egw_exit(); + } + /** * Ajax callback to receive an incoming file * @@ -91,7 +139,7 @@ class etemplate_widget_vfs extends etemplate_widget_file // Find real path if($path[0] != '/') { - $path = self::get_vfs_path($path, $file['name']); + $path = self::get_vfs_path($path); } $filename = $file['name']; if (substr($path,-1) != '/') @@ -120,6 +168,8 @@ class etemplate_widget_vfs extends etemplate_widget_file // Try to remove temp file unlink($file['tmp_name']); + + return $path; } /** diff --git a/etemplate/js/et2_widget_file.js b/etemplate/js/et2_widget_file.js index 3cf1e6349e..8bed84eb03 100644 --- a/etemplate/js/et2_widget_file.js +++ b/etemplate/js/et2_widget_file.js @@ -19,9 +19,9 @@ /** * Class which implements file upload - * + * * @augments et2_inputWidget - */ + */ var et2_file = et2_inputWidget.extend( { attributes: { @@ -90,14 +90,14 @@ var et2_file = et2_inputWidget.extend( "type": "any", "default": et2_no_init, "description": "A (js) function called when a file to be uploaded is finished." - }, + } }, asyncOptions: {}, /** * Constructor - * + * * @memberOf et2_file */ init: function() { @@ -121,19 +121,19 @@ var et2_file = et2_inputWidget.extend( // Set up the URL to have the request ID & the widget ID var instance = this.getInstanceManager(); - + var self = this; this.asyncOptions = jQuery.extend({ // Callbacks - onStart: function(event, file_count) { - return self.onStart(event, file_count); + onStart: function(event, file_count) { + return self.onStart(event, file_count); }, - onFinish: function(event, file_count) { + onFinish: function(event, file_count) { self.onFinish.apply(self, [event, file_count]) }, - onStartOne: function(event, file_name, index, file_count) { - + onStartOne: function(event, file_name, index, file_count) { + }, onFinishOne: function(event, response, name, number, total) { return self.finishUpload(event,response,name,number,total);}, onProgress: function(event, progress, name, number, total) { return self.onProgress(event,progress,name,number,total);}, @@ -158,7 +158,7 @@ var et2_file = et2_inputWidget.extend( this.span = null; this.progress = null; }, - + createInputWidget: function() { this.node = $j(document.createElement("div")).addClass("et2_file"); this.span = $j(document.createElement("span")) @@ -172,7 +172,7 @@ var et2_file = et2_inputWidget.extend( .hover(function(e){ $j(span) .toggleClass('et2_file_spanHover'); - }) + }) .on({ mousedown:function (e){ $j(span).addClass('et2_file_spanActive'); @@ -218,14 +218,14 @@ var et2_file = et2_inputWidget.extend( this.setDOMNode(this.node[0]); }, - + /** * Set a widget or DOM node as a HTML5 file drop target - * + * * @param String new_target widget ID or DOM node ID to be used as a new target */ set_drop_target: function(new_target) - { + { // Cancel old drop target if(this.options.drop_target) { @@ -236,11 +236,11 @@ var et2_file = et2_inputWidget.extend( this.resumable.unAssignDrop(drop_target); } } - + this.options.drop_target = new_target; - + if(!this.options.drop_target) return; - + // Set up new drop target var widget = this.getRoot().getWidgetById(this.options.drop_target); var drop_target = widget && widget.getDOMNode() || document.getElementById(this.options.drop_target); @@ -265,10 +265,10 @@ var et2_file = et2_inputWidget.extend( }, /** - * Set the value of the file widget. - * + * Set the value of the file widget. + * * If you pass a FileList or list of files, it will trigger the async upload - * + * * @param {FileList|File[]|false} value List of files to be uploaded, or false to reset. * @param {Event} event Most browsers require the user to initiate file transfers in some way. * Pass the event in, if you have it. @@ -289,7 +289,7 @@ var et2_file = et2_inputWidget.extend( return; } - + if(typeof value == 'object' && value.length && typeof value[0] == 'object' && value[0].name) { try @@ -304,11 +304,11 @@ var et2_file = et2_inputWidget.extend( } } }, - + /** * Set the value for label * The label is used as caption for span tag which customize the HTML file upload styling - * + * * @param {string} value text value of label */ set_label: function (value) @@ -402,13 +402,13 @@ var et2_file = et2_inputWidget.extend( } }, - + /** * Add in the request id */ beforeSend: function(form) { var instance = this.getInstanceManager(); - + return { request_id: instance.etemplate_exec_id, widget_id: this.id @@ -431,13 +431,13 @@ var et2_file = et2_inputWidget.extend( .css('cursor', 'default'); event.data = this; - + //Add dropdown_progress if (this.options.progress_dropdownlist) { this._build_progressDropDownList(); } - + // Callback if(this.options.onStart) return et2_call(this.options.onStart, event, file_count); return true; @@ -448,7 +448,7 @@ var et2_file = et2_inputWidget.extend( */ onFinish: function() { this.disabled_buttons.attr("disabled", false).css('cursor','pointer'); - + var file_count = this.resumable.files.length; // Remove files from list @@ -458,19 +458,19 @@ var et2_file = et2_inputWidget.extend( } var event = jQuery.Event('upload'); - + event.data = this; var result = false; - + //Remove progress_dropDown_fileList class and unbind the click handler from body if (this.options.progress_dropdownlist) { this.progress.removeClass("progress_dropDown_fileList"); jQuery(this.node).find('span').removeClass('totalProgress_loader'); jQuery('body').off('click'); - } - + } + if(this.options.onFinish && !jQuery.isEmptyObject(this.getValue())) { result = et2_call(this.options.onFinish, event, file_count); @@ -485,19 +485,19 @@ var et2_file = et2_inputWidget.extend( this.change(this.input); } }, - + /** * Build up dropdown progress with total count indicator - * + * * @todo Implement totalProgress bar instead of ajax-loader, in order to show how much percent of uploading is completed */ _build_progressDropDownList: function () { this.progress.addClass("progress_dropDown_fileList"); - + //Add uploading indicator and bind hover handler on it jQuery(this.node).find('span').addClass('totalProgress_loader'); - + jQuery(this.node).find('input').hover(function(){ jQuery('.progress_dropDown_fileList').show(); }); @@ -506,11 +506,11 @@ var et2_file = et2_inputWidget.extend( if (event.target.className != 'remove') { jQuery('.progress_dropDown_fileList').hide(); - } + } }); - + }, - + /** * Creates the elements used for displaying the file, and it's upload status, and * attaches them to the DOM @@ -523,7 +523,7 @@ var et2_file = et2_inputWidget.extend( if(this.options.max_file_size && file.size > this.options.max_file_size) { error = this.egw().lang("File too large. Maximum %1", et2_vfsSize.prototype.human_size(this.options.max_file_size)); } - + if(this.options.progress) { var widget = this.getRoot().getWidgetById(this.options.progress); @@ -599,9 +599,9 @@ var et2_file = et2_inputWidget.extend( .text(this.egw().lang("Server error")); } var event = jQuery.Event('upload'); - + event.data = this; - + // Callback if(this.options.onFinishOne) { @@ -642,7 +642,7 @@ var et2_file = et2_inputWidget.extend( e.preventDefault(); // Look for file name in list var target = $j(e.target).parents("li"); - + this.remove_file(e.data); // In case it didn't make it to the list (error) diff --git a/etemplate/js/et2_widget_htmlarea.js b/etemplate/js/et2_widget_htmlarea.js index a22875fb1f..18d4a4683c 100644 --- a/etemplate/js/et2_widget_htmlarea.js +++ b/etemplate/js/et2_widget_htmlarea.js @@ -71,11 +71,11 @@ var et2_htmlarea = et2_inputWidget.extend([et2_IResizeable], type: "html", // "string" would remove html tags by running html_entity_decode default: et2_no_init }, - imageDataUrl: { - name: "imageDataUrl", - description: "Allow images dragged in as data-url, default false = handle them as fileupload", - type: "boolean", - default: false + imageUpload: { + name: "imageUpload", + description: "Url to upload images dragged in or id of link_to widget to it's vfs upload", + type: "string", + default: null } }, @@ -123,6 +123,20 @@ var et2_htmlarea = et2_inputWidget.extend([et2_IResizeable], if(this.mode == 'ascii' || this.ckeditor != null) return; var self = this; + if (!this.options.imageUpload) + { + delete self.options.config.imageUploadUrl; + } + else if (this.options.imageUpload[0] !== '/' && this.options.imageUpload.substr(0, 4) != 'http') + { + self.options.config.imageUploadUrl = egw.ajaxUrl(self.egw().getAppName()+".etemplate_widget_vfs.ajax_htmlarea_upload.etemplate")+ + '&request_id='+self.getInstanceManager().etemplate_exec_id+'&widget_id='+this.options.imageUpload; + self.options.config.imageUploadUrl = self.options.config.imageUploadUrl.substr(egw.webserverUrl.length+1); + } + else + { + self.options.config.imageUploadUrl = this.options.imageUpload.substr(egw.webserverUrl.length+1); + } try { this.ckeditor = CKEDITOR.replace(this.dom_id,jQuery.extend({},this.options.config,this.options)); @@ -172,14 +186,13 @@ var et2_htmlarea = et2_inputWidget.extend([et2_IResizeable], return; // allow data-URL, returning false to stop regular upload - if (self.options.imageDataUrl) + if (!self.options.imageUpload) { - return false; + // Remove the image from the text + setTimeout( function() { + editor.document.$.body.innerHTML = replaceImgText(editor.document.$.body.innerHTML); + },200); } - // Remove the image from the text - setTimeout( function() { - editor.document.$.body.innerHTML = replaceImgText(editor.document.$.body.innerHTML); - },200); // Try to pass the image into the first et2_file that will accept it if(e.data.$.dataTransfer) @@ -274,7 +287,7 @@ var et2_htmlarea = et2_inputWidget.extend([et2_IResizeable], var h = 0; if (typeof this.ckeditor.container !='undefined' && this.ckeditor.container.$.clientHeight > 0) { - h = (this.ckeditor.container.$.clientHeight + _height) > 0 ? + h = (this.ckeditor.container.$.clientHeight + _height) > 0 ? this.ckeditor.container.$.clientHeight + _height: this.ckeditor.config.height; } else if (this.ckeditor.ui.space('contents')) @@ -289,7 +302,7 @@ var et2_htmlarea = et2_inputWidget.extend([et2_IResizeable], this.ckeditor.resize('',h); } else // No CKEDITOR - { + { this.htmlNode.height(this.htmlNode.height() + _height); } } diff --git a/resources/templates/default/edit.xet b/resources/templates/default/edit.xet index f3296929e0..f18d1b5726 100644 --- a/resources/templates/default/edit.xet +++ b/resources/templates/default/edit.xet @@ -79,7 +79,7 @@ - + @@ -105,7 +105,7 @@ - +