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