From 5de17cf60759bfcd1b59a541773c60159e70fdea Mon Sep 17 00:00:00 2001 From: ArtyomLosev Date: Wed, 16 May 2018 21:00:48 +0500 Subject: [PATCH] [11.0][PORT] mail_private (#141) * [11.0][PORT] mail_private * [LINT] lint --- mail_base/static/lib/base.js | 6 + mail_private/__openerp__.py | 2 +- mail_private/models.py | 103 +++++++++- mail_private/static/src/js/mail_private.js | 203 ++++++++++++------- mail_private/static/src/xml/mail_private.xml | 10 +- 5 files changed, 236 insertions(+), 88 deletions(-) diff --git a/mail_base/static/lib/base.js b/mail_base/static/lib/base.js index 2cbcf81..42433e7 100644 --- a/mail_base/static/lib/base.js +++ b/mail_base/static/lib/base.js @@ -877,6 +877,12 @@ chat_manager.post_message = function (data, options) { attachment_ids: data.attachment_ids, }; + // for module mail_private + if (data.is_private) { + msg.is_private = data.is_private; + msg.channel_ids = data.channel_ids; + } + // Replace emojis by their unicode character _.each(_.keys(emoji_unicodes), function (key) { var escaped_key = String(key).replace(/([.*+?=^!:${}()|[\]\/\\])/g, '\\$1'); diff --git a/mail_private/__openerp__.py b/mail_private/__openerp__.py index 7cd6ffc..fdf16ea 100644 --- a/mail_private/__openerp__.py +++ b/mail_private/__openerp__.py @@ -33,5 +33,5 @@ "post_init_hook": None, "auto_install": False, - "installable": False, + "installable": True, } diff --git a/mail_private/models.py b/mail_private/models.py index 3a7b545..dd6b60d 100644 --- a/mail_private/models.py +++ b/mail_private/models.py @@ -6,8 +6,103 @@ class MailComposeMessage(models.TransientModel): is_private = fields.Boolean(string='Send Internal Message') + +class MailMessage(models.Model): + _inherit = 'mail.message' + + is_private = fields.Boolean(string='Send Internal Message') + + def send_recepients_for_internal_message(self, model, domain): + result = [] + default_resource = self.env[model].search(domain) + follower_ids = default_resource.message_follower_ids + + recipient_ids = [r.partner_id for r in follower_ids if r.partner_id] + # channel_ids = [c.channel_id for c in follower_ids if c.channel_id] + + for recipient in recipient_ids: + result.append({ + 'checked': len(recipient.user_ids) > 0, + 'partner_id': recipient.id, + 'full_name': recipient.name, + 'name': recipient.name, + 'email_address': recipient.email, + 'reason': 'Recipient' + }) + + # for channel in channel_ids: + # result.append({ + # 'checked': True, + # 'channel_id': channel.id, + # 'full_name': channel, + # 'name': '# '+channel.name, + # 'reason': 'Channel', + # }) + return result + @api.multi - def send_mail(self, auto_commit=False): - for w in self: - w.is_log = True if w.is_private else w.is_log - super(MailComposeMessage, self).send_mail(auto_commit=False) + def _notify(self, force_send=False, send_after_commit=True, user_signature=True): + self_sudo = self.sudo() + if not self_sudo.is_private: + super(MailMessage, self)._notify(force_send, send_after_commit, user_signature) + else: + self._notify_mail_private(force_send, send_after_commit, user_signature) + + @api.multi + def _notify_mail_private(self, force_send=False, send_after_commit=True, user_signature=True): + """ Compute recipients to notify based on specified recipients and document + followers. Delegate notification to partners to send emails and bus notifications + and to channels to broadcast messages on channels """ + group_user = self.env.ref('base.group_user') + # have a sudoed copy to manipulate partners (public can go here with website modules like forum / blog / ... ) + self_sudo = self.sudo() + + self.ensure_one() + partners_sudo = self_sudo.partner_ids + channels_sudo = self_sudo.channel_ids + + if self_sudo.subtype_id and self.model and self.res_id: + followers = self_sudo.env['mail.followers'].search([ + ('res_model', '=', self.model), + ('res_id', '=', self.res_id), + ('subtype_ids', 'in', self_sudo.subtype_id.id), + ]) + if self_sudo.subtype_id.internal: + followers = followers.filtered(lambda fol: fol.channel_id or (fol.partner_id.user_ids and group_user in fol.partner_id.user_ids[0].mapped('groups_id'))) + channels_sudo |= followers.mapped('channel_id') + + # remove author from notified partners + if not self._context.get('mail_notify_author', False) and self_sudo.author_id: + partners_sudo = partners_sudo - self_sudo.author_id + + # update message, with maybe custom valuesz + message_values = {} + if channels_sudo: + message_values['channel_ids'] = [(6, 0, channels_sudo.ids)] + if partners_sudo: + message_values['needaction_partner_ids'] = [(6, 0, partners_sudo.ids)] + if self.model and self.res_id and hasattr(self.env[self.model], 'message_get_message_notify_values'): + message_values.update(self.env[self.model].browse(self.res_id).message_get_message_notify_values(self, message_values)) + if message_values: + self.write(message_values) + + # notify partners and channels + # those methods are called as SUPERUSER because portal users posting messages + # have no access to partner model. Maybe propagating a real uid could be necessary. + email_channels = channels_sudo.filtered(lambda channel: channel.email_send) + notif_partners = partners_sudo.filtered(lambda partner: 'inbox' in partner.mapped('user_ids.notification_type')) + if email_channels or partners_sudo - notif_partners: + partners_sudo.search([ + '|', + ('id', 'in', (partners_sudo - notif_partners).ids), + ('channel_ids', 'in', email_channels.ids), + ('email', '!=', self_sudo.author_id.email or self_sudo.email_from), + ])._notify(self, force_send=force_send, send_after_commit=send_after_commit, user_signature=user_signature) + channels_sudo._notify(self) + + # Discard cache, because child / parent allow reading and therefore + # change access rights. + if self.parent_id: + self.parent_id.invalidate_cache() + + return True diff --git a/mail_private/static/src/js/mail_private.js b/mail_private/static/src/js/mail_private.js index d311b3c..9ee48e1 100644 --- a/mail_private/static/src/js/mail_private.js +++ b/mail_private/static/src/js/mail_private.js @@ -3,10 +3,11 @@ odoo.define('mail_private', function (require) { var core = require('web.core'); var Chatter = require('mail.Chatter'); -var MailComposer = require('mail_base.base').MailComposer; -var chat_manager = require('mail.chat_manager'); +var ChatterComposer = require('mail.ChatterComposer'); +var chat_manager = require('mail_base.base').chat_manager; var session = require('web.session'); -var Model = require('web.Model'); + +var rpc = require('web.rpc'); var config = require('web.config'); var utils = require('mail.utils'); @@ -18,97 +19,126 @@ Chatter.include({ this.events['click .oe_compose_post_private'] = 'on_open_composer_private_message'; }, - on_post_message: function (message) { - var self = this; - if (this.private) { - message.subtype = false; - } - var options = {model: this.model, res_id: this.res_id}; - chat_manager.post_message(message, options).then( - function () { - self.close_composer(); - if (message.partner_ids.length) { - self.refresh_followers(); - } - }).fail(function () { - // todo: display notification - }); - }, - on_open_composer_private_message: function (event) { var self = this; - this.private = true; - this.get_recipients_for_internal_message().then(function (data) { - self.recipients_for_internal_message = data; - self.open_composer({is_private: true}); + this.fetch_recipients_for_internal_message().then(function (data) { + self._openComposer({ + is_private: true, + suggested_partners: data + }); }); }, - on_open_composer_new_message: function () { - this._super.apply(this, arguments); - this.private = false; - }, - - open_composer: function (options) { + _openComposer: function (options) { var self = this; - this._super.apply(this, arguments); - if (options && options.is_private) { - - this.composer.options.is_private = options.is_private; - - _.each(self.recipients_for_internal_message, function (partner) { - self.composer.suggested_partners.push({ - checked: (partner.user_ids.length > 0), - partner_id: partner.id, - full_name: partner.name, - name: partner.name, - email_address: partner.email, - reason: _.include(partner.user_ids, self.session.uid) - ?'Partner' - :'Follower' + var old_composer = this.composer; + // create the new composer + this.composer = new ChatterComposer(this, this.record.model, options.suggested_partners || [], { + commands_enabled: false, + context: this.context, + input_min_height: 50, + input_max_height: Number.MAX_VALUE, + input_baseline: 14, + is_log: options && options.is_log, + record_name: this.record_name, + default_body: old_composer && old_composer.$input && old_composer.$input.val(), + default_mention_selections: old_composer && old_composer.mention_get_listener_selections(), + is_private: options.is_private + }); + this.composer.on('input_focused', this, function () { + this.composer.mention_set_prefetched_partners(this.mentionSuggestions || []); + }); + this.composer.insertAfter(this.$('.o_chatter_topbar')).then(function () { + // destroy existing composer + if (old_composer) { + old_composer.destroy(); + } + if (!config.device.touch) { + self.composer.focus(); + } + self.composer.on('post_message', self, function (message) { + if (options.is_private) { + self.composer.options.is_log = true; + } + self.fields.thread.postMessage(message).then(function () { + + self._closeComposer(true); + if (self.postRefresh === 'always' || (self.postRefresh === 'recipients' && message.partner_ids.length)) { + self.trigger_up('reload'); + } }); }); - } + var toggle_post_private = self.composer.options.is_private || false; + self.composer.on('need_refresh', self, self.trigger_up.bind(self, 'reload')); + self.composer.on('close_composer', null, self._closeComposer.bind(self, true)); + + self.$el.addClass('o_chatter_composer_active'); + self.$('.o_chatter_button_new_message, .o_chatter_button_log_note, .oe_compose_post_private').removeClass('o_active'); + self.$('.o_chatter_button_new_message').toggleClass('o_active', !self.composer.options.is_log && !self.composer.options.is_private); + self.$('.o_chatter_button_log_note').toggleClass('o_active', (self.composer.options.is_log && !options.is_private)); + self.$('.oe_compose_post_private').toggleClass('o_active', toggle_post_private); + }); }, - get_recipients_for_internal_message: function () { + fetch_recipients_for_internal_message: function () { var self = this; self.result = {}; - return new Model(this.context.default_model).query( - ['message_follower_ids', 'partner_id']).filter( - [['id', '=', self.context.default_res_id]]).all(). - then(function (thread) { - var follower_ids = thread[0].message_follower_ids; - self.result[self.context.default_res_id] = []; - self.customer = thread[0].partner_id; - - // Fetch partner ids - return new Model('mail.followers').call( - 'read', [follower_ids, ['partner_id']]).then(function (res_partners) { - // Filter result and push to array - var res_partners_filtered = _.map(res_partners, function (partner) { - if (partner.partner_id[0] && partner.partner_id[0] !== session.partner_id ) { - return partner.partner_id[0]; - } - }).filter(function (partner) { - return typeof partner !== 'undefined'; - }); - - return new Model('res.partner').call( - 'read', [res_partners_filtered, ['name', 'email', 'user_ids']] - ).then(function (recipients) { - return recipients; - }); - }); + var follower_ids_domain = [['id', '=', self.context.default_res_id]]; + return rpc.query({ + model: 'mail.message', + method: 'send_recepients_for_internal_message', + args: [[], self.context.default_model, follower_ids_domain] + }).then(function (res) { + return _.filter(res, function (obj) { + return obj.partner_id !== session.partner_id; + }); }); } }); -MailComposer.include({ - init: function (parent, dataset, options) { - this._super(parent, dataset, options); +ChatterComposer.include({ + init: function (parent, model, suggested_partners, options) { + this._super(parent, model, suggested_partners, options); this.events['click .oe_composer_uncheck'] = 'on_uncheck_recipients'; + }, + + preprocess_message: function () { + var self = this; + var def = $.Deferred(); + this._super().then(function (message) { + message = _.extend(message, { + subtype: 'mail.mt_comment', + message_type: 'comment', + content_subtype: 'html', + context: self.context, + }); + + // Subtype + if (self.options.is_log) { + message.subtype = 'mail.mt_note'; + } + + if (self.options.is_private) { + message.is_private = true; + message.channel_ids = self.get_checked_channel_ids(); + } + // Partner_ids + if (!self.options.is_log) { + var checked_suggested_partners = self.get_checked_suggested_partners(); + self.check_suggested_partners(checked_suggested_partners).done(function (partner_ids) { + message.partner_ids = (message.partner_ids || []).concat(partner_ids); + // update context + message.context = _.defaults({}, message.context, { + mail_post_autofollow: true, + }); + def.resolve(message); + }); + } else { + def.resolve(message); + } + }); + return def; }, on_uncheck_recipients: function () { @@ -121,7 +151,6 @@ MailComposer.include({ if (!this.do_check_attachment_upload()){ return false; } - var self = this; var recipient_done = $.Deferred(); if (this.options.is_log) { @@ -138,6 +167,7 @@ MailComposer.include({ default_partner_ids: partner_ids, default_is_log: self.options.is_log, mail_post_autofollow: true, + is_private: self.options.is_private, }; if (self.options && self.options.is_private) { @@ -148,7 +178,6 @@ MailComposer.include({ context.default_model = self.context.default_model; context.default_res_id = self.context.default_res_id; } - self.do_action({ type: 'ir.actions.act_window', res_model: 'mail.compose.message', @@ -174,9 +203,27 @@ MailComposer.include({ _.each(checked_partners, function (partner) { partner.checked = true; }); + checked_partners = _.uniq(_.filter(checked_partners, function (obj) { + return obj.reason !== 'Channel'; + })); + this.get_checked_channel_ids(); return checked_partners; }, + get_checked_channel_ids: function () { + var self = this; + var checked_channels = []; + this.$('.o_composer_suggested_partners input:checked').each(function() { + var full_name = $(this).data('fullname'); + checked_channels = checked_channels.concat(_.filter(self.suggested_partners, function(item) { + return full_name === item.full_name; + })); + }); + checked_channels = _.uniq(_.filter(checked_channels, function (obj) { + return obj.reason === 'Channel'; + })); + return _.pluck(checked_channels, 'channel_id'); + } }); }); diff --git a/mail_private/static/src/xml/mail_private.xml b/mail_private/static/src/xml/mail_private.xml index 5607c9e..baadbb5 100644 --- a/mail_private/static/src/xml/mail_private.xml +++ b/mail_private/static/src/xml/mail_private.xml @@ -1,15 +1,15 @@