diff --git a/setup/web_drop_target/odoo/addons/web_drop_target b/setup/web_drop_target/odoo/addons/web_drop_target new file mode 120000 index 000000000..58aaa3635 --- /dev/null +++ b/setup/web_drop_target/odoo/addons/web_drop_target @@ -0,0 +1 @@ +../../../../web_drop_target \ No newline at end of file diff --git a/setup/web_drop_target/setup.py b/setup/web_drop_target/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/web_drop_target/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/web_drop_target/README.rst b/web_drop_target/README.rst new file mode 100644 index 000000000..5311ca60e --- /dev/null +++ b/web_drop_target/README.rst @@ -0,0 +1,107 @@ +=================== +Drop target support +=================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/12.0/web_drop_target + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_drop_target + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/162/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +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. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +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:: https://raw.githubusercontent.com/web_drop_target/static/description/screenshot.png + :alt: Screenshot + +Development +=========== + +**Libraries** + +* `base64js `_. + +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 +* Install document module to display attachments in the sidebar + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Therp BV + +Contributors +~~~~~~~~~~~~ + +* Holger Brunn +* Pablo Fuentes +* Akim Juillerat +* Enric Tobella + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_drop_target/__init__.py b/web_drop_target/__init__.py new file mode 100644 index 000000000..f7c59d20c --- /dev/null +++ b/web_drop_target/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). diff --git a/web_drop_target/__manifest__.py b/web_drop_target/__manifest__.py new file mode 100644 index 000000000..af0da347d --- /dev/null +++ b/web_drop_target/__manifest__.py @@ -0,0 +1,14 @@ +# Copyright 2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "Drop target support", + "version": "13.0.1.0.0", + "author": "Therp BV,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/web", + "license": "AGPL-3", + "category": "Usability", + "summary": "Allows to drag files into Odoo", + "depends": ["web"], + "data": ["views/templates.xml"], + "qweb": ["static/src/xml/widgets.xml"], +} diff --git a/web_drop_target/i18n/fr.po b/web_drop_target/i18n/fr.po new file mode 100644 index 000000000..93cb2f33a --- /dev/null +++ b/web_drop_target/i18n/fr.po @@ -0,0 +1,24 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_drop_target +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-07-10 14:31+0000\n" +"PO-Revision-Date: 2018-07-10 14:31+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: web_drop_target +#. openerp-web +#: code:addons/web_drop_target/static/src/xml/widgets.xml:11 +#, python-format +msgid "Drop your files here" +msgstr "Déposez les fichiers ici" diff --git a/web_drop_target/i18n/web_drop_target.pot b/web_drop_target/i18n/web_drop_target.pot new file mode 100644 index 000000000..66924ca08 --- /dev/null +++ b/web_drop_target/i18n/web_drop_target.pot @@ -0,0 +1,22 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_drop_target +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: web_drop_target +#. openerp-web +#: code:addons/web_drop_target/static/src/xml/widgets.xml:11 +#, python-format +msgid "Drop your files here" +msgstr "" + diff --git a/web_drop_target/readme/CONTRIBUTORS.rst b/web_drop_target/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..4230b091a --- /dev/null +++ b/web_drop_target/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* Holger Brunn +* Pablo Fuentes +* Akim Juillerat +* Enric Tobella diff --git a/web_drop_target/readme/DESCRIPTION.rst b/web_drop_target/readme/DESCRIPTION.rst new file mode 100644 index 000000000..0701cee01 --- /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 000000000..b91df8e2d --- /dev/null +++ b/web_drop_target/readme/DEVELOP.rst @@ -0,0 +1,3 @@ +**Libraries** + +* `base64js `_. diff --git a/web_drop_target/readme/ROADMAP.rst b/web_drop_target/readme/ROADMAP.rst new file mode 100644 index 000000000..486912d63 --- /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 diff --git a/web_drop_target/readme/USAGE.rst b/web_drop_target/readme/USAGE.rst new file mode 100644 index 000000000..9db80f992 --- /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/icon.png b/web_drop_target/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/web_drop_target/static/description/icon.png differ diff --git a/web_drop_target/static/description/index.html b/web_drop_target/static/description/index.html new file mode 100644 index 000000000..971f87f53 --- /dev/null +++ b/web_drop_target/static/description/index.html @@ -0,0 +1,453 @@ + + + + + + +Drop target support + + + +
+

Drop target support

+ + +

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runbot

+

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.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  1. drag a file from your local computer onto an Odoo form view
  2. +
  3. it should become an attachment of the currently opened record
  4. +
+Screenshot +
+
+

Development

+

Libraries

+ +
+
+

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
  • +
  • Install document module to display attachments in the sidebar
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Therp BV
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_drop_target/static/description/screenshot.png b/web_drop_target/static/description/screenshot.png new file mode 100644 index 000000000..88dc4dced Binary files /dev/null and b/web_drop_target/static/description/screenshot.png differ diff --git a/web_drop_target/static/lib/base64js.min.js b/web_drop_target/static/lib/base64js.min.js new file mode 100644 index 000000000..8b055fb56 --- /dev/null +++ b/web_drop_target/static/lib/base64js.min.js @@ -0,0 +1 @@ +(function(r){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=r()}else if(typeof define==="function"&&define.amd){define([],r)}else{var e;if(typeof window!=="undefined"){e=window}else if(typeof global!=="undefined"){e=global}else if(typeof self!=="undefined"){e=self}else{e=this}e.base64js=r()}})(function(){var r,e,n;return function(){function r(e,n,t){function o(i,a){if(!n[i]){if(!e[i]){var u=typeof require=="function"&&require;if(!a&&u)return u(i,!0);if(f)return f(i,!0);var d=new Error("Cannot find module '"+i+"'");throw d.code="MODULE_NOT_FOUND",d}var c=n[i]={exports:{}};e[i][0].call(c.exports,function(r){var n=e[i][1][r];return o(n?n:r)},c,c.exports,r,e,n,t)}return n[i].exports}var f=typeof require=="function"&&require;for(var i=0;i0){throw new Error("Invalid string. Length must be a multiple of 4")}return r[e-2]==="="?2:r[e-1]==="="?1:0}function c(r){return r.length*3/4-d(r)}function v(r){var e,n,t,i,a;var u=r.length;i=d(r);a=new f(u*3/4-i);n=i>0?u-4:u;var c=0;for(e=0;e>16&255;a[c++]=t>>8&255;a[c++]=t&255}if(i===2){t=o[r.charCodeAt(e)]<<2|o[r.charCodeAt(e+1)]>>4;a[c++]=t&255}else if(i===1){t=o[r.charCodeAt(e)]<<10|o[r.charCodeAt(e+1)]<<4|o[r.charCodeAt(e+2)]>>2;a[c++]=t>>8&255;a[c++]=t&255}return a}function l(r){return t[r>>18&63]+t[r>>12&63]+t[r>>6&63]+t[r&63]}function h(r,e,n){var t;var o=[];for(var f=e;fd?d:u+a))}if(o===1){e=r[n-1];f+=t[e>>2];f+=t[e<<4&63];f+="=="}else if(o===2){e=(r[n-2]<<8)+r[n-1];f+=t[e>>10];f+=t[e>>4&63];f+=t[e<<2&63];f+="="}i.push(f);return i.join("")}},{}]},{},[])("/")}); diff --git a/web_drop_target/static/src/js/web_drop_target.js b/web_drop_target/static/src/js/web_drop_target.js new file mode 100644 index 000000000..c4acc06cc --- /dev/null +++ b/web_drop_target/static/src/js/web_drop_target.js @@ -0,0 +1,190 @@ +// Copyright 2018 Therp BV +// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +/* global Uint8Array base64js*/ + +odoo.define("web_drop_target", function(require) { + "use strict"; + + var FormController = require("web.FormController"); + var 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_items in your class + _drop_allowed_types: [], + + _drop_overlay: null, + + start: function() { + var result = this._super.apply(this, arguments); + this.$el.on("drop.widget_events", this.proxy("_on_drop")); + this.$el.on("dragenter.widget_events", this.proxy("_on_dragenter")); + this.$el.on("dragover.widget_events", this.proxy("_on_dragenter")); + this.$el.on("dragleave.widget_events", this.proxy("_on_dragleave")); + return result; + }, + + _on_drop: function(e) { + if (!this._drop_overlay) { + return; + } + var drop_items = this._get_drop_items(e); + e.preventDefault(); + this._remove_overlay(); + if (!drop_items) { + return; + } + this._handle_drop_items(drop_items, e); + }, + + _on_dragenter: function(e) { + e.preventDefault(); + this._add_overlay(); + return false; + }, + + _on_dragleave: function(e) { + this._remove_overlay(); + e.preventDefault(); + }, + + _get_drop_items: function(e) { + if (this.renderer.state.res_id) { + var self = this, + dataTransfer = e.originalEvent.dataTransfer, + drop_items = []; + _.each(dataTransfer.files, function(item) { + if ( + _.contains(self._drop_allowed_types, item.type) || + _.isEmpty(self._drop_allowed_types) + ) { + drop_items.push(item); + } + }); + return drop_items; + } + return null; + }, + + // eslint-disable-next-line no-unused-vars + _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 + }, + + _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 this._rpc({ + model: "ir.attachment", + method: "create", + args: [ + _.extend( + { + name: file.name, + datas: base64js.fromByteArray( + new Uint8Array(reader.result) + ), + res_model: res_model, + res_id: res_id, + }, + extra_data || {} + ), + ], + }).then(function() { + // Find the chatter among the children, there should be only + // one + var res = _.filter(self.getChildren(), "chatter"); + if (res.length) { + res[0].chatter._reloadAttachmentBox(); + res[0].chatter.trigger_up("reload"); + res[0].chatter.$(".o_chatter_button_attachment").click(); + } + }); + }, + + _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() { + var self = this; + 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", { + id: self.renderer.state.res_id, + }) + ); + 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(), + }); + if (!this.renderer.state.res_id) { + this._drop_overlay.css("background", "#FF000020"); + } + o_content.append(this._drop_overlay); + } + }, + + _remove_overlay: function() { + if (this._drop_overlay) { + this._drop_overlay.remove(); + this._drop_overlay = null; + } + }, + }; + + // And here we apply the mixin to form views, allowing any files and + // adding them as attachment + FormController.include( + _.extend(DropTargetMixin, { + // eslint-disable-next-line no-unused-vars + _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.renderer.state.model, + self.renderer.state.res_id + ); + }); + }, + }) + ); + + return { + DropTargetMixin: DropTargetMixin, + }; +}); 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 000000000..e0b5140e7 --- /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; + } + } +} 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 000000000..df2b85889 --- /dev/null +++ b/web_drop_target/static/src/xml/widgets.xml @@ -0,0 +1,20 @@ + + diff --git a/web_drop_target/views/templates.xml b/web_drop_target/views/templates.xml new file mode 100644 index 000000000..de2bfd7cb --- /dev/null +++ b/web_drop_target/views/templates.xml @@ -0,0 +1,23 @@ + + +