Browse Source
[10.0][ADD][web_chatter_paste] (#548)
[10.0][ADD][web_chatter_paste] (#548)
* Initial commit * Initial commit * [ADD] Tests * [FIX] Dependencies * [REM] Controller tests * [FIX] Removed unnecessary incompatibility warning * [FIX] Hyperlink to issues * [ADD] Tests * [FIX] Tests * [?] Testing test problem * [?] Testing test problem * [?] Testing test problem * [ADD] web_chatter_paste Initial commit Initial commit [ADD] Tests [FIX] Dependencies [REM] Controller tests [FIX] Removed unnecessary incompatibility warning [FIX] Hyperlink to issues [ADD] Tests [FIX] Tests [?] Testing test problem [?] Testing test problem [?] Testing test problem * Testing problem * Testing problem * Testing problem * [ADD] Mock for http.request * [FIX] Use patch to mockpull/589/head
Dennis Sluijk
8 years ago
committed by
Dave Lasley
9 changed files with 304 additions and 0 deletions
-
67web_chatter_paste/README.rst
-
5web_chatter_paste/__init__.py
-
24web_chatter_paste/__manifest__.py
-
5web_chatter_paste/controllers/__init__.py
-
33web_chatter_paste/controllers/main.py
-
110web_chatter_paste/static/src/js/web_chatter_paste.js
-
12web_chatter_paste/templates/assets.xml
-
5web_chatter_paste/tests/__init__.py
-
43web_chatter_paste/tests/test_web_chatter_paste.py
@ -0,0 +1,67 @@ |
|||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
============= |
||||
|
Chatter Paste |
||||
|
============= |
||||
|
|
||||
|
Paste images and drop files (while composing a message) into the chatter and upload them directly as attachment. |
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
|
||||
|
No configuration is needed. |
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
To paste an image: |
||||
|
|
||||
|
#. Copy an image (e.g. a screenshot); |
||||
|
#. paste the image into the chatter's composer. |
||||
|
|
||||
|
To drop a file: |
||||
|
|
||||
|
#. Drag any file (multiple) from your file system; |
||||
|
#. drop them into the chatter's composer. |
||||
|
|
||||
|
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
||||
|
:alt: Try me on Runbot |
||||
|
:target: https://runbot.odoo-community.org/runbot/162/10.0 |
||||
|
|
||||
|
Known issues / Roadmap |
||||
|
====================== |
||||
|
|
||||
|
* Dropping files only works in Chrome. |
||||
|
|
||||
|
Bug Tracker |
||||
|
=========== |
||||
|
|
||||
|
Bugs are tracked on `GitHub Issues |
||||
|
<https://github.com/OCA/web/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. |
||||
|
|
||||
|
Credits |
||||
|
======= |
||||
|
|
||||
|
Contributors |
||||
|
------------ |
||||
|
|
||||
|
* Dennis Sluijk <d.sluijk@onestein.nl> |
||||
|
|
||||
|
Maintainer |
||||
|
---------- |
||||
|
|
||||
|
.. image:: https://odoo-community.org/logo.png |
||||
|
:alt: Odoo Community Association |
||||
|
:target: https://odoo-community.org |
||||
|
|
||||
|
This module is maintained by the OCA. |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2017 Onestein (<http://www.onestein.eu>) |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
from . import controllers |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2017 Onestein (<http://www.onestein.eu>) |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
{ |
||||
|
'name': 'Chatter Paste', |
||||
|
'summary': """ |
||||
|
Paste images and drop files into the chatter and upload them directly |
||||
|
""", |
||||
|
'version': '10.0.1.0.0', |
||||
|
'category': 'Web', |
||||
|
'author': 'Onestein,Odoo Community Association (OCA)', |
||||
|
'website': 'http://www.onestein.eu', |
||||
|
'license': 'AGPL-3', |
||||
|
'depends': [ |
||||
|
'base', |
||||
|
'web' |
||||
|
], |
||||
|
'data': [ |
||||
|
'templates/assets.xml' |
||||
|
], |
||||
|
'installable': True, |
||||
|
'application': False, |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2017 Onestein (<http://www.onestein.eu>) |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
from . import main |
@ -0,0 +1,33 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2017 Onestein (<http://www.onestein.eu>) |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
from odoo import http |
||||
|
from json import dumps |
||||
|
|
||||
|
|
||||
|
class ChatterPasteController(http.Controller): |
||||
|
|
||||
|
@http.route('/web_chatter_paste/upload_attachment', type='http', |
||||
|
auth="user") |
||||
|
def upload_attachment(self, callback, model, id, filename, mimetype, |
||||
|
content): |
||||
|
request = http.request |
||||
|
model_obj = request.env['ir.attachment'] |
||||
|
out = """<script language="javascript" type="text/javascript"> |
||||
|
var win = window.top.window; |
||||
|
win.jQuery(win).trigger(%s, %s); |
||||
|
</script>""" |
||||
|
attachment = model_obj.create({ |
||||
|
'name': filename, |
||||
|
'datas': content, |
||||
|
'datas_fname': filename, |
||||
|
'res_model': model, |
||||
|
'res_id': int(id) |
||||
|
}) |
||||
|
args = { |
||||
|
'filename': filename, |
||||
|
'mimetype': mimetype, |
||||
|
'id': attachment.id |
||||
|
} |
||||
|
return out % (dumps(callback), dumps(args)) |
@ -0,0 +1,110 @@ |
|||||
|
/* Copyright 2017 Onestein |
||||
|
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
||||
|
|
||||
|
odoo.define('web_chatter_paste', function (require) { |
||||
|
"use strict"; |
||||
|
var core = require('web.core'), |
||||
|
composer = require('mail.composer'); |
||||
|
|
||||
|
composer.BasicComposer.include({ |
||||
|
start: function() { |
||||
|
var self = this; |
||||
|
var res = this._super.apply(this, arguments); |
||||
|
this.$('.o_composer_text_field').bind('drop', function(e) { |
||||
|
e.stopPropagation(); |
||||
|
e.preventDefault(); |
||||
|
var files = e.originalEvent.dataTransfer.files; |
||||
|
var i = 0; |
||||
|
|
||||
|
var next = function() { |
||||
|
$(window).off(self.fileupload_id, next); |
||||
|
i++; |
||||
|
upload(); |
||||
|
} |
||||
|
|
||||
|
var upload = function() { |
||||
|
if (files.length <= i) return; |
||||
|
var reader = new FileReader(); |
||||
|
reader.onload = function() { |
||||
|
$(window).on(self.fileupload_id, next); |
||||
|
self.add_as_attachment(reader.result, files[i].name); |
||||
|
} |
||||
|
reader.readAsDataURL(files[i]); |
||||
|
} |
||||
|
upload(); |
||||
|
}); |
||||
|
this.$('.o_composer_text_field').bind('paste', function(e) { |
||||
|
if (!e.originalEvent.clipboardData.items) return; |
||||
|
var items = e.originalEvent.clipboardData.items; |
||||
|
for (var i = 0; i < items.length; i++) { |
||||
|
var item = items[i]; |
||||
|
if (item.type != 'image/png') continue; |
||||
|
var reader = new FileReader(); |
||||
|
reader.onload = function() { |
||||
|
self.add_as_attachment(reader.result, _.uniqueId('pasted_file') + '.png'); |
||||
|
} |
||||
|
reader.readAsDataURL(item.getAsFile()); |
||||
|
} |
||||
|
}); |
||||
|
return res; |
||||
|
}, |
||||
|
add_as_attachment: function(data, filename, cb) { |
||||
|
//Fetch mimetype and base64
|
||||
|
var mimetype = data.substring(5, data.indexOf(';')); |
||||
|
var base64_data = data.substr(data.indexOf(',') + 1, data.length); |
||||
|
|
||||
|
//Change and submit form
|
||||
|
this.prepare_form(); |
||||
|
this.$('form.o_form_binary_form input.filename').val(filename); |
||||
|
this.$('form.o_form_binary_form input.content').val(base64_data); |
||||
|
this.$('form.o_form_binary_form input.mimetype').val(mimetype); |
||||
|
|
||||
|
this.$('form.o_form_binary_form').submit(); |
||||
|
this.reverse_form(); |
||||
|
|
||||
|
var attachments = this.get('attachment_ids'); |
||||
|
this.$attachment_button.prop('disabled', true); |
||||
|
attachments.push({ |
||||
|
'id': 0, |
||||
|
'name': _.uniqueId('attachment_name'), |
||||
|
'filename': filename, |
||||
|
'url': filename, |
||||
|
'upload': true, |
||||
|
'mimetype': '', |
||||
|
}); |
||||
|
}, |
||||
|
prepare_form: function() { |
||||
|
//Change action
|
||||
|
this.$('form.o_form_binary_form').attr('action', '/web_chatter_paste/upload_attachment'); |
||||
|
|
||||
|
//Remove ufile
|
||||
|
this.$('form.o_form_binary_form input.o_form_input_file').remove(); |
||||
|
|
||||
|
//Add hidden input content
|
||||
|
var $content = $('<input type="hidden" name="content" class="content" />'); |
||||
|
this.$('form.o_form_binary_form').append($content); |
||||
|
|
||||
|
//Add hidden input filename
|
||||
|
var $filename = $('<input type="hidden" name="filename" class="filename" />'); |
||||
|
this.$('form.o_form_binary_form').append($filename); |
||||
|
|
||||
|
//Add hidden input filename
|
||||
|
var $mimetype = $('<input type="hidden" name="mimetype" class="mimetype" />'); |
||||
|
this.$('form.o_form_binary_form').append($mimetype); |
||||
|
}, |
||||
|
reverse_form: function() { |
||||
|
//Change action
|
||||
|
this.$('form.o_form_binary_form').attr('action', '/web/binary/upload_attachment'); |
||||
|
|
||||
|
//Remove new input
|
||||
|
this.$('form.o_form_binary_form input.content').remove(); |
||||
|
this.$('form.o_form_binary_form input.filename').remove(); |
||||
|
this.$('form.o_form_binary_form input.mimetype').remove(); |
||||
|
|
||||
|
//Restore old input
|
||||
|
var $ufile = $('<input class="o_form_input_file" name="ufile" type="file" />'); |
||||
|
this.$('form.o_form_binary_form').append($ufile); |
||||
|
|
||||
|
} |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,12 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!-- Copyright 2017 Onestein |
||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> |
||||
|
|
||||
|
<odoo> |
||||
|
<template id="assets_backend" inherit_id="web.assets_backend"> |
||||
|
<xpath expr="."> |
||||
|
<script type="text/javascript" |
||||
|
src="/web_chatter_paste/static/src/js/web_chatter_paste.js"/> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</odoo> |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2017 Onestein (<http://www.onestein.eu>) |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
from . import test_web_chatter_paste |
@ -0,0 +1,43 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2017 Onestein (<http://www.onestein.eu>) |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
from mock import patch |
||||
|
from odoo import http |
||||
|
from odoo.tests.common import HttpCase |
||||
|
from odoo.addons.web_chatter_paste.controllers.main \ |
||||
|
import ChatterPasteController |
||||
|
|
||||
|
|
||||
|
class TestWebChatterPaste(HttpCase): |
||||
|
def test_controller(self): |
||||
|
partner_id = self.ref('base.main_partner') |
||||
|
attachment_obj = self.env['ir.attachment'] |
||||
|
|
||||
|
attachment_count = attachment_obj.search_count([ |
||||
|
('res_model', '=', 'res.partner'), |
||||
|
('res_id', '=', partner_id) |
||||
|
]) |
||||
|
|
||||
|
f = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6' \ |
||||
|
'QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFN' \ |
||||
|
'vZnR3YXJlAHBhaW50Lm5ldCA0LjAuOWwzfk4AAAAMSURBVBhXY/j//z8ABf4C/q' \ |
||||
|
'c1gYQAAAAASUVORK5CYII=' |
||||
|
|
||||
|
with patch.object(http, 'request') as request: |
||||
|
request.env = self.env |
||||
|
controller = ChatterPasteController() |
||||
|
controller.upload_attachment( |
||||
|
'_', |
||||
|
'res.partner', |
||||
|
partner_id, |
||||
|
'test.png', |
||||
|
'image/png', |
||||
|
f |
||||
|
) |
||||
|
|
||||
|
new_attachment_count = attachment_obj.search_count([ |
||||
|
('res_model', '=', 'res.partner'), |
||||
|
('res_id', '=', partner_id) |
||||
|
]) |
||||
|
self.assertEqual(attachment_count + 1, new_attachment_count) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue