From 327de507a7e4de8aa70638a717465312a872fb0b Mon Sep 17 00:00:00 2001 From: Kinjal Mehta Date: Tue, 18 Aug 2015 17:33:13 +0530 Subject: [PATCH 1/3] [MOVE] auth_signup: Moved files in related models and views directory. --- addons/auth_oauth/res_users.py | 2 +- addons/auth_signup/__init__.py | 3 +-- addons/auth_signup/__openerp__.py | 8 ++++---- addons/auth_signup/controllers/main.py | 3 ++- addons/auth_signup/{ => data}/auth_signup_data.xml | 0 addons/auth_signup/models/__init__.py | 4 ++++ addons/auth_signup/{ => models}/res_config.py | 0 addons/auth_signup/{ => models}/res_users.py | 1 + ...h_signup_login.xml => auth_signup_login_templates.xml} | 0 .../{res_config.xml => views/res_config_views.xml} | 0 .../{res_users_view.xml => views/res_users_views.xml} | 0 11 files changed, 13 insertions(+), 8 deletions(-) rename addons/auth_signup/{ => data}/auth_signup_data.xml (100%) create mode 100644 addons/auth_signup/models/__init__.py rename addons/auth_signup/{ => models}/res_config.py (100%) rename addons/auth_signup/{ => models}/res_users.py (99%) rename addons/auth_signup/views/{auth_signup_login.xml => auth_signup_login_templates.xml} (100%) rename addons/auth_signup/{res_config.xml => views/res_config_views.xml} (100%) rename addons/auth_signup/{res_users_view.xml => views/res_users_views.xml} (100%) diff --git a/addons/auth_oauth/res_users.py b/addons/auth_oauth/res_users.py index 7b3e1a3a0d5..fa88695a585 100644 --- a/addons/auth_oauth/res_users.py +++ b/addons/auth_oauth/res_users.py @@ -6,7 +6,7 @@ import urllib2 import json import openerp -from openerp.addons.auth_signup.res_users import SignupError +from openerp.addons.auth_signup.models.res_users import SignupError from openerp.osv import osv, fields from openerp import SUPERUSER_ID diff --git a/addons/auth_signup/__init__.py b/addons/auth_signup/__init__.py index 620b7d866b1..ac0011716f6 100644 --- a/addons/auth_signup/__init__.py +++ b/addons/auth_signup/__init__.py @@ -2,5 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import controllers -import res_config -import res_users +import models diff --git a/addons/auth_signup/__openerp__.py b/addons/auth_signup/__openerp__.py index a79d6690cda..150ed6d315d 100644 --- a/addons/auth_signup/__openerp__.py +++ b/addons/auth_signup/__openerp__.py @@ -17,10 +17,10 @@ Allow users to sign up and reset their password 'web', ], 'data': [ - 'auth_signup_data.xml', - 'res_config.xml', - 'res_users_view.xml', - 'views/auth_signup_login.xml', + 'views/res_config_views.xml', + 'views/res_users_views.xml', + 'views/auth_signup_login_templates.xml', + 'data/auth_signup_data.xml', ], 'bootstrap': True, } diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 9035a650809..23f92bddbc5 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. + import logging import werkzeug import openerp -from openerp.addons.auth_signup.res_users import SignupError +from openerp.addons.auth_signup.models.res_users import SignupError from openerp.addons.web.controllers.main import ensure_db from openerp import http from openerp.http import request diff --git a/addons/auth_signup/auth_signup_data.xml b/addons/auth_signup/data/auth_signup_data.xml similarity index 100% rename from addons/auth_signup/auth_signup_data.xml rename to addons/auth_signup/data/auth_signup_data.xml diff --git a/addons/auth_signup/models/__init__.py b/addons/auth_signup/models/__init__.py new file mode 100644 index 00000000000..493fb2dd1bb --- /dev/null +++ b/addons/auth_signup/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +import res_config +import res_users diff --git a/addons/auth_signup/res_config.py b/addons/auth_signup/models/res_config.py similarity index 100% rename from addons/auth_signup/res_config.py rename to addons/auth_signup/models/res_config.py diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/models/res_users.py similarity index 99% rename from addons/auth_signup/res_users.py rename to addons/auth_signup/models/res_users.py index 92ebcb093b0..655b9c803e6 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/models/res_users.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. + from datetime import datetime, timedelta import random from urlparse import urljoin diff --git a/addons/auth_signup/views/auth_signup_login.xml b/addons/auth_signup/views/auth_signup_login_templates.xml similarity index 100% rename from addons/auth_signup/views/auth_signup_login.xml rename to addons/auth_signup/views/auth_signup_login_templates.xml diff --git a/addons/auth_signup/res_config.xml b/addons/auth_signup/views/res_config_views.xml similarity index 100% rename from addons/auth_signup/res_config.xml rename to addons/auth_signup/views/res_config_views.xml diff --git a/addons/auth_signup/res_users_view.xml b/addons/auth_signup/views/res_users_views.xml similarity index 100% rename from addons/auth_signup/res_users_view.xml rename to addons/auth_signup/views/res_users_views.xml From f0db2d72f71e2133f80c6cef0184029ee8e107bf Mon Sep 17 00:00:00 2001 From: Kinjal Mehta Date: Thu, 20 Aug 2015 14:49:05 +0530 Subject: [PATCH 2/3] [IMP] auth_signup: split files. --- addons/auth_signup/models/__init__.py | 1 + addons/auth_signup/models/res_partner.py | 149 +++++++++++++++++++++++ addons/auth_signup/models/res_users.py | 131 -------------------- 3 files changed, 150 insertions(+), 131 deletions(-) create mode 100644 addons/auth_signup/models/res_partner.py diff --git a/addons/auth_signup/models/__init__.py b/addons/auth_signup/models/__init__.py index 493fb2dd1bb..62ce1aa437e 100644 --- a/addons/auth_signup/models/__init__.py +++ b/addons/auth_signup/models/__init__.py @@ -2,3 +2,4 @@ import res_config import res_users +import res_partner diff --git a/addons/auth_signup/models/res_partner.py b/addons/auth_signup/models/res_partner.py new file mode 100644 index 00000000000..8f87b80245b --- /dev/null +++ b/addons/auth_signup/models/res_partner.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import datetime, timedelta +import random +from urlparse import urljoin +import werkzeug + +from openerp.osv import osv, fields +from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, ustr +from openerp.tools.translate import _ + +class SignupError(Exception): + pass + +def random_token(): + # the token has an entropy of about 120 bits (6 bits/char * 20 chars) + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + return ''.join(random.SystemRandom().choice(chars) for i in xrange(20)) + +def now(**kwargs): + dt = datetime.now() + timedelta(**kwargs) + return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + +class res_partner(osv.Model): + _inherit = 'res.partner' + + def _get_signup_valid(self, cr, uid, ids, name, arg, context=None): + dt = now() + res = {} + for partner in self.browse(cr, uid, ids, context): + res[partner.id] = bool(partner.signup_token) and \ + (not partner.signup_expiration or dt <= partner.signup_expiration) + return res + + def _get_signup_url_for_action(self, cr, uid, ids, action=None, view_type=None, menu_id=None, res_id=None, model=None, context=None): + """ generate a signup url for the given partner ids and action, possibly overriding + the url state components (menu_id, id, view_type) """ + if context is None: + context= {} + res = dict.fromkeys(ids, False) + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + for partner in self.browse(cr, uid, ids, context): + # when required, make sure the partner has a valid signup token + if context.get('signup_valid') and not partner.user_ids: + self.signup_prepare(cr, uid, [partner.id], context=context) + + route = 'login' + # the parameters to encode for the query + query = dict(db=cr.dbname) + signup_type = context.get('signup_force_type_in_url', partner.signup_type or '') + if signup_type: + route = 'reset_password' if signup_type == 'reset' else signup_type + + if partner.signup_token and signup_type: + query['token'] = partner.signup_token + elif partner.user_ids: + query['login'] = partner.user_ids[0].login + else: + continue # no signup token, no user, thus no signup url! + + fragment = dict() + if action: + fragment['action'] = action + if view_type: + fragment['view_type'] = view_type + if menu_id: + fragment['menu_id'] = menu_id + if model: + fragment['model'] = model + if res_id: + fragment['id'] = res_id + + if fragment: + query['redirect'] = '/web#' + werkzeug.url_encode(fragment) + + res[partner.id] = urljoin(base_url, "/web/%s?%s" % (route, werkzeug.url_encode(query))) + + return res + + def _get_signup_url(self, cr, uid, ids, name, arg, context=None): + """ proxy for function field towards actual implementation """ + return self._get_signup_url_for_action(cr, uid, ids, context=context) + + _columns = { + 'signup_token': fields.char('Signup Token', copy=False), + 'signup_type': fields.char('Signup Token Type', copy=False), + 'signup_expiration': fields.datetime('Signup Expiration', copy=False), + 'signup_valid': fields.function(_get_signup_valid, type='boolean', string='Signup Token is Valid'), + 'signup_url': fields.function(_get_signup_url, type='char', string='Signup URL'), + } + + def action_signup_prepare(self, cr, uid, ids, context=None): + return self.signup_prepare(cr, uid, ids, context=context) + + def signup_cancel(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'signup_token': False, 'signup_type': False, 'signup_expiration': False}, context=context) + + def signup_prepare(self, cr, uid, ids, signup_type="signup", expiration=False, context=None): + """ 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.browse(cr, uid, ids, context): + if expiration or not partner.signup_valid: + token = random_token() + while self._signup_retrieve_partner(cr, uid, token, context=context): + token = random_token() + partner.write({'signup_token': token, 'signup_type': signup_type, 'signup_expiration': expiration}) + return True + + def _signup_retrieve_partner(self, cr, uid, token, + check_validity=False, raise_exception=False, context=None): + """ 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) + """ + partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context) + if not partner_ids: + if raise_exception: + raise SignupError("Signup token '%s' is not valid" % token) + return False + partner = self.browse(cr, uid, partner_ids[0], context) + if check_validity and not partner.signup_valid: + if raise_exception: + raise SignupError("Signup token '%s' is no longer valid" % token) + return False + return partner + + def signup_retrieve_info(self, cr, uid, token, context=None): + """ 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 + """ + partner = self._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None) + res = {'db': cr.dbname} + if partner.signup_valid: + res['token'] = token + res['name'] = partner.name + if partner.user_ids: + res['login'] = partner.user_ids[0].login + else: + res['email'] = partner.email or '' + return res \ No newline at end of file diff --git a/addons/auth_signup/models/res_users.py b/addons/auth_signup/models/res_users.py index 655b9c803e6..f4cce8863c8 100644 --- a/addons/auth_signup/models/res_users.py +++ b/addons/auth_signup/models/res_users.py @@ -3,7 +3,6 @@ from datetime import datetime, timedelta import random -from urlparse import urljoin import werkzeug from openerp.addons.base.ir.ir_mail_server import MailDeliveryException @@ -25,136 +24,6 @@ def now(**kwargs): dt = datetime.now() + timedelta(**kwargs) return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - -class res_partner(osv.Model): - _inherit = 'res.partner' - - def _get_signup_valid(self, cr, uid, ids, name, arg, context=None): - dt = now() - res = {} - for partner in self.browse(cr, uid, ids, context): - res[partner.id] = bool(partner.signup_token) and \ - (not partner.signup_expiration or dt <= partner.signup_expiration) - return res - - def _get_signup_url_for_action(self, cr, uid, ids, action=None, view_type=None, menu_id=None, res_id=None, model=None, context=None): - """ generate a signup url for the given partner ids and action, possibly overriding - the url state components (menu_id, id, view_type) """ - if context is None: - context= {} - res = dict.fromkeys(ids, False) - base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') - for partner in self.browse(cr, uid, ids, context): - # when required, make sure the partner has a valid signup token - if context.get('signup_valid') and not partner.user_ids: - self.signup_prepare(cr, uid, [partner.id], context=context) - - route = 'login' - # the parameters to encode for the query - query = dict(db=cr.dbname) - signup_type = context.get('signup_force_type_in_url', partner.signup_type or '') - if signup_type: - route = 'reset_password' if signup_type == 'reset' else signup_type - - if partner.signup_token and signup_type: - query['token'] = partner.signup_token - elif partner.user_ids: - query['login'] = partner.user_ids[0].login - else: - continue # no signup token, no user, thus no signup url! - - fragment = dict() - base = '/web#' - if action == '/mail/view': - base = '/mail/view?' - elif action: - fragment['action'] = action - if view_type: - fragment['view_type'] = view_type - if menu_id: - fragment['menu_id'] = menu_id - if model: - fragment['model'] = model - if res_id: - fragment['res_id'] = res_id - - if fragment: - query['redirect'] = base + werkzeug.url_encode(fragment) - - res[partner.id] = urljoin(base_url, "/web/%s?%s" % (route, werkzeug.url_encode(query))) - - return res - - def _get_signup_url(self, cr, uid, ids, name, arg, context=None): - """ proxy for function field towards actual implementation """ - return self._get_signup_url_for_action(cr, uid, ids, context=context) - - _columns = { - 'signup_token': fields.char('Signup Token', copy=False), - 'signup_type': fields.char('Signup Token Type', copy=False), - 'signup_expiration': fields.datetime('Signup Expiration', copy=False), - 'signup_valid': fields.function(_get_signup_valid, type='boolean', string='Signup Token is Valid'), - 'signup_url': fields.function(_get_signup_url, type='char', string='Signup URL'), - } - - def action_signup_prepare(self, cr, uid, ids, context=None): - return self.signup_prepare(cr, uid, ids, context=context) - - def signup_cancel(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'signup_token': False, 'signup_type': False, 'signup_expiration': False}, context=context) - - def signup_prepare(self, cr, uid, ids, signup_type="signup", expiration=False, context=None): - """ 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.browse(cr, uid, ids, context): - if expiration or not partner.signup_valid: - token = random_token() - while self._signup_retrieve_partner(cr, uid, token, context=context): - token = random_token() - partner.write({'signup_token': token, 'signup_type': signup_type, 'signup_expiration': expiration}) - return True - - def _signup_retrieve_partner(self, cr, uid, token, - check_validity=False, raise_exception=False, context=None): - """ 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) - """ - partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context) - if not partner_ids: - if raise_exception: - raise SignupError("Signup token '%s' is not valid" % token) - return False - partner = self.browse(cr, uid, partner_ids[0], context) - if check_validity and not partner.signup_valid: - if raise_exception: - raise SignupError("Signup token '%s' is no longer valid" % token) - return False - return partner - - def signup_retrieve_info(self, cr, uid, token, context=None): - """ 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 - """ - partner = self._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None) - res = {'db': cr.dbname} - if partner.signup_valid: - 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 - class res_users(osv.Model): _inherit = 'res.users' From 416903d1b953459bf1c3a01ffc7c6bb474789724 Mon Sep 17 00:00:00 2001 From: Kinjal Mehta Date: Fri, 21 Aug 2015 16:14:25 +0530 Subject: [PATCH 3/3] [MIG] auth_signup: Migrated into new api. --- addons/auth_oauth/res_users.py | 2 +- addons/auth_signup/__openerp__.py | 4 +- addons/auth_signup/controllers/__init__.py | 2 + addons/auth_signup/controllers/main.py | 37 +++-- addons/auth_signup/data/auth_signup_data.xml | 6 +- addons/auth_signup/models/res_config.py | 41 +++--- addons/auth_signup/models/res_partner.py | 109 ++++++++------- addons/auth_signup/models/res_users.py | 132 ++++++++---------- .../views/auth_signup_login_templates.xml | 7 +- addons/auth_signup/views/res_config_views.xml | 10 +- addons/auth_signup/views/res_users_views.xml | 10 +- 11 files changed, 164 insertions(+), 196 deletions(-) diff --git a/addons/auth_oauth/res_users.py b/addons/auth_oauth/res_users.py index fa88695a585..4d6dcd356c1 100644 --- a/addons/auth_oauth/res_users.py +++ b/addons/auth_oauth/res_users.py @@ -6,7 +6,7 @@ import urllib2 import json import openerp -from openerp.addons.auth_signup.models.res_users import SignupError +from openerp.addons.auth_signup.models.res_partner import SignupError from openerp.osv import osv, fields from openerp import SUPERUSER_ID diff --git a/addons/auth_signup/__openerp__.py b/addons/auth_signup/__openerp__.py index 150ed6d315d..31920e62cac 100644 --- a/addons/auth_signup/__openerp__.py +++ b/addons/auth_signup/__openerp__.py @@ -7,9 +7,7 @@ Allow users to sign up and reset their password =============================================== """, - 'version': '1.0', 'category': 'Authentication', - 'installable': True, 'auto_install': True, 'depends': [ 'base_setup', @@ -17,10 +15,10 @@ Allow users to sign up and reset their password 'web', ], 'data': [ + 'data/auth_signup_data.xml', 'views/res_config_views.xml', 'views/res_users_views.xml', 'views/auth_signup_login_templates.xml', - 'data/auth_signup_data.xml', ], 'bootstrap': True, } diff --git a/addons/auth_signup/controllers/__init__.py b/addons/auth_signup/controllers/__init__.py index 8ee9bae18d9..bbd183e955b 100644 --- a/addons/auth_signup/controllers/__init__.py +++ b/addons/auth_signup/controllers/__init__.py @@ -1 +1,3 @@ +# -*- coding: utf-8 -*- + import main diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 23f92bddbc5..9ebd4e657b5 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -1,19 +1,16 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. - import logging import werkzeug -import openerp -from openerp.addons.auth_signup.models.res_users import SignupError -from openerp.addons.web.controllers.main import ensure_db -from openerp import http -from openerp.http import request -from openerp.tools.translate import _ +from odoo import http, _ +from odoo.addons.auth_signup.models.res_users import SignupError +from odoo.addons.web.controllers.main import ensure_db, Home +from odoo.http import request _logger = logging.getLogger(__name__) -class AuthSignupHome(openerp.addons.web.controllers.main.Home): +class AuthSignupHome(Home): @http.route() def web_login(self, *args, **kw): @@ -56,8 +53,7 @@ class AuthSignupHome(openerp.addons.web.controllers.main.Home): else: login = qcontext.get('login') assert login, "No login provided." - res_users = request.registry.get('res.users') - res_users.reset_password(request.cr, openerp.SUPERUSER_ID, login) + request.env['res.users'].sudo().reset_password(login) qcontext['message'] = _("An email has been sent with credentials to reset your password") except SignupError: qcontext['error'] = _("Could not reset your password") @@ -71,10 +67,10 @@ class AuthSignupHome(openerp.addons.web.controllers.main.Home): def get_auth_signup_config(self): """retrieve the module config (which features are enabled) for the login page""" - icp = request.registry.get('ir.config_parameter') + IrConfigParam = request.env['ir.config_parameter'] return { - 'signup_enabled': icp.get_param(request.cr, openerp.SUPERUSER_ID, 'auth_signup.allow_uninvited') == 'True', - 'reset_password_enabled': icp.get_param(request.cr, openerp.SUPERUSER_ID, 'auth_signup.reset_password') == 'True', + 'signup_enabled': IrConfigParam.sudo().get_param('auth_signup.allow_uninvited') == 'True', + 'reset_password_enabled': IrConfigParam.sudo().get_param('auth_signup.reset_password') == 'True', } def get_auth_signup_qcontext(self): @@ -84,8 +80,7 @@ class AuthSignupHome(openerp.addons.web.controllers.main.Home): if qcontext.get('token'): try: # retrieve the user info (name, login or email) corresponding to a signup token - res_partner = request.registry.get('res.partner') - token_infos = res_partner.signup_retrieve_info(request.cr, openerp.SUPERUSER_ID, qcontext.get('token')) + token_infos = request.env['res.partner'].sudo().signup_retrieve_info(qcontext.get('token')) for k, v in token_infos.items(): qcontext.setdefault(k, v) except: @@ -95,18 +90,18 @@ class AuthSignupHome(openerp.addons.web.controllers.main.Home): def do_signup(self, qcontext): """ Shared helper that creates a res.partner out of a token """ - values = dict((key, qcontext.get(key)) for key in ('login', 'name', 'password')) - assert any([k for k in values.values()]), "The form was not properly filled in." + values = { key: qcontext.get(key) for key in ('login', 'name', 'password') } + assert values.values(), "The form was not properly filled in." assert values.get('password') == qcontext.get('confirm_password'), "Passwords do not match; please retype them." - supported_langs = [lang['code'] for lang in request.registry['res.lang'].search_read(request.cr, openerp.SUPERUSER_ID, [], ['code'])] + supported_langs = [lang['code'] for lang in request.env['res.lang'].sudo().search_read([], ['code'])] if request.lang in supported_langs: values['lang'] = request.lang self._signup_with_values(qcontext.get('token'), values) - request.cr.commit() + request.env.cr.commit() def _signup_with_values(self, token, values): - db, login, password = request.registry['res.users'].signup(request.cr, openerp.SUPERUSER_ID, values, token) - request.cr.commit() # as authenticate will use its own cursor we need to commit the current transaction + db, login, password = request.env['res.users'].sudo().signup(values, token) + request.env.cr.commit() # as authenticate will use its own cursor we need to commit the current transaction uid = request.session.authenticate(db, login, password) if not uid: raise SignupError(_('Authentication Failed.')) diff --git a/addons/auth_signup/data/auth_signup_data.xml b/addons/auth_signup/data/auth_signup_data.xml index 98d1902cfc1..c8ded45916e 100644 --- a/addons/auth_signup/data/auth_signup_data.xml +++ b/addons/auth_signup/data/auth_signup_data.xml @@ -1,5 +1,5 @@ - - + + @@ -94,4 +94,4 @@ - + diff --git a/addons/auth_signup/models/res_config.py b/addons/auth_signup/models/res_config.py index b2852a95b46..bfb89e76d3d 100644 --- a/addons/auth_signup/models/res_config.py +++ b/addons/auth_signup/models/res_config.py @@ -1,34 +1,31 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from openerp.osv import osv, fields -from openerp.tools.safe_eval import safe_eval +from odoo import api, fields, models +from odoo.tools.safe_eval import safe_eval -class base_config_settings(osv.TransientModel): +class BaseConfigSettings(models.TransientModel): _inherit = 'base.config.settings' - _columns = { - 'auth_signup_reset_password': fields.boolean('Enable password reset from Login page', - help="This allows users to trigger a password reset from the Login page."), - 'auth_signup_uninvited': fields.boolean('Allow external users to sign up', - help="If unchecked, only invited users may sign up."), - 'auth_signup_template_user_id': fields.many2one('res.users', - string='Template user for new users created through signup'), - } + auth_signup_reset_password = fields.Boolean(string='Enable password reset from Login page', help="This allows users to trigger a password reset from the Login page.") + auth_signup_uninvited = fields.Boolean(string='Allow external users to sign up', help="If unchecked, only invited users may sign up.") + auth_signup_template_user_id = fields.Many2one('res.users', string='Template user for new users created through signup') - def get_default_auth_signup_template_user_id(self, cr, uid, fields, context=None): - icp = self.pool.get('ir.config_parameter') + @api.model + def get_default_auth_signup_template_user_id(self, fields): + IrConfigParam = self.env['ir.config_parameter'] # we use safe_eval on the result, since the value of the parameter is a nonempty string return { - 'auth_signup_reset_password': safe_eval(icp.get_param(cr, uid, 'auth_signup.reset_password', 'False')), - 'auth_signup_uninvited': safe_eval(icp.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')), - 'auth_signup_template_user_id': safe_eval(icp.get_param(cr, uid, 'auth_signup.template_user_id', 'False')), + 'auth_signup_reset_password': safe_eval(IrConfigParam.get_param('auth_signup.reset_password', 'False')), + 'auth_signup_uninvited': safe_eval(IrConfigParam.get_param('auth_signup.allow_uninvited', 'False')), + 'auth_signup_template_user_id': safe_eval(IrConfigParam.get_param('auth_signup.template_user_id', 'False')), } - def set_auth_signup_template_user_id(self, cr, uid, ids, context=None): - config = self.browse(cr, uid, ids[0], context=context) - icp = self.pool.get('ir.config_parameter') + @api.multi + def set_auth_signup_template_user_id(self): + self.ensure_one() + IrConfigParam = self.env['ir.config_parameter'] # we store the repr of the values, since the value of the parameter is a required string - icp.set_param(cr, uid, 'auth_signup.reset_password', repr(config.auth_signup_reset_password)) - icp.set_param(cr, uid, 'auth_signup.allow_uninvited', repr(config.auth_signup_uninvited)) - icp.set_param(cr, uid, 'auth_signup.template_user_id', repr(config.auth_signup_template_user_id.id)) + IrConfigParam.set_param('auth_signup.reset_password', repr(self.auth_signup_reset_password)) + IrConfigParam.set_param('auth_signup.allow_uninvited', repr(self.auth_signup_uninvited)) + IrConfigParam.set_param('auth_signup.template_user_id', repr(self.auth_signup_template_user_id.id)) diff --git a/addons/auth_signup/models/res_partner.py b/addons/auth_signup/models/res_partner.py index 8f87b80245b..8f20b722167 100644 --- a/addons/auth_signup/models/res_partner.py +++ b/addons/auth_signup/models/res_partner.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from datetime import datetime, timedelta import random -from urlparse import urljoin import werkzeug -from openerp.osv import osv, fields -from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, ustr -from openerp.tools.translate import _ +from datetime import datetime, timedelta +from urlparse import urljoin + +from odoo import api, fields, models, _ + class SignupError(Exception): pass @@ -20,35 +20,48 @@ def random_token(): def now(**kwargs): dt = datetime.now() + timedelta(**kwargs) - return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + return fields.Datetime.to_string(dt) -class res_partner(osv.Model): + +class ResPartner(models.Model): _inherit = 'res.partner' - def _get_signup_valid(self, cr, uid, ids, name, arg, context=None): - dt = now() - res = {} - for partner in self.browse(cr, uid, ids, context): - res[partner.id] = bool(partner.signup_token) and \ - (not partner.signup_expiration or dt <= partner.signup_expiration) - return res + signup_token = fields.Char(copy=False) + signup_type = fields.Char(string='Signup Token Type', copy=False) + signup_expiration = fields.Datetime(copy=False) + 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 _get_signup_url_for_action(self, cr, uid, ids, action=None, view_type=None, menu_id=None, res_id=None, model=None, context=None): + @api.multi + def _compute_signup_valid(self): + dt = now() + for partner in self: + partner.signup_valid = bool(partner.signup_token) and \ + (not partner.signup_expiration or dt <= partner.signup_expiration) + + @api.multi + def _compute_signup_url(self): + """ proxy for function field towards actual implementation """ + result = self._get_signup_url_for_action() + for partner in self: + partner.signup_url = result.get(partner.id, False) + + @api.multi + def _get_signup_url_for_action(self, 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 the url state components (menu_id, id, view_type) """ - if context is None: - context= {} - res = dict.fromkeys(ids, False) - base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') - for partner in self.browse(cr, uid, ids, context): + + res = dict.fromkeys(self.ids, False) + base_url = self.env['ir.config_parameter'].get_param('web.base.url') + for partner in self: # when required, make sure the partner has a valid signup token - if context.get('signup_valid') and not partner.user_ids: - self.signup_prepare(cr, uid, [partner.id], context=context) + if self.env.context.get('signup_valid') and not partner.user_ids: + partner.signup_prepare() route = 'login' # the parameters to encode for the query - query = dict(db=cr.dbname) - signup_type = context.get('signup_force_type_in_url', partner.signup_type or '') + query = dict(db=self.env.cr.dbname) + signup_type = self.env.context.get('signup_force_type_in_url', partner.signup_type or '') if signup_type: route = 'reset_password' if signup_type == 'reset' else signup_type @@ -75,60 +88,50 @@ class res_partner(osv.Model): query['redirect'] = '/web#' + werkzeug.url_encode(fragment) res[partner.id] = urljoin(base_url, "/web/%s?%s" % (route, werkzeug.url_encode(query))) - return res - def _get_signup_url(self, cr, uid, ids, name, arg, context=None): - """ proxy for function field towards actual implementation """ - return self._get_signup_url_for_action(cr, uid, ids, context=context) + @api.multi + def action_signup_prepare(self): + return self.signup_prepare() - _columns = { - 'signup_token': fields.char('Signup Token', copy=False), - 'signup_type': fields.char('Signup Token Type', copy=False), - 'signup_expiration': fields.datetime('Signup Expiration', copy=False), - 'signup_valid': fields.function(_get_signup_valid, type='boolean', string='Signup Token is Valid'), - 'signup_url': fields.function(_get_signup_url, type='char', string='Signup URL'), - } + @api.multi + def signup_cancel(self): + return self.write({'signup_token': False, 'signup_type': False, 'signup_expiration': False}) - def action_signup_prepare(self, cr, uid, ids, context=None): - return self.signup_prepare(cr, uid, ids, context=context) - - def signup_cancel(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'signup_token': False, 'signup_type': False, 'signup_expiration': False}, context=context) - - def signup_prepare(self, cr, uid, ids, signup_type="signup", expiration=False, context=None): + @api.multi + 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.browse(cr, uid, ids, context): + for partner in self: if expiration or not partner.signup_valid: token = random_token() - while self._signup_retrieve_partner(cr, uid, token, context=context): + while self._signup_retrieve_partner(token): token = random_token() partner.write({'signup_token': token, 'signup_type': signup_type, 'signup_expiration': expiration}) return True - def _signup_retrieve_partner(self, cr, uid, token, - check_validity=False, raise_exception=False, context=None): + @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) """ - partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context) - if not partner_ids: + partner = self.search([('signup_token', '=', token)], limit=1) + if not partner: if raise_exception: raise SignupError("Signup token '%s' is not valid" % token) return False - partner = self.browse(cr, uid, partner_ids[0], context) if check_validity and not partner.signup_valid: if raise_exception: raise SignupError("Signup token '%s' is no longer valid" % token) return False return partner - def signup_retrieve_info(self, cr, uid, token, context=None): + @api.model + 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 @@ -137,8 +140,8 @@ class res_partner(osv.Model): - 'login': the user login, if the user already exists - 'email': the partner email, if the user does not exist """ - partner = self._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None) - res = {'db': cr.dbname} + partner = self._signup_retrieve_partner(token, raise_exception=True) + res = {'db': self.env.cr.dbname} if partner.signup_valid: res['token'] = token res['name'] = partner.name @@ -146,4 +149,4 @@ class res_partner(osv.Model): res['login'] = partner.user_ids[0].login else: res['email'] = partner.email or '' - return res \ No newline at end of file + return res diff --git a/addons/auth_signup/models/res_users.py b/addons/auth_signup/models/res_users.py index f4cce8863c8..bb87389b5a0 100644 --- a/addons/auth_signup/models/res_users.py +++ b/addons/auth_signup/models/res_users.py @@ -1,44 +1,30 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from datetime import datetime, timedelta -import random -import werkzeug -from openerp.addons.base.ir.ir_mail_server import MailDeliveryException -from openerp.osv import osv, fields -from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, ustr from ast import literal_eval -from openerp.tools.translate import _ -from openerp.exceptions import UserError -class SignupError(Exception): - pass +from odoo import api, fields, models, _ +from odoo.exceptions import UserError +from odoo.tools.misc import ustr -def random_token(): - # the token has an entropy of about 120 bits (6 bits/char * 20 chars) - chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' - return ''.join(random.SystemRandom().choice(chars) for i in xrange(20)) +from odoo.addons.base.ir.ir_mail_server import MailDeliveryException +from odoo.addons.auth_signup.models.res_partner import SignupError, now -def now(**kwargs): - dt = datetime.now() + timedelta(**kwargs) - return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) -class res_users(osv.Model): +class ResUsers(models.Model): _inherit = 'res.users' - def _get_state(self, cr, uid, ids, name, arg, context=None): - res = {} - for user in self.browse(cr, uid, ids, context): - res[user.id] = ('active' if user.login_date else 'new') - return res + state = fields.Selection(compute='_compute_state', string='Status', + selection=[('new', 'Never Connected'), ('active', 'Activated')]) - _columns = { - 'state': fields.function(_get_state, string='Status', type='selection', - selection=[('new', 'Never Connected'), ('active', 'Connected')]), - } + @api.multi + def _compute_state(self): + for user in self: + user.state = 'active' if user.login_date else 'new' - def signup(self, cr, uid, values, token=None, context=None): + @api.model + def signup(self, values, token=None): """ signup a user, to either: - create a new user (no token), or - create a user for a partner (with token, but no user for partner), or @@ -49,9 +35,7 @@ class res_users(osv.Model): """ if token: # signup with a token: find the corresponding partner id - res_partner = self.pool.get('res.partner') - partner = res_partner._signup_retrieve_partner( - cr, uid, token, check_validity=True, raise_exception=True, context=None) + 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}) @@ -69,7 +53,7 @@ class res_users(osv.Model): values.pop('login', None) values.pop('name', None) partner_user.write(values) - return (cr.dbname, partner_user.login, values.get('password')) + return (self.env.cr.dbname, partner_user.login, values.get('password')) else: # user does not exist: sign up invited user values.update({ @@ -80,23 +64,25 @@ class res_users(osv.Model): if partner.company_id: values['company_id'] = partner.company_id.id values['company_ids'] = [(6, 0, [partner.company_id.id])] - self._signup_create_user(cr, uid, values, context=context) + self._signup_create_user(values) else: # no token, sign up an external user values['email'] = values.get('email') or values.get('login') - self._signup_create_user(cr, uid, values, context=context) + self._signup_create_user(values) - return (cr.dbname, values.get('login'), values.get('password')) + return (self.env.cr.dbname, values.get('login'), values.get('password')) - def _signup_create_user(self, cr, uid, values, context=None): + @api.model + def _signup_create_user(self, values): """ create a new user from the template user """ - ir_config_parameter = self.pool.get('ir.config_parameter') - template_user_id = literal_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id', 'False')) - assert template_user_id and self.exists(cr, uid, template_user_id, context=context), 'Signup: invalid template user' + IrConfigParam = self.env['ir.config_parameter'] + template_user_id = literal_eval(IrConfigParam.get_param('auth_signup.template_user_id', 'False')) + template_user = self.browse(template_user_id) + assert template_user.exists(), 'Signup: invalid template user' # check that uninvited users may sign up if 'partner_id' not in values: - if not literal_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')): + if not literal_eval(IrConfigParam.get_param('auth_signup.allow_uninvited', 'False')): raise SignupError('Signup is not allowed for uninvited users') assert values.get('login'), "Signup: no login given for new user" @@ -104,73 +90,67 @@ class res_users(osv.Model): # create a copy of the template user (attached to a specific partner_id if given) values['active'] = True - context = dict(context or {}, no_reset_password=True) try: - with cr.savepoint(): - return self.copy(cr, uid, template_user_id, values, context=context) + with self.env.cr.savepoint(): + return template_user.with_context(no_reset_password=True).copy(values) except Exception, e: # copy may failed if asked login is not available. raise SignupError(ustr(e)) - def reset_password(self, cr, uid, login, context=None): + def reset_password(self, login): """ retrieve the user corresponding to login (login or email), and reset their password """ - user_ids = self.search(cr, uid, [('login', '=', login)], context=context) - if not user_ids: - user_ids = self.search(cr, uid, [('email', '=', login)], context=context) - if len(user_ids) != 1: + users = self.search([('login', '=', login)]) + if not users: + users = self.search([('email', '=', login)]) + if len(users) != 1: raise Exception(_('Reset password: invalid username or email')) - return self.action_reset_password(cr, uid, user_ids, context=context) + return users.action_reset_password() - def action_reset_password(self, cr, uid, ids, context=None): + @api.multi + def action_reset_password(self): """ create signup token for each user, and send their signup url by email """ # prepare reset password signup - create_mode = bool(context.get('create_user')) - res_partner = self.pool.get('res.partner') - partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context)] + 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) - res_partner.signup_prepare(cr, uid, partner_ids, signup_type="reset", expiration=expiration, context=context) - - context = dict(context or {}) + self.mapped('partner_id').signup_prepare(signup_type="reset", expiration=expiration) # send email to users with their signup url template = False if create_mode: try: - # get_object() raises ValueError if record does not exist - template = self.pool.get('ir.model.data').get_object(cr, uid, 'auth_signup', 'set_password_email') + template = self.env.ref('auth_signup.set_password_email', raise_if_not_found=False) except ValueError: pass - if not bool(template): - template = self.pool.get('ir.model.data').get_object(cr, uid, 'auth_signup', 'reset_password_email') + if not template: + template = self.env.ref('auth_signup.reset_password_email') assert template._name == 'mail.template' - for user in self.browse(cr, uid, ids, context): + for user in self: if not user.email: raise UserError(_("Cannot send email: user %s has no email address.") % user.name) - context['lang'] = user.lang - self.pool.get('mail.template').send_mail(cr, uid, template.id, user.id, force_send=True, raise_exception=True, context=context) + template.with_context(lang=user.lang).send_mail(user.id, force_send=True, raise_exception=True) - def create(self, cr, uid, values, context=None): - if context is None: - context = {} + @api.model + def create(self, values): # overridden to automatically invite user to sign up - user_id = super(res_users, self).create(cr, uid, values, context=context) - user = self.browse(cr, uid, user_id, context=context) - if user.email and not context.get('no_reset_password'): - context = dict(context, create_user=True) + user = super(ResUsers, self).create(values) + if user.email and not self.env.context.get('no_reset_password'): try: - self.action_reset_password(cr, uid, [user.id], context=context) + user.with_context(create_user=True).action_reset_password() except MailDeliveryException: - self.pool.get('res.partner').signup_cancel(cr, uid, [user.partner_id.id], context=context) - return user_id + user.partner_id.with_context(create_user=True).signup_cancel() + return user - def copy(self, cr, uid, id, default=None, context=None): + @api.multi + def copy(self, default=None): + self.ensure_one() + context = self.env.context if not default or not default.get('email'): # avoid sending email to the user we are duplicating - context = dict(context or {}, reset_password=False) - return super(res_users, self).copy(cr, uid, id, default=default, context=context) + context['reset_password'] = False + return super(ResUsers, self.with_context(context)).copy(default=default) diff --git a/addons/auth_signup/views/auth_signup_login_templates.xml b/addons/auth_signup/views/auth_signup_login_templates.xml index a8efc4b3cc1..3af04d123b4 100644 --- a/addons/auth_signup/views/auth_signup_login_templates.xml +++ b/addons/auth_signup/views/auth_signup_login_templates.xml @@ -1,6 +1,5 @@ - - + - - - + diff --git a/addons/auth_signup/views/res_config_views.xml b/addons/auth_signup/views/res_config_views.xml index 598ef3c40f8..66c53ef2254 100644 --- a/addons/auth_signup/views/res_config_views.xml +++ b/addons/auth_signup/views/res_config_views.xml @@ -1,9 +1,8 @@ - - - + + - base.config.settings.signup + base.config.settings.inherit.signup base.config.settings @@ -26,5 +25,4 @@ - - + diff --git a/addons/auth_signup/views/res_users_views.xml b/addons/auth_signup/views/res_users_views.xml index 0d5eeed2178..0a5f43eb8a0 100644 --- a/addons/auth_signup/views/res_users_views.xml +++ b/addons/auth_signup/views/res_users_views.xml @@ -1,9 +1,8 @@ - - - + + - user.form.state + res.users.form.inherit res.users @@ -34,5 +33,4 @@ - - +