1# -*- coding: utf-8 -*-
2# Part of Odoo. See LICENSE file for full copyright and licensing details.
3
4import logging
5
6from odoo.tools.translate import _
7from odoo.tools import email_split
8from odoo.exceptions import UserError
9
10from odoo import api, fields, models
11
12
13_logger = logging.getLogger(__name__)
14
15# welcome email sent to portal users
16# (note that calling '_' has no effect except exporting those strings for translation)
17
18def extract_email(email):
19    """ extract the email address from a user-friendly email address """
20    addresses = email_split(email)
21    return addresses[0] if addresses else ''
22
23
24class PortalWizard(models.TransientModel):
25    """
26        A wizard to manage the creation/removal of portal users.
27    """
28
29    _name = 'portal.wizard'
30    _description = 'Grant Portal Access'
31
32    def _default_user_ids(self):
33        # for each partner, determine corresponding portal.wizard.user records
34        partner_ids = self.env.context.get('active_ids', [])
35        contact_ids = set()
36        user_changes = []
37        for partner in self.env['res.partner'].sudo().browse(partner_ids):
38            contact_partners = partner.child_ids.filtered(lambda p: p.type in ('contact', 'other')) | partner
39            for contact in contact_partners:
40                # make sure that each contact appears at most once in the list
41                if contact.id not in contact_ids:
42                    contact_ids.add(contact.id)
43                    in_portal = False
44                    if contact.user_ids:
45                        in_portal = self.env.ref('base.group_portal') in contact.user_ids[0].groups_id
46                    user_changes.append((0, 0, {
47                        'partner_id': contact.id,
48                        'email': contact.email,
49                        'in_portal': in_portal,
50                    }))
51        return user_changes
52
53    user_ids = fields.One2many('portal.wizard.user', 'wizard_id', string='Users',default=_default_user_ids)
54    welcome_message = fields.Text('Invitation Message', help="This text is included in the email sent to new users of the portal.")
55
56    def action_apply(self):
57        self.ensure_one()
58        self.user_ids.action_apply()
59        return {'type': 'ir.actions.act_window_close'}
60
61
62class PortalWizardUser(models.TransientModel):
63    """
64        A model to configure users in the portal wizard.
65    """
66
67    _name = 'portal.wizard.user'
68    _description = 'Portal User Config'
69
70    wizard_id = fields.Many2one('portal.wizard', string='Wizard', required=True, ondelete='cascade')
71    partner_id = fields.Many2one('res.partner', string='Contact', required=True, readonly=True, ondelete='cascade')
72    email = fields.Char('Email')
73    in_portal = fields.Boolean('In Portal')
74    user_id = fields.Many2one('res.users', string='Login User')
75
76    def get_error_messages(self):
77        emails = []
78        partners_error_empty = self.env['res.partner']
79        partners_error_emails = self.env['res.partner']
80        partners_error_user = self.env['res.partner']
81        partners_error_internal_user = self.env['res.partner']
82
83        for wizard_user in self.with_context(active_test=False).filtered(lambda w: w.in_portal and not w.partner_id.user_ids):
84            email = extract_email(wizard_user.email)
85            if not email:
86                partners_error_empty |= wizard_user.partner_id
87            elif email in emails:
88                partners_error_emails |= wizard_user.partner_id
89            user = self.env['res.users'].sudo().with_context(active_test=False).search([('login', '=ilike', email)])
90            if user:
91                partners_error_user |= wizard_user.partner_id
92            emails.append(email)
93
94        for wizard_user in self.with_context(active_test=False):
95            if any(u.has_group('base.group_user') for u in wizard_user.sudo().partner_id.user_ids):
96                partners_error_internal_user |= wizard_user.partner_id
97
98        error_msg = []
99        if partners_error_empty:
100            error_msg.append("%s\n- %s" % (_("Some contacts don't have a valid email: "),
101                                '\n- '.join(partners_error_empty.mapped('display_name'))))
102        if partners_error_emails:
103            error_msg.append("%s\n- %s" % (_("Several contacts have the same email: "),
104                                '\n- '.join(partners_error_emails.mapped('email'))))
105        if partners_error_user:
106            error_msg.append("%s\n- %s" % (_("Some contacts have the same email as an existing portal user:"),
107                                '\n- '.join([p.email_formatted for p in partners_error_user])))
108        if partners_error_internal_user:
109            error_msg.append("%s\n- %s" % (_("Some contacts are already internal users:"),
110                                '\n- '.join(partners_error_internal_user.mapped('email'))))
111        if error_msg:
112            error_msg.append(_("To resolve this error, you can: \n"
113                "- Correct the emails of the relevant contacts\n"
114                "- Grant access only to contacts with unique emails"))
115            error_msg[-1] += _("\n- Switch the internal users to portal manually")
116        return error_msg
117
118    def action_apply(self):
119        self.env['res.partner'].check_access_rights('write')
120        """ From selected partners, add corresponding users to chosen portal group. It either granted
121            existing user, or create new one (and add it to the group).
122        """
123        error_msg = self.get_error_messages()
124        if error_msg:
125            raise UserError("\n\n".join(error_msg))
126
127        for wizard_user in self.sudo().with_context(active_test=False):
128
129            group_portal = self.env.ref('base.group_portal')
130            #Checking if the partner has a linked user
131            user = wizard_user.partner_id.user_ids[0] if wizard_user.partner_id.user_ids else None
132            # update partner email, if a new one was introduced
133            if wizard_user.partner_id.email != wizard_user.email:
134                wizard_user.partner_id.write({'email': wizard_user.email})
135            # add portal group to relative user of selected partners
136            if wizard_user.in_portal:
137                user_portal = None
138                # create a user if necessary, and make sure it is in the portal group
139                if not user:
140                    if wizard_user.partner_id.company_id:
141                        company_id = wizard_user.partner_id.company_id.id
142                    else:
143                        company_id = self.env.company.id
144                    user_portal = wizard_user.sudo().with_company(company_id)._create_user()
145                else:
146                    user_portal = user
147                wizard_user.write({'user_id': user_portal.id})
148                if not wizard_user.user_id.active or group_portal not in wizard_user.user_id.groups_id:
149                    wizard_user.user_id.write({'active': True, 'groups_id': [(4, group_portal.id)]})
150                    # prepare for the signup process
151                    wizard_user.user_id.partner_id.signup_prepare()
152                wizard_user.with_context(active_test=True)._send_email()
153                wizard_user.refresh()
154            else:
155                # remove the user (if it exists) from the portal group
156                if user and group_portal in user.groups_id:
157                    # if user belongs to portal only, deactivate it
158                    if len(user.groups_id) <= 1:
159                        user.write({'groups_id': [(3, group_portal.id)], 'active': False})
160                    else:
161                        user.write({'groups_id': [(3, group_portal.id)]})
162
163    def _create_user(self):
164        """ create a new user for wizard_user.partner_id
165            :returns record of res.users
166        """
167        return self.env['res.users'].with_context(no_reset_password=True)._create_user_from_template({
168            'email': extract_email(self.email),
169            'login': extract_email(self.email),
170            'partner_id': self.partner_id.id,
171            'company_id': self.env.company.id,
172            'company_ids': [(6, 0, self.env.company.ids)],
173        })
174
175    def _send_email(self):
176        """ send notification email to a new portal user """
177        if not self.env.user.email:
178            raise UserError(_('You must have an email address in your User Preferences to send emails.'))
179
180        # determine subject and body in the portal user's language
181        template = self.env.ref('portal.mail_template_data_portal_welcome')
182        for wizard_line in self:
183            lang = wizard_line.user_id.lang
184            partner = wizard_line.user_id.partner_id
185
186            portal_url = partner.with_context(signup_force_type_in_url='', lang=lang)._get_signup_url_for_action()[partner.id]
187            partner.signup_prepare()
188
189            if template:
190                template.with_context(dbname=self._cr.dbname, portal_url=portal_url, lang=lang).send_mail(wizard_line.id, force_send=True)
191            else:
192                _logger.warning("No email template found for sending email to the portal user")
193
194        return True
195