mirror of https://github.com/OCA/web.git
Merge branch 'guewen-web-notify-add-actions' into 10.0
commit
b4ba0eeb4a
|
@ -30,6 +30,34 @@ or
|
||||||
:scale: 80 %
|
:scale: 80 %
|
||||||
:alt: Sample notifications
|
:alt: Sample notifications
|
||||||
|
|
||||||
|
The notifications can bring interactivity with some buttons.
|
||||||
|
|
||||||
|
* One allowing to refresh the active view
|
||||||
|
* Another allowing to send a window / client action
|
||||||
|
|
||||||
|
The reload button is activated when sending the notification with:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
self.env.user.notify_info('My information message', show_reload=True)
|
||||||
|
|
||||||
|
The action can be used using the ``action`` keyword:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
action = self.env.ref('sale.action_orders').read()[0]
|
||||||
|
action.update({
|
||||||
|
'res_id': self.id,
|
||||||
|
'views': [(False, 'form')],
|
||||||
|
})
|
||||||
|
self.env.user.notify_info(
|
||||||
|
'My information message',
|
||||||
|
action=action,
|
||||||
|
# optional
|
||||||
|
action_link_name=_('Open Sale'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
|
@ -64,6 +92,7 @@ Contributors
|
||||||
|
|
||||||
* Laurent Mignon <laurent.mignon@acsone.eu>
|
* Laurent Mignon <laurent.mignon@acsone.eu>
|
||||||
* Serpent Consulting Services Pvt. Ltd.<jay.vora@serpentcs.com>
|
* Serpent Consulting Services Pvt. Ltd.<jay.vora@serpentcs.com>
|
||||||
|
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
|
||||||
|
|
||||||
Maintainer
|
Maintainer
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
'name': 'Web Notify',
|
'name': 'Web Notify',
|
||||||
'summary': """
|
'summary': """
|
||||||
Send notification messages to user""",
|
Send notification messages to user""",
|
||||||
'version': '10.0.1.0.0',
|
'version': '10.0.1.1.0',
|
||||||
'description': 'Web Notify',
|
'description': 'Web Notify',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'author': 'ACSONE SA/NV,Odoo Community Association (OCA)',
|
'author': 'ACSONE SA/NV,Odoo Community Association (OCA)',
|
||||||
|
@ -18,7 +18,8 @@
|
||||||
'data': [
|
'data': [
|
||||||
'views/web_notify.xml'
|
'views/web_notify.xml'
|
||||||
],
|
],
|
||||||
'demo': [
|
'qweb': [
|
||||||
|
'static/src/xml/*.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2016 ACSONE SA/NV
|
# Copyright 2016 ACSONE SA/NV
|
||||||
|
# Copyright 2018 Camptocamp
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import api, fields, models, _
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.addons.web.controllers.main import clean_action
|
||||||
|
|
||||||
|
|
||||||
class ResUsers(models.Model):
|
class ResUsers(models.Model):
|
||||||
|
@ -23,24 +25,36 @@ class ResUsers(models.Model):
|
||||||
compute='_compute_channel_names')
|
compute='_compute_channel_names')
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def notify_info(self, message, title=None, sticky=False):
|
def notify_info(self, message, title=None, sticky=False,
|
||||||
|
show_reload=False, action=None,
|
||||||
|
action_link_name=None, **options):
|
||||||
title = title or _('Information')
|
title = title or _('Information')
|
||||||
self._notify_channel(
|
self._notify_channel(
|
||||||
'notify_info_channel_name', message, title, sticky)
|
'notify_info_channel_name', message, title,
|
||||||
|
sticky=sticky, show_reload=show_reload, action=action,
|
||||||
|
action_link_name=action_link_name, **options
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def notify_warning(self, message, title=None, sticky=False):
|
def notify_warning(self, message, title=None, sticky=False,
|
||||||
|
show_reload=False, action=None,
|
||||||
|
action_link_name=None, **options):
|
||||||
title = title or _('Warning')
|
title = title or _('Warning')
|
||||||
self._notify_channel(
|
self._notify_channel(
|
||||||
'notify_warning_channel_name', message, title, sticky)
|
'notify_warning_channel_name', message, title,
|
||||||
|
sticky=sticky, show_reload=show_reload, action=action,
|
||||||
|
action_link_name=action_link_name, **options
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _notify_channel(self, channel_name_field, message, title, sticky):
|
def _notify_channel(self, channel_name_field, message, title, **options):
|
||||||
|
if options.get('action'):
|
||||||
|
options['action'] = clean_action(options['action'])
|
||||||
bus_message = {
|
bus_message = {
|
||||||
'message': message,
|
'message': message,
|
||||||
'title': title,
|
'title': title,
|
||||||
'sticky': sticky
|
|
||||||
}
|
}
|
||||||
|
bus_message.update(options)
|
||||||
notifications = [(getattr(record, channel_name_field), bus_message)
|
notifications = [(getattr(record, channel_name_field), bus_message)
|
||||||
for record in self]
|
for record in self]
|
||||||
self.env['bus.bus'].sendmany(notifications)
|
self.env['bus.bus'].sendmany(notifications)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
.o_notification {
|
||||||
|
.o_notification_action {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* Copyright 2018 Camptocamp
|
||||||
|
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
|
||||||
|
odoo.define('web_notify.notification', function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var base_notification = require('web.notification'),
|
||||||
|
WebClient = require('web.WebClient'),
|
||||||
|
Notification = base_notification.Notification;
|
||||||
|
|
||||||
|
var InteractiveNotification = Notification.extend({
|
||||||
|
template: 'InteractiveNotification',
|
||||||
|
events: _.extend(
|
||||||
|
{},
|
||||||
|
Notification.prototype.events,
|
||||||
|
{'click .o_notification_reload_view': function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
this.reload_active_view();
|
||||||
|
},
|
||||||
|
'click .o_notification_do_action': function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
this.button_do_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
init: function(parent, title, text, options) {
|
||||||
|
this.options = options || {};
|
||||||
|
var sticky = this.options.sticky;
|
||||||
|
this._super.apply(this, [parent, title, text, sticky]);
|
||||||
|
},
|
||||||
|
reload_active_view: function() {
|
||||||
|
this.trigger_up('reload_active_view');
|
||||||
|
},
|
||||||
|
button_do_action: function() {
|
||||||
|
this.getParent().do_action(this.options.action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var InteractiveWarning = InteractiveNotification.extend({
|
||||||
|
template: 'InteractiveWarning',
|
||||||
|
});
|
||||||
|
|
||||||
|
base_notification.NotificationManager.include({
|
||||||
|
interactive_notify(title, text, options) {
|
||||||
|
return this.display(new InteractiveNotification(this, title, text, options));
|
||||||
|
},
|
||||||
|
interactive_warn(title, text, options) {
|
||||||
|
return this.display(new InteractiveWarning(this, title, text, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
InteractiveNotification: InteractiveNotification,
|
||||||
|
InteractiveWarning: InteractiveWarning
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
|
@ -1,13 +1,60 @@
|
||||||
odoo.define('web_notify.WebClient', function (require) {
|
odoo.define('web_notify.WebClient', function (require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var WebClient = require('web.WebClient');
|
var core = require('web.core'),
|
||||||
var base_bus = require('bus.bus');
|
WebClient = require('web.WebClient'),
|
||||||
|
base_bus = require('bus.bus'),
|
||||||
|
Widget = require('web.Widget');
|
||||||
|
|
||||||
|
|
||||||
|
Widget.include({
|
||||||
|
do_interactive_notify: function(title, message, options) {
|
||||||
|
this.trigger_up(
|
||||||
|
'interactive_notification',
|
||||||
|
{title: title, message: message, options: options});
|
||||||
|
},
|
||||||
|
do_interactive_warn: function(title, message, options) {
|
||||||
|
this.trigger_up(
|
||||||
|
'interactive_warning',
|
||||||
|
{title: title, message: message, options: options});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
WebClient.include({
|
WebClient.include({
|
||||||
|
custom_events: _.extend(
|
||||||
|
{},
|
||||||
|
WebClient.prototype.custom_events,
|
||||||
|
{reload_active_view: 'reload_active_view',
|
||||||
|
interactive_notification: function (e) {
|
||||||
|
if(this.notification_manager) {
|
||||||
|
this.notification_manager.interactive_notify(
|
||||||
|
e.data.title, e.data.message, e.data.options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interactive_warning: function (e) {
|
||||||
|
if(this.notification_manager) {
|
||||||
|
this.notification_manager.interactive_warn(
|
||||||
|
e.data.title, e.data.message, e.data.options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
init: function(parent, client_options){
|
init: function(parent, client_options){
|
||||||
this._super(parent, client_options);
|
this._super(parent, client_options);
|
||||||
},
|
},
|
||||||
|
reload_active_view: function(){
|
||||||
|
var action_manager = this.action_manager;
|
||||||
|
if(!action_manager){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var active_view = action_manager.inner_widget.active_view;
|
||||||
|
if(active_view) {
|
||||||
|
active_view.controller.reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
show_application: function() {
|
show_application: function() {
|
||||||
var res = this._super();
|
var res = this._super();
|
||||||
this.start_polling();
|
this.start_polling();
|
||||||
|
@ -40,12 +87,16 @@ WebClient.include({
|
||||||
},
|
},
|
||||||
on_message_warning: function(message){
|
on_message_warning: function(message){
|
||||||
if(this.notification_manager) {
|
if(this.notification_manager) {
|
||||||
this.notification_manager.do_warn(message.title, message.message, message.sticky);
|
this.notification_manager.do_interactive_warn(
|
||||||
|
message.title, message.message, message
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
on_message_info: function(message){
|
on_message_info: function(message){
|
||||||
if(this.notification_manager) {
|
if(this.notification_manager) {
|
||||||
this.notification_manager.do_notify(message.title, message.message, message.sticky);
|
this.notification_manager.do_interactive_notify(
|
||||||
|
message.title, message.message, message
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates id="template" xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="Notification.reload">
|
||||||
|
<div class="o_notification_action" t-if="widget.options.show_reload">
|
||||||
|
<a href="#" class="o_notification_reload_view">
|
||||||
|
<span class="fa fa-refresh"/> Reload current view
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-name="Notification.do_action">
|
||||||
|
<div class="o_notification_action" t-if="widget.options.action">
|
||||||
|
<a href="#" class="o_notification_do_action">
|
||||||
|
<span t-att-class="'fa ' + (widget.options.action_fa_icon ? widget.options.action_fa_icon : 'fa-arrow-circle-left')"/>
|
||||||
|
<t t-if="widget.options.action_link_name">
|
||||||
|
<t t-esc="widget.options.action_link_name"/>
|
||||||
|
</t>
|
||||||
|
<t t-else="">Open</t>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-name="InteractiveNotification" t-extend="Notification">
|
||||||
|
<t t-jquery=".o_notification_content" t-operation="after">
|
||||||
|
<t t-call="Notification.reload"/>
|
||||||
|
<t t-call="Notification.do_action"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-name="InteractiveWarning" t-extend="Warning">
|
||||||
|
<t t-jquery=".o_notification_content" t-operation="after">
|
||||||
|
<t t-call="Notification.reload"/>
|
||||||
|
<t t-call="Notification.do_action"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2016 ACSONE SA/NV
|
# Copyright 2016 ACSONE SA/NV
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
import json
|
||||||
|
|
||||||
from odoo.tests import common
|
from odoo.tests import common
|
||||||
from odoo.addons.bus.models.bus import json_dump
|
from odoo.addons.bus.models.bus import json_dump
|
||||||
|
@ -17,12 +18,18 @@ class TestResUsers(common.TransactionCase):
|
||||||
]
|
]
|
||||||
existing = bus_bus.search(domain)
|
existing = bus_bus.search(domain)
|
||||||
self.env.user.notify_info(
|
self.env.user.notify_info(
|
||||||
message='message', title='title', sticky=True)
|
message='message', title='title', sticky=True,
|
||||||
|
show_reload=True, foo="bar"
|
||||||
|
)
|
||||||
news = bus_bus.search(domain) - existing
|
news = bus_bus.search(domain) - existing
|
||||||
self.assertEqual(1, len(news))
|
self.assertEqual(1, len(news))
|
||||||
self.assertEqual(
|
expected = ('{"message":"message","sticky":true,"title":"title",'
|
||||||
'{"message":"message","sticky":true,"title":"title"}',
|
'"show_reload":true,"action":null,'
|
||||||
news.message)
|
'"action_link_name":null,"foo":"bar"}')
|
||||||
|
self.assertDictEqual(
|
||||||
|
json.loads(expected),
|
||||||
|
json.loads(news.message)
|
||||||
|
)
|
||||||
|
|
||||||
def test_notify_warning(self):
|
def test_notify_warning(self):
|
||||||
bus_bus = self.env['bus.bus']
|
bus_bus = self.env['bus.bus']
|
||||||
|
@ -32,12 +39,46 @@ class TestResUsers(common.TransactionCase):
|
||||||
]
|
]
|
||||||
existing = bus_bus.search(domain)
|
existing = bus_bus.search(domain)
|
||||||
self.env.user.notify_warning(
|
self.env.user.notify_warning(
|
||||||
message='message', title='title', sticky=True)
|
message='message', title='title', sticky=True,
|
||||||
|
show_reload=True, foo="bar"
|
||||||
|
)
|
||||||
news = bus_bus.search(domain) - existing
|
news = bus_bus.search(domain) - existing
|
||||||
self.assertEqual(1, len(news))
|
self.assertEqual(1, len(news))
|
||||||
self.assertEqual(
|
expected = ('{"message":"message","sticky":true,"title":"title",'
|
||||||
'{"message":"message","sticky":true,"title":"title"}',
|
'"show_reload":true,"action":null,'
|
||||||
news.message)
|
'"action_link_name":null,"foo":"bar"}')
|
||||||
|
self.assertDictEqual(
|
||||||
|
json.loads(expected),
|
||||||
|
json.loads(news.message)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_notify_with_action(self):
|
||||||
|
bus_bus = self.env['bus.bus']
|
||||||
|
domain = [
|
||||||
|
('channel', '=',
|
||||||
|
json_dump(self.env.user.notify_info_channel_name))
|
||||||
|
]
|
||||||
|
existing = bus_bus.search(domain)
|
||||||
|
self.env.user.notify_info(
|
||||||
|
message='message', title='title', sticky=True,
|
||||||
|
action={
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"view_mode": "form",
|
||||||
|
},
|
||||||
|
action_link_name="Open"
|
||||||
|
)
|
||||||
|
news = bus_bus.search(domain) - existing
|
||||||
|
self.assertEqual(1, len(news))
|
||||||
|
# the action should be transformed by Odoo (clean_action)
|
||||||
|
expected = ('{"message":"message","sticky":true,"title":"title",'
|
||||||
|
'"show_reload":false,"action":'
|
||||||
|
'{"type": "ir.actions.act_window", "view_mode":"form",'
|
||||||
|
'"flags":{},"views":[[false, "form"]]},'
|
||||||
|
'"action_link_name":"Open"}')
|
||||||
|
self.assertDictEqual(
|
||||||
|
json.loads(expected),
|
||||||
|
json.loads(news.message)
|
||||||
|
)
|
||||||
|
|
||||||
def test_notify_many(self):
|
def test_notify_many(self):
|
||||||
# check that the notification of a list of users is done with
|
# check that the notification of a list of users is done with
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<data>
|
<data>
|
||||||
<template id="assets_backend" name="web_noify assets" inherit_id="web.assets_backend">
|
<template id="assets_backend" name="web_noify assets" inherit_id="web.assets_backend">
|
||||||
<xpath expr="." position="inside">
|
<xpath expr="." position="inside">
|
||||||
|
<link rel="stylesheet" href="/web_notify/static/src/css/notification.less"/>
|
||||||
<script type="text/javascript" src="/web_notify/static/src/js/web_client.js"/>
|
<script type="text/javascript" src="/web_notify/static/src/js/web_client.js"/>
|
||||||
|
<script type="text/javascript" src="/web_notify/static/src/js/notification.js"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
</data>
|
</data>
|
||||||
|
|
Loading…
Reference in New Issue