diff --git a/web_drop_target/README.rst b/web_drop_target/README.rst index 66d085ac..b0d171b5 100644 --- a/web_drop_target/README.rst +++ b/web_drop_target/README.rst @@ -28,7 +28,6 @@ Known issues / Roadmap ====================== * dropping on list or kanban views would be nice too -* handle multiple files * add an upload progress meter for huge files * trigger custom events about different stages of the drop operation for other addons to hook in @@ -57,6 +56,7 @@ Contributors ------------ * Holger Brunn +* Pablo Fuentes Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list `_ or the `appropriate specialized mailinglist `_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues. diff --git a/web_drop_target/__manifest__.py b/web_drop_target/__manifest__.py index 69a5be2a..348157ec 100644 --- a/web_drop_target/__manifest__.py +++ b/web_drop_target/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { "name": "Drop target support", - "version": "10.0.1.0.1", + "version": "10.0.1.1.0", "author": "Therp BV,Odoo Community Association (OCA)", "website": "https://github.com/OCA/web", "license": "AGPL-3", @@ -11,8 +11,12 @@ "summary": "Allows to drag files into Odoo", "depends": [ 'web', + 'document' ], "data": [ 'views/templates.xml', ], + "qweb": [ + 'static/src/xml/widgets.xml', + ] } diff --git a/web_drop_target/readme/CONTRIBUTORS.rst b/web_drop_target/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..001a3cc8 --- /dev/null +++ b/web_drop_target/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Holger Brunn +* Pablo Fuentes +* Akim Juillerat \ No newline at end of file diff --git a/web_drop_target/readme/DESCRIPTION.rst b/web_drop_target/readme/DESCRIPTION.rst new file mode 100644 index 00000000..0701cee0 --- /dev/null +++ b/web_drop_target/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module extends the functionality of the web client to support dropping local files into the web client. + +By default, an attachment will be created when dropping a file on a form. + +Further, this module is meant as a base drag&drop module supporting other actions after some file is dropped so that other modules can add more features. diff --git a/web_drop_target/readme/DEVELOP.rst b/web_drop_target/readme/DEVELOP.rst new file mode 100644 index 00000000..1a531118 --- /dev/null +++ b/web_drop_target/readme/DEVELOP.rst @@ -0,0 +1,4 @@ +Libraries +--------- + +* `base64js `_. \ No newline at end of file diff --git a/web_drop_target/readme/ROADMAP.rst b/web_drop_target/readme/ROADMAP.rst new file mode 100644 index 00000000..cffa9915 --- /dev/null +++ b/web_drop_target/readme/ROADMAP.rst @@ -0,0 +1,5 @@ +* dropping on list or kanban views would be nice too +* handle multiple files +* add an upload progress meter for huge files +* trigger custom events about different stages of the drop operation for other addons to hook in +* Install document module to display attachments in the sidebar \ No newline at end of file diff --git a/web_drop_target/readme/USAGE.rst b/web_drop_target/readme/USAGE.rst new file mode 100644 index 00000000..9db80f99 --- /dev/null +++ b/web_drop_target/readme/USAGE.rst @@ -0,0 +1,7 @@ +To use this module, you need to: + +#. drag a file from your local computer onto an Odoo form view +#. it should become an attachment of the currently opened record + +.. image:: /web_drop_target/static/description/screenshot.png + :alt: Screenshot diff --git a/web_drop_target/static/description/screenshot.png b/web_drop_target/static/description/screenshot.png new file mode 100644 index 00000000..88dc4dce Binary files /dev/null and b/web_drop_target/static/description/screenshot.png differ diff --git a/web_drop_target/static/src/css/web_drop_target.css b/web_drop_target/static/src/css/web_drop_target.css deleted file mode 100644 index e69de29b..00000000 diff --git a/web_drop_target/static/src/js/web_drop_target.js b/web_drop_target/static/src/js/web_drop_target.js index 399bc4e1..24d63bc9 100644 --- a/web_drop_target/static/src/js/web_drop_target.js +++ b/web_drop_target/static/src/js/web_drop_target.js @@ -5,17 +5,18 @@ odoo.define('web_drop_target', function(require) { var Model = require('web.Model'), - FormView = require('web.FormView'); + FormView = require('web.FormView'), + core = require('web.core'); + var qweb = core.qweb; // this is the main contribution of this addon: A mixin you can use // to make some widget a drop target. Read on how to use this yourself var DropTargetMixin = { // add the mime types you want to support here, leave empty for - // all types. For more control, override _get_drop_item in your class + // all types. For more control, override _get_drop_items in your class _drop_allowed_types: [], - // a class being applied when the user drags something we can handle - _drag_over_class: 'o_drag_over', + _drop_overlay: null, start: function() { var result = this._super.apply(this, arguments); @@ -27,68 +28,60 @@ odoo.define('web_drop_target', function(require) { }, _on_drop: function(e) { - var drop_item = this._get_drop_item(e); - if(!drop_item || !(drop_item.getAsFile() instanceof Blob)) { + var drop_items = this._get_drop_items(e); + e.preventDefault(); + this._remove_overlay(); + if(!drop_items) { return; } - jQuery(e.delegateTarget).removeClass(this._drag_over_class); - var reader = new FileReader(); - reader.onloadend = this.proxy( - _.partial(this._handle_file_drop, drop_item.getAsFile()) - ); - reader.readAsArrayBuffer(drop_item.getAsFile()); - e.stopPropagation(); - e.preventDefault(); + this._handle_drop_items(drop_items, e) }, _on_dragenter: function(e) { - if(this._get_drop_item(e)) { - e.preventDefault(); - jQuery(e.delegateTarget).addClass(this._drag_over_class); - return false; - } + e.preventDefault(); + this._add_overlay(); + return false; }, _on_dragleave: function(e) { - jQuery(e.delegateTarget).removeClass(this._drag_over_class); + this._remove_overlay(); + e.preventDefault(); }, - _get_drop_item: function(e) { + _get_drop_items: function(e) { var self = this, dataTransfer = e.originalEvent.dataTransfer, - drop_item = null; - _.each(dataTransfer.items, function(item) { + drop_items = []; + _.each(dataTransfer.files, function(item) { if( _.contains(self._drop_allowed_types, item.type) || _.isEmpty(self._drop_allowed_types) ) { - drop_item = item; + drop_items.push(item); } }); - return drop_item; + return drop_items; }, // eslint-disable-next-line no-unused-vars - _handle_file_drop: function(drop_file, e) { + _handle_drop_items: function(drop_items, e) { // do something here, for example call the helper function below // e is the on_load_end handler for the FileReader above, // so e.target.result contains an ArrayBuffer of the data }, - _handle_file_drop_attach: function( - drop_file, e, res_model, res_id, extra_data - ) { + _create_attachment: function(file, reader, e, res_model, res_id, extra_data) { // helper to upload an attachment and update the sidebar var self = this; return new Model('ir.attachment').call( 'create', [ _.extend({ - name: drop_file.name, + name: file.name, datas: base64js.fromByteArray( - new Uint8Array(e.target.result) + new Uint8Array(reader.result) ), - datas_fname: drop_file.name, + datas_fname: file.name, res_model: res_model, res_id: res_id, }, extra_data || {}) @@ -107,6 +100,51 @@ odoo.define('web_drop_target', function(require) { ); } }); + }, + + _file_reader_error_handler: function(e){ + console.error(e); + }, + + _handle_file_drop_attach: function( + item, e, res_model, res_id, extra_data + ) { + var self = this; + var file = item; + if(!file || !(file instanceof Blob)) { + return; + } + var reader = new FileReader(); + reader.onloadend = self.proxy( + _.partial(self._create_attachment, file, reader, e, res_model, res_id, extra_data) + ); + reader.onerror = self.proxy('_file_reader_error_handler'); + reader.readAsArrayBuffer(file); + }, + + _add_overlay: function() { + if(!this._drop_overlay){ + var o_content = jQuery('.o_content'), + view_manager = jQuery('.o_view_manager_content'); + this._drop_overlay = jQuery( + qweb.render('web_drop_target.drop_overlay') + ); + var o_content_position = o_content.position(); + this._drop_overlay.css({ + 'top': o_content_position.top, + 'left': o_content_position.left, + 'width': view_manager.width(), + 'height': view_manager.height() + }); + o_content.append(this._drop_overlay); + } + }, + + _remove_overlay: function() { + if (this._drop_overlay) { + this._drop_overlay.remove(); + this._drop_overlay = null; + } } }; @@ -120,10 +158,13 @@ odoo.define('web_drop_target', function(require) { } return this._super.apply(this, arguments); }, - _handle_file_drop: function(drop_file, e) { - return this._handle_file_drop_attach( - drop_file, e, this.dataset.model, this.datarecord.id - ); + _handle_drop_items: function(drop_items, e) { + var self = this; + _.each(drop_items, function(item, e) { + return self._handle_file_drop_attach( + item, e, self.dataset.model, self.datarecord.id + ); + }); } })); diff --git a/web_drop_target/static/src/less/web_drop_target.less b/web_drop_target/static/src/less/web_drop_target.less new file mode 100644 index 00000000..eb0f7521 --- /dev/null +++ b/web_drop_target/static/src/less/web_drop_target.less @@ -0,0 +1,19 @@ +.o_content { + .o_drag_over{ + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255,255,255,0.6); + border: 1px dashed #4c4c4c; + pointer-events: none; + .o_drag_over_content{ + position: relative; + top: 50%; + transform: translate(0%, -50%); + width: 100%; + text-align: center; + } + } +} \ No newline at end of file diff --git a/web_drop_target/static/src/xml/widgets.xml b/web_drop_target/static/src/xml/widgets.xml new file mode 100644 index 00000000..50142051 --- /dev/null +++ b/web_drop_target/static/src/xml/widgets.xml @@ -0,0 +1,17 @@ + + + + diff --git a/web_drop_target/views/templates.xml b/web_drop_target/views/templates.xml index 6021a21e..7fc15344 100644 --- a/web_drop_target/views/templates.xml +++ b/web_drop_target/views/templates.xml @@ -5,7 +5,7 @@ - +