[FIX] mail, BaseModel, portal_sale: fixes and improvements in the URL
[odoo/odoo.git] / addons / portal / tests / test_portal.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.common import TestMail
23 from openerp.exceptions import AccessError
24 from openerp.osv.orm import except_orm
25 from openerp.tools.misc import mute_logger
26
27
28 class test_portal(TestMail):
29
30     def setUp(self):
31         super(test_portal, self).setUp()
32         cr, uid = self.cr, self.uid
33
34         # Find Portal group
35         group_portal = self.registry('ir.model.data').get_object(cr, uid, 'base', 'group_portal')
36         self.group_portal_id = group_portal.id
37
38         # Create Chell (portal user)
39         self.user_chell_id = self.res_users.create(cr, uid, {'name': 'Chell Gladys', 'login': 'chell', 'email': 'chell@gladys.portal', 'groups_id': [(6, 0, [self.group_portal_id])]})
40         self.user_chell = self.res_users.browse(cr, uid, self.user_chell_id)
41         self.partner_chell_id = self.user_chell.partner_id.id
42
43         # Create a PigsPortal group
44         self.group_port_id = self.mail_group.create(cr, uid,
45                         {'name': 'PigsPortal', 'public': 'groups', 'group_public_id': self.group_portal_id},
46                         {'mail_create_nolog': True})
47
48         # Set an email address for the user running the tests, used as Sender for outgoing mails
49         self.res_users.write(cr, uid, uid, {'email': 'test@localhost'})
50
51     @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
52     def test_00_mail_access_rights(self):
53         """ Test basic mail_message and mail_group access rights for portal users. """
54         cr, uid = self.cr, self.uid
55         mail_compose = self.registry('mail.compose.message')
56
57         # Prepare group: Pigs and PigsPortal
58         pigs_msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
59         port_msg_id = self.mail_group.message_post(cr, uid, self.group_port_id, body='Message')
60
61         # Do: Chell browses Pigs -> ko, employee group
62         chell_pigs = self.mail_group.browse(cr, self.user_chell_id, self.group_pigs_id)
63         with self.assertRaises(except_orm):
64             trigger_read = chell_pigs.name
65
66         # Do: Chell posts a message on Pigs, crash because can not write on group or is not in the followers
67         with self.assertRaises(AccessError):
68             self.mail_group.message_post(cr, self.user_chell_id, self.group_pigs_id, body='Message')
69
70         # Do: Chell is added into Pigs followers and browse it -> ok for messages, ko for partners (no read permission)
71         self.mail_group.message_subscribe_users(cr, uid, [self.group_pigs_id], [self.user_chell_id])
72         chell_pigs = self.mail_group.browse(cr, self.user_chell_id, self.group_pigs_id)
73         trigger_read = chell_pigs.name
74         for message in chell_pigs.message_ids:
75             trigger_read = message.subject
76         for partner in chell_pigs.message_follower_ids:
77             if partner.id == self.partner_chell_id:
78                 # Chell can read her own partner record
79                 continue
80             with self.assertRaises(except_orm):
81                 trigger_read = partner.name
82
83         # Do: Chell comments Pigs, ok because he is now in the followers
84         self.mail_group.message_post(cr, self.user_chell_id, self.group_pigs_id, body='I love Pigs')
85         # Do: Chell creates a mail.compose.message record on Pigs, because he uses the wizard
86         compose_id = mail_compose.create(cr, self.user_chell_id,
87             {'subject': 'Subject', 'body': 'Body text', 'partner_ids': []},
88             {'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id})
89         mail_compose.send_mail(cr, self.user_chell_id, [compose_id])
90         # Do: Chell replies to a Pigs message using the composer
91         compose_id = mail_compose.create(cr, self.user_chell_id,
92             {'subject': 'Subject', 'body': 'Body text'},
93             {'default_composition_mode': 'comment', 'default_parent_id': pigs_msg_id})
94         mail_compose.send_mail(cr, self.user_chell_id, [compose_id])
95
96         # Do: Chell browses PigsPortal -> ok because groups security, ko for partners (no read permission)
97         chell_port = self.mail_group.browse(cr, self.user_chell_id, self.group_port_id)
98         trigger_read = chell_port.name
99         for message in chell_port.message_ids:
100             trigger_read = message.subject
101         for partner in chell_port.message_follower_ids:
102             with self.assertRaises(except_orm):
103                 trigger_read = partner.name
104
105     def test_10_mail_invite(self):
106         cr, uid = self.cr, self.uid
107         mail_invite = self.registry('mail.wizard.invite')
108         base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='')
109         # Carine Poilvache, with email, should receive emails for comments and emails
110         partner_carine_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c'})
111
112         # Do: create a mail_wizard_invite, validate it
113         self._init_mock_build_email()
114         context = {'default_res_model': 'mail.group', 'default_res_id': self.group_pigs_id}
115         mail_invite_id = mail_invite.create(cr, uid, {'partner_ids': [(4, partner_carine_id)], 'send_mail': True}, context)
116         mail_invite.add_followers(cr, uid, [mail_invite_id])
117
118         # Test: Pigs followers should contain Admin and Bert
119         group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
120         follower_ids = [follower.id for follower in group_pigs.message_follower_ids]
121         self.assertEqual(set(follower_ids), set([self.partner_admin_id, partner_carine_id]), 'Pigs followers after invite is incorrect')
122
123         # Test: partner must have been prepared for signup
124         partner_carine = self.res_partner.browse(cr, uid, partner_carine_id)
125         self.assertTrue(partner_carine.signup_valid, 'partner has not been prepared for signup')
126         self.assertTrue(base_url in partner_carine.signup_url, 'signup url is incorrect')
127         self.assertTrue(cr.dbname in partner_carine.signup_url, 'signup url is incorrect')
128         self.assertTrue(partner_carine.signup_token in partner_carine.signup_url, 'signup url is incorrect')
129
130         # Test: (pretend to) send email and check subject, body
131         self.assertEqual(len(self._build_email_kwargs_list), 1, 'sent email number incorrect, should be only for Bert')
132         for sent_email in self._build_email_kwargs_list:
133             self.assertEqual(sent_email.get('subject'), 'Invitation to follow Discussion group: Pigs',
134                              'invite: subject of invitation email is incorrect')
135             self.assertIn('Administrator invited you to follow Discussion group document: Pigs', sent_email.get('body'),
136                           'invite: body of invitation email is incorrect')
137             self.assertIn(partner_carine.signup_token, sent_email.get('body'),
138                           'invite: body of invitation email does not contain signup token')
139
140     def test_20_notification_url(self):
141         """ Tests designed to test the URL added in notification emails. """
142         cr, uid, group_pigs = self.cr, self.uid, self.group_pigs
143
144         # Partner data
145         partner_raoul = self.res_partner.browse(cr, uid, self.partner_raoul_id)
146         partner_bert_id = self.res_partner.create(cr, uid, {'name': 'bert'})
147         partner_bert = self.res_partner.browse(cr, uid, partner_bert_id)
148         # Mail data
149         mail_mail_id = self.mail_mail.create(cr, uid, {'state': 'exception'})
150         mail = self.mail_mail.browse(cr, uid, mail_mail_id)
151
152         # Test: link for nobody -> None
153         url = self.mail_mail._get_partner_access_link(cr, uid, mail)
154         self.assertEqual(url, None,
155                         'notification email: mails not send to a specific partner should not have any URL')
156
157         # Test: link for partner -> signup URL
158         url = self.mail_mail._get_partner_access_link(cr, uid, mail, partner=partner_bert)
159         self.assertIn(partner_bert.signup_token, url,
160                         'notification email: mails send to a not-user partner should contain the signup token')
161
162         # Test: link for user -> signin
163         url = self.mail_mail._get_partner_access_link(cr, uid, mail, partner=partner_raoul)
164         self.assertIn('action=mail.action_mail_redirect', url,
165                         'notification email: link should contain the redirect action')
166         self.assertIn('login=%s' % partner_raoul.user_ids[0].login, url,
167                         'notification email: link should contain the user login')
168
169     @mute_logger('openerp.addons.mail.mail_thread', 'openerp.models')
170     def test_21_inbox_redirection(self):
171         """ Tests designed to test the inbox redirection of emails notification URLs. """
172         cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
173         model, act_id = self.ir_model_data.get_object_reference(cr, uid, 'mail', 'action_mail_inbox_feeds')
174         model, port_act_id = self.ir_model_data.get_object_reference(cr, uid, 'portal', 'action_mail_inbox_feeds_portal')
175         # Data: post a message on pigs
176         msg_id = self.group_pigs.message_post(body='My body', partner_ids=[self.partner_bert_id, self.partner_chell_id], type='comment', subtype='mail.mt_comment')
177
178         # No specific parameters -> should redirect to Inbox
179         action = self.mail_thread.message_redirect_action(cr, self.user_raoul_id, {'params': {}})
180         self.assertEqual(action.get('type'), 'ir.actions.client',
181                         'URL redirection: action without parameters should redirect to client action Inbox')
182         self.assertEqual(action.get('id'), act_id,
183                         'URL redirection: action without parameters should redirect to client action Inbox')
184
185         # Bert has read access to Pigs -> should redirect to form view of Pigs
186         action = self.mail_thread.message_redirect_action(cr, self.user_raoul_id, {'params': {'message_id': msg_id}})
187         self.assertEqual(action.get('type'), 'ir.actions.act_window',
188                         'URL redirection: action with message_id for read-accredited user should redirect to Pigs')
189         self.assertEqual(action.get('res_id'), group_pigs.id,
190                         'URL redirection: action with message_id for read-accredited user should redirect to Pigs')
191
192         # Bert has no read access to Pigs -> should redirect to Inbox
193         action = self.mail_thread.message_redirect_action(cr, self.user_bert_id, {'params': {'message_id': msg_id}})
194         self.assertEqual(action.get('type'), 'ir.actions.client',
195                         'URL redirection: action without parameters should redirect to client action Inbox')
196         self.assertEqual(action.get('id'), act_id,
197                         'URL redirection: action without parameters should redirect to client action Inbox')
198
199         # Chell has no read access to pigs -> should redirect to Portal Inbox
200         action = self.mail_thread.message_redirect_action(cr, self.user_chell_id, {'params': {'message_id': msg_id}})
201         self.assertEqual(action.get('type'), 'ir.actions.client',
202                         'URL redirection: action without parameters should redirect to client action Inbox')
203         self.assertEqual(action.get('id'), port_act_id,
204                         'URL redirection: action without parameters should redirect to client action Inbox')
205
206     def test_30_message_read(self):
207         cr, uid, group_port_id = self.cr, self.uid, self.group_port_id
208
209         # Data: custom subtypes
210         mt_group_public_id = self.mail_message_subtype.create(cr, uid, {'name': 'group_public', 'description': 'Group changed'})
211         self.ir_model_data.create(cr, uid, {'name': 'mt_group_public', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_group_public_id})
212         # Data: post messages with various subtypes
213         msg1_id = self.mail_group.message_post(cr, uid, group_port_id, body='Body1', type='comment', subtype='mail.mt_comment')
214         msg2_id = self.mail_group.message_post(cr, uid, group_port_id, body='Body2', type='comment', subtype='mail.mt_group_public')
215         msg3_id = self.mail_group.message_post(cr, uid, group_port_id, body='Body3', type='comment', subtype='mail.mt_comment')
216         msg4_id = self.mail_group.message_post(cr, uid, group_port_id, body='Body4', type='comment')
217         # msg5_id = self.mail_group.message_post(cr, uid, group_port_id, body='Body5', type='notification')
218
219         # Do: Chell search messages: should not see internal notes (comment without subtype)
220         msg_ids = self.mail_message.search(cr, self.user_chell_id, [('model', '=', 'mail.group'), ('res_id', '=', group_port_id)])
221         self.assertEqual(set(msg_ids), set([msg1_id, msg2_id, msg3_id]),
222                         'mail_message: portal user has access to messages he should not read')
223
224         # Do: Chell read messages she can read
225         self.mail_message.read(cr, self.user_chell_id, msg_ids, ['body', 'type', 'subtype_id'])
226
227         # Do: Chell read a message she should not be able to read
228         with self.assertRaises(except_orm):
229             self.mail_message.read(cr, self.user_chell_id, [msg4_id], ['body', 'type', 'subtype_id'])