Added achtung images
[odoo/odoo.git] / addons / mail / tests / test_mail_message.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Business Applications
5 #    Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from openerp.addons.mail.tests.test_mail_base import TestMailBase
23 from openerp.osv.orm import except_orm
24 from openerp.tools import mute_logger
25
26
27 class test_mail_access_rights(TestMailBase):
28
29     def setUp(self):
30         super(test_mail_access_rights, self).setUp()
31         cr, uid = self.cr, self.uid
32
33         # Test mail.group: public to provide access to everyone
34         self.group_jobs_id = self.mail_group.create(cr, uid, {'name': 'Jobs', 'public': 'public'})
35         # Test mail.group: private to restrict access
36         self.group_priv_id = self.mail_group.create(cr, uid, {'name': 'Private', 'public': 'private'})
37
38     @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
39     def test_00_mail_group_access_rights(self):
40         """ Testing mail_group access rights and basic mail_thread features """
41         cr, uid, user_bert_id, user_raoul_id = self.cr, self.uid, self.user_bert_id, self.user_raoul_id
42
43         # Do: Bert reads Jobs -> ok, public
44         self.mail_group.read(cr, user_bert_id, [self.group_jobs_id])
45         # Do: Bert read Pigs -> ko, restricted to employees
46         self.assertRaises(except_orm, self.mail_group.read,
47             cr, user_bert_id, [self.group_pigs_id])
48         # Do: Raoul read Pigs -> ok, belong to employees
49         self.mail_group.read(cr, user_raoul_id, [self.group_pigs_id])
50
51         # Do: Bert creates a group -> ko, no access rights
52         self.assertRaises(except_orm, self.mail_group.create,
53             cr, user_bert_id, {'name': 'Test'})
54         # Do: Raoul creates a restricted group -> ok
55         new_group_id = self.mail_group.create(cr, user_raoul_id, {'name': 'Test'})
56         # Do: Bert added in followers, read -> ok, in followers
57         self.mail_group.message_subscribe_users(cr, uid, [new_group_id], [user_bert_id])
58         self.mail_group.read(cr, user_bert_id, [new_group_id])
59
60         # Do: Raoul reads Priv -> ko, private
61         self.assertRaises(except_orm, self.mail_group.read,
62             cr, user_raoul_id, [self.group_priv_id])
63         # Do: Raoul added in follower, read -> ok, in followers
64         self.mail_group.message_subscribe_users(cr, uid, [self.group_priv_id], [user_raoul_id])
65         self.mail_group.read(cr, user_raoul_id, [self.group_priv_id])
66
67         # Do: Raoul write on Jobs -> ok
68         self.mail_group.write(cr, user_raoul_id, [self.group_priv_id], {'name': 'modified'})
69         # Do: Bert cannot write on Private -> ko (read but no write)
70         self.assertRaises(except_orm, self.mail_group.write,
71             cr, user_bert_id, [self.group_priv_id], {'name': 're-modified'})
72         # Test: Bert cannot unlink the group
73         self.assertRaises(except_orm,
74             self.mail_group.unlink,
75             cr, user_bert_id, [self.group_priv_id])
76         # Do: Raoul unlinks the group, there are no followers and messages left
77         self.mail_group.unlink(cr, user_raoul_id, [self.group_priv_id])
78         fol_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', self.group_priv_id)])
79         self.assertFalse(fol_ids, 'unlinked document should not have any followers left')
80         msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_priv_id)])
81         self.assertFalse(msg_ids, 'unlinked document should not have any followers left')
82
83     @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
84     def test_10_mail_message_search_access_rights(self):
85         """ Testing mail_message.search() using specific _search implementation """
86         cr, uid, group_pigs_id = self.cr, self.uid, self.group_pigs_id
87         # Data: comment subtype for mail.message creation
88         ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'mail', 'mt_comment')
89         subtype_id = ref and ref[1] or False
90
91         # Data: Birds group, private
92         group_birds_id = self.mail_group.create(self.cr, self.uid, {'name': 'Birds', 'public': 'private'})
93         # Data: Raoul is member of Pigs
94         self.mail_group.message_subscribe(cr, uid, [group_pigs_id], [self.partner_raoul_id])
95         # Data: various author_ids, partner_ids, documents
96         msg_id1 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A', 'subtype_id': subtype_id})
97         msg_id2 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A+B', 'partner_ids': [(6, 0, [self.partner_bert_id])], 'subtype_id': subtype_id})
98         msg_id3 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A Pigs', 'model': 'mail.group', 'res_id': group_pigs_id, 'subtype_id': subtype_id})
99         msg_id4 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A+B Pigs', 'model': 'mail.group', 'res_id': group_pigs_id, 'partner_ids': [(6, 0, [self.partner_bert_id])], 'subtype_id': subtype_id})
100         msg_id5 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A+R Pigs', 'model': 'mail.group', 'res_id': group_pigs_id, 'partner_ids': [(6, 0, [self.partner_raoul_id])], 'subtype_id': subtype_id})
101         msg_id6 = self.mail_message.create(cr, uid, {'subject': '_Test', 'body': 'A Birds', 'model': 'mail.group', 'res_id': group_birds_id, 'subtype_id': subtype_id})
102         msg_id7 = self.mail_message.create(cr, self.user_raoul_id, {'subject': '_Test', 'body': 'B', 'subtype_id': subtype_id})
103         msg_id8 = self.mail_message.create(cr, self.user_raoul_id, {'subject': '_Test', 'body': 'B+R', 'partner_ids': [(6, 0, [self.partner_raoul_id])], 'subtype_id': subtype_id})
104
105         # Test: Bert: 2 messages that have Bert in partner_ids
106         msg_ids = self.mail_message.search(cr, self.user_bert_id, [('subject', 'like', '_Test')])
107         self.assertEqual(set([msg_id2, msg_id4]), set(msg_ids), 'mail_message search failed')
108         # Test: Raoul: 3 messages on Pigs Raoul can read (employee can read group with default values), 0 on Birds (private group)
109         msg_ids = self.mail_message.search(cr, self.user_raoul_id, [('subject', 'like', '_Test'), ('body', 'like', 'A')])
110         self.assertEqual(set([msg_id3, msg_id4, msg_id5]), set(msg_ids), 'mail_message search failed')
111         # Test: Raoul: 3 messages on Pigs Raoul can read (employee can read group with default values), 0 on Birds (private group) + 2 messages as author
112         msg_ids = self.mail_message.search(cr, self.user_raoul_id, [('subject', 'like', '_Test')])
113         self.assertEqual(set([msg_id3, msg_id4, msg_id5, msg_id7, msg_id8]), set(msg_ids), 'mail_message search failed')
114         # Test: Admin: all messages
115         msg_ids = self.mail_message.search(cr, uid, [('subject', 'like', '_Test')])
116         self.assertEqual(set([msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8]), set(msg_ids), 'mail_message search failed')
117
118     @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
119     def test_15_mail_message_check_access_rule(self):
120         """ Testing mail_message.check_access_rule() """
121         cr, uid = self.cr, self.uid
122         partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
123         user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
124
125         # Prepare groups: Pigs (employee), Jobs (public)
126         pigs_msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
127         priv_msg_id = self.mail_group.message_post(cr, uid, self.group_priv_id, body='Message')
128
129         # prepare an attachment
130         attachment_id = self.ir_attachment.create(cr, uid, {'datas': 'My attachment'.encode('base64'), 'name': 'doc.txt', 'datas_fname': 'doc.txt'})
131
132         # ----------------------------------------
133         # CASE1: read
134         # ----------------------------------------
135
136         # Do: create a new mail.message
137         message_id = self.mail_message.create(cr, uid, {'body': 'My Body', 'attachment_ids': [(4, attachment_id)]})
138
139         # Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
140         self.assertRaises(except_orm, self.mail_message.read,
141             cr, user_bert_id, message_id)
142         # Do: message is pushed to Bert
143         notif_id = self.mail_notification.create(cr, uid, {'message_id': message_id, 'partner_id': partner_bert_id})
144         # Test: Bert reads the message, ok because notification pushed
145         self.mail_message.read(cr, user_bert_id, message_id)
146         # Test: Bert downloads attachment, ok because he can read message
147         self.mail_message.download_attachment(cr, user_bert_id, message_id, attachment_id)
148         # Do: remove notification
149         self.mail_notification.unlink(cr, uid, notif_id)
150         # Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
151         self.assertRaises(except_orm, self.mail_message.read,
152             cr, self.user_bert_id, message_id)
153         # Test: Bert downloads attachment, crash because he can't read message
154         self.assertRaises(except_orm, self.mail_message.download_attachment,
155             cr, user_bert_id, message_id, attachment_id)
156         # Do: Bert is now the author
157         self.mail_message.write(cr, uid, [message_id], {'author_id': partner_bert_id})
158         # Test: Bert reads the message, ok because Bert is the author
159         self.mail_message.read(cr, user_bert_id, message_id)
160         # Do: Bert is not the author anymore
161         self.mail_message.write(cr, uid, [message_id], {'author_id': partner_raoul_id})
162         # Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
163         self.assertRaises(except_orm, self.mail_message.read,
164             cr, user_bert_id, message_id)
165         # Do: message is attached to a document Bert can read, Jobs
166         self.mail_message.write(cr, uid, [message_id], {'model': 'mail.group', 'res_id': self.group_jobs_id})
167         # Test: Bert reads the message, ok because linked to a doc he is allowed to read
168         self.mail_message.read(cr, user_bert_id, message_id)
169         # Do: message is attached to a document Bert cannot read, Pigs
170         self.mail_message.write(cr, uid, [message_id], {'model': 'mail.group', 'res_id': self.group_pigs_id})
171         # Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
172         self.assertRaises(except_orm, self.mail_message.read,
173             cr, user_bert_id, message_id)
174
175         # ----------------------------------------
176         # CASE2: create
177         # ----------------------------------------
178
179         # Do: Bert creates a message on Pigs -> ko, no creation rights
180         self.assertRaises(except_orm, self.mail_message.create,
181             cr, user_bert_id, {'model': 'mail.group', 'res_id': self.group_pigs_id, 'body': 'Test'})
182         # Do: Bert create a message on Jobs -> ko, no creation rights
183         self.assertRaises(except_orm, self.mail_message.create,
184             cr, user_bert_id, {'model': 'mail.group', 'res_id': self.group_jobs_id, 'body': 'Test'})
185         # Do: Bert create a private message -> ko, no creation rights
186         self.assertRaises(except_orm, self.mail_message.create,
187             cr, user_bert_id, {'body': 'Test'})
188
189         # Do: Raoul creates a message on Jobs -> ok, write access to the related document
190         self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_jobs_id, 'body': 'Test'})
191         # Do: Raoul creates a message on Priv -> ko, no write access to the related document
192         self.assertRaises(except_orm, self.mail_message.create,
193             cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_priv_id, 'body': 'Test'})
194         # Do: Raoul creates a private message -> ok
195         self.mail_message.create(cr, user_raoul_id, {'body': 'Test'})
196         # Do: Raoul creates a reply to a message on Priv -> ko
197         self.assertRaises(except_orm, self.mail_message.create,
198             cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_priv_id, 'body': 'Test', 'parent_id': priv_msg_id})
199         # Do: Raoul creates a reply to a message on Priv-> ok if has received parent
200         self.mail_notification.create(cr, uid, {'message_id': priv_msg_id, 'partner_id': self.partner_raoul_id})
201         self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_priv_id, 'body': 'Test', 'parent_id': priv_msg_id})
202
203     def test_20_message_set_star(self):
204         """ Tests for starring messages and its related access rights """
205         cr, uid = self.cr, self.uid
206         # Data: post a message on Pigs
207         msg_id = self.group_pigs.message_post(body='My Body', subject='1')
208         msg = self.mail_message.browse(cr, uid, msg_id)
209         msg_raoul = self.mail_message.browse(cr, self.user_raoul_id, msg_id)
210
211         # Do: Admin stars msg
212         self.mail_message.set_message_starred(cr, uid, [msg.id], True)
213         msg.refresh()
214         # Test: notification exists
215         notif_ids = self.mail_notification.search(cr, uid, [('partner_id', '=', self.partner_admin_id), ('message_id', '=', msg.id)])
216         self.assertEqual(len(notif_ids), 1, 'mail_message set_message_starred: more than one notification created')
217         # Test: notification starred
218         notif = self.mail_notification.browse(cr, uid, notif_ids[0])
219         self.assertTrue(notif.starred, 'mail_notification starred failed')
220         self.assertTrue(msg.starred, 'mail_message starred failed')
221
222         # Do: Raoul stars msg
223         self.mail_message.set_message_starred(cr, self.user_raoul_id, [msg.id], True)
224         msg_raoul.refresh()
225         # Test: notification exists
226         notif_ids = self.mail_notification.search(cr, uid, [('partner_id', '=', self.partner_raoul_id), ('message_id', '=', msg.id)])
227         self.assertEqual(len(notif_ids), 1, 'mail_message set_message_starred: more than one notification created')
228         # Test: notification starred
229         notif = self.mail_notification.browse(cr, uid, notif_ids[0])
230         self.assertTrue(notif.starred, 'mail_notification starred failed')
231         self.assertTrue(msg_raoul.starred, 'mail_message starred failed')
232
233         # Do: Admin unstars msg
234         self.mail_message.set_message_starred(cr, uid, [msg.id], False)
235         msg.refresh()
236         msg_raoul.refresh()
237         # Test: msg unstarred for Admin, starred for Raoul
238         self.assertFalse(msg.starred, 'mail_message starred failed')
239         self.assertTrue(msg_raoul.starred, 'mail_message starred failed')
240
241     def test_30_message_set_read(self):
242         """ Tests for reading messages and its related access rights """
243         cr, uid = self.cr, self.uid
244         # Data: post a message on Pigs
245         msg_id = self.group_pigs.message_post(body='My Body', subject='1')
246         msg = self.mail_message.browse(cr, uid, msg_id)
247         msg_raoul = self.mail_message.browse(cr, self.user_raoul_id, msg_id)
248
249         # Do: Admin reads msg
250         self.mail_message.set_message_read(cr, uid, [msg.id], True)
251         msg.refresh()
252         # Test: notification exists
253         notif_ids = self.mail_notification.search(cr, uid, [('partner_id', '=', self.partner_admin_id), ('message_id', '=', msg.id)])
254         self.assertEqual(len(notif_ids), 1, 'mail_message set_message_read: more than one notification created')
255         # Test: notification read
256         notif = self.mail_notification.browse(cr, uid, notif_ids[0])
257         self.assertTrue(notif.read, 'mail_notification read failed')
258         self.assertFalse(msg.to_read, 'mail_message read failed')
259
260         # Do: Raoul reads msg
261         self.mail_message.set_message_read(cr, self.user_raoul_id, [msg.id], True)
262         msg_raoul.refresh()
263         # Test: notification exists
264         notif_ids = self.mail_notification.search(cr, uid, [('partner_id', '=', self.partner_raoul_id), ('message_id', '=', msg.id)])
265         self.assertEqual(len(notif_ids), 1, 'mail_message set_message_read: more than one notification created')
266         # Test: notification read
267         notif = self.mail_notification.browse(cr, uid, notif_ids[0])
268         self.assertTrue(notif.read, 'mail_notification starred failed')
269         self.assertFalse(msg_raoul.to_read, 'mail_message starred failed')
270
271         # Do: Admin unreads msg
272         self.mail_message.set_message_read(cr, uid, [msg.id], False)
273         msg.refresh()
274         msg_raoul.refresh()
275         # Test: msg unread for Admin, read for Raoul
276         self.assertTrue(msg.to_read, 'mail_message read failed')
277         self.assertFalse(msg_raoul.to_read, 'mail_message read failed')
278
279     def test_40_message_vote(self):
280         """ Test designed for the vote/unvote feature. """
281         cr, uid = self.cr, self.uid
282         # Data: post a message on Pigs
283         msg_id = self.group_pigs.message_post(body='My Body', subject='1')
284         msg = self.mail_message.browse(cr, uid, msg_id)
285         msg_raoul = self.mail_message.browse(cr, self.user_raoul_id, msg_id)
286
287         # Do: Admin vote for msg
288         self.mail_message.vote_toggle(cr, uid, [msg.id])
289         msg.refresh()
290         # Test: msg has Admin as voter
291         self.assertEqual(set(msg.vote_user_ids), set([self.user_admin]), 'mail_message vote: after voting, Admin should be in the voter')
292         # Do: Bert vote for msg
293         self.mail_message.vote_toggle(cr, self.user_raoul_id, [msg.id])
294         msg_raoul.refresh()
295         # Test: msg has Admin and Bert as voters
296         self.assertEqual(set(msg_raoul.vote_user_ids), set([self.user_admin, self.user_raoul]), 'mail_message vote: after voting, Admin and Bert should be in the voters')
297         # Do: Admin unvote for msg
298         self.mail_message.vote_toggle(cr, uid, [msg.id])
299         msg.refresh()
300         msg_raoul.refresh()
301         # Test: msg has Bert as voter
302         self.assertEqual(set(msg.vote_user_ids), set([self.user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter')
303         self.assertEqual(set(msg_raoul.vote_user_ids), set([self.user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter')
304
305     @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
306     def test_50_mail_flow_access_rights(self):
307         """ Test a Chatter-looks alike flow to test access rights """
308         cr, uid = self.cr, self.uid
309         mail_compose = self.registry('mail.compose.message')
310         partner_bert_id, partner_raoul_id = self.partner_bert_id, self.partner_raoul_id
311         user_bert_id, user_raoul_id = self.user_bert_id, self.user_raoul_id
312
313         # Prepare groups: Pigs (employee), Jobs (public)
314         pigs_msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message', partner_ids=[self.partner_admin_id])
315         jobs_msg_id = self.mail_group.message_post(cr, uid, self.group_jobs_id, body='Message', partner_ids=[self.partner_admin_id])
316
317         # ----------------------------------------
318         # CASE1: Bert, without groups
319         # ----------------------------------------
320
321         # Do: Bert reads Jobs basic fields, ok because public = read access on the group
322         self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['name', 'description'])
323         # Do: Bert reads Jobs messages, ok because read access on the group => read access on its messages
324         jobs_message_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_ids'])['message_ids']
325         self.mail_message.read(cr, user_bert_id, jobs_message_ids)
326         # Do: Bert browses Jobs, ok (no direct browse of partners), ok for messages, ko for followers (accessible to employees or partner manager)
327         bert_jobs = self.mail_group.browse(cr, user_bert_id, self.group_jobs_id)
328         trigger_read = bert_jobs.name
329         for message in bert_jobs.message_ids:
330             trigger_read = message.subject
331         for partner in bert_jobs.message_follower_ids:
332             with self.assertRaises(except_orm):
333                 trigger_read = partner.name
334         # Do: Bert comments Jobs, ko because no creation right
335         self.assertRaises(except_orm,
336                           self.mail_group.message_post,
337                           cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
338
339         # Do: Bert writes on its own profile, ko because no message create access
340         with self.assertRaises(except_orm):
341             self.res_users.message_post(cr, user_bert_id, user_bert_id, body='I love Bert')
342             self.res_partner.message_post(cr, user_bert_id, partner_bert_id, body='I love Bert')
343
344         # ----------------------------------------
345         # CASE2: Raoul, employee
346         # ----------------------------------------
347
348         # Do: Raoul browses Jobs -> ok, ok for message_ids, of for message_follower_ids
349         raoul_jobs = self.mail_group.browse(cr, user_raoul_id, self.group_jobs_id)
350         trigger_read = raoul_jobs.name
351         for message in raoul_jobs.message_ids:
352             trigger_read = message.subject
353         for partner in raoul_jobs.message_follower_ids:
354             trigger_read = partner.name
355
356         # Do: Raoul comments Jobs, ok
357         self.mail_group.message_post(cr, user_raoul_id, self.group_jobs_id, body='I love Pigs')
358         # Do: Raoul create a mail.compose.message record on Jobs, because he uses the wizard
359         compose_id = mail_compose.create(cr, user_raoul_id,
360             {'subject': 'Subject', 'body': 'Body text', 'partner_ids': []},
361             {'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_jobs_id})
362         mail_compose.send_mail(cr, user_raoul_id, [compose_id])
363         # Do: Raoul replies to a Jobs message using the composer
364         compose_id = mail_compose.create(cr, user_raoul_id,
365             {'subject': 'Subject', 'body': 'Body text'},
366             {'default_composition_mode': 'reply', 'default_parent_id': pigs_msg_id})
367         mail_compose.send_mail(cr, user_raoul_id, [compose_id])