mirror of
https://github.com/bringout/oca-ocb-security.git
synced 2026-04-22 16:12:09 +02:00
19.0 vanilla
This commit is contained in:
parent
20ddc1b4a3
commit
c0efcc53f5
1162 changed files with 125577 additions and 105287 deletions
|
|
@ -5,7 +5,7 @@ from odoo import models
|
|||
from odoo.http import request
|
||||
|
||||
|
||||
class Http(models.AbstractModel):
|
||||
class IrHttp(models.AbstractModel):
|
||||
_inherit = 'ir.http'
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import werkzeug.urls
|
|||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from odoo import api, exceptions, fields, models, _
|
||||
from odoo.tools import sql
|
||||
from odoo import api, exceptions, fields, models, tools, _
|
||||
|
||||
class SignupError(Exception):
|
||||
pass
|
||||
|
||||
|
|
@ -24,42 +24,16 @@ def now(**kwargs):
|
|||
class ResPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
signup_token = fields.Char(copy=False, groups="base.group_erp_manager", compute='_compute_token', inverse='_inverse_token')
|
||||
signup_type = fields.Char(string='Signup Token Type', copy=False, groups="base.group_erp_manager")
|
||||
signup_expiration = fields.Datetime(copy=False, groups="base.group_erp_manager")
|
||||
signup_valid = fields.Boolean(compute='_compute_signup_valid', string='Signup Token is Valid')
|
||||
signup_url = fields.Char(compute='_compute_signup_url', string='Signup URL')
|
||||
|
||||
def init(self):
|
||||
super().init()
|
||||
if not sql.column_exists(self.env.cr, self._table, "signup_token"):
|
||||
self.env.cr.execute("ALTER TABLE res_partner ADD COLUMN signup_token varchar")
|
||||
|
||||
@api.depends('signup_token', 'signup_expiration')
|
||||
def _compute_signup_valid(self):
|
||||
dt = now()
|
||||
for partner, partner_sudo in zip(self, self.sudo()):
|
||||
partner.signup_valid = bool(partner_sudo.signup_token) and \
|
||||
(not partner_sudo.signup_expiration or dt <= partner_sudo.signup_expiration)
|
||||
|
||||
def _compute_signup_url(self):
|
||||
""" proxy for function field towards actual implementation """
|
||||
def _get_signup_url(self):
|
||||
self.ensure_one()
|
||||
result = self.sudo()._get_signup_url_for_action()
|
||||
for partner in self:
|
||||
if any(u._is_internal() for u in partner.user_ids if u != self.env.user):
|
||||
self.env['res.users'].check_access_rights('write')
|
||||
if any(u.has_group('base.group_portal') for u in partner.user_ids if u != self.env.user):
|
||||
self.env['res.partner'].check_access_rights('write')
|
||||
partner.signup_url = result.get(partner.id, False)
|
||||
|
||||
def _compute_token(self):
|
||||
for partner in self.filtered('id'):
|
||||
self.env.cr.execute('SELECT signup_token FROM res_partner WHERE id=%s', (partner._origin.id,))
|
||||
partner.signup_token = self.env.cr.fetchone()[0]
|
||||
|
||||
def _inverse_token(self):
|
||||
for partner in self.filtered('id'):
|
||||
self.env.cr.execute('UPDATE res_partner SET signup_token = %s WHERE id=%s', (partner.signup_token or None, partner.id))
|
||||
if any(u._is_internal() for u in self.user_ids if u != self.env.user):
|
||||
self.env['res.users'].check_access('write')
|
||||
if any(u._is_portal() for u in self.user_ids if u != self.env.user):
|
||||
self.env['res.partner'].check_access('write')
|
||||
return result.get(self.id, False)
|
||||
|
||||
def _get_signup_url_for_action(self, url=None, action=None, view_type=None, menu_id=None, res_id=None, model=None):
|
||||
""" generate a signup url for the given partner ids and action, possibly overriding
|
||||
|
|
@ -82,18 +56,13 @@ class ResPartner(models.Model):
|
|||
if signup_type:
|
||||
route = 'reset_password' if signup_type == 'reset' else signup_type
|
||||
|
||||
if partner.sudo().signup_token and signup_type:
|
||||
query['token'] = partner.sudo().signup_token
|
||||
elif partner.user_ids:
|
||||
query['login'] = partner.user_ids[0].login
|
||||
else:
|
||||
continue # no signup token, no user, thus no signup url!
|
||||
query['token'] = partner.sudo()._generate_signup_token()
|
||||
|
||||
if url:
|
||||
query['redirect'] = url
|
||||
else:
|
||||
fragment = dict()
|
||||
base = '/web#'
|
||||
base = '/odoo/'
|
||||
if action == '/mail/view':
|
||||
base = '/mail/view?'
|
||||
elif action:
|
||||
|
|
@ -112,9 +81,8 @@ class ResPartner(models.Model):
|
|||
|
||||
signup_url = "/web/%s?%s" % (route, werkzeug.urls.url_encode(query))
|
||||
if not self.env.context.get('relative_url'):
|
||||
signup_url = werkzeug.urls.url_join(base_url, signup_url)
|
||||
signup_url = tools.urls.urljoin(base_url, signup_url)
|
||||
res[partner.id] = signup_url
|
||||
|
||||
return res
|
||||
|
||||
def action_signup_prepare(self):
|
||||
|
|
@ -134,64 +102,100 @@ class ResPartner(models.Model):
|
|||
partner = partner.sudo()
|
||||
if allow_signup and not partner.user_ids:
|
||||
partner.signup_prepare()
|
||||
res[partner.id]['auth_signup_token'] = partner.signup_token
|
||||
res[partner.id]['auth_signup_token'] = partner._generate_signup_token()
|
||||
elif partner.user_ids:
|
||||
res[partner.id]['auth_login'] = partner.user_ids[0].login
|
||||
return res
|
||||
|
||||
def signup_cancel(self):
|
||||
return self.write({'signup_token': False, 'signup_type': False, 'signup_expiration': False})
|
||||
return self.write({'signup_type': None})
|
||||
|
||||
def signup_prepare(self, signup_type="signup", expiration=False):
|
||||
""" generate a new token for the partners with the given validity, if necessary
|
||||
:param expiration: the expiration datetime of the token (string, optional)
|
||||
"""
|
||||
for partner in self:
|
||||
if expiration or not partner.signup_valid:
|
||||
token = random_token()
|
||||
while self._signup_retrieve_partner(token):
|
||||
token = random_token()
|
||||
partner.write({'signup_token': token, 'signup_type': signup_type, 'signup_expiration': expiration})
|
||||
def signup_prepare(self, signup_type="signup"):
|
||||
""" generate a new token for the partners with the given validity, if necessary """
|
||||
self.write({'signup_type': signup_type})
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def _signup_retrieve_partner(self, token, check_validity=False, raise_exception=False):
|
||||
""" find the partner corresponding to a token, and possibly check its validity
|
||||
:param token: the token to resolve
|
||||
:param check_validity: if True, also check validity
|
||||
:param raise_exception: if True, raise exception instead of returning False
|
||||
:return: partner (browse record) or False (if raise_exception is False)
|
||||
|
||||
:param token: the token to resolve
|
||||
:param bool check_validity: if True, also check validity
|
||||
:param bool raise_exception: if True, raise exception instead of returning False
|
||||
:return: partner (browse record) or False (if raise_exception is False)
|
||||
"""
|
||||
self.env.cr.execute("SELECT id FROM res_partner WHERE signup_token = %s AND active", (token,))
|
||||
partner_id = self.env.cr.fetchone()
|
||||
partner = self.browse(partner_id[0]) if partner_id else None
|
||||
partner = self._get_partner_from_token(token)
|
||||
if not partner:
|
||||
if raise_exception:
|
||||
raise exceptions.UserError(_("Signup token '%s' is not valid", token))
|
||||
return False
|
||||
if check_validity and not partner.signup_valid:
|
||||
if raise_exception:
|
||||
raise exceptions.UserError(_("Signup token '%s' is no longer valid", token))
|
||||
return False
|
||||
raise exceptions.UserError(_("Signup token '%s' is not valid or expired", token))
|
||||
return partner
|
||||
|
||||
@api.model
|
||||
def signup_retrieve_info(self, token):
|
||||
def _signup_retrieve_info(self, token):
|
||||
""" retrieve the user info about the token
|
||||
:return: a dictionary with the user information:
|
||||
- 'db': the name of the database
|
||||
- 'token': the token, if token is valid
|
||||
- 'name': the name of the partner, if token is valid
|
||||
- 'login': the user login, if the user already exists
|
||||
- 'email': the partner email, if the user does not exist
|
||||
|
||||
:rtype: dict | None
|
||||
:return: a dictionary with the user information if the token is valid,
|
||||
None otherwise:
|
||||
|
||||
db
|
||||
the name of the database
|
||||
token
|
||||
the token, if token is valid
|
||||
name
|
||||
the name of the partner, if token is valid
|
||||
login
|
||||
the user login, if the user already exists
|
||||
email
|
||||
the partner email, if the user does not exist
|
||||
"""
|
||||
partner = self._signup_retrieve_partner(token, raise_exception=True)
|
||||
partner = self._get_partner_from_token(token)
|
||||
if not partner:
|
||||
return None
|
||||
res = {'db': self.env.cr.dbname}
|
||||
if partner.signup_valid:
|
||||
res['token'] = token
|
||||
res['name'] = partner.name
|
||||
res['token'] = token
|
||||
res['name'] = partner.name
|
||||
if partner.user_ids:
|
||||
res['login'] = partner.user_ids[0].login
|
||||
else:
|
||||
res['email'] = res['login'] = partner.email or ''
|
||||
return res
|
||||
|
||||
def _get_login_date(self):
|
||||
self.ensure_one()
|
||||
users_login_dates = self.user_ids.mapped('login_date')
|
||||
users_login_dates = list(filter(None, users_login_dates)) # remove falsy values
|
||||
if any(users_login_dates):
|
||||
return int(max(map(datetime.timestamp, users_login_dates)))
|
||||
return None
|
||||
|
||||
def _generate_signup_token(self, expiration=None):
|
||||
""" Generate the signup token for the partner in self.
|
||||
|
||||
Assume that :attr:`signup_type` is either ``'signup'`` or ``'reset'``.
|
||||
|
||||
:param expiration: the time in hours before the expiration of the token
|
||||
:return: the signed payload/token that can be used to reset the
|
||||
password/signup.
|
||||
|
||||
Since ``last_login_date`` is part of the payload, this token is
|
||||
invalidated as soon as the user logs in.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if not expiration:
|
||||
if self.signup_type == 'reset':
|
||||
expiration = int(self.env['ir.config_parameter'].get_param("auth_signup.reset_password.validity.hours", 4))
|
||||
else:
|
||||
expiration = int(self.env['ir.config_parameter'].get_param("auth_signup.signup.validity.hours", 144))
|
||||
plist = [self.id, self.user_ids.ids, self._get_login_date(), self.signup_type]
|
||||
payload = tools.hash_sign(self.sudo().env, 'signup', plist, expiration_hours=expiration)
|
||||
return payload
|
||||
|
||||
@api.model
|
||||
def _get_partner_from_token(self, token):
|
||||
if payload := tools.verify_hash_signed(self.sudo().env, 'signup', token):
|
||||
partner_id, user_ids, login_date, signup_type = payload
|
||||
# login_date can be either an int or "None" as a string for signup
|
||||
partner = self.browse(partner_id)
|
||||
if login_date == partner._get_login_date() and partner.user_ids.ids == user_ids and signup_type == partner.browse(partner_id).signup_type:
|
||||
return partner
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -1,52 +1,34 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
|
||||
from ast import literal_eval
|
||||
from collections import defaultdict
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools.misc import ustr
|
||||
from odoo.fields import Domain
|
||||
|
||||
from odoo.addons.base.models.ir_mail_server import MailDeliveryException
|
||||
from odoo.addons.auth_signup.models.res_partner import SignupError, now
|
||||
from odoo.addons.auth_signup.models.res_partner import SignupError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
state = fields.Selection(compute='_compute_state', search='_search_state', string='Status',
|
||||
selection=[('new', 'Never Connected'), ('active', 'Confirmed')])
|
||||
selection=[('new', 'Invited'), ('active', 'Confirmed')])
|
||||
|
||||
def _search_state(self, operator, value):
|
||||
negative = operator in expression.NEGATIVE_TERM_OPERATORS
|
||||
|
||||
# In case we have no value
|
||||
if not value:
|
||||
return expression.TRUE_DOMAIN if negative else expression.FALSE_DOMAIN
|
||||
|
||||
if operator in ['in', 'not in']:
|
||||
if len(value) > 1:
|
||||
return expression.FALSE_DOMAIN if negative else expression.TRUE_DOMAIN
|
||||
if value[0] == 'new':
|
||||
comp = '!=' if negative else '='
|
||||
if value[0] == 'active':
|
||||
comp = '=' if negative else '!='
|
||||
return [('log_ids', comp, False)]
|
||||
|
||||
if operator in ['=', '!=']:
|
||||
# In case we search against anything else than new, we have to invert the operator
|
||||
if value != 'new':
|
||||
operator = expression.TERM_OPERATORS_NEGATION[operator]
|
||||
|
||||
return [('log_ids', operator, False)]
|
||||
|
||||
return expression.TRUE_DOMAIN
|
||||
if operator != 'in':
|
||||
return NotImplemented
|
||||
if len(value) > 1:
|
||||
return Domain.TRUE
|
||||
in_log = 'active' in value
|
||||
return Domain('log_ids', '!=' if in_log else '=', False)
|
||||
|
||||
def _compute_state(self):
|
||||
for user in self:
|
||||
|
|
@ -66,8 +48,7 @@ class ResUsers(models.Model):
|
|||
# signup with a token: find the corresponding partner id
|
||||
partner = self.env['res.partner']._signup_retrieve_partner(token, check_validity=True, raise_exception=True)
|
||||
# invalidate signup token
|
||||
partner.write({'signup_token': False, 'signup_type': False, 'signup_expiration': False})
|
||||
|
||||
partner.write({'signup_type': False})
|
||||
partner_user = partner.user_ids and partner.user_ids[0] or False
|
||||
|
||||
# avoid overwriting existing (presumably correct) values with geolocation data
|
||||
|
|
@ -82,7 +63,7 @@ class ResUsers(models.Model):
|
|||
values.pop('login', None)
|
||||
values.pop('name', None)
|
||||
partner_user.write(values)
|
||||
if not partner_user.login_date:
|
||||
if not partner_user.login_date and partner_user._is_internal():
|
||||
partner_user._notify_inviter()
|
||||
return (partner_user.login, values.get('password'))
|
||||
else:
|
||||
|
|
@ -96,7 +77,6 @@ class ResUsers(models.Model):
|
|||
values['company_id'] = partner.company_id.id
|
||||
values['company_ids'] = [(6, 0, [partner.company_id.id])]
|
||||
partner_user = self._signup_create_user(values)
|
||||
partner_user._notify_inviter()
|
||||
else:
|
||||
# no token, sign up an external user
|
||||
values['email'] = values.get('email') or values.get('login')
|
||||
|
|
@ -120,13 +100,10 @@ class ResUsers(models.Model):
|
|||
|
||||
def _notify_inviter(self):
|
||||
for user in self:
|
||||
invite_partner = user.create_uid.partner_id
|
||||
if invite_partner:
|
||||
# notify invite user that new user is connected
|
||||
self.env['bus.bus']._sendone(invite_partner, 'res.users/connection', {
|
||||
'username': user.name,
|
||||
'partnerId': user.partner_id.id,
|
||||
})
|
||||
# notify invite user that new user is connected
|
||||
user.create_uid._bus_send(
|
||||
"res.users/connection", {"username": user.name, "partnerId": user.partner_id.id}
|
||||
)
|
||||
|
||||
def _create_user_from_template(self, values):
|
||||
template_user_id = literal_eval(self.env['ir.config_parameter'].sudo().get_param('base.template_portal_user_id', 'False'))
|
||||
|
|
@ -146,7 +123,7 @@ class ResUsers(models.Model):
|
|||
return template_user.with_context(no_reset_password=True).copy(values)
|
||||
except Exception as e:
|
||||
# copy may failed if asked login is not available.
|
||||
raise SignupError(ustr(e))
|
||||
raise SignupError(str(e))
|
||||
|
||||
def reset_password(self, login):
|
||||
""" retrieve the user corresponding to login (login or email),
|
||||
|
|
@ -162,29 +139,43 @@ class ResUsers(models.Model):
|
|||
return users.action_reset_password()
|
||||
|
||||
def action_reset_password(self):
|
||||
try:
|
||||
if self.env.context.get('create_user') == 1:
|
||||
return self._action_reset_password(signup_type="signup")
|
||||
else:
|
||||
return self._action_reset_password(signup_type="reset")
|
||||
except MailDeliveryException as mde:
|
||||
if len(mde.args) == 2 and isinstance(mde.args[1], ConnectionRefusedError):
|
||||
raise UserError(_("Could not contact the mail server, please check your outgoing email server configuration")) from mde
|
||||
else:
|
||||
raise UserError(_("There was an error when trying to deliver your Email, please check your configuration")) from mde
|
||||
|
||||
def _action_reset_password(self, signup_type="reset"):
|
||||
""" create signup token for each user, and send their signup url by email """
|
||||
if self.env.context.get('install_mode', False):
|
||||
if self.env.context.get('install_mode') or self.env.context.get('import_file'):
|
||||
return
|
||||
if self.filtered(lambda user: not user.active):
|
||||
raise UserError(_("You cannot perform this action on an archived user."))
|
||||
# prepare reset password signup
|
||||
create_mode = bool(self.env.context.get('create_user'))
|
||||
|
||||
# no time limit for initial invitation, only for reset password
|
||||
expiration = False if create_mode else now(days=+1)
|
||||
|
||||
self.mapped('partner_id').signup_prepare(signup_type="reset", expiration=expiration)
|
||||
self.mapped('partner_id').signup_prepare(signup_type=signup_type)
|
||||
|
||||
# send email to users with their signup url
|
||||
template = False
|
||||
internal_account_created_template = None
|
||||
portal_account_created_template = None
|
||||
if create_mode:
|
||||
try:
|
||||
template = self.env.ref('auth_signup.set_password_email', raise_if_not_found=False)
|
||||
except ValueError:
|
||||
pass
|
||||
if not template:
|
||||
template = self.env.ref('auth_signup.reset_password_email')
|
||||
assert template._name == 'mail.template'
|
||||
if any(user._is_internal() for user in self):
|
||||
internal_account_created_template = self.env.ref('auth_signup.set_password_email', raise_if_not_found=False)
|
||||
if internal_account_created_template and internal_account_created_template._name != 'mail.template':
|
||||
_logger.error("Wrong set password template %r", internal_account_created_template)
|
||||
return
|
||||
|
||||
if any(not user._is_internal() for user in self):
|
||||
portal_account_created_template = self.env.ref('auth_signup.portal_set_password_email', raise_if_not_found=False)
|
||||
if portal_account_created_template and portal_account_created_template._name != 'mail.template':
|
||||
_logger.error("Wrong set password template %r", portal_account_created_template)
|
||||
return
|
||||
|
||||
email_values = {
|
||||
'email_cc': False,
|
||||
|
|
@ -199,36 +190,69 @@ class ResUsers(models.Model):
|
|||
if not user.email:
|
||||
raise UserError(_("Cannot send email: user %s has no email address.", user.name))
|
||||
email_values['email_to'] = user.email
|
||||
# TDE FIXME: make this template technical (qweb)
|
||||
with self.env.cr.savepoint():
|
||||
force_send = not(self.env.context.get('import_file', False))
|
||||
template.send_mail(user.id, force_send=force_send, raise_exception=True, email_values=email_values)
|
||||
_logger.info("Password reset email sent for user <%s> to <%s>", user.login, user.email)
|
||||
with contextlib.closing(self.env.cr.savepoint()):
|
||||
is_internal = user._is_internal()
|
||||
account_created_template = internal_account_created_template if is_internal else portal_account_created_template
|
||||
if account_created_template:
|
||||
account_created_template.send_mail(
|
||||
user.id, force_send=True,
|
||||
raise_exception=True, email_values=email_values)
|
||||
else:
|
||||
user_lang = user.lang or self.env.lang or 'en_US'
|
||||
body = self.env['mail.render.mixin'].with_context(lang=user_lang)._render_template(
|
||||
self.env.ref('auth_signup.reset_password_email'),
|
||||
model='res.users', res_ids=user.ids,
|
||||
engine='qweb_view', options={'post_process': True})[user.id]
|
||||
mail = self.env['mail.mail'].sudo().create({
|
||||
'subject': self.with_context(lang=user_lang).env._('Password reset'),
|
||||
'email_from': user.company_id.email_formatted or user.email_formatted,
|
||||
'body_html': body,
|
||||
**email_values,
|
||||
})
|
||||
mail.send()
|
||||
if signup_type == 'reset':
|
||||
_logger.info("Password reset email sent for user <%s> to <%s>", user.login, user.email)
|
||||
message = _('A reset password link was sent by email')
|
||||
else:
|
||||
_logger.info("Signup email sent for user <%s> to <%s>", user.login, user.email)
|
||||
message = _('A signup link was sent by email')
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': 'Notification',
|
||||
'message': message,
|
||||
'sticky': False
|
||||
}
|
||||
}
|
||||
|
||||
def send_unregistered_user_reminder(self, after_days=5):
|
||||
def send_unregistered_user_reminder(self, *, after_days=5, batch_size=100):
|
||||
email_template = self.env.ref('auth_signup.mail_template_data_unregistered_users', raise_if_not_found=False)
|
||||
if not email_template:
|
||||
_logger.warning("Template 'auth_signup.mail_template_data_unregistered_users' was not found. Cannot send reminder notifications.")
|
||||
self.env['ir.cron']._commit_progress(deactivate=True)
|
||||
return
|
||||
datetime_min = fields.Datetime.today() - relativedelta(days=after_days)
|
||||
datetime_max = datetime_min + relativedelta(hours=23, minutes=59, seconds=59)
|
||||
datetime_max = datetime_min + relativedelta(days=1)
|
||||
|
||||
res_users_with_details = self.env['res.users'].search_read([
|
||||
invited_by_users = self.search_fetch([
|
||||
('share', '=', False),
|
||||
('create_uid.email', '!=', False),
|
||||
('create_date', '>=', datetime_min),
|
||||
('create_date', '<=', datetime_max),
|
||||
('log_ids', '=', False)], ['create_uid', 'name', 'login'])
|
||||
('create_date', '<', datetime_max),
|
||||
('log_ids', '=', False),
|
||||
], ['name', 'login', 'create_uid']).grouped('create_uid')
|
||||
|
||||
# group by invited by
|
||||
invited_users = defaultdict(list)
|
||||
for user in res_users_with_details:
|
||||
invited_users[user.get('create_uid')[0]].append("%s (%s)" % (user.get('name'), user.get('login')))
|
||||
# Do not use progress since we have no way of knowing to whom we have
|
||||
# already sent e-mails.
|
||||
|
||||
# For sending mail to all the invitors about their invited users
|
||||
for user in invited_users:
|
||||
template = email_template.with_context(dbname=self._cr.dbname, invited_users=invited_users[user])
|
||||
template.send_mail(user, email_layout_xmlid='mail.mail_notification_light', force_send=False)
|
||||
for user, invited_users in invited_by_users.items():
|
||||
invited_user_emails = [f"{u.name} ({u.login})" for u in invited_users]
|
||||
template = email_template.with_context(dbname=self.env.cr.dbname, invited_users=invited_user_emails)
|
||||
template.send_mail(user.id, email_layout_xmlid='mail.mail_notification_light', force_send=False)
|
||||
if not self.env['ir.cron']._commit_progress(len(invited_users)):
|
||||
_logger.info("send_unregistered_user_reminder: timeout reached, stopping")
|
||||
break
|
||||
|
||||
@api.model
|
||||
def web_create_users(self, emails):
|
||||
|
|
@ -247,16 +271,25 @@ class ResUsers(models.Model):
|
|||
users_with_email = users.filtered('email')
|
||||
if users_with_email:
|
||||
try:
|
||||
users_with_email.with_context(create_user=True).action_reset_password()
|
||||
users_with_email.with_context(create_user=True)._action_reset_password(signup_type='signup')
|
||||
except MailDeliveryException:
|
||||
users_with_email.partner_id.with_context(create_user=True).signup_cancel()
|
||||
return users
|
||||
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def write(self, vals):
|
||||
if 'active' in vals and not vals['active']:
|
||||
self.partner_id.sudo().signup_cancel()
|
||||
return super().write(vals)
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _ondelete_signup_cancel(self):
|
||||
# Cancel pending partner signup when the user is deleted.
|
||||
for user in self:
|
||||
if user.partner_id:
|
||||
user.partner_id.signup_cancel()
|
||||
|
||||
def copy(self, default=None):
|
||||
self.ensure_one()
|
||||
sup = super(ResUsers, self)
|
||||
if not default or not default.get('email'):
|
||||
# avoid sending email to the user we are duplicating
|
||||
sup = super(ResUsers, self.with_context(no_reset_password=True))
|
||||
return sup.copy(default=default)
|
||||
self = self.with_context(no_reset_password=True)
|
||||
return super().copy(default=default)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue