Browse Source

[add] mail_digest

pull/158/head
Simone Orsi 7 years ago
parent
commit
8fda6099ff
  1. 90
      mail_digest/README.rst
  2. 1
      mail_digest/__init__.py
  3. 22
      mail_digest/__openerp__.py
  4. 27
      mail_digest/data/ir_cron.xml
  5. 39
      mail_digest/demo/ir_ui_view.xml
  6. 10
      mail_digest/demo/mail_template.xml
  7. 184
      mail_digest/i18n/de.po
  8. 175
      mail_digest/i18n/mail_digest.pot
  9. BIN
      mail_digest/images/preview.png
  10. 2
      mail_digest/models/__init__.py
  11. 183
      mail_digest/models/mail_digest.py
  12. 208
      mail_digest/models/res_partner.py
  13. 5
      mail_digest/security/ir.model.access.csv
  14. BIN
      mail_digest/static/description/icon.png
  15. 47
      mail_digest/templates/digest_default.xml
  16. 3
      mail_digest/tests/__init__.py
  17. 147
      mail_digest/tests/test_digest.py
  18. 169
      mail_digest/tests/test_partner_domains.py
  19. 136
      mail_digest/tests/test_subtypes_conf.py
  20. 50
      mail_digest/views/mail_digest_views.xml
  21. 41
      mail_digest/views/partner_views.xml
  22. 18
      mail_digest/views/user_views.xml

90
mail_digest/README.rst

@ -0,0 +1,90 @@
.. 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
=========================
Mail digest notifications
=========================
Features
--------
This module allows users/partners to:
* select "digest" mode in their notification settings
* with digest mode on select a frequency: "daily" or "weekly"
* configure specific rules per message subtype (enabled/disabled)
to receive or to not receive any email notification for a given subtype.
The preference tab on user's form will look like:
.. image:: ./images/preview.png
Behavior
--------
When a partner with digest mode on is notified with a message of type email or an email
all the messages are collected inside a `mail.digest` container.
A daily cron and a weekly cron will take care of creating a single email per each digest,
which will be sent as a standard email.
If the message has a specific subtype, all of this will work only
if personal settings allow to receive notification for that specific subtype.
Specifically:
* no record for type: message passes
* record disabled for type: message don't pass
* record enabled for type: message pass
NOTE: under the hood the digest notification logic excludes followers to be notified,
since you really want to notify only mail.digest's partner.
Known issues / Roadmap
======================
* take full control of message and email template.
Right now the notification message and the digest mail itself is wrapped inside Odoo mail template.
We should be able to customize this easily.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/social/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
------------
* Simone Orsi <simone.orsi@camptocamp.com>
Funders
-------
The development of this module has been financially supported by: `Fluxdock.io <https://fluxdock.io>`_
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.

1
mail_digest/__init__.py

@ -0,0 +1 @@
from . import models

22
mail_digest/__openerp__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
{
'name': 'Mail digest',
'summary': """Basic digest mail handling.""",
'version': '9.0.1.0.0',
'license': 'AGPL-3',
'author': 'Camptocamp,Odoo Community Association (OCA)',
'depends': [
'mail',
],
'data': [
'data/ir_cron.xml',
'security/ir.model.access.csv',
'views/mail_digest_views.xml',
'views/partner_views.xml',
'views/user_views.xml',
'templates/digest_default.xml',
],
}

27
mail_digest/data/ir_cron.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record forcecreate="True" id="ir_cron_mail_digest_daily_action" model="ir.cron">
<field name="name">Digest mail process - daily</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field eval="'mail.digest'" name="model"/>
<field eval="'process'" name="function"/>
<field eval="'()'" name="args"/>
</record>
<record forcecreate="True" id="ir_cron_mail_digest_weekly_action" model="ir.cron">
<field name="name">Digest mail process - weekly</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">weeks</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field eval="'mail.digest'" name="model"/>
<field eval="'process'" name="function"/>
<field eval="'(\'weekly\',)'" name="args"/>
</record>
</data>
</odoo>

39
mail_digest/demo/ir_ui_view.xml

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="view_email_template_corporate_identity">
<body>
<html>
<img style="float: right" t-attf-src="data:image;base64,{{env.user.company_id.logo}}" />
<!-- if some template calling us sets this variable,
we print a h1 tag /-->
<h1 t-if="email_heading"><t t-esc="email_heading" /></h1>
<t t-raw="0" />
<!-- use some standard footer if the user doesn't have
a signature /-->
<footer t-if="not email_use_user_signature">
<p>
<a t-att-href="env.user.company_id.website">
<t t-esc="env.user.company_id.name" />
</a>
</p>
<p><t t-esc="env.user.company_id.phone" /></p>
</footer>
<footer t-if="email_use_user_signature">
<t t-raw="env.user.signature" />
</footer>
</html>
</body>
</template>
<template id="view_email_template_demo1">
<!-- because we can simply call the ci here, we don't need to
repeat it /-->
<t t-call="email_template_qweb.view_email_template_corporate_identity">
<!-- the template we call uses this as title if we set it /-->
<t t-set="email_heading" t-value="email_template.subject" />
<h2>Dear <t t-esc="object.name" />,</h2>
<p>
This is an email template using qweb.
</p>
</t>
</template>
</odoo>

10
mail_digest/demo/mail_template.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="email_template_demo1" model="mail.template">
<field name="name">QWeb demo</field>
<field name="body_type">qweb</field>
<field name="body_view_id" ref="view_email_template_demo1" />
<field name="model_id" ref="base.model_res_users" />
<field name="subject">QWeb demo email</field>
</record>
</odoo>

184
mail_digest/i18n/de.po

@ -0,0 +1,184 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_digest
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 9.0c\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-23 07:08+0000\n"
"PO-Revision-Date: 2017-04-27 16:55+0200\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"X-Generator: Poedit 1.8.9\n"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_create_uid
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_create_uid
msgid "Created by"
msgstr "Angelegt von"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_create_date
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_create_date
msgid "Created on"
msgstr "Angelegt am"
#. module: mail_digest
#: selection:res.partner,notify_frequency:0
msgid "Daily"
msgstr "Täglich"
#. module: mail_digest
#: code:addons/mail_digest/models/mail_digest.py:106
msgid "Daily update"
msgstr "täglich Übersicht"
#. module: mail_digest
#: code:addons/mail_digest/models/res_partner.py:11
#: model:ir.actions.act_window,name:mail_digest.action_digest_all
#: model:ir.ui.menu,name:mail_digest.menu_email_digest
#, python-format
msgid "Digest"
msgstr "Übersicht"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_display_name
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_display_name
msgid "Display Name"
msgstr "Angezeigter Name"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_enabled
msgid "Enabled"
msgstr "Aktivieren"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_frequency
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_notify_frequency
msgid "Frequency"
msgstr "Häufigkeit"
#. module: mail_digest
#: model:ir.ui.view,arch_db:mail_digest.default_digest_tmpl
msgid "Hello,"
msgstr "Guten Tag"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_id
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_id
msgid "ID"
msgstr "ID"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest___last_update
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf___last_update
msgid "Last Modified on"
msgstr "Zuletzt geändert am"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_write_uid
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_write_uid
msgid "Last Updated by"
msgstr "Zuletzt aktualisiert durch"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_write_date
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_write_date
msgid "Last Updated on"
msgstr "Zuletzt aktualisiert am"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_mail_id
msgid "Mail"
msgstr "E-Mail"
#. module: mail_digest
#: model:ir.model,name:mail_digest.model_mail_digest
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_form
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_tree
msgid "Mail digest"
msgstr "Übersicht"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_message_ids
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_form
msgid "Messages"
msgstr "Mitteilungen"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_name
msgid "Name"
msgstr "Bezeichnung"
#. module: mail_digest
#: model:ir.ui.view,arch_db:mail_digest.notification_form
msgid "Notification"
msgstr "Benachrichtigung"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_subtype_id
msgid "Notification type"
msgstr "Benachrichtigung"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_notify_conf_ids
#: model:ir.ui.view,arch_db:mail_digest.notification_tree
msgid "Notifications"
msgstr "Benachrichtigungen"
#. module: mail_digest
#: model:ir.model,name:mail_digest.model_res_partner
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_partner_id
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_partner_id
msgid "Partner"
msgstr "Partner"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_disabled_notify_subtype_ids
msgid "Partner disabled subtypes"
msgstr "Partner disabled subtypes"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_enabled_notify_subtype_ids
msgid "Partner enabled subtypes"
msgstr "Partner enabled subtypes"
#. module: mail_digest
#: model:ir.model,name:mail_digest.model_partner_notification_conf
msgid "Partner notification configuration"
msgstr "Partner notification configuration"
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_state
msgid "Status"
msgstr "Status"
#. module: mail_digest
#: selection:res.partner,notify_frequency:0
msgid "Weekly"
msgstr "Wöchentlich"
#. module: mail_digest
#: code:addons/mail_digest/models/mail_digest.py:108
msgid "Weekly update"
msgstr "wöchentlich Übersicht"
#. module: mail_digest
#: sql_constraint:partner.notification.conf:0
msgid "You can have only one configuration per subtype!"
msgstr "You can have only one configuration per subtype!"
#~ msgid "Mail notification"
#~ msgstr "Mail notification"
#~ msgid "Mail notifications control panel"
#~ msgstr "Mail notifications control panel"
#~ msgid "Message subtypes"
#~ msgstr "Nachrichten-Subtyp"

175
mail_digest/i18n/mail_digest.pot

@ -0,0 +1,175 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_digest
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 9.0c\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-23 07:08+0000\n"
"PO-Revision-Date: 2017-05-23 07:08+0000\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: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_create_uid
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_create_uid
msgid "Created by"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_create_date
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_create_date
msgid "Created on"
msgstr ""
#. module: mail_digest
#: selection:res.partner,notify_frequency:0
msgid "Daily"
msgstr ""
#. module: mail_digest
#: code:addons/mail_digest/models/mail_digest.py:106
#, python-format
msgid "Daily update"
msgstr ""
#. module: mail_digest
#: code:addons/mail_digest/models/res_partner.py:11
#: model:ir.actions.act_window,name:mail_digest.action_digest_all
#: model:ir.ui.menu,name:mail_digest.menu_email_digest
#, python-format
msgid "Digest"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_display_name
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_display_name
msgid "Display Name"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_enabled
msgid "Enabled"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_frequency
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_notify_frequency
msgid "Frequency"
msgstr ""
#. module: mail_digest
#: model:ir.ui.view,arch_db:mail_digest.default_digest_tmpl
msgid "Hello,"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_id
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_id
msgid "ID"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest___last_update
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf___last_update
msgid "Last Modified on"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_write_uid
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_write_uid
msgid "Last Updated by"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_write_date
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_write_date
msgid "Last Updated on"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_mail_id
msgid "Mail"
msgstr ""
#. module: mail_digest
#: model:ir.model,name:mail_digest.model_mail_digest
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_form
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_tree
msgid "Mail digest"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_message_ids
#: model:ir.ui.view,arch_db:mail_digest.mail_digest_form
msgid "Messages"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_name
msgid "Name"
msgstr ""
#. module: mail_digest
#: model:ir.ui.view,arch_db:mail_digest.notification_form
msgid "Notification"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_subtype_id
msgid "Notification type"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_notify_conf_ids
#: model:ir.ui.view,arch_db:mail_digest.notification_tree
msgid "Notifications"
msgstr ""
#. module: mail_digest
#: model:ir.model,name:mail_digest.model_res_partner
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_partner_id
#: model:ir.model.fields,field_description:mail_digest.field_partner_notification_conf_partner_id
msgid "Partner"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_disabled_notify_subtype_ids
msgid "Partner disabled subtypes"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_res_partner_enabled_notify_subtype_ids
msgid "Partner enabled subtypes"
msgstr ""
#. module: mail_digest
#: model:ir.model,name:mail_digest.model_partner_notification_conf
msgid "Partner notification configuration"
msgstr ""
#. module: mail_digest
#: model:ir.model.fields,field_description:mail_digest.field_mail_digest_state
msgid "Status"
msgstr ""
#. module: mail_digest
#: selection:res.partner,notify_frequency:0
msgid "Weekly"
msgstr ""
#. module: mail_digest
#: code:addons/mail_digest/models/mail_digest.py:108
#, python-format
msgid "Weekly update"
msgstr ""
#. module: mail_digest
#: sql_constraint:partner.notification.conf:0
msgid "You can have only one configuration per subtype!"
msgstr ""

BIN
mail_digest/images/preview.png

After

Width: 837  |  Height: 253  |  Size: 20 KiB

2
mail_digest/models/__init__.py

@ -0,0 +1,2 @@
from . import mail_digest
from . import res_partner

183
mail_digest/models/mail_digest.py

@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from openerp import fields, models, api, _
import logging
logger = logging.getLogger('[mail_digest]')
class MailDigest(models.Model):
_name = 'mail.digest'
_description = 'Mail digest'
_order = 'create_date desc'
name = fields.Char(
string="Name",
compute="_compute_name",
readonly=True,
)
# maybe we can retrieve the from messages?
partner_id = fields.Many2one(
string='Partner',
comodel_name='res.partner',
readonly=True,
required=True,
ondelete='cascade',
)
frequency = fields.Selection(
related='partner_id.notify_frequency',
readonly=True,
)
message_ids = fields.Many2many(
comodel_name='mail.message',
string='Messages'
)
# TODO: take care of `auto_delete` feature
mail_id = fields.Many2one(
'mail.mail',
'Mail',
ondelete='set null',
)
state = fields.Selection(related='mail_id.state')
@api.multi
@api.depends("partner_id", "partner_id.notify_frequency")
def _compute_name(self):
for rec in self:
rec.name = u'{} - {}'.format(
rec.partner_id.name, rec._get_subject())
@api.model
def create_or_update(self, partners, message, subtype_id=None):
subtype_id = subtype_id or message.subtype_id
for partner in partners:
digest = self._get_or_create_by_partner(partner, message)
digest.message_ids |= message
return True
@api.model
def _get_by_partner(self, partner, mail_id=False):
domain = [
('partner_id', '=', partner.id),
('mail_id', '=', mail_id),
]
return self.search(domain, limit=1)
@api.model
def _get_or_create_by_partner(self, partner, message=None, mail_id=False):
existing = self._get_by_partner(partner, mail_id=mail_id)
if existing:
return existing
values = {'partner_id': partner.id, }
return self.create(values)
@api.model
def _message_group_by_key(self, msg):
return msg.subtype_id.id
@api.multi
def _message_group_by(self):
self.ensure_one()
grouped = {}
for msg in self.message_ids:
grouped.setdefault(self._message_group_by_key(msg), []).append(msg)
return grouped
def _get_template(self):
# TODO: move this to a configurable field
return self.env.ref('mail_digest.default_digest_tmpl')
def _get_site_name(self):
# default to company
name = self.env.user.company_id.name
if 'website' in self.env:
try:
ws = self.env['website'].get_current_website()
except RuntimeError:
# RuntimeError: object unbound -> no website request
ws = None
if ws:
name = ws.name
return name
@api.multi
def _get_subject(self):
# TODO: shall we move this to computed field?
self.ensure_one()
subject = self._get_site_name() + ' '
if self.partner_id.notify_frequency == 'daily':
subject += _('Daily update')
elif self.partner_id.notify_frequency == 'weekly':
subject += _('Weekly update')
return subject
@api.multi
def _get_template_values(self):
self.ensure_one()
subject = self._get_subject()
template_values = {
'digest': self,
'subject': subject,
'grouped_messages': self._message_group_by(),
'base_url':
self.env['ir.config_parameter'].get_param('web.base.url'),
}
return template_values
@api.multi
def _get_email_values(self, template=None):
self.ensure_one()
template = template or self._get_template()
subject = self._get_subject()
template_values = self._get_template_values()
values = {
'email_from': self.env.user.company_id.email,
'recipient_ids': [(4, self.partner_id.id)],
'subject': subject,
'body_html': template.with_context(
**self._template_context()
).render(template_values),
}
return values
def _create_mail_context(self):
return {
'notify_only_recipients': True,
}
@api.multi
def _template_context(self):
self.ensure_one()
return {
'lang': self.partner_id.lang,
}
@api.multi
def create_email(self, template=None):
mail_model = self.env['mail.mail'].with_context(
**self._create_mail_context())
created = []
for item in self:
if not item.message_ids:
# useless to create a mail for a digest w/ messages
# messages could be deleted by admin for instance.
continue
values = item.with_context(
**item._template_context()
)._get_email_values(template=template)
item.mail_id = mail_model.create(values)
created.append(item.id)
if created:
logger.info('Create email for digest IDS=%s', str(created))
@api.model
def process(self, frequency='daily', domain=None):
if not domain:
domain = [
('mail_id', '=', False),
('partner_id.notify_frequency', '=', frequency),
]
self.search(domain).create_email()

208
mail_digest/models/res_partner.py

@ -0,0 +1,208 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from openerp import models, fields, api, _
class ResPartner(models.Model):
_inherit = 'res.partner'
notify_email = fields.Selection(selection_add=[('digest', _('Digest'))])
notify_frequency = fields.Selection(
string='Frequency',
selection=[
('daily', 'Daily'),
('weekly', 'Weekly')
],
default='weekly',
required=True,
)
notify_conf_ids = fields.One2many(
string='Notifications',
inverse_name='partner_id',
comodel_name='partner.notification.conf',
)
enabled_notify_subtype_ids = fields.Many2many(
string='Partner enabled subtypes',
comodel_name='mail.message.subtype',
compute='_compute_enabled_notify_subtype_ids',
search='_search_enabled_notify_subtype_ids',
)
disabled_notify_subtype_ids = fields.Many2many(
string='Partner disabled subtypes',
comodel_name='mail.message.subtype',
compute='_compute_disabled_notify_subtype_ids',
search='_search_disabled_notify_subtype_ids',
)
@api.multi
def _compute_notify_subtypes(self, enabled):
self.ensure_one()
query = (
'SELECT subtype_id FROM partner_notification_conf '
'WHERE partner_id=%s AND enabled = %s'
)
self.env.cr.execute(
query, (self.id, enabled))
return [x[0] for x in self.env.cr.fetchall()]
@api.multi
@api.depends('notify_conf_ids.subtype_id')
def _compute_enabled_notify_subtype_ids(self):
for partner in self:
partner.enabled_notify_subtype_ids = \
partner._compute_notify_subtypes(True)
@api.multi
@api.depends('notify_conf_ids.subtype_id')
def _compute_disabled_notify_subtype_ids(self):
for partner in self:
partner.disabled_notify_subtype_ids = \
partner._compute_notify_subtypes(False)
def _search_notify_subtype_ids_domain(self, operator, value, enabled):
if operator in ('in', 'not in') and \
not isinstance(value, (tuple, list)):
value = [value, ]
conf_value = value
if isinstance(conf_value, int):
# we search conf records always w/ 'in'
conf_value = [conf_value]
_value = self.env['partner.notification.conf'].search([
('subtype_id', 'in', conf_value),
('enabled', '=', enabled),
]).mapped('partner_id').ids
return [('id', operator, _value)]
def _search_enabled_notify_subtype_ids(self, operator, value):
return self._search_notify_subtype_ids_domain(
operator, value, True)
def _search_disabled_notify_subtype_ids(self, operator, value):
return self._search_notify_subtype_ids_domain(
operator, value, False)
@api.multi
def _notify(self, message, force_send=False, user_signature=True):
"""Override to delegate domain generation."""
# notify_by_email
email_domain = self._get_notify_by_email_domain(message)
# `sudo` from original odoo method
# the reason should be that anybody can write messages to a partner
# and you really want to find all ppl to be notified
partners = self.sudo().search(email_domain)
partners._notify_by_email(
message, force_send=force_send, user_signature=user_signature)
# notify_by_digest
digest_domain = self._get_notify_by_email_domain(
message, digest=True)
partners = self.sudo().search(digest_domain)
partners._notify_by_digest(message)
# notify_by_chat
self._notify_by_chat(message)
return True
@api.multi
def _notify_by_digest(self, message):
message_sudo = message.sudo()
if not message_sudo.message_type == 'email':
return
self.env['mail.digest'].sudo().create_or_update(self, message)
@api.model
def _get_notify_by_email_domain(self, message, digest=False):
"""Return domain to collect partners to be notified by email.
:param message: instance of mail.message
:param digest: include/exclude digest enabled partners
NOTE: since mail.mail inherits from mail.message
this method is called even when
we create the final email for mail.digest object.
Here we introduce a new context flag `notify_only_recipients`
to explicitely retrieve only partners among message's recipients.
"""
message_sudo = message.sudo()
channels = message.channel_ids.filtered(
lambda channel: channel.email_send)
email = message_sudo.author_id \
and message_sudo.author_id.email or message.email_from
ids = self.ids
if self.env.context.get('notify_only_recipients'):
ids = [x for x in ids if x in message.partner_ids.ids]
domain = [
'|',
('id', 'in', ids),
('channel_ids', 'in', channels.ids),
('email', '!=', email)
]
if not digest:
domain.append(('notify_email', 'not in', ('none', 'digest')))
else:
domain.append(('notify_email', '=', 'digest'))
if message.subtype_id:
domain.extend(self._get_domain_subtype_leaf(message.subtype_id))
return domain
@api.model
def _get_domain_subtype_leaf(self, subtype):
return [
'|',
('disabled_notify_subtype_ids', 'not in', (subtype.id, )),
('enabled_notify_subtype_ids', 'in', (subtype.id, )),
]
@api.multi
def _notify_update_subtype(self, subtype, enable):
self.ensure_one()
exists = self.env['partner.notification.conf'].search([
('subtype_id', '=', subtype.id),
('partner_id', '=', self.id)
], limit=1)
if exists:
exists.enabled = enable
else:
self.write({
'notify_conf_ids': [
(0, 0, {'enabled': enable, 'subtype_id': subtype.id})]
})
@api.multi
def _notify_enable_subtype(self, subtype):
self._notify_update_subtype(subtype, True)
@api.multi
def _notify_disable_subtype(self, subtype):
self._notify_update_subtype(subtype, False)
class PartnerNotificationConf(models.Model):
"""Hold partner's single notification configuration."""
_name = 'partner.notification.conf'
_description = 'Partner notification configuration'
# TODO: add friendly onchange to not yield errors when editin via UI
_sql_constraints = [
('unique_partner_subtype_conf',
'unique (partner_id,subtype_id)',
'You can have only one configuration per subtype!')
]
partner_id = fields.Many2one(
string='Partner',
comodel_name='res.partner',
readonly=True,
required=True,
ondelete='cascade',
index=True,
)
subtype_id = fields.Many2one(
'mail.message.subtype',
'Notification type',
ondelete='cascade',
required=True,
)
enabled = fields.Boolean(default=True, index=True)

5
mail_digest/security/ir.model.access.csv

@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_digest_all,mail.digest.all,model_mail_digest,,1,0,0,0
access_partner_notification_conf_all,partner.notification.all,model_partner_notification_conf,,1,0,0,0
access_mail_digest_system,mail.digest.all,model_mail_digest,base.group_system,1,1,1,1
access_partner_notification_conf_system,partner.notification.all,model_partner_notification_conf,base.group_system,1,1,1,1

BIN
mail_digest/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

47
mail_digest/templates/digest_default.xml

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="digest_layout" name="Mail digest layout">
<html>
<body>
<img style="float: right" t-attf-src="data:image;base64,{{env.user.company_id.logo}}" />
<!-- if some template calling us sets this variable,
we print a h1 tag /-->
<h1 t-if="email_heading"><t t-raw="email_heading" /></h1>
<t t-raw="0" />
<!-- use some standard footer if the user doesn't have
a signature /-->
<footer t-if="not email_use_user_signature">
<p>
<a t-att-href="env.user.company_id.website">
<t t-esc="env.user.company_id.name" />
</a>
</p>
<p><t t-esc="env.user.company_id.phone" /></p>
</footer>
<footer t-if="email_use_user_signature">
<t t-raw="env.user.signature" />
</footer>
</body>
</html>
</template>
<template id="default_digest_tmpl" name="Mail digest default template">
<t t-call="mail_digest.digest_layout">
<div id="mail_content">
<h2>Hello,</h2>
<div id="mail_inner_content">
<t t-foreach="grouped_messages.iterkeys()" t-as="gkey">
<t t-set="messages" t-value="grouped_messages[gkey]" />
<t t-foreach="messages" t-as="msg">
<div style="margin:20px">
<h3 t-esc="msg.subject" />
<t t-raw="msg.body" />
</div>
</t>
</t>
</div>
</div>
</t>
</template>
</odoo>

3
mail_digest/tests/__init__.py

@ -0,0 +1,3 @@
from . import test_digest
from . import test_partner_domains
from . import test_subtypes_conf

147
mail_digest/tests/test_digest.py

@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from openerp.tests.common import TransactionCase
class DigestCase(TransactionCase):
def setUp(self):
super(DigestCase, self).setUp()
self.partner_model = self.env['res.partner']
self.message_model = self.env['mail.message']
self.subtype_model = self.env['mail.message.subtype']
self.digest_model = self.env['mail.digest']
self.conf_model = self.env['partner.notification.conf']
self.partner1 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 1',
'email': 'partner1@test.foo.com',
})
self.partner2 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 2',
'email': 'partner2@test.foo.com',
})
self.partner3 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 3',
'email': 'partner3@test.foo.com',
})
self.subtype1 = self.subtype_model.create({'name': 'Type 1'})
self.subtype2 = self.subtype_model.create({'name': 'Type 2'})
def test_get_or_create_digest(self):
message1 = self.message_model.create({
'body': 'My Body 1',
'subtype_id': self.subtype1.id,
})
message2 = self.message_model.create({
'body': 'My Body 2',
'subtype_id': self.subtype2.id,
})
# 2 messages, 1 digest container
dig1 = self.digest_model._get_or_create_by_partner(
self.partner1, message1)
dig2 = self.digest_model._get_or_create_by_partner(
self.partner1, message2)
self.assertEqual(dig1, dig2)
def test_create_or_update_digest(self):
partners = self.partner_model
partners |= self.partner1
partners |= self.partner2
message1 = self.message_model.create({
'body': 'My Body 1',
'subtype_id': self.subtype1.id,
})
message2 = self.message_model.create({
'body': 'My Body 2',
'subtype_id': self.subtype2.id,
})
# partner 1
self.digest_model.create_or_update(self.partner1, message1)
self.digest_model.create_or_update(self.partner1, message2)
p1dig = self.digest_model._get_or_create_by_partner(self.partner1)
self.assertIn(message1, p1dig.message_ids)
self.assertIn(message2, p1dig.message_ids)
# partner 2
self.digest_model.create_or_update(self.partner2, message1)
self.digest_model.create_or_update(self.partner2, message2)
p2dig = self.digest_model._get_or_create_by_partner(self.partner2)
self.assertIn(message1, p2dig.message_ids)
self.assertIn(message2, p2dig.message_ids)
def test_notify_partner_digest(self):
message = self.message_model.create({
'body': 'My Body 1',
'subtype_id': self.subtype1.id,
})
self.partner1.notify_email = 'digest'
# notify partner
self.partner1._notify(message)
# we should find the message in its digest
dig1 = self.digest_model._get_or_create_by_partner(
self.partner1, message)
self.assertIn(message, dig1.message_ids)
def test_notify_partner_digest_followers(self):
self.partner3.message_subscribe(self.partner2.ids)
self.partner1.notify_email = 'digest'
self.partner2.notify_email = 'digest'
partners = self.partner1 + self.partner2
message = self.message_model.create({
'body': 'My Body 1',
'subtype_id': self.subtype1.id,
'res_id': self.partner3.id,
'model': 'res.partner',
'partner_ids': [(4, self.partner1.id)]
})
# notify partners
partners._notify(message)
# we should find the a digest for each partner
dig1 = self.digest_model._get_by_partner(self.partner1)
dig2 = self.digest_model._get_by_partner(self.partner2)
# and the message in them
self.assertIn(message, dig1.message_ids)
self.assertIn(message, dig2.message_ids)
# now we exclude followers
dig1.unlink()
dig2.unlink()
partners.with_context(notify_only_recipients=1)._notify(message)
# we should find the a digest for each partner
self.assertTrue(self.digest_model._get_by_partner(self.partner1))
self.assertFalse(self.digest_model._get_by_partner(self.partner2))
def _create_for_partner(self, partner):
messages = {}
for type_id in (self.subtype1.id, self.subtype2.id):
for k in xrange(1, 3):
key = '{}_{}'.format(type_id, k)
messages[key] = self.message_model.create({
'subject': 'My Subject {}'.format(key),
'body': 'My Body {}'.format(key),
'subtype_id': type_id,
})
self.digest_model.create_or_update(
partner, messages[key])
return self.digest_model._get_or_create_by_partner(partner)
def test_digest_group_messages(self):
dig = self._create_for_partner(self.partner1)
grouped = dig._message_group_by()
for type_id in (self.subtype1.id, self.subtype2.id):
self.assertIn(type_id, grouped)
self.assertEqual(len(grouped[type_id]), 2)
def test_digest_mail_values(self):
dig = self._create_for_partner(self.partner1)
values = dig._get_email_values()
expected = ('recipient_ids', 'subject', 'body_html')
for k in expected:
self.assertIn(k, values)
self.assertEqual(self.env.user.company_id.email, values['email_from'])
self.assertEqual([(4, self.partner1.id)], values['recipient_ids'])

169
mail_digest/tests/test_partner_domains.py

@ -0,0 +1,169 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from openerp.tests.common import TransactionCase
class PartnerDomainCase(TransactionCase):
def setUp(self):
super(PartnerDomainCase, self).setUp()
self.partner_model = self.env['res.partner']
self.message_model = self.env['mail.message']
self.subtype_model = self.env['mail.message.subtype']
self.partner1 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 1',
'email': 'partner1@test.foo.com',
})
self.partner2 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 2',
'email': 'partner2@test.foo.com',
})
self.partner3 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 3',
'email': 'partner3@test.foo.com',
})
self.subtype1 = self.subtype_model.create({'name': 'Type 1'})
self.subtype2 = self.subtype_model.create({'name': 'Type 2'})
def _assert_found(self, domain, not_found=False, partner=None):
partner = partner or self.partner1
if not_found:
self.assertNotIn(partner, self.partner_model.search(domain))
else:
self.assertIn(partner, self.partner_model.search(domain))
def test_notify_domains_always(self):
# we don't set recipients
# because we call `_get_notify_by_email_domain` directly
message = self.message_model.create({'body': 'My Body', })
partner = self.partner1
partner.notify_email = 'always'
domain = partner._get_notify_by_email_domain(message)
self._assert_found(domain)
domain = partner._get_notify_by_email_domain(message, digest=1)
self._assert_found(domain, not_found=1)
def test_notify_domains_only_recipients(self):
# we don't set recipients
# because we call `_get_notify_by_email_domain` directly
self.partner1.notify_email = 'always'
self.partner2.notify_email = 'always'
partners = self.partner1 + self.partner2
# followers
self.partner3.message_subscribe(self.partner2.ids)
# partner1 is the only recipient
message = self.message_model.create({
'body': 'My Body',
'res_id': self.partner3.id,
'model': 'res.partner',
'partner_ids': [(4, self.partner1.id)]
})
domain = partners._get_notify_by_email_domain(message)
# we find both of them since partner2 is a follower
self._assert_found(domain)
self._assert_found(domain, partner=self.partner2)
# no one here in digest mode
domain = partners._get_notify_by_email_domain(message, digest=1)
self._assert_found(domain, not_found=1)
self._assert_found(domain, not_found=1, partner=self.partner2)
# include only recipients
domain = partners.with_context(
notify_only_recipients=1)._get_notify_by_email_domain(message)
self._assert_found(domain)
self._assert_found(domain, partner=self.partner2, not_found=1)
def test_notify_domains_digest(self):
# we don't set recipients
# because we call `_get_notify_by_email_domain` directly
message = self.message_model.create({'body': 'My Body', })
partner = self.partner1
partner.notify_email = 'digest'
domain = partner._get_notify_by_email_domain(message)
self._assert_found(domain, not_found=1)
domain = partner._get_notify_by_email_domain(message, digest=1)
self._assert_found(domain)
def test_notify_domains_none(self):
message = self.message_model.create({'body': 'My Body', })
partner = self.partner1
partner.notify_email = 'none'
domain = partner._get_notify_by_email_domain(message)
self._assert_found(domain, not_found=1)
domain = partner._get_notify_by_email_domain(message, digest=1)
self._assert_found(domain, not_found=1)
def test_notify_domains_match_type_digest(self):
# Test message subtype matches partner settings.
# The partner can have several `partner.notification.conf` records.
# Each records establish notification rules by type.
# If you don't have any record in it, you allow all subtypes.
# Record `typeX` with `enable=True` enables notification for `typeX`.
# Record `typeX` with `enable=False` disables notification for `typeX`.
partner = self.partner1
# enable digest
partner.notify_email = 'digest'
message_t1 = self.message_model.create({
'body': 'My Body',
'subtype_id': self.subtype1.id,
})
message_t2 = self.message_model.create({
'body': 'My Body',
'subtype_id': self.subtype2.id,
})
# enable subtype on partner
partner._notify_enable_subtype(self.subtype1)
domain = partner._get_notify_by_email_domain(
message_t1, digest=True)
# notification enabled: we find the partner.
self._assert_found(domain)
# for subtype2 we don't have any explicit rule: we find the partner
domain = partner._get_notify_by_email_domain(
message_t2, digest=True)
self._assert_found(domain)
# enable subtype2: find the partner anyway
partner._notify_enable_subtype(self.subtype2)
domain = partner._get_notify_by_email_domain(
message_t2, digest=True)
self._assert_found(domain)
# disable subtype2: we don't find the partner anymore
partner._notify_disable_subtype(self.subtype2)
domain = partner._get_notify_by_email_domain(
message_t2, digest=True)
self._assert_found(domain, not_found=1)
def test_notify_domains_match_type_always(self):
message_t1 = self.message_model.create({
'body': 'My Body',
'subtype_id': self.subtype1.id,
})
message_t2 = self.message_model.create({
'body': 'My Body',
'subtype_id': self.subtype2.id,
})
# enable always
partner = self.partner1
partner.notify_email = 'always'
# enable subtype on partner
partner._notify_enable_subtype(self.subtype1)
domain = partner._get_notify_by_email_domain(message_t1)
# notification enabled: we find the partner.
self._assert_found(domain)
# for subtype2 we don't have any explicit rule: we find the partner
domain = partner._get_notify_by_email_domain(message_t2)
self._assert_found(domain)
# enable subtype2: find the partner anyway
partner._notify_enable_subtype(self.subtype2)
domain = partner._get_notify_by_email_domain(message_t2)
self._assert_found(domain)
# disable subtype2: we don't find the partner anymore
partner._notify_disable_subtype(self.subtype2)
domain = partner._get_notify_by_email_domain(message_t2)
self._assert_found(domain, not_found=1)

136
mail_digest/tests/test_subtypes_conf.py

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from openerp.tests.common import TransactionCase
class SubtypesCase(TransactionCase):
def setUp(self):
super(SubtypesCase, self).setUp()
self.partner_model = self.env['res.partner']
self.message_model = self.env['mail.message']
self.subtype_model = self.env['mail.message.subtype']
self.partner1 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 1!',
'email': 'partner1@test.foo.com',
})
self.partner2 = self.partner_model.with_context(
tracking_disable=1).create({
'name': 'Partner 2!',
'email': 'partner2@test.foo.com',
})
self.subtype1 = self.subtype_model.create({'name': 'Type 1'})
self.subtype2 = self.subtype_model.create({'name': 'Type 2'})
def _test_subtypes_rel(self):
# setup:
# t1, t2 enabled
# t3 disabled
# t4 no conf
self.subtype3 = self.subtype_model.create({'name': 'Type 3'})
self.subtype4 = self.subtype_model.create({'name': 'Type 4'})
# enable t1 t2
self.partner1._notify_enable_subtype(self.subtype1)
self.partner1._notify_enable_subtype(self.subtype2)
# disable t3
self.partner1._notify_disable_subtype(self.subtype3)
def test_partner_computed_subtype(self):
self._test_subtypes_rel()
# check computed fields
self.assertIn(
self.subtype1, self.partner1.enabled_notify_subtype_ids)
self.assertNotIn(
self.subtype1, self.partner1.disabled_notify_subtype_ids)
self.assertIn(
self.subtype2, self.partner1.enabled_notify_subtype_ids)
self.assertNotIn(
self.subtype2, self.partner1.disabled_notify_subtype_ids)
self.assertIn(
self.subtype3, self.partner1.disabled_notify_subtype_ids)
self.assertNotIn(
self.subtype3, self.partner1.enabled_notify_subtype_ids)
self.assertNotIn(
self.subtype4,
self.partner1.enabled_notify_subtype_ids)
self.assertNotIn(
self.subtype4,
self.partner1.disabled_notify_subtype_ids)
def test_partner_find_by_subtype_incl(self):
self._test_subtypes_rel()
domain = [(
'enabled_notify_subtype_ids',
'in', (self.subtype1.id, self.subtype2.id),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
domain = [(
'disabled_notify_subtype_ids', 'in', self.subtype3.id,
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
domain = [(
'enabled_notify_subtype_ids', 'in', (self.subtype3.id, ),
)]
self.assertNotIn(
self.partner1,
self.partner_model.search(domain)
)
domain = [(
'enabled_notify_subtype_ids', 'in', (self.subtype4.id, ),
)]
self.assertNotIn(
self.partner1,
self.partner_model.search(domain)
)
domain = [(
'disabled_notify_subtype_ids', 'in', (self.subtype4.id, ),
)]
self.assertNotIn(
self.partner1,
self.partner_model.search(domain)
)
def test_partner_find_by_subtype_escl(self):
self._test_subtypes_rel()
domain = [(
'enabled_notify_subtype_ids',
'not in', (self.subtype4.id, ),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
domain = [(
'disabled_notify_subtype_ids',
'not in', (self.subtype4.id, ),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
domain = [(
'enabled_notify_subtype_ids',
'not in', (self.subtype3.id, ),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)
domain = [(
'disabled_notify_subtype_ids',
'not in', (self.subtype1.id, self.subtype2.id),
)]
self.assertIn(
self.partner1,
self.partner_model.search(domain)
)

50
mail_digest/views/mail_digest_views.xml

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="mail_digest_tree">
<field name="name">mail_digest mail.digest.tree</field>
<field name="model">mail.digest</field>
<field name="arch" type="xml">
<tree string="Mail digest">
<field name="partner_id" />
<field name="mail_id" />
<field name="state" />
</tree>
</field>
</record>
<record model="ir.ui.view" id="mail_digest_form">
<field name="name">mail_digest mail.digest.form</field>
<field name="model">mail.digest</field>
<field name="arch" type="xml">
<form string="Mail digest">
<group name="main" col="4">
<field name="name" />
<field name="partner_id" />
<field name="frequency" />
<field name="mail_id" />
<field name="state" />
</group>
<group name="messages" string="Messages" col="4">
<field name="message_ids" nolabel="1" />
</group>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="action_digest_all">
<field name="name">Digest</field>
<field name="res_model">mail.digest</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
<field name="view_id" ref="mail_digest_tree" />
</record>
<menuitem id="menu_email_digest" parent="base.menu_email"
action="action_digest_all"
sequence="90"/>
</data>
</openerp>

41
mail_digest/views/partner_views.xml

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="notifications_emails_partner_info_form" model="ir.ui.view">
<field name="name">mail.notifications res.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="mail.view_emails_partner_info_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='notify_email']" position="after">
<field name="notify_conf_ids" attrs="{'invisible': [('notify_email','=', 'none')]}"/>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="notification_form">
<field name="name">partner.notification.conf form</field>
<field name="model">partner.notification.conf</field>
<field name="arch" type="xml">
<form string="Notification">
<group name="main">
<field name="enabled" />
<field name="subtype_id" options="{'no_create': True}" />
</group>
</form>
</field>
</record>
<record model="ir.ui.view" id="notification_tree">
<field name="name">partner.notification.conf tree</field>
<field name="model">partner.notification.conf</field>
<field name="arch" type="xml">
<tree string="Notifications" editable="top">
<field name="enabled" />
<field name="subtype_id" />
</tree>
</field>
</record>
</data>
</openerp>

18
mail_digest/views/user_views.xml

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="notifications_email_user_info_form" model="ir.ui.view">
<field name="name">mail.notifications res.users.form</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="mail.view_users_form_mail"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='notify_email']" position="after">
<field name="notify_frequency" attrs="{'invisible': [('notify_email','=', 'none')]}" />
<field name="notify_conf_ids" attrs="{'invisible': [('notify_email','=', 'none')]}" />
</xpath>
</field>
</record>
</data>
</openerp>
Loading…
Cancel
Save