Browse Source

[11.0][PORT] mail_private (#141)

* [11.0][PORT] mail_private

* [LINT] lint
pull/151/head
ArtyomLosev 6 years ago
committed by Ivan Yelizariev
parent
commit
5de17cf607
  1. 6
      mail_base/static/lib/base.js
  2. 2
      mail_private/__openerp__.py
  3. 103
      mail_private/models.py
  4. 203
      mail_private/static/src/js/mail_private.js
  5. 10
      mail_private/static/src/xml/mail_private.xml

6
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');

2
mail_private/__openerp__.py

@ -33,5 +33,5 @@
"post_init_hook": None,
"auto_install": False,
"installable": False,
"installable": True,
}

103
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

203
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');
}
});
});

10
mail_private/static/src/xml/mail_private.xml

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<t t-extend="mail.Chatter">
<t t-extend="mail.Chatter.Buttons">
<t t-jquery="button[title='Send a message']" t-operation="after">
<button class="btn btn-sm btn-link oe_compose_post_private" t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox" title="Send a message to specified recipients only">Send internal message</button>
<button class="btn btn-sm btn-link oe_compose_post_private" title="Send a message to specified recipients only">Send internal message</button>
</t>
</t>
<t t-extend="mail.chatter.ChatComposer">
<t t-jquery="span[class='o_chatter_composer_info']" t-operation="replace">
<span class="o_chatter_composer_info" t-if="!widget.options.is_private">
<t t-jquery="small[class='o_chatter_composer_info']" t-operation="replace">
<small class="o_chatter_composer_info" t-if="!widget.options.is_private">
To: Followers of
<t t-if="widget.options.record_name">
&quot;<t t-esc="widget.options.record_name"/>&quot;
@ -17,7 +17,7 @@
<t t-if="!widget.options.record_name">
this document
</t>
</span>
</small>
</t>
<t t-jquery="div[class='o_composer_suggested_partners']" t-operation="after">
<button class="btn btn-sm btn-link oe_composer_uncheck" t-if="widget.options.is_private">Uncheck all</button>

Loading…
Cancel
Save