[MERGE] Merged a Mail update

- fix of the behavior of the mailboxes, now more closely related to a todo-list
- some code optimization to lessen the number of queries when displaying messages or for list view using the message_unread field
- code update of chatter (mail.js)
- added some demo data

bzr revid: tde@openerp.com-20121112155148-ktljqs3w58cvsk6r
This commit is contained in:
Thibault Delavallée
2012-11-12 16:51:48 +01:00
35 changed files with 1409 additions and 845 deletions
+1 -1
View File
@@ -22,12 +22,12 @@
import mail_message_subtype
import mail_alias
import mail_followers
import mail_vote
import mail_favorite
import mail_message
import mail_mail
import mail_thread
import mail_group
import mail_vote
import res_partner
import res_users
import report
+194 -4
View File
@@ -7,6 +7,7 @@
<field name="notification_email_send">none</field>
</record>
<!-- Pushed to all employees -->
<record id="message_blogpost0" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_all_employees"/>
@@ -15,7 +16,6 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
</record>
<record id="message_blogpost0_comment0" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_all_employees"/>
@@ -24,7 +24,6 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
</record>
<record id="message_blogpost0_comment1" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_all_employees"/>
@@ -33,7 +32,7 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
</record>
<!-- This one is starred for having mailboxes with demo data -->
<record id="message_blogpost0_comment2" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_all_employees"/>
@@ -41,8 +40,8 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
<field name="parent_id" ref="message_blogpost0"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="favorite_user_ids" eval="[(6, 0, [ref('base.user_root'), ref('base.user_demo')])]"/>
</record>
<record id="message_blogpost0_comment3" model="mail.message">
<field name="model">mail.group</field>
<field name="res_id" ref="group_all_employees"/>
@@ -52,5 +51,196 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
<field name="subtype_id" ref="mt_comment"/>
</record>
<!-- Demo user and admin conversation -->
<record id="message_discussion" model="mail.message">
<field name="body">Hello Demo User! I was wondering whether you had some issues with our secret task about putting cats everywhere in OpenERP.</field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]"/>
</record>
<record id="message_discussion_answer1" model="mail.message">
<field name="body">No specific issues, I think everything is clear.</field>
<field name="parent_id" ref="message_discussion"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
</record>
<record id="message_discussion_answer2" model="mail.message">
<field name="body">Ow, just to be sure... we were talking about lolcats, right ?</field>
<field name="parent_id" ref="message_discussion"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
</record>
<record id="message_discussion_answer3" model="mail.message">
<field name="body">Absolutely!</field>
<field name="parent_id" ref="message_discussion"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]"/>
</record>
<!-- External mail + reply with attachment conversation -->
<record id="message_video2" model="mail.message">
<field name="subject">Plan to install OpenERP</field>
<field name="model">mail.message</field>
<field name="body">
&lt;![CDATA[Email0 inquiry]]&gt;
&lt;div&gt;&lt;font size="2"&gt;Hello,&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font size="2"&gt;I am interested in your company's product and I plan to install OpenERP for my company and affordable price.&lt;/font&gt;&lt;/div&gt;&lt;br/&gt;
&lt;div&gt;&lt;font size="2"&gt;Can you please send me services catalogue?&lt;/font&gt;&lt;/div&gt;&lt;br/&gt;
Sophie
</field>
<field name="type">email</field>
<field name="author_id" ref="base.res_partner_2"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
</record>
<record id="message_video2_attachment1" model="ir.attachment">
<field name="model">ir.attachment</field>
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
<field name="datas_fname">catalogue 2012.pdf</field>
<field name="name">catalogue 2012.pdf</field>
</record>
<record id="message_video2_message1" model="mail.message">
<field name="subject">Re: Plan to install OpenERP</field>
<field name="body">
Dear Customer,&lt;br/&gt;
Thanks for showing interest in our products.&lt;br/&gt;
We have attached the catalogue,&lt;br/&gt;
We would like to know your interests, so let us know when we can call you for more details.&lt;br/&gt;
Regards
</field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="parent_id" ref="message_video2"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="attachment_ids" eval="[(6, 0, [ref('message_video2_attachment1')])]"/>
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo'),ref('base.partner_root'),ref('base.res_partner_2')])]"/>
</record>
<!-- Employee & other + attachments conversation -->
<record id="message_video1_attachment1" model="ir.attachment">
<field name="model">ir.attachment</field>
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
<field name="datas_fname">migration.doc</field>
<field name="name">migration.doc</field>
</record>
<record id="message_video1_attachment2" model="ir.attachment">
<field name="model">ir.attachment</field>
<field name="datas">
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
CAEAIAQAgP/Z
</field>
<field name="datas_fname">activity graph 2012.jpg</field>
<field name="name">activity graph 2012</field>
</record>
<record id="message_video1" model="mail.message">
<field name="model">mail.group</field>
<field name="body">
Hi,&lt;br/&gt;
The beta OpenERP 7 is scheduled for November 12.&lt;br/&gt;
You will find attached the document for migration from version 6 to 7, and the activity graph for the current year. Good reading.&lt;br/&gt;
Sincerely
</field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="attachment_ids" eval="[(6, 0, [ref('message_video1_attachment1'), ref('message_video1_attachment2')])]"/>
<field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
</record>
<record id="message_video1_message1" model="mail.message">
<field name="model">mail.group</field>
<field name="body">
Thank you,&lt;br/&gt;
Could you prepare and send us also the document for version 7.1 which will come soon?&lt;br/&gt;
Sincerely
</field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="parent_id" ref="message_video1"/>
<field name="author_id" ref="base.partner_root"/>
<field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
</record>
<!-- Network admin & employee conversation -->
<record id="message_video0" model="mail.message">
<field name="model">mail.group</field>
<field name="body">I changed the infrastructure of networks, if there are still changes to be made please do not hesitate to contact me.</field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
</record>
<record id="message_video0_message1" model="mail.message">
<field name="model">mail.group</field>
<field name="body">Thank you, the networks is perfect now ! Could you add a IP phone for Jhon ?</field>
<field name="parent_id" ref="message_video0"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_demo"/>
<field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
</record>
<record id="message_video0_message2" model="mail.message">
<field name="model">mail.group</field>
<field name="body">It's right, his internal phone number is 0093</field>
<field name="parent_id" ref="message_video0"/>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>
<field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
</record>
</data>
</openerp>
+4 -4
View File
@@ -61,10 +61,10 @@ class mail_notification(osv.Model):
_columns = {
'partner_id': fields.many2one('res.partner', string='Contact',
ondelete='cascade', required=True),
'read': fields.boolean('Read'),
ondelete='cascade', required=True, select=1),
'read': fields.boolean('Read', select=1),
'message_id': fields.many2one('mail.message', string='Message',
ondelete='cascade', required=True),
ondelete='cascade', required=True, select=1),
}
_defaults = {
@@ -102,7 +102,7 @@ class mail_notification(osv.Model):
# some messages do not have notifications: find which one, create notification, update read status
exist_notification = dict.fromkeys(msg_ids, False)
for notification in self.browse(cr, uid, notif_ids, context=context):
exist_notification[notification.message_id] = True
exist_notification[notification.message_id.id] = True
for msg_id in exist_notification.keys():
self.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context)
return self.write(cr, uid, notif_ids, {'read': read}, context=context)
+174 -140
View File
@@ -50,8 +50,9 @@ class mail_message(osv.Model):
_description = 'Message'
_inherit = ['ir.needaction_mixin']
_order = 'id desc'
_rec_name = 'record_name'
_message_read_limit = 10
_message_read_limit = 30
_message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'email_from',
'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids']
_message_record_name_length = 18
@@ -120,21 +121,26 @@ class mail_message(osv.Model):
"message, comment for other messages such as user replies"),
'email_from': fields.char('From',
help="Email address of the sender. This field is set when no matching partner is found for incoming emails."),
'author_id': fields.many2one('res.partner', 'Author',
'author_id': fields.many2one('res.partner', 'Author', select=1,
ondelete='set null',
help="Author of the message. If not set, email_from may hold an email address that did not match any partner."),
'partner_ids': fields.many2many('res.partner', string='Recipients'),
'notified_partner_ids': fields.many2many('res.partner', 'mail_notification',
'message_id', 'partner_id', 'Recipients'),
'message_id', 'partner_id', 'Notified partners',
help='Partners that have a notification pushing this message in their mailboxes'),
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel',
'message_id', 'attachment_id', 'Attachments'),
'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', help="Initial thread message."),
'parent_id': fields.many2one('mail.message', 'Parent Message', select=True,
ondelete='set null', help="Initial thread message."),
'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'),
'model': fields.char('Related Document Model', size=128, select=1),
'res_id': fields.integer('Related Document ID', select=1),
'record_name': fields.function(_get_record_name, type='char',
store=True, string='Message Record Name',
help="Name get of the related document."),
'notification_ids': fields.one2many('mail.notification', 'message_id', 'Notifications'),
'notification_ids': fields.one2many('mail.notification', 'message_id',
string='Notifications',
help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'),
'subject': fields.char('Subject'),
'date': fields.datetime('Date'),
'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1),
@@ -142,7 +148,8 @@ class mail_message(osv.Model):
'to_read': fields.function(_get_to_read, fnct_search=_search_to_read,
type='boolean', string='To read',
help='Functional field to search for messages the current user has to read'),
'subtype_id': fields.many2one('mail.message.subtype', 'Subtype'),
'subtype_id': fields.many2one('mail.message.subtype', 'Subtype',
ondelete='set null', select=1,),
'vote_user_ids': fields.many2many('res.users', 'mail_vote',
'message_id', 'user_id', string='Votes',
help='Users that voted for this message'),
@@ -200,67 +207,99 @@ class mail_message(osv.Model):
# Message loading for web interface
#------------------------------------------------------
def _message_get_dict(self, cr, uid, message, context=None):
""" Return a dict representation of the message. This representation is
used in the JS client code, to display the messages.
def _message_read_dict_postprocess(self, cr, uid, messages, message_tree, context=None):
""" Post-processing on values given by message_read. This method will
handle partners in batch to avoid doing numerous queries.
:param dict message: read result of a mail.message
:param list messages: list of message, as get_dict result
:param dict message_tree: {[msg.id]: msg browse record}
"""
# TDE note: this method should be optimized, to lessen the number of queries, will be done ASAP
is_author = False
if message['author_id']:
is_author = message['author_id'][0] == self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
author_id = message['author_id']
elif message['email_from']:
author_id = (0, message['email_from'])
res_partner_obj = self.pool.get('res.partner')
ir_attachment_obj = self.pool.get('ir.attachment')
pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
has_voted = False
if uid in message.get('vote_user_ids'):
has_voted = True
# 1. Aggregate partners (author_id and partner_ids) and attachments
partner_ids = set()
attachment_ids = set()
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.attachment_ids:
attachment_ids |= set([attachment.id for attachment in message.attachment_ids])
is_favorite = False
if uid in message.get('favorite_user_ids'):
is_favorite = True
# Filter author_ids uid can see
# partner_ids = self.pool.get('res.partner').search(cr, uid, [('id', 'in', partner_ids)], context=context)
partners = res_partner_obj.name_get(cr, uid, list(partner_ids), context=context)
partner_tree = dict((partner[0], partner) for partner in partners)
is_private = True
if message.get('model') and message.get('res_id'):
is_private = False
# 2. Attachments
attachments = ir_attachment_obj.read(cr, uid, list(attachment_ids), ['id', 'datas_fname'], context=context)
attachments_tree = dict((attachment['id'], {'id': attachment['id'], 'filename': attachment['datas_fname']}) for attachment in attachments)
try:
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, message['attachment_ids'], context=context)]
except (orm.except_orm, osv.except_osv):
attachment_ids = []
# TDE note: should we send partner_ids ?
# TDE note: shouldn't we separated followers and other partners ? costly to compute maybe ,
try:
partner_ids = self.pool.get('res.partner').name_get(cr, uid, message['partner_ids'], context=context)
except (orm.except_orm, osv.except_osv):
# 3. Update message dictionaries
for message_dict in messages:
message_id = message_dict.get('id')
message = message_tree[message_id]
if message.author_id:
author = partner_tree[message.author_id.id]
else:
author = (0, message.email_from)
partner_ids = []
for partner in message.partner_ids:
if partner.id in partner_tree:
partner_ids.append(partner_tree[partner.id])
attachment_ids = []
for attachment in message.attachment_ids:
if attachment.id in attachments_tree:
attachment_ids.append(attachments_tree[attachment.id])
message_dict.update({
'is_author': pid == author[0],
'author_id': author,
'partner_ids': partner_ids,
'attachment_ids': attachment_ids,
})
return True
return {
'id': message['id'],
'type': message['type'],
'attachment_ids': attachment_ids,
'body': message['body'],
'model': message['model'],
'res_id': message['res_id'],
'record_name': message['record_name'],
'subject': message['subject'],
'date': message['date'],
'author_id': author_id,
'is_author': is_author,
'partner_ids': partner_ids,
'parent_id': False,
'vote_nb': len(message['vote_user_ids']),
'has_voted': has_voted,
'is_private': is_private,
'is_favorite': is_favorite,
'to_read': message['to_read'],
}
def _message_read_dict(self, cr, uid, message, parent_id=False, context=None):
""" Return a dict representation of the message. This representation is
used in the JS client code, to display the messages. Partners and
attachments related stuff will be done in post-processing in batch.
def _message_read_add_expandables(self, cr, uid, message_list, read_messages,
thread_level=0, message_loaded_ids=[], domain=[], parent_id=False, context=None, limit=None):
:param dict message: mail.message browse record
"""
# private message: no model, no res_id
is_private = False
if not message.model or not message.res_id:
is_private = True
# votes and favorites: res.users ids, no prefetching should be done
vote_nb = len(message.vote_user_ids)
has_voted = uid in [user.id for user in message.vote_user_ids]
is_favorite = uid in [user.id for user in message.favorite_user_ids]
return {'id': message.id,
'type': message.type,
'body': message.body,
'model': message.model,
'res_id': message.res_id,
'record_name': message.record_name,
'subject': message.subject,
'date': message.date,
'to_read': message.to_read,
'parent_id': parent_id,
'is_private': is_private,
'author_id': False,
'is_author': False,
'partner_ids': [],
'vote_nb': vote_nb,
'has_voted': has_voted,
'is_favorite': is_favorite,
'attachment_ids': [],
}
def _message_read_add_expandables(self, cr, uid, messages, message_tree, parent_tree,
message_unload_ids=[], thread_level=0, domain=[], parent_id=False, context=None):
""" Create expandables for message_read, to load new messages.
1. get the expandable for new threads
if display is flat (thread_level == 0):
@@ -275,96 +314,82 @@ class mail_message(osv.Model):
for each hole in the child list based on message displayed,
create an expandable
:param list message_list:list of message structure for the Chatter
:param list messages: list of message structure for the Chatter
widget to which expandables are added
:param dict read_messages: dict [id]: read result of the messages to
easily have access to their values, given their ID
:param dict message_tree: dict [id]: browse record of this message
:param dict parent_tree: dict [parent_id]: [child_ids]
:param list message_unload_ids: list of message_ids we do not want
to load
:return bool: True
"""
def _get_expandable(domain, message_nb, parent_id, id, model):
def _get_expandable(domain, message_nb, parent_id, max_limit):
return {
'domain': domain,
'nb_messages': message_nb,
'type': 'expandable',
'parent_id': parent_id,
'id': id,
# TDE note: why do we need model sometimes, and sometimes not ???
'model': model,
'max_limit': max_limit,
}
# all_not_loaded_ids = []
id_list = sorted(read_messages.keys())
if not id_list:
return message_list
if not messages:
return True
message_ids = sorted(message_tree.keys())
# 1. get the expandable for new threads
if thread_level == 0:
exp_domain = domain + [('id', '<', min(message_loaded_ids + id_list))]
exp_domain = domain + [('id', '<', min(message_unload_ids + message_ids))]
else:
exp_domain = domain + ['!', ('id', 'child_of', message_loaded_ids + id_list)]
exp_domain = domain + ['!', ('id', 'child_of', message_unload_ids + parent_tree.keys())]
ids = self.search(cr, uid, exp_domain, context=context, limit=1)
if ids:
message_list.append(_get_expandable(exp_domain, -1, parent_id, -1, None))
# inside a thread: prepend
if parent_id:
messages.insert(0, _get_expandable(exp_domain, -1, parent_id, True))
# new threads: append
else:
messages.append(_get_expandable(exp_domain, -1, parent_id, True))
# 2. get the expandables for new messages inside threads if display is not flat
if thread_level == 0:
return True
for message_id in id_list:
message = read_messages[message_id]
for message_id in message_ids:
message = message_tree[message_id]
# message is not a thread header (has a parent_id)
# TDE note: parent_id is false is there is a parent we can not see -> ok
if message.get('parent_id'):
# generate only for thread header messages (TDE note: parent_id may be False is uid cannot see parent_id, seems ok)
if message.parent_id:
continue
# TDE note: check search is correctly implemented in mail.message
not_loaded_ids = self.search(cr, uid, [
('id', 'child_of', message['id']),
('id', 'not in', message_loaded_ids),
], context=context, limit=self._message_read_more_limit)
if not not_loaded_ids:
# check there are message for expandable
child_ids = set([child.id for child in message.child_ids]) - set(message_unload_ids)
child_ids = sorted(list(child_ids), reverse=True)
if not child_ids:
continue
# all_not_loaded_ids += not_loaded_ids
# group childs not read
id_min, id_max, nb = max(not_loaded_ids), 0, 0
for not_loaded_id in not_loaded_ids:
if not read_messages.get(not_loaded_id):
# make groups of unread messages
id_min, id_max, nb = max(child_ids), 0, 0
for child_id in child_ids:
if not child_id in message_ids:
nb += 1
if id_min > not_loaded_id:
id_min = not_loaded_id
if id_max < not_loaded_id:
id_max = not_loaded_id
if id_min > child_id:
id_min = child_id
if id_max < child_id:
id_max = child_id
elif nb > 0:
exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
message_list.append(_get_expandable(exp_domain, nb, message_id, id_min, message.get('model')))
id_min, id_max, nb = max(not_loaded_ids), 0, 0
messages.append(_get_expandable(exp_domain, nb, message_id, False))
id_min, id_max, nb = max(child_ids), 0, 0
else:
id_min, id_max, nb = max(not_loaded_ids), 0, 0
id_min, id_max, nb = max(child_ids), 0, 0
if nb > 0:
exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
message_list.append(_get_expandable(exp_domain, nb, message_id, id_min, message.get('model')))
# message_loaded_ids = list(set(message_loaded_ids + read_messages.keys() + all_not_loaded_ids))
idx = [msg.get('id') for msg in messages].index(message_id) + 1
# messages.append(_get_expandable(exp_domain, nb, message_id, id_min))
messages.insert(idx, _get_expandable(exp_domain, nb, message_id, False))
return True
def _get_parent(self, cr, uid, message, context=None):
""" Tools method that tries to get the parent of a mail.message. If
no parent, or if uid has no access right on the parent, False
is returned.
:param dict message: read result of a mail.message
"""
if not message['parent_id']:
return False
parent_id = message['parent_id'][0]
try:
return self.read(cr, uid, parent_id, self._message_read_fields, context=context)
except (orm.except_orm, osv.except_osv):
return False
def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None, thread_level=0, context=None, parent_id=False, limit=None):
def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None,
thread_level=0, context=None, parent_id=False, limit=None):
""" Read messages from mail.message, and get back a list of structured
messages to be displayed as discussion threads. If IDs is set,
fetch these records. Otherwise use the domain to fetch messages.
@@ -388,46 +413,56 @@ class mail_message(osv.Model):
ancestors and expandables
:return list: list of message structure for the Chatter widget
"""
# print 'message_read', ids, domain, message_unload_ids, thread_level, context, parent_id, limit
assert thread_level in [0, 1], 'message_read() thread_level should be 0 (flat) or 1 (1 level of thread); given %s.' % thread_level
domain = domain if domain is not None else []
message_unload_ids = message_unload_ids if message_unload_ids is not None else []
if message_unload_ids:
domain += [('id', 'not in', message_unload_ids)]
limit = limit or self._message_read_limit
read_messages = {}
message_tree = {}
message_list = []
parent_tree = {}
# no specific IDS given: fetch messages according to the domain, add their parents if uid has access to
if ids is None:
ids = self.search(cr, uid, domain, context=context, limit=limit)
for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
message_id = message['id']
# if not in tree and not in message_loaded list
if not message_id in read_messages and not message_id in message_unload_ids:
read_messages[message_id] = message
message_list.append(self._message_get_dict(cr, uid, message, context=context))
# fetch parent if threaded, sort messages
for message in self.browse(cr, uid, ids, context=context):
message_id = message.id
if message_id in message_tree:
continue
message_tree[message_id] = message
# get the older ancestor the user can read, update its ancestor field
if not thread_level:
message_list[-1]['parent_id'] = parent_id
continue
parent = self._get_parent(cr, uid, message, context=context)
while parent and parent.get('id') != parent_id:
message_list[-1]['parent_id'] = parent.get('id')
message = parent
parent = self._get_parent(cr, uid, message, context=context)
# if in thread: add its ancestor to the list of messages
if not message['id'] in read_messages and not message['id'] in message_unload_ids:
read_messages[message['id']] = message
message_list.append(self._message_get_dict(cr, uid, message, context=context))
# find parent_id
if thread_level == 0:
tree_parent_id = parent_id
else:
tree_parent_id = message_id
parent = message
while parent.parent_id and parent.parent_id.id != parent_id:
parent = parent.parent_id
tree_parent_id = parent.id
if not parent.id in message_tree:
message_tree[parent.id] = parent
# newest messages first
parent_tree.setdefault(tree_parent_id, [])
if tree_parent_id != message_id:
parent_tree[tree_parent_id].append(self._message_read_dict(cr, uid, message_tree[message_id], parent_id=tree_parent_id, context=context))
if thread_level:
for key, message_id_list in parent_tree.iteritems():
message_id_list.sort(key=lambda item: item['id'])
message_id_list.insert(0, self._message_read_dict(cr, uid, message_tree[key], context=context))
parent_list = parent_tree.items()
parent_list = sorted(parent_list, key=lambda item: max([msg.get('id') for msg in item[1]]) if item[1] else item[0], reverse=True)
message_list = [message for (key, msg_list) in parent_list for message in msg_list]
# get the child expandable messages for the tree
message_list = sorted(message_list, key=lambda k: k['id'])
self._message_read_add_expandables(cr, uid, message_list, read_messages, thread_level=thread_level,
message_loaded_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context, limit=limit)
self._message_read_dict_postprocess(cr, uid, message_list, message_tree, context=context)
self._message_read_add_expandables(cr, uid, message_list, message_tree, parent_tree,
thread_level=thread_level, message_unload_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context)
return message_list
# TDE Note: do we need this ?
@@ -461,7 +496,6 @@ class mail_message(osv.Model):
- otherwise: remove the id
"""
# Rules do not apply to administrator
# print '_search', uid, args
if uid == SUPERUSER_ID:
return super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order,
context=context, count=count, access_rights_uid=access_rights_uid)
+8 -4
View File
@@ -59,21 +59,26 @@
<field name="type"/>
<field name="author_id"/>
<field name="partner_ids"/>
<filter string="Read"
name="message_read" help="Show messages to read"
domain="[('to_read', '=', False)]"/>
<filter string="Unread"
name="message_unread" help="Show messages to read"
domain="[('to_read', '=', True)]"/>
<separator/>
<filter string="Comments"
name="comments" help="Comments"
domain="[('type', '=', 'comment')]"/>
<filter string="Has attachments"
name="attachments"
domain="[('attachment_ids', '!=', False)]"/>
<filter string="Notifications"
name="notifications" help="Notifications"
domain="[('type', '=', 'notification')]"/>
<filter string="Emails"
name="emails" help="Emails"
domain="[('type', '=', 'email')]"/>
<separator/>
<filter string="Has attachments"
name="attachments"
domain="[('attachment_ids', '!=', False)]"/>
</search>
</field>
</record>
@@ -84,7 +89,6 @@
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_message_search"/>
<field name="context">{'search_default_to_read_message':True}</field>
</record>
<!-- Add menu entry in Settings/Email -->
+10 -10
View File
@@ -74,17 +74,17 @@ class mail_thread(osv.AbstractModel):
- 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)
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
# search for unread messages, by reading directly mail.notification, as SUPERUSER
notif_obj = self.pool.get('mail.notification')
notif_ids = notif_obj.search(cr, SUPERUSER_ID, [
('partner_id.user_ids', 'in', [uid]),
('message_id.res_id', 'in', ids),
('message_id.model', '=', self._name),
('read', '=', False)
], context=context)
for notif in notif_obj.browse(cr, SUPERUSER_ID, notif_ids, context=context):
res[notif.message_id.res_id]['message_unread'] = True
# search for unread messages, directly in SQL to improve performances
cr.execute(""" SELECT m.res_id FROM mail_message m
RIGHT JOIN mail_notification n
ON (n.message_id = m.id AND n.partner_id = %s AND n.read = False)
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 ''
+94 -13
View File
@@ -4,42 +4,123 @@
<record id="action_mail_inbox_feeds" model="ir.actions.client">
<field name="name">Inbox</field>
<field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', True)],
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'inbox'} }&quot;"/>
<field name="res_model">mail.message</field>
<field name="context">{
'default_model': 'res.users',
'default_res_id': uid
}</field>
<field name="params" eval="&quot;{
'domain': [
('notification_ids.partner_id.user_ids', 'in', [uid]),
('to_read', '=', True),
('favorite_user_ids', 'not in', [uid])
],
'view_mailbox': True,
'view_inbox': True,
'read_action': 'read'
}&quot;"/>
<field name="help" type="html">
<p>
<b>Hurray! No message here!</b>
</p><p>
Congratulations! You have processed all your incoming emails.
Remember that when you receive new messages, you can either
set them as done, or use the star button to send those messages
into your <i>Todo</i> mailbox.
</p>
</field>
</record>
<record id="action_mail_to_me_feeds" model="ir.actions.client">
<field name="name">To: me</field>
<field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('partner_ids.user_ids', 'in', [uid]), ('to_read', '=', True), ('author_id.user_ids', 'in', [uid])],
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'inbox'} }&quot;"/>
<field name="res_model">mail.message</field>
<field name="context">{
'default_model': 'res.users',
'default_res_id': uid,
'search_default_message_unread': True
}</field>
<field name="params" eval="&quot;{
'domain': [('partner_ids.user_ids', 'in', [uid])],
'view_mailbox': True,
'read_action': 'read', }&quot;"/>
<field name="help" type="html">
<p>
No message here!
</p><p>
There are no new messages matching the current search
that were directly sent to you.
Remember you can always uncheck the Read filter to see
messages sent to you that you already read.
</p>
</field>
</record>
<record id="action_mail_star_feeds" model="ir.actions.client">
<field name="name">Favorites</field>
<field name="name">Todo</field>
<field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('favorite_user_ids.user_ids', 'in', [uid])],
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'stared'} }&quot;"/>
<field name="res_model">mail.message</field>
<field name="context">{
'default_model': 'res.users',
'default_res_id': uid,
'search_default_message_unread': True
}</field>
<field name="params" eval="&quot;{
'domain': [('favorite_user_ids', 'in', [uid])],
'view_mailbox': True,
'read_action': 'read', }&quot;"/>
<field name="help" type="html">
<p>
<b>Hurray! No message here!</b>
</p><p>
Congratulations! You have processed all your messages in
your todo list, or at least those matching your search.
Remember you can always search for read messages by
unchecking the Read filter.
</p>
</field>
</record>
<record id="action_mail_archives_feeds" model="ir.actions.client">
<field name="name">Archives</field>
<field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', False)],
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'archives'} }&quot;"/>
<field name="context">{
'default_model': 'res.users',
'default_res_id': uid,
'search_default_message_read': True
}</field>
<field name="params" eval="&quot;{
'domain': [('notification_ids.partner_id.user_ids', 'in', [uid])],
'view_mailbox': True,
'read_action': 'unread', }&quot;"/>
<field name="help" type="html">
<p>
There are no message for this search.
</p>
</field>
</record>
<record id="action_mail_sent_feeds" model="ir.actions.client">
<field name="name">Sent</field>
<field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('author_id.user_ids', 'in', [uid])],
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'send'} }&quot;"/>
<field name="context">{
'default_model': 'res.users',
'default_res_id': uid
}</field>
<field name="params" eval="&quot;{
'domain': [('author_id.user_ids', 'in', [uid])],
'view_mailbox': True, }&quot;"/>
<field name="help" type="html">
<p>
There are no message for this search.
</p>
</field>
</record>
<!-- MENU -->
<!-- Top menu item -->
<menuitem name="Emails"
<menuitem name="Messages"
id="mail.mail_feeds_main"
groups="base.group_user"
sequence="10"/>
@@ -61,7 +142,7 @@
<field name="parent_id" ref="mail.mail_feeds"/>
</record>
<record id="mail_starfeeds" model="ir.ui.menu">
<field name="name">Favorites</field>
<field name="name">Todo</field>
<field name="sequence" eval="14"/>
<field name="action" ref="action_mail_star_feeds"/>
<field name="parent_id" ref="mail.mail_feeds"/>
+216 -48
View File
@@ -1,7 +1,36 @@
/* ------------ TOPBAR MAIL BUTTON --------------- */
/* FIXME this css is not very pretty because it uses a
* 'button' element wich comes with a lot of inappropriate
* styling. Entypo is also a headache to center properly
* */
.openerp .oe_topbar_item.oe_topbar_compose_full_email{
padding: 0px;
width: 32px;
height: 32px;
}
.openerp .oe_topbar_item.oe_topbar_compose_full_email button{
position: relative;
top: -3px; /* centering entypo ... urgh */
box-sizing: border-box;
border: none;
box-shadow: none;
color: white;
background: none;
text-shadow: 0px 1px 2px black;
width: 32px;
height: 32px;
padding: 0px;
margin: 0px
border-radius: 0px;
}
/* ------------ MAIL WIDGET --------------- */
.openerp .oe_mail, .openerp .oe_mail *{
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.openerp .oe_mail {
display: block;
@@ -42,9 +71,10 @@
}
.openerp .oe_mail .oe_msg .oe_msg_footer{
padding-left: 4px;
padding-top: 3px;
overflow: hidden;
opacity:0.8;
-webkit-transition: opacity 0.2s linear;
margin-bottom: 4px;
font-size: 11px;
}
.openerp .oe_mail .oe_msg .oe_msg_content{
display: block;
@@ -80,18 +110,24 @@
.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_content{
padding-top:2px;
}
.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_footer{
margin-bottom: 0px;
}
/* b) Votes (likes) */
.openerp .oe_mail .oe_mail_vote_count{
display: inline;
position: relative;
background: #7C7BAD;
color: white;
background: white;
box-shadow: 0px 0px 0px 1px rgba(124, 123, 173, 0.36) inset;
color: #7c7bad;
text-shadow: none;
border-radius: 3px;
margin: 0px;
padding-left: 3px;
padding-right: 18px;
margin-right: 3px;
padding-right: 15px;
margin-right: 5px;
}
.openerp .oe_mail .oe_mail_vote_count .oe_e{
position: absolute;
@@ -102,12 +138,6 @@
/* c) Message action icons */
.openerp .oe_mail .oe_msg.oe_msg_unread .oe_unread{
display:none;
}
.openerp .oe_mail .oe_msg.oe_msg_read .oe_read{
display:none;
}
.openerp .oe_mail .oe_msg .oe_msg_icons{
float: right;
margin-top: 4px;
@@ -115,6 +145,9 @@
margin-left: 8px;
height: 24px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.openerp .oe_mail .oe_msg .oe_msg_icons span{
float:right;
@@ -128,10 +161,16 @@
color: #FFF;
text-shadow: 0px 1px #AAA,0px -1px #AAA, -1px 0px #AAA, 1px 0px #AAA, 0px 3px 3px rgba(0,0,0,0.1);
-webkit-transition: all 0.2s linear;
-moz-transition: all 0.2s linear;
-o-transition: all 0.2s linear;
transition: all 0.2s linear;
}
.openerp .oe_mail .oe_msg:hover .oe_msg_icons a{
opacity: 1;
-webkit-transition: all 0.1s linear;
-moz-transition: all 0.1s linear;
-o-transition: all 0.1s linear;
transition: all 0.1s linear;
}
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star:hover a{
color: #FFF6C0;
@@ -155,7 +194,7 @@
}
.openerp .oe_mail .oe_msg .oe_msg_content textarea{
width: 100%;
height: 32px;
height: 64px;
margin: 0px;
padding: 0px;
resize: vertical;
@@ -171,6 +210,150 @@
width: 100%;
}
/* --------------------- ATTACHMENTS --------------------- */
.openerp .oe_mail .oe_msg_attachment_list{
display: none;
margin-top: 12px;
margin-bottom: 12px;
}
.openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list{
display: block;
}
.openerp .oe_mail .oe_attachment{
display: inline-block;
width: 100px;
margin: 2px;
min-height: 80px;
position: relative;
border-radius: 3px;
text-align: center;
vertical-align: top;
}
.openerp .oe_mail .oe_attachment .oe_name{
display: inline-block;
max-width: 100%;
padding: 1px 3px;
margin-top: 50px;
margin-bottom: 5px;
background: rgba(124, 123, 173, 0.13);
overflow: hidden;
color: #4c4c4c;
text-shadow: none;
border-radius: 3px;
}
.openerp .oe_mail .oe_attachment.oe_preview{
background: url( data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAJ0lEQVQYV2MsLS39z4AGLCws0IUYGIeCwrVr12J45sSJE5ieGQIKAbuZKf/EMCs7AAAAAElFTkSuQmCC );
}
.openerp .oe_mail .oe_attachment .oe_progress_bar{
display: none;
position: absolute;
top: 18px;
left: 16px;
right: 16px;
height: 17px;
line-height: 13px;
padding: 0px;
background: #4BBD00;
color: white;
text-align: center;
border-radius: 3px;
border: solid 1px rgba(0,0,0,0.2);
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.34);
-webkit-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
-moz-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
-o-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
animation: oe_mail_attach_loading_anim 0.75s infinite linear;
}
.openerp .oe_mail .oe_attachment.oe_uploading .oe_progress_bar{
display: block;
}
@-webkit-keyframes oe_mail_attach_loading_anim{
0% { background: #4BBD00 }
50% { background: #009123 }
100% { background: #4BBD00 }
}
@-moz-keyframes oe_mail_attach_loading_anim{
0% { background: #4BBD00 }
50% { background: #009123 }
100% { background: #4BBD00 }
}
@-o-keyframes oe_mail_attach_loading_anim{
0% { background: #4BBD00 }
50% { background: #009123 }
100% { background: #4BBD00 }
}
@keyframes oe_mail_attach_loading_anim{
0% { background: #4BBD00 }
50% { background: #009123 }
100% { background: #4BBD00 }
}
.openerp .oe_mail .oe_attachment.oe_preview .oe_name{
position: absolute;
bottom: 0px;
margin: 0px;
left: 0px;
right: 0px;
max-height: 64px;
background: rgba(0,0,0,0.8);
color: white;
border-top-left-radius: 0px;
border-top-right-radius: 0px;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.openerp .oe_mail .oe_attachment.oe_preview:hover .oe_name{
opacity: 1;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.openerp .oe_mail .oe_attachment img{
position: absolute;
width: 48px;
height: 48px;
top: 0px;
left: 50%;
margin-left: -24px;
}
.openerp .oe_mail .oe_attachment.oe_preview img{
display: block;
position: relative;
margin:0px;
width: 100px;
height: 100px;
border-radius: 3px;
margin-left: -50px;
}
.openerp .oe_mail .oe_attachment .oe_delete{
display: none;
}
.openerp .oe_mail .oe_msg_composer .oe_attachment .oe_delete{
display: block;
position: absolute;
top: -7px;
right: 0px;
color: black;
text-shadow: 1px 0px white, -1px 0px white, 0px 1px white, 0px -1px white;
cursor: pointer;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.openerp .oe_mail .oe_msg_composer .oe_attachment:hover .oe_delete{
opacity: 1;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
/* ---------------- MESSAGE QUICK COMPOSER --------------- */
.openerp .oe_mail .oe_msg_composer .oe_msg_footer{
@@ -178,37 +361,6 @@
padding-top: 2px;
padding-bottom:6px;
}
.openerp .oe_mail .oe_msg_attachments.oe_hidden,
.openerp .oe_mail .oe_msg_images.oe_hidden{
margin:0px;
border: none;
display: none;
}
.openerp .oe_mail .oe_msg_attachments{
margin-bottom: 4px;
margin-right: 0px;
font-size: 12px;
border-radius: 2px;
border: solid 1px rgba(124,123,173,0.14);
}
.openerp .oe_mail .oe_msg_attachments .oe_attachment{
padding: 2px;
padding-left: 4px;
padding-right: 4px;
}
.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e{
font-size: 23px;
margin-top: -5px;
}
.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e:hover{
text-decoration: none;
}
.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(odd){
background:white;
}
.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(even){
background: #F4F5FA;
}
.openerp .oe_mail .oe_msg_images {
display: block;
}
@@ -269,6 +421,7 @@
.openerp .oe_mail .oe_msg_content.oe_msg_more_message{
text-align: right;
cursor: pointer;
}
.openerp .oe_mail .oe_msg_content.oe_msg_more_message .oe_separator{
height: 0;
@@ -280,7 +433,7 @@
}
.openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more {
background: white;
margin-right: 280px;
margin-right: 210px;
padding-left: 8px;
padding-right: 8px;
text-decoration: none;
@@ -298,6 +451,7 @@
padding-top: 5px;
width: 160px;
float: right;
margin-right: 16px;
}
/* a) THE FOLLOW BUTTON */
@@ -308,9 +462,22 @@
width:100%;
}
.openerp .oe_followers button.oe_follower.oe_following{
color: white;
background-color: #3465A4;
background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
background-image: -moz-linear-gradient(top, #729FCF, #3465A4);
background-image: -ms-linear-gradient(top, #729FCF, #3465A4);
background-image: -o-linear-gradient(top, #729FCF, #3465A4);
background-image: linear-gradient(to bottom, #729FCF, #3465A4);
}
.openerp .oe_followers button.oe_follower.oe_following:hover{
color: white;
background-color: #A21A1A;
background-image: -webkit-linear-gradient(top, #DF3F3F, #A21A1A);
background-image: -moz-linear-gradient(top, #DF3F3F, #A21A1A);
background-image: -ms-linear-gradient(top, #DF3F3F, #A21A1A);
background-image: -o-linear-gradient(top, #DF3F3F, #A21A1A);
background-image: linear-gradient(to bottom, #DF3F3F, #A21A1A);
}
.openerp .oe_followers button.oe_follower .oe_follow,
@@ -371,12 +538,13 @@
.openerp .oe_record_thread{
display: block;
margin-right: 180px;
margin-left: 16px;
margin-right: 212px;
}
/* ----------- INBOX INTEGRATION ----------- */
.openerp .oe_mail_wall .oe_mail{
margin: 16px;
width: 720px;
width: 600px;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because it is too large Load Diff
+15 -6
View File
@@ -30,6 +30,8 @@ openerp_mail_followers = function(session, mail) {
this.ds_model = new session.web.DataSetSearch(this, this.view.model);
this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
this.ds_users = new session.web.DataSetSearch(this, 'res.users');
this.value = [];
},
start: function() {
@@ -41,6 +43,11 @@ openerp_mail_followers = function(session, mail) {
this._super();
},
set_value: function(_value) {
this.value = _value;
this._super(_value);
},
_check_visibility: function() {
this.$el.toggle(this.view.get("actual_mode") !== "create");
},
@@ -86,13 +93,14 @@ openerp_mail_followers = function(session, mail) {
read_value: function () {
var self = this;
return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).then(function (results) {
self.set_value(results[0].message_follower_ids);
self.value = results[0].message_follower_ids;
self.render_value();
});
},
render_value: function () {
this.reinit();
return this.fetch_followers(this.get("value"));
return this.fetch_followers(this.value);
},
fetch_followers: function (value_) {
@@ -111,7 +119,7 @@ openerp_mail_followers = function(session, mail) {
event.preventDefault();
return this.ds_users.call('read', [this.session.uid, ['partner_id']]).then(function (results) {
var pid = results['partner_id'][0];
self.message_is_follower = (_.indexOf(self.get('value'), pid) != -1);
self.message_is_follower = (_.indexOf(self.value, pid) != -1);
}).then(self.proxy('display_generic'));
},
_format_followers: function(count){
@@ -131,7 +139,7 @@ openerp_mail_followers = function(session, mail) {
display_generic: function () {
var self = this;
var node_user_list = this.$('.oe_follower_list').empty();
this.$('.oe_follower_title').html(this._format_followers(this.get('value').length));
this.$('.oe_follower_title').html(this._format_followers(this.value.length));
},
/** Display the followers */
@@ -206,7 +214,8 @@ openerp_mail_followers = function(session, mail) {
$(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 this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context])
.then(this.proxy('read_value'));
},
do_update_subscription: function (event) {
@@ -220,7 +229,7 @@ openerp_mail_followers = function(session, mail) {
});
var context = new session.web.CompoundContext(this.build_context(), {});
return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist:undefined, context])
return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist : undefined, context])
.then(this.proxy('read_value'));
},
});
+49 -50
View File
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<!-- this template contains the mail widget and is used to namespace the css -->
<t t-name="mail.Widget">
<!--
mail.Widget template used to namespace the css -->
<t t-name="mail.Root">
<div class="oe_mail">
</div>
</t>
@@ -38,7 +39,8 @@
</div>
</div>
<div t-if="widget.show_compact_message and !widget.show_composer" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
<textarea class="field_text oe_compact" placeholder="Write a reply..."/>
<textarea t-if="!widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to the followers of this docmuent..."/>
<textarea t-if="widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to my followers..."/>
</div>
<span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span>
</t>
@@ -69,33 +71,28 @@
Template used to display attachments in a mail.message
-->
<t t-name="mail.thread.message.attachments">
<span class="oe_msg_attachments">
<t t-foreach="widget.attachment_ids" t-as="attachment" t-if="!attachment.is_image">
<div class="oe_attachment">
<span t-if="(attachment.upload and attachment.percent_loaded&lt;100)" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-attf-name="{attachment.name || attachment.filename}">
<div class="oe_upload_in_process">
<span>...Upload in progress...</span>
</div>
<t t-raw="attachment.name || attachment.filename"/>
</span>
<t t-if="(!attachment.upload or attachment.percent_loaded&gt;=100)">
<a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
<t t-raw="attachment.name || attachment.filename"/>
</a>
</t>
<t t-if="(widget.show_delete_attachment and (!attachment.upload or attachment.percent_loaded&gt;=100))">
<a class="oe_right oe_mail_attachment_delete oe_e" title="Delete this attachment" t-attf-data-id="{attachment.id}">[</a>
</t>
<t t-foreach='widget.attachment_ids' t-as='attachment'>
<t t-if="attachment.filetype !== 'webimage'">
<div t-attf-class="oe_attachment #{attachment.upload ? 'oe_uploading' : ''}">
<a t-att-href='attachment.url'><img t-att-src="'/mail/static/src/img/mimetypes/' + attachment.filetype + '.png'"></img></a>
<div class='oe_delete oe_e' title="Delete this attachment" t-attf-data-id="{attachment.id}">[</div>
<div class='oe_name'><t t-raw='attachment.name' /></div>
<div class='oe_progress_bar'>
uploading
</div>
</div>
</t>
</span>
<span class="oe_msg_images">
<t t-foreach="widget.attachment_ids" t-as="attachment" t-if="attachment.is_image">
<a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
<img t-if="attachment.is_image" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-att-src="attachment.url"/>
</a>
<t t-if="attachment.filetype === 'webimage'">
<div t-attf-class="oe_attachment oe_preview #{attachment.upload ? 'oe_uploading' : ''}">
<a t-att-href='attachment.url'><img t-att-src="widget.attachments_resize_image(attachment.id, [100,100])"></img></a>
<div class='oe_delete oe_e'>[</div>
<div class='oe_name'><t t-raw='attachment.name' /></div>
<div class='oe_progress_bar'>
uploading
</div>
</div>
</t>
</span>
</t>
</t>
<t t-name="mail.thread.message.private">
@@ -112,7 +109,7 @@
<t t-name="mail.thread.list_recipients">
<div class="oe_mail_list_recipients">
To:
<span t-if="!widget.is_private" class="oe_all_follower">Everyone</span>
<span t-if="!widget.is_private" class="oe_all_follower">Followers <t t-raw="' of ' + (widget.parent_thread.parent_message.record_name ? '&quot;' + widget.parent_thread.parent_message.record_name + '&quot;' : 'this document')"/></span>
<t t-if="!widget.is_private and widget.partner_ids.length"> and </t>
<t t-set="inc" t-value="0"/>
<t t-if="widget.partner_ids.length" t-foreach="widget.partner_ids" t-as="partner"><span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/><a t-attf-href="#model=res.partner&amp;id=#{partner[0]}"><t t-raw="partner[1]"/></a></span><t t-set="inc" t-value="inc+1"/>
@@ -140,22 +137,20 @@
<td colspan="2">
<h2 class="oe_view_title">
<span class="oe_view_title_text">
News Feed
<t t-if="widget.options.res_model"> / </t>
<t t-if="widget.options.res_model" t-esc="widget.options.res_model"/>
Email box
</span>
</h2>
</td>
<td><div class="oe_view_manager_view_search" t-opentag="true"/></td>
</tr>
<tr class="oe_header_row">
<tr class="oe_header_row" t-if="widget.action.params.view_inbox" >
<td colspan="2">
<button type="button" class="oe_write_full oe_highlight">
Compose a new message
</button>
<span class='oe_alternative'>
or
<a href='#' class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write to your followers</a>
<a href='#' class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write to my followers</a>
</span>
</td>
</tr>
@@ -187,7 +182,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} oe_msg_#{widget.to_read?'unread':'read'}">
<div t-attf-class="oe_msg #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_#{widget.type}">
<div class='oe_msg_left'>
<a t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}" t-att-title="widget.author_id[1]">
@@ -197,10 +192,11 @@
<div class="oe_msg_center">
<div class='oe_msg_icons'>
<span class='oe_read' t-if="widget.show_read_unread_button"><a title="Mark as read" class="oe_e">X</a></span>
<span class='oe_unread' t-if="widget.show_read_unread_button"><a title="Mark as unread" class="oe_e">v</a></span>
<span class='oe_reply' t-if="widget.show_reply_button"><a title="Reply" class="oe_e">(</a></span>
<span t-attf-class="oe_star #{widget.is_favorite?'oe_starred':''}"><a title="Add To Favorites" class="oe_e">7</a></span>
<span class='oe_read' t-if="widget.options.show_read"><a title="Done" class="oe_e">X</a></span>
<span class='oe_unread' t-if="widget.is_favorite and widget.options.show_unread"><a title="Set back to Todo" class="oe_e">v</a></span>
<span class='oe_unread' t-if="!widget.is_favorite and widget.options.show_unread"><a title="Set back to Inbox" class="oe_e">v</a></span>
<span class='oe_reply' t-if="widget.options.show_reply_button"><a title="Reply" class="oe_e">(</a></span>
<span t-attf-class="oe_star #{widget.is_favorite?'oe_starred':''}"><a title="Mark as Todo" class="oe_e">7</a></span>
</div>
<!-- message itself -->
<div class="oe_msg_content">
@@ -208,23 +204,26 @@
<t t-raw="widget.subject"/>
</h1>
<div class="oe_msg_body">
<t t-if="widget.options.show_record_name and widget.record_name and (!widget.subject) and !widget.options.thread_level and !widget.options.display_on_thread[0] and widget.model!='res.partner'">
<t t-if="widget.show_record_name">
<a class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&amp;id=#{widget.res_id}"><t t-raw="widget.record_name"/></a>
</t>
<t t-raw="widget.body"/>
</div>
<t t-if="widget.attachment_ids.length > 0">
<t t-call="mail.thread.message.attachments"/>
</t>
</div>
<div class="oe_msg_footer">
<t t-if="widget.attachment_ids.length > 0">
<div class="oe_msg_attachment_list"></div>
</t>
<a t-if="widget.author_id" t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}"><t t-raw="widget.author_id[1]"/></a>
<span class='oe_subtle'>•</span>
<span t-att-title="widget.date"><t t-raw="widget.timerelative"/></span>
<a t-if="widget.attachment_ids.length > 0" class="oe_mail_msg_view_attachments">
<t t-if='widget.attachment_ids.length > 0'>
<span class='oe_subtle'>•</span>
<a class="oe_view_attachments">
<t t-if="widget.attachment_ids.length == 1">1 Attachment</t>
<t t-if="widget.attachment_ids.length > 1"><t t-raw="widget.attachment_ids.length"/> Attachments</t>
</a>
</t>
<span class='oe_subtle'>•</span>
<t t-call="mail.thread.message.vote"/>
</div>
@@ -234,11 +233,12 @@
<!-- expandable message layout -->
<t t-name="mail.thread.expandable">
<div t-attf-class="oe_msg oe_msg_#{widget.type} #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_unread">
<div t-attf-class="oe_msg oe_msg_#{widget.type} #{widget.max_limit ? 'oe_max_limit' : ''} #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
<div class="oe_msg_content oe_msg_more_message">
<div class='oe_separator'></div>
<a t-if="widget.nb_messages &lt;= 0" class="oe_msg_fetch_more">show more message</a>
<a t-if="widget.nb_messages === 1" class="oe_msg_fetch_more">show one more message</a>
<a t-if="widget.nb_messages !== 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a>
<a t-if="widget.nb_messages &gt; 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a>
</div>
</div>
</t>
@@ -247,9 +247,9 @@
mail.compose_message.button_top_bar
render of the button on the user bar for open wizard compose message
-->
<t t-name="mail.compose_message.button_top_bar">
<div class="oe_topbar_compose_full_email">
<!-- <button class="oe_button oe_highlight">Write an email</button> -->
<t t-name="mail.ComposeMessageTopButton">
<div title='Compose new Message' class="oe_topbar_item oe_topbar_compose_full_email">
<button class="oe_e">%</button>
</div>
</t>
@@ -257,12 +257,11 @@
Template used to display Like/Unlike in a mail.message
-->
<span t-name="mail.thread.message.vote">
<span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
<t t-raw='widget.vote_nb' />
<span class='oe_e'>8</span>
</span>
<a href='#' t-attf-class="oe_msg_vote">
<a href='#' class="oe_msg_vote">
<t t-if="!widget.has_voted">like</t>
<t t-if="widget.has_voted">unlike</t>
</a>
+25 -13
View File
@@ -576,7 +576,7 @@ class test_mail(TestMailMockups):
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
pigs_domain = [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)]
# Data: create a discussion in Pigs (2 messages, one with 2 and one with 3 answers)
# Data: create a discussion in Pigs (3 threads, with respectively 0, 4 and 4 answers)
msg_id0 = self.group_pigs.message_post(body='0', subtype='mt_comment')
msg_id1 = self.group_pigs.message_post(body='1', subtype='mt_comment')
msg_id2 = self.group_pigs.message_post(body='2', subtype='mt_comment')
@@ -588,7 +588,8 @@ class test_mail(TestMailMockups):
msg_id8 = self.group_pigs.message_post(body='2-1-1', subtype='mt_comment', parent_id=msg_id4)
msg_id9 = self.group_pigs.message_post(body='1-1-1', subtype='mt_comment', parent_id=msg_id3)
msg_id10 = self.group_pigs.message_post(body='2-1-1', subtype='mt_comment', parent_id=msg_id4)
msg_ids = [msg_id0, msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8, msg_id9, msg_id10]
msg_ids = [msg_id10, msg_id9, msg_id8, msg_id7, msg_id6, msg_id5, msg_id4, msg_id3, msg_id2, msg_id1, msg_id0]
ordered_msg_ids = [msg_id2, msg_id4, msg_id6, msg_id8, msg_id10, msg_id1, msg_id3, msg_id5, msg_id7, msg_id9, msg_id0]
# Test: read some specific ids
read_msg_list = self.mail_message.message_read(cr, uid, ids=msg_ids[2:4], domain=[('body', 'like', 'dummy')])
@@ -601,7 +602,8 @@ class test_mail(TestMailMockups):
self.assertEqual(msg_ids, read_msg_ids, 'message_read flat with domain on Pigs should equal all messages of Pigs')
read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=200, thread_level=1)
read_msg_ids = [msg.get('id') for msg in read_msg_list]
self.assertEqual(msg_ids, read_msg_ids, 'message_read threaded with domain on Pigs should equal all messages of Pigs')
self.assertEqual(ordered_msg_ids, read_msg_ids,
'message_read threaded with domain on Pigs should equal all messages of Pigs, and sort them with newer thread first, last message last in thread')
# ----------------------------------------
# CASE1: message_read with domain, threaded
@@ -611,6 +613,8 @@ class test_mail(TestMailMockups):
# Do: read last message, threaded
read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=1, thread_level=1)
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
# TDE TODO: test expandables order
type_list = map(lambda item: item.get('type'), read_msg_list)
# Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
self.assertEqual(len(read_msg_list), 4, 'message_read on last Pigs message should return 2 messages and 2 expandables')
self.assertEqual(set([msg_id2, msg_id10]), set(read_msg_ids), 'message_read on the last Pigs message should also get its parent')
@@ -618,7 +622,7 @@ class test_mail(TestMailMockups):
# Data: get expandables
new_threads_exp, new_msg_exp = None, None
for msg in read_msg_list:
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
new_threads_exp = msg
elif msg.get('type') == 'expandable':
new_msg_exp = msg
@@ -630,12 +634,20 @@ class test_mail(TestMailMockups):
self.assertIn(('id', 'child_of', msg_id2), domain, 'new messages expandable domain should contain a child_of condition')
self.assertIn(('id', '>=', msg_id4), domain, 'new messages expandable domain should contain an id greater than condition')
self.assertIn(('id', '<=', msg_id8), domain, 'new messages expandable domain should contain an id less than condition')
self.assertEqual(new_msg_exp.get('parent_id'), msg_id2, 'new messages expandable should have ancestor_id set to the thread header')
# Do: message_read with domain, thread_level=0, parent_id=msg_id2 (should be imposed by JS)
read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=200, thread_level=0, parent_id=msg_id2)
self.assertEqual(new_msg_exp.get('parent_id'), msg_id2, 'new messages expandable should have parent_id set to the thread header')
# Do: message_read with domain, thread_level=0, parent_id=msg_id2 (should be imposed by JS), 2 messages
read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=2, thread_level=0, parent_id=msg_id2)
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
# Test: other message in thread have been fetch
self.assertEqual(set([msg_id4, msg_id6, msg_id8]), set(read_msg_ids), 'message_read in Pigs thread should return all the previous messages')
new_msg_exp = [msg for msg in read_msg_list if msg.get('type') == 'expandable'][0]
# Test: structure content, 2 messages and 1 thread expandable
self.assertEqual(len(read_msg_list), 3, 'message_read in Pigs thread should return 2 messages and 1 expandables')
self.assertEqual(set([msg_id6, msg_id8]), set(read_msg_ids), 'message_read in Pigs thread should return 2 more previous messages in thread')
# Do: read the last message
read_msg_list = self.mail_message.message_read(cr, uid, domain=new_msg_exp.get('domain'), limit=2, thread_level=0, parent_id=msg_id2)
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
# Test: structure content, 1 message
self.assertEqual(len(read_msg_list), 1, 'message_read in Pigs thread should return 1 message')
self.assertEqual(set([msg_id4]), set(read_msg_ids), 'message_read in Pigs thread should return the last message in thread')
# Do: fetch a new thread, domain from expandable
self.assertIsNotNone(new_threads_exp, 'message_read on last Pigs message should have returned a new threads expandable')
@@ -643,7 +655,7 @@ class test_mail(TestMailMockups):
# Test: expandable, conditions in domain
for condition in pigs_domain:
self.assertIn(condition, domain, 'new threads expandable domain should contain the message_read domain parameter')
self.assertFalse(new_threads_exp.get('parent_id'), 'new threads expandable should not have an ancestor_id')
self.assertFalse(new_threads_exp.get('parent_id'), 'new threads expandable should not have an parent_id')
# Do: message_read with domain, thread_level=1 (should be imposed by JS)
read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=1, thread_level=1)
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
@@ -654,7 +666,7 @@ class test_mail(TestMailMockups):
# Data: get expandables
new_threads_exp, new_msg_exp = None, None
for msg in read_msg_list:
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
new_threads_exp = msg
elif msg.get('type') == 'expandable':
new_msg_exp = msg
@@ -701,7 +713,7 @@ class test_mail(TestMailMockups):
# Data: get expandables
new_threads_exp, new_msg_exp = None, None
for msg in read_msg_list:
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
new_threads_exp = msg
# Do: fetch new messages, domain from expandable
@@ -715,7 +727,7 @@ class test_mail(TestMailMockups):
read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
# Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
self.assertEqual(len(read_msg_list), 9, 'message_read on Pigs should return 9 messages and 0 expandable')
self.assertEqual([msg_id0, msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8], read_msg_ids,
self.assertEqual([msg_id8, msg_id7, msg_id6, msg_id5, msg_id4, msg_id3, msg_id2, msg_id1, msg_id0], read_msg_ids,
'message_read, More on flat, should return all remaning messages')
def test_40_needaction(self):
+7 -1
View File
@@ -135,7 +135,12 @@ class mail_compose_message(osv.TransientModel):
related to.
:param int res_id: id of the document record this mail is related to
"""
return {'model': model, 'res_id': res_id}
doc_name_get = self.pool.get(model).name_get(cr, uid, res_id, context=context)
if doc_name_get:
record_name = doc_name_get[0][1]
else:
record_name = False
return {'model': model, 'res_id': res_id, 'record_name': record_name}
def get_message_data(self, cr, uid, message_id, context=None):
""" Returns a defaults-like dict with initial values for the composition
@@ -161,6 +166,7 @@ class mail_compose_message(osv.TransientModel):
# update the result
result = {
'record_name': message_data.record_name,
'model': message_data.model,
'res_id': message_data.res_id,
'parent_id': message_data.id,
@@ -14,10 +14,19 @@
<field name="parent_id" invisible="1"/>
<field name="content_subtype" invisible="1"/>
<!-- visible wizard -->
<field name="partner_ids" widget="many2many_tags_email" placeholder="Add contacts to notify..."
<label for="partner_ids" string="Recipients"/>
<div>
<span attrs="{'invisible':[('model', '=', False)]}">
Followers of
<field name="record_name" readonly="1" class="oe_inline"
attrs="{'invisible':[('model', '=', False)]}"/>
and
</span>
<field name="partner_ids" widget="many2many_tags_email" placeholder="Add contacts to notify..."
context="{'force_email':True}" required="1"/>
</div>
<field name="subject" placeholder="Subject..."
attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
</group>
<field name="body"/>
<field name="attachment_ids" widget="one2many_binary" blockui="0"/>
+1 -1
View File
@@ -120,7 +120,7 @@
<field name="memo" widget="html" class="oe_memo" editor_height="450px" />
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field class="oe_chatter" name="message_ids" widget="mail_thread"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>