diff --git a/.gitignore b/.gitignore index f7f8a408..e2ee2379 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ build/ develop-eggs/ dist/ eggs/ -lib/ lib64/ parts/ sdist/ @@ -36,6 +35,7 @@ coverage.xml # Translations *.mo +*.pot # Pycharm .idea diff --git a/cms_notification/README.rst b/cms_notification/README.rst new file mode 100644 index 00000000..c0e033f8 --- /dev/null +++ b/cms_notification/README.rst @@ -0,0 +1,119 @@ +.. image:: https://img.shields.io/badge/licence-lgpl--3-blue.svg + :target: http://www.gnu.org/licenses/LGPL-3.0-standalone.html + :alt: License: LGPL-3 + +CMS notification +================= + +Features +-------- + +Manage notifications in your CMS. Provides: + +* menu item for settings +* menu item for notifications listing +* flag `has_unread_notif` on user to highlight when the user has an unread notification +* flag `cms_type` on message subtype to enable/disable visibility in CMS + + +Depends on `mail_digest [WIP PR] `_ +and `cms_form [WIP PR] `_. + + +Default appearance +------------------ + +.. image:: ./images/cms_notif_menu.png +.. image:: ./images/cms_notif_settings.png +.. image:: ./images/cms_notif_listing.png + + +Customization +------------- + +You can customize the form as specified in ``cms_form`` docs. +``cms_notification`` extends the form behavior allowing you to just +provide the fields you need and a mapping between fields and messages' +subtypes. + +.. code:: python + + class CMSNotificationPanel(models.AbstractModel): + _inherit = 'cms.notification.panel.form' + + enable_my_type = fields.Boolean( + string='Enable my type notifications', + help=("If active, you will receive notifications " + "about this type.") + ) + enable_my_type2 = fields.Boolean( + string='Enable my type 2 notifications', + help=("If active, you will receive notifications " + "about this type 2.") + ) + + @property + def _form_subtype_fields(self): + res = super(CMSNotificationPanel, self)._form_subtype_fields + res.update({ + 'enable_my_type': 'module.mt_mytype', + 'enable_my_type2': 'module.mt_mytype2', + }) + return res + + +Usage +----- + + +To bring the user to her/his panel: + +.. code:: html + + Notifications + + +To bring the user to her/his notifications: + +.. code:: html + + Notification settings + + +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. + +Credits +======= + +Contributors +------------ + +- Simone Orsi simone.orsi@camptocamp.com + +Funders +------- + +The development of this module has been financially supported by: `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. diff --git a/cms_notification/__init__.py b/cms_notification/__init__.py new file mode 100644 index 00000000..91c5580f --- /dev/null +++ b/cms_notification/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/cms_notification/__openerp__.py b/cms_notification/__openerp__.py new file mode 100644 index 00000000..432fac86 --- /dev/null +++ b/cms_notification/__openerp__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + 'name': 'CMS notification', + 'summary': """Basic notifications management for your CMS system""", + 'version': '9.0.1.0.0', + 'license': 'LGPL-3', + 'author': 'Camptocamp,Odoo Community Association (OCA)', + 'depends': [ + 'cms_form', + 'mail_digest', + # We need latest fontawesome resources. + # Use server-tools/9.0/base_fontawesome + 'base_fontawesome', + ], + 'data': [ + 'views/mail_message_subtype_views.xml', + 'templates/assets.xml', + 'templates/help_msg.xml', + 'templates/widget.xml', + 'templates/user_menu.xml', + 'templates/notification_listing.xml', + ], +} diff --git a/cms_notification/controllers/__init__.py b/cms_notification/controllers/__init__.py new file mode 100644 index 00000000..12a7e529 --- /dev/null +++ b/cms_notification/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/cms_notification/controllers/main.py b/cms_notification/controllers/main.py new file mode 100644 index 00000000..11b57430 --- /dev/null +++ b/cms_notification/controllers/main.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp import http +from openerp.addons.cms_form.controllers.main \ + import FormControllerMixin, SearchFormControllerMixin + + +class PanelFormController(http.Controller, FormControllerMixin): + """Notification panel form controller.""" + + @http.route([ + '/my/settings/notifications', + ], type='http', auth='user', website=True) + def cms_form(self, **kw): + model = 'res.partner' + # get current user partner + model_id = http.request.env.user.partner_id.id + return self.make_response( + model, model_id=model_id, **kw) + + def form_model_key(self, model): + return 'cms.notification.panel.form' + + +class MyNotificationsController(http.Controller, SearchFormControllerMixin): + """Personal notifications controller.""" + + @http.route([ + '/my/notifications', + ], type='http', auth='user', website=True) + def cms_form(self, **kw): + model = 'res.partner' + # get current user partner + model_id = http.request.env.user.partner_id.id + return self.make_response( + model, model_id=model_id, **kw) + + def form_model_key(self, model): + return 'cms.notification.listing' diff --git a/cms_notification/i18n/de.po b/cms_notification/i18n/de.po new file mode 100644 index 00000000..d85a1dd2 --- /dev/null +++ b/cms_notification/i18n/de.po @@ -0,0 +1,138 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * cms_notification +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-05-16 06:34+0000\n" +"PO-Revision-Date: 2017-05-16 06:34+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: cms_notification +#: model:ir.model,name:cms_notification.model_cms_notification_listing +msgid "CMS notification" +msgstr "CMS notification" + +#. module: cms_notification +#: model:ir.model,name:cms_notification.model_cms_notification_panel_form +msgid "CMS notification control panel" +msgstr "CMS notification control panel" + +#. module: cms_notification +#: code:addons/cms_notification/models/notification_panel_form.py:34 +#: code:addons/odoo/external-src/website-cms/cms_notification/models/notification_panel_form.py:34 +#, python-format +msgid "Changes applied." +msgstr "Änderungen wurden übernommen." + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_create_uid +msgid "Created by" +msgstr "Angelegt von" + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_create_date +msgid "Created on" +msgstr "Angelegt am" + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_cms_notification_listing_display_name +#: model:ir.model.fields,field_description:cms_notification.field_cms_notification_panel_form_display_name +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_display_name +msgid "Display Name" +msgstr "Anzeigename" + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_enabled +msgid "Enabled" +msgstr "Aktivieren" + +#. module: cms_notification +#: model:ir.ui.view,arch_db:cms_notification.notify_email_help_none +msgid "I don't want to get notification emails." +msgstr "Ich möchte keine Benachrichtigungen erhalten." + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_cms_notification_listing_id +#: model:ir.model.fields,field_description:cms_notification.field_cms_notification_panel_form_id +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_id +msgid "ID" +msgstr "ID" + +#. module: cms_notification +#: model:ir.model.fields,help:cms_notification.field_mail_message_subtype_cms_type +msgid "If active, this message subtype will be visible in users' notifications control panel." +msgstr "If active, this message subtype will be visible in users' notifications control panel." + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_cms_notification_listing___last_update +#: model:ir.model.fields,field_description:cms_notification.field_cms_notification_panel_form___last_update +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf___last_update +msgid "Last Modified on" +msgstr "Zuletzt geändert am" + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_write_uid +msgid "Last Updated by" +msgstr "Zuletzt aktualisiert durch" + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_write_date +msgid "Last Updated on" +msgstr "Zuletzt aktualisiert am" + +#. module: cms_notification +#: model:ir.model,name:cms_notification.model_mail_message_subtype +msgid "Message subtypes" +msgstr "Nachrichten-Subtyp" + +#. module: cms_notification +#: code:addons/cms_notification/models/notification_listing.py:24 +#: code:addons/odoo/external-src/website-cms/cms_notification/models/notification_listing.py:24 +#, python-format +msgid "My notifications" +msgstr "Meine Benachrichtigungen" + +#. module: cms_notification +#: code:addons/cms_notification/models/notification_panel_form.py:26 +#: code:addons/odoo/external-src/website-cms/cms_notification/models/notification_panel_form.py:26 +#, python-format +msgid "Notification settings." +msgstr "Benachrichtigungseinstellungen." + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_subtype_id +msgid "Notification type" +msgstr "Notification type" + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_partner_notification_conf_partner_id +msgid "Partner" +msgstr "Partner" + +#. module: cms_notification +#: model:ir.model,name:cms_notification.model_partner_notification_conf +msgid "Partner notification configuration" +msgstr "Partner notification configuration" + +#. module: cms_notification +#: model:ir.model.fields,field_description:cms_notification.field_mail_message_subtype_cms_type +msgid "Visible in CMS control panel" +msgstr "Visible in CMS control panel" + +#. module: cms_notification +#: model:ir.ui.view,arch_db:cms_notification.notify_email_help_digest +msgid "You will receive a summary of the notification in the chosen frequency." +msgstr "Sie erhalten eine Übersicht der Benachrichtigungen entsprechend der gewählten Häufigkeit." + +#. module: cms_notification +#: model:ir.ui.view,arch_db:cms_notification.notify_email_help_always +msgid "You will receive an email for every notification." +msgstr "Sie erhalten eine Email für jede Benachrichtigung." diff --git a/cms_notification/images/cms_notif_listing.png b/cms_notification/images/cms_notif_listing.png new file mode 100644 index 00000000..c8543298 Binary files /dev/null and b/cms_notification/images/cms_notif_listing.png differ diff --git a/cms_notification/images/cms_notif_menu.png b/cms_notification/images/cms_notif_menu.png new file mode 100644 index 00000000..d542494d Binary files /dev/null and b/cms_notification/images/cms_notif_menu.png differ diff --git a/cms_notification/images/cms_notif_settings.png b/cms_notification/images/cms_notif_settings.png new file mode 100644 index 00000000..2ecd2d56 Binary files /dev/null and b/cms_notification/images/cms_notif_settings.png differ diff --git a/cms_notification/models/__init__.py b/cms_notification/models/__init__.py new file mode 100644 index 00000000..a359503b --- /dev/null +++ b/cms_notification/models/__init__.py @@ -0,0 +1,7 @@ +from . import mail_message +from . import mail_message_subtype +from . import notification_panel_form +from . import notification_listing +from . import res_users +from . import test_models +from . import widget diff --git a/cms_notification/models/mail_message.py b/cms_notification/models/mail_message.py new file mode 100644 index 00000000..2b602897 --- /dev/null +++ b/cms_notification/models/mail_message.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp import models, fields, api, tools + + +class MailMessage(models.Model): + _inherit = 'mail.message' + + ref_item_id = fields.Reference( + string="Referenced item", + selection="_selection_ref_item_id", + compute="_compute_ref_item_id", + readonly=True, + ) + ref_model_id = fields.Many2one( + string="Referenced model", + comodel_name="ir.model", + compute="_compute_ref_model_id", + ) + + @api.model + @tools.ormcache("self") + def _selection_ref_item_id(self): + """Allow any model; after all, this field is readonly.""" + return [(r.model, r.name) for r in self._reference_models_search()] + + @api.multi + @api.depends("model", "res_id") + def _compute_ref_item_id(self): + for item in self: + if item.model and item.res_id: + item.ref_item_id = "{0.model},{0.res_id}".format(item) + + @api.multi + @api.depends("model") + def _compute_ref_model_id(self): + for item in self: + if item.model: + item.ref_model_id = self.env['ir.model'].search( + [('model', '=', item.model)], limit=1 + ) + + def _reference_models_search(self): + return self.env["ir.model"].search([]) + + def is_unread(self, partner=None): + partner = partner or self.env.user.partner_id + return partner in self.needaction_partner_ids + + def is_read(self, partner=None): + partner = partner or self.env.user.partner_id + return partner not in self.needaction_partner_ids diff --git a/cms_notification/models/mail_message_subtype.py b/cms_notification/models/mail_message_subtype.py new file mode 100644 index 00000000..235d910c --- /dev/null +++ b/cms_notification/models/mail_message_subtype.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp import fields, models + + +class MailMessageSubtype(models.Model): + _inherit = 'mail.message.subtype' + + cms_type = fields.Boolean( + 'Visible in CMS control panel', + help=("If active, this message subtype will be visible " + "in users' notifications control panel.") + ) diff --git a/cms_notification/models/notification_listing.py b/cms_notification/models/notification_listing.py new file mode 100644 index 00000000..78b8e2b9 --- /dev/null +++ b/cms_notification/models/notification_listing.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp import models, _ + + +class CMSNotificationListing(models.AbstractModel): + """Hold users notifications settings.""" + _name = 'cms.notification.listing' + _inherit = 'cms.form.search' + _description = 'CMS notification' + + _form_model = 'mail.message' + _form_model_fields = ( + 'subtype_id', + ) + form_search_results_template = 'cms_notification.listing' + # form_wrapper_template = 'cms_notification.notifications_wrapper' + # form_template = 'cms_notification.notifications_search_form' + + @property + def form_title(self): + return _('My notifications') + + @property + def form_description(self): + return '' + + @property + def _super(self): + return super(CMSNotificationListing, self) + + def form_search_domain(self, search_values): + domain = self._super.form_search_domain(search_values) + default_domain = [ + ('partner_ids', 'in', [self.env.user.partner_id.id, ]), + ('subtype_id.cms_type', '=', True), + ] + domain.extend(default_domain) + return domain + + def check_view_permission(self, item): + """Check read permission on given item. + + We could list messages that are attached to other records. + If the permissions for this records has changed + then the user viewing the notification may not have + permissions to read it anymore. + """ + try: + item.check_access_rights('read') + item.check_access_rule('read') + return True + except: + return False diff --git a/cms_notification/models/notification_panel_form.py b/cms_notification/models/notification_panel_form.py new file mode 100644 index 00000000..3ceb57fc --- /dev/null +++ b/cms_notification/models/notification_panel_form.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp import models, _ +from openerp.addons.cms_form.utils import string_to_bool + + +class CMSNotificationPanel(models.AbstractModel): + """Hold users notifications settings.""" + _name = 'cms.notification.panel.form' + _inherit = 'cms.form' + _description = 'CMS notification control panel' + + _form_model = 'res.partner' + _form_model_fields = ( + 'notify_email', + 'notify_frequency', + ) + _form_sub_fields = { + 'notify_email': { + 'digest': ('notify_frequency', ), + } + } + + @property + def form_title(self): + return _('Notification settings.') + + @property + def form_description(self): + return '' + + @property + def form_msg_success_updated(self): + return _('Changes applied.') + + def form_next_url(self, main_object=None): + return '/my/settings/notifications' + + @property + def form_widgets(self): + return { + 'notify_email': 'cms.form.widget.notif_radio', + } + + @property + def _super(self): + return super(CMSNotificationPanel, self) + + def _form_master_slave_info(self): + info = self._super._form_master_slave_info() + info.update({ + 'notify_email': { + 'readonly': { + 'notify_frequency': ('always', 'none', ), + }, + 'no_readonly': { + 'notify_frequency': ('digest', ), + }, + 'required': { + 'notify_frequency': ('digest', ), + }, + 'no_required': { + 'notify_frequency': ('always', 'none', ), + }, + }, + }) + return info + + # CMS form does not support o2m fields in an advanced way + # so we cannot add/remove/edit `notify_conf_ids` easily. + # Furthermore, we want to automatically show just checkboxes + # to enable/disable subtypes in an handy way. + # Hence, you are supposed to add a boolean field + a mapping + # field:subtype that allows to enable/disable it on the partner. + # The following methods take care of this. + + @property + def _form_subtype_fields(self): + """Return mapping from boolean form field to subtype xmlid.""" + return { + # 'boolean_field_name': 'subtype_xmlid', + } + + def form_get_loader(self, fname, field, + main_object=None, value=None, **req_values): + """Override to provide automatic loader for boolean fields.""" + loader = self._super.form_get_loader( + fname, field, main_object=main_object, + value=value, **req_values + ) + if fname in self._form_subtype_fields.keys(): + loader = self._form_load_subtype_conf_loader + return loader + + def _form_load_subtype_conf_loader( + self, fname, field, value, **req_values): + """Automatically load value for subtype conf fields.""" + if fname in req_values: + value = string_to_bool(req_values.get(fname)) + else: + subtype = self.env.ref(self._form_subtype_fields[fname]) + explicitly_enabled = \ + subtype in self.main_object.enabled_notify_subtype_ids + explicitly_disabled = \ + subtype in self.main_object.disabled_notify_subtype_ids + # mail_digest machinery will send you emails in this 2 cases: + # * you've enabled notification explicitly + # * you have no specific settings for the subtype + # (hence you did not disabled it) + value = explicitly_enabled or not explicitly_disabled + + return value + + def form_after_create_or_update(self, values, extra_values): + """Update subtype configuration for `_form_subtype_fields`.""" + self._super.form_after_create_or_update(values, extra_values) + for fname, subtype_xmlid in self._form_subtype_fields.iteritems(): + value = extra_values.get(fname) + subtype = self.env.ref(subtype_xmlid) + # use sudo as we don't know if the user + # has been allowed to update its own partner + # and sincerely, we don't care in this case. + self.main_object.sudo()._notify_update_subtype(subtype, value) diff --git a/cms_notification/models/res_users.py b/cms_notification/models/res_users.py new file mode 100644 index 00000000..28d59a84 --- /dev/null +++ b/cms_notification/models/res_users.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp import models, fields, api + + +class ResUsers(models.Model): + _inherit = 'res.users' + + has_unread_notif = fields.Boolean( + string='Has unread notif', + compute='_compute_has_unread_notif', + readonly=True, + ) + + @api.multi + @api.depends() + def _compute_has_unread_notif(self): + msg_model = self.env['mail.message'] + for item in self: + if not item.partner_id: + continue + domain = [ + ('partner_ids', 'in', item.partner_id.id), + ('needaction_partner_ids', 'in', item.partner_id.id), + ('subtype_id.cms_type', '=', True), + ] + item.has_unread_notif = bool(msg_model.search(domain, limit=1)) diff --git a/cms_notification/models/test_models.py b/cms_notification/models/test_models.py new file mode 100644 index 00000000..09f73e62 --- /dev/null +++ b/cms_notification/models/test_models.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp import fields, models, tools +import os + +# ease testing w/ pytest +testing = tools.config.get('test_enable') or \ + os.environ.get('ODOO_TEST_ENABLED') + + +if testing: + class CMSNotificationPanel(models.AbstractModel): + + _inherit = 'cms.notification.panel.form' + + enable_1 = fields.Boolean(string='Enable 1') + enable_2 = fields.Boolean(string='Enable 2') + enable_3 = fields.Boolean(string='Enable 3') + + @property + def _form_subtype_fields(self): + res = super(CMSNotificationPanel, self)._form_subtype_fields + res.update({ + 'enable_1': 'cms_notification.test_subtype1', + 'enable_2': 'cms_notification.test_subtype2', + 'enable_3': 'cms_notification.test_subtype3', + }) + return res diff --git a/cms_notification/models/widget.py b/cms_notification/models/widget.py new file mode 100644 index 00000000..6ab2a572 --- /dev/null +++ b/cms_notification/models/widget.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp import models + + +class NotificationSelectionWidget(models.AbstractModel): + _name = 'cms.form.widget.notif_radio' + _inherit = 'cms.form.widget.radio' + _w_template = 'cms_notification.field_widget_notification_selection' + + help_tmpl_prefix = 'cms_notification.notify_email_help_' + + @property + def w_option_items(self): + """Change options order and inject help text.""" + items = [] + sel = dict(self.w_field['selection']) + for item in ('always', 'digest', 'none', ): + template = self.env.ref( + self.help_tmpl_prefix + item, raise_if_not_found=0) + _help = None + if template: + _help = template.render({'sel': sel, 'field': self.w_field}) + items.append({ + 'value': item, + 'label': sel[item], + 'help': _help + }) + return items diff --git a/cms_notification/static/description/icon.png b/cms_notification/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/cms_notification/static/description/icon.png differ diff --git a/cms_notification/static/src/js/read_unread.js b/cms_notification/static/src/js/read_unread.js new file mode 100644 index 00000000..238c733d --- /dev/null +++ b/cms_notification/static/src/js/read_unread.js @@ -0,0 +1,52 @@ +odoo.define('cms_notification.read_unread', function (require) { + 'use strict'; + /* + Mark messages as read/unread + */ + + var animation = require("web_editor.snippets.animation"); + var $ = require("$"); + var session = require('web.session'); + var Model = require('web.Model'); + var MessageModel = new Model('mail.message', session.user_context); + + return animation.registry.CMSNotificationsReadUnread = animation.Class.extend({ + selector: ".notification_listing a.mark_as", + start: function (editable_mode) { + var self = this; + this.$el.on('click', function(e){ + e.stopPropagation(); + var action = $(e.currentTarget).data('action'); + var ids = [$(e.currentTarget).closest('.list_item').data('id')]; + self[action](ids).then($.proxy(self.update_ui, self, ids)); + }); + }, + mark_as_read: function (ids) { + return MessageModel.call('set_message_done', [ids]); + }, + mark_as_unread: function (ids) { + return MessageModel.call('mark_as_unread', [ids]); + }, + update_ui: function(ids){ + var self = this; + $.each(ids, function(){ + var $item = $('#item_' + this), + status = $item.data('status'), + next_status = status == 'unread'? 'read': 'unread'; + // update current item status + $item.data('status', next_status); + $item.switchClass('item_' + status, 'item_' + next_status); + }); + // update global indicator + var unread_count = + self.$el.closest('.listing').find('.item_unread').length; + if (unread_count) { + $('#user-menu').addClass('has_unread_notif'); + } else { + $('#user-menu').removeClass('has_unread_notif'); + } + } + + }); + +}); diff --git a/cms_notification/static/src/less/styles.less b/cms_notification/static/src/less/styles.less new file mode 100644 index 00000000..e4066539 --- /dev/null +++ b/cms_notification/static/src/less/styles.less @@ -0,0 +1,49 @@ +.notif-circle{ + display: block; + content: ' \25CF'; + color: @brand-warning; + font-size: 20px; + line-height: 0; + position: absolute; + top: 20px; + left: 5px; + z-index: 999; +} +#user-menu.has_unread_notif{ + position: relative; + &::before{ + .notif-circle; + } + #notifications{ + position: relative; + a::before{ + .notif-circle; + top: 8px; + left: 15px; + } + } +} +#wrapwrap .cms_form_wrapper.cms_notification_panel_form{ + // hide required fields mark. Is useless here. + .field-required-legend, + form .field-required .control-label::after { + display:none; + } +} +.cms_form_wrapper.mail_message{ + .listing{ + padding: 1em 0; + .list_item{ + border-bottom: 1px solid; + } + .item_unread{ + .subject{ + font-weight: bold; + } + .mark_as_unread{ display: none } + } + .item_read{ + .mark_as_read{ display: none } + } + } +} diff --git a/cms_notification/templates/assets.xml b/cms_notification/templates/assets.xml new file mode 100644 index 00000000..b558e8ca --- /dev/null +++ b/cms_notification/templates/assets.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/cms_notification/templates/help_msg.xml b/cms_notification/templates/help_msg.xml new file mode 100644 index 00000000..64b6c435 --- /dev/null +++ b/cms_notification/templates/help_msg.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/cms_notification/templates/notification_listing.xml b/cms_notification/templates/notification_listing.xml new file mode 100644 index 00000000..d5516e54 --- /dev/null +++ b/cms_notification/templates/notification_listing.xml @@ -0,0 +1,94 @@ + + + + + + + diff --git a/cms_notification/templates/user_menu.xml b/cms_notification/templates/user_menu.xml new file mode 100644 index 00000000..70abb3d1 --- /dev/null +++ b/cms_notification/templates/user_menu.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/cms_notification/templates/widget.xml b/cms_notification/templates/widget.xml new file mode 100644 index 00000000..09669aae --- /dev/null +++ b/cms_notification/templates/widget.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/cms_notification/tests/__init__.py b/cms_notification/tests/__init__.py new file mode 100644 index 00000000..4a2c327a --- /dev/null +++ b/cms_notification/tests/__init__.py @@ -0,0 +1 @@ +from . import test_notifications diff --git a/cms_notification/tests/test_notifications.py b/cms_notification/tests/test_notifications.py new file mode 100644 index 00000000..89450c77 --- /dev/null +++ b/cms_notification/tests/test_notifications.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from openerp.tests.common import TransactionCase +from openerp.addons.cms_form.tests.common import fake_request + + +def add_xmlid(env, record, xmlid, noupdate=False): + """ Add a XMLID on an existing record """ + try: + ref_id, __, __ = env['ir.model.data'].xmlid_lookup(xmlid) + except ValueError: + pass # does not exist, we'll create a new one + else: + return env['ir.model.data'].browse(ref_id) + if '.' in xmlid: + module, name = xmlid.split('.') + else: + module = '' + name = xmlid + return env['ir.model.data'].create({ + 'name': name, + 'module': module, + 'model': record._name, + 'res_id': record.id, + 'noupdate': noupdate, + }) + + +class CMSNotificationCase(TransactionCase): + + at_install = False + post_install = True + + def setUp(self): + super(CMSNotificationCase, self).setUp() + self.partner_model = self.env['res.partner'] + self.subtype_model = self.env['mail.message.subtype'] + + self.partner1 = self.partner_model.with_context( + tracking_disable=1).create({'name': 'Marty McFly'}) + self.subtype1 = self.subtype_model.create( + {'name': 'Back to the future I'}) + add_xmlid(self.env, self.subtype1, 'cms_notification.test_subtype1') + self.subtype2 = self.subtype_model.create( + {'name': 'Back to the future II'}) + add_xmlid(self.env, self.subtype2, 'cms_notification.test_subtype2') + self.subtype3 = self.subtype_model.create( + {'name': 'Back to the future III'}) + add_xmlid(self.env, self.subtype3, 'cms_notification.test_subtype3') + self.form_model_key = 'cms.notification.panel.form' + + def get_form(self, req=None, **kw): + request = req or fake_request() + return self.env[self.form_model_key].form_init(request, **kw) + + def _assert_values(self, expected, values): + for k, v in expected.iteritems(): + self.assertEqual(values[k], v) + + def test_form_defaults(self): + form = self.get_form(main_object=self.partner1) + defaults = form.form_load_defaults() + expected = dict( + # not specific record for disabling: all ON + enable_1=True, + enable_2=True, + enable_3=True, + ) + self._assert_values(expected, defaults) + + self.partner1._notify_enable_subtype(self.subtype1) + self.partner1._notify_enable_subtype(self.subtype2) + self.partner1._notify_disable_subtype(self.subtype3) + defaults = form.form_load_defaults() + expected = dict( + enable_1=True, + enable_2=True, + enable_3=False, + ) + self._assert_values(expected, defaults) + + def test_form_updates(self): + data = { + 'enable_1': 'on', + 'enable_2': '', + 'enable_3': 'on', + } + req = fake_request(form_data=data, method='POST') + form = self.get_form( + req=req, main_object=self.partner1) + form.form_process() + self.assertIn(self.subtype1, self.partner1.enabled_notify_subtype_ids) + self.assertIn(self.subtype2, self.partner1.disabled_notify_subtype_ids) + self.assertIn(self.subtype3, self.partner1.enabled_notify_subtype_ids) diff --git a/cms_notification/views/mail_message_subtype_views.xml b/cms_notification/views/mail_message_subtype_views.xml new file mode 100644 index 00000000..e16d5ab0 --- /dev/null +++ b/cms_notification/views/mail_message_subtype_views.xml @@ -0,0 +1,32 @@ + + + + + + + mail_digest mail.message.subtype.tree + mail.message.subtype + + + + + + + + + + mail_digest mail.message.subtype.form + mail.message.subtype + + + + + + + + + + diff --git a/oca_dependencies.txt b/oca_dependencies.txt index ac0117d1..2107237b 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,15 +1,4 @@ -# List the OCA project dependencies, one per line -# Add a repository url and branch if you need a forked version -# -# Examples -# ======== -# -# To depend on the standard version of sale-workflow, use: -# sale-workflow -# -# To explicitely give the URL of a fork, and still use the version specified in -# .travis.yml, use: -# sale-workflow https://github.com/OCA/sale-workflow -# -# To provide both the URL and a branch, use: -# sale-workflow https://github.com/OCA/sale-workflow branchname +# include `mail_digest` for `cms_notification` +social https://github.com/OCA/social 9.0 +# include `base_fontawesome` for `cms_notification` +server-tools https://github.com/OCA/server-tools 9.0