[MERGE] Chatter and CRM: usability fixes and improvements
Chatter: - now displays the 'To' of messages, aka notified people, - now allows to Send a message or to Log a note, that is a message not pushed to anyone; however users that see the document still see the log message, - fixed suggested recipient behavior: canceling the partner creation popup now correctly avoids creating a partner, - moved 'Advanced wizard' button on top-right, - unfollowing or removing someone from followers now displays a warning, CRM, Recruitment, Issues: - better management of customers/applicants: Chatter suggests to notify and add as follower the customer, or to create a partner based on the email_from, CRM, Recruitment, Tasks, Issues: - changing the user_id (salesman, responsible) still adds the related partner as follower; but now an unread notification is pushed, with the first email or first message if no email, - updated and added if missing message_summary in kanban views, now displayed only when having unread messages, and the related number, to be more visible, CRM: - crm_partner_assign: fixed forward-to-partner wizard, - crm_partner_assign: geo localization now also sets salesteam along with salesman, - crm: fixed opportunity email_template, - crm: removed 'Send mail' button, as the functionality should be covered by Chatter (but the code of the action is left untouched to avoid errors), Mail: - merged message_post and message_post_user_api because there was an opportunity to avoid doing similar things in two different methods, - fixed email_from of incoming email not always stored, - followers: authors of discussion messages are now added as followers, this is not only limited to incoming emails, - followers: recipients of emails coming through the mail gateway are not automatically added as followers of the target documents, - followers: slightly updated _notify, to be able to notify a partner of a specific message; the notification process is therefore accessible outside of the mail_message.create() process bzr revid: tde@openerp.com-20130301101122-l18mr6hb0j5k4atv
This commit is contained in:
@@ -80,8 +80,7 @@
|
||||
basis for email recipients, name and to ease the definition of a further elaborated template. -->
|
||||
<record id="email_template_opportunity_mail" model="email.template">
|
||||
<field name="name">Opportunity - Send Emails</field>
|
||||
<field name="email_from">${object.user_id.email or ''}</field>
|
||||
<field name="subject">Opportunity ${object.name | h})</field>
|
||||
<field name="subject">${object.name}</field>
|
||||
<field name="model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="email_recipients">${object.partner_id.id}</field>
|
||||
|
||||
+10
-1
@@ -930,7 +930,7 @@ class crm_lead(base_stage, format_address, osv.osv):
|
||||
try:
|
||||
compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
|
||||
except ValueError:
|
||||
compose_form_id = False
|
||||
compose_form_id = False
|
||||
if context is None:
|
||||
context = {}
|
||||
ctx = context.copy()
|
||||
@@ -961,6 +961,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:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, lead, partner=lead.partner_id, reason=_('Customer'))
|
||||
elif lead.email_from:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, lead, email=lead.email_from, reason=_('Customer Email'))
|
||||
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.
|
||||
|
||||
@@ -377,7 +377,6 @@
|
||||
states="draft,open,pending" class="oe_highlight"/>
|
||||
<button name="case_mark_lost" string="Mark Lost" type="object"
|
||||
states="draft,open" class="oe_highlight"/>
|
||||
<button name="new_mail_send" string="Send Mail" type="object" states="draft,open,pending"/>
|
||||
<field name="stage_id" widget="statusbar" clickable="True"/>
|
||||
</header>
|
||||
<sheet>
|
||||
|
||||
@@ -157,8 +157,9 @@ class crm_lead(osv.osv):
|
||||
self.assign_geo_localize(cr, uid, [lead.id], lead.partner_latitude, lead.partner_longitude, context=context)
|
||||
partner = res_partner.browse(cr, uid, partner_id, context=context)
|
||||
if partner.user_id:
|
||||
salesteam_id = partner.section_id and partner.section_id.id or False
|
||||
for lead_id in ids:
|
||||
self.allocate_salesman(cr, uid, [lead_id], [partner.user_id.id], context=context)
|
||||
self.allocate_salesman(cr, uid, [lead_id], [partner.user_id.id], team_id=salesteam_id, context=context)
|
||||
self.write(cr, uid, [lead.id], {'date_assign': fields.date.context_today(self,cr,uid,context=context), 'partner_assigned_id': partner_id}, context=context)
|
||||
return res
|
||||
|
||||
|
||||
@@ -20,14 +20,11 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import re
|
||||
import time
|
||||
from openerp import tools
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class crm_lead_forward_to_partner(osv.osv_memory):
|
||||
|
||||
class crm_lead_forward_to_partner(osv.TransientModel):
|
||||
""" Forward info history to partners. """
|
||||
_name = 'crm.lead.forward.to.partner'
|
||||
_inherit = "mail.compose.message"
|
||||
@@ -116,15 +113,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
|
||||
value = self.default_get(cr, uid, ['body', 'email_to', 'email_cc', 'subject', 'history_mode'], context=context)
|
||||
self.write(cr, uid, ids, value, context=context)
|
||||
|
||||
self.send_mail(cr, uid, ids, context=context)
|
||||
# for case in lead.browse(cr, uid, lead_ids, context=context):
|
||||
# TODO: WHAT TO DO WITH THAT ?
|
||||
# if (this.send_to == 'partner' and this.partner_id):
|
||||
# lead.assign_partner(cr, uid, [case.id], this.partner_id.id, context=context)
|
||||
|
||||
# if this.send_to == 'user':
|
||||
# lead.allocate_salesman(cr, uid, [case.id], [this.user_id.id], context=context)
|
||||
return res
|
||||
return self.send_mail(cr, uid, ids, context=context)
|
||||
|
||||
def _get_info_body(self, cr, uid, lead, context=None):
|
||||
field_names = []
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
<field name="history_mode" colspan="4"
|
||||
on_change="on_change_history_mode(history_mode, model, res_id)"/>
|
||||
<field name="subject" colspan="4"/>
|
||||
<field name="partner_ids" colspan="4" widget="many2many_tags"
|
||||
on_change="onchange_partner_ids(partner_ids)"/>
|
||||
<field name="partner_ids" colspan="4" widget="many2many_tags_email"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Body">
|
||||
<field name="body"/>
|
||||
|
||||
@@ -340,6 +340,15 @@ class hr_applicant(base_stage, osv.Model):
|
||||
value = self.pool.get("survey").action_print_survey(cr, uid, ids, context=context)
|
||||
return value
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
recipients = super(hr_applicant, self).message_get_suggested_recipients(cr, uid, ids, context=context)
|
||||
for applicant in self.browse(cr, uid, ids, context=context):
|
||||
if applicant.partner_id:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, applicant, partner=applicant.partner_id, reason=_('Contact'))
|
||||
elif applicant.email_from:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, applicant, email=applicant.email_from, reason=_('Contact Email'))
|
||||
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.
|
||||
|
||||
@@ -237,6 +237,7 @@
|
||||
<field name="title_action"/>
|
||||
<field name="department_id"/>
|
||||
<field name="categ_ids"/>
|
||||
<field name="message_summary"/>
|
||||
<templates>
|
||||
<t t-name="kanban-tooltip">
|
||||
<ul class="oe_kanban_tooltip">
|
||||
@@ -282,6 +283,7 @@
|
||||
|
||||
</div>
|
||||
<div class="oe_kanban_footer_left" style="margin-top:5px;">
|
||||
<t t-raw="record.message_summary.raw_value"/>
|
||||
<field name="categ_ids"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,16 +75,21 @@ class mail_notification(osv.Model):
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)')
|
||||
|
||||
def get_partners_to_notify(self, cr, uid, message, context=None):
|
||||
def get_partners_to_notify(self, cr, uid, message, partners_to_notify=None, context=None):
|
||||
""" Return the list of partners to notify, based on their preferences.
|
||||
|
||||
:param browse_record message: mail.message to notify
|
||||
:param list partners_to_notify: optional list of partner ids restricting
|
||||
the notifications to process
|
||||
"""
|
||||
notify_pids = []
|
||||
for notification in message.notification_ids:
|
||||
if notification.read:
|
||||
continue
|
||||
partner = notification.partner_id
|
||||
# If partners_to_notify specified: restrict to them
|
||||
if partners_to_notify and partner.id not in partners_to_notify:
|
||||
continue
|
||||
# Do not send to partners without email address defined
|
||||
if not partner.email:
|
||||
continue
|
||||
@@ -100,16 +105,35 @@ class mail_notification(osv.Model):
|
||||
notify_pids.append(partner.id)
|
||||
return notify_pids
|
||||
|
||||
def _notify(self, cr, uid, msg_id, context=None):
|
||||
""" Send by email the notification depending on the user preferences """
|
||||
def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None):
|
||||
""" Send by email the notification depending on the user preferences
|
||||
|
||||
:param list partners_to_notify: optional list of partner ids restricting
|
||||
the notifications to process
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
mail_message_obj = self.pool.get('mail.message')
|
||||
|
||||
# optional list of partners to notify: subscribe them if not already done or update the notification
|
||||
if partners_to_notify:
|
||||
notifications_to_update = []
|
||||
notified_partners = []
|
||||
notif_ids = self.search(cr, SUPERUSER_ID, [('message_id', '=', msg_id), ('partner_id', 'in', partners_to_notify)], context=context)
|
||||
for notification in self.browse(cr, SUPERUSER_ID, notif_ids, context=context):
|
||||
notified_partners.append(notification.partner_id.id)
|
||||
notifications_to_update.append(notification.id)
|
||||
partners_to_notify = filter(lambda item: item not in notified_partners, partners_to_notify)
|
||||
if notifications_to_update:
|
||||
self.write(cr, SUPERUSER_ID, notifications_to_update, {'read': False}, context=context)
|
||||
mail_message_obj.write(cr, uid, msg_id, {'notified_partner_ids': [(4, id) for id in partners_to_notify]}, context=context)
|
||||
|
||||
# mail_notify_noemail (do not send email) or no partner_ids: do not send, return
|
||||
if context.get('mail_notify_noemail'):
|
||||
return True
|
||||
# browse as SUPERUSER_ID because of access to res_partner not necessarily allowed
|
||||
msg = self.pool.get('mail.message').browse(cr, SUPERUSER_ID, msg_id, context=context)
|
||||
notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, context=context)
|
||||
notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, partners_to_notify=partners_to_notify, context=context)
|
||||
if not notify_partner_ids:
|
||||
return True
|
||||
|
||||
@@ -136,16 +160,12 @@ class mail_notification(osv.Model):
|
||||
|
||||
mail_values = {
|
||||
'mail_message_id': msg.id,
|
||||
'email_to': [],
|
||||
'auto_delete': True,
|
||||
'body_html': body_html,
|
||||
'email_from': email_from,
|
||||
'state': 'outgoing',
|
||||
}
|
||||
mail_values['email_to'] = ', '.join(mail_values['email_to'])
|
||||
email_notif_id = mail_mail.create(cr, uid, mail_values, context=context)
|
||||
try:
|
||||
return mail_mail.send(cr, uid, [email_notif_id], recipient_ids=notify_partner_ids, context=context)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="message_ids" widget="mail_thread" options='{"thread_level": 1}' placeholder="Send a message to the group..."/>
|
||||
<field name="message_ids" widget="mail_thread" options='{"thread_level": 1}' placeholder="Send a message to the group"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
@@ -301,8 +301,8 @@ class mail_message(osv.Model):
|
||||
for key, message in message_tree.iteritems():
|
||||
if message.author_id:
|
||||
partner_ids |= set([message.author_id.id])
|
||||
if message.partner_ids:
|
||||
partner_ids |= set([partner.id for partner in message.partner_ids])
|
||||
if message.notified_partner_ids:
|
||||
partner_ids |= set([partner.id for partner in message.notified_partner_ids])
|
||||
if message.attachment_ids:
|
||||
attachment_ids |= set([attachment.id for attachment in message.attachment_ids])
|
||||
# Read partners as SUPERUSER -> display the names like classic m2o even if no access
|
||||
@@ -322,7 +322,7 @@ class mail_message(osv.Model):
|
||||
else:
|
||||
author = (0, message.email_from)
|
||||
partner_ids = []
|
||||
for partner in message.partner_ids:
|
||||
for partner in message.notified_partner_ids:
|
||||
if partner.id in partner_tree:
|
||||
partner_ids.append(partner_tree[partner.id])
|
||||
attachment_ids = []
|
||||
@@ -861,7 +861,7 @@ class mail_message(osv.Model):
|
||||
# message has no subtype_id: pure log message -> no partners, no one notified
|
||||
if not message.subtype_id:
|
||||
return True
|
||||
|
||||
|
||||
# all followers of the mail.message document have to be added as partners and notified
|
||||
if message.model and message.res_id:
|
||||
fol_obj = self.pool.get("mail.followers")
|
||||
@@ -884,8 +884,7 @@ class mail_message(osv.Model):
|
||||
|
||||
# notify
|
||||
if partners_to_notify:
|
||||
self.write(cr, SUPERUSER_ID, [newid], {'notified_partner_ids': [(4, p.id) for p in partners_to_notify]}, context=context)
|
||||
notification_obj._notify(cr, uid, newid, context=context)
|
||||
notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context)
|
||||
message.refresh()
|
||||
|
||||
# An error appear when a user receive a notification without notifying
|
||||
|
||||
+148
-137
@@ -35,6 +35,7 @@ from openerp import SUPERUSER_ID
|
||||
from openerp.addons.mail.mail_message import decode
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -92,7 +93,7 @@ class mail_thread(osv.AbstractModel):
|
||||
""" Computes:
|
||||
- message_unread: has uid unread message for the document
|
||||
- message_summary: html snippet summarizing the Chatter for kanban views """
|
||||
res = dict((id, dict(message_unread=False, message_summary='')) for id in ids)
|
||||
res = dict((id, dict(message_unread=False, message_unread_count=0, message_summary=' ')) for id in ids)
|
||||
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
|
||||
# search for unread messages, directly in SQL to improve performances
|
||||
@@ -101,14 +102,14 @@ class mail_thread(osv.AbstractModel):
|
||||
ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL))
|
||||
WHERE m.model = %s AND m.res_id in %s""",
|
||||
(user_pid, self._name, tuple(ids),))
|
||||
msg_ids = [result[0] for result in cr.fetchall()]
|
||||
for msg_id in msg_ids:
|
||||
res[msg_id]['message_unread'] = True
|
||||
|
||||
for thread in self.browse(cr, uid, ids, context=context):
|
||||
cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or ''
|
||||
res[thread.id]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread.message_ids), len(thread.message_follower_ids))
|
||||
for result in cr.fetchall():
|
||||
res[result[0]]['message_unread'] = True
|
||||
res[result[0]]['message_unread_count'] += 1
|
||||
|
||||
for id in ids:
|
||||
if res[id]['message_unread_count']:
|
||||
title = res[id]['message_unread_count'] > 1 and _("You have %d unread messages") % res[id]['message_unread_count'] or _("You have one unread message")
|
||||
res[id]['message_summary'] = "<span class='oe_kanban_mail_new' title='%s'><span class='oe_e'>9</span> %d %s</span>" % (title, res[id].pop('message_unread_count'), _("New"))
|
||||
return res
|
||||
|
||||
def _get_subscription_data(self, cr, uid, ids, name, args, context=None):
|
||||
@@ -606,15 +607,12 @@ class mail_thread(osv.AbstractModel):
|
||||
if thread_id and hasattr(model_pool, 'message_update'):
|
||||
model_pool.message_update(cr, user_id, [thread_id], msg, context=nosub_ctx)
|
||||
else:
|
||||
nosub_ctx = dict(nosub_ctx, mail_create_nolog=True)
|
||||
thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=nosub_ctx)
|
||||
else:
|
||||
assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
|
||||
model_pool = self.pool.get('mail.thread')
|
||||
new_msg_id = model_pool.message_post_user_api(cr, uid, [thread_id], context=context, content_subtype='html', **msg)
|
||||
|
||||
# when posting an incoming email to a document: subscribe the author, if a partner, as follower
|
||||
if model and thread_id and msg.get('author_id'):
|
||||
model_pool.message_subscribe(cr, uid, [thread_id], [msg.get('author_id')], context=context)
|
||||
new_msg_id = model_pool.message_post(cr, uid, [thread_id], context=context, subtype='mail.mt_comment', **msg)
|
||||
|
||||
if partner_ids:
|
||||
# postponed after message_post, because this is an external message and we don't want to create
|
||||
@@ -771,8 +769,7 @@ class mail_thread(osv.AbstractModel):
|
||||
author_ids = self._message_find_partners(cr, uid, message, ['From'], context=context)
|
||||
if author_ids:
|
||||
msg_dict['author_id'] = author_ids[0]
|
||||
else:
|
||||
msg_dict['email_from'] = decode(message.get('from'))
|
||||
msg_dict['email_from'] = decode(message.get('from'))
|
||||
partner_ids = self._message_find_partners(cr, uid, message, ['To', 'Cc'], context=context)
|
||||
msg_dict['partner_ids'] = [(4, partner_id) for partner_id in partner_ids]
|
||||
|
||||
@@ -820,77 +817,140 @@ 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_add_suggested_recipient(self, cr, uid, result, obj, partner=None, email=None, reason='', context=None):
|
||||
""" Called by message_get_suggested_recipients, to add a suggested
|
||||
recipient in the result dictionary. The form is :
|
||||
partner_id, partner_name<partner_email> or partner_name, reason """
|
||||
if email and not partner:
|
||||
partner_info = self.message_get_partner_info_from_emails(cr, uid, [email], context=context)[0]
|
||||
if partner_info.get('partner_id'):
|
||||
partner = self.pool.get('res.partner').browse(cr, SUPERUSER_ID, [partner_info.get('partner_id')], context=context)[0]
|
||||
if email and email in [val[1] for val in result[obj.id]]: # already existing email -> skip
|
||||
return result
|
||||
if partner and partner in obj.message_follower_ids: # recipient already in the followers -> skip
|
||||
return result
|
||||
if partner and partner in [val[0] for val in result[obj.id]]: # already existing partner ID -> skip
|
||||
return result
|
||||
if partner and partner.email: # complete profile: id, name <email>
|
||||
result[obj.id].append((partner.id, '%s<%s>' % (partner.name, partner.email), reason))
|
||||
elif partner: # incomplete profile: id, name
|
||||
result[obj.id].append((partner.id, '%s' % (partner.name), reason))
|
||||
else: # unknown partner, we are probably managing an email address
|
||||
result[obj.id].append((False, email, reason))
|
||||
return result
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
""" Returns suggested recipients for ids. Those are a list of
|
||||
tuple (partner_id, partner_name, reason), to be managed by Chatter. """
|
||||
result = dict.fromkeys(ids, list())
|
||||
if self._all_columns.get('user_id'):
|
||||
for obj in self.browse(cr, SUPERUSER_ID, ids, context=context): # SUPERUSER because of a read on res.users that would crash otherwise
|
||||
if not obj.user_id or not obj.user_id.partner_id:
|
||||
continue
|
||||
self._message_add_suggested_recipient(cr, uid, result, obj, partner=obj.user_id.partner_id, reason=self._all_columns['user_id'].column.string, context=context)
|
||||
return result
|
||||
|
||||
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')
|
||||
result = list()
|
||||
for email in emails:
|
||||
partner_info = {'full_name': email, 'partner_id': False}
|
||||
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)
|
||||
if not m:
|
||||
continue
|
||||
email_address = m.group(3)
|
||||
ids = partner_obj.search(cr, SUPERUSER_ID, [('email', '=', email_address)], context=context)
|
||||
if ids:
|
||||
partner_ids.append(ids[0])
|
||||
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['partner_id'] = ids[0]
|
||||
result.append(partner_info)
|
||||
|
||||
# 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:
|
||||
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)
|
||||
return result
|
||||
|
||||
def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification',
|
||||
subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
|
||||
def message_post(self, cr, uid, thread_id, body='', subject=None,
|
||||
content_subtype='html', type='notification', subtype=None,
|
||||
parent_id=False, attachments=None, context=None, **kwargs):
|
||||
""" Post a new message in an existing thread, returning the new
|
||||
mail.message ID. Extra keyword arguments will be used as default
|
||||
column values for the new mail.message record.
|
||||
Auto link messages for same id and object
|
||||
mail.message ID.
|
||||
|
||||
:param int thread_id: thread ID to post into, or list with one ID;
|
||||
if False/0, mail.message model will also be set as False
|
||||
:param str body: body of the message, usually raw HTML that will
|
||||
be sanitized
|
||||
:param str subject: optional subject
|
||||
:param str type: mail_message.type
|
||||
:param int parent_id: optional ID of parent message in this thread
|
||||
:param str type: see mail_message.type field
|
||||
:param str content_subtype:: if plaintext: convert body into html
|
||||
:param int parent_id: handle reply to a previous message by adding the
|
||||
parent partners to the message in case of private discussion
|
||||
:param tuple(str,str) attachments or list id: list of attachment tuples in the form
|
||||
``(name,content)``, where content is NOT base64 encoded
|
||||
:return: ID of newly created mail.message
|
||||
|
||||
Extra keyword arguments will be used as default column values for the
|
||||
new mail.message record. Special cases:
|
||||
- attachment_ids: supposed not attached to any document; attach them
|
||||
to the related document. Should only be set by Chatter.
|
||||
:return int: ID of newly created mail.message
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
if attachments is None:
|
||||
attachments = {}
|
||||
|
||||
assert (not thread_id) or isinstance(thread_id, (int, long)) or \
|
||||
(isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), "Invalid thread_id; should be 0, False, an ID or a list with one ID"
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id and thread_id[0]
|
||||
mail_message = self.pool.get('mail.message')
|
||||
ir_attachment = self.pool.get('ir.attachment')
|
||||
|
||||
assert (not thread_id) or \
|
||||
isinstance(thread_id, (int, long)) or \
|
||||
(isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), \
|
||||
"Invalid thread_id; should be 0, False, an ID or a list with one ID"
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id[0]
|
||||
|
||||
# if we're processing a message directly coming from the gateway, the destination model was
|
||||
# set in the context.
|
||||
# set in the context.
|
||||
model = False
|
||||
if thread_id:
|
||||
model = context.get('thread_model', self._name) if self._name == 'mail.thread' else self._name
|
||||
|
||||
attachment_ids = kwargs.pop('attachment_ids', [])
|
||||
# 1: Handle content subtype: if plaintext, converto into HTML
|
||||
if content_subtype == 'plaintext':
|
||||
body = tools.plaintext2html(body)
|
||||
|
||||
# 2: Private message: add recipients (recipients and author of parent message)
|
||||
partner_ids = set(kwargs.pop('partner_ids', []))
|
||||
if parent_id and model == 'mail.thread':
|
||||
parent_message = mail_message.browse(cr, uid, parent_id, context=context)
|
||||
partner_ids |= set([partner.id for partner in parent_message.partner_ids])
|
||||
if parent_message.author_id:
|
||||
partner_ids.add(parent_message.author_id.id)
|
||||
|
||||
# 3. Attachments
|
||||
# - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
|
||||
attachment_ids = kwargs.pop('attachment_ids', []) or [] # because we could receive None (some old code sends None)
|
||||
if attachment_ids:
|
||||
filtered_attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [
|
||||
('res_model', '=', 'mail.compose.message'),
|
||||
('res_id', '=', 0),
|
||||
('create_uid', '=', uid),
|
||||
('id', 'in', attachment_ids)], context=context)
|
||||
if filtered_attachment_ids:
|
||||
ir_attachment.write(cr, SUPERUSER_ID, filtered_attachment_ids, {'res_model': model, 'res_id': thread_id}, context=context)
|
||||
attachment_ids = [(4, id) for id in attachment_ids]
|
||||
# Handle attachments parameter, that is a dictionary of attachments
|
||||
for name, content in attachments:
|
||||
if isinstance(content, unicode):
|
||||
content = content.encode('utf-8')
|
||||
@@ -899,20 +959,25 @@ class mail_thread(osv.AbstractModel):
|
||||
'datas': base64.b64encode(str(content)),
|
||||
'datas_fname': name,
|
||||
'description': name,
|
||||
'res_model': context.get('thread_model') or self._name,
|
||||
'res_model': model,
|
||||
'res_id': thread_id,
|
||||
}
|
||||
attachment_ids.append((0, 0, data_attach))
|
||||
|
||||
# fetch subtype
|
||||
# 4: mail.message.subtype
|
||||
subtype_id = False
|
||||
if subtype:
|
||||
s_data = subtype.split('.')
|
||||
if len(s_data) == 1:
|
||||
s_data = ('mail', s_data[0])
|
||||
ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, s_data[0], s_data[1])
|
||||
if '.' not in subtype:
|
||||
subtype = 'mail.%s' % subtype
|
||||
ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, *subtype.split('.'))
|
||||
subtype_id = ref and ref[1] or False
|
||||
else:
|
||||
subtype_id = False
|
||||
|
||||
# automatically subscribe recipients if asked to
|
||||
if context.get('mail_post_autofollow') and thread_id and partner_ids:
|
||||
partner_to_subscribe = partner_ids
|
||||
if context.get('mail_post_autofollow_partner_ids'):
|
||||
partner_to_subscribe = filter(lambda item: item in context.get('mail_post_autofollow_partner_ids'), partner_ids)
|
||||
self.message_subscribe(cr, uid, [thread_id], list(partner_to_subscribe), context=context)
|
||||
|
||||
# _mail_flat_thread: automatically set free messages to the first posted message
|
||||
if self._mail_flat_thread and not parent_id and thread_id:
|
||||
@@ -940,86 +1005,19 @@ class mail_thread(osv.AbstractModel):
|
||||
'parent_id': parent_id,
|
||||
'attachment_ids': attachment_ids,
|
||||
'subtype_id': subtype_id,
|
||||
'partner_ids': [(4, pid) for pid in partner_ids],
|
||||
})
|
||||
|
||||
# Avoid warnings about non-existing fields
|
||||
for x in ('from', 'to', 'cc'):
|
||||
values.pop(x, None)
|
||||
|
||||
return mail_message.create(cr, uid, values, context=context)
|
||||
|
||||
def message_post_user_api(self, cr, uid, thread_id, body='', parent_id=False,
|
||||
attachment_ids=None, content_subtype='plaintext',
|
||||
context=None, **kwargs):
|
||||
""" Wrapper on message_post, used for user input :
|
||||
- mail gateway
|
||||
- quick reply in Chatter (refer to mail.js), not
|
||||
the mail.compose.message wizard
|
||||
The purpose is to perform some pre- and post-processing:
|
||||
- if body is plaintext: convert it into html
|
||||
- if parent_id: handle reply to a previous message by adding the
|
||||
parent partners to the message
|
||||
- type and subtype: comment and mail.mt_comment by default
|
||||
- attachment_ids: supposed not attached to any document; attach them
|
||||
to the related document. Should only be set by Chatter.
|
||||
"""
|
||||
mail_message_obj = self.pool.get('mail.message')
|
||||
ir_attachment = self.pool.get('ir.attachment')
|
||||
|
||||
# 1.A.1: add recipients of parent message (# TDE FIXME HACK: mail.thread -> private message)
|
||||
partner_ids = set([])
|
||||
if parent_id and self._name == 'mail.thread':
|
||||
parent_message = mail_message_obj.browse(cr, uid, parent_id, context=context)
|
||||
partner_ids |= set([(4, partner.id) for partner in parent_message.partner_ids])
|
||||
if parent_message.author_id.id:
|
||||
partner_ids.add((4, parent_message.author_id.id))
|
||||
|
||||
# 1.A.2: add specified recipients
|
||||
param_partner_ids = set()
|
||||
for item in kwargs.pop('partner_ids', []):
|
||||
if isinstance(item, (list)):
|
||||
param_partner_ids.add((item[0], item[1]))
|
||||
elif isinstance(item, (int, long)):
|
||||
param_partner_ids.add((4, item))
|
||||
else:
|
||||
param_partner_ids.add(item)
|
||||
partner_ids |= param_partner_ids
|
||||
|
||||
# 1.A.3: add parameters recipients as follower
|
||||
# TDE FIXME in 7.1: should check whether this comes from email_list or partner_ids
|
||||
if param_partner_ids and self._name != 'mail.thread':
|
||||
self.message_subscribe(cr, uid, [thread_id], [pid[1] for pid in param_partner_ids], context=context)
|
||||
|
||||
# 1.B: handle body, message_type and message_subtype
|
||||
if content_subtype == 'plaintext':
|
||||
body = tools.plaintext2html(body)
|
||||
msg_type = kwargs.pop('type', 'comment')
|
||||
msg_subtype = kwargs.pop('subtype', 'mail.mt_comment')
|
||||
|
||||
# 2. Pre-processing: attachments
|
||||
# HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
|
||||
if attachment_ids:
|
||||
# TDE FIXME (?): when posting a private message, we use mail.thread as a model
|
||||
# However, attaching doc to mail.thread is not possible, mail.thread does not have any table
|
||||
model = self._name
|
||||
if model == 'mail.thread':
|
||||
model = False
|
||||
filtered_attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [
|
||||
('res_model', '=', 'mail.compose.message'),
|
||||
('res_id', '=', 0),
|
||||
('create_uid', '=', uid),
|
||||
('id', 'in', attachment_ids)], context=context)
|
||||
if filtered_attachment_ids:
|
||||
if thread_id and model:
|
||||
ir_attachment.write(cr, SUPERUSER_ID, attachment_ids, {'res_model': model, 'res_id': thread_id}, context=context)
|
||||
else:
|
||||
attachment_ids = []
|
||||
attachment_ids = [(4, id) for id in attachment_ids]
|
||||
|
||||
# 3. Post message
|
||||
return self.message_post(cr, uid, thread_id=thread_id, body=body,
|
||||
type=msg_type, subtype=msg_subtype, parent_id=parent_id,
|
||||
attachment_ids=attachment_ids, partner_ids=list(partner_ids), context=context, **kwargs)
|
||||
# Create and auto subscribe the author
|
||||
msg_id = mail_message.create(cr, uid, values, context=context)
|
||||
message = mail_message.browse(cr, uid, msg_id, context=context)
|
||||
if message.author_id and thread_id and type != 'notification':
|
||||
self.message_subscribe(cr, uid, [thread_id], [message.author_id.id], context=context)
|
||||
return msg_id
|
||||
|
||||
#------------------------------------------------------
|
||||
# Followers API
|
||||
@@ -1141,12 +1139,25 @@ class mail_thread(osv.AbstractModel):
|
||||
|
||||
# add followers coming from res.users relational fields that are tracked
|
||||
user_ids = [getattr(record, name).id for name in user_field_lst if getattr(record, name)]
|
||||
for partner_id in [user.partner_id.id for user in self.pool.get('res.users').browse(cr, SUPERUSER_ID, user_ids, context=context)]:
|
||||
user_id_partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, SUPERUSER_ID, user_ids, context=context)]
|
||||
for partner_id in user_id_partner_ids:
|
||||
new_followers.setdefault(partner_id, None)
|
||||
|
||||
for pid, subtypes in new_followers.items():
|
||||
subtypes = list(subtypes) if subtypes is not None else None
|
||||
self.message_subscribe(cr, uid, [record.id], [pid], subtypes, context=context)
|
||||
|
||||
# find first email message, set it as unread for auto_subscribe fields for them to have a notification
|
||||
if user_id_partner_ids:
|
||||
msg_ids = self.pool.get('mail.message').search(cr, uid, [
|
||||
('model', '=', self._name),
|
||||
('res_id', '=', record.id),
|
||||
('type', '=', 'email')], limit=1, context=context)
|
||||
if not msg_ids and record.message_ids:
|
||||
msg_ids = [record.message_ids[-1].id]
|
||||
if msg_ids:
|
||||
self.pool.get('mail.notification')._notify(cr, uid, msg_ids[0], partners_to_notify=user_id_partner_ids, context=context)
|
||||
|
||||
return True
|
||||
|
||||
#------------------------------------------------------
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.tools.translate import _
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
|
||||
class res_partner_mail(osv.Model):
|
||||
""" Update partner to add a field about notification preferences """
|
||||
_name = "res.partner"
|
||||
@@ -42,6 +43,12 @@ class res_partner_mail(osv.Model):
|
||||
'notification_email_send': lambda *args: 'comment'
|
||||
}
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
recipients = super(res_partner_mail, self).message_get_suggested_recipients(cr, uid, ids, context=context)
|
||||
for partner in self.browse(cr, uid, ids, context=context):
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, partner, partner=partner, reason=_('Partner Profile'))
|
||||
return recipients
|
||||
|
||||
def message_post(self, cr, uid, thread_id, **kwargs):
|
||||
""" Override related to res.partner. In case of email message, set it as
|
||||
private:
|
||||
|
||||
@@ -113,13 +113,6 @@ class res_users(osv.Model):
|
||||
thread_id = thread_id[0]
|
||||
return self.browse(cr, SUPERUSER_ID, thread_id).partner_id.id
|
||||
|
||||
def message_post_user_api(self, cr, uid, thread_id, context=None, **kwargs):
|
||||
""" Redirect the posting of message on res.users to the related partner.
|
||||
This is done because when giving the context of Chatter on the
|
||||
various mailboxes, we do not have access to the current partner_id. """
|
||||
partner_id = self._message_post_get_pid(cr, uid, thread_id, context=context)
|
||||
return self.pool.get('res.partner').message_post_user_api(cr, uid, partner_id, context=context, **kwargs)
|
||||
|
||||
def message_post(self, cr, uid, thread_id, context=None, **kwargs):
|
||||
""" Redirect the posting of message on res.users to the related partner.
|
||||
This is done because when giving the context of Chatter on the
|
||||
@@ -139,8 +132,14 @@ class res_users(osv.Model):
|
||||
self.pool.get('res.partner').message_subscribe(cr, uid, [partner_id], partner_ids, subtype_ids=subtype_ids, context=context)
|
||||
return True
|
||||
|
||||
def message_create_partners_from_emails(self, cr, uid, emails, context=None):
|
||||
return self.pool.get('res.partner').message_create_partners_from_emails(cr, uid, emails, context=context)
|
||||
def message_get_partner_info_from_emails(self, cr, uid, emails, link_mail=False, context=None):
|
||||
return self.pool.get('res.partner').message_get_partner_info_from_emails(cr, uid, emails, link_mail=link_mail, context=context)
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
partner_ids = []
|
||||
for id in ids:
|
||||
partner_ids.append(self.browse(cr, SUPERUSER_ID, id).partner_id.id)
|
||||
return self.pool.get('res.partner').message_get_suggested_recipients(cr, uid, partner_ids, context=context)
|
||||
|
||||
|
||||
class res_users_mail_group(osv.Model):
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
min-height: 42px;
|
||||
border: solid 1px rgba(0,0,0,0.03);
|
||||
}
|
||||
.openerp .oe_mail .oe_msg.oe_msg_nobody{
|
||||
background: #F8F8F8;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg .oe_msg_left{
|
||||
position: absolute;
|
||||
left:0; top: 0; bottom: 0; width: 40px;
|
||||
@@ -228,16 +231,23 @@
|
||||
height: 24px;
|
||||
width: 100%;
|
||||
padding: 2px 4px;
|
||||
color: #AAA;
|
||||
word-spacing: 3px;
|
||||
cursor: text;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg.oe_msg_composer_compact .oe_compact_record {
|
||||
font-size: 13px;
|
||||
font-style: bold;
|
||||
text-align: center;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg.oe_msg_composer_compact .oe_compact_inbox {
|
||||
border: 1px solid #CCC;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
background: white;
|
||||
font-size: 14px;
|
||||
color: #AAA;
|
||||
font-style: italic;
|
||||
word-spacing: 3px;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
/* d) I.E. tweaks for Message action icons */
|
||||
@@ -269,20 +279,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{
|
||||
@@ -439,7 +449,8 @@
|
||||
line-height: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_post{
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_post,
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_log{
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
@@ -458,18 +469,20 @@
|
||||
.openerp .oe_mail .oe_hidden_input_file, .openerp .oe_mail .oe_hidden_input_file form{
|
||||
display:inline;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_full{
|
||||
width:24px;
|
||||
overflow:hidden;
|
||||
.openerp .oe_mail .oe_msg_center button.oe_full{
|
||||
width: 24px;
|
||||
height: 22px;
|
||||
overflow: hidden;
|
||||
float: right;
|
||||
filter:none;
|
||||
filter: none;
|
||||
}
|
||||
.openerp .oe_mail .oe_msg_footer button.oe_full .oe_e{
|
||||
.openerp .oe_mail .oe_msg_center button.oe_full .oe_e{
|
||||
position: relative;
|
||||
top: -4px;
|
||||
margin-left: -9px;
|
||||
vertical-align: middle;
|
||||
filter:none;
|
||||
top: -9px;
|
||||
margin-left: -5px;
|
||||
vertical-align: top;
|
||||
filter: none;
|
||||
height: 14px;
|
||||
}
|
||||
.openerp .oe_mail button.oe_attach, .openerp .oe_mail button.oe_full{
|
||||
background: transparent;
|
||||
|
||||
@@ -21,6 +21,19 @@ openerp.mail = function (session) {
|
||||
|
||||
mail.ChatterUtils = {
|
||||
|
||||
/** parse text to find email: Tagada <address@mail.fr> -> [Tagada, address@mail.fr] or False */
|
||||
parse_email: function (text) {
|
||||
var result = text.match(/(.*)<(.*@.*)>/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[2])];
|
||||
}
|
||||
result = text.match(/(.*@.*)/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[1])];
|
||||
}
|
||||
return [text, false];
|
||||
},
|
||||
|
||||
/* Get an image in /web/binary/image?... */
|
||||
get_image: function (session, model, field, id, resize) {
|
||||
var r = resize ? encodeURIComponent(resize) : '';
|
||||
@@ -255,11 +268,15 @@ 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;
|
||||
this.extra_partners_str = ''
|
||||
var extra_partners = this.partner_ids.slice(3);
|
||||
for (var key in extra_partners) {
|
||||
this.extra_partners_str += extra_partners[key][1];
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -371,14 +388,24 @@ 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<email_address>,
|
||||
},
|
||||
{ ... },
|
||||
]
|
||||
*/
|
||||
|
||||
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.is_log = false;
|
||||
this.recipients = [];
|
||||
this.recipient_ids = [];
|
||||
},
|
||||
|
||||
start: function () {
|
||||
@@ -479,15 +506,13 @@ 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_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment') );
|
||||
this.$('.oe_compact_inbox').on('click', self.on_toggle_quick_composer);
|
||||
this.$('.oe_compose_post').on('click', self.on_toggle_quick_composer);
|
||||
this.$('.oe_compose_log').on('click', self.on_toggle_quick_composer);
|
||||
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', self.on_message_post);
|
||||
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));
|
||||
@@ -497,13 +522,12 @@ openerp.mail = function (session) {
|
||||
this.$('textarea').autosize();
|
||||
|
||||
// auto close
|
||||
this.$('textarea').on('blur', _.bind( this.on_compose_expandable, this));
|
||||
this.$('textarea').on('blur', self.on_toggle_quick_composer);
|
||||
|
||||
// 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) {
|
||||
@@ -511,16 +535,26 @@ openerp.mail = function (session) {
|
||||
if(!this.do_check_attachment_upload()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// create list of new partners
|
||||
this.check_recipient_partners().done(function (partner_ids) {
|
||||
var recipient_done = $.Deferred();
|
||||
if (this.is_log) {
|
||||
recipient_done.resolve([]);
|
||||
}
|
||||
else {
|
||||
recipient_done = this.check_recipient_partners();
|
||||
}
|
||||
$.when(recipient_done).done(function (partner_ids) {
|
||||
var context = {
|
||||
'default_composition_mode': default_composition_mode,
|
||||
'default_parent_id': self.id,
|
||||
'default_body': mail.ChatterUtils.get_text2html(self.$el ? (self.$el.find('textarea:not(.oe_compact)').val() || '') : ''),
|
||||
'default_attachment_ids': self.attachment_ids,
|
||||
'default_partner_ids': partner_ids,
|
||||
'mail_post_autofollow': true,
|
||||
'mail_post_autofollow_partner_ids': partner_ids,
|
||||
};
|
||||
if (self.is_log) {
|
||||
_.extend(context, {'mail_compose_log': true});
|
||||
}
|
||||
if (default_composition_mode != 'reply' && 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;
|
||||
@@ -573,68 +607,122 @@ 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_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.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [names_to_find]);
|
||||
}
|
||||
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) {
|
||||
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()
|
||||
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);
|
||||
|
||||
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);
|
||||
if (partner_id) {
|
||||
recipient_ids_to_remove.push(partner_id);
|
||||
}
|
||||
});
|
||||
});
|
||||
$.when.apply( $, deferreds ).then(function () {
|
||||
deferred_check.resolve(partners_from);
|
||||
|
||||
$.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.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [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 deferred_check;
|
||||
|
||||
return check_done;
|
||||
},
|
||||
|
||||
on_message_post: function (event) {
|
||||
var self = this;
|
||||
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 (this.is_log) {
|
||||
this.do_send_message_post([], this.is_log);
|
||||
}
|
||||
else {
|
||||
this.check_recipient_partners().done(function (partner_ids) {
|
||||
self.do_send_message_post(partner_ids, self.is_log);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/*do post a message and fetch the message*/
|
||||
do_send_message_post: function (partner_ids) {
|
||||
/* do post a message and fetch the message */
|
||||
do_send_message_post: function (partner_ids, log) {
|
||||
var self = this;
|
||||
this.parent_thread.ds_thread._model.call('message_post_user_api', [this.context.default_res_id], {
|
||||
var values = {
|
||||
'body': this.$('textarea').val(),
|
||||
'subject': false,
|
||||
'parent_id': this.context.default_parent_id,
|
||||
'attachment_ids': _.map(this.attachment_ids, function (file) {return file.id;}),
|
||||
'partner_ids': partner_ids,
|
||||
'context': this.parent_thread.context,
|
||||
}).done(function (message_id) {
|
||||
'context': _.extend(this.parent_thread.context, {
|
||||
'mail_post_autofollow': true,
|
||||
'mail_post_autofollow_partner_ids': partner_ids,
|
||||
}),
|
||||
'type': 'comment',
|
||||
'content_subtype': 'plaintext',
|
||||
};
|
||||
if (log) {
|
||||
values['subtype'] = false;
|
||||
}
|
||||
else {
|
||||
values['subtype'] = 'mail.mt_comment';
|
||||
}
|
||||
this.parent_thread.ds_thread._model.call('message_post', [this.context.default_res_id], values).done(function (message_id) {
|
||||
var thread = self.parent_thread;
|
||||
var root = thread == self.options.root_thread;
|
||||
if (self.options.display_indented_thread < self.thread_level && thread.parent_message) {
|
||||
@@ -650,18 +738,55 @@ 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_recipients` 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;
|
||||
var $input = $(event.target);
|
||||
this.compute_emails_from();
|
||||
var email_addresses = _.pluck(this.recipients, 'email_address');
|
||||
var suggested_partners = $.Deferred();
|
||||
|
||||
// if clicked: call for suggested recipients
|
||||
if (event.type == 'click') {
|
||||
this.is_log = $input.hasClass('oe_compose_log');
|
||||
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];
|
||||
_.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],
|
||||
'reason': recipient[2],
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
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) {
|
||||
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 () {
|
||||
@@ -678,7 +803,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 = [];
|
||||
|
||||
@@ -691,22 +819,28 @@ 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,
|
||||
'reason': 'Incoming email author'
|
||||
});
|
||||
}
|
||||
});
|
||||
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");
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -1236,12 +1370,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;
|
||||
},
|
||||
|
||||
@@ -1810,7 +1944,7 @@ openerp.mail = function (session) {
|
||||
};
|
||||
session.client.action_manager.do_action(action);
|
||||
});
|
||||
this.$(".oe_write_onwall").click(function(){ self.root.thread.on_compose_message(); });
|
||||
this.$(".oe_write_onwall").click(function (event) { self.root.thread.on_compose_message(event); });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -106,9 +106,12 @@ openerp_mail_followers = function(session, mail) {
|
||||
|
||||
on_remove_follower: function (event) {
|
||||
var partner_id = $(event.target).data('id');
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id], [partner_id], context])
|
||||
.then(this.proxy('read_value'));
|
||||
var name = $(event.target).parent().find("a").html();
|
||||
if (confirm(_.str.sprintf(_t("Warning! \n %s won't be notified of any email or discussion on this document. Do you really want to remove him from the followers ?"), name))) {
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id], [partner_id], context])
|
||||
.then(this.proxy('read_value'));
|
||||
}
|
||||
},
|
||||
|
||||
read_value: function () {
|
||||
@@ -248,12 +251,15 @@ openerp_mail_followers = function(session, mail) {
|
||||
},
|
||||
|
||||
do_unfollow: function () {
|
||||
_(this.$('.oe_msg_subtype_check')).each(function (record) {
|
||||
$(record).attr('checked',false);
|
||||
});
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context])
|
||||
.then(this.proxy('read_value'));
|
||||
if (confirm(_t("Warning! \nYou won't be notified of any email or discussion on this document. Do you really want to unfollow this document ?"))) {
|
||||
_(this.$('.oe_msg_subtype_check')).each(function (record) {
|
||||
$(record).attr('checked',false);
|
||||
});
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context])
|
||||
.then(this.proxy('read_value'));
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
do_update_subscription: function (event) {
|
||||
@@ -267,7 +273,9 @@ openerp_mail_followers = function(session, mail) {
|
||||
});
|
||||
|
||||
if (!checklist.length) {
|
||||
this.do_unfollow();
|
||||
if (!this.do_unfollow()) {
|
||||
$(event.target).attr("checked", "checked");
|
||||
}
|
||||
} else {
|
||||
var context = new session.web.CompoundContext(this.build_context(), {});
|
||||
return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], checklist, context])
|
||||
|
||||
@@ -26,24 +26,37 @@
|
||||
</div>
|
||||
<div class="oe_msg_center">
|
||||
<div class="oe_msg_content">
|
||||
<button class="oe_full" title="Open the full mail composer"><span class='oe_e'>&ograve</span></button>
|
||||
<t t-call="mail.thread.list_recipients"/>
|
||||
<textarea class="field_text"></textarea>
|
||||
</div>
|
||||
<div class="oe_msg_footer">
|
||||
<div class="oe_msg_attachment_list"></div>
|
||||
<button class="oe_post">Post</button>
|
||||
<button class="oe_post">
|
||||
<t t-if="!widget.is_log">Send</t>
|
||||
<t t-if="widget.is_log">Log a note</t>
|
||||
</button>
|
||||
<t t-call="mail.compose_message.add_attachment"/>
|
||||
<!--<a class="oe_cancel oe_e">X</a>-->
|
||||
<button class="oe_full" title="Open the full mail composer"><span class='oe_e'>&ograve</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="widget.show_compact_message and !widget.show_composer and !widget.options.readonly" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
|
||||
<div class="field_text oe_compact">
|
||||
<t t-if="widget.options.compose_placeholder" t-raw="widget.options.compose_placeholder"/>
|
||||
<t t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox">Write to the followers of this document...</t>
|
||||
<t t-if="!widget.options.compose_placeholder and widget.options.view_mailbox">Share with my followers...</t>
|
||||
</div>
|
||||
<t t-if="widget.options.view_mailbox">
|
||||
<div class="field_text oe_compact oe_compact_inbox">
|
||||
<t t-if="widget.options.compose_placeholder" t-raw="widget.options.compose_placeholder"/>
|
||||
<t t-if="!widget.options.compose_placeholder and widget.options.view_mailbox">Share with my followers...</t>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="!widget.options.view_mailbox">
|
||||
<div class="field_text oe_compact oe_compact_record">
|
||||
<a class="oe_compose_post">
|
||||
<t t-if="widget.options.compose_placeholder" t-raw="widget.options.compose_placeholder"/>
|
||||
<t t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox">Send a message</t>
|
||||
</a>
|
||||
<span class="oe_grey">or</span>
|
||||
<a class="oe_compose_log">Log a note</a>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span>
|
||||
</t>
|
||||
@@ -57,7 +70,7 @@
|
||||
<span class="oe_add">
|
||||
<!-- uploader of file -->
|
||||
<button class="oe_attach"><span class="oe_e">'</span></button>
|
||||
<span class='oe_attach_label'>File</span>
|
||||
<span class='oe_attach_label'>Attach a File</span>
|
||||
<t t-call="HiddenInputFile">
|
||||
<t t-set="fileupload_id" t-value="widget.fileupload_id"/>
|
||||
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
|
||||
@@ -106,7 +119,7 @@
|
||||
template to the recipients list
|
||||
-->
|
||||
<t t-name="mail.thread.list_recipients">
|
||||
<div class="oe_mail_list_recipients">
|
||||
<div class="oe_mail_list_recipients" t-if="!widget.is_log">
|
||||
To:
|
||||
<t t-if="!widget.is_private">
|
||||
<span class="oe_all_follower">
|
||||
@@ -129,11 +142,11 @@
|
||||
<a class="oe_more_hidden"><<<</a>
|
||||
</t>
|
||||
</div>
|
||||
<div class="oe_emails_from">
|
||||
<t t-foreach='widget.emails_from' t-as='email_from'>
|
||||
<label title="Add them into recipients and followers">
|
||||
<input type="checkbox" t-att-checked="email_from[1] ? 'checked' : undefind" t-att-data="email_from[0][4]"/>
|
||||
<t t-raw="email_from[0][2]"/>
|
||||
<div class="oe_recipients" t-if="!widget.is_log">
|
||||
<t t-foreach='widget.recipients' t-as='recipient'>
|
||||
<label t-attf-title="Add as recipient and follower (reason: #{recipient.reason})">
|
||||
<input type="checkbox" t-att-checked="recipient.checked ? 'checked' : undefined" t-att-data="recipient.email_address"/>
|
||||
<t t-raw="recipient.name"/> (<t t-raw="recipient.email_address"/>)
|
||||
</label>
|
||||
</t>
|
||||
</div>
|
||||
@@ -212,7 +225,7 @@
|
||||
|
||||
<!-- default layout -->
|
||||
<t t-name="mail.thread.message">
|
||||
<div t-attf-class="oe_msg #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_#{widget.type}">
|
||||
<div t-attf-class="oe_msg #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} #{widget.partner_ids.length == 0 ? 'oe_msg_nobody' : ''} oe_msg_#{widget.type}">
|
||||
|
||||
<div class='oe_msg_left'>
|
||||
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}" t-att-title="widget.author_id[1]">
|
||||
@@ -245,6 +258,22 @@
|
||||
</t>
|
||||
<a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-raw="widget.author_id[2]"/></a>
|
||||
<span t-if="widget.author_id and (!widget.options.show_link or !widget.author_id[0])"><t t-raw="widget.author_id[2]"/></span>
|
||||
<t t-if="widget.partner_ids.length == 0">
|
||||
logged a note
|
||||
</t>
|
||||
<t t-if="widget.partner_ids.length > 0">
|
||||
to
|
||||
</t>
|
||||
<t t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
|
||||
<span t-attf-class="oe_partner_follower">
|
||||
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-raw="partner[1]"/></a>
|
||||
<t t-if="!widget.options.show_link" t-raw="partner[1]"/>
|
||||
</span>
|
||||
<t t-if="!partner_last">,</t>
|
||||
</t>
|
||||
<t t-if="widget.partner_ids.length > 3">
|
||||
<span t-att-title="widget.extra_partners_str">and <t t-raw="widget.extra_partners_nbr"/> more</span>
|
||||
</t>
|
||||
<span class='oe_subtle'>•</span>
|
||||
<span t-att-title="widget.date"><t t-if="widget.timerelative" t-raw="widget.timerelative"/><t t-if="!widget.timerelative" t-raw="widget.date"/></span>
|
||||
<span t-if="!widget.options.readonly" class='oe_subtle'>•</span>
|
||||
|
||||
@@ -119,18 +119,18 @@ class test_mail(TestMailBase):
|
||||
# Previously-created group can be emailed now - it should have an implicit alias group+frogs@...
|
||||
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
|
||||
group_messages = frog_group.message_ids
|
||||
self.assertTrue(len(group_messages) == 2, 'New group should only have the original message + creation log')
|
||||
self.assertTrue(len(group_messages) == 1, 'New group should only have the original message')
|
||||
mail_frog_news = MAIL_TEMPLATE.format(to='Friendly Frogs <group+frogs@example.com>', subject='news', extra='')
|
||||
self.mail_thread.message_process(cr, uid, None, mail_frog_news)
|
||||
frog_group.refresh()
|
||||
self.assertTrue(len(frog_group.message_ids) == 3, 'Group should contain 3 messages now')
|
||||
self.assertTrue(len(frog_group.message_ids) == 2, 'Group should contain 2 messages now')
|
||||
|
||||
# Even with a wrong destination, a reply should end up in the correct thread
|
||||
mail_reply = MAIL_TEMPLATE.format(to='erroneous@example.com>', subject='Re: news',
|
||||
extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
|
||||
self.mail_thread.message_process(cr, uid, None, mail_reply)
|
||||
frog_group.refresh()
|
||||
self.assertTrue(len(frog_group.message_ids) == 4, 'Group should contain 4 messages now')
|
||||
self.assertTrue(len(frog_group.message_ids) == 3, 'Group should contain 3 messages now')
|
||||
|
||||
# No model passed and no matching alias must raise
|
||||
mail_spam = MAIL_TEMPLATE.format(to='noone@example.com', subject='spam', extra='')
|
||||
@@ -154,7 +154,7 @@ class test_mail(TestMailBase):
|
||||
new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
|
||||
# Test: author_id set, not email_from
|
||||
self.assertEqual(new_mail.author_id, user_raoul.partner_id, 'message process wrong author found')
|
||||
self.assertFalse(new_mail.email_from, 'message process should not set the email_from when an author is found')
|
||||
self.assertEqual(new_mail.email_from, user_raoul.email, 'message process wrong email_from')
|
||||
|
||||
# Do: post a new message, with a unknown partner
|
||||
test_msg_id = '<deadcafe.1337-3@smtp.agrolait.com>'
|
||||
@@ -391,7 +391,8 @@ class test_mail(TestMailBase):
|
||||
# 1. Post a new email comment on Pigs
|
||||
self._init_mock_build_email()
|
||||
msg2_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, body=_body2, type='email', subtype='mt_comment',
|
||||
partner_ids=[(6, 0, [p_d_id])], parent_id=msg1_id, attachments=_attachments)
|
||||
partner_ids=[p_d_id], parent_id=msg1_id, attachments=_attachments,
|
||||
context={'mail_post_autofollow': True})
|
||||
message2 = self.mail_message.browse(cr, uid, msg2_id)
|
||||
sent_emails = self._build_email_kwargs_list
|
||||
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg2_id)]), 'mail.mail notifications should have been auto-deleted!')
|
||||
@@ -482,7 +483,7 @@ class test_mail(TestMailBase):
|
||||
self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id')
|
||||
|
||||
# 2. Post the comment, get created message
|
||||
mail_compose.send_mail(cr, uid, [compose_id])
|
||||
mail_compose.send_mail(cr, uid, [compose_id], {'mail_post_autofollow': True})
|
||||
group_pigs.refresh()
|
||||
message = group_pigs.message_ids[0]
|
||||
# Test: mail.message: subject, body inside pre
|
||||
|
||||
@@ -311,8 +311,8 @@ class test_mail_access_rights(TestMailBase):
|
||||
user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
|
||||
|
||||
# Prepare groups: Pigs (employee), Jobs (public)
|
||||
pigs_msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message', partner_ids=[(4, self.partner_admin_id)])
|
||||
jobs_msg_id = self.mail_group.message_post(cr, uid, self.group_jobs_id, body='Message', partner_ids=[(4, self.partner_admin_id)])
|
||||
pigs_msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message', partner_ids=[self.partner_admin_id])
|
||||
jobs_msg_id = self.mail_group.message_post(cr, uid, self.group_jobs_id, body='Message', partner_ids=[self.partner_admin_id])
|
||||
|
||||
# ----------------------------------------
|
||||
# CASE1: Bert, without groups
|
||||
|
||||
@@ -191,6 +191,7 @@ class mail_compose_message(osv.TransientModel):
|
||||
if context is None:
|
||||
context = {}
|
||||
active_ids = context.get('active_ids')
|
||||
is_log = context.get('mail_compose_log', False)
|
||||
|
||||
for wizard in self.browse(cr, uid, ids, context=context):
|
||||
mass_mail_mode = wizard.composition_mode == 'mass_mail'
|
||||
@@ -204,26 +205,22 @@ class mail_compose_message(osv.TransientModel):
|
||||
'subject': wizard.subject,
|
||||
'body': wizard.body,
|
||||
'parent_id': wizard.parent_id and wizard.parent_id.id,
|
||||
'partner_ids': [(4, partner.id) for partner in wizard.partner_ids],
|
||||
'partner_ids': [partner.id for partner in wizard.partner_ids],
|
||||
'attachments': [(attach.datas_fname or attach.name, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids],
|
||||
}
|
||||
# mass mailing: render and override default values
|
||||
if mass_mail_mode and wizard.model:
|
||||
email_dict = self.render_message(cr, uid, wizard, res_id, context=context)
|
||||
new_partner_ids = email_dict.pop('partner_ids', [])
|
||||
post_values['partner_ids'] += [(4, partner_id) for partner_id in new_partner_ids]
|
||||
post_values['partner_ids'] += new_partner_ids
|
||||
new_attachments = email_dict.pop('attachments', [])
|
||||
post_values['attachments'] += new_attachments
|
||||
post_values.update(email_dict)
|
||||
# automatically subscribe recipients if asked to
|
||||
if context.get('mail_post_autofollow') and wizard.model and post_values.get('partner_ids'):
|
||||
active_model_pool.message_subscribe(cr, uid, [res_id], [item[1] for item in post_values.get('partner_ids')], context=context)
|
||||
# post the message
|
||||
active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype='mt_comment', context=context, **post_values)
|
||||
|
||||
# post process: update attachments, because id is not necessarily known when adding attachments in Chatter
|
||||
# self.pool.get('ir.attachment').write(cr, uid, [attach.id for attach in wizard.attachment_ids], {
|
||||
# 'res_id': wizard.id, 'res_model': wizard.model or False}, context=context)
|
||||
subtype = 'mail.mt_comment'
|
||||
if is_log:
|
||||
subtype = False
|
||||
active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
|
||||
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
@@ -258,7 +255,7 @@ class mail_compose_message(osv.TransientModel):
|
||||
result = eval(exp, {
|
||||
'user': self.pool.get('res.users').browse(cr, uid, uid, context=context),
|
||||
'object': self.pool.get(model).browse(cr, uid, res_id, context=context),
|
||||
'context': dict(context), # copy context to prevent side-effects of eval
|
||||
'context': dict(context), # copy context to prevent side-effects of eval
|
||||
})
|
||||
return result and tools.ustr(result) or ''
|
||||
return template and EXPRESSION_PATTERN.sub(merge, template)
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
<field name="res_id" invisible="1"/>
|
||||
<field name="parent_id" invisible="1"/>
|
||||
<!-- visible wizard -->
|
||||
<label for="partner_ids" string="Recipients"/>
|
||||
<div groups="base.group_user">
|
||||
<label for="partner_ids" string="Recipients"
|
||||
invisible="context.get('mail_compose_log', False)"/>
|
||||
<div groups="base.group_user" invisible="context.get('mail_compose_log', False)">
|
||||
<span attrs="{'invisible':['|', ('model', '=', False), ('composition_mode', '!=', 'mass_mail')]}">
|
||||
Followers of selected items and
|
||||
</span>
|
||||
|
||||
@@ -476,7 +476,6 @@
|
||||
<field name="remaining_hours" sum="Remaining Time" groups="project.group_time_work_estimation_tasks"/>
|
||||
<field name="date_deadline"/>
|
||||
<field name="message_summary"/>
|
||||
<field name="message_unread"/>
|
||||
<field name="categ_ids"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
@@ -523,6 +522,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="oe_kanban_footer_left" style="margin-top:5px;">
|
||||
<t t-raw="record.message_summary.raw_value"/>
|
||||
<field name="categ_ids"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -485,6 +485,15 @@ class project_issue(base_stage, osv.osv):
|
||||
return [issue.project_id.message_get_reply_to()[0] if issue.project_id else False
|
||||
for issue in self.browse(cr, uid, ids, context=context)]
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
recipients = super(project_issue, self).message_get_suggested_recipients(cr, uid, ids, context=context)
|
||||
for issue in self.browse(cr, uid, ids, context=context):
|
||||
if issue.partner_id:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, issue, partner=issue.partner_id, reason=_('Customer'))
|
||||
elif issue.email_from:
|
||||
self._message_add_suggested_recipient(cr, uid, recipients, issue, email=issue.email_from, reason=_('Customer Email'))
|
||||
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.
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
<field name="user_id"/>
|
||||
<field name="date_deadline"/>
|
||||
<field name="kanban_state"/>
|
||||
<field name="message_summary"/>
|
||||
<templates>
|
||||
<t t-name="kanban-tooltip">
|
||||
<ul class="oe_kanban_tooltip">
|
||||
@@ -240,6 +241,9 @@
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_kanban_footer_left">
|
||||
<t t-raw="record.message_summary.raw_value"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_clear"></div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user