[IMP] portal: improve the "grant access wizard" usability
Purpose ======= Improve the "grant access wizard" usability, allow to re-invite the partners and grant / access per partner and not in batch. Specifications ============== Add 3 buttons to grant / revoke the access and to re-invite the partner. When the partner has an internal user linked, do not allow to manage him in the view (disable the button) because we do not want to remove the "internal user group" in the wizard, but only the "portal user group". When we revoke the access to a partner, if the corresponding user belong only to the portal group, we archive it instead of deleting it. So if we re-grant the portal access, he will have the same user as before and keep his preference, etc. Task 2381921 closes odoo/odoo#63024 Related: odoo/upgrade#2011 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -2,3 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_tours
|
||||
from . import test_portal_wizard
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.mail.tests.common import MailCommon, mail_new_test_user
|
||||
from odoo.exceptions import UserError, AccessError
|
||||
from odoo.tests.common import users
|
||||
|
||||
|
||||
class TestPortalWizard(MailCommon):
|
||||
def setUp(self):
|
||||
super(TestPortalWizard, self).setUp()
|
||||
|
||||
self.partner = self.env['res.partner'].create({
|
||||
'name': 'Testing Partner',
|
||||
'email': 'testing_partner@example.com',
|
||||
})
|
||||
|
||||
self.public_user = mail_new_test_user(
|
||||
self.env,
|
||||
name='Public user',
|
||||
login='public_user',
|
||||
email='public_user@example.com',
|
||||
groups='base.group_public',
|
||||
)
|
||||
|
||||
self.portal_user = mail_new_test_user(
|
||||
self.env,
|
||||
name='Portal user',
|
||||
login='portal_user',
|
||||
email='portal_user@example.com',
|
||||
groups='base.group_portal',
|
||||
)
|
||||
|
||||
self.internal_user = mail_new_test_user(
|
||||
self.env,
|
||||
name='Internal user',
|
||||
login='internal_user',
|
||||
email='internal_user@example.com',
|
||||
groups='base.group_user',
|
||||
)
|
||||
|
||||
def test_portal_wizard_acl(self):
|
||||
portal_wizard = self.env['portal.wizard'].with_context(active_ids=[self.partner.id]).create({})
|
||||
|
||||
with self.assertRaises(AccessError, msg='Standard users should not be able to open the portal wizard'):
|
||||
self.env['portal.wizard'].with_context(active_ids=[self.partner.id]).with_user(self.user_employee).create({})
|
||||
|
||||
portal_wizard.invalidate_cache()
|
||||
|
||||
with self.assertRaises(AccessError, msg='Standard users should not be able to open the portal wizard'):
|
||||
portal_wizard.with_user(self.user_employee).welcome_message
|
||||
|
||||
portal_wizard.user_ids.invalidate_cache()
|
||||
|
||||
with self.assertRaises(AccessError, msg='Standard users should not be able to open the portal wizard'):
|
||||
portal_wizard.user_ids.with_user(self.user_employee).email
|
||||
|
||||
@users('admin')
|
||||
def test_portal_wizard_partner(self):
|
||||
portal_wizard = self.env['portal.wizard'].with_context(active_ids=[self.partner.id]).create({})
|
||||
|
||||
self.assertEqual(len(portal_wizard.user_ids), 1)
|
||||
|
||||
portal_user = portal_wizard.user_ids
|
||||
|
||||
self.assertFalse(portal_user.user_id.id)
|
||||
self.assertFalse(portal_user.is_portal)
|
||||
self.assertFalse(portal_user.is_internal)
|
||||
|
||||
portal_user.email = 'first_email@example.com'
|
||||
with self.mock_mail_gateway():
|
||||
portal_user.action_grant_access()
|
||||
new_user = portal_user.user_id
|
||||
|
||||
self.assertTrue(new_user.id, 'Must create a new user')
|
||||
self.assertTrue(new_user.has_group('base.group_portal'), 'Must add the group to the user')
|
||||
self.assertEqual(self.partner.email, 'first_email@example.com', 'Must write on the email of the partner')
|
||||
self.assertEqual(new_user.email, 'first_email@example.com', 'Must create the user with the right email')
|
||||
self.assertSentEmail(self.env.user.partner_id, [self.partner])
|
||||
|
||||
@users('admin')
|
||||
def test_portal_wizard_public_user(self):
|
||||
"""Test to grant the access to a public user.
|
||||
|
||||
Should remove the group "base.group_public" and add the group "base.group_portal"
|
||||
"""
|
||||
group_public = self.env.ref('base.group_public')
|
||||
public_partner = self.public_user.partner_id
|
||||
portal_wizard = self.env['portal.wizard'].with_context(active_ids=[public_partner.id]).create({})
|
||||
|
||||
self.assertEqual(len(portal_wizard.user_ids), 1)
|
||||
portal_user = portal_wizard.user_ids
|
||||
|
||||
self.assertEqual(portal_user.user_id, self.public_user)
|
||||
self.assertFalse(portal_user.is_portal)
|
||||
self.assertFalse(portal_user.is_internal)
|
||||
|
||||
portal_user.email = 'new_email@example.com'
|
||||
with self.mock_mail_gateway():
|
||||
portal_user.action_grant_access()
|
||||
|
||||
self.assertTrue(portal_user.is_portal)
|
||||
self.assertFalse(portal_user.is_internal)
|
||||
|
||||
self.assertTrue(self.public_user.has_group('base.group_portal'), 'Must add the group portal')
|
||||
self.assertFalse(self.public_user.has_group('base.group_public'), 'Must remove the group public')
|
||||
self.assertEqual(public_partner.email, 'new_email@example.com', 'Must change the email of the partner')
|
||||
self.assertEqual(self.public_user.email, 'new_email@example.com', 'Must change the email of the user')
|
||||
self.assertSentEmail(self.env.user.partner_id, [public_partner])
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
portal_user.action_revoke_access()
|
||||
|
||||
self.assertEqual(portal_user.user_id, self.public_user, 'Must keep the user even if it is archived')
|
||||
self.assertEqual(group_public, portal_user.user_id.groups_id, 'Must add the group public after removing the portal group')
|
||||
self.assertFalse(portal_user.user_id.active, 'Must have archived the user')
|
||||
self.assertFalse(portal_user.is_portal)
|
||||
self.assertFalse(portal_user.is_internal)
|
||||
self.assertNotSentEmail()
|
||||
|
||||
@users('admin')
|
||||
def test_portal_wizard_internal_user(self):
|
||||
"""Internal user can not be managed from this wizard."""
|
||||
internal_partner = self.internal_user.partner_id
|
||||
portal_wizard = self.env['portal.wizard'].with_context(active_ids=[internal_partner.id]).create({})
|
||||
|
||||
self.assertEqual(len(portal_wizard.user_ids), 1)
|
||||
portal_user = portal_wizard.user_ids
|
||||
|
||||
self.assertTrue(portal_user.is_internal)
|
||||
|
||||
with self.assertRaises(UserError, msg='Should not be able to manage internal user'), self.mock_mail_gateway():
|
||||
portal_wizard.user_ids.action_grant_access()
|
||||
|
||||
self.assertNotSentEmail()
|
||||
|
||||
with self.assertRaises(UserError, msg='Should not be able to manage internal user'):
|
||||
portal_wizard.user_ids.action_revoke_access()
|
||||
|
||||
@users('admin')
|
||||
def test_portal_wizard_error(self):
|
||||
portal_wizard = self.env['portal.wizard'].with_context(active_ids=[self.portal_user.partner_id.id]).create({})
|
||||
|
||||
self.assertEqual(len(portal_wizard.user_ids), 1)
|
||||
portal_user = portal_wizard.user_ids
|
||||
|
||||
self.internal_user.login = 'test_error@example.com'
|
||||
portal_user.email = 'test_error@example.com'
|
||||
|
||||
with self.assertRaises(UserError, msg='Must detect the already used email.'):
|
||||
portal_user.action_revoke_access()
|
||||
|
||||
portal_user.email = 'wrong email format'
|
||||
with self.assertRaises(UserError, msg='Must detect wrong email format.'):
|
||||
portal_user.action_revoke_access()
|
||||
|
||||
portal_wizard = self.env['portal.wizard'].with_context(active_ids=[self.internal_user.partner_id.id]).create({})
|
||||
with self.assertRaises(UserError, msg='Must not be able to change internal user group.'):
|
||||
portal_user.action_revoke_access()
|
||||
|
||||
def test_portal_wizard_multi_company(self):
|
||||
company_1 = self.env['res.company'].search([], limit=1)
|
||||
company_2 = self.env['res.company'].create({'name': 'Company 2'})
|
||||
|
||||
partner_company_2 = self.env['res.partner'].with_company(company_2).create({
|
||||
'name': 'Testing Partner',
|
||||
'email': 'testing_partner@example.com',
|
||||
'company_id': company_2.id,
|
||||
})
|
||||
|
||||
portal_wizard = self.env['portal.wizard'].with_context(active_ids=[partner_company_2.id]).create({})
|
||||
portal_user = portal_wizard.user_ids
|
||||
|
||||
portal_user.with_company(company_1).action_grant_access()
|
||||
|
||||
self.assertEqual(portal_user.user_id.company_id, company_2, 'Must create the user in the same company as the partner.')
|
||||
@@ -4,22 +4,14 @@
|
||||
import logging
|
||||
|
||||
from odoo.tools.translate import _
|
||||
from odoo.tools import email_split
|
||||
from odoo.tools import email_normalize
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import api, fields, models, Command
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# welcome email sent to portal users
|
||||
# (note that calling '_' has no effect except exporting those strings for translation)
|
||||
|
||||
def extract_email(email):
|
||||
""" extract the email address from a user-friendly email address """
|
||||
addresses = email_split(email)
|
||||
return addresses[0] if addresses else ''
|
||||
|
||||
|
||||
class PortalWizard(models.TransientModel):
|
||||
"""
|
||||
@@ -29,34 +21,56 @@ class PortalWizard(models.TransientModel):
|
||||
_name = 'portal.wizard'
|
||||
_description = 'Grant Portal Access'
|
||||
|
||||
def _default_user_ids(self):
|
||||
# for each partner, determine corresponding portal.wizard.user records
|
||||
partner_ids = self.env.context.get('active_ids', [])
|
||||
def _default_partner_ids(self):
|
||||
partner_ids = self.env.context.get('default_partner_ids', []) or self.env.context.get('active_ids', [])
|
||||
contact_ids = set()
|
||||
user_changes = []
|
||||
for partner in self.env['res.partner'].sudo().browse(partner_ids):
|
||||
contact_partners = partner.child_ids.filtered(lambda p: p.type in ('contact', 'other')) | partner
|
||||
for contact in contact_partners:
|
||||
# make sure that each contact appears at most once in the list
|
||||
if contact.id not in contact_ids:
|
||||
contact_ids.add(contact.id)
|
||||
in_portal = False
|
||||
if contact.user_ids:
|
||||
in_portal = self.env.ref('base.group_portal') in contact.user_ids[0].groups_id
|
||||
user_changes.append((0, 0, {
|
||||
'partner_id': contact.id,
|
||||
'email': contact.email,
|
||||
'in_portal': in_portal,
|
||||
}))
|
||||
return user_changes
|
||||
contact_ids |= set(contact_partners.ids)
|
||||
|
||||
user_ids = fields.One2many('portal.wizard.user', 'wizard_id', string='Users',default=_default_user_ids)
|
||||
return [Command.link(contact_id) for contact_id in contact_ids]
|
||||
|
||||
partner_ids = fields.Many2many('res.partner', string='Partners', default=_default_partner_ids)
|
||||
user_ids = fields.One2many('portal.wizard.user', 'wizard_id', string='Users', compute='_compute_user_ids', store=True, readonly=False)
|
||||
welcome_message = fields.Text('Invitation Message', help="This text is included in the email sent to new users of the portal.")
|
||||
|
||||
def action_apply(self):
|
||||
self.ensure_one()
|
||||
self.user_ids.action_apply()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
@api.depends('partner_ids')
|
||||
def _compute_user_ids(self):
|
||||
for portal_wizard in self:
|
||||
portal_wizard.user_ids = [
|
||||
Command.create({
|
||||
'partner_id': partner.id,
|
||||
'email': partner.email,
|
||||
})
|
||||
for partner in portal_wizard.partner_ids
|
||||
]
|
||||
|
||||
@api.model
|
||||
def action_open_wizard(self):
|
||||
"""Create a "portal.wizard" and open the form view.
|
||||
|
||||
We need a server action for that because the one2many "user_ids" records need to
|
||||
exist to be able to execute an a button action on it. If they have no ID, the
|
||||
buttons will be disabled and we won't be able to click on them.
|
||||
|
||||
That's why we need a server action, to create the records and then open the form
|
||||
view on them.
|
||||
"""
|
||||
portal_wizard = self.create({})
|
||||
return portal_wizard._action_open_modal()
|
||||
|
||||
def _action_open_modal(self):
|
||||
"""Allow to keep the wizard modal open after executing the action."""
|
||||
self.refresh()
|
||||
return {
|
||||
'name': _('Portal Access Management'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'portal.wizard',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_id': self.id,
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
|
||||
class PortalWizardUser(models.TransientModel):
|
||||
@@ -70,103 +84,125 @@ class PortalWizardUser(models.TransientModel):
|
||||
wizard_id = fields.Many2one('portal.wizard', string='Wizard', required=True, ondelete='cascade')
|
||||
partner_id = fields.Many2one('res.partner', string='Contact', required=True, readonly=True, ondelete='cascade')
|
||||
email = fields.Char('Email')
|
||||
in_portal = fields.Boolean('In Portal')
|
||||
user_id = fields.Many2one('res.users', string='Login User')
|
||||
|
||||
def get_error_messages(self):
|
||||
emails = []
|
||||
partners_error_empty = self.env['res.partner']
|
||||
partners_error_emails = self.env['res.partner']
|
||||
partners_error_user = self.env['res.partner']
|
||||
partners_error_internal_user = self.env['res.partner']
|
||||
user_id = fields.Many2one('res.users', string='User', compute='_compute_user_id', compute_sudo=True)
|
||||
login_date = fields.Datetime(related='user_id.login_date', string='Latest Authentication')
|
||||
is_portal = fields.Boolean('Is Portal', compute='_compute_group_details')
|
||||
is_internal = fields.Boolean('Is Internal', compute='_compute_group_details')
|
||||
|
||||
for wizard_user in self.with_context(active_test=False).filtered(lambda w: w.in_portal and not w.partner_id.user_ids):
|
||||
email = extract_email(wizard_user.email)
|
||||
if not email:
|
||||
partners_error_empty |= wizard_user.partner_id
|
||||
elif email in emails:
|
||||
partners_error_emails |= wizard_user.partner_id
|
||||
user = self.env['res.users'].sudo().with_context(active_test=False).search([('login', '=ilike', email)])
|
||||
if user:
|
||||
partners_error_user |= wizard_user.partner_id
|
||||
emails.append(email)
|
||||
@api.depends('partner_id')
|
||||
def _compute_user_id(self):
|
||||
for portal_wizard_user in self:
|
||||
user = portal_wizard_user.partner_id.with_context(active_test=False).user_ids
|
||||
portal_wizard_user.user_id = user[0] if user else False
|
||||
|
||||
for wizard_user in self.with_context(active_test=False):
|
||||
if any(u.has_group('base.group_user') for u in wizard_user.sudo().partner_id.user_ids):
|
||||
partners_error_internal_user |= wizard_user.partner_id
|
||||
@api.depends('user_id', 'user_id.groups_id')
|
||||
def _compute_group_details(self):
|
||||
for portal_wizard_user in self:
|
||||
user = portal_wizard_user.user_id
|
||||
|
||||
error_msg = []
|
||||
if partners_error_empty:
|
||||
error_msg.append("%s\n- %s" % (_("Some contacts don't have a valid email: "),
|
||||
'\n- '.join(partners_error_empty.mapped('display_name'))))
|
||||
if partners_error_emails:
|
||||
error_msg.append("%s\n- %s" % (_("Several contacts have the same email: "),
|
||||
'\n- '.join(partners_error_emails.mapped('email'))))
|
||||
if partners_error_user:
|
||||
error_msg.append("%s\n- %s" % (_("Some contacts have the same email as an existing portal user:"),
|
||||
'\n- '.join([p.email_formatted for p in partners_error_user])))
|
||||
if partners_error_internal_user:
|
||||
error_msg.append("%s\n- %s" % (_("Some contacts are already internal users:"),
|
||||
'\n- '.join(partners_error_internal_user.mapped('email'))))
|
||||
if error_msg:
|
||||
error_msg.append(_("To resolve this error, you can: \n"
|
||||
"- Correct the emails of the relevant contacts\n"
|
||||
"- Grant access only to contacts with unique emails"))
|
||||
error_msg[-1] += _("\n- Switch the internal users to portal manually")
|
||||
return error_msg
|
||||
|
||||
def action_apply(self):
|
||||
self.env['res.partner'].check_access_rights('write')
|
||||
""" From selected partners, add corresponding users to chosen portal group. It either granted
|
||||
existing user, or create new one (and add it to the group).
|
||||
"""
|
||||
error_msg = self.get_error_messages()
|
||||
if error_msg:
|
||||
raise UserError("\n\n".join(error_msg))
|
||||
|
||||
for wizard_user in self.sudo().with_context(active_test=False):
|
||||
|
||||
group_portal = self.env.ref('base.group_portal')
|
||||
#Checking if the partner has a linked user
|
||||
user = wizard_user.partner_id.user_ids[0] if wizard_user.partner_id.user_ids else None
|
||||
# update partner email, if a new one was introduced
|
||||
if wizard_user.partner_id.email != wizard_user.email:
|
||||
wizard_user.partner_id.write({'email': wizard_user.email})
|
||||
# add portal group to relative user of selected partners
|
||||
if wizard_user.in_portal:
|
||||
user_portal = None
|
||||
# create a user if necessary, and make sure it is in the portal group
|
||||
if not user:
|
||||
if wizard_user.partner_id.company_id:
|
||||
company_id = wizard_user.partner_id.company_id.id
|
||||
else:
|
||||
company_id = self.env.company.id
|
||||
user_portal = wizard_user.sudo().with_company(company_id)._create_user()
|
||||
else:
|
||||
user_portal = user
|
||||
wizard_user.write({'user_id': user_portal.id})
|
||||
if not wizard_user.user_id.active or group_portal not in wizard_user.user_id.groups_id:
|
||||
wizard_user.user_id.write({'active': True, 'groups_id': [(4, group_portal.id)]})
|
||||
# prepare for the signup process
|
||||
wizard_user.user_id.partner_id.signup_prepare()
|
||||
wizard_user.with_context(active_test=True)._send_email()
|
||||
wizard_user.refresh()
|
||||
if user and user.has_group('base.group_user'):
|
||||
portal_wizard_user.is_internal = True
|
||||
portal_wizard_user.is_portal = False
|
||||
elif user and user.has_group('base.group_portal'):
|
||||
portal_wizard_user.is_internal = False
|
||||
portal_wizard_user.is_portal = True
|
||||
else:
|
||||
# remove the user (if it exists) from the portal group
|
||||
if user and group_portal in user.groups_id:
|
||||
# if user belongs to portal only, deactivate it
|
||||
if len(user.groups_id) <= 1:
|
||||
user.write({'groups_id': [(3, group_portal.id)], 'active': False})
|
||||
else:
|
||||
user.write({'groups_id': [(3, group_portal.id)]})
|
||||
portal_wizard_user.is_internal = False
|
||||
portal_wizard_user.is_portal = False
|
||||
|
||||
def action_grant_access(self):
|
||||
"""Grant the portal access to the partner.
|
||||
|
||||
If the partner has no linked user, we will create a new one in the same company
|
||||
as the partner (or in the current company if not set).
|
||||
|
||||
An invitation email will be sent to the partner.
|
||||
"""
|
||||
self.ensure_one()
|
||||
self._assert_user_email_uniqueness()
|
||||
|
||||
if self.is_portal or self.is_internal:
|
||||
raise UserError(_('The partner "%s" already has the portal access.', self.partner_id.name))
|
||||
|
||||
group_portal = self.env.ref('base.group_portal')
|
||||
group_public = self.env.ref('base.group_public')
|
||||
|
||||
# update partner email, if a new one was introduced
|
||||
if self.partner_id.email != self.email:
|
||||
self.partner_id.write({'email': self.email})
|
||||
|
||||
user_sudo = self.user_id.sudo()
|
||||
|
||||
if not user_sudo:
|
||||
# create a user if necessary and make sure it is in the portal group
|
||||
company = self.partner_id.company_id or self.env.company
|
||||
user_sudo = self.sudo().with_company(company.id)._create_user()
|
||||
|
||||
if not user_sudo.active or not self.is_portal:
|
||||
user_sudo.write({'active': True, 'groups_id': [(4, group_portal.id), (3, group_public.id)]})
|
||||
# prepare for the signup process
|
||||
user_sudo.partner_id.signup_prepare()
|
||||
|
||||
self.with_context(active_test=True)._send_email()
|
||||
|
||||
return self.wizard_id._action_open_modal()
|
||||
|
||||
def action_revoke_access(self):
|
||||
"""Remove the user of the partner from the portal group.
|
||||
|
||||
If the user was only in the portal group, we archive it.
|
||||
"""
|
||||
self.ensure_one()
|
||||
self._assert_user_email_uniqueness()
|
||||
|
||||
if not self.is_portal:
|
||||
raise UserError(_('The partner "%s" has no portal access.', self.partner_id.name))
|
||||
|
||||
group_portal = self.env.ref('base.group_portal')
|
||||
group_public = self.env.ref('base.group_public')
|
||||
|
||||
# update partner email, if a new one was introduced
|
||||
if self.partner_id.email != self.email:
|
||||
self.partner_id.write({'email': self.email})
|
||||
|
||||
# Remove the sign up token, so it can not be used
|
||||
self.partner_id.signup_token = False
|
||||
|
||||
user_sudo = self.user_id.sudo()
|
||||
|
||||
# remove the user from the portal group
|
||||
if user_sudo and user_sudo.has_group('base.group_portal'):
|
||||
# if user belongs to portal only, deactivate it
|
||||
if len(user_sudo.groups_id) <= 1:
|
||||
user_sudo.write({'groups_id': [(3, group_portal.id), (4, group_public.id)], 'active': False})
|
||||
else:
|
||||
user_sudo.write({'groups_id': [(3, group_portal.id), (4, group_public.id)]})
|
||||
|
||||
return self.wizard_id._action_open_modal()
|
||||
|
||||
def action_invite_again(self):
|
||||
"""Re-send the invitation email to the partner."""
|
||||
self.ensure_one()
|
||||
|
||||
if not self.is_portal:
|
||||
raise UserError(_('You should first grant the portal access to the partner "%s".', self.partner_id.name))
|
||||
|
||||
# update partner email, if a new one was introduced
|
||||
if self.partner_id.email != self.email:
|
||||
self.partner_id.write({'email': self.email})
|
||||
|
||||
self.with_context(active_test=True)._send_email()
|
||||
|
||||
return self.wizard_id._action_open_modal()
|
||||
|
||||
def _create_user(self):
|
||||
""" create a new user for wizard_user.partner_id
|
||||
:returns record of res.users
|
||||
"""
|
||||
return self.env['res.users'].with_context(no_reset_password=True)._create_user_from_template({
|
||||
'email': extract_email(self.email),
|
||||
'login': extract_email(self.email),
|
||||
'email': email_normalize(self.email),
|
||||
'login': email_normalize(self.email),
|
||||
'partner_id': self.partner_id.id,
|
||||
'company_id': self.env.company.id,
|
||||
'company_ids': [(6, 0, self.env.company.ids)],
|
||||
@@ -174,21 +210,36 @@ class PortalWizardUser(models.TransientModel):
|
||||
|
||||
def _send_email(self):
|
||||
""" send notification email to a new portal user """
|
||||
if not self.env.user.email:
|
||||
raise UserError(_('You must have an email address in your User Preferences to send emails.'))
|
||||
self.ensure_one()
|
||||
|
||||
# determine subject and body in the portal user's language
|
||||
template = self.env.ref('portal.mail_template_data_portal_welcome')
|
||||
for wizard_line in self:
|
||||
lang = wizard_line.user_id.lang
|
||||
partner = wizard_line.user_id.partner_id
|
||||
if not template:
|
||||
raise UserError(_('The template "Portal: new user" not found for sending email to the portal user.'))
|
||||
|
||||
portal_url = partner.with_context(signup_force_type_in_url='', lang=lang)._get_signup_url_for_action()[partner.id]
|
||||
partner.signup_prepare()
|
||||
lang = self.user_id.sudo().lang
|
||||
partner = self.user_id.sudo().partner_id
|
||||
|
||||
if template:
|
||||
template.with_context(dbname=self._cr.dbname, portal_url=portal_url, lang=lang).send_mail(wizard_line.id, force_send=True)
|
||||
else:
|
||||
_logger.warning("No email template found for sending email to the portal user")
|
||||
portal_url = partner.with_context(signup_force_type_in_url='', lang=lang)._get_signup_url_for_action()[partner.id]
|
||||
partner.signup_prepare()
|
||||
|
||||
template.with_context(dbname=self._cr.dbname, portal_url=portal_url, lang=lang).send_mail(self.id, force_send=True)
|
||||
|
||||
return True
|
||||
|
||||
def _assert_user_email_uniqueness(self):
|
||||
"""Check that the email can be used to create a new user."""
|
||||
self.ensure_one()
|
||||
|
||||
email = email_normalize(self.email)
|
||||
|
||||
if not email:
|
||||
raise UserError(_('The contact "%s" does not have a valid email.', self.partner_id.name))
|
||||
|
||||
user = self.env['res.users'].sudo().with_context(active_test=False).search([
|
||||
('id', '!=', self.user_id.id),
|
||||
('login', '=ilike', email),
|
||||
])
|
||||
|
||||
if user:
|
||||
raise UserError(_('The contact "%s" has the same email has an existing user (%s).', self.partner_id.name, user.name))
|
||||
|
||||
@@ -1,37 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- wizard action on res.partner -->
|
||||
<record id="partner_wizard_action_create_and_open" model="ir.actions.server">
|
||||
<field name="name">Grant portal access</field>
|
||||
<field name="model_id" ref="portal.model_portal_wizard"/>
|
||||
<field name="binding_model_id" ref="base.model_res_partner"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.action_open_wizard()</field>
|
||||
</record>
|
||||
|
||||
<record id="partner_wizard_action" model="ir.actions.act_window">
|
||||
<field name="name">Grant portal access</field>
|
||||
<field name="res_model">portal.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="binding_model_id" ref="base.model_res_partner"/>
|
||||
</record>
|
||||
|
||||
<!-- wizard view -->
|
||||
<record id="wizard_view" model="ir.ui.view">
|
||||
<field name="name">Grant Portal Access</field>
|
||||
<field name="name">Grant portal access</field>
|
||||
<field name="model">portal.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Grant Portal Access">
|
||||
<div>
|
||||
<form string="Portal Access Management">
|
||||
<div class="mb-3">
|
||||
Select which contacts should belong to the portal in the list below.
|
||||
The email address of each selected contact must be valid and unique.
|
||||
If necessary, you can fix any contact's email address directly in the list.
|
||||
</div>
|
||||
<field name="welcome_message"
|
||||
placeholder="This text is included in the email sent to new portal users."
|
||||
class="mb-3"/>
|
||||
<field name="user_ids">
|
||||
<tree string="Contacts" editable="bottom" create="false" delete="false">
|
||||
<field name="partner_id" force_save="1"/>
|
||||
<field name="email"/>
|
||||
<field name="in_portal"/>
|
||||
<field name="email" attrs="{'readonly': [('is_internal', '=', True)]}"/>
|
||||
<field name="login_date"/>
|
||||
<field name="is_portal" invisible="1"/>
|
||||
<field name="is_internal" invisible="1"/>
|
||||
<button string="Grant Access" name="action_grant_access" type="object" class="btn-secondary"
|
||||
attrs="{'invisible': ['|', ('is_portal', '=', True), ('is_internal', '=', True)]}"/>
|
||||
<button string="Revoke Access" name="action_revoke_access" type="object" class="btn-secondary"
|
||||
attrs="{'invisible': ['|', ('is_portal', '=', False), ('is_internal', '=', True)]}"/>
|
||||
<button string="Re-Invite" name="action_invite_again" type="object" class="btn-secondary"
|
||||
attrs="{'invisible': ['|', ('is_portal', '=', False), ('is_internal', '=', True)]}"/>
|
||||
<button string="Internal User" attrs="{'invisible': [('is_internal', '=', False)]}"
|
||||
disabled="True" title="This partner is linked to an internal User and already has access to the Portal."/>
|
||||
</tree>
|
||||
</field>
|
||||
<field name="welcome_message"
|
||||
placeholder="This text is included in the email sent to new portal users."/>
|
||||
<footer>
|
||||
<button string="Apply" name="action_apply" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" />
|
||||
<button string="Close" class="btn-primary" special="save" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user