diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 294adc863b5..4fd2bcfcfd1 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -963,6 +963,15 @@ class crm_lead(base_stage, format_address, osv.osv): return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False for lead in self.browse(cr, uid, ids, context=context)] + def message_get_suggested_recipients(self, cr, uid, ids, context=None): + recipients = super(crm_lead, self).message_get_suggested_recipients(cr, uid, ids, context=context) + for lead in self.browse(cr, uid, ids, context=context): + if lead.partner_id: + recipients[lead.id].append([lead.partner_id.id, '%s<%s>' % (lead.partner_id.name, lead.partner_id.email)]) + elif lead.email_from: + recipients[lead.id].append([False, lead.email_from]) + return recipients + def message_new(self, cr, uid, msg, custom_values=None, context=None): """ Overrides mail_thread message_new that is called by the mailgateway through message_process. diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index d9ad86af1e7..1ccb4b318cc 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -820,41 +820,42 @@ class mail_thread(osv.AbstractModel): "now deprecated res.log.") self.message_post(cr, uid, [id], message, context=context) - def message_create_partners_from_emails(self, cr, uid, emails, context=None): + def message_get_suggested_recipients(self, cr, uid, ids, context=None): + return dict.fromkeys(ids, list()) + + def message_get_partner_info_from_emails(self, cr, uid, emails, link_mail=False, context=None): """ Convert a list of emails into a list partner_ids and a list new_partner_ids. The return value is non conventional because it is meant to be used by the mail widget. :return dict: partner_ids and new_partner_ids """ - partner_obj = self.pool.get('res.partner') mail_message_obj = self.pool.get('mail.message') - - partner_ids = [] - new_partner_ids = [] + partner_obj = self.pool.get('res.partner') + partner_info = dict() for email in emails: m = re.search(r"((.+?)\s*<)?([^<>]+@[^<>]+)>?", email, re.IGNORECASE | re.DOTALL) - name = m.group(2) or m.group(0) - email = m.group(3) - ids = partner_obj.search(cr, SUPERUSER_ID, [('email', '=', email)], context=context) + email_address = m.group(3) + partner_info.setdefault(email_address, dict()).setdefault('full_name', email) + ids = partner_obj.search(cr, SUPERUSER_ID, [('email', '=', email_address)], context=context) if ids: - partner_ids.append(ids[0]) - partner_id = ids[0] + partner_info[email_address]['partner_id'] = ids[0] else: - partner_id = partner_obj.create(cr, uid, { - 'name': name or email, - 'email': email, - }, context=context) - new_partner_ids.append(partner_id) + partner_info[email_address]['partner_id'] = False # link mail with this from mail to the new partner id - message_ids = mail_message_obj.search(cr, SUPERUSER_ID, ['|', ('email_from', '=', email), ('email_from', 'ilike', '<%s>' % email), ('author_id', '=', False)], context=context) - if message_ids: - mail_message_obj.write(cr, SUPERUSER_ID, message_ids, {'email_from': None, 'author_id': partner_id}, context=context) - return { - 'partner_ids': partner_ids, - 'new_partner_ids': new_partner_ids, - } + if link_mail and ids: + print email, ids + message_ids = mail_message_obj.search(cr, SUPERUSER_ID, [ + '|', + ('email_from', '=', email), + ('email_from', 'ilike', '<%s>' % email), + ('author_id', '=', False) + ], context=context) + if message_ids: + # mail_message_obj.write(cr, SUPERUSER_ID, message_ids, {'author_id': ids[0]}, context=context) + print 'found', message_ids + return partner_info def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs): diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index c441499ddd8..18010937a11 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -272,20 +272,20 @@ margin-top: 4px; margin-bottom: 4px; } -.openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list{ +.openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list { display: block; } -.openerp .oe_mail .oe_msg_composer .oe_emails_from{ +.openerp .oe_mail .oe_msg_composer .oe_recipients { font-size: 12px; margin-left: 20px; margin-bottom: 2px; } -.openerp .oe_mail .oe_msg_composer .oe_emails_from label{ +.openerp .oe_mail .oe_msg_composer .oe_recipients label{ vertical-align: middle; display: block; line-height: 14px; } -.openerp .oe_mail .oe_msg_composer .oe_emails_from input{ +.openerp .oe_mail .oe_msg_composer .oe_recipients input{ vertical-align: middle; } .openerp .oe_mail .oe_attachment{ diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 5c36c32c15a..777f17f1ceb 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -21,6 +21,15 @@ openerp.mail = function (session) { mail.ChatterUtils = { + /** parse text to find email: Tagada -> [Tagada, address@mail.fr] or False */ + parse_email: function (text) { + var email = text.match(/(.*)<(.*@.*)>/); + if (! email) { + return [text, false]; + } + return [_.str.trim(email[1]), email[2]]; + }, + /* Get an image in /web/binary/image?... */ get_image: function (session, model, field, id, resize) { var r = resize ? encodeURIComponent(resize) : ''; @@ -255,12 +264,8 @@ openerp.mail = function (session) { this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid); } if (this.author_id && this.author_id[1]) { - var email = this.author_id[1].match(/(.*)<(.*@.*)>/); - if (!email) { - this.author_id.push(_.str.escapeHTML(this.author_id[1]), '', this.author_id[1]); - } else { - this.author_id.push(_.str.escapeHTML(email[0]), _.str.trim(email[1]), email[2]); - } + var parsed_email = mail.ChatterUtils.parse_email(this.author_id[1]); + this.author_id.push(parsed_email[0], parsed_email[1]); } if (this.partner_ids && this.partner_ids.length > 3) { this.extra_partners_nbr = this.partner_ids.length - 3; @@ -379,14 +384,23 @@ openerp.mail = function (session) { * @param {Object} [context] context passed to the * mail.compose.message DataSetSearch. Please refer to this model * for more details about fields and default values. + * @param {Object} recipients = [ + { + 'email_address': [str], + 'partner_id': False/[int], + 'name': [str], + 'full_name': name, + }, + { ... }, + ] */ init: function (parent, datasets, options) { this._super(parent, datasets, options); this.show_compact_message = false; this.show_delete_attachment = true; - this.emails_from = []; - this.partners_from = []; + this.recipients = []; + this.recipient_ids = []; }, start: function () { @@ -487,16 +501,12 @@ openerp.mail = function (session) { bind_events: function () { var self = this; - - this.$('.oe_compact').on('click', _.bind( this.on_compose_expandable, this)); - - // set the function called when attachments are added - this.$('input.oe_form_binary_file').on('change', _.bind( this.on_attachment_change, this) ); - - this.$('.oe_cancel').on('click', _.bind( this.on_cancel, this) ); - this.$('.oe_post').on('click', _.bind( this.on_message_post, this) ); - this.$('.oe_log').on('click', _.bind( this.on_message_log, this) ); - this.$('.oe_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment') ); + this.$('.oe_compact').on('click', _.bind( this.on_toggle_quick_composer, this)); + this.$('input.oe_form_binary_file').on('change', _.bind( this.on_attachment_change, this)); + this.$('.oe_cancel').on('click', _.bind( this.on_cancel, this)); + this.$('.oe_post').on('click', _.bind( this.on_message_post, this)); + this.$('.oe_log').on('click', _.bind( this.on_message_post, this)); + this.$('.oe_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment')); /* stack for don't close the compose form if the user click on a button */ this.$('.oe_msg_left, .oe_msg_center').on('mousedown', _.bind( function () { this.stay_open = true; }, this)); this.$('.oe_msg_left, .oe_msg_content').on('mouseup', _.bind( function () { this.$('textarea').focus(); }, this)); @@ -506,13 +516,12 @@ openerp.mail = function (session) { this.$('textarea').autosize(); // auto close - this.$('textarea').on('blur', _.bind( this.on_compose_expandable, this)); + this.$('textarea').on('blur', _.bind( this.on_toggle_quick_composer, this)); // event: delete child attachments off the oe_msg_attachment_list box this.$(".oe_msg_attachment_list").on('click', '.oe_delete', this.on_attachment_delete); - this.$(".oe_emails_from").on('change', 'input', this.on_checked_email_from); - this.$(".oe_partners_from").on('change', 'input', this.on_checked_partner_from); + this.$(".oe_recipients").on('change', 'input', this.on_checked_recipient); }, on_compose_fullmail: function (default_composition_mode) { @@ -582,64 +591,92 @@ openerp.mail = function (session) { check_recipient_partners: function () { var self = this; - var partners_from = []; - var emails = []; - _.each(this.emails_from, function (email_from) { - if (email_from[1] && !_.find(emails, function (email) {return email == email_from[0][4];})) { - emails.push(email_from[0][1]); - } - }); - var deferred_check = $.Deferred(); - if (emails.length == 0) { - return deferred_check.resolve(partners_from); + var check_done = $.Deferred(); + + var recipients = _.filter(this.recipients, function (recipient) { return recipient.checked }); + var recipients_to_check = _.filter(recipients, function (recipient) { return (! recipient.partner_id || ! recipient.email_address) }); + var names_to_check = _.pluck(recipients_to_check, 'full_name'); + var names_to_remove = []; + var recipient_ids = _.pluck(_.filter(recipients, function (recipient) { return recipient.partner_id && recipient.email_address }), 'partner_id'); + + // some debug + console.group('check_recipient_partners'); + console.log('recipients', recipients); + console.log('recipient_ids', recipient_ids); + console.log('recipients_to_check', recipients_to_check); + console.log('names_to_check', names_to_check); + + // no emails -> do not make call, proceed to next step + if (names_to_check.length == 0) { + console.groupEnd(); + return check_done.resolve(recipient_ids); } - self.parent_thread.ds_thread._model.call('message_create_partners_from_emails', [emails]).then(function (partners) { - partners_from = _.clone(partners.partner_ids); - var deferreds = []; - _.each(partners.new_partner_ids, function (id) { + + // for each unknown email -> filter already existing partners + self.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [names_to_check]).done(function (result) { + var emails_deferred = []; + _.each(result, function (partner_info) { var deferred = $.Deferred() - deferreds.push(deferred); - var pop = new session.web.form.FormOpenPopup(this); + emails_deferred.push(deferred); + + var partner_name = partner_info.full_name; + var partner_id = partner_info.partner_id; + var parsed_email = mail.ChatterUtils.parse_email(partner_name); + console.log('checking', partner_name, 'parsed email', parsed_email); + + var pop = new session.web.form.FormOpenPopup(this); pop.show_element( 'res.partner', - id, - { - 'force_email': true, + partner_id, + { 'force_email': true, 'ref': "compound_context", - }, - { + 'default_name': parsed_email[0], + 'default_email': parsed_email[1], + }, { title: _t("Please complete partner's informations"), } ); pop.on('closed', self, function () { deferred.resolve(); }); - partners_from.push(id); + pop.view_form.on('on_button_cancel', self, function () { + names_to_remove.push(partner_name); + }); }); - $.when.apply( $, deferreds ).then(function () { - deferred_check.resolve(partners_from); + $.when.apply($, emails_deferred).then(function () { + console.log('post truc', names_to_remove); + var new_names_to_check = _.difference(names_to_check, names_to_remove); + self.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [new_names_to_check, true]).done(function (result) { + _.each(result, function (partner_info) { + recipient_ids.push(partner_info.partner_id); + }); + }).pipe(function (caca) { + console.log('caca', caca); + check_done.resolve(recipient_ids); + }); }); }); - return deferred_check; + console.groupEnd(); + return check_done; }, on_message_post: function (event) { var self = this; + var is_log = $(event.target).data('is_log'); + console.log(is_log); if (this.do_check_attachment_upload() && (this.attachment_ids.length || this.$('textarea').val().match(/\S+/))) { - // create list of new partners - this.check_recipient_partners().done(function (partner_ids) { - self.do_send_message_post(partner_ids); - }); + if (is_log) { + self.do_send_message_post([], is_log); + } + else { + this.check_recipient_partners().done(function (partner_ids) { + self.do_send_message_post(partner_ids, is_log); + }); + } } }, - on_message_log: function (event) { - if (this.do_check_attachment_upload() && (this.attachment_ids.length || this.$('textarea').val().match(/\S+/))) { - this.do_send_message_post([], true); - } - }, - - /*do post a message and fetch the message*/ + /* do post a message and fetch the message */ do_send_message_post: function (partner_ids, log) { var self = this; var values = { @@ -669,18 +706,60 @@ openerp.mail = function (session) { }); }, - /* convert the compact mode into the compose message - */ - on_compose_expandable: function (event) { - this.get_emails_from(); - if ((!this.stay_open || (event && event.type == 'click')) && (!this.show_composer || !this.$('textarea:not(.oe_compact)').val().match(/\S+/) && !this.attachment_ids.length)) { - this.show_composer = !this.show_composer || this.stay_open; - this.reinit(); + /* Quick composer: toggle minimal / expanded mode + * - toggle minimal (one-liner) / expanded (textarea, buttons) mode + * - when going into expanded mode: + * - call `message_get_suggested_partners` to have a list of partners to add + * - compute email_from list (list of unknown email_from to propose to create partners) + */ + on_toggle_quick_composer: function (event) { + var self = this; + this.compute_emails_from(); + var email_addresses = _.pluck(this.recipients, 'email_address'); + var suggested_partners = $.Deferred(); + + // some debug + console.group('on_toggle_quick_composer'); + console.log('event', event); + console.log('computed recipients', this.recipients); + + // if clicked: call for suggested recipients + if (event.type == 'click') { + suggested_partners = this.parent_thread.ds_thread.call('message_get_suggested_recipients', [[this.context.default_res_id]]).done(function (additional_recipients) { + var thread_recipients = additional_recipients[self.context.default_res_id]; + console.log('message_get_suggested_recipients:', thread_recipients); + _.each(thread_recipients, function (recipient) { + var parsed_email = mail.ChatterUtils.parse_email(recipient[1]); + if (_.indexOf(email_addresses, parsed_email[1]) == -1) { + self.recipients.push({ + 'checked': true, + 'partner_id': recipient[0], + 'full_name': recipient[1], + 'name': parsed_email[0], + 'email_address': parsed_email[1], + }) + } + }); + }); } - if (!this.stay_open && this.show_composer && (!event || event.type != 'blur')) { - this.$('textarea:not(.oe_compact):first').focus(); + else { + suggested_partners.resolve({}); } - return true; + + // when call for suggested partners finished: re-render the widget + $.when(suggested_partners).pipe(function (additional_recipients) { + console.log('recipients after toogle', self.recipients); + console.groupEnd(); + if ((!self.stay_open || (event && event.type == 'click')) && (!self.show_composer || !self.$('textarea:not(.oe_compact)').val().match(/\S+/) && !self.attachment_ids.length)) { + self.show_composer = !self.show_composer || self.stay_open; + self.reinit(); + } + if (!self.stay_open && self.show_composer && (!event || event.type != 'blur')) { + self.$('textarea:not(.oe_compact):first').focus(); + } + }); + + return suggested_partners; }, do_hide_compact: function () { @@ -697,7 +776,10 @@ openerp.mail = function (session) { } }, - get_emails_from: function () { + /** Compute the list of unknown email_from the the given thread + * TDE FIXME: seems odd to delegate to the composer + * TDE TODO: please de-obfuscate and comment your code */ + compute_emails_from: function () { var self = this; var messages = []; @@ -710,22 +792,26 @@ openerp.mail = function (session) { // get all wall messages if is not a mail.Wall _.each(this.options.root_thread.messages, function (msg) {messages.push(msg); messages.concat(msg.get_childs());}); } - + _.each(messages, function (thread) { if (thread.author_id && !thread.author_id[0] && - !_.find(self.emails_from, function (from) {return from[0][4] == thread.author_id[4];})) { - self.emails_from.push([thread.author_id, true]); + !_.find(self.recipients, function (recipient) {return recipient.email_address == thread.author_id[3];})) { + self.recipients.push({ 'full_name': thread.author_id[1], + 'name': thread.author_id[2], + 'email_address': thread.author_id[3], + 'partner_id': false, + 'checked': true }); } }); - return self.emails_from; + return self.recipients; }, - on_checked_email_from: function (event) { + on_checked_recipient: function (event) { var $input = $(event.target); var email = $input.attr("data"); - _.each(this.emails_from, function (email_from) { - if (email_from[0][4] == email) { - email_from[1] = $input.is(":checked"); + _.each(this.recipients, function (recipient) { + if (recipient.email_address == email) { + recipient.checked = $input.is(":checked"); } }); }, @@ -1255,12 +1341,12 @@ openerp.mail = function (session) { /** *If compose_message doesn't exist, instantiate the compose message. - * Call the on_compose_expandable method to allow the user to write his message. + * Call the on_toggle_quick_composer method to allow the user to write his message. * (Is call when a user click on "Reply" button) */ on_compose_message: function (event) { this.instantiate_compose_message(); - this.compose_message.on_compose_expandable(event); + this.compose_message.on_toggle_quick_composer(event); return false; }, diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 4c003cbeae1..dfd560c52d9 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -33,9 +33,8 @@ @@ -130,11 +129,11 @@ <<< -
- -