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