[MERGE] trunk-fix-mail-mail-rules-tde: OpenChatter 'Rule Yur Daddy' Branch

Fixes some of access rights issues. This addons branch features :
- message_is_follower field added in invisible in all views using Chatter
- update of message_follower_ids, to use message_is_follower, and to properly handle users not having access to the res.partner model (ex: basic portal users, anonymous users)
- misc access rights tuning

bzr revid: abo@openerp.com-20120928173655-uh9rzio2zpl8vfpx
This commit is contained in:
Antonin Bourguignon
2012-09-28 19:36:55 +02:00
56 changed files with 572 additions and 215 deletions
+2
View File
@@ -278,6 +278,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -437,6 +438,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -108,6 +108,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -243,6 +243,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -518,6 +519,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -146,6 +146,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -301,6 +302,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1
View File
@@ -53,6 +53,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -222,6 +222,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+2
View File
@@ -223,6 +223,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -525,6 +526,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1 -1
View File
@@ -146,10 +146,10 @@
<field name="priority"/>
<field name="opportunity_id"/>
</group>
<field name="description" placeholder="Description..."/>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1
View File
@@ -181,6 +181,7 @@
</group>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -99,6 +99,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+12 -10
View File
@@ -196,6 +196,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -246,12 +247,12 @@
<t t-name="kanban-box">
<div class="oe_module_vignette">
<a type="open" class="oe_module_icon">
<div class="oe_event_date "><t t-esc="record.date_begin.raw_value.getDate()"/></div>
<div class="oe_event_month_year">
<t t-esc="record.date_begin.raw_value.toString('MMM')"/>
<t t-esc="record.date_begin.raw_value.getFullYear()"/>
</div>
<div class="oe_event_time"><t t-esc="record.date_begin.raw_value.toString('hh:mm tt')"/></div>
<div class="oe_event_date "><t t-esc="record.date_begin.raw_value.getDate()"/></div>
<div class="oe_event_month_year">
<t t-esc="record.date_begin.raw_value.toString('MMM')"/>
<t t-esc="record.date_begin.raw_value.getFullYear()"/>
</div>
<div class="oe_event_time"><t t-esc="record.date_begin.raw_value.toString('hh:mm tt')"/></div>
</a>
<div class="oe_module_desc">
<h4><a type="open"><field name="name"/></a></h4>
@@ -271,10 +272,10 @@
</p>
<t t-if="record.register_avail.raw_value != 0">
<t t-if="!record.is_subscribed.raw_value">
<input t-att-id="record.id.raw_value" type="text" name="subscribe" class="no_of_seats" value="1" onchange="document.getElementById('btn_sub' +this.id).setAttribute('data-context',JSON.stringify({'ticket':this.value}))"/>
<button t-att-id="'btn_sub'+record.id.raw_value" type="object" name="subscribe_to_event" class="oe_mail_button_follow">
<span >Subscribe</span>
</button>
<input t-att-id="record.id.raw_value" type="text" name="subscribe" class="no_of_seats" value="1" onchange="document.getElementById('btn_sub' +this.id).setAttribute('data-context',JSON.stringify({'ticket':this.value}))"/>
<button t-att-id="'btn_sub'+record.id.raw_value" type="object" name="subscribe_to_event" class="oe_mail_button_follow">
<span >Subscribe</span>
</button>
</t>
</t>
<t t-if="record.is_subscribed.raw_value">
@@ -478,6 +479,7 @@
</group>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1
View File
@@ -139,6 +139,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+2
View File
@@ -121,6 +121,7 @@
</group>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -158,6 +159,7 @@
<field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..."/>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -183,6 +183,7 @@
<field name="description" placeholder="Feedback of interviews..."/>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1
View File
@@ -77,6 +77,7 @@
<field name="description"/>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+2 -1
View File
@@ -19,6 +19,7 @@
#
##############################################################################
from openerp import SUPERUSER_ID
from osv import osv
from osv import fields
import tools
@@ -91,7 +92,7 @@ class mail_notification(osv.Model):
:param browse_record message: mail.message to notify
"""
notify_pids = []
for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context):
for partner in self.pool.get('res.partner').browse(cr, SUPERUSER_ID, partner_ids, context=context):
# Do not send an email to the writer
if partner.user_ids and partner.user_ids[0].id == uid:
continue
+6 -4
View File
@@ -19,9 +19,10 @@
#
##############################################################################
from openerp import SUPERUSER_ID
from osv import osv
from osv import fields
from tools.translate import _
class ir_ui_menu(osv.osv):
""" Override of ir.ui.menu class. When adding mail_thread module, each
@@ -38,13 +39,14 @@ class ir_ui_menu(osv.osv):
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
""" Override to take off menu entries (mail.group) the user is not
following. """
following. Access are done using SUPERUSER_ID to avoid access
rights issues for an internal back-end algorithm. """
ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0, limit=None, order=order, context=context, count=False)
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
follower_obj = self.pool.get('mail.followers')
for menu in self.browse(cr, uid, ids, context=context):
if menu.mail_group_id:
sub_ids = follower_obj.search(cr, uid, [
sub_ids = follower_obj.search(cr, SUPERUSER_ID, [
('partner_id', '=', partner_id), ('res_model', '=', 'mail.group'),
('res_id', '=', menu.mail_group_id.id)
], context=context)
+1
View File
@@ -81,6 +81,7 @@
</group>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"
options='{"thread_level": 1}'/>
<field name="message_follower_ids" widget="mail_followers"/>
+4 -4
View File
@@ -19,13 +19,12 @@
#
##############################################################################
# import ast
import base64
import logging
import tools
from osv import osv
from osv import fields
from openerp import SUPERUSER_ID
from osv import osv, fields
from tools.translate import _
_logger = logging.getLogger(__name__)
@@ -134,7 +133,8 @@ class mail_mail(osv.Model):
:return: True
"""
if mail.auto_delete:
mail.unlink()
# done with SUPERUSER_ID to avoid giving large unlink access rights
self.unlink(cr, SUPERUSER_ID, [mail.id], context=context)
return True
def send_get_mail_subject(self, cr, uid, mail, force=False, partner=None, context=None):
+129 -45
View File
@@ -24,8 +24,9 @@ import openerp
import tools
from email.header import decode_header
from openerp import SUPERUSER_ID
from operator import itemgetter
from osv import osv, fields
from osv import osv, orm, fields
from tools.translate import _
_logger = logging.getLogger(__name__)
@@ -62,14 +63,14 @@ class mail_message(osv.Model):
continue
try:
result[message.id] = self._shorten_name(self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1])
except openerp.exceptions.AccessDenied, e:
except (orm.except_orm, osv.except_osv):
pass
return result
def _get_unread(self, cr, uid, ids, name, arg, context=None):
""" Compute if the message is unread by the current user. """
res = dict((id, {'unread': False}) for id in ids)
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
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, [
('partner_id', 'in', [partner_id]),
@@ -87,7 +88,7 @@ class mail_message(osv.Model):
read_cond = '(read = false or read is null)'
else:
read_cond = 'read = true'
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
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,
(partner_id,))
@@ -140,7 +141,8 @@ class mail_message(osv.Model):
return []
def _get_default_author(self, cr, uid, context=None):
return self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
# 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
_defaults = {
'type': 'email',
@@ -154,16 +156,17 @@ class mail_message(osv.Model):
#------------------------------------------------------
def vote_toggle(self, cr, uid, ids, user_ids=None, context=None):
''' Toggles voting '''
''' Toggles voting. Done as SUPERUSER_ID because of write access on
mail.message not always granted. '''
if not user_ids:
user_ids = [uid]
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, uid, message.get('id'), {'vote_user_ids': [(4, user_id)]}, context=context)
self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(4, user_id)]}, context=context)
else:
self.write(cr, uid, message.get('id'), {'vote_user_ids': [(3, user_id)]}, context=context)
self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(3, user_id)]}, context=context)
return True
#------------------------------------------------------
@@ -171,17 +174,31 @@ class mail_message(osv.Model):
#------------------------------------------------------
def _message_dict_get(self, cr, uid, msg, context=None):
""" Return a dict representation of the message browse record. """
""" Return a dict representation of the message browse record. A read
is performed to because of access rights issues (reading many2one
fields allow to have the foreign record name without having
to check external access rights).
"""
has_voted = False
vote_ids = self.pool.get('res.users').name_get(cr, uid, [user.id for user in msg.vote_user_ids], context=context)
vote_ids = self.pool.get('res.users').name_get(cr, SUPERUSER_ID, [user.id for user in msg.vote_user_ids], context=context)
for vote in vote_ids:
if vote[0] == uid:
has_voted = True
break
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context)]
author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0]
author_user_id = self.pool.get('res.users').name_get(cr, uid, [msg.author_id.user_ids[0].id], context=context)[0]
partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context)
try:
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context)]
except (orm.except_orm, osv.except_osv):
attachment_ids = []
try:
author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0]
is_author = uid in msg.author_id.user_ids
except (orm.except_orm, osv.except_osv):
author_id = False
is_author = False
try:
partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context)
except (orm.except_orm, osv.except_osv):
partner_ids = []
return {
'id': msg.id,
'type': msg.type,
@@ -193,7 +210,7 @@ class mail_message(osv.Model):
'subject': msg.subject,
'date': msg.date,
'author_id': author_id,
'author_user_id': author_user_id,
'is_author': is_author,
'partner_ids': partner_ids,
'child_ids': [],
'vote_user_ids': vote_ids,
@@ -246,7 +263,7 @@ class mail_message(osv.Model):
limit = limit or self._message_read_limit
context = context or {}
if not ids:
ids = self.search(cr, uid, domain, context=context, limit=limit)
ids = self.search(cr, SUPERUSER_ID, domain, context=context, limit=limit)
messages = self.browse(cr, uid, ids, context=context)
result = []
@@ -294,41 +311,99 @@ class mail_message(osv.Model):
cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""")
def check_access_rule(self, cr, uid, ids, operation, context=None):
""" mail.message access rule check
- message received (a notification exists) -> ok
- check rules of related document if exists
- fallback on normal mail.message check """
""" Access rules of mail.message:
- read: if
- notification exist (I receive pushed message) OR
- author_id = pid (I am the author) OR
- I can read the related document if res_model, res_id
- 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
- Otherwise: raise
- write: if
- I can write on the related document if res_model, res_id
- Otherwise: raise
- unlink: if
- I can write on the related document if res_model, res_id
- Otherwise: raise
"""
if uid == SUPERUSER_ID:
return
if isinstance(ids, (int, long)):
ids = [ids]
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
# check messages for which you have a notification
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
not_obj = self.pool.get('mail.notification')
not_ids = not_obj.search(cr, uid, [
('partner_id', '=', partner_id),
('message_id', 'in', ids),
], context=context)
notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, uid, not_ids, context=context)
if notification.message_id.id in ids]
# check messages linked to an existing document
# Read mail_message.ids to have their values
model_record_ids = {}
document_ids = []
cr.execute('SELECT DISTINCT id, model, res_id FROM mail_message WHERE id = ANY (%s)', (ids,))
for id, rmod, rid in cr.fetchall():
if not (rmod and rid):
continue
document_ids.append(id)
model_record_ids.setdefault(rmod, set()).add(rid)
message_values = dict.fromkeys(ids)
cr.execute('SELECT DISTINCT id, model, res_id, author_id FROM "%s" WHERE id = ANY (%%s)' % self._table, (ids,))
for id, rmod, rid, author_id in cr.fetchall():
message_values[id] = {'res_model': rmod, 'res_id': rid, 'author_id': author_id}
if rmod:
model_record_ids.setdefault(rmod, set()).add(rid)
# Read: Check for received notifications -> could become an ir.rule, but not till we do not have a many2one variable field
if operation == 'read':
not_obj = self.pool.get('mail.notification')
not_ids = not_obj.search(cr, SUPERUSER_ID, [
('partner_id', '=', partner_id),
('message_id', 'in', ids),
], context=context)
notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)]
else:
notified_ids = []
# Read: Check messages you are author -> could become an ir.rule, but not till we do not have a many2one variable field
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]
else:
author_ids = []
# Create: Check message_follower_ids
if operation == 'create':
doc_follower_ids = []
for model, mids in model_record_ids.items():
fol_obj = self.pool.get('mail.followers')
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
('res_model', '=', model),
('res_id', 'in', list(mids)),
('partner_id', '=', partner_id),
], context=context)
fol_mids = [follower.res_id for follower in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)]
doc_follower_ids += [mid for mid, message in message_values.iteritems()
if message.get('res_model') == model and message.get('res_id') in fol_mids]
else:
doc_follower_ids = []
# Calculate remaining ids, and related model/res_ids
model_record_ids = {}
other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids))
for id in other_ids:
if message_values[id]['res_model']:
model_record_ids.setdefault(message_values[id]['res_model'], set()).add(message_values[id]['res_id'])
# CRUD: Access rights related to the document
document_related_ids = []
for model, mids in model_record_ids.items():
model_obj = self.pool.get(model)
mids = model_obj.exists(cr, uid, mids)
model_obj.check_access_rights(cr, uid, operation)
model_obj.check_access_rule(cr, uid, mids, operation, context=context)
if operation in ['create', 'write', 'unlink']:
model_obj.check_access_rights(cr, uid, 'write')
model_obj.check_access_rule(cr, uid, mids, 'write', context=context)
else:
model_obj.check_access_rights(cr, uid, operation)
model_obj.check_access_rule(cr, uid, mids, operation, context=context)
document_related_ids += [mid for mid, message in message_values.iteritems()
if message.get('res_model') == model and message.get('res_id') in mids]
# fall back on classic operation for other ids
other_ids = set(ids).difference(set(notified_ids), set(document_ids))
super(mail_message, self).check_access_rule(cr, uid, other_ids, operation, context=None)
# Calculate remaining ids: if not void, raise an error
other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids), set(document_related_ids))
if not other_ids:
return
raise orm.except_orm(_('Access Denied'),
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
(self._description, operation))
def create(self, cr, uid, values, context=None):
if not values.get('message_id') and values.get('res_id') and values.get('model'):
@@ -337,6 +412,13 @@ class mail_message(osv.Model):
self.notify(cr, uid, newid, context=context)
return newid
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)
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).
@@ -360,12 +442,14 @@ class mail_message(osv.Model):
partners_to_notify |= set(partner.id for partner in message.partner_ids)
# add all followers and set add them in partner_ids
if message.model and message.res_id:
record = self.pool.get(message.model).browse(cr, uid, message.res_id, context=context)
record = self.pool.get(message.model).browse(cr, SUPERUSER_ID, message.res_id, context=context)
extra_notified = set(partner.id for partner in record.message_follower_ids)
missing_notified = extra_notified - partners_to_notify
if missing_notified:
message.write({'partner_ids': [(4, p_id) for p_id in missing_notified]})
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(4, p_id) for p_id in missing_notified]}, context=context)
partners_to_notify |= extra_notified
# # remove uid from partners
self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(3, uid)]}, context=context)
self.pool.get('mail.notification').notify(cr, uid, list(partners_to_notify), newid, context=context)
def copy(self, cr, uid, id, default=None, context=None):
+1 -1
View File
@@ -95,7 +95,7 @@
</record>
<record id="action_mail_my_feeds" model="ir.actions.client">
<field name="name">My Feeds</field>
<field name="name">My Posts</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} }&quot;"/>
+19 -10
View File
@@ -30,6 +30,7 @@ import xmlrpclib
from email.message import Message
from mail_message import decode
from openerp import SUPERUSER_ID
from osv import osv, fields
from tools.safe_eval import safe_eval as eval
@@ -118,26 +119,27 @@ class mail_thread(osv.AbstractModel):
def _get_message_data(self, cr, uid, ids, name, args, context=None):
res = dict((id, dict(message_unread=False, message_summary='')) for id in ids)
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
partner_id = 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, uid, [
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, uid, notif_ids, context=context):
for notif in notif_obj.browse(cr, SUPERUSER_ID, notif_ids, context=context):
res[notif.message_id.res_id]['message_unread'] = True
for thread in self.browse(cr, uid, ids, context=context):
cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or ''
res[thread.id]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids))
res[thread.id]['message_is_follower'] = user.partner_id.id in [follower.id for follower in thread.message_follower_ids]
for thread in self.read(cr, uid, ids, ['message_follower_ids', 'message_comment_ids', 'message_ids'], context=context):
cls = res[thread['id']]['message_unread'] and ' class="oe_kanban_mail_new"' or ''
res[thread['id']]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread['message_comment_ids']), len(thread['message_follower_ids']))
res[thread['id']]['message_is_follower'] = partner_id in thread['message_follower_ids']
return res
def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None):
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
res = {}
notif_obj = self.pool.get('mail.notification')
notif_ids = notif_obj.search(cr, uid, [
@@ -196,6 +198,13 @@ class mail_thread(osv.AbstractModel):
fol_obj.unlink(cr, uid, fol_ids, context=context)
return super(mail_thread, self).unlink(cr, uid, ids, context=context)
def copy(self, cr, uid, id, default=None, context=None):
default = default or {}
default['message_ids'] = []
default['message_comment_ids'] = []
default['message_follower_ids'] = []
return super(mail_thread, self).copy(cr, uid, id, default=default, context=context)
#------------------------------------------------------
# mail.message wrappers and tools
#------------------------------------------------------
@@ -620,7 +629,7 @@ class mail_thread(osv.AbstractModel):
""" Wrapper on message_subscribe, using users. If user_ids is not
provided, subscribe uid instead. """
if not user_ids: user_ids = [uid]
partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)]
partner_ids = [user['partner_id'][0] for user in self.pool.get('res.users').read(cr, uid, user_ids, ['partner_id'], context=context)]
return self.message_subscribe(cr, uid, ids, partner_ids, context=context)
def message_subscribe(self, cr, uid, ids, partner_ids, context=None):
@@ -631,7 +640,7 @@ class mail_thread(osv.AbstractModel):
""" Wrapper on message_subscribe, using users. If user_ids is not
provided, unsubscribe uid instead. """
if not user_ids: user_ids = [uid]
partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)]
partner_ids = [user['partner_id'][0] for user in self.pool.get('res.users').read(cr, uid, user_ids, ['partner_id'], context=context)]
return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context)
def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None):
+1
View File
@@ -9,6 +9,7 @@
<field name="arch" type="xml">
<xpath expr="//sheet" position="after">
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"
options='{"thread_level": 1}'/>
<field name="message_follower_ids" widget="mail_followers"/>
+10 -9
View File
@@ -1,13 +1,14 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_global,mail.mail,model_mail_mail,base.group_system,1,1,1,1
access_mail_message_all,mail.message.all,model_mail_message,,1,0,0,0
access_mail_message_all,mail.message.all,model_mail_message,,1,0,1,0
access_mail_message_group_user,mail.message.group.user,model_mail_message,base.group_user,1,1,1,1
access_mail_thread,mail.thread,model_mail_thread,base.group_user,1,1,1,0
access_mail_followers_all,mail.followers.all,model_mail_followers,,1,1,1,1
access_mail_notification_all,mail.notification.all,model_mail_notification,,1,1,1,1
access_mail_group,mail.group,model_mail_group,base.group_user,1,1,1,1
access_mail_mail_all,mail.mail.all,model_mail_mail,,0,0,1,0
access_mail_mail_system,mail.mail.system,model_mail_mail,base.group_system,1,1,1,1
access_mail_followers_all,mail.followers.all,model_mail_followers,,0,0,0,0
access_mail_followers_system,mail.followers.system,model_mail_followers,base.group_system,1,1,1,1
access_mail_notification_all,mail.notification.all,model_mail_notification,,1,0,0,0
access_mail_notification_aystem,mail.notification.system,model_mail_notification,base.group_system,1,1,1,1
access_mail_group_all,mail.group.all,model_mail_group,,1,0,0,0
access_mail_group_user,mail.group.user,model_mail_group,base.group_user,1,1,1,1
access_mail_alias_all,mail.alias.all,model_mail_alias,,1,0,0,0
access_mail_alias_user,mail.alias,model_mail_alias,base.group_user,1,1,1,0
access_mail_alias_system,mail.alias,model_mail_alias,base.group_system,1,1,1,1
access_mail_mail_user,mail.mail,model_mail_mail,base.group_user,1,1,1,0
access_mail_mail_manager,mail.mail,model_mail_mail,group_mail_manager,1,1,1,1
access_mail_vote_all,mail.vote.all,model_mail_vote,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mail_global access_mail_message_all mail.mail mail.message.all model_mail_mail model_mail_message base.group_system 1 1 0 1 1 0
access_mail_message_all mail.message.all model_mail_message 1 0 0 0
3 access_mail_message_group_user mail.message.group.user model_mail_message base.group_user 1 1 1 1
4 access_mail_thread access_mail_mail_all mail.thread mail.mail.all model_mail_thread model_mail_mail base.group_user 1 0 1 0 1 0
5 access_mail_followers_all access_mail_mail_system mail.followers.all mail.mail.system model_mail_followers model_mail_mail base.group_system 1 1 1 1
6 access_mail_notification_all access_mail_followers_all mail.notification.all mail.followers.all model_mail_notification model_mail_followers 1 0 1 0 1 0 1 0
7 access_mail_group access_mail_followers_system mail.group mail.followers.system model_mail_group model_mail_followers base.group_user base.group_system 1 1 1 1
8 access_mail_notification_all mail.notification.all model_mail_notification 1 0 0 0
9 access_mail_notification_aystem mail.notification.system model_mail_notification base.group_system 1 1 1 1
10 access_mail_group_all mail.group.all model_mail_group 1 0 0 0
11 access_mail_group_user mail.group.user model_mail_group base.group_user 1 1 1 1
12 access_mail_alias_all mail.alias.all model_mail_alias 1 0 0 0
13 access_mail_alias_user mail.alias model_mail_alias base.group_user 1 1 1 0
access_mail_alias_system mail.alias model_mail_alias base.group_system 1 1 1 1
access_mail_mail_user mail.mail model_mail_mail base.group_user 1 1 1 0
access_mail_mail_manager mail.mail model_mail_mail group_mail_manager 1 1 1 1
14 access_mail_vote_all mail.vote.all model_mail_vote 1 1 1 1
+13 -14
View File
@@ -2,20 +2,6 @@
<openerp>
<data>
<!-- CATEGORY -->
<record model="ir.module.category" id="module_category_social">
<field name="name">Home</field>
<field name="parent_id" ref="base.module_category_tools"/>
<field name="sequence">26</field>
</record>
<!-- GROUPS -->
<record id="group_mail_manager" model="res.groups">
<field name="name">Mail manager</field>
<field name="comment"></field>
<field name="category_id" ref="module_category_social"/>
</record>
<!-- RULES -->
<record id="group_rule_public_and_joined" model="ir.rule">
<field name="name">Mail.group: access only public and joined groups</field>
@@ -24,5 +10,18 @@
<field name="domain_force">['|', '|', ('public', '=', 'public'), ('message_follower_ids', 'in', [user.id]), '&amp;', ('public','=','groups'), ('group_public_id','in', [x.id for x in user.groups_id])]</field>
</record>
<!--
This rule can not be uncommented, because we have a more wide method in mail.message. When we implement a many2one_variable field, we will be able to uncomment this.
<record id="mail_message_read_partner_or_author" model="ir.rule">
<field name="name">mail.message: read if notified or author</field>
<field name="model_id" ref="model_mail_message"/>
<field name="domain_force">['|', ('partner_ids', 'in', user.partner_id.id), ('author_id', '=', user.partner_id.id)]</field>
<field name="perm_create" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
-->
</data>
</openerp>
+9 -25
View File
@@ -124,6 +124,14 @@
}
/* ------------------------------------------------------------ */
/* Followers
/* ------------------------------------------------------------ */
.openerp div.oe_mail_recthread_aside h4 {
display: inline-block;
}
/* ------------------------------------------------------------ */
/* Thread
/* ------------------------------------------------------------ */
@@ -224,7 +232,7 @@
clear: both;
}
.openerp .oe_mail_msg_content a {
.openerp .oe_chatter a {
cursor: pointer;
}
@@ -258,30 +266,6 @@
display: none;
}
/*--------------------------------------------------------------*/
/* mail.vote
/*--------------------------------------------------------------*/
.openerp .oe_mail_msg_content button.oe_mail_msg_vote {
height:21px;
width: 30px;
padding: 1px;
background: #8A89BA;
margin-top: -4px;
}
.openerp .oe_mail_msg_content button.oe_mail_msg_vote_true {
background:#DC5F59;
}
.openerp .oe_mail_msg_content button.oe_mail_msg_vote span {
color: white;
}
.openerp .oe_mail_msg_content span.oe_mail_vote_count{
color: #807FB4;
}
/* ------------------------------------------------------------ */
/* mail.compose.message form view & OpenERP hacks
+7 -11
View File
@@ -56,11 +56,6 @@ openerp.mail = function(session) {
return session.origin + '/web/binary/saveas?session_id=' + session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id=' + attachment['id'];
},
/** Check if the current user is the message author */
is_author: function (widget, message_user_id) {
return (widget.session && widget.session.uid != 0 && widget.session.uid == message_user_id);
},
/** Replaces some expressions
* - :name - shortcut to an image
*/
@@ -301,6 +296,7 @@ openerp.mail = function(session) {
// TDE TODO: check for deferred, not sure it is correct
this._super.apply(this, arguments);
this.bind_events();
this.display_user_avatar();
// fetch and display message, using message_ids if set
var display_done = $.when(this.message_fetch(true, [], {})).then(this.proxy('do_customize_display'));
// add message composition form view
@@ -313,7 +309,6 @@ openerp.mail = function(session) {
/** Customize the display
* - show_header_compose: show the composition form in the header */
do_customize_display: function() {
this.display_user_avatar();
if (this.options.show_header_compose) {
this.$el.find('div.oe_mail_thread_action').eq(0).show();
}
@@ -491,8 +486,7 @@ openerp.mail = function(session) {
* - record.date: formatting according to the user timezone
* - record.timerelative: relative time givein by timeago lib
* - record.avatar: image url
* - record.attachment_ids[].url: url of each attachment
* - record.is_author: is the current user the author of the record */
* - record.attachment_ids[].url: url of each attachment */
display_record: function (record) {
// formatting and additional fields
record.date = session.web.format_value(record.date, {type:"datetime"});
@@ -506,7 +500,6 @@ openerp.mail = function(session) {
var attach = record.attachment_ids[l];
attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
}
record.is_author = mail.ChatterUtils.is_author(this, record.author_user_id[0]);
// add to internal storage
this.records[record.id] = record;
// render, add the expand feature
@@ -649,12 +642,15 @@ openerp.mail = function(session) {
// update domain
var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]);
// create and render Thread widget
var show_header_compose = this.view.is_action_enabled('edit') ||
(this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
this.$el.find('div.oe_mail_recthread_main').empty();
var thread = new mail.Thread(self, domain, this.options.context,
{ 'thread_level': this.options.thread_level,
'use_composer': true,
'show_header_compose': show_header_compose,
'use_composer': show_header_compose,
'show_dd_delete': true,
'show_reply_by_email': true });
'show_reply_by_email': show_header_compose });
return thread.appendTo(this.$el.find('div.oe_mail_recthread_main'));
},
});
+44 -20
View File
@@ -45,8 +45,8 @@ openerp_mail_followers = function(session, mail) {
},
reinit: function() {
this.$el.find('button.oe_mail_button_follow').hide();
this.$el.find('button.oe_mail_button_unfollow').hide();
this.message_is_follower == undefined;
this.display_buttons();
},
bind_events: function() {
@@ -55,7 +55,7 @@ openerp_mail_followers = function(session, mail) {
.mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
this.$el.on('click', 'button.oe_mail_button_follow', function () { self.do_follow(); });
this.$el.on('click', 'button.oe_mail_button_invite', function(event) {
this.$el.on('click', 'a.oe_mail_invite', function(event) {
action = {
type: 'ir.actions.act_window',
res_model: 'mail.wizard.invite',
@@ -74,41 +74,65 @@ 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']).pipe(function (results) {
return results[0].message_follower_ids;
}).pipe(this.proxy('set_value'));
return this.ds_model.read_ids([this.view.datarecord.id], ['message_is_follower', 'message_follower_ids']).then(function (results) {
self.set_value(results[0].message_follower_ids, results[0].message_is_follower);
});
},
set_value: function(value_) {
set_value: function(value_, message_is_follower) {
this.reinit();
if (! this.view.datarecord.id ||
session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
this.$el.find('div.oe_mail_recthread_aside').hide();
this.$('div.oe_mail_recthread_aside').hide();
return;
}
return this.fetch_followers(value_ || this.get_value());
return this.fetch_followers(value_ || this.get_value(), message_is_follower);
},
fetch_followers: function (value_) {
return this.ds_follow.call('read', [value_, ['name', 'user_ids']]).pipe(this.proxy('display_followers'));
fetch_followers: function (value_, message_is_follower) {
this.value = value_;
this.message_is_follower = message_is_follower || (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
return this.ds_follow.call('read', [value_, ['name', 'user_ids']]).pipe(this.proxy('display_followers'), this.proxy('display_generic'));
},
/* Display generic info about follower, for people not having access to res_partner */
display_generic: function (error, event) {
event.preventDefault();
var node_user_list = this.$('ul.oe_mail_followers_display').empty();
// format content: Followers (You and 0 other) // Followers (3)
var content = this.options.title;
if (this.message_is_follower) {
content += ' (You and ' + (this.value.length-1) + ' other)';
}
else {
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 */
display_followers: function (records) {
var self = this;
this.message_is_follower = _.indexOf(_.flatten(_.pluck(records, 'user_ids')), this.session.uid) != -1;
var node_user_list = this.$el.find('ul.oe_mail_followers_display').empty();
this.$el.find('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')');
var node_user_list = this.$('ul.oe_mail_followers_display').empty();
this.$('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')');
_(records).each(function (record) {
record.avatar_url = mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record.id);
$(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(node_user_list);
});
if (this.message_is_follower) {
this.$el.find('button.oe_mail_button_follow').hide();
this.$el.find('button.oe_mail_button_unfollow').show(); }
else {
this.$el.find('button.oe_mail_button_follow').show();
this.$el.find('button.oe_mail_button_unfollow').hide(); }
this.display_buttons();
},
display_buttons: function () {
this.$('button.oe_mail_button_follow').hide();
this.$('button.oe_mail_button_unfollow').hide();
this.$('span.oe_mail_invite_wrapper').hide();
if (! this.view.is_action_enabled('edit')) return;
this.$('span.oe_mail_invite_wrapper').show();
if (this.message_is_follower) { this.$('button.oe_mail_button_unfollow').show(); }
else if (this.message_is_follower == false) { this.$('button.oe_mail_button_follow').show(); }
},
do_follow: function () {
+2 -2
View File
@@ -140,7 +140,7 @@
<li t-if="options.show_record_name and record.record_name and record.subject and options.thread_level > 0">
<a t-attf-href="#model=#{record.model}&amp;id=#{record.res_id}"><t t-raw="record.record_name"/></a>
</li>
<li><a t-attf-href="#model=res.partner&amp;id=#{record.author_id[0]}"><t t-raw="record.author_id[1]"/></a></li>
<li t-if="record.author_id"><a t-attf-href="#model=res.partner&amp;id=#{record.author_id[0]}"><t t-raw="record.author_id[1]"/></a></li>
<li><span t-att-title="record.date"><t t-raw="record.timerelative"/></span></li>
<t t-call="mail.thread.message.vote"/>
<li t-if="options.show_reply"><a class="oe_mail_msg_reply">Reply</a></li>
@@ -185,7 +185,7 @@
<t t-if='record.vote_user_ids.length > 0'>
<span class="oe_left oe_mail_vote_count"><t t-esc="record.vote_user_ids.length"/> votes</span>
</t>
<button t-attf-class="oe_mail_msg_vote oe_mail_msg_vote_#{record.has_voted}" t-attf-data-msg_id="{record.id}">
<button t-attf-class="oe_mail_msg_vote oe_tag" t-attf-data-msg_id="{record.id}">
<t t-if="! record.has_voted"><span>+1</span></t>
<t t-if="record.has_voted"><span>-1</span></t>
</button>
@@ -7,7 +7,6 @@
-->
<div t-name="mail.followers" class="oe_mail_recthread_aside oe_semantic_html_override">
<div class="oe_mail_recthread_actions">
<button type="button" class="oe_mail_button_invite">Invite</button>
<button type="button" class="oe_mail_button_follow">Follow</button>
<button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button>
</div>
@@ -18,7 +17,7 @@
</div>
<div class="oe_mail_recthread_followers">
<t t-if="widget.options.title">
<h4><t t-raw="widget.options.title"/></h4>
<h4><t t-raw="widget.options.title"/></h4><span class="oe_mail_invite_wrapper"> · <a class="oe_mail_invite" >Invite partners</a></span>
</t>
<ul class="oe_mail_followers_display"></ul>
</div>
+3 -2
View File
@@ -18,10 +18,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_mail
from . import test_mail, test_mail_access_rights
checks = [
test_mail,
test_mail_access_rights,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://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 openerp.addons.mail.tests import test_mail
from osv.orm import except_orm
class test_mail_access_rights(test_mail.TestMailMockups):
def setUp(self):
super(test_mail_access_rights, self).setUp()
cr, uid = self.cr, self.uid
self.mail_group = self.registry('mail.group')
self.mail_message = self.registry('mail.message')
self.mail_notification = self.registry('mail.notification')
self.res_users = self.registry('res.users')
self.res_groups = self.registry('res.groups')
self.res_partner = self.registry('res.partner')
# create a 'pigs' group that will be used through the various tests
self.group_pigs_id = self.mail_group.create(self.cr, self.uid,
{'name': 'Pigs', 'description': 'Fans of Pigs, unite !'})
# Find Employee group
group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
self.group_employee_id = group_employee_ref and group_employee_ref[1] or False
# Create Bert (without groups) and Raoul( employee)
self.user_bert_id = self.res_users.create(cr, uid, {'name': 'Bert Tartopoils', 'login': 'bert', 'groups_id': [(6, 0, [])]})
self.user_raoul_id = self.res_users.create(cr, uid, {'name': 'Raoul Grosbedon', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]})
self.user_bert = self.res_users.browse(cr, uid, self.user_bert_id)
self.partner_bert_id = self.user_bert.partner_id.id
self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
self.partner_raoul_id = self.user_raoul.partner_id.id
def test_00_mail_message_read_access_rights(self):
""" Test basic mail_message read access rights. """
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, basic mail.message read access
# ----------------------------------------
# Do: create a new mail.message
message_id = self.mail_message.create(cr, uid, {'body': 'My Body'})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
self.assertRaises(except_orm, self.mail_message.read,
cr, user_bert_id, message_id)
# Do: message is pushed to Bert
notif_id = self.mail_notification.create(cr, uid, {'message_id': message_id, 'partner_id': partner_bert_id})
# Test: Bert reads the message, ok because notification pushed
self.mail_message.read(cr, user_bert_id, message_id)
# Do: remove notification
self.mail_notification.unlink(cr, uid, notif_id)
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
self.assertRaises(except_orm, self.mail_message.read,
cr, self.user_bert_id, message_id)
# Do: Bert is now the author
self.mail_message.write(cr, uid, [message_id], {'author_id': partner_bert_id})
# Test: Bert reads the message, ok because Bert is the author
self.mail_message.read(cr, user_bert_id, message_id)
# Do: Bert is not the author anymore
self.mail_message.write(cr, uid, [message_id], {'author_id': partner_raoul_id})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
self.assertRaises(except_orm, self.mail_message.read,
cr, user_bert_id, message_id)
# Do: message is attached to a document Bert can read, Jobs
self.mail_message.write(cr, uid, [message_id], {'model': 'mail.group', 'res_id': self.group_jobs_id})
# Test: Bert reads the message, ok because linked to a doc he is allowed to read
self.mail_message.read(cr, user_bert_id, message_id)
# Do: message is attached to a document Bert cannot read, Pigs
self.mail_message.write(cr, uid, [message_id], {'model': 'mail.group', 'res_id': self.group_pigs_id})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
self.assertRaises(except_orm, self.mail_message.read,
cr, user_bert_id, message_id)
def test_05_mail_message_search_access_rights(self):
""" 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)
+4 -2
View File
@@ -409,8 +409,9 @@
</page>
</notebook>
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
@@ -799,6 +800,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -107,6 +107,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1
View File
@@ -189,6 +189,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+5 -2
View File
@@ -117,8 +117,11 @@
<field name="stage_id" domain="[('user_id','=',uid)]" widget="statusbar" clickable="1"/>
</header>
<field name="memo" widget="html"/><!-- editor_width="100%%" editor_height="60%%" -->
<field name="message_follower_ids" widget="mail_followers"/>
<field class="oe_chatter" name="message_ids" widget="mail_thread"/>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
</record>
+2
View File
@@ -5,6 +5,7 @@
<!-- Mail group for the company's news -->
<record id="company_news_feed" model="mail.group">
<field name="name">Company's news</field>
<field name="public">public</field>
</record>
<record id="action_news" model="ir.actions.act_window">
@@ -17,6 +18,7 @@
<!-- Mail group for the company's jobs -->
<record id="company_jobs" model="mail.group">
<field name="name">Company's jobs</field>
<field name="public">public</field>
</record>
<record id="action_jobs" model="ir.actions.act_window">
+3 -1
View File
@@ -9,7 +9,9 @@
<field name="password">portal</field>
<!-- Avoid auto-including this user in any default group -->
<field name="groups_id" eval="[(5,)]"/>
<field name="partner_id" eval="ref('base.res_partner_1')"/>
<field name="supplier" eval="False"/>
<field name="customer" eval="True"/>
<field name="email">demo@portal.wrong.address</field>
</record>
<!-- Add the demo user to the portal (and therefore to the portal member group) -->
+1 -7
View File
@@ -1,8 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_message,mail.message,mail.model_mail_message,group_portal,1,0,1,1
access_mail_message_all,mail.message.all,mail.model_mail_message,group_portal,1,0,0,0
access_mail_thread,mail.thread,mail.model_mail_thread,group_portal,1,0,0,0
access_mail_followers,mail.followers,mail.model_mail_followers,group_portal,1,0,1,1
access_mail_notification,mail.notification,mail.model_mail_notification,group_portal,1,0,1,0
access_mail_group,mail.group,mail.model_mail_group,group_portal,1,0,0,0
access_mail_alias,mail.alias,mail.model_mail_alias,group_portal,1,0,0,0
access_mail_message_portal,mail.message.portal,mail.model_mail_message,group_portal,1,0,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mail_message access_mail_message_portal mail.message mail.message.portal mail.model_mail_message group_portal 1 0 1 1
access_mail_message_all mail.message.all mail.model_mail_message group_portal 1 0 0 0
access_mail_thread mail.thread mail.model_mail_thread group_portal 1 0 0 0
access_mail_followers mail.followers mail.model_mail_followers group_portal 1 0 1 1
access_mail_notification mail.notification mail.model_mail_notification group_portal 1 0 1 0
access_mail_group mail.group mail.model_mail_group group_portal 1 0 0 0
access_mail_alias mail.alias mail.model_mail_alias group_portal 1 0 0 0
+39 -1
View File
@@ -21,15 +21,18 @@
from openerp.addons.mail.tests import test_mail
from openerp.tools import append_content_to_html
from osv.orm import except_orm
class test_portal(test_mail.TestMailMockups):
def setUp(self):
super(test_portal, self).setUp()
cr, uid = self.cr, self.uid
self.ir_model = self.registry('ir.model')
self.mail_group = self.registry('mail.group')
self.mail_mail = self.registry('mail.mail')
self.mail_message = self.registry('mail.message')
self.res_users = self.registry('res.users')
self.res_partner = self.registry('res.partner')
@@ -37,7 +40,42 @@ class test_portal(test_mail.TestMailMockups):
self.group_pigs_id = self.mail_group.create(self.cr, self.uid,
{'name': 'Pigs', 'description': 'Fans of Pigs, unite !'})
def test_00_mail_invite(self):
# Find Portal group
group_portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
self.group_portal_id = group_portal_ref and group_portal_ref[1] or False
# Create Chell (portal user)
self.user_chell_id = self.res_users.create(cr, uid, {'name': 'Chell Gladys', 'login': 'chell', 'groups_id': [(6, 0, [self.group_portal_id])]})
self.user_chell = self.res_users.browse(cr, uid, self.user_chell_id)
self.partner_chell_id = self.user_chell.partner_id.id
def test_00_access_rights(self):
""" Test basic mail_message and mail_group access rights for portal users. """
cr, uid = self.cr, self.uid
partner_chell_id = self.partner_chell_id
user_chell_id = self.user_chell_id
# Prepare group: Pigs (portal)
self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
self.mail_group.write(cr, uid, [self.group_pigs_id], {'name': 'Jobs', 'public': 'groups', 'group_public_id': self.group_portal_id})
# ----------------------------------------
# CASE1: Chell will use the Chatter
# ----------------------------------------
# Do: Chell reads Pigs messages, ok because restricted to portal group
message_ids = self.mail_group.read(cr, user_chell_id, self.group_pigs_id, ['message_ids'])['message_ids']
self.mail_message.read(cr, user_chell_id, message_ids)
# Do: Chell posts a message on Pigs, crash because can not write on group or is not in the followers
self.assertRaises(except_orm,
self.mail_group.message_post,
cr, user_chell_id, self.group_pigs_id, body='Message')
# Do: Chell is added to Pigs followers
self.mail_group.message_subscribe(cr, uid, [self.group_pigs_id], [partner_chell_id])
# Test: Chell posts a message on Pigs, ok because in the followers
self.mail_group.message_post(cr, user_chell_id, self.group_pigs_id, body='Message')
def test_50_mail_invite(self):
cr, uid = self.cr, self.uid
user_admin = self.res_users.browse(cr, uid, uid)
self.mail_invite = self.registry('mail.wizard.invite')
@@ -5,7 +5,7 @@
<record id="portal_stock_picking_user_rule" model="ir.rule">
<field name="name">Portal Personal Claims</field>
<field ref="crm_claim.model_crm_claim" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
+50 -22
View File
@@ -40,32 +40,51 @@ class crm_contact_us(osv.TransientModel):
r = self.pool.get('res.company').search(cr, uid, [], context=context)
return r
def _get_current_user_email(self, cr, uid, context=None):
def _get_user_name(self, cr, uid, context=None):
"""
If the user is logged in (i.e. not anonymous), get the user's email to
pre-fill the email_from field.
If the user is logged in (i.e. not anonymous), get the user's name to
pre-fill the partner_name field.
Same goes for the other _get_user_attr methods.
@return current user's email if the user isn't "anonymous", None otherwise
@return current user's name if the user isn't "anonymous", None otherwise
"""
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
user = self.pool.get('res.users').read(cr, uid, uid, ['login'], context)
if (user.login != 'anonymous'):
return user.email
if (user['login'] != 'anonymous'):
return self.pool.get('res.users').name_get(cr, uid, uid, context)[0][1]
else:
return None
def _get_user_email(self, cr, uid, context=None):
user = self.pool.get('res.users').read(cr, uid, uid, ['login', 'email'], context)
if (user['login'] != 'anonymous' and user['email']):
return user['email']
else:
return None
def _get_user_phone(self, cr, uid, context=None):
user = self.pool.get('res.users').read(cr, uid, uid, ['login', 'phone'], context)
if (user['login'] != 'anonymous' and user['phone']):
return user['phone']
else:
return None
_defaults = {
'email_from' : _get_current_user_email,
'company_ids' : _get_companies,
'partner_name': _get_user_name,
'email_from': _get_user_email,
'phone': _get_user_phone,
'company_ids': _get_companies,
}
def create(self, cr, uid, values, context=None):
"""
Since they potentially sensitive, we don't want any user to be able to
read datas generated through this module. That's why we'll write those
information in the crm.lead table and leave blank entries in the
portal_crm.crm_contact_us table. This is why the create() method is
overwritten.
Since they are potentially sensitive, we don't want any user to be able
to read datas generated through this module. That's why we'll write
those information directly in the crm.lead table and leave blank
entries in the portal_crm.crm_contact_us table.
This is why the create() method is overwritten.
"""
crm_lead = self.pool.get('crm.lead')
@@ -88,11 +107,20 @@ class crm_contact_us(osv.TransientModel):
def submit(self, cr, uid, ids, context=None):
""" When the form is submitted, redirect the user to a "Thanks" message """
return {'type': 'ir.actions.act_window',
'view_mode': 'form',
'view_type': 'form',
'res_model': self._name,
'res_id': ids[0],
'view_id': self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal_crm', 'wizard_contact_form_view_thanks')[1],
'target': 'inline'
}
return {
'type': 'ir.actions.act_window',
'view_mode': 'form',
'view_type': 'form',
'res_model': self._name,
'res_id': ids[0],
'view_id': self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal_crm', 'wizard_contact_form_view_thanks')[1],
'target': 'inline',
}
def _needaction_domain_get(self, cr, uid, context=None):
"""
This model doesn't need the needactions mechanism inherited from
crm_lead, so simply override the method to return an empty domain
and, therefore, 0 needactions.
"""
return False
@@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_event,event,event.model_event_event,portal.group_portal,1,0,0,0
access_registration,registration,event.model_event_registration,portal.group_portal,1,0,0,0
access_registration,registration,event.model_event_registration,portal.group_portal,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_event event event.model_event_event portal.group_portal 1 0 0 0
3 access_registration registration event.model_event_registration portal.group_portal 1 0 1 0 1 0 1
@@ -12,7 +12,7 @@
<record id="portal_registration_rule" model="ir.rule">
<field name="name">Portal Personal Registrations</field>
<field ref="event.model_event_registration" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="domain_force">[('user_id','=',user.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
@@ -5,7 +5,7 @@
<record id="portal_task_rule" model="ir.rule">
<field name="name">Portal Personal Task</field>
<field ref="project.model_project_task" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
@@ -5,7 +5,7 @@
<record id="portal_issue_rule" model="ir.rule">
<field name="name">Portal Personal Issues</field>
<field ref="project_issue.model_project_issue" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
+11 -11
View File
@@ -5,43 +5,43 @@
<!-- Sale Portal Access Rules -->
<record id="portal_sale_order_user_rule" model="ir.rule">
<field name="name">Portal Personal Quotations/Sales Orders</field>
<field ref="sale.model_sale_order" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_stock_picking_user_rule" model="ir.rule">
<field name="name">Portal Personal Delivery Orders</field>
<field name="model_id" ref="stock.model_stock_picking"/>
<field name="domain_force">[('partner_id','child_of',[user.partner_id.id])]</field>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_stock_picking_user_rule" model="ir.rule">
<field name="name">Portal Personal Delivery Orders Out</field>
<field ref="stock.model_stock_picking_out" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',[user.partner_id.id])]</field>
<field name="model_id" ref="stock.model_stock_picking_out"/>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_account_invoice_user_rule" model="ir.rule">
<field name="name">Portal Personal Account Invoices</field>
<field ref="account.model_account_invoice" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="model_id" ref="account.model_account_invoice"/>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_personal_payment" model="ir.rule">
<field name="name">Portal Personal Payments</field>
<field ref="account_voucher.model_account_voucher" name="model_id"/>
<field name="domain_force">[('partner_id','child_of',user.partner_id.id)]</field>
<field name="model_id" ref="account_voucher.model_account_voucher"/>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
<record id="portal_personal_contact" model="ir.rule">
<field name="name">Portal Personal Contacts</field>
<field ref="base.model_res_partner" name="model_id"/>
<field name="domain_force">[('id','child_of',user.partner_id.id)]</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="domain_force">[('message_follower_ids','in',user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
</record>
+1
View File
@@ -103,6 +103,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1
View File
@@ -203,6 +203,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+2
View File
@@ -150,6 +150,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers" help="Follow this project to automatically follow all related tasks and issues."/>
</div>
@@ -467,6 +468,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1 -1
View File
@@ -31,7 +31,7 @@
</record>
<record id="crm_case_programnotgivingproperoutput0" model="project.issue">
<field eval="time.strftime('%Y-%m-15 12:50:00')" name="date"/>
<field eval="time.strftime('%Y-%m-15 12:50:00')" name="date"/>
<field name="priority">3</field>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_1"/>
@@ -161,6 +161,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+1
View File
@@ -285,6 +285,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -102,8 +102,9 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
+1
View File
@@ -310,6 +310,7 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
+2
View File
@@ -917,6 +917,7 @@
</xpath>
<xpath expr="/form/sheet" position="after">
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
@@ -1043,6 +1044,7 @@
</xpath>
<xpath expr="/form/sheet" position="after">
<div class="oe_chatter">
<field name="message_is_follower" invisible="1"/>
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>