2011-07-22 16:34:57 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# OpenERP, Open Source Management Solution
|
2012-03-13 15:06:35 +00:00
|
|
|
# Copyright (C) 2009-today OpenERP SA (<http://www.openerp.com>)
|
2011-07-22 16:34:57 +00:00
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
2011-08-23 17:58:09 +00:00
|
|
|
import base64
|
2011-07-22 16:34:57 +00:00
|
|
|
import email
|
|
|
|
from email.utils import parsedate
|
|
|
|
import logging
|
2012-04-05 21:04:43 +00:00
|
|
|
from mail_message import decode, to_email
|
|
|
|
from operator import itemgetter
|
2011-07-22 16:34:57 +00:00
|
|
|
from osv import osv, fields
|
2012-04-05 21:04:43 +00:00
|
|
|
import re
|
|
|
|
import time
|
|
|
|
import tools
|
2011-07-22 16:34:57 +00:00
|
|
|
from tools.translate import _
|
2012-04-05 21:04:43 +00:00
|
|
|
import xmlrpclib
|
2011-07-22 16:34:57 +00:00
|
|
|
|
2012-02-01 16:21:36 +00:00
|
|
|
_logger = logging.getLogger(__name__)
|
2011-07-22 16:34:57 +00:00
|
|
|
|
2012-06-04 15:27:06 +00:00
|
|
|
class mail_thread(osv.Model):
|
2011-07-22 16:34:57 +00:00
|
|
|
'''Mixin model, meant to be inherited by any model that needs to
|
|
|
|
act as a discussion topic on which messages can be attached.
|
2011-08-25 12:27:57 +00:00
|
|
|
Public methods are prefixed with ``message_`` in order to avoid
|
|
|
|
name collisions with methods of the models that will inherit
|
|
|
|
from this mixin.
|
|
|
|
|
2012-03-13 15:06:35 +00:00
|
|
|
``mail.thread`` is designed to work without adding any field
|
|
|
|
to the extended models. All functionalities and expected behavior
|
|
|
|
are managed by mail.thread, using model name and record ids.
|
|
|
|
A widget has been designed for the 6.1 and following version of OpenERP
|
|
|
|
web-client. However, due to technical limitations, ``mail.thread``
|
|
|
|
adds a simulated one2many field, to display the web widget by
|
|
|
|
overriding the default field displayed. Using this field
|
|
|
|
is not recommanded has it will disappeear in future version
|
|
|
|
of OpenERP, leading to a pure mixin class.
|
2011-08-25 12:27:57 +00:00
|
|
|
|
|
|
|
Inheriting classes are not required to implement any method, as the
|
|
|
|
default implementation will work for any model. However it is common
|
|
|
|
to override at least the ``message_new`` and ``message_update``
|
|
|
|
methods (calling ``super``) to add model-specific behavior at
|
2012-03-13 15:06:35 +00:00
|
|
|
creation and update of a thread; and ``message_get_subscribers``
|
|
|
|
to manage more precisely the social aspect of the thread through
|
|
|
|
the followers.
|
2011-07-22 16:34:57 +00:00
|
|
|
'''
|
|
|
|
_name = 'mail.thread'
|
|
|
|
_description = 'Email Thread'
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-06-21 13:11:34 +00:00
|
|
|
def _get_message_ids(self, cr, uid, ids, name, args, context=None):
|
2012-02-03 11:21:16 +00:00
|
|
|
res = {}
|
2012-03-22 12:08:36 +00:00
|
|
|
for id in ids:
|
2012-07-03 12:20:20 +00:00
|
|
|
message_ids = self.message_search(cr, uid, [id], context=context)
|
2012-06-21 13:11:34 +00:00
|
|
|
subscriber_ids = self.message_get_subscribers(cr, uid, [id], context=context)
|
|
|
|
res[id] = {
|
|
|
|
'message_ids': message_ids,
|
2012-07-10 14:57:20 +00:00
|
|
|
'message_summary': "<span><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (len(message_ids), len(subscriber_ids)),
|
2012-06-21 13:11:34 +00:00
|
|
|
}
|
2012-02-03 11:21:16 +00:00
|
|
|
return res
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-06-14 10:09:22 +00:00
|
|
|
def _search_message_ids(self, cr, uid, obj, name, args, context=None):
|
|
|
|
msg_obj = self.pool.get('mail.message')
|
|
|
|
msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', 'in', args[0][2]), ('model', '=', self._name)], context=context)
|
|
|
|
return [('id', 'in', msg_ids)]
|
|
|
|
|
2011-07-22 16:34:57 +00:00
|
|
|
_columns = {
|
2012-07-05 10:22:19 +00:00
|
|
|
'message_ids': fields.function(_get_message_ids,
|
2012-06-27 12:00:20 +00:00
|
|
|
fnct_search=_search_message_ids,
|
2012-06-21 13:11:34 +00:00
|
|
|
type='one2many', obj='mail.message', _fields_id = 'res_id',
|
|
|
|
string='Temp messages', multi="_get_message_ids",
|
|
|
|
help="Functional field holding messages related to the current document."),
|
2012-06-21 08:40:35 +00:00
|
|
|
'message_state': fields.boolean('Read',
|
2012-06-21 13:11:34 +00:00
|
|
|
help="When checked, new messages require your attention."),
|
|
|
|
'message_summary': fields.function(_get_message_ids, method=True,
|
2012-06-21 15:23:11 +00:00
|
|
|
type='text', string='Summary', multi="_get_message_ids",
|
|
|
|
help="Holds the Chatter summary (number of messages, ...). "\
|
|
|
|
"This summary is directly in html format in order to "\
|
|
|
|
"be inserted in kanban views."),
|
2011-07-22 16:34:57 +00:00
|
|
|
}
|
2012-06-04 15:27:06 +00:00
|
|
|
|
|
|
|
_defaults = {
|
2012-06-21 08:40:35 +00:00
|
|
|
'message_state': True,
|
2011-07-22 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
2012-02-28 14:06:32 +00:00
|
|
|
#------------------------------------------------------
|
|
|
|
# Automatic subscription when creating/reading
|
|
|
|
#------------------------------------------------------
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-02-28 14:06:32 +00:00
|
|
|
def create(self, cr, uid, vals, context=None):
|
2012-06-04 09:33:24 +00:00
|
|
|
"""Automatically subscribe the creator """
|
|
|
|
thread_id = super(mail_thread, self).create(cr, uid, vals, context=context)
|
|
|
|
if thread_id:
|
|
|
|
self.message_subscribe(cr, uid, [thread_id], [uid], context=context)
|
|
|
|
return thread_id
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-02-28 14:06:32 +00:00
|
|
|
def write(self, cr, uid, ids, vals, context=None):
|
2012-03-22 13:01:14 +00:00
|
|
|
"""Automatically subscribe the writer"""
|
2012-03-22 08:07:45 +00:00
|
|
|
if isinstance(ids, (int, long)):
|
2012-03-21 17:59:49 +00:00
|
|
|
ids = [ids]
|
2012-02-28 14:06:32 +00:00
|
|
|
write_res = super(mail_thread, self).write(cr, uid, ids, vals, context=context);
|
|
|
|
if write_res:
|
|
|
|
self.message_subscribe(cr, uid, ids, [uid], context=context)
|
|
|
|
return write_res;
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-03-13 15:06:35 +00:00
|
|
|
def unlink(self, cr, uid, ids, context=None):
|
|
|
|
"""Override unlink, to automatically delete
|
|
|
|
- subscriptions
|
|
|
|
- messages
|
|
|
|
that are linked with res_model and res_id, not through
|
|
|
|
a foreign key with a 'cascade' ondelete attribute.
|
|
|
|
Notifications will be deleted with messages
|
|
|
|
"""
|
|
|
|
subscr_obj = self.pool.get('mail.subscription')
|
|
|
|
msg_obj = self.pool.get('mail.message')
|
2012-04-02 11:50:02 +00:00
|
|
|
# delete subscriptions
|
2012-03-13 15:06:35 +00:00
|
|
|
subscr_to_del_ids = subscr_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
|
|
|
|
subscr_obj.unlink(cr, uid, subscr_to_del_ids, context=context)
|
2012-04-20 12:42:00 +00:00
|
|
|
# delete messages and notifications
|
2012-03-13 15:06:35 +00:00
|
|
|
msg_to_del_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context)
|
|
|
|
msg_obj.unlink(cr, uid, msg_to_del_ids, context=context)
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-03-22 12:08:36 +00:00
|
|
|
return super(mail_thread, self).unlink(cr, uid, ids, context=context)
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-02-01 16:21:36 +00:00
|
|
|
#------------------------------------------------------
|
2012-06-21 09:37:55 +00:00
|
|
|
# mail.message wrappers and tools
|
2012-02-01 16:21:36 +00:00
|
|
|
#------------------------------------------------------
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-02-28 17:13:51 +00:00
|
|
|
def message_create(self, cr, uid, thread_id, vals, context=None):
|
2012-06-04 09:33:24 +00:00
|
|
|
""" OpenChatter: wrapper of mail.message create method
|
2012-03-22 13:01:14 +00:00
|
|
|
- creates the mail.message
|
2012-04-02 11:50:02 +00:00
|
|
|
- automatically subscribe the message writer
|
2012-03-22 13:01:14 +00:00
|
|
|
- push the message to subscribed users
|
|
|
|
"""
|
2012-02-28 17:13:51 +00:00
|
|
|
if context is None:
|
|
|
|
context = {}
|
2012-06-04 09:33:24 +00:00
|
|
|
|
2012-04-02 12:59:49 +00:00
|
|
|
message_obj = self.pool.get('mail.message')
|
2012-02-22 09:09:23 +00:00
|
|
|
notification_obj = self.pool.get('mail.notification')
|
2012-04-20 09:36:45 +00:00
|
|
|
body = vals.get('body_html', '') if vals.get('content_subtype') == 'html' else vals.get('body_text', '')
|
2012-02-22 09:09:23 +00:00
|
|
|
|
2012-04-02 11:50:02 +00:00
|
|
|
# automatically subscribe the writer of the message
|
|
|
|
if vals['user_id']:
|
2012-03-13 15:06:35 +00:00
|
|
|
self.message_subscribe(cr, uid, [thread_id], [vals['user_id']], context=context)
|
2012-02-22 09:58:12 +00:00
|
|
|
|
2012-04-03 17:34:49 +00:00
|
|
|
# create message
|
|
|
|
msg_id = message_obj.create(cr, uid, vals, context=context)
|
2012-06-21 09:37:55 +00:00
|
|
|
|
2012-06-07 15:17:53 +00:00
|
|
|
# Set as unread if writer is not the document responsible
|
2012-07-02 15:46:30 +00:00
|
|
|
self.message_create_set_unread(cr, uid, [thread_id], context=context)
|
2012-06-07 15:17:53 +00:00
|
|
|
|
2012-04-03 20:27:53 +00:00
|
|
|
# special: if install mode, do not push demo data
|
|
|
|
if context.get('install_mode', False):
|
2012-05-11 10:23:34 +00:00
|
|
|
return msg_id
|
2012-04-03 20:27:53 +00:00
|
|
|
|
2012-05-11 10:23:34 +00:00
|
|
|
# get users that will get a notification pushed
|
|
|
|
user_to_push_ids = self.message_get_user_ids_to_notify(cr, uid, [thread_id], vals, context=context)
|
2012-03-13 16:35:55 +00:00
|
|
|
for id in user_to_push_ids:
|
|
|
|
notification_obj.create(cr, uid, {'user_id': id, 'message_id': msg_id}, context=context)
|
2012-03-02 17:07:49 +00:00
|
|
|
|
2012-05-11 10:23:34 +00:00
|
|
|
# create the email to send
|
|
|
|
email_id = self.message_create_notify_by_email(cr, uid, vals, user_to_push_ids, context=context)
|
|
|
|
|
2012-02-22 09:09:23 +00:00
|
|
|
return msg_id
|
2012-03-02 17:07:49 +00:00
|
|
|
|
2012-05-11 10:23:34 +00:00
|
|
|
def message_get_user_ids_to_notify(self, cr, uid, thread_ids, new_msg_vals, context=None):
|
2012-04-20 13:19:44 +00:00
|
|
|
subscription_obj = self.pool.get('mail.subscription')
|
|
|
|
# get body
|
2012-04-20 09:36:45 +00:00
|
|
|
body = new_msg_vals.get('body_html', '') if new_msg_vals.get('content_subtype') == 'html' else new_msg_vals.get('body_text', '')
|
2012-04-20 13:19:44 +00:00
|
|
|
|
|
|
|
# get subscribers
|
2012-07-17 13:06:02 +00:00
|
|
|
notif_user_ids = self.message_get_subscribers(cr, uid, thread_ids, context=context)
|
2012-04-20 13:19:44 +00:00
|
|
|
|
|
|
|
# add users requested via parsing message (@login)
|
2012-05-11 10:23:34 +00:00
|
|
|
notif_user_ids += self.message_parse_users(cr, uid, body, context=context)
|
2012-04-20 13:19:44 +00:00
|
|
|
|
|
|
|
# add users requested to perform an action (need_action mechanism)
|
|
|
|
if hasattr(self, 'get_needaction_user_ids'):
|
|
|
|
user_ids_dict = self.get_needaction_user_ids(cr, uid, thread_ids, context=context)
|
|
|
|
for id, user_ids in user_ids_dict.iteritems():
|
|
|
|
notif_user_ids += user_ids
|
|
|
|
|
|
|
|
# add users notified of the parent messages (because: if parent message contains @login, login must receive the replies)
|
|
|
|
if new_msg_vals.get('parent_id'):
|
|
|
|
notif_obj = self.pool.get('mail.notification')
|
|
|
|
parent_notif_ids = notif_obj.search(cr, uid, [('message_id', '=', new_msg_vals.get('parent_id'))], context=context)
|
|
|
|
parent_notifs = notif_obj.read(cr, uid, parent_notif_ids, context=context)
|
|
|
|
notif_user_ids += [parent_notif['user_id'][0] for parent_notif in parent_notifs]
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-03-29 08:53:06 +00:00
|
|
|
# remove duplicate entries
|
|
|
|
notif_user_ids = list(set(notif_user_ids))
|
|
|
|
return notif_user_ids
|
|
|
|
|
2012-05-11 10:23:34 +00:00
|
|
|
def message_parse_users(self, cr, uid, string, context=None):
|
2012-03-22 13:01:14 +00:00
|
|
|
"""Parse message content
|
2012-04-02 11:50:02 +00:00
|
|
|
- if find @login -(^|\s)@((\w|@|\.)*)-: returns the related ids
|
2012-05-11 10:23:34 +00:00
|
|
|
this supports login that are emails (such as @raoul@grobedon.net)
|
2012-03-22 13:01:14 +00:00
|
|
|
"""
|
2012-04-02 11:50:02 +00:00
|
|
|
regex = re.compile('(^|\s)@((\w|@|\.)*)')
|
2012-03-08 11:24:45 +00:00
|
|
|
login_lst = [item[1] for item in regex.findall(string)]
|
|
|
|
if not login_lst: return []
|
|
|
|
user_ids = self.pool.get('res.users').search(cr, uid, [('login', 'in', login_lst)], context=context)
|
2012-03-02 17:07:49 +00:00
|
|
|
return user_ids
|
2012-02-22 09:09:23 +00:00
|
|
|
|
2012-06-21 09:37:55 +00:00
|
|
|
#------------------------------------------------------
|
|
|
|
# Generic message api
|
|
|
|
#------------------------------------------------------
|
|
|
|
|
2011-12-09 14:28:39 +00:00
|
|
|
def message_capable_models(self, cr, uid, context=None):
|
|
|
|
ret_dict = {}
|
|
|
|
for model_name in self.pool.obj_list():
|
|
|
|
model = self.pool.get(model_name)
|
|
|
|
if 'mail.thread' in getattr(model, '_inherit', []):
|
2012-04-25 05:41:43 +00:00
|
|
|
ret_dict[model_name] = model._description
|
2011-12-09 14:28:39 +00:00
|
|
|
return ret_dict
|
|
|
|
|
2012-04-04 08:57:22 +00:00
|
|
|
def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None,
|
2012-07-17 11:07:41 +00:00
|
|
|
type='email', email_date=None, parent_id=False,
|
2012-07-12 14:17:58 +00:00
|
|
|
content_subtype='plain', state=None,
|
|
|
|
partner_ids=None, email_from=False, email_to=False,
|
2012-04-20 10:38:50 +00:00
|
|
|
email_cc=None, email_bcc=None, reply_to=None,
|
|
|
|
headers=None, message_id=False, references=None,
|
|
|
|
attachments=None, original=None, context=None):
|
2012-07-12 14:17:58 +00:00
|
|
|
""" Creates a new mail.message through message_create. The new message
|
|
|
|
is attached to the current mail.thread, containing all the details
|
|
|
|
passed as parameters. All attachments will be attached to the
|
|
|
|
thread record as well as to the actual message.
|
2012-04-20 10:38:50 +00:00
|
|
|
|
2012-07-12 14:17:58 +00:00
|
|
|
This method calls message_create that will handle management of
|
|
|
|
subscription and notifications, and effectively create the message.
|
2012-04-20 10:38:50 +00:00
|
|
|
|
2012-07-12 14:17:58 +00:00
|
|
|
If ``email_from`` is not set or ``type`` not set as 'email',
|
|
|
|
a note message is created (comment or system notification),
|
|
|
|
without the usual envelope attributes (sender, recipients, etc.).
|
2011-07-22 16:34:57 +00:00
|
|
|
|
2012-07-12 14:17:58 +00:00
|
|
|
:param threads: list of thread ids, or list of browse_records
|
|
|
|
representing threads to which a new message should be attached
|
|
|
|
:param subject: subject of the message, or description of the event;
|
|
|
|
this is totally optional as subjects are not important except
|
|
|
|
for specific messages (blog post, job offers) or for emails
|
|
|
|
:param body_text: plaintext contents of the mail or log message
|
|
|
|
:param body_html: html contents of the mail or log message
|
|
|
|
:param type: type of message: 'email', 'comment', 'notification';
|
|
|
|
email by default
|
|
|
|
:param email_date: email date string if different from now, in
|
|
|
|
server timezone
|
|
|
|
:param parent_id: id of the parent message (threaded messaging model)
|
|
|
|
:param content_subtype: optional content_subtype of message: 'plain'
|
|
|
|
or 'html', corresponding to the main body contents (body_text or
|
|
|
|
body_html).
|
|
|
|
:param state: state of message
|
|
|
|
:param partner_ids: destination partners of the message, in addition
|
|
|
|
to the now fully optional email_to; this method is supposed to
|
|
|
|
received a list of ids is not None. The specific many2many
|
|
|
|
instruction will be generated by this method.
|
|
|
|
:param email_from: Email From / Sender address if any
|
|
|
|
:param email_to: Email-To / Recipient address
|
|
|
|
:param email_cc: Comma-Separated list of Carbon Copy Emails To
|
|
|
|
addresses if any
|
|
|
|
:param email_bcc: Comma-Separated list of Blind Carbon Copy Emails To
|
|
|
|
addresses if any
|
|
|
|
:param reply_to: reply_to header
|
|
|
|
:param headers: mail headers to store
|
|
|
|
:param message_id: optional email identifier
|
|
|
|
:param references: optional email references
|
|
|
|
:param dict attachments: map of attachment filenames to binary
|
|
|
|
contents, if any.
|
|
|
|
:param str original: optional full source of the RFC2822 email, for
|
|
|
|
reference
|
|
|
|
:param dict context: if a ``thread_model`` value is present in the
|
|
|
|
context, its value will be used to determine the model of the
|
|
|
|
thread to update (instead of the current model).
|
2011-07-22 16:34:57 +00:00
|
|
|
"""
|
2012-04-25 05:41:43 +00:00
|
|
|
if context is None:
|
2011-07-22 16:34:57 +00:00
|
|
|
context = {}
|
|
|
|
if attachments is None:
|
|
|
|
attachments = {}
|
|
|
|
|
|
|
|
if email_date:
|
|
|
|
edate = parsedate(email_date)
|
|
|
|
if edate is not None:
|
|
|
|
email_date = time.strftime('%Y-%m-%d %H:%M:%S', edate)
|
|
|
|
|
|
|
|
if all(isinstance(thread_id, (int, long)) for thread_id in threads):
|
|
|
|
model = context.get('thread_model') or self._name
|
|
|
|
model_pool = self.pool.get(model)
|
|
|
|
threads = model_pool.browse(cr, uid, threads, context=context)
|
|
|
|
|
|
|
|
ir_attachment = self.pool.get('ir.attachment')
|
|
|
|
mail_message = self.pool.get('mail.message')
|
|
|
|
|
2012-04-04 08:57:22 +00:00
|
|
|
new_msg_ids = []
|
2011-07-22 16:34:57 +00:00
|
|
|
for thread in threads:
|
|
|
|
to_attach = []
|
2011-10-18 03:39:13 +00:00
|
|
|
for attachment in attachments:
|
|
|
|
fname, fcontent = attachment
|
2011-07-22 16:34:57 +00:00
|
|
|
if isinstance(fcontent, unicode):
|
|
|
|
fcontent = fcontent.encode('utf-8')
|
|
|
|
data_attach = {
|
|
|
|
'name': fname,
|
2011-08-23 17:58:09 +00:00
|
|
|
'datas': base64.b64encode(str(fcontent)),
|
2011-07-22 16:34:57 +00:00
|
|
|
'datas_fname': fname,
|
|
|
|
'description': _('Mail attachment'),
|
|
|
|
'res_model': thread._name,
|
|
|
|
'res_id': thread.id,
|
|
|
|
}
|
|
|
|
to_attach.append(ir_attachment.create(cr, uid, data_attach, context=context))
|
2012-07-12 14:17:58 +00:00
|
|
|
# find related partner: partner_id column in thread object, or self is res.partner model
|
2012-06-13 06:23:28 +00:00
|
|
|
partner_id = ('partner_id' in thread._columns.keys()) and (thread.partner_id and thread.partner_id.id or False) or False
|
2011-07-22 16:34:57 +00:00
|
|
|
if not partner_id and thread._name == 'res.partner':
|
|
|
|
partner_id = thread.id
|
2012-07-12 14:17:58 +00:00
|
|
|
# destination partners
|
|
|
|
if partner_ids is None:
|
|
|
|
partner_ids = []
|
2012-07-12 15:42:59 +00:00
|
|
|
mail_partner_ids = [(6, 0, partner_ids)]
|
2012-07-12 14:17:58 +00:00
|
|
|
|
2011-07-22 16:34:57 +00:00
|
|
|
data = {
|
|
|
|
'subject': subject,
|
2012-03-09 10:56:11 +00:00
|
|
|
'body_text': body_text or (hasattr(thread, 'description') and thread.description or ''),
|
2012-03-22 12:08:36 +00:00
|
|
|
'body_html': body_html or '',
|
2012-04-04 08:57:22 +00:00
|
|
|
'parent_id': parent_id,
|
|
|
|
'date': email_date or fields.datetime.now(),
|
2012-02-02 09:48:45 +00:00
|
|
|
'type': type,
|
2012-04-20 09:36:45 +00:00
|
|
|
'content_subtype': content_subtype,
|
2012-04-04 08:57:22 +00:00
|
|
|
'state': state,
|
|
|
|
'message_id': message_id,
|
2012-07-12 14:17:58 +00:00
|
|
|
'partner_ids': mail_partner_ids,
|
2012-04-04 08:57:22 +00:00
|
|
|
'attachment_ids': [(6, 0, to_attach)],
|
2011-07-22 16:34:57 +00:00
|
|
|
'user_id': uid,
|
|
|
|
'model' : thread._name,
|
|
|
|
'res_id': thread.id,
|
2012-04-04 08:57:22 +00:00
|
|
|
'partner_id': partner_id,
|
2011-07-22 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
2012-03-22 12:08:36 +00:00
|
|
|
if email_from or type == 'email':
|
2011-07-22 16:34:57 +00:00
|
|
|
for param in (email_to, email_cc, email_bcc):
|
|
|
|
if isinstance(param, list):
|
|
|
|
param = ", ".join(param)
|
2012-03-22 12:08:36 +00:00
|
|
|
data.update({
|
2011-07-22 16:34:57 +00:00
|
|
|
'email_to': email_to,
|
|
|
|
'email_from': email_from or \
|
2011-08-09 23:44:28 +00:00
|
|
|
(hasattr(thread, 'user_id') and thread.user_id and thread.user_id.user_email),
|
2011-07-22 16:34:57 +00:00
|
|
|
'email_cc': email_cc,
|
|
|
|
'email_bcc': email_bcc,
|
|
|
|
'references': references,
|
|
|
|
'headers': headers,
|
|
|
|
'reply_to': reply_to,
|
2012-03-22 12:08:36 +00:00
|
|
|
'original': original, })
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-04-04 08:57:22 +00:00
|
|
|
new_msg_ids.append(self.message_create(cr, uid, thread.id, data, context=context))
|
|
|
|
return new_msg_ids
|
2011-07-22 16:34:57 +00:00
|
|
|
|
2011-08-25 12:27:57 +00:00
|
|
|
def message_append_dict(self, cr, uid, ids, msg_dict, context=None):
|
|
|
|
"""Creates a new mail.message attached to the given threads (``ids``),
|
|
|
|
with the contents of ``msg_dict``, by calling ``message_append``
|
|
|
|
with the mail details. All attachments in msg_dict will be
|
|
|
|
attached to the object record as well as to the actual
|
|
|
|
mail message.
|
|
|
|
|
|
|
|
:param dict msg_dict: a map containing the email details and
|
|
|
|
attachments. See ``message_process()`` and
|
|
|
|
``mail.message.parse()`` for details on
|
|
|
|
the dict structure.
|
|
|
|
:param dict context: if a ``thread_model`` value is present
|
|
|
|
in the context, its value will be used
|
|
|
|
to determine the model of the thread to
|
|
|
|
update (instead of the current model).
|
|
|
|
"""
|
|
|
|
return self.message_append(cr, uid, ids,
|
|
|
|
subject = msg_dict.get('subject'),
|
|
|
|
body_text = msg_dict.get('body_text'),
|
2012-04-04 08:57:22 +00:00
|
|
|
body_html= msg_dict.get('body_html'),
|
2012-03-02 17:07:49 +00:00
|
|
|
parent_id = msg_dict.get('parent_id', False),
|
2012-03-22 12:08:36 +00:00
|
|
|
type = msg_dict.get('type', 'email'),
|
2012-04-20 10:38:50 +00:00
|
|
|
content_subtype = msg_dict.get('content_subtype'),
|
|
|
|
state = msg_dict.get('state'),
|
2012-07-12 14:17:58 +00:00
|
|
|
partner_ids = msg_dict.get('partner_ids'),
|
2012-03-22 12:08:36 +00:00
|
|
|
email_from = msg_dict.get('from', msg_dict.get('email_from')),
|
|
|
|
email_to = msg_dict.get('to', msg_dict.get('email_to')),
|
|
|
|
email_cc = msg_dict.get('cc', msg_dict.get('email_cc')),
|
|
|
|
email_bcc = msg_dict.get('bcc', msg_dict.get('email_bcc')),
|
|
|
|
reply_to = msg_dict.get('reply', msg_dict.get('reply_to')),
|
2011-08-25 12:27:57 +00:00
|
|
|
email_date = msg_dict.get('date'),
|
2012-03-28 09:02:13 +00:00
|
|
|
message_id = msg_dict.get('message-id', msg_dict.get('message_id')),
|
2011-08-25 12:27:57 +00:00
|
|
|
references = msg_dict.get('references')\
|
|
|
|
or msg_dict.get('in-reply-to'),
|
|
|
|
attachments = msg_dict.get('attachments'),
|
|
|
|
headers = msg_dict.get('headers'),
|
|
|
|
original = msg_dict.get('original'),
|
|
|
|
context = context)
|
|
|
|
|
2012-07-03 12:20:20 +00:00
|
|
|
#------------------------------------------------------
|
2012-02-01 16:21:36 +00:00
|
|
|
# Message loading
|
2012-07-03 12:20:20 +00:00
|
|
|
#------------------------------------------------------
|
|
|
|
|
|
|
|
def _message_search_ancestor_ids(self, cr, uid, ids, child_ids, ancestor_ids, context=None):
|
2012-05-03 09:37:25 +00:00
|
|
|
""" Given message child_ids ids, find their ancestors until ancestor_ids
|
2012-07-03 12:20:20 +00:00
|
|
|
using their parent_id relationship.
|
|
|
|
|
|
|
|
:param child_ids: the first nodes of the search
|
|
|
|
:param ancestor_ids: list of ancestors. When the search reach an
|
|
|
|
ancestor, it stops.
|
|
|
|
"""
|
|
|
|
def _get_parent_ids(message_list, ancestor_ids, child_ids):
|
|
|
|
""" Tool function: return the list of parent_ids of messages
|
|
|
|
contained in message_list. Parents that are in ancestor_ids
|
|
|
|
or in child_ids are not returned. """
|
|
|
|
return [message['parent_id'][0] for message in message_list
|
|
|
|
if message['parent_id']
|
|
|
|
and message['parent_id'][0] not in ancestor_ids
|
|
|
|
and message['parent_id'][0] not in child_ids
|
|
|
|
]
|
|
|
|
|
|
|
|
message_obj = self.pool.get('mail.message')
|
|
|
|
messages_temp = message_obj.read(cr, uid, child_ids, ['id', 'parent_id'], context=context)
|
|
|
|
parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids)
|
2012-03-07 13:10:22 +00:00
|
|
|
child_ids += parent_ids
|
2012-04-02 11:50:02 +00:00
|
|
|
cur_iter = 0; max_iter = 100; # avoid infinite loop
|
2012-03-07 13:10:22 +00:00
|
|
|
while (parent_ids and (cur_iter < max_iter)):
|
|
|
|
cur_iter += 1
|
2012-07-03 12:20:20 +00:00
|
|
|
messages_temp = message_obj.read(cr, uid, parent_ids, ['id', 'parent_id'], context=context)
|
|
|
|
parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids)
|
2012-03-07 13:10:22 +00:00
|
|
|
child_ids += parent_ids
|
2012-04-02 11:50:02 +00:00
|
|
|
if (cur_iter > max_iter):
|
2012-07-03 12:20:20 +00:00
|
|
|
_logger.warning("Possible infinite loop in _message_search_ancestor_ids. "\
|
|
|
|
"Note that this algorithm is intended to check for cycle in "\
|
|
|
|
"message graph, leading to a curious error. Have fun.")
|
2012-03-07 13:10:22 +00:00
|
|
|
return child_ids
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-07-03 12:20:20 +00:00
|
|
|
def message_search_get_domain(self, cr, uid, ids, context=None):
|
|
|
|
""" OpenChatter feature: get the domain to search the messages related
|
|
|
|
to a document. mail.thread defines the default behavior as
|
|
|
|
being messages with model = self._name, id in ids.
|
|
|
|
This method should be overridden if a model has to implement a
|
|
|
|
particular behavior.
|
2012-02-07 17:07:46 +00:00
|
|
|
"""
|
2012-07-03 12:20:20 +00:00
|
|
|
return ['&', ('res_id', 'in', ids), ('model', '=', self._name)]
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-07-03 12:20:20 +00:00
|
|
|
def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None,
|
2012-07-11 12:36:13 +00:00
|
|
|
limit=100, offset=0, domain=None, count=False, context=None):
|
2012-07-03 12:20:20 +00:00
|
|
|
""" OpenChatter feature: return thread messages ids according to the
|
|
|
|
search domain given by ``message_search_get_domain``.
|
2012-05-03 09:37:25 +00:00
|
|
|
|
|
|
|
It is possible to add in the search the parent of messages by
|
|
|
|
setting the fetch_ancestors flag to True. In that case, using
|
|
|
|
the parent_id relationship, the method returns the id list according
|
2012-07-03 12:20:20 +00:00
|
|
|
to the search domain, but then calls ``_message_search_ancestor_ids``
|
2012-05-03 09:37:25 +00:00
|
|
|
that will add to the list the ancestors ids. The search is limited
|
|
|
|
to parent messages having an id in ancestor_ids or having
|
|
|
|
parent_id set to False.
|
|
|
|
|
|
|
|
If ``count==True``, the number of ids is returned instead of the
|
|
|
|
id list. The count is done by hand instead of passing it as an
|
|
|
|
argument to the search call because we might want to perform
|
|
|
|
a research including parent messages until some ancestor_ids.
|
|
|
|
|
|
|
|
:param fetch_ancestors: performs an ascended search; will add
|
|
|
|
to fetched msgs all their parents until
|
|
|
|
ancestor_ids
|
|
|
|
:param ancestor_ids: used when fetching ancestors
|
2012-04-02 11:50:02 +00:00
|
|
|
:param domain: domain to add to the search; especially child_of
|
2012-05-03 09:37:25 +00:00
|
|
|
is interesting when dealing with threaded display.
|
|
|
|
Note that the added domain is anded with the
|
|
|
|
default domain.
|
|
|
|
:param limit, offset, count, context: as usual
|
2012-02-07 17:07:46 +00:00
|
|
|
"""
|
2012-07-03 12:20:20 +00:00
|
|
|
search_domain = self.message_search_get_domain(cr, uid, ids, context=context)
|
2012-05-03 09:37:25 +00:00
|
|
|
if domain:
|
|
|
|
search_domain += domain
|
2012-07-03 12:20:20 +00:00
|
|
|
message_obj = self.pool.get('mail.message')
|
|
|
|
message_res = message_obj.search(cr, uid, search_domain, limit=limit, offset=offset, count=count, context=context)
|
|
|
|
if not count and fetch_ancestors:
|
|
|
|
message_res += self._message_search_ancestor_ids(cr, uid, ids, message_res, ancestor_ids, context=context)
|
|
|
|
return message_res
|
|
|
|
|
|
|
|
def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None,
|
2012-07-11 12:36:13 +00:00
|
|
|
limit=100, offset=0, domain=None, context=None):
|
2012-07-03 12:20:20 +00:00
|
|
|
""" OpenChatter feature: read the messages related to some threads.
|
|
|
|
This method is used mainly the Chatter widget, to directly have
|
|
|
|
read result instead of searching then reading.
|
|
|
|
|
|
|
|
Please see message_search for more information about the parameters.
|
|
|
|
"""
|
|
|
|
message_ids = self.message_search(cr, uid, ids, fetch_ancestors, ancestor_ids,
|
2012-07-11 12:36:13 +00:00
|
|
|
limit, offset, domain, context=context)
|
2012-07-03 12:20:20 +00:00
|
|
|
messages = self.pool.get('mail.message').read(cr, uid, message_ids, context=context)
|
2012-07-19 12:03:15 +00:00
|
|
|
|
2012-06-19 18:27:23 +00:00
|
|
|
""" Retrieve all attachments names """
|
2012-07-03 12:20:20 +00:00
|
|
|
map_id_to_name = dict((attachment_id, '') for message in messages for attachment_id in message['attachment_ids'])
|
2012-06-19 18:27:23 +00:00
|
|
|
map_id_to_name = {}
|
2012-07-03 12:20:20 +00:00
|
|
|
for msg in messages:
|
2012-06-19 18:27:23 +00:00
|
|
|
for attach_id in msg["attachment_ids"]:
|
|
|
|
map_id_to_name[attach_id] = '' # use empty string as a placeholder
|
2012-07-03 12:20:20 +00:00
|
|
|
|
2012-06-19 18:27:23 +00:00
|
|
|
ids = map_id_to_name.keys()
|
2012-06-20 14:02:04 +00:00
|
|
|
names = self.pool.get('ir.attachment').name_get(cr, uid, ids, context=context)
|
2012-06-19 18:27:23 +00:00
|
|
|
|
|
|
|
# convert the list of tuples into a dictionnary
|
|
|
|
for name in names:
|
|
|
|
map_id_to_name[name[0]] = name[1]
|
|
|
|
|
|
|
|
# give corresponding ids and names to each message
|
2012-07-03 12:20:20 +00:00
|
|
|
for msg in messages:
|
2012-06-19 18:27:23 +00:00
|
|
|
msg["attachments"] = []
|
|
|
|
|
|
|
|
for attach_id in msg["attachment_ids"]:
|
|
|
|
msg["attachments"].append({'id': attach_id, 'name': map_id_to_name[attach_id]})
|
|
|
|
|
2012-07-03 12:20:20 +00:00
|
|
|
# Set the threads as read
|
|
|
|
self.message_check_and_set_read(cr, uid, ids, context=context)
|
|
|
|
# Sort and return the messages
|
|
|
|
messages = sorted(messages, key=lambda d: (-d['id']))
|
|
|
|
return messages
|
|
|
|
|
|
|
|
def message_get_pushed_messages(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None,
|
2012-05-03 09:37:25 +00:00
|
|
|
limit=100, offset=0, msg_search_domain=[], context=None):
|
2012-07-03 12:20:20 +00:00
|
|
|
""" OpenChatter: wall: get the pushed notifications and used them
|
|
|
|
to fetch messages to display on the wall.
|
2012-05-03 09:37:25 +00:00
|
|
|
|
|
|
|
:param fetch_ancestors: performs an ascended search; will add
|
|
|
|
to fetched msgs all their parents until
|
|
|
|
ancestor_ids
|
|
|
|
:param ancestor_ids: used when fetching ancestors
|
2012-04-02 12:59:49 +00:00
|
|
|
:param domain: domain to add to the search; especially child_of
|
|
|
|
is interesting when dealing with threaded display
|
|
|
|
:param ascent: performs an ascended search; will add to fetched msgs
|
|
|
|
all their parents until root_ids
|
|
|
|
:param root_ids: for ascent search
|
2012-07-03 12:20:20 +00:00
|
|
|
:return: list of mail.messages sorted by date
|
2012-02-23 17:49:36 +00:00
|
|
|
"""
|
|
|
|
notification_obj = self.pool.get('mail.notification')
|
2012-03-07 13:10:22 +00:00
|
|
|
msg_obj = self.pool.get('mail.message')
|
2012-04-02 12:59:49 +00:00
|
|
|
# update message search
|
|
|
|
for arg in msg_search_domain:
|
|
|
|
if isinstance(arg, (tuple, list)):
|
|
|
|
arg[0] = 'message_id.' + arg[0]
|
|
|
|
# compose final domain
|
2012-04-03 13:38:59 +00:00
|
|
|
domain = [('user_id', '=', uid)] + msg_search_domain
|
2012-04-02 12:59:49 +00:00
|
|
|
# get notifications
|
|
|
|
notification_ids = notification_obj.search(cr, uid, domain, limit=limit, offset=offset, context=context)
|
2012-02-23 17:49:36 +00:00
|
|
|
notifications = notification_obj.browse(cr, uid, notification_ids, context=context)
|
|
|
|
msg_ids = [notification.message_id.id for notification in notifications]
|
2012-04-02 12:59:49 +00:00
|
|
|
# get messages
|
|
|
|
msg_ids = msg_obj.search(cr, uid, [('id', 'in', msg_ids)], context=context)
|
2012-07-03 12:20:20 +00:00
|
|
|
if (fetch_ancestors): msg_ids = self._message_search_ancestor_ids(cr, uid, ids, msg_ids, ancestor_ids, context=context)
|
2012-03-07 13:10:22 +00:00
|
|
|
msgs = msg_obj.read(cr, uid, msg_ids, context=context)
|
2012-02-23 17:49:36 +00:00
|
|
|
return msgs
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-06-14 14:17:32 +00:00
|
|
|
|
2012-06-21 06:14:05 +00:00
|
|
|
def _get_user(self, cr, uid, alias, context):
|
|
|
|
"""
|
2012-06-21 09:07:14 +00:00
|
|
|
param alias: browse record of alias.
|
|
|
|
return: int user_id.
|
2012-06-21 06:14:05 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
user_obj = self.pool.get('res.user')
|
2012-06-21 09:07:14 +00:00
|
|
|
user_id = 1
|
2012-06-21 06:14:05 +00:00
|
|
|
if alias.alias_user_id:
|
2012-06-22 10:50:57 +00:00
|
|
|
user_id = alias.alias_user_id.id
|
2012-06-21 06:14:05 +00:00
|
|
|
#if user_id not defined in the alias then search related user using name of Email sender
|
2012-06-21 06:23:33 +00:00
|
|
|
else:
|
2012-06-21 09:07:14 +00:00
|
|
|
from_email = msg.get('from')
|
|
|
|
user_ids = user_obj.search(cr, uid, [('name','=',from_email)], context)
|
2012-06-21 06:14:05 +00:00
|
|
|
if user_ids:
|
|
|
|
user_id = user_obj.browse(cr, uid, user_ids[0], context).id
|
2012-06-21 09:07:14 +00:00
|
|
|
return user_id
|
2012-06-21 06:14:05 +00:00
|
|
|
|
2012-06-14 14:17:32 +00:00
|
|
|
def message_catchall(self, cr, uid, message, context=None):
|
2012-06-21 06:14:05 +00:00
|
|
|
"""
|
|
|
|
Process incoming mail and call messsage_process using details of the mail.alias model
|
|
|
|
else raise Exception so that mailgate script will reject the mail and
|
|
|
|
send notification mail sender that this mailbox does not exist so your mail have been rejected.
|
|
|
|
"""
|
2012-08-06 00:44:17 +00:00
|
|
|
mail_alias = self.pool.get('mail.alias')
|
|
|
|
mail_message = self.pool.get('mail.message')
|
2012-06-21 06:14:05 +00:00
|
|
|
if isinstance(message, xmlrpclib.Binary):
|
|
|
|
message = str(message.data)
|
|
|
|
if isinstance(message, unicode):
|
|
|
|
message = message.encode('utf-8')
|
|
|
|
msg_txt = email.message_from_string(message)
|
2012-08-06 00:44:17 +00:00
|
|
|
msg = mail_message.parse_message(msg_txt)
|
|
|
|
alias_name = msg.get('to').split("@")[0] # @@@@
|
|
|
|
alias_ids = mail_alias.search(cr, uid, [('alias_name','=',alias_name)])
|
|
|
|
#if alias found then call message_process method. # @@@@
|
2012-06-25 11:31:33 +00:00
|
|
|
if alias_ids:
|
2012-08-06 00:44:17 +00:00
|
|
|
alias_id = mail_alias.browse(cr, uid, alias_ids[0], context)
|
2012-06-22 10:50:57 +00:00
|
|
|
user_id = self._get_user( cr, uid, alias_id, context)
|
2012-06-29 11:52:33 +00:00
|
|
|
alias_defaults = dict(eval(alias_id.alias_defaults or {}))
|
2012-06-22 10:50:57 +00:00
|
|
|
self.message_process(cr, user_id, alias_id.alias_model_id.model, message,
|
2012-08-06 00:44:17 +00:00
|
|
|
custom_values=alias_defaults,
|
|
|
|
thread_id=alias_id.alias_force_thread_id or False,
|
2012-06-22 10:50:57 +00:00
|
|
|
context=context)
|
2012-06-21 06:14:05 +00:00
|
|
|
else:
|
2012-06-29 05:26:51 +00:00
|
|
|
#if Mail box for the intended Mail Alias then give logger warning
|
2012-06-29 09:09:27 +00:00
|
|
|
_logger.warning("No Mail Alias Found for the name '%s'."%(alias_name))
|
2012-08-06 00:44:17 +00:00
|
|
|
raise # @@@@
|
2012-06-21 06:14:05 +00:00
|
|
|
return True
|
2012-06-14 14:17:32 +00:00
|
|
|
|
2012-02-01 16:21:36 +00:00
|
|
|
#------------------------------------------------------
|
2012-06-21 09:37:55 +00:00
|
|
|
# Mail gateway
|
2012-02-01 16:21:36 +00:00
|
|
|
#------------------------------------------------------
|
|
|
|
# message_process will call either message_new or message_update.
|
2011-08-25 12:27:57 +00:00
|
|
|
|
2011-09-07 15:13:48 +00:00
|
|
|
def message_process(self, cr, uid, model, message, custom_values=None,
|
2011-09-08 00:16:51 +00:00
|
|
|
save_original=False, strip_attachments=False,
|
2012-06-14 14:17:32 +00:00
|
|
|
thread_id=None, context=None):
|
2011-07-22 16:34:57 +00:00
|
|
|
"""Process an incoming RFC2822 email message related to the
|
|
|
|
given thread model, relying on ``mail.message.parse()``
|
|
|
|
for the parsing operation, and then calling ``message_new``
|
|
|
|
(if the thread record did not exist) or ``message_update``
|
2011-08-25 12:27:57 +00:00
|
|
|
(if it did), then calling ``message_forward`` to automatically
|
|
|
|
notify other people that should receive this message.
|
2011-07-22 16:34:57 +00:00
|
|
|
|
|
|
|
:param string model: the thread model for which a new message
|
|
|
|
must be processed
|
|
|
|
:param message: source of the RFC2822 mail
|
|
|
|
:type message: string or xmlrpclib.Binary
|
2011-09-13 13:23:40 +00:00
|
|
|
:type dict custom_values: optional dictionary of field values
|
2011-07-22 16:34:57 +00:00
|
|
|
to pass to ``message_new`` if a new
|
|
|
|
record needs to be created. Ignored
|
|
|
|
if the thread record already exists.
|
2011-09-07 15:13:48 +00:00
|
|
|
:param bool save_original: whether to keep a copy of the original
|
2012-07-03 12:20:20 +00:00
|
|
|
email source attached to the message after it is imported.
|
2011-09-08 00:16:51 +00:00
|
|
|
:param bool strip_attachments: whether to strip all attachments
|
2012-07-03 12:20:20 +00:00
|
|
|
before processing the message, in order to save some space.
|
2012-06-14 14:17:32 +00:00
|
|
|
:param int thread_id: optional ID of the record/thread from ``model``
|
|
|
|
to which this mail should be attached. When provided, this
|
|
|
|
overrides the automatic detection based on the message
|
|
|
|
headers.
|
2011-07-22 16:34:57 +00:00
|
|
|
"""
|
|
|
|
# extract message bytes - we are forced to pass the message as binary because
|
|
|
|
# we don't know its encoding until we parse its headers and hence can't
|
|
|
|
# convert it to utf-8 for transport between the mailgate script and here.
|
|
|
|
if isinstance(message, xmlrpclib.Binary):
|
|
|
|
message = str(message.data)
|
|
|
|
|
2012-06-14 14:17:32 +00:00
|
|
|
if context is None: context = {}
|
|
|
|
|
|
|
|
mail_message = self.pool.get('mail.message')
|
2011-07-22 16:34:57 +00:00
|
|
|
model_pool = self.pool.get(model)
|
|
|
|
if self._name != model:
|
2011-08-25 12:27:57 +00:00
|
|
|
context.update({'thread_model': model})
|
2011-07-22 16:34:57 +00:00
|
|
|
|
|
|
|
# Parse Message
|
|
|
|
# Warning: message_from_string doesn't always work correctly on unicode,
|
|
|
|
# we must use utf-8 strings here :-(
|
|
|
|
if isinstance(message, unicode):
|
|
|
|
message = message.encode('utf-8')
|
|
|
|
msg_txt = email.message_from_string(message)
|
2012-05-10 13:54:47 +00:00
|
|
|
msg = mail_message.parse_message(msg_txt, save_original=save_original, context=context)
|
2011-07-22 16:34:57 +00:00
|
|
|
|
2012-05-10 15:14:45 +00:00
|
|
|
# update state
|
|
|
|
msg['state'] = 'received'
|
|
|
|
|
2011-09-08 00:16:51 +00:00
|
|
|
if strip_attachments and 'attachments' in msg:
|
|
|
|
del msg['attachments']
|
|
|
|
|
2011-07-22 16:34:57 +00:00
|
|
|
# Create New Record into particular model
|
|
|
|
def create_record(msg):
|
|
|
|
if hasattr(model_pool, 'message_new'):
|
|
|
|
return model_pool.message_new(cr, uid, msg,
|
|
|
|
custom_values,
|
|
|
|
context=context)
|
2012-06-14 14:17:32 +00:00
|
|
|
if not thread_id and (msg.get('references') or msg.get('in-reply-to')):
|
2011-07-22 16:34:57 +00:00
|
|
|
references = msg.get('references') or msg.get('in-reply-to')
|
|
|
|
if '\r\n' in references:
|
|
|
|
references = references.split('\r\n')
|
|
|
|
else:
|
|
|
|
references = references.split(' ')
|
|
|
|
for ref in references:
|
|
|
|
ref = ref.strip()
|
2012-06-14 14:17:32 +00:00
|
|
|
thread_id = tools.reference_re.search(ref)
|
|
|
|
if not thread_id:
|
|
|
|
thread_id = tools.res_re.search(msg['subject'])
|
|
|
|
if thread_id:
|
|
|
|
thread_id = int(thread_id.group(1))
|
|
|
|
if not model_pool.exists(cr, uid, thread_id) or \
|
|
|
|
not hasattr(model_pool, 'message_update'):
|
|
|
|
# referenced thread not found or not updatable,
|
|
|
|
# -> create a new one
|
|
|
|
thread_id = False
|
|
|
|
if not thread_id:
|
|
|
|
thread_id = create_record(msg)
|
|
|
|
else:
|
|
|
|
model_pool.message_update(cr, uid, [thread_id], msg, {}, context=context)
|
2012-06-04 15:27:06 +00:00
|
|
|
# To forward the email to other followers
|
2012-06-14 14:17:32 +00:00
|
|
|
self.message_forward(cr, uid, model, [thread_id], msg_txt, context=context)
|
2012-06-04 10:24:35 +00:00
|
|
|
# Set as Unread
|
2012-08-03 14:24:50 +00:00
|
|
|
model_pool.message_mark_as_unread(cr, uid, [thread_id], context=context)
|
2012-06-14 14:17:32 +00:00
|
|
|
return thread_id
|
2011-07-22 16:34:57 +00:00
|
|
|
|
2012-02-01 16:21:36 +00:00
|
|
|
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
|
|
|
|
"""Called by ``message_process`` when a new message is received
|
2012-04-25 05:41:43 +00:00
|
|
|
for a given thread model, if the message did not belong to
|
2012-02-01 16:21:36 +00:00
|
|
|
an existing thread.
|
|
|
|
The default behavior is to create a new record of the corresponding
|
|
|
|
model (based on some very basic info extracted from the message),
|
|
|
|
then attach the message to the newly created record
|
|
|
|
(by calling ``message_append_dict``).
|
|
|
|
Additional behavior may be implemented by overriding this method.
|
|
|
|
|
|
|
|
:param dict msg_dict: a map containing the email details and
|
|
|
|
attachments. See ``message_process`` and
|
|
|
|
``mail.message.parse`` for details.
|
|
|
|
:param dict custom_values: optional dictionary of additional
|
|
|
|
field values to pass to create()
|
|
|
|
when creating the new thread record.
|
|
|
|
Be careful, these values may override
|
|
|
|
any other values coming from the message.
|
|
|
|
:param dict context: if a ``thread_model`` value is present
|
|
|
|
in the context, its value will be used
|
|
|
|
to determine the model of the record
|
|
|
|
to create (instead of the current model).
|
|
|
|
:rtype: int
|
|
|
|
:return: the id of the newly created thread object
|
|
|
|
"""
|
|
|
|
if context is None:
|
|
|
|
context = {}
|
|
|
|
model = context.get('thread_model') or self._name
|
|
|
|
model_pool = self.pool.get(model)
|
|
|
|
fields = model_pool.fields_get(cr, uid, context=context)
|
|
|
|
data = model_pool.default_get(cr, uid, fields, context=context)
|
|
|
|
if 'name' in fields and not data.get('name'):
|
2012-06-04 14:12:54 +00:00
|
|
|
data['name'] = msg_dict.get('from', '')
|
2012-02-01 16:21:36 +00:00
|
|
|
if custom_values and isinstance(custom_values, dict):
|
|
|
|
data.update(custom_values)
|
|
|
|
res_id = model_pool.create(cr, uid, data, context=context)
|
|
|
|
self.message_append_dict(cr, uid, [res_id], msg_dict, context=context)
|
|
|
|
return res_id
|
|
|
|
|
2012-06-04 14:12:54 +00:00
|
|
|
def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
|
2012-02-01 16:21:36 +00:00
|
|
|
"""Called by ``message_process`` when a new message is received
|
|
|
|
for an existing thread. The default behavior is to create a
|
|
|
|
new mail.message in the given thread (by calling
|
|
|
|
``message_append_dict``)
|
|
|
|
Additional behavior may be implemented by overriding this
|
|
|
|
method.
|
|
|
|
:param dict msg_dict: a map containing the email details and
|
2012-07-05 10:22:19 +00:00
|
|
|
attachments. See ``message_process`` and
|
|
|
|
``mail.message.parse()`` for details.
|
|
|
|
:param dict update_vals: a dict containing values to update records
|
2012-06-04 14:12:54 +00:00
|
|
|
given their ids; if the dict is None or is
|
|
|
|
void, no write operation is performed.
|
2012-02-01 16:21:36 +00:00
|
|
|
"""
|
2012-06-04 14:12:54 +00:00
|
|
|
if update_vals:
|
|
|
|
self.write(cr, uid, ids, update_vals, context=context)
|
2012-02-01 16:21:36 +00:00
|
|
|
return self.message_append_dict(cr, uid, ids, msg_dict, context=context)
|
|
|
|
|
|
|
|
def message_thread_followers(self, cr, uid, ids, context=None):
|
2012-06-14 08:40:33 +00:00
|
|
|
""" Returns a list of email addresses of the people following
|
|
|
|
this thread, including the sender of each mail, and the
|
|
|
|
people who were in CC of the messages, if any.
|
2012-02-01 16:21:36 +00:00
|
|
|
"""
|
|
|
|
res = {}
|
|
|
|
if isinstance(ids, (str, int, long)):
|
|
|
|
ids = [long(ids)]
|
|
|
|
for thread in self.browse(cr, uid, ids, context=context):
|
|
|
|
l = set()
|
|
|
|
for message in thread.message_ids:
|
|
|
|
l.add((message.user_id and message.user_id.user_email) or '')
|
|
|
|
l.add(message.email_from or '')
|
|
|
|
l.add(message.email_cc or '')
|
|
|
|
res[thread.id] = filter(None, l)
|
|
|
|
return res
|
2011-08-23 17:58:09 +00:00
|
|
|
|
|
|
|
def message_forward(self, cr, uid, model, thread_ids, msg, email_error=False, context=None):
|
2011-07-22 16:34:57 +00:00
|
|
|
"""Sends an email to all people following the given threads.
|
|
|
|
The emails are forwarded immediately, not queued for sending,
|
|
|
|
and not archived.
|
|
|
|
|
|
|
|
:param str model: thread model
|
|
|
|
:param list thread_ids: ids of the thread records
|
|
|
|
:param msg: email.message.Message object to forward
|
|
|
|
:param email_error: optional email address to notify in case
|
|
|
|
of any delivery error during the forward.
|
|
|
|
:return: True
|
|
|
|
"""
|
|
|
|
model_pool = self.pool.get(model)
|
|
|
|
smtp_server_obj = self.pool.get('ir.mail_server')
|
|
|
|
mail_message = self.pool.get('mail.message')
|
|
|
|
for res in model_pool.browse(cr, uid, thread_ids, context=context):
|
2011-08-23 17:58:09 +00:00
|
|
|
if hasattr(model_pool, 'message_thread_followers'):
|
|
|
|
followers = model_pool.message_thread_followers(cr, uid, [res.id])[res.id]
|
|
|
|
else:
|
|
|
|
followers = self.message_thread_followers(cr, uid, [res.id])[res.id]
|
2011-09-07 15:13:48 +00:00
|
|
|
message_followers_emails = to_email(','.join(filter(None, followers)))
|
|
|
|
message_recipients = to_email(','.join(filter(None,
|
2011-07-22 16:34:57 +00:00
|
|
|
[decode(msg['from']),
|
|
|
|
decode(msg['to']),
|
|
|
|
decode(msg['cc'])])))
|
2011-08-25 12:27:57 +00:00
|
|
|
forward_to = [i for i in message_followers_emails if (i and (i not in message_recipients))]
|
|
|
|
if forward_to:
|
2011-07-22 16:34:57 +00:00
|
|
|
# TODO: we need an interface for this for all types of objects, not just leads
|
|
|
|
if hasattr(res, 'section_id'):
|
|
|
|
del msg['reply-to']
|
|
|
|
msg['reply-to'] = res.section_id.reply_to
|
|
|
|
|
2011-09-07 20:27:02 +00:00
|
|
|
smtp_from, = to_email(msg['from'])
|
2011-07-22 16:34:57 +00:00
|
|
|
msg['from'] = smtp_from
|
2011-09-07 20:27:02 +00:00
|
|
|
msg['to'] = ", ".join(forward_to)
|
2011-07-22 16:34:57 +00:00
|
|
|
msg['message-id'] = tools.generate_tracking_message_id(res.id)
|
|
|
|
if not smtp_server_obj.send_email(cr, uid, msg) and email_error:
|
|
|
|
subj = msg['subject']
|
|
|
|
del msg['subject'], msg['to'], msg['cc'], msg['bcc']
|
|
|
|
msg['subject'] = _('[OpenERP-Forward-Failed] %s') % subj
|
|
|
|
msg['to'] = email_error
|
|
|
|
smtp_server_obj.send_email(cr, uid, msg)
|
|
|
|
return True
|
|
|
|
|
2011-08-25 12:27:57 +00:00
|
|
|
def message_partner_by_email(self, cr, uid, email, context=None):
|
2011-07-22 16:34:57 +00:00
|
|
|
"""Attempts to return the id of a partner address matching
|
2011-08-25 12:27:57 +00:00
|
|
|
the given ``email``, and the corresponding partner id.
|
|
|
|
Can be used by classes using the ``mail.thread`` mixin
|
|
|
|
to lookup the partner and use it in their implementation
|
|
|
|
of ``message_new`` to link the new record with a
|
|
|
|
corresponding partner.
|
|
|
|
The keys used in the returned dict are meant to map
|
|
|
|
to usual names for relationships towards a partner
|
|
|
|
and one of its addresses.
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2011-07-22 16:34:57 +00:00
|
|
|
:param email: email address for which a partner
|
|
|
|
should be searched for.
|
|
|
|
:rtype: dict
|
|
|
|
:return: a map of the following form::
|
|
|
|
|
|
|
|
{ 'partner_address_id': id or False,
|
|
|
|
'partner_id': pid or False }
|
|
|
|
"""
|
2012-03-05 08:25:05 +00:00
|
|
|
partner_pool = self.pool.get('res.partner')
|
|
|
|
res = {'partner_id': False}
|
2011-09-07 16:28:19 +00:00
|
|
|
if email:
|
|
|
|
email = to_email(email)[0]
|
2012-03-05 08:25:05 +00:00
|
|
|
contact_ids = partner_pool.search(cr, uid, [('email', '=', email)])
|
|
|
|
if contact_ids:
|
|
|
|
contact = partner_pool.browse(cr, uid, contact_ids[0])
|
|
|
|
res['partner_id'] = contact.id
|
2011-07-22 16:34:57 +00:00
|
|
|
return res
|
|
|
|
|
2012-02-01 16:21:36 +00:00
|
|
|
# for backwards-compatibility with old scripts
|
|
|
|
process_email = message_process
|
|
|
|
|
|
|
|
#------------------------------------------------------
|
|
|
|
# Note specific
|
|
|
|
#------------------------------------------------------
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-04-02 11:50:02 +00:00
|
|
|
def log(self, cr, uid, id, message, secondary=False, context=None):
|
2012-04-20 10:38:50 +00:00
|
|
|
_logger.warning("log() is deprecated. As this module inherit from \
|
|
|
|
mail.thread, the message will be managed by this \
|
|
|
|
module instead of by the res.log mechanism. Please \
|
|
|
|
use the mail.thread OpenChatter API instead of the \
|
|
|
|
now deprecated res.log.")
|
2012-04-02 14:09:58 +00:00
|
|
|
self.message_append_note(cr, uid, [id], 'res.log', message, context=context)
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-04-20 10:38:50 +00:00
|
|
|
def message_append_note(self, cr, uid, ids, subject=None, body=None, parent_id=False,
|
2012-07-17 11:07:41 +00:00
|
|
|
type='notification', content_subtype='html', context=None):
|
2012-04-20 09:36:45 +00:00
|
|
|
if content_subtype == 'html':
|
2012-04-05 15:48:21 +00:00
|
|
|
body_html = body
|
|
|
|
body_text = body
|
|
|
|
else:
|
|
|
|
body_html = body
|
|
|
|
body_text = body
|
2012-04-20 10:38:50 +00:00
|
|
|
return self.message_append(cr, uid, ids, subject, body_html, body_text,
|
2012-07-17 11:07:41 +00:00
|
|
|
type, parent_id=parent_id,
|
2012-04-20 10:38:50 +00:00
|
|
|
content_subtype=content_subtype, context=context)
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-02-01 16:21:36 +00:00
|
|
|
#------------------------------------------------------
|
|
|
|
# Subscription mechanism
|
|
|
|
#------------------------------------------------------
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-06-05 12:22:21 +00:00
|
|
|
def message_get_subscribers(self, cr, uid, ids, context=None):
|
|
|
|
""" Returns the current document followers. Basically this method
|
|
|
|
checks in mail.subscription for entries with matching res_model,
|
|
|
|
res_id.
|
2012-06-14 08:40:33 +00:00
|
|
|
This method can be overriden to add implicit subscribers, such
|
|
|
|
as project managers, by adding their user_id to the list of
|
|
|
|
ids returned by this method.
|
2012-06-05 12:22:21 +00:00
|
|
|
"""
|
2012-03-13 15:06:35 +00:00
|
|
|
subscr_obj = self.pool.get('mail.subscription')
|
|
|
|
subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
|
2012-06-05 12:22:21 +00:00
|
|
|
return [sub['user_id'][0] for sub in subscr_obj.read(cr, uid, subscr_ids, ['user_id'], context=context)]
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-06-05 12:22:21 +00:00
|
|
|
def message_read_subscribers(self, cr, uid, ids, fields=['id', 'name', 'avatar'], context=None):
|
|
|
|
""" Returns the current document followers as a read result. Used
|
|
|
|
mainly for Chatter having only one method to call to have
|
|
|
|
details about users.
|
|
|
|
"""
|
|
|
|
user_ids = self.message_get_subscribers(cr, uid, ids, context=context)
|
|
|
|
return self.pool.get('res.users').read(cr, uid, user_ids, fields=fields, context=context)
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-02-28 14:38:30 +00:00
|
|
|
def message_is_subscriber(self, cr, uid, ids, user_id = None, context=None):
|
2012-06-05 12:22:21 +00:00
|
|
|
""" Check if uid or user_id (if set) is a subscriber to the current
|
|
|
|
document.
|
|
|
|
|
|
|
|
:param user_id: if set, check is done on user_id; if not set
|
|
|
|
check is done on uid
|
|
|
|
"""
|
2012-02-28 14:38:30 +00:00
|
|
|
sub_user_id = uid if user_id is None else user_id
|
2012-06-05 12:22:21 +00:00
|
|
|
if sub_user_id in self.message_get_subscribers(cr, uid, ids, context=context):
|
2012-02-28 14:06:32 +00:00
|
|
|
return True
|
|
|
|
return False
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-02-20 17:21:06 +00:00
|
|
|
def message_subscribe(self, cr, uid, ids, user_ids = None, context=None):
|
2012-06-05 12:22:21 +00:00
|
|
|
""" Subscribe the user (or user_ids) to the current document.
|
|
|
|
|
|
|
|
:param user_ids: a list of user_ids; if not set, subscribe
|
|
|
|
uid instead
|
|
|
|
"""
|
2012-02-02 10:35:01 +00:00
|
|
|
subscription_obj = self.pool.get('mail.subscription')
|
2012-03-08 16:55:08 +00:00
|
|
|
to_subscribe_uids = [uid] if user_ids is None else user_ids
|
2012-02-28 14:38:30 +00:00
|
|
|
create_ids = []
|
2012-02-02 10:35:01 +00:00
|
|
|
for id in ids:
|
2012-06-05 12:22:21 +00:00
|
|
|
already_subscribed_user_ids = self.message_get_subscribers(cr, uid, [id], context=context)
|
2012-03-08 16:55:08 +00:00
|
|
|
for user_id in to_subscribe_uids:
|
2012-06-05 12:22:21 +00:00
|
|
|
if user_id in already_subscribed_user_ids: continue
|
2012-02-28 14:38:30 +00:00
|
|
|
create_ids.append(subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context))
|
2012-02-20 17:21:06 +00:00
|
|
|
return create_ids
|
2012-02-01 16:21:36 +00:00
|
|
|
|
2012-03-08 16:55:08 +00:00
|
|
|
def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None):
|
2012-06-05 12:22:21 +00:00
|
|
|
""" Unsubscribe the user (or user_ids) from the current document.
|
|
|
|
|
|
|
|
:param user_ids: a list of user_ids; if not set, subscribe
|
|
|
|
uid instead
|
|
|
|
"""
|
|
|
|
# Trying to unsubscribe somebody not in subscribers: returns False
|
|
|
|
# if special management is needed; allows to know that an automatically
|
|
|
|
# subscribed user tries to unsubscribe and allows to warn him
|
2012-03-08 16:55:08 +00:00
|
|
|
to_unsubscribe_uids = [uid] if user_ids is None else user_ids
|
2012-07-11 10:46:50 +00:00
|
|
|
subscription_obj = self.pool.get('mail.subscription')
|
2012-03-08 16:55:08 +00:00
|
|
|
to_delete_sub_ids = subscription_obj.search(cr, uid,
|
|
|
|
['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('user_id', 'in', to_unsubscribe_uids)], context=context)
|
2012-07-11 10:46:50 +00:00
|
|
|
if not to_delete_sub_ids:
|
|
|
|
return False
|
2012-06-05 12:22:21 +00:00
|
|
|
return subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context)
|
2012-02-01 16:21:36 +00:00
|
|
|
|
2012-03-21 17:20:18 +00:00
|
|
|
#------------------------------------------------------
|
|
|
|
# Notification API
|
|
|
|
#------------------------------------------------------
|
2012-04-25 05:41:43 +00:00
|
|
|
|
2012-05-11 10:23:34 +00:00
|
|
|
def message_create_notify_by_email(self, cr, uid, new_msg_values, user_to_notify_ids, context=None):
|
|
|
|
""" When creating a new message and pushing notifications, emails
|
|
|
|
must be send if users have chosen to receive notifications
|
|
|
|
by email via the notification_email_pref field.
|
|
|
|
|
|
|
|
``notification_email_pref`` can have 3 values :
|
|
|
|
- all: receive all notification by email (for example for shared
|
|
|
|
users)
|
|
|
|
- to_me: messages send directly to me (@login, messages on res.users)
|
|
|
|
- never: never receive notifications
|
|
|
|
Note that an user should never receive notifications for messages
|
|
|
|
he has created.
|
|
|
|
|
|
|
|
:param new_msg_values: dictionary of message values, those that
|
|
|
|
are given to the create method
|
|
|
|
:param user_to_notify_ids: list of user_ids, user that will
|
|
|
|
receive a notification on their Wall
|
|
|
|
"""
|
|
|
|
message_obj = self.pool.get('mail.message')
|
|
|
|
res_users_obj = self.pool.get('res.users')
|
|
|
|
body = new_msg_values.get('body_html', '') if new_msg_values.get('content_subtype') == 'html' else new_msg_values.get('body_text', '')
|
|
|
|
|
|
|
|
# remove message writer
|
|
|
|
if user_to_notify_ids.count(new_msg_values.get('user_id')) > 0:
|
|
|
|
user_to_notify_ids.remove(new_msg_values.get('user_id'))
|
|
|
|
|
|
|
|
# get user_ids directly asked
|
|
|
|
user_to_push_from_parse_ids = self.message_parse_users(cr, uid, body, context=context)
|
|
|
|
|
|
|
|
# try to find an email_to
|
2012-05-11 12:40:06 +00:00
|
|
|
email_to = ''
|
2012-05-11 10:23:34 +00:00
|
|
|
for user in res_users_obj.browse(cr, uid, user_to_notify_ids, context=context):
|
|
|
|
if not user.notification_email_pref == 'all' and \
|
|
|
|
not (user.notification_email_pref == 'to_me' and user.id in user_to_push_from_parse_ids):
|
|
|
|
continue
|
|
|
|
if not user.user_email:
|
|
|
|
continue
|
|
|
|
email_to = '%s, %s' % (email_to, user.user_email)
|
|
|
|
email_to = email_to.lstrip(', ')
|
|
|
|
|
|
|
|
# did not find any email address: not necessary to create an email
|
|
|
|
if not email_to:
|
|
|
|
return
|
|
|
|
|
|
|
|
# try to find an email_from
|
|
|
|
current_user = res_users_obj.browse(cr, uid, [uid], context=context)[0]
|
|
|
|
email_from = new_msg_values.get('email_from')
|
2012-05-11 12:40:06 +00:00
|
|
|
if not email_from:
|
2012-05-11 10:23:34 +00:00
|
|
|
email_from = current_user.user_email
|
|
|
|
|
|
|
|
# get email content, create it (with mail_message.create)
|
|
|
|
email_values = self.message_create_notify_get_email_dict(cr, uid, new_msg_values, email_from, email_to, context)
|
|
|
|
email_id = message_obj.create(cr, uid, email_values, context=context)
|
|
|
|
return email_id
|
|
|
|
|
|
|
|
def message_create_notify_get_email_dict(self, cr, uid, new_msg_values, email_from, email_to, context=None):
|
|
|
|
values = dict(new_msg_values)
|
2012-05-11 12:58:30 +00:00
|
|
|
|
|
|
|
body_html = new_msg_values.get('body_html', '')
|
|
|
|
if body_html:
|
|
|
|
body_html += '\n\n----------\nThis email was send automatically by OpenERP, because you have subscribed to a document.'
|
|
|
|
body_text = new_msg_values.get('body_text', '')
|
|
|
|
if body_text:
|
|
|
|
body_text += '\n\n----------\nThis email was send automatically by OpenERP, because you have subscribed to a document.'
|
2012-05-11 10:23:34 +00:00
|
|
|
values.update({
|
|
|
|
'type': 'email',
|
|
|
|
'state': 'outgoing',
|
|
|
|
'email_from': email_from,
|
|
|
|
'email_to': email_to,
|
|
|
|
'subject': 'New message',
|
2012-05-11 12:58:30 +00:00
|
|
|
'content_subtype': new_msg_values.get('content_subtype', 'plain'),
|
|
|
|
'body_html': body_html,
|
|
|
|
'body_text': body_text,
|
2012-05-11 12:40:06 +00:00
|
|
|
'auto_delete': True,
|
|
|
|
'res_model': '',
|
|
|
|
'res_id': False,
|
2012-05-11 10:23:34 +00:00
|
|
|
})
|
|
|
|
return values
|
2012-06-04 08:01:34 +00:00
|
|
|
|
2012-04-02 11:50:02 +00:00
|
|
|
def message_remove_pushed_notifications(self, cr, uid, ids, msg_ids, remove_childs=True, context=None):
|
2012-03-21 17:20:18 +00:00
|
|
|
notif_obj = self.pool.get('mail.notification')
|
|
|
|
msg_obj = self.pool.get('mail.message')
|
|
|
|
if remove_childs:
|
|
|
|
notif_msg_ids = msg_obj.search(cr, uid, [('id', 'child_of', msg_ids)], context=context)
|
|
|
|
else:
|
|
|
|
notif_msg_ids = msg_ids
|
|
|
|
to_del_notif_ids = notif_obj.search(cr, uid, ['&', ('user_id', '=', uid), ('message_id', 'in', notif_msg_ids)], context=context)
|
|
|
|
return notif_obj.unlink(cr, uid, to_del_notif_ids, context=context)
|
|
|
|
|
2012-06-04 09:33:24 +00:00
|
|
|
#------------------------------------------------------
|
|
|
|
# Thread_state
|
|
|
|
#------------------------------------------------------
|
2012-06-07 15:17:53 +00:00
|
|
|
|
2012-07-02 15:46:30 +00:00
|
|
|
def message_create_set_unread(self, cr, uid, ids, context=None):
|
|
|
|
""" When creating a new message, set as unread if uid is not the
|
|
|
|
object responsible. """
|
2012-06-07 15:17:53 +00:00
|
|
|
for obj in self.browse(cr, uid, ids, context=context):
|
2012-06-25 16:13:12 +00:00
|
|
|
if obj.message_state and hasattr(obj, 'user_id') and (not obj.user_id or obj.user_id.id != uid):
|
|
|
|
self.message_mark_as_unread(cr, uid, [obj.id], context=context)
|
|
|
|
|
|
|
|
def message_check_and_set_unread(self, cr, uid, ids, context=None):
|
2012-07-02 15:46:30 +00:00
|
|
|
""" Set unread if uid is the object responsible or if the object has
|
|
|
|
no responsible. """
|
2012-06-25 16:13:12 +00:00
|
|
|
for obj in self.browse(cr, uid, ids, context=context):
|
|
|
|
if obj.message_state and hasattr(obj, 'user_id') and (not obj.user_id or obj.user_id.id == uid):
|
2012-06-21 10:07:14 +00:00
|
|
|
self.message_mark_as_unread(cr, uid, [obj.id], context=context)
|
2012-06-21 09:37:55 +00:00
|
|
|
|
2012-06-04 09:33:24 +00:00
|
|
|
def message_mark_as_unread(self, cr, uid, ids, context=None):
|
2012-07-02 15:46:30 +00:00
|
|
|
""" Set as unread. """
|
2012-06-21 08:40:35 +00:00
|
|
|
return self.write(cr, uid, ids, {'message_state': False}, context=context)
|
2012-06-04 14:12:54 +00:00
|
|
|
|
2012-06-25 16:13:12 +00:00
|
|
|
def message_check_and_set_read(self, cr, uid, ids, context=None):
|
2012-07-02 15:46:30 +00:00
|
|
|
""" Set read if uid is the object responsible. """
|
2012-06-25 16:13:12 +00:00
|
|
|
for obj in self.browse(cr, uid, ids, context=context):
|
|
|
|
if not obj.message_state and hasattr(obj, 'user_id') and obj.user_id and obj.user_id.id == uid:
|
|
|
|
self.message_mark_as_read(cr, uid, [obj.id], context=context)
|
|
|
|
|
|
|
|
def message_mark_as_read(self, cr, uid, ids, context=None):
|
2012-07-02 15:46:30 +00:00
|
|
|
""" Set as read. """
|
2012-06-25 16:13:12 +00:00
|
|
|
return self.write(cr, uid, ids, {'message_state': True}, context=context)
|
|
|
|
|
2012-06-04 09:33:24 +00:00
|
|
|
|
2011-07-22 16:34:57 +00:00
|
|
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|