diff --git a/mail_private/README.rst b/mail_private/README.rst new file mode 100644 index 000000000..0416e0a55 --- /dev/null +++ b/mail_private/README.rst @@ -0,0 +1,74 @@ +============ +Mail Private +============ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/11.0/mail_private + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-11-0/social-11-0-mail_private + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/205/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This addon allows to define private groups on models. +Then, you can define private messages only visible for members of the groups + +**Table of contents** + +.. contents:: + :local: + +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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Creu Blanca + +Contributors +~~~~~~~~~~~~ + +* Enric Tobella + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +This module is part of the `OCA/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_private/__init__.py b/mail_private/__init__.py new file mode 100644 index 000000000..aee8895e7 --- /dev/null +++ b/mail_private/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/mail_private/__manifest__.py b/mail_private/__manifest__.py new file mode 100644 index 000000000..89904fc2f --- /dev/null +++ b/mail_private/__manifest__.py @@ -0,0 +1,28 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Mail Private', + 'summary': """ + Create private emails""", + 'version': '12.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Creu Blanca,Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/social', + 'depends': [ + 'mail', + ], + 'qweb': [ + 'static/src/xml/thread.xml', + 'static/src/xml/composer.xml', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/mail_security_group.xml', + 'wizards/mail_compose_message.xml', + 'security/security.xml', + 'views/ir_model.xml', + 'views/assets.xml', + 'views/mail_message.xml', + ], +} diff --git a/mail_private/models/__init__.py b/mail_private/models/__init__.py new file mode 100644 index 000000000..0826c2ae4 --- /dev/null +++ b/mail_private/models/__init__.py @@ -0,0 +1,8 @@ +from . import mail_message +from . import mail_thread +from . import ir_model +from . import res_partner +from . import mail_channel +from . import res_users +from . import ir_attachment +from . import mail_security_group diff --git a/mail_private/models/ir_attachment.py b/mail_private/models/ir_attachment.py new file mode 100644 index 000000000..a787a3be7 --- /dev/null +++ b/mail_private/models/ir_attachment.py @@ -0,0 +1,12 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class IrAttachment(models.Model): + _inherit = 'ir.attachment' + + mail_group_id = fields.Many2one( + 'mail.security.group' + ) diff --git a/mail_private/models/ir_model.py b/mail_private/models/ir_model.py new file mode 100644 index 000000000..49aece5ad --- /dev/null +++ b/mail_private/models/ir_model.py @@ -0,0 +1,15 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class IrModel(models.Model): + _inherit = 'ir.model' + + private_group_ids = fields.Many2many( + 'res.groups', + ) + mail_group_ids = fields.Many2many( + 'mail.security.group' + ) diff --git a/mail_private/models/mail_channel.py b/mail_private/models/mail_channel.py new file mode 100644 index 000000000..755bad839 --- /dev/null +++ b/mail_private/models/mail_channel.py @@ -0,0 +1,14 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class MailChannel(models.Model): + _inherit = 'mail.channel' + + @api.multi + def _notify(self, message): + if message.mail_group_id: + return + return super()._notify(message) diff --git a/mail_private/models/mail_message.py b/mail_private/models/mail_message.py new file mode 100644 index 000000000..fc57b67c6 --- /dev/null +++ b/mail_private/models/mail_message.py @@ -0,0 +1,21 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class MailMessage(models.Model): + _inherit = 'mail.message' + + mail_group_id = fields.Many2one('mail.security.group', readonly=False) + + @api.model + def _message_read_dict_postprocess(self, messages, message_tree): + result = super()._message_read_dict_postprocess(messages, message_tree) + for message_dict in messages: + message_id = message_dict.get('id') + message = message_tree[message_id] + message_dict['private'] = bool(message.mail_group_id) + message_dict['mail_group_id'] = message.mail_group_id.id + message_dict['mail_group'] = message.mail_group_id.name + return result diff --git a/mail_private/models/mail_security_group.py b/mail_private/models/mail_security_group.py new file mode 100644 index 000000000..59f92084f --- /dev/null +++ b/mail_private/models/mail_security_group.py @@ -0,0 +1,37 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MailSecurityGroup(models.Model): + + _name = 'mail.security.group' + _description = 'Mail Security Group' + + name = fields.Char(required=True) + model_ids = fields.Many2many( + 'ir.model' + ) + group_ids = fields.Many2many( + 'res.groups' + ) + button_name = fields.Char() + icon = fields.Char() + active = fields.Boolean( + default=True + ) + + def _get_security_groups(self): + vals = [] + for record in self: + vals.append(record._get_security_group()) + return vals + + def _get_security_group(self): + return { + 'id': self.id, + 'name': self.name, + 'button_name': self.button_name or self.name, + 'icon': self.icon, + } diff --git a/mail_private/models/mail_thread.py b/mail_private/models/mail_thread.py new file mode 100644 index 000000000..957880efa --- /dev/null +++ b/mail_private/models/mail_thread.py @@ -0,0 +1,93 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from lxml import etree +from odoo import api, fields, models +from odoo.osv.orm import setup_modifiers + + +class MailThread(models.AbstractModel): + + _inherit = 'mail.thread' + + allow_private = fields.Boolean( + compute='_compute_allow_private', + ) + + def _compute_allow_private(self): + groups = self.env['mail.security.group'].search([ + ('model_ids.model', '=', self._name), + ]) + users = groups.mapped('group_ids.users') + for record in self: + record.allow_private = groups and self.env.user in users + + @api.model + def get_message_security_groups(self): + return self.env['mail.security.group'].search([ + ('model_ids.model', '=', self._name), + ('group_ids.users', '=', self.env.user.id) + ])._get_security_groups() + + @api.model + def fields_view_get( + self, view_id=None, view_type="form", toolbar=False, submenu=False + ): + res = super().fields_view_get( + view_id=view_id, + view_type=view_type, + toolbar=toolbar, + submenu=submenu, + ) + if view_type == 'form': + doc = etree.XML(res['arch']) + for node in doc.xpath("//field[@name='message_ids']/.."): + element = etree.Element( + 'field', + attrib={ + "name": "allow_private", + "invisible": "1", + } + ) + setup_modifiers(element) + node.insert(0, element) + res['arch'] = etree.tostring(doc, encoding='unicode') + return res + + def _message_post_process_attachments( + self, attachments, attachment_ids, message_data + ): + if attachment_ids and self.env.context.get('default_mail_group_id'): + filtered_attachment_ids = self.env['ir.attachment'].sudo().search([ + ('res_model', '=', 'mail.compose.message'), + ('create_uid', '=', self._uid), + ('id', 'in', attachment_ids), + ]) + filtered_attachment_ids.write({ + 'mail_group_id': self.env.context.get('default_mail_group_id'), + }) + return super()._message_post_process_attachments( + attachments, attachment_ids, message_data) + + def message_get_message_notify_values(self, message, message_values): + if hasattr(super(), 'message_get_message_notify_values'): + message_vals = super().message_get_message_notify_values( + message, message_values) + else: + message_vals = message_values.copy() + if not message.mail_group_id: + return message_vals + partner_obj = self.env['res.partner'] + accepted_users = message.mail_group_id.mapped( + 'group_ids.users') + new_partners = partner_obj.browse() + for act, ext, pids in message_values.get('needaction_partner_ids', []): + if act != 6: + continue + partners = partner_obj.browse(pids) + for partner in partners: + if partner.user_ids in accepted_users: + new_partners |= partner + new_vals = {} + if new_partners: + new_vals['needaction_partner_ids'] = [(6, 0, new_partners.ids)] + return new_vals diff --git a/mail_private/models/res_partner.py b/mail_private/models/res_partner.py new file mode 100644 index 000000000..695126427 --- /dev/null +++ b/mail_private/models/res_partner.py @@ -0,0 +1,52 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ResPartner(models.Model): + + _inherit = 'res.partner' + + @api.multi + def _notify( + self, message, rdata, record, force_send=False, send_after_commit=True, + model_description=False, mail_auto_delete=True + ): + if not message.mail_group_id: + return super(ResPartner, self)._notify( + message, rdata=rdata, + record=record, + force_send=force_send, + send_after_commit=send_after_commit, + model_description=model_description, + mail_auto_delete=mail_auto_delete + ) + accepted_users = message.mail_group_id.mapped( + 'group_ids.users') + partners = self.browse() + for partner in self: + if partner.user_ids in accepted_users: + partners |= partner + return super(ResPartner, partners)._notify( + message, rdata=rdata, + record=record, + force_send=force_send, + send_after_commit=send_after_commit, + model_description=model_description, + mail_auto_delete=mail_auto_delete + ) + + @api.multi + def _notify_by_chat(self, message): + if not message.mail_group_id: + return super()._notify_by_chat(message) + accepted_users = message.mail_group_id.mapped( + 'group_ids.users') + partners = self.browse() + for partner in self: + if partner.user_ids in accepted_users: + partners |= partner + elif partner.user_ids: + message.sudo(partner.user_ids[0]).set_message_done() + return super(ResPartner, partners)._notify_by_chat(message) diff --git a/mail_private/models/res_users.py b/mail_private/models/res_users.py new file mode 100644 index 000000000..e766ced09 --- /dev/null +++ b/mail_private/models/res_users.py @@ -0,0 +1,20 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ResUsers(models.Model): + _inherit = 'res.users' + + message_group_ids = fields.Many2many( + 'mail.security.group', + compute='_compute_message_group_ids' + ) + + @api.depends('groups_id') + def _compute_message_group_ids(self): + for record in self: + record.message_group_ids = self.env['mail.security.group'].search([ + ('group_ids', 'in', record.groups_id.ids) + ]) diff --git a/mail_private/readme/CONFIGURE.rst b/mail_private/readme/CONFIGURE.rst new file mode 100644 index 000000000..fac5fbdb2 --- /dev/null +++ b/mail_private/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +* Access in developer mode +* Access to `Settings > Technical > Email > Mail Group` +* Create a new group from on a model that has a thread and specify some + security groups diff --git a/mail_private/readme/CONTRIBUTORS.rst b/mail_private/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..94e7b0a40 --- /dev/null +++ b/mail_private/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Enric Tobella +* Jaime Arroyo diff --git a/mail_private/readme/DESCRIPTION.rst b/mail_private/readme/DESCRIPTION.rst new file mode 100644 index 000000000..683d61829 --- /dev/null +++ b/mail_private/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This addon allows to define private groups on models. +Then, you can define private messages only visible for members of message groups. +Each model may have one or more visible groups. diff --git a/mail_private/readme/USAGE.rst b/mail_private/readme/USAGE.rst new file mode 100644 index 000000000..e9df5be95 --- /dev/null +++ b/mail_private/readme/USAGE.rst @@ -0,0 +1,5 @@ +* Access a record from a model with at least one mail group defined +* Try to send a message +* A new button will be added that allows to set the message as private. + There will be as many buttons as groups you can use. +* The message will only be visible by users that are on the group. diff --git a/mail_private/security/ir.model.access.csv b/mail_private/security/ir.model.access.csv new file mode 100644 index 000000000..66759ed53 --- /dev/null +++ b/mail_private/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_mail_message_group_all,mail.security.group.all,model_mail_security_group,,1,0,0,0 +manage_mail_message_group_all,mail.security.group.manage,model_mail_security_group,base.group_system,1,1,1,1 diff --git a/mail_private/security/security.xml b/mail_private/security/security.xml new file mode 100644 index 000000000..02dac7e67 --- /dev/null +++ b/mail_private/security/security.xml @@ -0,0 +1,16 @@ + + + + + + Message private rules + ['|', '|', ('mail_group_id', '=', False), ('model', '=', False), ('mail_group_id', 'in', user.message_group_ids.ids)] + + + + + + Attachment private rules + ['|', '|', ('mail_group_id', '=', False), ('res_model', '=', False), ('mail_group_id', 'in', user.message_group_ids.ids)] + + diff --git a/mail_private/static/description/icon.png b/mail_private/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/mail_private/static/description/icon.png differ diff --git a/mail_private/static/description/index.html b/mail_private/static/description/index.html new file mode 100644 index 000000000..43840e693 --- /dev/null +++ b/mail_private/static/description/index.html @@ -0,0 +1,420 @@ + + + + + + +Mail Private + + + +
+

Mail Private

+ + +

Beta License: AGPL-3 OCA/social Translate me on Weblate Try me on Runbot

+

This addon allows to define private groups on models. +Then, you can define private messages only visible for members of the groups

+

Table of contents

+ +
+

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.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Creu Blanca
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

This module is part of the OCA/social project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/mail_private/static/src/js/composer.js b/mail_private/static/src/js/composer.js new file mode 100644 index 000000000..baf4228fa --- /dev/null +++ b/mail_private/static/src/js/composer.js @@ -0,0 +1,75 @@ +/* Copyright 2020 Creu Blanca + License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */ + +odoo.define('mail_private.composer', function (require) { + "use strict"; + var ChatterComposer = require('mail.composer.Chatter'); + + ChatterComposer.include({ + init: function (parent, model, suggested_partners, options) { + this._super(parent, model, suggested_partners, options); + _.extend(this.events, { + 'click .o_composer_button_send_private': 'on_send_private', + }); + this.options.allow_private = + parent.record.data.allow_private || false; + }, + renderElement: function () { + var result = this._super.apply(this, arguments); + if (this.options.allow_private) { + var self = this; + this._rpc({ + model: this._model, + method: 'get_message_security_groups', + }).then(function (data) { + self.security_groups = data; + self._update_security_groups(); + }); + } + return result; + }, + _get_group_button: function (group) { + var $button = $('