[MERGE] Merged OpenChatter part 5.2: Mailbox Gangnam Style, Pooh Strikes Back
Improvements - added Favorites, by adding the 'starred' mechanism. Starred messages are messages the user wants to keep in a specific mailbox, independantly of notifications and follow mechanism - refactored the mailboxes, now having Inbox; To:me; Archives; Sent; Favorites - refactored expandables - overall refactoring of mail widget - fixed message_read method Next steps : - CSS/design, client-side code - more tests, maybe some doc, could be interesting ... bzr revid: tde@openerp.com-20121019125158-k1rlrkhxw9q51t53
This commit is contained in:
commit
583f26d691
|
@ -27,6 +27,7 @@ import mail_mail
|
|||
import mail_thread
|
||||
import mail_group
|
||||
import mail_vote
|
||||
import mail_favorite
|
||||
import res_partner
|
||||
import res_users
|
||||
import report
|
||||
|
@ -36,4 +37,3 @@ import mail_group_menu
|
|||
import update
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
'description': """
|
||||
Business oriented Social Networking
|
||||
===================================
|
||||
The Social Networking module provides a unified social network abstraction layer allowing applications to display a complete
|
||||
The Social Networking module provides a unified social network abstraction layer allowing applications to display a complete
|
||||
communication history on documents with a fully-integrated email and message management system.
|
||||
|
||||
It enables the users to read and send messages as well as emails. It also provides a feeds page combined to a subscription mechanism that allows to follow documents and to be constantly updated about recent news.
|
||||
|
@ -54,6 +54,7 @@ Main Features
|
|||
'mail_message_view.xml',
|
||||
'mail_mail_view.xml',
|
||||
'mail_followers_view.xml',
|
||||
'mail_favorite_view.xml',
|
||||
'mail_thread_view.xml',
|
||||
'mail_group_view.xml',
|
||||
'res_partner_view.xml',
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>).
|
||||
#
|
||||
# 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/>
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import osv, fields
|
||||
|
||||
|
||||
class mail_favorite(osv.Model):
|
||||
''' Favorite model: relationship table between messages and users. A favorite
|
||||
message is a message the user wants to see in a specific 'Favorite'
|
||||
mailbox, like a starred mechanism. '''
|
||||
|
||||
_name = 'mail.favorite'
|
||||
_description = 'Favorite messages'
|
||||
_columns = {
|
||||
'message_id': fields.many2one('mail.message', 'Message', select=1,
|
||||
ondelete='cascade', required=True),
|
||||
'user_id': fields.many2one('res.users', 'User', select=1,
|
||||
ondelete='cascade', required=True),
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- FOLLOWERS !-->
|
||||
<record model="ir.ui.view" id="view_mail_favorite_tree">
|
||||
<field name="name">mail.favorite.tree</field>
|
||||
<field name="model">mail.favorite</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Favorites">
|
||||
<field name="user_id"/>
|
||||
<field name="message_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_favorite_form">
|
||||
<field name="name">mail.favorite.form</field>
|
||||
<field name="model">mail.favorite</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Favorite Form" version="7.0">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="message_id"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_view_favorites" model="ir.actions.act_window">
|
||||
<field name="name">Favorites</field>
|
||||
<field name="res_model">mail.favorite</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- Add favorites related menu entries in Settings/Email -->
|
||||
<menuitem name="Favorites" id="menu_email_favorites" parent="base.menu_email"
|
||||
action="action_view_favorites" sequence="40" groups="base.group_no_one"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -20,12 +20,10 @@
|
|||
##############################################################################
|
||||
|
||||
import logging
|
||||
import openerp
|
||||
import tools
|
||||
|
||||
from email.header import decode_header
|
||||
from openerp import SUPERUSER_ID
|
||||
from operator import itemgetter
|
||||
from osv import osv, orm, fields
|
||||
from tools.translate import _
|
||||
|
||||
|
@ -48,7 +46,10 @@ class mail_message(osv.Model):
|
|||
_order = 'id desc'
|
||||
|
||||
_message_read_limit = 10
|
||||
_message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read',
|
||||
'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids']
|
||||
_message_record_name_length = 18
|
||||
_message_read_more_limit = 1024
|
||||
|
||||
def _shorten_name(self, name):
|
||||
if len(name) <= (self._message_record_name_length + 3):
|
||||
|
@ -56,7 +57,9 @@ class mail_message(osv.Model):
|
|||
return name[:self._message_record_name_length] + '...'
|
||||
|
||||
def _get_record_name(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Return the related document name, using get_name. """
|
||||
""" Return the related document name, using name_get. It is included in
|
||||
a try/except statement, because if uid cannot read the related
|
||||
document, he should see a void string instead of crashing. """
|
||||
result = dict.fromkeys(ids, False)
|
||||
for message in self.read(cr, uid, ids, ['model', 'res_id'], context=context):
|
||||
if not message['model'] or not message['res_id']:
|
||||
|
@ -69,7 +72,7 @@ class mail_message(osv.Model):
|
|||
|
||||
def _get_to_read(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Compute if the message is unread by the current user. """
|
||||
res = dict((id, {'to_read': False}) for id in ids)
|
||||
res = dict((id, False) for id in ids)
|
||||
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
notif_obj = self.pool.get('mail.notification')
|
||||
notif_ids = notif_obj.search(cr, uid, [
|
||||
|
@ -78,16 +81,16 @@ class mail_message(osv.Model):
|
|||
('read', '=', False)
|
||||
], context=context)
|
||||
for notif in notif_obj.browse(cr, uid, notif_ids, context=context):
|
||||
res[notif.message_id.id]['to_read'] = True
|
||||
res[notif.message_id.id] = not notif.read
|
||||
return res
|
||||
|
||||
def _search_to_read(self, cr, uid, obj, name, domain, context=None):
|
||||
""" Search for messages to read by the current user. Condition is
|
||||
inversed because we search unread message on a read column. """
|
||||
if domain[0][2]:
|
||||
read_cond = '(read = false or read is null)'
|
||||
read_cond = "(read = False OR read IS NULL)"
|
||||
else:
|
||||
read_cond = 'read = true'
|
||||
read_cond = "read = True"
|
||||
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
cr.execute("SELECT message_id FROM mail_notification "\
|
||||
"WHERE partner_id = %%s AND %s" % read_cond,
|
||||
|
@ -132,8 +135,12 @@ class mail_message(osv.Model):
|
|||
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'),
|
||||
'vote_user_ids': fields.many2many('res.users', 'mail_vote', 'message_id', 'user_id', string='Votes',
|
||||
'vote_user_ids': fields.many2many('res.users', 'mail_vote',
|
||||
'message_id', 'user_id', string='Votes',
|
||||
help='Users that voted for this message'),
|
||||
'favorite_user_ids': fields.many2many('res.users', 'mail_favorite',
|
||||
'message_id', 'user_id', string='Favorite',
|
||||
help='Users that set this message in their favorites'),
|
||||
}
|
||||
|
||||
def _needaction_domain_get(self, cr, uid, context=None):
|
||||
|
@ -142,8 +149,7 @@ class mail_message(osv.Model):
|
|||
return []
|
||||
|
||||
def _get_default_author(self, cr, uid, context=None):
|
||||
# remove context to avoid possible hack in browse with superadmin using context keys that could trigger a specific behavior
|
||||
return self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=None).partner_id.id
|
||||
return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||
|
||||
_defaults = {
|
||||
'type': 'email',
|
||||
|
@ -156,33 +162,49 @@ class mail_message(osv.Model):
|
|||
# Vote/Like
|
||||
#------------------------------------------------------
|
||||
|
||||
def vote_toggle(self, cr, uid, ids, user_ids=None, context=None):
|
||||
''' Toggles voting. Done as SUPERUSER_ID because of write access on
|
||||
mail.message not always granted. '''
|
||||
if not user_ids:
|
||||
user_ids = [uid]
|
||||
def vote_toggle(self, cr, uid, ids, context=None):
|
||||
''' Toggles vote. Performed using read to avoid access rights issues.
|
||||
Done as SUPERUSER_ID because uid may vote for a message he cannot modify. '''
|
||||
for message in self.read(cr, uid, ids, ['vote_user_ids'], context=context):
|
||||
for user_id in user_ids:
|
||||
has_voted = user_id in message.get('vote_user_ids')
|
||||
if not has_voted:
|
||||
self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(4, user_id)]}, context=context)
|
||||
else:
|
||||
self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(3, user_id)]}, context=context)
|
||||
return not(has_voted) or False
|
||||
new_has_voted = not (uid in message.get('vote_user_ids'))
|
||||
if new_has_voted:
|
||||
self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(4, uid)]}, context=context)
|
||||
else:
|
||||
self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(3, uid)]}, context=context)
|
||||
return new_has_voted or False
|
||||
|
||||
#------------------------------------------------------
|
||||
# Favorite
|
||||
#------------------------------------------------------
|
||||
|
||||
def favorite_toggle(self, cr, uid, ids, context=None):
|
||||
''' Toggles favorite. Performed using read to avoid access rights issues.
|
||||
Done as SUPERUSER_ID because uid may star a message he cannot modify. '''
|
||||
for message in self.read(cr, uid, ids, ['favorite_user_ids'], context=context):
|
||||
new_is_favorite = not (uid in message.get('favorite_user_ids'))
|
||||
if new_is_favorite:
|
||||
self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(4, uid)]}, context=context)
|
||||
else:
|
||||
self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(3, uid)]}, context=context)
|
||||
return new_is_favorite or False
|
||||
|
||||
#------------------------------------------------------
|
||||
# Message loading for web interface
|
||||
#------------------------------------------------------
|
||||
|
||||
def _message_get_dict(self, cr, uid, message, context=None):
|
||||
""" Return a dict representation of the message.
|
||||
""" Return a dict representation of the message. This representation is
|
||||
used in the JS client code, to display the messages.
|
||||
|
||||
:param dict message: read result of a mail.message
|
||||
"""
|
||||
has_voted = False
|
||||
if uid in message['vote_user_ids']:
|
||||
has_voted = True
|
||||
else:
|
||||
has_voted = False
|
||||
|
||||
is_favorite = False
|
||||
if uid in message['favorite_user_ids']:
|
||||
is_favorite = True
|
||||
|
||||
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)]
|
||||
|
@ -206,35 +228,48 @@ class mail_message(osv.Model):
|
|||
'date': message['date'],
|
||||
'author_id': message['author_id'],
|
||||
'is_author': message['author_id'] and message['author_id'][0] == uid,
|
||||
# TDE note: is this useful ? to check
|
||||
'partner_ids': partner_ids,
|
||||
'parent_id': message['parent_id'] and message['parent_id'][0] or False,
|
||||
# TDE note: see with CHM about votes, how they are displayed (only number, or name_get ?)
|
||||
# 'vote_user_ids': vote_ids,
|
||||
# vote: should only use number of votes
|
||||
'vote_nb': len(message['vote_user_ids']),
|
||||
'has_voted': has_voted,
|
||||
'is_private': message['model'] and message['res_id'],
|
||||
'is_favorite': is_favorite,
|
||||
'to_read': message['to_read'],
|
||||
}
|
||||
|
||||
def _message_read_expandable(self, cr, uid, tree, result, message_loaded, domain, context, parent_id, limit):
|
||||
def _message_read_expandable(self, cr, uid, message_list, read_messages,
|
||||
message_loaded_ids=[], domain=[], context=None, parent_id=False, limit=None):
|
||||
""" Create the expandable message for all parent message read
|
||||
this function is used by message_read
|
||||
TDE note: add default values for args, add some comments
|
||||
|
||||
:param dict tree: tree of message ids
|
||||
:param list message_list: list of messages given by message_read to
|
||||
which we have to add expandables
|
||||
:param dict read_messages: dict [id]: read result of the messages to
|
||||
easily have access to their values, given their ID
|
||||
"""
|
||||
# sort for group items / TDE: move to message_read
|
||||
# result = sorted(result, key=lambda k: k['id'])
|
||||
tree_not = []
|
||||
# expandable for not show message
|
||||
for msg_id in tree:
|
||||
# get all childs
|
||||
not_loaded_ids = self.search(cr, SUPERUSER_ID, [
|
||||
('parent_id', '=', msg_id),
|
||||
('id', 'not in', message_loaded)
|
||||
], context=context, limit=1000)
|
||||
id_list = sorted(read_messages.keys())
|
||||
for message_id in id_list:
|
||||
message = read_messages[message_id]
|
||||
|
||||
# TDE note: check search is correctly implemented in mail.message
|
||||
not_loaded_ids = self.search(cr, uid, [
|
||||
('parent_id', '=', message['id']),
|
||||
('id', 'not in', message_loaded_ids),
|
||||
], context=context, limit=self._message_read_more_limit)
|
||||
# group childs not read
|
||||
id_min = None
|
||||
id_max = None
|
||||
nb = 0
|
||||
|
||||
for not_loaded_id in not_loaded_ids:
|
||||
if not_loaded_id not in tree:
|
||||
if not read_messages.get(not_loaded_id):
|
||||
nb += 1
|
||||
if id_min == None or id_min > not_loaded_id:
|
||||
id_min = not_loaded_id
|
||||
|
@ -243,42 +278,43 @@ class mail_message(osv.Model):
|
|||
tree_not.append(not_loaded_id)
|
||||
else:
|
||||
if nb > 0:
|
||||
result.append({
|
||||
'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', msg_id)],
|
||||
message_list.append({
|
||||
'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', message_id)],
|
||||
'nb_messages': nb,
|
||||
'type': 'expandable',
|
||||
'parent_id': msg_id,
|
||||
'parent_id': message_id,
|
||||
'id': id_min,
|
||||
'model': message['model']
|
||||
})
|
||||
id_min = None
|
||||
id_max = None
|
||||
nb = 0
|
||||
if nb > 0:
|
||||
result.append({
|
||||
'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', msg_id)],
|
||||
message_list.append({
|
||||
'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', message_id)],
|
||||
'nb_messages': nb,
|
||||
'type': 'expandable',
|
||||
'parent_id': msg_id,
|
||||
'id': id_min
|
||||
'parent_id': message_id,
|
||||
'id': id_min,
|
||||
'model': message['model'],
|
||||
})
|
||||
|
||||
for msg_id in read_messages.keys() + tree_not:
|
||||
message_loaded_ids.append(msg_id)
|
||||
|
||||
# expandable for limit max
|
||||
ids = self.search(cr, SUPERUSER_ID, domain + [('id', 'not in', message_loaded + tree + tree_not)], context=context, limit=1)
|
||||
ids = self.search(cr, uid, domain + [('id', 'not in', message_loaded_ids)], context=context, limit=1)
|
||||
if len(ids) > 0:
|
||||
result.append({
|
||||
message_list.append({
|
||||
'domain': domain,
|
||||
'nb_messages': 0,
|
||||
'type': 'expandable',
|
||||
'parent_id': parent_id,
|
||||
'id': -1
|
||||
'id': -1,
|
||||
'max_limit': True,
|
||||
})
|
||||
|
||||
result = sorted(result, key=lambda k: k['id'])
|
||||
|
||||
return result
|
||||
|
||||
_message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read',
|
||||
'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name']
|
||||
return message_list
|
||||
|
||||
def _get_parent(self, cr, uid, message, context=None):
|
||||
""" Tools method that tries to get the parent of a mail.message. If
|
||||
|
@ -295,11 +331,11 @@ class mail_message(osv.Model):
|
|||
except (orm.except_orm, osv.except_osv):
|
||||
return False
|
||||
|
||||
def message_read(self, cr, uid, ids=False, domain=[], context=None, parent_id=False, limit=None):
|
||||
def message_read(self, cr, uid, ids=False, domain=[], message_loaded_ids=[], context=None, parent_id=False, limit=None):
|
||||
""" Read messages from mail.message, and get back a structured tree
|
||||
of messages to be displayed as discussion threads. If IDs is set,
|
||||
fetch these records. Otherwise use the domain to fetch messages.
|
||||
After having fetch messages, their parents will be added to obtain
|
||||
After having fetch messages, their parents & child will be added to obtain
|
||||
well formed threads.
|
||||
|
||||
TDE note: update this comment after final method implementation
|
||||
|
@ -310,60 +346,51 @@ class mail_message(osv.Model):
|
|||
further parents
|
||||
:return list: list of trees of messages
|
||||
"""
|
||||
# don't read the message display by .js, in context message_loaded list
|
||||
# TDE note: use an argument, do not use context
|
||||
if context is None:
|
||||
context = {}
|
||||
if context.get('message_loaded'):
|
||||
domain += [('id', 'not in', context.get('message_loaded'))]
|
||||
if message_loaded_ids:
|
||||
domain += [('id', 'not in', message_loaded_ids)]
|
||||
limit = limit or self._message_read_limit
|
||||
id_tree = []
|
||||
read_messages = {}
|
||||
message_list = []
|
||||
record = None
|
||||
|
||||
# select ids
|
||||
# TDE note: should not receive [None] -> investigate
|
||||
if ids and ids != [None]:
|
||||
# specific IDs given: fetch those ids and return directly the message list
|
||||
if ids:
|
||||
for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
|
||||
message_list.append(self._message_get_dict(cr, uid, message, context=context))
|
||||
message_list = sorted(message_list, key=lambda k: k['id'])
|
||||
return message_list
|
||||
|
||||
# key: ID, value: record
|
||||
ids = self.search(cr, SUPERUSER_ID, domain, context=context, limit=limit)
|
||||
|
||||
# TDE FIXME: check access rights on search are implemented for mail.message
|
||||
# fetch messages according to the domain, add their parents if uid has access to
|
||||
ids = self.search(cr, uid, domain, context=context, limit=limit)
|
||||
for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
|
||||
# if not in record and not in message_loded list
|
||||
if message['id'] not in id_tree and message['id'] not in context.get('message_loaded', []):
|
||||
record = self._message_get_dict(cr, uid, message, context=context)
|
||||
id_tree.append(message['id'])
|
||||
message_list.append(record)
|
||||
# if not in tree and not in message_loded list
|
||||
if not read_messages.get(message.get('id')) and message.get('id') not in message_loaded_ids:
|
||||
read_messages[message.get('id')] = message
|
||||
message_list.append(self._message_get_dict(cr, uid, message, context=context))
|
||||
|
||||
parent = self._get_parent(cr, uid, message, context=context)
|
||||
while parent and parent['id'] != parent_id:
|
||||
if parent['id'] not in id_tree:
|
||||
message = parent
|
||||
id_tree.append(message['id'])
|
||||
# if not in record and not in message_loded list
|
||||
if message['id'] not in context.get('message_loaded', []):
|
||||
record = self._message_get_dict(cr, uid, message, context=context)
|
||||
message_list.append(record)
|
||||
parent = self._get_parent(cr, uid, parent, context=context)
|
||||
# get all parented message if the user have the access
|
||||
parent = self._get_parent(cr, uid, message, context=context)
|
||||
while parent and parent.get('id') != parent_id:
|
||||
if not read_messages.get(parent.get('id')) and parent.get('id') not in message_loaded_ids:
|
||||
read_messages[parent.get('id')] = parent
|
||||
message_list.append(self._message_get_dict(cr, uid, parent, context=context))
|
||||
parent = self._get_parent(cr, uid, parent, context=context)
|
||||
|
||||
# get the child expandable messages for the tree
|
||||
message_list = sorted(message_list, key=lambda k: k['id'])
|
||||
message_list = self._message_read_expandable(cr, uid, message_list, read_messages,
|
||||
message_loaded_ids=message_loaded_ids, domain=domain, context=context, parent_id=parent_id, limit=limit)
|
||||
|
||||
message_list = self._message_read_expandable(cr, uid, id_tree, message_list, context.get('message_loaded', []), domain, context, parent_id, limit)
|
||||
|
||||
# message_list = sorted(message_list, key=lambda k: k['id'])
|
||||
return message_list
|
||||
|
||||
# TDE Note: do we need this ?
|
||||
# def user_free_attachment(self, cr, uid, context=None):
|
||||
# attachment_list = []
|
||||
|
||||
# attachment = self.pool.get('ir.attachment')
|
||||
# attachment_ids = attachment.search(cr, uid, [('res_model','=',''),('create_uid','=',uid)])
|
||||
# attachment_list = []
|
||||
# attachment_ids = attachment.search(cr, uid, [('res_model', '=', 'mail.message'), ('create_uid', '=', uid)])
|
||||
# if len(attachment_ids):
|
||||
# attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)]
|
||||
|
||||
# return attachment_list
|
||||
|
||||
#------------------------------------------------------
|
||||
|
@ -384,7 +411,8 @@ class mail_message(osv.Model):
|
|||
- Otherwise: raise
|
||||
- create: if
|
||||
- I am in the document message_follower_ids OR
|
||||
- I can write on the related document if res_model, res_id
|
||||
- I can write on the related document if res_model, res_id OR
|
||||
- I create a private message (no model, no res_id)
|
||||
- Otherwise: raise
|
||||
- write: if
|
||||
- I can write on the related document if res_model, res_id
|
||||
|
@ -422,6 +450,10 @@ class mail_message(osv.Model):
|
|||
if operation == 'read':
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
if message.get('author_id') and message.get('author_id') == partner_id]
|
||||
# Create: Check messages you create that are private messages -> ir.rule ?
|
||||
elif operation == 'create':
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
if not message.get('model') and not message.get('res_id')]
|
||||
else:
|
||||
author_ids = []
|
||||
|
||||
|
@ -480,13 +512,14 @@ class mail_message(osv.Model):
|
|||
def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
|
||||
""" Override to explicitely call check_access_rule, that is not called
|
||||
by the ORM. It instead directly fetches ir.rules and apply them. """
|
||||
res = super(mail_message, self).read(cr, uid, ids, fields=fields, context=context, load=load)
|
||||
self.check_access_rule(cr, uid, ids, 'read', context=context)
|
||||
res = super(mail_message, self).read(cr, uid, ids, fields=fields, context=context, load=load)
|
||||
return res
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
# cascade-delete attachments that are directly attached to the message (should only happen
|
||||
# for mail.messages that act as parent for a standalone mail.mail record).
|
||||
self.check_access_rule(cr, uid, ids, 'unlink', context=context)
|
||||
attachments_to_delete = []
|
||||
for message in self.browse(cr, uid, ids, context=context):
|
||||
for attach in message.attachment_ids:
|
||||
|
@ -514,7 +547,6 @@ class mail_message(osv.Model):
|
|||
fol_objs = fol_obj.browse(cr, uid, fol_ids, context=context)
|
||||
extra_notified = set(fol.partner_id.id for fol in fol_objs)
|
||||
missing_notified = extra_notified - partners_to_notify
|
||||
missing_notified = missing_notified
|
||||
if missing_notified:
|
||||
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(4, p_id) for p_id in missing_notified]}, context=context)
|
||||
|
||||
|
@ -523,7 +555,7 @@ class mail_message(osv.Model):
|
|||
Call mail_notification.notify to manage the email sending
|
||||
"""
|
||||
message = self.browse(cr, uid, newid, context=context)
|
||||
if message and message.model and message.res_id:
|
||||
if message.model and message.res_id:
|
||||
self._notify_followers(cr, uid, newid, message, context=context)
|
||||
|
||||
# add myself if I wrote on my wall, otherwise remove myself author
|
||||
|
|
|
@ -56,8 +56,9 @@
|
|||
<field name="subject" string="Content" filter_domain="['|', ('subject', 'ilike', self), ('body', 'ilike', self)]" />
|
||||
<field name="type"/>
|
||||
<field name="author_id"/>
|
||||
<field name="partner_ids"/>
|
||||
<filter string="Unread"
|
||||
name="messages_unread" help="Show messages to read"
|
||||
name="message_unread" help="Show messages to read"
|
||||
domain="[('to_read', '=', True)]"/>
|
||||
<filter string="Comments"
|
||||
name="comments" help="Comments"
|
||||
|
@ -84,25 +85,5 @@
|
|||
<!-- Add menu entry in Settings/Email -->
|
||||
<menuitem name="Messages" id="menu_mail_message" parent="base.menu_email" action="action_view_mail_message"/>
|
||||
|
||||
<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=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', True)],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid} }""/>
|
||||
</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=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', False)],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid} }""/>
|
||||
</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=""{'domain': [('author_id.user_ids', 'in', [uid])],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid} }""/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
</openerp>
|
|
@ -703,12 +703,12 @@ class mail_thread(osv.AbstractModel):
|
|||
if attachments:
|
||||
ir_attachment = self.pool.get('ir.attachment')
|
||||
mail_message = self.pool.get('mail.message')
|
||||
attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [('res_model', '=', False), ('res_id', '=', False), ('user_id', '=', uid), ('id', 'in', attachments)], context=context)
|
||||
attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [('res_model', '=', 'mail.message'), ('res_id', '=', 0), ('create_uid', '=', uid), ('id', 'in', attachments)], context=context)
|
||||
if attachment_ids:
|
||||
ir_attachment.write(cr, SUPERUSER_ID, attachment_ids, {'res_model': self._name, 'res_id': thread_id}, context=context)
|
||||
mail_message.write(cr, SUPERUSER_ID, [new_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]}, context=context)
|
||||
new_message = self.pool.get('mail.message').message_read(cr, uid, [new_message_id], context=context)
|
||||
|
||||
new_message = self.pool.get('mail.message').message_read(cr, uid, [new_message_id], context=context)
|
||||
return new_message
|
||||
|
||||
#------------------------------------------------------
|
||||
|
@ -716,7 +716,7 @@ class mail_thread(osv.AbstractModel):
|
|||
#------------------------------------------------------
|
||||
|
||||
def message_get_subscription_data(self, cr, uid, ids, context=None):
|
||||
""" Wrapper to get subtypes. """
|
||||
""" Wrapper to get subtypes data. """
|
||||
return self._get_subscription_data(cr, uid, ids, None, None, context=context)
|
||||
|
||||
def message_subscribe_users(self, cr, uid, ids, user_ids=None, subtype_ids=None, context=None):
|
||||
|
|
|
@ -1,35 +1,82 @@
|
|||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<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=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', True)],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'inbox'} }""/>
|
||||
</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=""{'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'} }""/>
|
||||
</record>
|
||||
|
||||
<record id="action_mail_star_feeds" model="ir.actions.client">
|
||||
<field name="name">Favorites</field>
|
||||
<field name="tag">mail.wall</field>
|
||||
<field name="params" eval=""{'domain': [('favorite_user_ids.user_ids', 'in', [uid])],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'stared'} }""/>
|
||||
</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=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', False)],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'archives'} }""/>
|
||||
</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=""{'domain': [('author_id.user_ids', 'in', [uid])],
|
||||
'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'send'} }""/>
|
||||
</record>
|
||||
|
||||
<!-- MENU -->
|
||||
|
||||
<!-- Top menu item -->
|
||||
<menuitem name="Home"
|
||||
id="mail_feeds_main"
|
||||
<menuitem name="Mails"
|
||||
id="mail.mail_feeds_main"
|
||||
groups="base.group_user"
|
||||
sequence="10"/>
|
||||
|
||||
<!-- Left-side menu: Feeds -->
|
||||
<menuitem id="mail_feeds" name="Feeds" parent="mail.mail_feeds_main" groups="base.group_user" sequence="10"/>
|
||||
<menuitem id="mail.mail_feeds" name="Feeds & Mailbox" parent="mail.mail_feeds_main" groups="base.group_user" sequence="10"/>
|
||||
<menuitem id="mail_my_stuff" name="Organizer" parent="mail.mail_feeds_main"/>
|
||||
|
||||
<record id="mail_inboxfeeds" model="ir.ui.menu">
|
||||
<field name="name">Inbox</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="action" ref="action_mail_inbox_feeds"/>
|
||||
<field name="parent_id" ref="mail_feeds"/>
|
||||
<field name="parent_id" ref="mail.mail_feeds"/>
|
||||
</record>
|
||||
<record id="mail_tomefeeds" model="ir.ui.menu">
|
||||
<field name="name">To: me</field>
|
||||
<field name="sequence" eval="11"/>
|
||||
<field name="action" ref="action_mail_to_me_feeds"/>
|
||||
<field name="parent_id" ref="mail.mail_feeds"/>
|
||||
</record>
|
||||
<record id="mail_starfeeds" model="ir.ui.menu">
|
||||
<field name="name">Favorites</field>
|
||||
<field name="sequence" eval="14"/>
|
||||
<field name="action" ref="action_mail_star_feeds"/>
|
||||
<field name="parent_id" ref="mail.mail_feeds"/>
|
||||
</record>
|
||||
<record id="mail_archivesfeeds" model="ir.ui.menu">
|
||||
<field name="name">Archives</field>
|
||||
<field name="sequence" eval="12"/>
|
||||
<field name="sequence" eval="16"/>
|
||||
<field name="action" ref="action_mail_archives_feeds"/>
|
||||
<field name="parent_id" ref="mail_feeds"/>
|
||||
<field name="parent_id" ref="mail.mail_feeds"/>
|
||||
</record>
|
||||
<record id="mail_sentfeeds" model="ir.ui.menu">
|
||||
<field name="name">Sent</field>
|
||||
<field name="sequence" eval="13"/>
|
||||
<field name="sequence" eval="18"/>
|
||||
<field name="action" ref="action_mail_sent_feeds"/>
|
||||
<field name="parent_id" ref="mail_feeds"/>
|
||||
<field name="parent_id" ref="mail.mail_feeds"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</openerp>
|
|
@ -126,6 +126,19 @@ class res_users(osv.Model):
|
|||
return self.pool.get('res.partner').message_post_api(cr, uid, partner_id, body=body, subject=subject,
|
||||
type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, **kwargs)
|
||||
|
||||
def message_post(self, cr, uid, thread_id, context=None, **kwargs):
|
||||
""" Redirect the posting of message on res.users to the related partner.
|
||||
This is done because when giving the context of Chatter on the
|
||||
various mailboxes, we do not have access to the current partner_id.
|
||||
We therefore post on the user and redirect on its partner. """
|
||||
assert thread_id, "res.users does not support posting global messages"
|
||||
if context and 'thread_model' in context:
|
||||
context['thread_model'] = 'res.partner'
|
||||
if isinstance(thread_id, (list, tuple)):
|
||||
thread_id = thread_id[0]
|
||||
partner_id = self.pool.get('res.users').read(cr, uid, thread_id, ['partner_id'], context=context)['partner_id'][0]
|
||||
return self.pool.get('res.partner').message_post(cr, uid, partner_id, context=context, **kwargs)
|
||||
|
||||
def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
|
||||
partner_id = self.pool.get('res.users').browse(cr, uid, ids)[0].partner_id.id
|
||||
return self.pool.get('res.partner').message_update(cr, uid, [partner_id], msg_dict,
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</record>
|
||||
|
||||
<record id="mail_followers_read_own" model="ir.rule">
|
||||
<field name="name">mail.followers: read its own entries</field>
|
||||
<field name="name">mail.followers: read and write its own entries</field>
|
||||
<field name="model_id" ref="model_mail_followers"/>
|
||||
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
|
||||
<field name="perm_create" eval="False"/>
|
||||
|
|
|
@ -54,13 +54,13 @@
|
|||
/* Specific display of threads in the wall */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea.oe_mail_compose_textarea {
|
||||
.openerp ul.oe_mail_wall_threads .oe_msg_content textarea.oe_mail_compose_textarea {
|
||||
width: 434px;
|
||||
height: 30px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.openerp li.oe_mail_wall_thread:first .oe_mail_msg_notification {
|
||||
.openerp li.oe_mail_wall_thread:first .oe_msg_notification {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@
|
|||
height: 28px;
|
||||
}
|
||||
|
||||
.openerp div.oe_thread_placeholder div.oe_mail_msg_content {
|
||||
.openerp div.oe_thread_placeholder div.oe_msg_content {
|
||||
width: 440px;
|
||||
}
|
||||
|
||||
|
@ -181,8 +181,8 @@
|
|||
}
|
||||
|
||||
/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */
|
||||
.openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea:focus,
|
||||
.openerp .oe_mail_msg_content div.oe_mail_compose_message_body textarea:focus {
|
||||
.openerp .oe_msg_content textarea.oe_mail_compose_textarea:focus,
|
||||
.openerp .oe_msg_content div.oe_mail_compose_message_body textarea:focus {
|
||||
outline: 0;
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
|
@ -191,10 +191,17 @@
|
|||
}
|
||||
|
||||
.openerp .oe_mail_vote_count,
|
||||
.openerp .oe_mail_msg_vote{
|
||||
.openerp .oe_msg_vote{
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.openerp button.oe_mail_starbox{
|
||||
background: #ff0000;
|
||||
}
|
||||
.openerp button.oe_mail_starbox.oe_stared{
|
||||
background: #00FF00;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_thread_display {
|
||||
white-space: normal;
|
||||
}
|
||||
|
@ -247,35 +254,35 @@
|
|||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg_notification,
|
||||
.openerp .oe_mail_msg_expandable,
|
||||
.openerp .oe_mail_msg_comment,
|
||||
.openerp .oe_mail_msg_email {
|
||||
.openerp .oe_msg_notification,
|
||||
.openerp .oe_msg_expandable,
|
||||
.openerp .oe_msg_comment,
|
||||
.openerp .oe_msg_email {
|
||||
padding: 8px;
|
||||
background: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg_notification:after,
|
||||
.openerp .oe_mail_msg_comment:after,
|
||||
.openerp .oe_mail_msg_email:after {
|
||||
.openerp .oe_msg_notification:after,
|
||||
.openerp .oe_msg_comment:after,
|
||||
.openerp .oe_msg_email:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_msg_content {
|
||||
.openerp div.oe_msg_content {
|
||||
float: left;
|
||||
position: relative;
|
||||
width: 486px;
|
||||
}
|
||||
|
||||
.openerp div.oe_mail_msg_content > li {
|
||||
.openerp div.oe_msg_content > li {
|
||||
float: left;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg_content:after {
|
||||
.openerp .oe_msg_content:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
|
@ -319,23 +326,23 @@
|
|||
/* Messages layout
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_mail_msg .oe_mail_msg_title {
|
||||
.openerp .oe_mail_msg .oe_msg_title {
|
||||
margin: 0;
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.openerp .oe_mail_msg .oe_mail_msg_title a:link,
|
||||
.openerp .oe_mail_msg .oe_mail_msg_title a:visited {
|
||||
.openerp .oe_mail_msg .oe_msg_title a:link,
|
||||
.openerp .oe_mail_msg .oe_msg_title a:visited {
|
||||
color: #4C4C4C;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg .oe_mail_msg_body {
|
||||
.openerp .oe_mail_msg .oe_msg_body {
|
||||
margin-bottom: .5em;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg .oe_mail_msg_body pre {
|
||||
.openerp .oe_mail_msg .oe_msg_body pre {
|
||||
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
|
||||
margin: 0px;
|
||||
white-space: pre-wrap;
|
||||
|
@ -373,31 +380,31 @@
|
|||
}
|
||||
|
||||
/* Message footer */
|
||||
.openerp .oe_mail_msg .oe_mail_msg_footer {
|
||||
.openerp .oe_mail_msg .oe_msg_footer {
|
||||
color: #888;
|
||||
}
|
||||
.openerp .oe_mail_msg .oe_mail_msg_footer li {
|
||||
.openerp .oe_mail_msg .oe_msg_footer li {
|
||||
float: left;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.openerp .oe_mail_msg .oe_mail_msg_footer li:after {
|
||||
.openerp .oe_mail_msg .oe_msg_footer li:after {
|
||||
content: " · ";
|
||||
}
|
||||
.openerp .oe_mail_msg .oe_mail_msg_footer li:last-child:after {
|
||||
.openerp .oe_mail_msg .oe_msg_footer li:last-child:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Attachments list */
|
||||
.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments {
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments {
|
||||
width: 100%;
|
||||
margin: .5em 0 0 0;
|
||||
padding: .5em 0;
|
||||
list-style-position: inside;
|
||||
}
|
||||
.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments.oe_hidden {
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments.oe_hidden {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments li {
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments li {
|
||||
float: none;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
|
@ -405,20 +412,28 @@
|
|||
padding: 0;
|
||||
list-style-type: square;
|
||||
}
|
||||
.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments .oe_upload_in_process {
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process {
|
||||
float: right;
|
||||
width: 200px;
|
||||
height: 16px;
|
||||
}
|
||||
.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments .oe_upload_in_process div {
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process div {
|
||||
float: left;
|
||||
width: 38px;
|
||||
height: 16px;
|
||||
margin-right: 2px;
|
||||
background: #66FF66;
|
||||
}
|
||||
.openerp .oe_mail_msg_content ul.oe_mail_msg_attachments .oe_upload_in_process span {
|
||||
.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process span {
|
||||
color: #aaaaaa;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Topbar button
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
.openerp .oe_topbar .oe_topbar_compose_full_email {
|
||||
float: right;
|
||||
margin: 3px 25px 0 0;
|
||||
}
|
|
@ -2,11 +2,11 @@
|
|||
/* Compose Message */
|
||||
/* ------------------------------ */
|
||||
|
||||
.openerp .oe_mail_msg_content .oe_mail_compose_message_footer {
|
||||
.openerp .oe_msg_content .oe_mail_compose_message_footer {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg_content .oe_mail_compose_message_footer button.oe_mail_compose_message_button_send {
|
||||
.openerp .oe_msg_content .oe_mail_compose_message_footer button.oe_mail_compose_message_button_send {
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@
|
|||
font-size: 30px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail .oe_mail_msg_attachments input {
|
||||
.openerp .oe_mail .oe_msg_attachments input {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
@ -128,39 +128,39 @@
|
|||
}
|
||||
|
||||
/* form_view: delete white background */
|
||||
.openerp .oe_mail_msg_content div.oe_formview {
|
||||
.openerp .oe_msg_content div.oe_formview {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg_content div.oe_form_nosheet {
|
||||
.openerp .oe_msg_content div.oe_form_nosheet {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg_content table.oe_form_group {
|
||||
.openerp .oe_msg_content table.oe_form_group {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg_content table.oe_form_field,
|
||||
.openerp .oe_mail_msg_content div.oe_form_field {
|
||||
.openerp .oe_msg_content table.oe_form_field,
|
||||
.openerp .oe_msg_content div.oe_form_field {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.openerp .oe_mail_msg_content td.oe_form_group_cell {
|
||||
.openerp .oe_msg_content td.oe_form_group_cell {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
/* subject: change width */
|
||||
.openerp .oe_mail_msg_content .oe_form .oe_form_field input[type='text'] {
|
||||
.openerp .oe_msg_content .oe_form .oe_form_field input[type='text'] {
|
||||
width: 472px;
|
||||
}
|
||||
|
||||
/* body_html: cleditor */
|
||||
.openerp .oe_mail_msg_content div.cleditorMain {
|
||||
.openerp .oe_msg_content div.cleditorMain {
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
/* destination_partner_ids */
|
||||
.openerp .oe_mail_msg_content div.text-core {
|
||||
.openerp .oe_msg_content div.text-core {
|
||||
height: 22px !important;
|
||||
width: 472px;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -39,6 +39,7 @@ openerp_mail_followers = function(session, mail) {
|
|||
this._check_visibility();
|
||||
this.reinit();
|
||||
this.bind_events();
|
||||
this._super();
|
||||
},
|
||||
|
||||
_check_visibility: function() {
|
||||
|
@ -72,7 +73,7 @@ openerp_mail_followers = function(session, mail) {
|
|||
target: 'new',
|
||||
context: {
|
||||
'default_res_model': self.view.dataset.model,
|
||||
'default_res_id': self.view.datarecord.id
|
||||
'default_res_id': self.view.datarecord.id,
|
||||
},
|
||||
}
|
||||
self.do_action(action, {
|
||||
|
@ -90,12 +91,6 @@ openerp_mail_followers = function(session, mail) {
|
|||
});
|
||||
},
|
||||
|
||||
set_value: function (value_) {
|
||||
this._super(value_);
|
||||
// TDE FIXME: render_value is never called... ask to niv
|
||||
this.render_value();
|
||||
},
|
||||
|
||||
render_value: function () {
|
||||
this.reinit();
|
||||
return this.fetch_followers(this.get("value"));
|
||||
|
@ -187,7 +182,7 @@ openerp_mail_followers = function(session, mail) {
|
|||
display_subtypes:function (data) {
|
||||
var self = this;
|
||||
var subtype_list_ul = this.$('.oe_subtypes');
|
||||
var records = (data[this.view.datarecord.id] || data[null]).message_subtype_data;
|
||||
var records = data[this.view.datarecord.id].message_subtype_data;
|
||||
|
||||
_(records).each(function (record, record_name) {
|
||||
record.name = record_name;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<t t-name="mail.compose_message">
|
||||
<div class="oe_mail_compose_textarea">
|
||||
<img class="oe_mail_icon oe_mail_frame oe_left" alt="User img"/>
|
||||
<div class="oe_mail_msg_content">
|
||||
<div class="oe_msg_content">
|
||||
<!-- contains the composition form -->
|
||||
<!-- default content: old basic textarea -->
|
||||
<div class="oe_mail_post_header">
|
||||
|
@ -55,17 +55,12 @@
|
|||
Template used to display attachments in a mail.message
|
||||
-->
|
||||
<t t-name="mail.thread.message.attachments">
|
||||
<ul t-attf-class="oe_mail_msg_attachments #{widget.attachment_ids[0] and widget.options.thread.show_attachment_link?'':'oe_hidden'}">
|
||||
<t t-foreach="widget.attachment_ids" t-as="attachment">
|
||||
<ul t-attf-class="oe_msg_attachments #{widget.datasets.attachment_ids[0] and widget.options.thread.show_attachment_link?'':'oe_hidden'}">
|
||||
<t t-foreach="widget.datasets.attachment_ids" t-as="attachment">
|
||||
<li>
|
||||
<span t-if="(attachment.upload or attachment.percent_loaded<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-attf-style="{attachment.percent_loaded>0?'':'display:none;'}"/>
|
||||
<div t-attf-style="{attachment.percent_loaded>20?'':'display:none;'}"/>
|
||||
<div t-attf-style="{attachment.percent_loaded>40?'':'display:none;'}"/>
|
||||
<div t-attf-style="{attachment.percent_loaded>60?'':'display:none;'}"/>
|
||||
<div t-attf-style="{attachment.percent_loaded>80?'':'display:none;'}"/>
|
||||
<span>...Upload in progress...</span>
|
||||
</div>
|
||||
<t t-raw="attachment.name || attachment.filename"/>
|
||||
</span>
|
||||
|
@ -94,14 +89,14 @@
|
|||
-->
|
||||
<t t-name="mail.thread.list_recipients">
|
||||
<div class="oe_mail_list_recipients">
|
||||
Post to:
|
||||
<span t-if="widget.context.default_res_id and widget.context.default_res_id" class="oe_all_follower">All Followers</span>
|
||||
<t t-if="!widget.context.default_res_id and widget.context.default_res_id and widget.partner_ids.length"> and </t>
|
||||
Post to:
|
||||
<span t-if="!widget.datasets.is_private" class="oe_all_follower">All Followers</span>
|
||||
<t t-if="!widget.datasets.is_private and widget.datasets.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&id=#{partner[0]}"><t t-raw="partner[1]"/></a></span><t t-set="inc" t-value="inc+1"/>
|
||||
<t t-if="widget.datasets.partner_ids.length" t-foreach="widget.datasets.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&id=#{partner[0]}"><t t-raw="partner[1]"/></a></span><t t-set="inc" t-value="inc+1"/>
|
||||
</t>
|
||||
<t t-if="widget.partner_ids.length>=3">
|
||||
<span class="oe_more">, <a><t t-raw="widget.partner_ids.length-3"/> others...</a></span>
|
||||
<t t-if="widget.datasets.partner_ids.length>=3">
|
||||
<span class="oe_more">, <a><t t-raw="widget.datasets.partner_ids.length-3"/> others...</a></span>
|
||||
<a class="oe_more_hidden"><<<</a>
|
||||
</t>
|
||||
</div>
|
||||
|
@ -111,8 +106,7 @@
|
|||
wall main template
|
||||
Template used to display the communication history in the wall.
|
||||
-->
|
||||
<div t-name="mail.wall" class="oe_view_manager oe_mail_wall oe_view_manag
|
||||
er_current">
|
||||
<div t-name="mail.wall" class="oe_view_manager oe_mail_wall oe_view_manager_current">
|
||||
<table class="oe_view_manager_header">
|
||||
<colgroup>
|
||||
<col width="33%"/>
|
||||
|
@ -186,59 +180,56 @@
|
|||
</div>
|
||||
|
||||
<!-- default layout -->
|
||||
<li t-name="mail.thread.message" t-attf-class="oe_mail oe_mail_thread_msg #{widget.to_read ?'oe_mail_unread':'oe_mail_read'}">
|
||||
<div t-attf-class="oe_mail_msg_#{widget.type} oe_semantic_html_override">
|
||||
<li t-name="mail.thread.message" t-attf-class="oe_mail oe_mail_thread_msg #{widget.datasets.to_read ?'oe_mail_unread':'oe_mail_read'}">
|
||||
<div t-attf-class="oe_msg_#{widget.datasets.type} oe_semantic_html_override">
|
||||
<!-- message actions (read/unread, reply, delete...) -->
|
||||
<ul class="oe_header">
|
||||
<li class="placeholder-mail-vote"><t t-call="mail.thread.message.vote"/></li>
|
||||
<li t-if="!widget.options.thread.display_on_flat" title="Read" class="oe_read"><a class="oe_read oe_e">W</a></li>
|
||||
<li t-if="!widget.options.thread.display_on_flat" title="Set back to unread" class="oe_unread"><a class="oe_unread oe_e">h</a></li>
|
||||
<li title="Quick reply"><a class="oe_reply oe_e">)</a></li>
|
||||
<t t-if="(widget.is_author and widget.options.message.show_dd_delete) or widget.type == 'email'">
|
||||
<li class="placeholder-mail-star"><t t-call="mail.thread.message.star"/></li>
|
||||
<li t-if="widget.datasets.show_read_unread" title="Read" class="oe_read"><a class="oe_read oe_e">W</a></li>
|
||||
<li t-if="widget.datasets.show_read_unread" title="Set back to unread" class="oe_unread"><a class="oe_unread oe_e">h</a></li>
|
||||
<li title="Quick reply" t-if="widget.datasets.show_reply"><a class="oe_reply oe_e">)</a></li>
|
||||
<t t-if="(widget.datasets.is_author and widget.options.message.show_dd_delete) or widget.datasets.type == 'email'">
|
||||
<li>
|
||||
<span class="oe_dropdown_toggle">
|
||||
<a class="oe_e" title="More options">í</a>
|
||||
<ul class="oe_dropdown_menu">
|
||||
<li t-if="widget.is_author and widget.options.message.show_dd_delete"><a class="oe_mail_msg_delete">Delete</a></li>
|
||||
<!-- Uncomment when adding subtype hiding
|
||||
<li t-if="display['show_hide']">
|
||||
<a href="#" class="oe_mail_msg_hide_type" t-attf-data-subtype='{widget.subtype}'>Hide '<t t-esc="widget.subtype"/>' for this document</a>
|
||||
</li> -->
|
||||
<li t-if="widget.type == 'email'"><a class="oe_mail_msg_details" t-attf-href="#model=mail.message&id=#{widget.id}" >Details</a></li>
|
||||
<li t-if="widget.datasets.is_author and widget.options.message.show_dd_delete"><a class="oe_msg_delete">Delete</a></li>
|
||||
<li t-if="widget.datasets.type == 'email'"><a class="oe_msg_details" t-attf-href="#model=mail.message&id=#{widget.datasets.id}" >Details</a></li>
|
||||
</ul>
|
||||
</span>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
|
||||
<a t-attf-href="#model=res.partner&id=#{widget.author_id[0]}" t-att-title="widget.author_id[1]">
|
||||
<img class="oe_mail_icon oe_mail_frame oe_left" t-att-src="widget.avatar"/>
|
||||
<a t-attf-href="#model=res.partner&id=#{widget.datasets.author_id[0]}" t-att-title="widget.datasets.author_id[1]">
|
||||
<img class="oe_mail_icon oe_mail_frame oe_left" t-att-src="widget.datasets.avatar"/>
|
||||
</a>
|
||||
|
||||
<div class="oe_mail_msg_content">
|
||||
<div class="oe_msg_content">
|
||||
<!-- message itself -->
|
||||
<div class="oe_mail_msg">
|
||||
<h1 t-if="widget.subject" class="oe_mail_msg_title">
|
||||
<t t-raw="widget.subject"/>
|
||||
<h1 t-if="widget.datasets.subject" class="oe_msg_title">
|
||||
<t t-raw="widget.datasets.subject"/>
|
||||
</h1>
|
||||
<ul class="oe_mail_msg_footer">
|
||||
<li t-if="widget.author_id"><a t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-raw="widget.author_id[1]"/></a></li>
|
||||
<li><span t-att-title="widget.date"><t t-raw="widget.timerelative"/></span></li>
|
||||
<li t-if="widget.attachment_ids.length > 0">
|
||||
<a class="oe_mail_msg_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>
|
||||
<ul class="oe_msg_footer">
|
||||
<li t-if="widget.datasets.author_id"><a t-attf-href="#model=res.partner&id=#{widget.datasets.author_id[0]}"><t t-raw="widget.datasets.author_id[1]"/></a></li>
|
||||
<li><span t-att-title="widget.datasets.date"><t t-raw="widget.datasets.timerelative"/></span></li>
|
||||
<li t-if="widget.datasets.attachment_ids.length > 0">
|
||||
<a class="oe_msg_view_attachments">
|
||||
<t t-if="widget.datasets.attachment_ids.length == 1">1 Attachment</t>
|
||||
<t t-if="widget.datasets.attachment_ids.length > 1"><t t-raw="widget.datasets.attachment_ids.length"/> Attachments</t>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="oe_clear"/>
|
||||
<div class="oe_mail_msg_body">
|
||||
<t t-if="widget.options.message.show_record_name and widget.record_name and (!widget.subject) and !widget.options.thread.thread_level and !widget.options.thread.display_on_flat and widget.model!='res.partner'">
|
||||
<a class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&id=#{widget.res_id}"><t t-raw="widget.record_name"/></a>
|
||||
<div class="oe_msg_body">
|
||||
<t t-if="widget.options.message.show_record_name and widget.datasets.record_name and (!widget.datasets.subject) and !widget.options.thread.thread_level and !widget.options.thread.display_on_thread[0] and widget.datasets.model!='res.partner'">
|
||||
<a class="oe_mail_action_model" t-attf-href="#model=#{widget.datasets.model}&id=#{widget.res_id}"><t t-raw="widget.datasets.record_name"/></a>
|
||||
</t>
|
||||
<t t-raw="widget.body"/>
|
||||
<t t-raw="widget.datasets.body"/>
|
||||
</div>
|
||||
<t t-if="widget.attachment_ids.length > 0">
|
||||
<t t-if="widget.datasets.attachment_ids.length > 0">
|
||||
<div class="oe_clear"></div>
|
||||
<t t-call="mail.thread.message.attachments"/>
|
||||
</t>
|
||||
|
@ -250,30 +241,40 @@
|
|||
|
||||
<!-- expandable message layout -->
|
||||
<li t-name="mail.thread.expandable" class="oe_mail oe_mail_thread_msg oe_mail_unread">
|
||||
<div t-attf-class="oe_mail_msg_#{widget.type} oe_semantic_html_override">
|
||||
<div class="oe_mail_msg_content oe_mail_msg_more_message">
|
||||
<a class="oe_mail_fetch_more">Load more messages <span t-if="widget.nb_messages>0">(<t t-raw="widget.nb_messages"/> messages not display)</span>...</a>
|
||||
<div t-attf-class="oe_msg_#{widget.datasets.type} oe_semantic_html_override">
|
||||
<div class="oe_msg_content oe_msg_more_message">
|
||||
<a class="oe_mail_fetch_more">Load more messages <span t-if="widget.datasets.nb_messages>0">(<t t-raw="widget.datasets.nb_messages"/> messages not display)</span>...</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!--
|
||||
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>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- mail.thread.message.vote
|
||||
Template used to display Like/Unlike in a mail.message
|
||||
-->
|
||||
<span t-name="mail.thread.message.vote">
|
||||
<span class="oe_left oe_mail_vote_count">
|
||||
<t t-if='widget.has_voted'>
|
||||
<t t-if='widget.datasets.has_voted'>
|
||||
You
|
||||
</t>
|
||||
<t t-if='(widget.vote_user_ids.length-(widget.has_voted?1:0)) > 0'>
|
||||
<t t-if='widget.has_voted'> and </t>
|
||||
<t t-esc="widget.vote_user_ids.length"/> people
|
||||
<t t-if='(widget.datasets.vote_user_ids.length-(widget.datasets.has_voted?1:0)) > 0'>
|
||||
<t t-if='widget.datasets.has_voted'> and </t>
|
||||
<t t-esc="widget.datasets.vote_user_ids.length"/> people
|
||||
</t>
|
||||
<t t-if='widget.vote_user_ids.length > 0'>
|
||||
<t t-if='widget.datasets.vote_user_ids.length > 0'>
|
||||
agree
|
||||
</t>
|
||||
</span>
|
||||
<button t-attf-class="oe_mail_msg_vote oe_tag">
|
||||
<button t-attf-class="oe_msg_vote oe_tag">
|
||||
<span>
|
||||
<t t-if="!widget.has_voted">Agree</t>
|
||||
<t t-if="widget.has_voted">Undo</t>
|
||||
|
@ -281,4 +282,13 @@
|
|||
</button>
|
||||
</span>
|
||||
|
||||
<!-- mail.thread.message.star
|
||||
Template used to display stared/unstared message in a mail.message
|
||||
-->
|
||||
<span t-name="mail.thread.message.star">
|
||||
<span class="oe_left">
|
||||
<button t-attf-class="oe_mail_starbox oe_tag #{widget.datasets.is_favorite?'oe_stared':''}">*</button>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
</template>
|
||||
|
|
|
@ -520,6 +520,10 @@ class test_mail(TestMailMockups):
|
|||
self.assertEqual(message2.subject, _subject, 'mail.message subject incorrect')
|
||||
self.assertEqual(message2.body, group_bird.description, 'mail.message body incorrect')
|
||||
|
||||
def test_30_message_read(self):
|
||||
""" Tests for message_read and expandables. """
|
||||
self.assertTrue(1 == 1, 'Test not implemented, do not replace by return True')
|
||||
|
||||
def test_40_needaction(self):
|
||||
""" Tests for mail.message needaction. """
|
||||
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
|
||||
|
@ -577,11 +581,19 @@ class test_mail(TestMailMockups):
|
|||
reply_msg = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1',
|
||||
extra='In-Reply-To: %s' % msg1.message_id)
|
||||
self.mail_group.message_process(cr, uid, None, reply_msg)
|
||||
# TDE note: temp various asserts because of the random bug about msg1.child_ids
|
||||
msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=1)
|
||||
new_msg = self.mail_message.browse(cr, uid, msg_ids[0])
|
||||
self.assertEqual(new_msg.parent_id, msg1, 'Newly processed mail_message (%d) should have msg1 as parent' % (new_msg.id))
|
||||
|
||||
# 2. References header
|
||||
reply_msg2 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: Re: 1',
|
||||
extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id)
|
||||
self.mail_group.message_process(cr, uid, None, reply_msg2)
|
||||
# TDE note: temp various asserts because of the random bug about msg1.child_ids
|
||||
msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=1)
|
||||
new_msg = self.mail_message.browse(cr, uid, msg_ids[0])
|
||||
self.assertEqual(new_msg.parent_id, msg1, 'Newly processed mail_message should have msg1 as parent')
|
||||
|
||||
# 3. Subject contains [<ID>] + model passed to message+process -> only attached to group, not to mail
|
||||
reply_msg3 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com',
|
||||
|
@ -591,9 +603,15 @@ class test_mail(TestMailMockups):
|
|||
group_pigs.refresh()
|
||||
msg1.refresh()
|
||||
self.assertEqual(5, len(group_pigs.message_ids), 'group should contain 5 messages')
|
||||
# TDE note: python test + debug because of the random error we see with the next assert
|
||||
if len(msg1.child_ids) != 2:
|
||||
msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=10)
|
||||
for new_msg in self.mail_message.browse(cr, uid, msg_ids):
|
||||
print new_msg.subject, '(id', new_msg.id, ')', 'parent_id:', new_msg.parent_id
|
||||
print '\tchild_ids', [child.id for child in new_msg.child_ids]
|
||||
self.assertEqual(2, len(msg1.child_ids), 'msg1 should have 2 children now')
|
||||
|
||||
def test_60_vote(self):
|
||||
def test_60_message_vote(self):
|
||||
""" Test designed for the vote/unvote feature. """
|
||||
cr, uid = self.cr, self.uid
|
||||
user_admin = self.res_users.browse(cr, uid, uid)
|
||||
|
@ -613,7 +631,7 @@ class test_mail(TestMailMockups):
|
|||
# Test: msg1 has Admin as voter
|
||||
self.assertEqual(set(msg1.vote_user_ids), set([user_admin]), 'after voting, Admin is not the voter')
|
||||
# Do: Bert vote for msg1
|
||||
self.mail_message.vote_toggle(cr, uid, [msg1.id], [user_bert_id])
|
||||
self.mail_message.vote_toggle(cr, user_bert_id, [msg1.id])
|
||||
msg1.refresh()
|
||||
# Test: msg1 has Admin and Bert as voters
|
||||
self.assertEqual(set(msg1.vote_user_ids), set([user_admin, user_bert]), 'after voting, Admin and Bert are not the voters')
|
||||
|
@ -622,3 +640,7 @@ class test_mail(TestMailMockups):
|
|||
msg1.refresh()
|
||||
# Test: msg1 has Bert as voter
|
||||
self.assertEqual(set(msg1.vote_user_ids), set([user_bert]), 'after unvoting for Admin, Bert is not the voter')
|
||||
|
||||
def test_70_message_favorite(self):
|
||||
""" Tests for favorites. """
|
||||
self.assertTrue(1 == 1, 'Test not implemented, do not replace by return True')
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
<field name="model">mail.compose.message</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Compose Email" version="7.0">
|
||||
<!-- truly invisible fields for control and options -->
|
||||
<field name="composition_mode" nolabel="1" invisible="0"/>
|
||||
<field name="model" nolabel="1" invisible="0"/>
|
||||
<field name="res_id" nolabel="1" invisible="0"/>
|
||||
<field name="parent_id" nolabel="1" invisible="0"/>
|
||||
<field name="content_subtype" nolabel="1" invisible="0"/>
|
||||
<!-- visible wizard -->
|
||||
<group>
|
||||
<!-- truly invisible fields for control and options -->
|
||||
<field name="composition_mode" invisible="1"/>
|
||||
<field name="model" invisible="1"/>
|
||||
<field name="res_id" invisible="1"/>
|
||||
<field name="parent_id" invisible="1"/>
|
||||
<field name="content_subtype" invisible="1"/>
|
||||
<!-- visible wizard -->
|
||||
<field name="subject" placeholder="Subject..."
|
||||
attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>/>
|
||||
<field name="partner_ids" widget="many2many_tags" placeholder="Add contacts to notify..."
|
||||
|
|
|
@ -110,7 +110,7 @@ class procurement_order(osv.osv):
|
|||
|
||||
def production_order_create_note(self, cr, uid, ids, context=None):
|
||||
for procurement in self.browse(cr, uid, ids, context=context):
|
||||
body = _("Manufacturing Order created.")
|
||||
body = _("Manufacturing Order <em>%s</em> created.") % ( procurement.production_id.name,)
|
||||
self.message_post(cr, uid, [procurement.id], body=body, context=context)
|
||||
|
||||
procurement_order()
|
||||
|
|
Loading…
Reference in New Issue