Browse Source

[IMP] select "internal partners" by defualt (#112)

[FIX] remove incorrect label "TO: followers of ..."
[FIX] support Mail Composer in Internal Message
pull/114/head
ArtyomLosev 7 years ago
committed by Ivan Yelizariev
parent
commit
48d9fe4531
  1. 290
      mail_base/static/lib/base.js
  2. 2
      mail_private/__openerp__.py
  3. 27
      mail_private/full_composer_wizard.xml
  4. 11
      mail_private/models.py
  5. 209
      mail_private/static/src/js/mail_private.js
  6. 26
      mail_private/static/src/xml/mail_private.xml

290
mail_base/static/lib/base.js

@ -11,6 +11,11 @@ var session = require('web.session');
var time = require('web.time');
var web_client = require('web.web_client');
var composer = require('mail.composer');
var config = require('web.config');
var Chatter = require('mail.Chatter');
var form_common = require('web.form_common');
var _t = core._t;
var _lt = core._lt;
var LIMIT = 100;
@ -125,6 +130,288 @@ ChatAction.include({
}
});
var MailComposer = composer.BasicComposer.extend({
template: 'mail.chatter.ChatComposer',
init: function (parent, dataset, options) {
this._super(parent, options);
this.thread_dataset = dataset;
this.suggested_partners = [];
this.options = _.defaults(this.options, {
display_mode: 'textarea',
record_name: false,
is_log: false,
});
if (this.options.is_log) {
this.options.send_text = _t('Log');
}
this.events = _.extend(this.events, {
'click .o_composer_button_full_composer': 'on_open_full_composer',
});
},
willStart: function () {
if (this.options.is_log) {
return this._super.apply(this, arguments);
}
return $.when(this._super.apply(this, arguments), this.message_get_suggested_recipients());
},
should_send: function () {
return false;
},
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';
}
// 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;
},
/**
* Send the message on SHIFT+ENTER, but go to new line on ENTER
*/
prevent_send: function (event) {
return !event.shiftKey;
},
message_get_suggested_recipients: function () {
var self = this;
var email_addresses = _.pluck(this.suggested_partners, 'email_address');
return this.thread_dataset
.call('message_get_suggested_recipients', [[this.context.default_res_id], this.context])
.done(function (suggested_recipients) {
var thread_recipients = suggested_recipients[self.context.default_res_id];
_.each(thread_recipients, function (recipient) {
var parsed_email = utils.parse_email(recipient[1]);
if (_.indexOf(email_addresses, parsed_email[1]) === -1) {
self.suggested_partners.push({
checked: true,
partner_id: recipient[0],
full_name: recipient[1],
name: parsed_email[0],
email_address: parsed_email[1],
reason: recipient[2],
});
}
});
});
},
/**
* Get the list of selected suggested partners
* @returns Array() : list of 'recipient' selected partners (may not be created in db)
**/
get_checked_suggested_partners: function () {
var self = this;
var checked_partners = [];
this.$('.o_composer_suggested_partners input:checked').each(function() {
var full_name = $(this).data('fullname');
checked_partners = checked_partners.concat(_.filter(self.suggested_partners, function(item) {
return full_name === item.full_name;
}));
});
return checked_partners;
},
/**
* Check the additional partners (not necessary registered partners), and open a popup form view
* for the ones who informations is missing.
* @param Array : list of 'recipient' partners to complete informations or validate
* @returns Deferred resolved with the list of checked suggested partners (real partner)
**/
check_suggested_partners: function (checked_suggested_partners) {
var self = this;
var check_done = $.Deferred();
var recipients = _.filter(checked_suggested_partners, function (recipient) { return recipient.checked; });
var recipients_to_find = _.filter(recipients, function (recipient) { return (! recipient.partner_id); });
var names_to_find = _.pluck(recipients_to_find, 'full_name');
var recipients_to_check = _.filter(recipients, function (recipient) { return (recipient.partner_id && ! recipient.email_address); });
var recipient_ids = _.pluck(_.filter(recipients, function (recipient) { return recipient.partner_id && recipient.email_address; }), 'partner_id');
var names_to_remove = [];
var recipient_ids_to_remove = [];
// have unknown names -> call message_get_partner_info_from_emails to try to find partner_id
var find_done = $.Deferred();
if (names_to_find.length > 0) {
find_done = self.thread_dataset.call('message_partner_info_from_emails', [[this.context.default_res_id], names_to_find]);
} else {
find_done.resolve([]);
}
// for unknown names + incomplete partners -> open popup - cancel = remove from recipients
$.when(find_done).pipe(function (result) {
var emails_deferred = [];
var recipient_popups = result.concat(recipients_to_check);
_.each(recipient_popups, function (partner_info) {
var deferred = $.Deferred();
emails_deferred.push(deferred);
var partner_name = partner_info.full_name;
var partner_id = partner_info.partner_id;
var parsed_email = utils.parse_email(partner_name);
var dialog = new form_common.FormViewDialog(self, {
res_model: 'res.partner',
res_id: partner_id,
context: {
force_email: true,
ref: "compound_context",
default_name: parsed_email[0],
default_email: parsed_email[1],
},
title: _t("Please complete partner's informations"),
disable_multiple_selection: true,
}).open();
dialog.on('closed', self, function () {
deferred.resolve();
});
dialog.opened().then(function () {
dialog.view_form.on('on_button_cancel', self, function () {
names_to_remove.push(partner_name);
if (partner_id) {
recipient_ids_to_remove.push(partner_id);
}
});
});
});
$.when.apply($, emails_deferred).then(function () {
var new_names_to_find = _.difference(names_to_find, names_to_remove);
find_done = $.Deferred();
if (new_names_to_find.length > 0) {
find_done = self.thread_dataset.call('message_partner_info_from_emails', [[self.context.default_res_id], new_names_to_find, true]);
} else {
find_done.resolve([]);
}
$.when(find_done).pipe(function (result) {
var recipient_popups = result.concat(recipients_to_check);
_.each(recipient_popups, function (partner_info) {
if (partner_info.partner_id && _.indexOf(partner_info.partner_id, recipient_ids_to_remove) === -1) {
recipient_ids.push(partner_info.partner_id);
}
});
}).pipe(function () {
check_done.resolve(recipient_ids);
});
});
});
return check_done;
},
on_open_full_composer: function() {
if (!this.do_check_attachment_upload()){
return false;
}
var self = this;
var recipient_done = $.Deferred();
if (this.options.is_log) {
recipient_done.resolve([]);
} else {
var checked_suggested_partners = this.get_checked_suggested_partners();
recipient_done = this.check_suggested_partners(checked_suggested_partners);
}
recipient_done.then(function (partner_ids) {
var context = {
default_parent_id: self.id,
default_body: utils.get_text2html(self.$input.val()),
default_attachment_ids: _.pluck(self.get('attachment_ids'), 'id'),
default_partner_ids: partner_ids,
default_is_log: self.options.is_log,
mail_post_autofollow: true,
};
if (self.context.default_model && self.context.default_res_id) {
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',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
target: 'new',
context: context,
}, {
on_close: function() {
self.trigger('need_refresh');
var parent = self.getParent();
chat_manager.get_messages({model: parent.model, res_id: parent.res_id});
},
}).then(self.trigger.bind(self, 'close_composer'));
});
}
});
Chatter.include({
open_composer: function (options) {
var self = this;
var old_composer = this.composer;
// create the new composer
this.composer = new MailComposer(this, this.thread_dataset, {
commands_enabled: false,
context: this.context,
input_min_height: 50,
input_max_height: Number.MAX_VALUE, // no max_height limit for the chatter
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(),
});
this.composer.on('input_focused', this, function () {
this.composer.mention_set_prefetched_partners(this.mention_suggestions || []);
});
this.composer.insertBefore(this.$('.o_mail_thread')).then(function () {
// destroy existing composer
if (old_composer) {
old_composer.destroy();
}
if (!config.device.touch) {
self.composer.focus();
}
self.composer.on('post_message', self, self.on_post_message);
self.composer.on('need_refresh', self, self.refresh_followers);
self.composer.on('close_composer', null, self.close_composer.bind(self, true));
});
this.mute_new_message_button(true);
},
});
var MailTools = core.Class.extend({
notify_incoming_message: function (msg, options) {
@ -1164,7 +1451,8 @@ chat_manager.is_ready = init();
return {
ODOOBOT_ID: ODOOBOT_ID,
chat_manager: chat_manager,
MailTools: MailTools
MailTools: MailTools,
MailComposer: MailComposer
};
});

2
mail_private/__openerp__.py

@ -16,10 +16,12 @@
"depends": [
"mail",
"base"
],
"external_dependencies": {"python": [], "bin": []},
"data": [
'template.xml',
'full_composer_wizard.xml',
],
"qweb": [
'static/src/xml/mail_private.xml',

27
mail_private/full_composer_wizard.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record model="ir.ui.view" id="email_compose_message_wizard_form_private">
<field name="name">mail.compose.message.form.private</field>
<field name="model">mail.compose.message</field>
<field name="groups_id" eval="[(4,ref('base.group_user'))]"/>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='active_domain']" position="after">
<field name="is_private" invisible="1" />
</xpath>
<xpath expr="//div[@groups='base.group_user']/span[2]" position="attributes">
<attribute name="attrs">{'invisible': [('is_private', '=', True)]}
</attribute>
</xpath>
</data>
</field>
</record>
</data>
</odoo>

11
mail_private/models.py

@ -1,9 +1,14 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
from odoo import models, fields, api
class MailComposeMessage(models.TransientModel):
_inherit = 'mail.compose.message'
private = fields.Boolean(string='Send Internal Message')
is_private = fields.Boolean(string='Send Internal Message')
@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)

209
mail_private/static/src/js/mail_private.js

@ -3,19 +3,22 @@ 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 session = require('web.session');
var Model = require('web.Model');
var config = require('web.config');
var utils = require('mail.utils');
Chatter.include({
init: function () {
this._super.apply(this, arguments);
this.private = false;
this.events['click .oe_compose_post_private'] = 'on_open_composer_private_message';
},
Chatter.include({
init: function () {
this._super.apply(this, arguments);
this.private = false;
this.events['click .oe_compose_post_private'] = 'on_open_composer_private_message';
},
on_post_message: function (message) {
on_post_message: function (message) {
var self = this;
if (this.private) {
message.subtype = false;
@ -30,71 +33,147 @@ var Model = require('web.Model');
}).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});
});
},
on_open_composer_private_message: function (event) {
var self = this;
this.private = true;
this.get_recipients_for_internal_message().then(function (data) {
var private_message = 'private_message';
self.recipients_for_internal_message = data;
self.open_composer(private_message);
on_open_composer_new_message: function () {
this._super.apply(this, arguments);
this.private = false;
},
open_composer: 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'
});
});
},
}
},
on_open_composer_new_message: function () {
this._super.apply(this, arguments);
this.private = false;
},
get_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;
open_composer: function (options) {
var self = this;
this._super.apply(this, arguments);
if (options === 'private_message') {
//Clear existing suggested partners
for (var i=0; i<self.recipients_for_internal_message.length; i++) {
this.composer.suggested_partners.push({
checked: true,
partner_id: self.recipients_for_internal_message[i].id,
full_name: self.recipients_for_internal_message[i].name,
name: self.recipients_for_internal_message[i].name,
email_address: self.recipients_for_internal_message[i].email,
reason: _.include(self.recipients_for_internal_message[i].user_ids, self.session.uid)
?'Partner'
:'Follower'
});
}
}
},
// 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';
});
get_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) {
var res_partners_filtered = [];
// Filter result and push to array
_.each(res_partners, function (partner) {
if (partner.partner_id[0] && partner.partner_id[0] !== session.partner_id ) {
res_partners_filtered.push(partner.partner_id[0]);
}
return new Model('res.partner').call(
'read', [res_partners_filtered, ['name', 'email', 'user_ids']]
).then(function (recipients) {
return recipients;
});
return new Model('res.partner').call(
'read', [res_partners_filtered, ['name', 'email', 'user_ids']]
).then(function (recipients) {
return recipients;
});
});
});
});
});
}
});
MailComposer.include({
init: function (parent, dataset, options) {
this._super(parent, dataset, options);
this.events['click .oe_composer_uncheck'] = 'on_uncheck_recipients';
},
on_uncheck_recipients: function () {
this.$('.o_composer_suggested_partners input:checked').each(function() {
$(this).prop('checked', false);
});
},
on_open_full_composer: function() {
if (!this.do_check_attachment_upload()){
return false;
}
});
var self = this;
var recipient_done = $.Deferred();
if (this.options.is_log) {
recipient_done.resolve([]);
} else {
var checked_suggested_partners = this.get_checked_suggested_partners();
recipient_done = this.check_suggested_partners(checked_suggested_partners);
}
recipient_done.then(function (partner_ids) {
var context = {
default_parent_id: self.id,
default_body: utils.get_text2html(self.$input.val()),
default_attachment_ids: _.pluck(self.get('attachment_ids'), 'id'),
default_partner_ids: partner_ids,
default_is_log: self.options.is_log,
default_is_private: self.options && self.options.is_private,
mail_post_autofollow: true,
};
if (self.context.default_model && self.context.default_res_id) {
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',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
target: 'new',
context: context,
}, {
on_close: function() {
self.trigger('need_refresh');
var parent = self.getParent();
chat_manager.get_messages({model: parent.model, res_id: parent.res_id});
},
}).then(self.trigger.bind(self, 'close_composer'));
});
},
get_checked_suggested_partners: function () {
var checked_partners = this._super(this, arguments);
// workaround: odoo code works only when all partners are checked intially,
// while may select only some of them (internal recepients)
_.each(checked_partners, function (partner) {
partner.checked = true;
});
return checked_partners;
},
});
});

26
mail_private/static/src/xml/mail_private.xml

@ -1,21 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<t t-extend="mail.Chatter">
<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>
</t>
</t>
<t t-extend="mail.chatter.ChatComposer">
<t t-jquery="[t-if='!widget.is_private']" t-operation="replace">
<t t-if="!widget.is_private and !widget.private">
<span class="oe_all_follower">
<t t-if="widget.parent_thread.parent_message.record_name">
Followers of <t t-raw="'&quot;' + widget.parent_thread.parent_message.record_name + '&quot;'"/>
<t t-jquery="span[class='o_chatter_composer_info']" t-operation="replace">
<span 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;
</t>
<t t-if="!widget.parent_thread.parent_message.record_name and widget.options.view_inbox">My Followers</t>
<t t-if="!widget.parent_thread.parent_message.record_name and !widget.options.view_inbox">Followers of this document</t>
</span>
</t>
</t>
<t t-if="!widget.options.record_name">
this document
</t>
</span>
</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>
</t>
</t>
</template>
Loading…
Cancel
Save