Thibault Delavallée (OpenERP) has proposed merging
lp:~openerp-dev/openobject-addons/trunk-mail-clean-rule-tde into
lp:openobject-addons.
Requested reviews:
OpenERP Core Team (openerp)
For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-mail-clean-rule-tde/+merge/129680
--
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-mail-clean-rule-tde/+merge/129680
Your team OpenERP R&D Team is subscribed to branch
lp:~openerp-dev/openobject-addons/trunk-mail-clean-rule-tde.
=== modified file 'mail/mail_group.py'
--- mail/mail_group.py 2012-10-01 18:27:22 +0000
+++ mail/mail_group.py 2012-10-15 14:20:29 +0000
@@ -30,7 +30,7 @@
group. The group mechanics are based on the followers. """
_description = 'Discussion group'
_name = 'mail.group'
- _mail_autothread = False
+ _mail_flat_thread = False
_inherit = ['mail.thread']
_inherits = {'mail.alias': 'alias_id', 'ir.ui.menu': 'menu_id'}
=== modified file 'mail/mail_thread.py'
--- mail/mail_thread.py 2012-10-10 11:20:18 +0000
+++ mail/mail_thread.py 2012-10-15 14:20:29 +0000
@@ -57,10 +57,16 @@
to override at least the ``message_new`` and ``message_update``
methods (calling ``super``) to add model-specific behavior at
creation and update of a thread when processing incoming emails.
+
+ Options:
+ - _mail_flat_thread: if set to True, all messages without parent_id
+ are automatically attached to the first message posted on the
+ ressource. If set to False, the display of Chatter is done using
+ threads, and no parent_id is automatically set.
'''
_name = 'mail.thread'
_description = 'Email Thread'
- _mail_autothread = True
+ _mail_flat_thread = True
def _get_message_data(self, cr, uid, ids, name, args, context=None):
""" Computes:
@@ -84,7 +90,7 @@
res[thread.id]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids))
return res
-
+
def _get_subscription_data(self, cr, uid, ids, name, args, context=None):
""" Computes:
- message_is_follower: is uid in the document followers
@@ -113,7 +119,7 @@
for subtype in fol.subtype_ids:
thread_subtype_dict[subtype.name]['followed'] = True
res[fol.res_id]['message_subtype_data'] = thread_subtype_dict
-
+
return res
def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None):
@@ -633,8 +639,10 @@
(isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), "Invalid thread_id"
if isinstance(thread_id, (list, tuple)):
thread_id = thread_id and thread_id[0]
+ mail_message = self.pool.get('mail.message')
+ model = context.get('thread_model', self._name) if thread_id else False
- attachment_ids=[]
+ attachment_ids = []
for name, content in attachments:
if isinstance(content, unicode):
content = content.encode('utf-8')
@@ -648,24 +656,20 @@
}
attachment_ids.append((0, 0, data_attach))
- # get subtype
- if not subtype:
- subtype = 'mail.mt_comment'
- s = subtype.split('.')
- if len(s)==1:
- s = ('mail', s[0])
- ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, s[0], s[1])
- subtype_id = ref and ref[1] or False
-
- model = context.get('thread_model', self._name) if thread_id else False
- messages = self.pool.get('mail.message')
-
- #auto link messages for same id and object
- if self._mail_autothread and thread_id:
- message_ids = messages.search(cr, uid, ['&',('res_id', '=', thread_id),('model','=',model)], context=context)
- if len(message_ids):
- parent_id = min(message_ids)
-
+ # fetch subtype
+ if subtype:
+ s_data = subtype.split('.')
+ if len(s_data) == 1:
+ s_data = ('mail', s_data[0])
+ ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, s_data[0], s_data[1])
+ subtype_id = ref and ref[1] or False
+ else:
+ subtype_id = False
+
+ # _mail_flat_thread: automatically set free messages to the first posted message
+ if self._mail_flat_thread and not parent_id and thread_id:
+ message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)], context=context, order="id ASC", limit=1)
+ parent_id = message_ids and message_ids[0] or False
values = kwargs
values.update({
@@ -681,51 +685,47 @@
# if the parent is private, the message must be private
if parent_id:
- msg = messages.browse(cr, uid, parent_id, context=context)
- if msg.is_private:
- values["is_private"] = msg.is_private
+ parent_message = mail_message.browse(cr, uid, parent_id, context=context)
+ if parent_message.is_private:
+ values["is_private"] = parent_message.is_private
# Avoid warnings about non-existing fields
for x in ('from', 'to', 'cc'):
values.pop(x, None)
- return messages.create(cr, uid, values, context=context)
-
- #------------------------------------------------------
- # Followers API
- #------------------------------------------------------
+ return mail_message.create(cr, uid, values, context=context)
def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification',
subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
- # if the user write on his wall
- if self._name=='res.partner' and not thread_id:
- user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
- thread_id = user.partner_id.id
-
- added_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type=type,
+ # when writing on res.partner, without specific thread_id -> redirect to the user's partner
+ if self._name == 'res.partner' and not thread_id:
+ thread_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ new_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type=type,
subtype=subtype, parent_id=parent_id, context=context)
-
- attachment_ids=[]
+ # Chatter: attachments linked to the document (not done JS-side), load the message
if attachments:
ir_attachment = self.pool.get('ir.attachment')
- attachment_ids = ir_attachment.search(cr, 1, [('res_model', '=', ""), ('res_id', '=', ""), ('user_id', '=', uid), ('id', 'in', attachments)], context=context)
+ 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)
if attachment_ids:
- self.pool.get('ir.attachment').write(cr, 1, attachment_ids, { 'res_model': self._name, 'res_id': thread_id }, context=context)
- self.pool.get('mail.message').write(cr, 1, [added_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]} )
-
- added_message = self.pool.get('mail.message').message_read(cr, uid, [added_message_id])
- return added_message
+ 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])]})
+ new_message = self.pool.get('mail.message').message_read(cr, uid, [new_message_id])
+ return new_message
+
+ #------------------------------------------------------
+ # Followers API
+ #------------------------------------------------------
def get_message_subtypes(self, cr, uid, ids, context=None):
- """ message_subtype_data: data about document subtypes: which are
- available, which are followed if any """
+ """ Wrapper to get subtypes. """
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):
""" Wrapper on message_subscribe, using users. If user_ids is not
provided, subscribe uid instead. """
- if not user_ids:
- return False
+ if user_ids is None:
+ user_ids = [uid]
partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)]
return self.message_subscribe(cr, uid, ids, partner_ids, subtype_ids=subtype_ids, context=context)
@@ -738,14 +738,14 @@
subtype_ids = subtype_obj.search(cr, uid, [('default', '=', True), '|', ('res_model', '=', self._name), ('res_model', '=', False)], context=context)
# update the subscriptions
fol_obj = self.pool.get('mail.followers')
- fol_ids = fol_obj.search(cr, 1, [('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)], context=context)
- fol_obj.write(cr, 1, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
+ fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)], context=context)
+ fol_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
return True
def message_unsubscribe_users(self, cr, uid, ids, user_ids=None, context=None):
""" Wrapper on message_subscribe, using users. If user_ids is not
provided, unsubscribe uid instead. """
- if not user_ids:
+ if user_ids is None:
user_ids = [uid]
partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)]
return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context)
=== modified file 'mail/res_partner.py'
--- mail/res_partner.py 2012-10-01 18:27:22 +0000
+++ mail/res_partner.py 2012-10-15 14:20:29 +0000
@@ -25,7 +25,7 @@
""" Update partner to add a field about notification preferences """
_name = "res.partner"
_inherit = ['res.partner', 'mail.thread']
- _mail_autothread = False
+ _mail_flat_thread = False
_columns = {
'notification_email_send': fields.selection([
=== modified file 'mail/static/src/css/mail.css'
--- mail/static/src/css/mail.css 2012-10-10 11:17:51 +0000
+++ mail/static/src/css/mail.css 2012-10-15 14:20:29 +0000
@@ -143,12 +143,12 @@
/* subtypes
/* ------------------------------------------------------------ */
-.openerp .oe_mouse_subtypes {
+.openerp .oe_mail_subtypes {
display:inline-block;
position: relative;
z-index: 5;
}
-.openerp .oe_mouse_subtypes .oe_recthread_subtypes {
+.openerp .oe_mail_subtypes .oe_recthread_subtypes {
background: #fff;
padding: 2px;
border: 1px solid #aaaaaa;
@@ -156,10 +156,10 @@
position: absolute;
z-index: 2;
}
-.openerp .oe_mouse_subtypes.oe_mouseout .oe_recthread_subtypes {
+.openerp .oe_mail_subtypes.oe_mouseout .oe_recthread_subtypes {
display: none;
}
-.openerp .oe_mouse_subtypes.oe_mouseover .oe_recthread_subtypes {
+.openerp .oe_mail_subtypes.oe_mouseover .oe_recthread_subtypes {
display: block;
}
=== modified file 'mail/static/src/js/mail.js'
--- mail/static/src/js/mail.js 2012-10-15 13:33:34 +0000
+++ mail/static/src/js/mail.js 2012-10-15 14:20:29 +0000
@@ -387,7 +387,7 @@
mail.ChatterUtils.get_text2html(body),
false,
'comment',
- false,
+ 'mail.mt_comment',,
this.context.default_parent_id,
attachments]
).then(this.parent_thread.proxy('switch_new_message'));
=== modified file 'mail/static/src/js/mail_followers.js'
--- mail/static/src/js/mail_followers.js 2012-10-10 18:16:11 +0000
+++ mail/static/src/js/mail_followers.js 2012-10-15 14:20:29 +0000
@@ -26,7 +26,6 @@
this._super.apply(this, arguments);
this.options.image = this.node.attrs.image || 'image_small';
this.options.title = this.node.attrs.title || 'Followers';
- this.options.context = this.node.attrs.context;
this.options.comment = this.node.attrs.help || false;
this.ds_model = new session.web.DataSetSearch(this, this.view.model);
this.sub_model = new session.web.DataSetSearch(this,'mail.message.subtype');
@@ -55,16 +54,14 @@
bind_events: function() {
var self = this;
this.$('button.oe_follower')
- .on('click', function () {
+ .on('click', function (event) {
if($(this).hasClass('oe_notfollow'))
self.do_follow();
else
self.do_unfollow();
});
-
- this.$el.on('click', 'ul.oe_subtypes input', self.do_update_subscription );
-
- this.$el.on('click', 'button.oe_invite', function(event) {
+ this.$('ul.oe_subtypes input').on('click', self.do_update_subscription);
+ this.$('button.oe_invite').on('click', function (event) {
action = {
type: 'ir.actions.act_window',
res_model: 'mail.wizard.invite',
@@ -93,23 +90,13 @@
return this.fetch_followers(value_ || this.get_value());
},
- set_is_follower: function(value_) {
- for(var i in value_){
- if(value_[i]['user_ids'][0]==this.session.uid)
- this.message_is_follower=true;
- this.display_buttons();
- return true;
- }
- this.message_is_follower=false;
- this.display_buttons();
- return false;
- },
-
fetch_followers: function (value_) {
this.value = value_ || {};
this.message_is_follower = (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
if(value_)
- return this.ds_follow.call('read', [this.value, ['name', 'user_ids']]).pipe(this.proxy('display_followers'), this.proxy('display_generic'));
+ return this.ds_follow.call('read', [this.value, ['name', 'user_ids']])
+ .pipe(this.proxy('display_followers'), this.proxy('display_generic'))
+ .pipe(this.proxy('display_buttons'));
},
/* Display generic info about follower, for people not having access to res_partner */
@@ -125,8 +112,6 @@
content += ' (' + this.value.length + ')'
}
this.$('div.oe_mail_recthread_followers h4').html(content);
- this.display_buttons();
- return $.when();
},
/** Display the followers, evaluate is_follower directly */
@@ -142,6 +127,17 @@
self.set_is_follower(records);
},
+ /** Computes whether the current user is in the followers */
+ set_is_follower: function(value_) {
+ this.message_is_follower = false;
+ for(var i in value_) {
+ if (value_[i]['user_ids'][0] == this.session.uid) {
+ this.message_is_follower = true;
+ return true;
+ }
+ }
+ },
+
display_buttons: function () {
if (this.message_is_follower) {
this.$('button.oe_follower').removeClass('oe_notfollow').addClass('oe_following');
@@ -149,7 +145,7 @@
else {
this.$('button.oe_follower').removeClass('oe_following').addClass('oe_notfollow');
}
-
+
if (this.view.is_action_enabled('edit'))
this.$('span.oe_mail_invite_wrapper').hide();
else
=== modified file 'mail/static/src/xml/mail_followers.xml'
--- mail/static/src/xml/mail_followers.xml 2012-10-10 18:16:11 +0000
+++ mail/static/src/xml/mail_followers.xml 2012-10-15 14:20:29 +0000
@@ -7,7 +7,7 @@
-->
<div t-name="mail.followers" class="oe_mail_recthread_aside oe_semantic_html_override">
<div class="oe_mail_recthread_actions">
- <div class="oe_mouse_subtypes">
+ <div class="oe_mail_subtypes">
<button type="button" class="oe_follower oe_notfollow">
<span class="oe_follow">Follow</span>
<span class="oe_unfollow">Unfollow</span>
@@ -18,11 +18,9 @@
<ul class="oe_subtypes"></ul>
</div>
</div>
- <div class="oe_grey">
- <t t-if="widget.options.comment">
- <h5><t t-raw="widget.options.comment"/></h5>
- </t>
- </div>
+ <t t-if="widget.options.comment">
+ <h5 class="oe_grey"><t t-raw="widget.options.comment"/></h5>
+ </t>
<div class="oe_mail_recthread_followers">
<button type="button" class="oe_invite"><span>Invite</span></button>
<t t-if="widget.options.title">
=== modified file 'mail/tests/test_mail_access_rights.py'
--- mail/tests/test_mail_access_rights.py 2012-09-27 16:15:58 +0000
+++ mail/tests/test_mail_access_rights.py 2012-10-15 14:20:29 +0000
@@ -102,53 +102,53 @@
""" Test mail_message search override about access rights. """
self.assertTrue(1 == 1, 'Test not implemented, do not replace by return True')
- # def test_10_mail_flow_access_rights(self):
- # """ Test a Chatter-looks alike flow. """
- # cr, uid = self.cr, self.uid
- # partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
- # user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
-
- # # Prepare groups: Pigs (employee), Jobs (public)
- # self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
- # self.group_jobs_id = self.mail_group.create(cr, uid, {'name': 'Jobs', 'public': 'public'})
-
- # # ----------------------------------------
- # # CASE1: Bert, without groups
- # # ----------------------------------------
- # # Do: Bert creates a group, should crash because perm_create only for employees
- # self.assertRaises(except_orm,
- # self.mail_group.create,
- # cr, user_bert_id, {'name': 'Bert\'s Group'})
- # # Do: Bert reads Jobs basic fields, ok because public = read access on the group
- # self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['name', 'description'])
- # # Do: Bert browse Pigs, ok (no direct browse of partners)
- # self.mail_group.browse(cr, user_bert_id, self.group_jobs_id)
- # # Do: Bert reads Jobs messages, ok because read access on the group => read access on its messages
- # jobs_message_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_ids'])['message_ids']
- # self.mail_message.read(cr, user_bert_id, jobs_message_ids)
- # # Do: Bert reads Jobs followers, ko because partner are accessible to employees or partner manager
- # jobs_followers_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_follower_ids'])['message_follower_ids']
- # self.assertRaises(except_orm,
- # self.res_partner.read,
- # cr, user_bert_id, jobs_followers_ids)
- # # Do: Bert comments Jobs, ko because no write access on the group and not in the followers
- # self.assertRaises(except_orm,
- # self.mail_group.message_post,
- # cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
- # # Do: add Bert to jobs followers
- # self.mail_group.message_subscribe(cr, uid, [self.group_jobs_id], [partner_bert_id])
- # # Do: Bert comments Jobs, ok because he is now in the followers
- # self.mail_group.message_post(cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
-
- # # Do: Bert reads Pigs, should crash because mail.group security=groups only for employee group
- # self.assertRaises(except_orm,
- # self.mail_group.read,
- # cr, user_bert_id, self.group_pigs_id)
-
- # # ----------------------------------------
- # # CASE1: Raoul, employee
- # # ----------------------------------------
- # # Do: Bert read Pigs, ok because public
- # self.mail_group.read(cr, user_raoul_id, self.group_pigs_id)
- # # Do: Bert read Jobs, ok because group_public_id = employee
- # self.mail_group.read(cr, user_raoul_id, self.group_jobs_id)
+ def test_10_mail_flow_access_rights(self):
+ """ Test a Chatter-looks alike flow. """
+ cr, uid = self.cr, self.uid
+ partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
+ user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
+
+ # Prepare groups: Pigs (employee), Jobs (public)
+ self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
+ self.group_jobs_id = self.mail_group.create(cr, uid, {'name': 'Jobs', 'public': 'public'})
+
+ # ----------------------------------------
+ # CASE1: Bert, without groups
+ # ----------------------------------------
+ # Do: Bert creates a group, should crash because perm_create only for employees
+ self.assertRaises(except_orm,
+ self.mail_group.create,
+ cr, user_bert_id, {'name': 'Bert\'s Group'})
+ # Do: Bert reads Jobs basic fields, ok because public = read access on the group
+ self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['name', 'description'])
+ # Do: Bert browse Pigs, ok (no direct browse of partners)
+ self.mail_group.browse(cr, user_bert_id, self.group_jobs_id)
+ # Do: Bert reads Jobs messages, ok because read access on the group => read access on its messages
+ jobs_message_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_ids'])['message_ids']
+ self.mail_message.read(cr, user_bert_id, jobs_message_ids)
+ # Do: Bert reads Jobs followers, ko because partner are accessible to employees or partner manager
+ jobs_followers_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_follower_ids'])['message_follower_ids']
+ self.assertRaises(except_orm,
+ self.res_partner.read,
+ cr, user_bert_id, jobs_followers_ids)
+ # Do: Bert comments Jobs, ko because no write access on the group and not in the followers
+ self.assertRaises(except_orm,
+ self.mail_group.message_post,
+ cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
+ # Do: add Bert to jobs followers
+ self.mail_group.message_subscribe(cr, uid, [self.group_jobs_id], [partner_bert_id])
+ # Do: Bert comments Jobs, ok because he is now in the followers
+ self.mail_group.message_post(cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
+
+ # Do: Bert reads Pigs, should crash because mail.group security=groups only for employee group
+ self.assertRaises(except_orm,
+ self.mail_group.read,
+ cr, user_bert_id, self.group_pigs_id)
+
+ # ----------------------------------------
+ # CASE1: Raoul, employee
+ # ----------------------------------------
+ # Do: Bert read Pigs, ok because public
+ self.mail_group.read(cr, user_raoul_id, self.group_pigs_id)
+ # Do: Bert read Jobs, ok because group_public_id = employee
+ self.mail_group.read(cr, user_raoul_id, self.group_jobs_id)
_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help : https://help.launchpad.net/ListHelp