From 7655344ec534467b68e0f9bdd78ebcd46efc29b3 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Thu, 20 Sep 2012 09:45:14 +0200
Subject: [PATCH 01/55] [FIX] auth_signup: fix reference to template user for
signup
bzr revid: rco@openerp.com-20120920074514-en23fuwudyn2zxjx
---
addons/auth_signup/res_users.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 5f6463508b6..ca778d68173 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -14,7 +14,7 @@ class res_users(osv.Model):
# groups (optional)
# sign (for partner_id and groups)
#
- user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth.signup_template_user_id', 0)
+ user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth_signup.template_user_id', 0)
if user_template_id:
self.pool.get('res.users').copy(cr, SUPERUSER_ID, user_template_id, new_user, context=context)
else:
From 7873305eb1050444330e77e99344929c7a5a8891 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 21 Sep 2012 12:42:37 +0200
Subject: [PATCH 02/55] [IMP] auth_signup: define new model and methods for
signup
bzr revid: rco@openerp.com-20120921104237-krjff0k8lvhric8c
---
addons/auth_signup/res_config.py | 4 +-
addons/auth_signup/res_config.xml | 4 +-
addons/auth_signup/res_users.py | 121 +++++++++++++++++++++++++++++-
3 files changed, 126 insertions(+), 3 deletions(-)
diff --git a/addons/auth_signup/res_config.py b/addons/auth_signup/res_config.py
index 8bfe1fc3ddb..36732d439f5 100644
--- a/addons/auth_signup/res_config.py
+++ b/addons/auth_signup/res_config.py
@@ -32,10 +32,12 @@ class base_config_settings(osv.TransientModel):
def get_default_auth_signup_template_user_id(self, cr, uid, fields, context=None):
icp = self.pool.get('ir.config_parameter')
return {
- 'auth_signup_template_user_id': icp.get_param(cr, uid, 'auth_signup.template_user_id', 0) or False
+ 'auth_signup_uninvited': icp.get_param(cr, uid, 'auth_signup.allow_uninvited', False),
+ 'auth_signup_template_user_id': icp.get_param(cr, uid, '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')
+ icp.set_param(cr, uid, 'auth_signup.allow_uninvited', config.auth_signup_uninvited)
icp.set_param(cr, uid, 'auth_signup.template_user_id', config.auth_signup_template_user_id.id)
diff --git a/addons/auth_signup/res_config.xml b/addons/auth_signup/res_config.xml
index c17634ececb..9d3eb4eaa5a 100644
--- a/addons/auth_signup/res_config.xml
+++ b/addons/auth_signup/res_config.xml
@@ -14,7 +14,9 @@
-
+
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index ca778d68173..636763e5ca9 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -1,6 +1,125 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+
import openerp
-from openerp.osv import osv
+from openerp.osv import osv, fields
from openerp import SUPERUSER_ID
+from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
+
+import time
+import random
+import urlparse
+
+def random_token():
+ # the token has an entropy of 120 bits (6 bits/char * 20 chars)
+ chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ return ''.join(random.choice(chars) for i in xrange(20))
+
+def now():
+ return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+
+
+class res_partner(osv.Model):
+ _inherit = 'res.partner'
+ _columns = {
+ 'signup_token': fields.char(size=24, string='Signup Ticket'),
+ 'signup_expiration': fields.datetime(string='Signup Expiration'),
+ }
+
+ def signup_generate_token(self, cr, uid, partner_id, context=None):
+ """ generate a new token for a partner, and return it
+ :param partner_id: the partner id
+ :param expiration: the expiration datetime of the token (string, optional)
+ :return: the token (string)
+ """
+ # generate a unique token
+ token = random_token()
+ while self.signup_retrieve_partner(cr, uid, token, context):
+ token = random_token()
+ self.write(cr, uid, [partner_id], {'signup_token': token, 'signup_expiration': expiration}, context=context)
+ return token
+
+ def signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None):
+ """ find the partner corresponding to a token, and return its partner id or False """
+ partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context)
+ return partner_ids and partner_ids[0] or False
+
+ def signup_get_url(self, cr, uid, partner_id, context):
+ """ determine a url for the partner_id to sign up """
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
+ token = self.browse(cr, uid, partner_id, context).signup_token
+ if not token:
+ token = self.signup_generate_token(cr, uid, partner_id, context=context)
+ return urlparse.urljoin(base_url, '/login?db=%s#action=signup&token=%s' % (cr.dbname, token))
+
+ def signup(self, cr, values, token=None, context=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
+ - change the password of a user (with token, and existing user).
+ :param values: a dictionary with field values
+ :param token: signup token (optional)
+ :return: the uid of the signed up user
+ """
+ # signup the user, then log in (for setting properly the login_date)
+ assert values.get('login') and values.get('password')
+ uid = self._signup_user(cr, values, token, context)
+ return self.login(cr.dbname, values['login'], values['password'])
+
+ def _signup_user(self, cr, values, token=None, context=None):
+ ir_config_parameter = self.pool.get('ir.config_parameter')
+ values.update({'signup_token': False, 'signup_expiration': False})
+
+ if token:
+ # signup with a token: find the corresponding partner id
+ partner_id = self.signup_retrieve_partner(cr, SUPERUSER_ID, token, context=None)
+ if not partner_id:
+ raise Exception('Signup token is not valid')
+ partner = self.browse(cr, SUPERUSER_ID, partner_id, context)
+ if partner.signup_expiration and partner.signup_expiration < now():
+ raise Exception('Signup token is no longer valid')
+ assert values['login'] == partner.email
+
+ # if user exists, modify its password
+ if partner.user_ids:
+ user = partner.user_ids[0]
+ user.write(values)
+ return user.id
+
+ # user does not exist: connect the new user to the partner
+ values.update({'name': partner.name, 'partner_id': partner.id})
+
+ else:
+ # check whether uninvited users may sign up
+ if not ir_config_parameter.get_param(cr, SUPERUSER_ID, 'auth_signup.allow_uninvited', False):
+ raise Exception('Signup is not allowed for uninvited users')
+
+ # create a new user
+ assert values.get('name')
+ values['email'] = values['login']
+ template_user_id = ir_config_parameter.get_param(cr, SUPERUSER_ID, 'auth_signup.template_user_id')
+ assert template_user_id, 'Signup: missing template user'
+ return self.pool.get('res.users').copy(cr, SUPERUSER_ID, template_user_id, data, context=context)
+
+
class res_users(osv.Model):
_inherit = 'res.users'
From d19ea766fa0499f5905c15894cf8e0615c1555d6 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Mon, 24 Sep 2012 10:26:09 +0200
Subject: [PATCH 03/55] [IMP] auth_signup: improve API and implementation of
signup
bzr revid: rco@openerp.com-20120924082609-75dnuzwrlhtvafh4
---
addons/auth_signup/res_users.py | 65 ++++++++++++++++++++-------------
1 file changed, 39 insertions(+), 26 deletions(-)
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 636763e5ca9..f37c547ad8c 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -77,47 +77,60 @@ class res_partner(osv.Model):
- change the password of a user (with token, and existing user).
:param values: a dictionary with field values
:param token: signup token (optional)
- :return: the uid of the signed up user
+ :return: (dbname, login, password) for the signed up user
"""
- # signup the user, then log in (for setting properly the login_date)
assert values.get('login') and values.get('password')
- uid = self._signup_user(cr, values, token, context)
- return self.login(cr.dbname, values['login'], values['password'])
-
- def _signup_user(self, cr, values, token=None, context=None):
- ir_config_parameter = self.pool.get('ir.config_parameter')
- values.update({'signup_token': False, 'signup_expiration': False})
+ result = (cr.dbname, values['login'], values['password'])
if token:
# signup with a token: find the corresponding partner id
- partner_id = self.signup_retrieve_partner(cr, SUPERUSER_ID, token, context=None)
+ partner_id = self.signup_retrieve_partner(cr, uid, token, context=None)
if not partner_id:
raise Exception('Signup token is not valid')
- partner = self.browse(cr, SUPERUSER_ID, partner_id, context)
+ partner = self.browse(cr, uid, partner_id, context)
if partner.signup_expiration and partner.signup_expiration < now():
raise Exception('Signup token is no longer valid')
- assert values['login'] == partner.email
- # if user exists, modify its password
if partner.user_ids:
- user = partner.user_ids[0]
- user.write(values)
- return user.id
+ # user exists, modify its password and clear token
+ partner.user_ids[0].write({
+ 'password': values['password'],
+ 'signup_token': False,
+ 'signup_expiration': False,
+ })
+ else:
+ # user does not exist: sign up invited user
+ self._signup_create_user(cr, uid, {
+ 'name': partner.name,
+ 'login': values['login'],
+ 'password': values['password'],
+ 'email': values['login'],
+ 'partner_id': partner.id,
+ }, token=token, context=context)
- # user does not exist: connect the new user to the partner
- values.update({'name': partner.name, 'partner_id': partner.id})
+ return result
- else:
- # check whether uninvited users may sign up
- if not ir_config_parameter.get_param(cr, SUPERUSER_ID, 'auth_signup.allow_uninvited', False):
- raise Exception('Signup is not allowed for uninvited users')
+ # sign up an external user
+ assert values.get('name'), 'Signup: no name given for new user'
+ self._signup_create_user(cr, uid, {
+ 'name': values['name'],
+ 'login': values['login'],
+ 'password': values['password'],
+ 'email': values['login'],
+ }, context=context)
+ return result
- # create a new user
- assert values.get('name')
- values['email'] = values['login']
- template_user_id = ir_config_parameter.get_param(cr, SUPERUSER_ID, 'auth_signup.template_user_id')
+ def _signup_create_user(self, cr, uid, values, token=None, context=None):
+ """ create a new user from the template user """
+ # check that uninvited users may sign up
+ ir_config_parameter = self.pool.get('ir.config_parameter')
+ if token and not ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', False):
+ raise Exception('Signup is not allowed for uninvited users')
+
+ template_user_id = ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id')
assert template_user_id, 'Signup: missing template user'
- return self.pool.get('res.users').copy(cr, SUPERUSER_ID, template_user_id, data, context=context)
+ values['active'] = True
+ return self.pool.get('res.users').copy(cr, uid, template_user_id, values, context=context)
From aceae3501efeece3366db5a478bf450067e37301 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Tue, 25 Sep 2012 12:40:13 +0200
Subject: [PATCH 04/55] [IMP] auth_signup: extend the login form to allow
signups
bzr revid: rco@openerp.com-20120925104013-f2vfqcjknx7zyo26
---
addons/auth_signup/controllers/main.py | 49 +++++--
addons/auth_signup/res_users.py | 121 ++++++++----------
.../auth_signup/static/src/js/auth_signup.js | 104 ++++++++-------
.../static/src/xml/auth_signup.xml | 43 +++----
4 files changed, 172 insertions(+), 145 deletions(-)
diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py
index 821ef44f459..343c660ff7d 100644
--- a/addons/auth_signup/controllers/main.py
+++ b/addons/auth_signup/controllers/main.py
@@ -1,25 +1,58 @@
-import logging
-
-import werkzeug.urls
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+from openerp import SUPERUSER_ID
from openerp.modules.registry import RegistryManager
from openerp.addons.web.controllers.main import login_and_redirect
import openerp.addons.web.common.http as openerpweb
-from openerp import SUPERUSER_ID
+import werkzeug
+
+import logging
_logger = logging.getLogger(__name__)
-class OpenIDController(openerpweb.Controller):
+class Controller(openerpweb.Controller):
_cp_path = '/auth_signup'
+ @openerpweb.jsonrequest
+ def retrieve(self, req, dbname, token):
+ """ retrieve the user info (name, login or email) corresponding to a signup token """
+ registry = RegistryManager.get(dbname)
+ user_info = None
+ with registry.cursor() as cr:
+ res_partner = registry.get('res.partner')
+ user_info = res_partner.signup_retrieve_info(cr, SUPERUSER_ID, token)
+ user_info.update(db=dbname, token=token)
+ return user_info
+
@openerpweb.httprequest
- def signup(self, req, dbname, name, login, password):
+ def signup(self, req, dbname, token, name, login, password):
+ """ sign up a user (new or existing), and log it in """
url = '/'
registry = RegistryManager.get(dbname)
with registry.cursor() as cr:
try:
- Users = registry.get('res.users')
- credentials = Users.auth_signup(cr, SUPERUSER_ID, name, login, password)
+ res_users = registry.get('res.users')
+ values = {'name': name, 'login': login, 'password': password}
+ credentials = res_users.signup(cr, SUPERUSER_ID, values, token)
cr.commit()
return login_and_redirect(req, *credentials)
except AttributeError:
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index f37c547ad8c..600ed9c000e 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -29,8 +29,8 @@ import random
import urlparse
def random_token():
- # the token has an entropy of 120 bits (6 bits/char * 20 chars)
- chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ # the token has an entropy of about 120 bits (6 bits/char * 20 chars)
+ chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
return ''.join(random.choice(chars) for i in xrange(20))
def now():
@@ -39,12 +39,25 @@ def now():
class res_partner(osv.Model):
_inherit = 'res.partner'
+
+ def signup_get_url(self, cr, uid, partner_ids, name, arg, context=None):
+ """ determine a url for the partner_id to sign up """
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
+ res = {}
+ for partner in self.browse(cr, uid, partner_ids, context):
+ token = partner.signup_token
+ if not token:
+ token = self._signup_generate_token(cr, uid, partner.id, context=context)
+ res[partner.id] = urlparse.urljoin(base_url, '#action=login&db=%s&token=%s' % (cr.dbname, token))
+ return res
+
_columns = {
'signup_token': fields.char(size=24, string='Signup Ticket'),
'signup_expiration': fields.datetime(string='Signup Expiration'),
+ 'signup_url': fields.function(signup_get_url, type='char', string='Signup URL'),
}
- def signup_generate_token(self, cr, uid, partner_id, context=None):
+ def _signup_generate_token(self, cr, uid, partner_id, expiration=False, context=None):
""" generate a new token for a partner, and return it
:param partner_id: the partner id
:param expiration: the expiration datetime of the token (string, optional)
@@ -52,25 +65,45 @@ class res_partner(osv.Model):
"""
# generate a unique token
token = random_token()
- while self.signup_retrieve_partner(cr, uid, token, context):
+ while self._signup_retrieve_partner(cr, uid, token, context=context):
token = random_token()
self.write(cr, uid, [partner_id], {'signup_token': token, 'signup_expiration': expiration}, context=context)
return token
- def signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None):
- """ find the partner corresponding to a token, and return its partner id or False """
+ def _signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None):
+ """ find the partner corresponding to a token, and check its validity
+ :return: partner (browse record) or False (if raise_exception is False)
+ :raise: when token not valid (if raise_exception is True)
+ """
partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context)
- return partner_ids and partner_ids[0] or False
+ if not partner_ids:
+ if raise_exception:
+ raise Exception("Signup token '%s' is not valid" % token)
+ return False
+ partner = self.browse(cr, uid, partner_ids[0], context)
+ if partner.signup_expiration and partner.signup_expiration < now():
+ if raise_exception:
+ raise Exception("Signup token '%s' is no longer valid" % token)
+ return False
+ return partner
- def signup_get_url(self, cr, uid, partner_id, context):
- """ determine a url for the partner_id to sign up """
- base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
- token = self.browse(cr, uid, partner_id, context).signup_token
- if not token:
- token = self.signup_generate_token(cr, uid, partner_id, context=context)
- return urlparse.urljoin(base_url, '/login?db=%s#action=signup&token=%s' % (cr.dbname, token))
+ def signup_retrieve_info(self, cr, uid, token, context=None):
+ """ retrieve the user info about the token
+ :return: either {'name': ..., 'login': ...} if a user exists for that token,
+ or {'name': ..., 'email': ...} otherwise
+ """
+ partner = self._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None)
+ if partner.user_ids:
+ return {'name': partner.name, 'login': partner.user_ids[0].login}
+ else:
+ return {'name': partner.name, 'email': partner.email}
- def signup(self, cr, values, token=None, context=None):
+
+
+class res_users(osv.Model):
+ _inherit = 'res.users'
+
+ def signup(self, cr, uid, values, token=None, context=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
@@ -84,13 +117,8 @@ class res_partner(osv.Model):
if token:
# signup with a token: find the corresponding partner id
- partner_id = self.signup_retrieve_partner(cr, uid, token, context=None)
- if not partner_id:
- raise Exception('Signup token is not valid')
- partner = self.browse(cr, uid, partner_id, context)
- if partner.signup_expiration and partner.signup_expiration < now():
- raise Exception('Signup token is no longer valid')
-
+ res_partner = self.pool.get('res.partner')
+ partner = res_partner._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None)
if partner.user_ids:
# user exists, modify its password and clear token
partner.user_ids[0].write({
@@ -129,51 +157,6 @@ class res_partner(osv.Model):
template_user_id = ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id')
assert template_user_id, 'Signup: missing template user'
- values['active'] = True
- return self.pool.get('res.users').copy(cr, uid, template_user_id, values, context=context)
+ values.update({'active': True, 'signup_token': False, 'signup_expiration': False})
+ return self.copy(cr, uid, template_user_id, values, context=context)
-
-
-class res_users(osv.Model):
- _inherit = 'res.users'
-
- def auth_signup_create(self, cr, uid, new_user, context=None):
- # new_user:
- # login
- # email
- # name (optional)
- # partner_id (optional)
- # groups (optional)
- # sign (for partner_id and groups)
- #
- user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth_signup.template_user_id', 0)
- if user_template_id:
- self.pool.get('res.users').copy(cr, SUPERUSER_ID, user_template_id, new_user, context=context)
- else:
- self.pool.get('res.users').create(cr, SUPERUSER_ID, new_user, context=context)
-
- def auth_signup(self, cr, uid, name, login, password, context=None):
- r = (cr.dbname, login, password)
- res = self.search(cr, uid, [("login", "=", login)])
- if res:
- # Existing user
- user_id = res[0]
- try:
- self.check(cr.dbname, user_id, password)
- # Same password
- except openerp.exceptions.AccessDenied:
- # Different password
- raise
- else:
- # New user
- new_user = {
- 'name': name,
- 'login': login,
- 'user_email': login,
- 'password': password,
- 'active': True,
- }
- self.auth_signup_create(cr, uid, new_user)
- return r
-
-#
diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js
index 2ab13aa7598..bec32ce2803 100644
--- a/addons/auth_signup/static/src/js/auth_signup.js
+++ b/addons/auth_signup/static/src/js/auth_signup.js
@@ -5,56 +5,68 @@ openerp.auth_signup = function(instance) {
instance.web.Login.include({
start: function() {
var self = this;
- this.$('a.oe_signup').click(function() {
- var dbname = self.$("form [name=db]").val();
- self.do_action({
- type: 'ir.actions.client',
- tag: 'auth_signup.signup',
- params: {'dbname': dbname},
- target: 'new',
- name: 'Sign up'
- });
- return true;
- });
- return this._super();
+ var d = this._super();
+ // hide the signup fields in the case of a regular login
+ self.on_change_mode()
+ self.$("form input[name=signup]").click(self.on_change_mode);
+ // in case of a signup, retrieve the user information from the token
+ if (self.params.db && self.params.token) {
+ d = self.rpc("/auth_signup/retrieve", {dbname: self.params.db, token: self.params.token})
+ .done(self.on_token_loaded)
+ .fail(self.on_token_failed);
+ }
+ return d;
},
- });
-
-
- instance.auth_signup.Signup = instance.web.Widget.extend({
- template: 'auth_signup.signup',
- init: function(parent, params) {
- this.params = params;
- return this._super();
+ on_token_loaded: function(result) {
+ // set the name and login of user
+ this.selected_db = result.db;
+ this.on_db_loaded({db_list: [result.db]});
+ this.$("form input[name=signup]").val(result.login ? [] : ["check_signup"]);
+ this.$("form input[name=name]").val(result.name);
+ this.$("form input[name=login]").val(result.login || result.email);
+ this.$("form input[name=password]").val("");
+ this.$("form input[name=confirm_password]").val("");
+ this.on_change_mode();
},
- start: function() {
- var self = this;
- this.$('input[name=password_confirmation]').keyup(function() {
- var v = $(this).val();
- var $b = self.$('button');
- if (_.isEmpty(v) || self.$('input[name=password]').val() === v) {
- $b.removeAttr('disabled');
+ on_token_failed: function(result) {
+ // currently does nothing
+ },
+ on_change_mode: function() {
+ // 'mode' has changed: regular login, sign up, reset password
+ var is_signup = this.$("input[name=signup]:checked").val();
+ this.$(".oe_signup").toggleClass('oe_form_invisible', false && !is_signup);
+ return true;
+ },
+ on_submit: function(ev) {
+ if (ev) {
+ ev.preventDefault();
+ }
+ if (this.params.token || this.$("input[name=signup]:checked").val()) {
+ // signup user (or reset password)
+ var db = this.params.db;
+ var name = this.$("form input[name=name]").val();
+ var login = this.$("form input[name=login]").val();
+ var password = this.$("form input[name=password]").val();
+ var confirm_password = this.$("form input[name=confirm_password]").val();
+
+ if (password && password === confirm_password) {
+ var params = {
+ dbname : db,
+ token: this.params.token,
+ name: name,
+ login: login,
+ password: password,
+ };
+ var url = "/auth_signup/signup?" + $.param(params);
+ window.location = url;
} else {
- $b.attr('disabled', 'disabled');
+ alert('Incorrect password; please retype your password.');
}
- });
-
- this.$('form').submit(function(ev) {
- if(ev) {
- ev.preventDefault();
- }
- var params = {
- dbname : self.params.dbname,
- name: self.$('input[name=name]').val(),
- login: self.$('input[name=email]').val(),
- password: self.$('input[name=password]').val(),
- };
- var url = "/auth_signup/signup?" + $.param(params);
- window.location = url;
- });
- return this._super();
- }
+ } else {
+ // regular login
+ this._super(ev);
+ }
+ },
});
- instance.web.client_actions.add("auth_signup.signup", "instance.auth_signup.Signup");
};
diff --git a/addons/auth_signup/static/src/xml/auth_signup.xml b/addons/auth_signup/static/src/xml/auth_signup.xml
index c2eec846d15..73e26eb6ba8 100644
--- a/addons/auth_signup/static/src/xml/auth_signup.xml
+++ b/addons/auth_signup/static/src/xml/auth_signup.xml
@@ -1,28 +1,27 @@
-
+
-
-
-
- Sign Up
-
+
+
+
+
+ Create a new account
+
+ Name
+
+
+
+
+
+ Username (Email)
+
+
+ Confirm Password
+
+
+
+
-
-
-
-
-
-
-
-
-
From 597919467d4ecedddb2b3e14798c10bdf020f7ea Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Tue, 25 Sep 2012 14:45:57 +0200
Subject: [PATCH 05/55] [IMP] auth_signup: add css
bzr revid: rco@openerp.com-20120925124557-2yka4yu0x7528atl
---
addons/auth_signup/__openerp__.py | 1 +
addons/auth_signup/static/src/css/Makefile | 3 +++
addons/auth_signup/static/src/css/base.css | 4 ++++
addons/auth_signup/static/src/css/base.sass | 7 +++++++
4 files changed, 15 insertions(+)
create mode 100644 addons/auth_signup/static/src/css/Makefile
create mode 100644 addons/auth_signup/static/src/css/base.css
create mode 100644 addons/auth_signup/static/src/css/base.sass
diff --git a/addons/auth_signup/__openerp__.py b/addons/auth_signup/__openerp__.py
index 9cfe89cdc1c..5bb3aab4ec0 100644
--- a/addons/auth_signup/__openerp__.py
+++ b/addons/auth_signup/__openerp__.py
@@ -36,5 +36,6 @@ Allow users to sign up.
'res_config.xml',
],
'js': ['static/src/js/auth_signup.js'],
+ 'css' : ['static/src/css/base.css'],
'qweb': ['static/src/xml/auth_signup.xml'],
}
diff --git a/addons/auth_signup/static/src/css/Makefile b/addons/auth_signup/static/src/css/Makefile
new file mode 100644
index 00000000000..d6b4f4b2fc8
--- /dev/null
+++ b/addons/auth_signup/static/src/css/Makefile
@@ -0,0 +1,3 @@
+base.css: base.sass
+ sass --trace -t expanded base.sass base.css
+
diff --git a/addons/auth_signup/static/src/css/base.css b/addons/auth_signup/static/src/css/base.css
new file mode 100644
index 00000000000..153f27bd129
--- /dev/null
+++ b/addons/auth_signup/static/src/css/base.css
@@ -0,0 +1,4 @@
+@charset "utf-8";
+.openerp .oe_login input[type="checkbox"] {
+ width: 21px !important;
+}
diff --git a/addons/auth_signup/static/src/css/base.sass b/addons/auth_signup/static/src/css/base.sass
new file mode 100644
index 00000000000..87bdb623727
--- /dev/null
+++ b/addons/auth_signup/static/src/css/base.sass
@@ -0,0 +1,7 @@
+@charset "utf-8"
+
+.openerp
+ // Login form
+ .oe_login
+ input[type="checkbox"]
+ width: 21px !important
From e184913f346f891fd74c2c6f1e0f048beb283fbb Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Tue, 25 Sep 2012 14:55:22 +0200
Subject: [PATCH 06/55] [FIX] auth_signup: store and retrieve config parameters
that may be null
bzr revid: rco@openerp.com-20120925125522-2t74fta15yfoesc1
---
addons/auth_signup/res_config.py | 11 +++++++----
addons/auth_signup/res_users.py | 8 +++++---
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/addons/auth_signup/res_config.py b/addons/auth_signup/res_config.py
index 36732d439f5..c69c01127f6 100644
--- a/addons/auth_signup/res_config.py
+++ b/addons/auth_signup/res_config.py
@@ -20,6 +20,7 @@
##############################################################################
from openerp.osv import osv, fields
+from openerp.tools.safe_eval import safe_eval
class base_config_settings(osv.TransientModel):
_inherit = 'base.config.settings'
@@ -31,13 +32,15 @@ class base_config_settings(osv.TransientModel):
def get_default_auth_signup_template_user_id(self, cr, uid, fields, context=None):
icp = self.pool.get('ir.config_parameter')
+ # we use safe_eval on the result, since the value of the parameter is a nonempty string
return {
- 'auth_signup_uninvited': icp.get_param(cr, uid, 'auth_signup.allow_uninvited', False),
- 'auth_signup_template_user_id': icp.get_param(cr, uid, 'auth_signup.template_user_id', 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')),
}
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')
- icp.set_param(cr, uid, 'auth_signup.allow_uninvited', config.auth_signup_uninvited)
- icp.set_param(cr, uid, 'auth_signup.template_user_id', config.auth_signup_template_user_id.id)
+ # we store the repr of the values, since the value of the parameter is a required string
+ 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))
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 600ed9c000e..6956c8e2109 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -23,6 +23,7 @@ import openerp
from openerp.osv import osv, fields
from openerp import SUPERUSER_ID
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
+from openerp.tools.safe_eval import safe_eval
import time
import random
@@ -152,10 +153,11 @@ class res_users(osv.Model):
""" create a new user from the template user """
# check that uninvited users may sign up
ir_config_parameter = self.pool.get('ir.config_parameter')
- if token and not ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', False):
- raise Exception('Signup is not allowed for uninvited users')
+ if not token:
+ if not safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')):
+ raise Exception('Signup is not allowed for uninvited users')
- template_user_id = ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id')
+ template_user_id = safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id', 'False'))
assert template_user_id, 'Signup: missing template user'
values.update({'active': True, 'signup_token': False, 'signup_expiration': False})
return self.copy(cr, uid, template_user_id, values, context=context)
From 9906a2bda10ca089fff55521f3252f4f8b178344 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Tue, 25 Sep 2012 16:34:33 +0200
Subject: [PATCH 07/55] [IMP] auth_signup: improve error message handling in
web client
bzr revid: rco@openerp.com-20120925143433-p4r2gl6t37dw0s17
---
addons/auth_signup/controllers/main.py | 8 +--
.../auth_signup/static/src/js/auth_signup.js | 61 ++++++++++++++-----
2 files changed, 47 insertions(+), 22 deletions(-)
diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py
index 343c660ff7d..6e053715c09 100644
--- a/addons/auth_signup/controllers/main.py
+++ b/addons/auth_signup/controllers/main.py
@@ -55,14 +55,10 @@ class Controller(openerpweb.Controller):
credentials = res_users.signup(cr, SUPERUSER_ID, values, token)
cr.commit()
return login_and_redirect(req, *credentials)
- except AttributeError:
- # auth_signup is not installed
- _logger.exception('attribute error when signup')
- url = "/#action=auth_signup&error=NA" # Not Available
- except Exception:
+ except Exception as e:
# signup error
_logger.exception('error when signup')
- url = "/#action=auth_signup&error=UE" # Unexcpected Error
+ url = "/#action=login&error_message=%s" % werkzeug.urls.url_quote(e.message)
return werkzeug.utils.redirect(url)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js
index bec32ce2803..9c90358d9e3 100644
--- a/addons/auth_signup/static/src/js/auth_signup.js
+++ b/addons/auth_signup/static/src/js/auth_signup.js
@@ -6,9 +6,21 @@ openerp.auth_signup = function(instance) {
start: function() {
var self = this;
var d = this._super();
+
// hide the signup fields in the case of a regular login
self.on_change_mode()
self.$("form input[name=signup]").click(self.on_change_mode);
+
+ // if there is an error message, show it then forget it
+ if (self.params.error_message) {
+ this.$el.addClass('oe_login_invalid');
+ this.$(".oe_login_error_message").text(self.params.error_message);
+ delete self.params.error_message;
+ } else {
+ this.$el.removeClass('oe_login_invalid');
+ this.$(".oe_login_error_message").text("Invalid username or password");
+ }
+
// in case of a signup, retrieve the user information from the token
if (self.params.db && self.params.token) {
d = self.rpc("/auth_signup/retrieve", {dbname: self.params.db, token: self.params.token})
@@ -28,8 +40,14 @@ openerp.auth_signup = function(instance) {
this.$("form input[name=confirm_password]").val("");
this.on_change_mode();
},
- on_token_failed: function(result) {
- // currently does nothing
+ on_token_failed: function(result, ev) {
+ if (ev) {
+ ev.preventDefault();
+ }
+ this.$el.addClass('oe_login_invalid');
+ this.$(".oe_login_error_message").text("Invalid signup token");
+ delete this.params.db;
+ delete this.params.token;
},
on_change_mode: function() {
// 'mode' has changed: regular login, sign up, reset password
@@ -43,25 +61,36 @@ openerp.auth_signup = function(instance) {
}
if (this.params.token || this.$("input[name=signup]:checked").val()) {
// signup user (or reset password)
- var db = this.params.db;
+ var db = this.$("form [name=db]").val();
var name = this.$("form input[name=name]").val();
var login = this.$("form input[name=login]").val();
var password = this.$("form input[name=password]").val();
var confirm_password = this.$("form input[name=confirm_password]").val();
-
- if (password && password === confirm_password) {
- var params = {
- dbname : db,
- token: this.params.token,
- name: name,
- login: login,
- password: password,
- };
- var url = "/auth_signup/signup?" + $.param(params);
- window.location = url;
- } else {
- alert('Incorrect password; please retype your password.');
+ if (!db) {
+ this.do_warn("Login", "No database selected !");
+ return false;
+ } else if (!name) {
+ this.do_warn("Login", "Please enter a name.")
+ return false;
+ } else if (!login) {
+ this.do_warn("Login", "Please enter a username.")
+ return false;
+ } else if (!password || !confirm_password) {
+ this.do_warn("Login", "Please enter a password and confirm it.")
+ return false;
+ } else if (password !== confirm_password) {
+ this.do_warn("Login", "Passwords do not match; please retype them.")
+ return false;
}
+ var params = {
+ dbname : db,
+ token: this.params.token || "",
+ name: name,
+ login: login,
+ password: password,
+ };
+ var url = "/auth_signup/signup?" + $.param(params);
+ window.location = url;
} else {
// regular login
this._super(ev);
From a50dfa668d9de6fe8e054deb24366d99b21e3d96 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Wed, 26 Sep 2012 11:53:53 +0200
Subject: [PATCH 08/55] [IMP] auth_signup: improve error message handling with
method show_error()
bzr revid: rco@openerp.com-20120926095353-7cgcby7biohfx0i2
---
addons/auth_signup/static/src/js/auth_signup.js | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js
index 9c90358d9e3..282221bc87e 100644
--- a/addons/auth_signup/static/src/js/auth_signup.js
+++ b/addons/auth_signup/static/src/js/auth_signup.js
@@ -11,14 +11,10 @@ openerp.auth_signup = function(instance) {
self.on_change_mode()
self.$("form input[name=signup]").click(self.on_change_mode);
- // if there is an error message, show it then forget it
+ // if there is an error message in params, show it then forget it
if (self.params.error_message) {
- this.$el.addClass('oe_login_invalid');
- this.$(".oe_login_error_message").text(self.params.error_message);
+ this.show_error(self.params.error_message);
delete self.params.error_message;
- } else {
- this.$el.removeClass('oe_login_invalid');
- this.$(".oe_login_error_message").text("Invalid username or password");
}
// in case of a signup, retrieve the user information from the token
@@ -44,8 +40,7 @@ openerp.auth_signup = function(instance) {
if (ev) {
ev.preventDefault();
}
- this.$el.addClass('oe_login_invalid');
- this.$(".oe_login_error_message").text("Invalid signup token");
+ this.show_error("Invalid signup token");
delete this.params.db;
delete this.params.token;
},
From 0605295602606a7e25f075f5e6bbfdd446e63dfc Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Wed, 26 Sep 2012 15:02:41 +0200
Subject: [PATCH 09/55] [IMP] auth_signup: improve web look-and-feel
bzr revid: rco@openerp.com-20120926130241-wlzeyzrsr1xwjdcm
---
addons/auth_signup/static/src/css/base.css | 10 ++++--
addons/auth_signup/static/src/css/base.sass | 12 +++++--
.../auth_signup/static/src/js/auth_signup.js | 32 +++++++++++--------
.../static/src/xml/auth_signup.xml | 31 +++++++++---------
4 files changed, 51 insertions(+), 34 deletions(-)
diff --git a/addons/auth_signup/static/src/css/base.css b/addons/auth_signup/static/src/css/base.css
index 153f27bd129..3f97e7a9301 100644
--- a/addons/auth_signup/static/src/css/base.css
+++ b/addons/auth_signup/static/src/css/base.css
@@ -1,4 +1,10 @@
@charset "utf-8";
-.openerp .oe_login input[type="checkbox"] {
- width: 21px !important;
+.openerp .oe_login .oe_signup_show {
+ display: none;
+}
+.openerp .oe_login_signup .oe_signup_show {
+ display: block !important;
+}
+.openerp .oe_login_signup .oe_signup_hide {
+ display: none;
}
diff --git a/addons/auth_signup/static/src/css/base.sass b/addons/auth_signup/static/src/css/base.sass
index 87bdb623727..f97665d192c 100644
--- a/addons/auth_signup/static/src/css/base.sass
+++ b/addons/auth_signup/static/src/css/base.sass
@@ -1,7 +1,13 @@
@charset "utf-8"
.openerp
- // Login form
+ // Regular login form
.oe_login
- input[type="checkbox"]
- width: 21px !important
+ .oe_signup_show
+ display: none
+ // Signup form
+ .oe_login_signup
+ .oe_signup_show
+ display: block !important
+ .oe_signup_hide
+ display: none
diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js
index 282221bc87e..927ee8de0c5 100644
--- a/addons/auth_signup/static/src/js/auth_signup.js
+++ b/addons/auth_signup/static/src/js/auth_signup.js
@@ -7,9 +7,14 @@ openerp.auth_signup = function(instance) {
var self = this;
var d = this._super();
- // hide the signup fields in the case of a regular login
- self.on_change_mode()
- self.$("form input[name=signup]").click(self.on_change_mode);
+ // to switch between the signup and regular login form
+ this.$('a.oe_signup_signup').click(function() {
+ self.$el.addClass("oe_login_signup");
+ });
+ this.$('a.oe_signup_back').click(function() {
+ self.$el.removeClass("oe_login_signup");
+ delete self.params.token;
+ });
// if there is an error message in params, show it then forget it
if (self.params.error_message) {
@@ -26,15 +31,20 @@ openerp.auth_signup = function(instance) {
return d;
},
on_token_loaded: function(result) {
- // set the name and login of user
+ // switch to signup mode
+ this.$el.addClass("oe_login_signup");
+ // select the right the database
this.selected_db = result.db;
this.on_db_loaded({db_list: [result.db]});
- this.$("form input[name=signup]").val(result.login ? [] : ["check_signup"]);
- this.$("form input[name=name]").val(result.name);
- this.$("form input[name=login]").val(result.login || result.email);
+ // set the name and login of user
+ this.$("form input[name=name]").val(result.name).attr("readonly", "readonly");
+ if (result.login) {
+ this.$("form input[name=login]").val(result.login).attr("readonly", "readonly");
+ } else {
+ this.$("form input[name=login]").val(result.email);
+ }
this.$("form input[name=password]").val("");
this.$("form input[name=confirm_password]").val("");
- this.on_change_mode();
},
on_token_failed: function(result, ev) {
if (ev) {
@@ -44,12 +54,6 @@ openerp.auth_signup = function(instance) {
delete this.params.db;
delete this.params.token;
},
- on_change_mode: function() {
- // 'mode' has changed: regular login, sign up, reset password
- var is_signup = this.$("input[name=signup]:checked").val();
- this.$(".oe_signup").toggleClass('oe_form_invisible', false && !is_signup);
- return true;
- },
on_submit: function(ev) {
if (ev) {
ev.preventDefault();
diff --git a/addons/auth_signup/static/src/xml/auth_signup.xml b/addons/auth_signup/static/src/xml/auth_signup.xml
index 73e26eb6ba8..e63bd5863b0 100644
--- a/addons/auth_signup/static/src/xml/auth_signup.xml
+++ b/addons/auth_signup/static/src/xml/auth_signup.xml
@@ -3,24 +3,25 @@
-
-
-
- Create a new account
-
- Name
-
-
-
+
+ Name
+
- Username (Email)
+ Username
+ Username (Email)
-
- Confirm Password
-
-
-
+
+ Confirm Password
+
+
+
+ Log in
+ Sign in
+
+
+ Sign Up
+ Back to Login
From 479dfb7ea079184c60dc39929c68ab37ada639e1 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Wed, 26 Sep 2012 17:04:59 +0200
Subject: [PATCH 10/55] [FIX] auth_signup: fix the creation of a user attached
to a partner from a user template
bzr revid: rco@openerp.com-20120926150459-9degade1ra9sk0ds
---
addons/auth_signup/res_users.py | 44 +++++++++++++++++++--------------
1 file changed, 26 insertions(+), 18 deletions(-)
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 6956c8e2109..4d5bdc28b8f 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -97,7 +97,7 @@ class res_partner(osv.Model):
if partner.user_ids:
return {'name': partner.name, 'login': partner.user_ids[0].login}
else:
- return {'name': partner.name, 'email': partner.email}
+ return {'name': partner.name, 'email': partner.email or ''}
@@ -120,23 +120,19 @@ class res_users(osv.Model):
# 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, raise_exception=True, context=None)
+ # invalidate signup token
+ partner.write({'signup_token': False, 'signup_expiration': False})
if partner.user_ids:
- # user exists, modify its password and clear token
- partner.user_ids[0].write({
- 'password': values['password'],
- 'signup_token': False,
- 'signup_expiration': False,
- })
+ # user exists, modify its password
+ partner.user_ids[0].write({'password': values['password']})
else:
# user does not exist: sign up invited user
self._signup_create_user(cr, uid, {
- 'name': partner.name,
'login': values['login'],
'password': values['password'],
'email': values['login'],
'partner_id': partner.id,
- }, token=token, context=context)
-
+ }, context=context)
return result
# sign up an external user
@@ -149,16 +145,28 @@ class res_users(osv.Model):
}, context=context)
return result
- def _signup_create_user(self, cr, uid, values, token=None, context=None):
+ def _signup_create_user(self, cr, uid, values, context=None):
""" create a new user from the template user """
- # check that uninvited users may sign up
ir_config_parameter = self.pool.get('ir.config_parameter')
- if not token:
+ template_user_id = safe_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'
+
+ values['active'] = True
+ if values.get('partner_id'):
+ # create a copy of the template user attached to values['partner_id']
+ # note: we do not include 'partner_id' here, as copy() does not handle it correctly
+ safe_values = {'login': values['login'], 'password': values['password']}
+ user_id = self.copy(cr, uid, template_user_id, safe_values, context=context)
+ # problem: the res.partner part of the template user has been duplicated
+ # solution: unlink it, and replace it by values['partner_id']
+ user = self.browse(cr, uid, user_id, context=context)
+ partner = user.partner_id
+ user.write(values)
+ partner.unlink()
+ else:
+ # check that uninvited users may sign up
if not safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')):
raise Exception('Signup is not allowed for uninvited users')
+ user_id = self.copy(cr, uid, template_user_id, values, context=context)
- template_user_id = safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id', 'False'))
- assert template_user_id, 'Signup: missing template user'
- values.update({'active': True, 'signup_token': False, 'signup_expiration': False})
- return self.copy(cr, uid, template_user_id, values, context=context)
-
+ return user_id
From b80d84aafc7f4d543247d22d445e71752272f925 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Thu, 27 Sep 2012 10:27:04 +0200
Subject: [PATCH 11/55] [IMP] auth_signup: remove temporary function field
signup_url, and clean methods
bzr revid: rco@openerp.com-20120927082704-wtuvir9tm2pgsvnq
---
addons/auth_signup/res_users.py | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 4d5bdc28b8f..333686d4ca5 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -41,24 +41,21 @@ def now():
class res_partner(osv.Model):
_inherit = 'res.partner'
- def signup_get_url(self, cr, uid, partner_ids, name, arg, context=None):
- """ determine a url for the partner_id to sign up """
- base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
- res = {}
- for partner in self.browse(cr, uid, partner_ids, context):
- token = partner.signup_token
- if not token:
- token = self._signup_generate_token(cr, uid, partner.id, context=context)
- res[partner.id] = urlparse.urljoin(base_url, '#action=login&db=%s&token=%s' % (cr.dbname, token))
- return res
-
_columns = {
'signup_token': fields.char(size=24, string='Signup Ticket'),
'signup_expiration': fields.datetime(string='Signup Expiration'),
- 'signup_url': fields.function(signup_get_url, type='char', string='Signup URL'),
}
- def _signup_generate_token(self, cr, uid, partner_id, expiration=False, context=None):
+ def signup_get_url(self, cr, uid, partner_id, context=None):
+ """ determine a signup url for the given partner_id """
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
+ partner = self.browse(cr, uid, partner_id, context)
+ token = partner.signup_token
+ if not token:
+ token = self.signup_generate_token(cr, uid, partner.id, context=context)
+ return urlparse.urljoin(base_url, '#action=login&db=%s&token=%s' % (cr.dbname, token))
+
+ def signup_generate_token(self, cr, uid, partner_id, expiration=False, context=None):
""" generate a new token for a partner, and return it
:param partner_id: the partner id
:param expiration: the expiration datetime of the token (string, optional)
From 0f4529603c667f786f152859ad16e08ce9cc1fea Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 28 Sep 2012 10:52:59 +0200
Subject: [PATCH 12/55] [FIX] auth_signup: change test to determine when
signing in
bzr revid: rco@openerp.com-20120928085259-cb51yq7e7mivxsw3
---
addons/auth_signup/static/src/js/auth_signup.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js
index 927ee8de0c5..9bc775c8355 100644
--- a/addons/auth_signup/static/src/js/auth_signup.js
+++ b/addons/auth_signup/static/src/js/auth_signup.js
@@ -58,7 +58,7 @@ openerp.auth_signup = function(instance) {
if (ev) {
ev.preventDefault();
}
- if (this.params.token || this.$("input[name=signup]:checked").val()) {
+ if (this.$el.hasClass("oe_login_signup")) {
// signup user (or reset password)
var db = this.$("form [name=db]").val();
var name = this.$("form input[name=name]").val();
From bf5395ab3dc4dfe1a26faa7f9d490a46c80271f7 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 28 Sep 2012 10:58:43 +0200
Subject: [PATCH 13/55] [IMP] auth_signup: add function fields signup_valid,
signup_url, and method prepare_signup
bzr revid: rco@openerp.com-20120928085843-0erxrzva9lk5tqfh
---
addons/auth_signup/res_users.py | 57 +++++++++++++++++++++------------
1 file changed, 37 insertions(+), 20 deletions(-)
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 333686d4ca5..0624b25b357 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -41,32 +41,49 @@ def now():
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 (partner.signup_expiration or '') <= dt
+ return res
+
+ def _get_signup_url(self, cr, uid, ids, name, arg, context=None):
+ """ determine a signup url for a given partner """
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
+ template_url = '#action=login&db=%s&token=%s'
+
+ # if required, make sure that every partner has a valid signup token
+ if context and context.get('signup_valid'):
+ self.signup_prepare(cr, uid, ids, context=context)
+
+ res = dict.fromkeys(ids, False)
+ for partner in self.browse(cr, uid, ids, context):
+ if partner.signup_token:
+ res[partner.id] = urlparse.urljoin(base_url, template_url % (cr.dbname, partner.signup_token))
+ return res
+
_columns = {
- 'signup_token': fields.char(size=24, string='Signup Ticket'),
+ 'signup_token': fields.char(size=24, string='Signup Token'),
'signup_expiration': fields.datetime(string='Signup Expiration'),
+ '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 signup_get_url(self, cr, uid, partner_id, context=None):
- """ determine a signup url for the given partner_id """
- base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
- partner = self.browse(cr, uid, partner_id, context)
- token = partner.signup_token
- if not token:
- token = self.signup_generate_token(cr, uid, partner.id, context=context)
- return urlparse.urljoin(base_url, '#action=login&db=%s&token=%s' % (cr.dbname, token))
+ def action_signup_prepare(self, cr, uid, ids, context=None):
+ return self.signup_prepare(cr, uid, ids, context=context)
- def signup_generate_token(self, cr, uid, partner_id, expiration=False, context=None):
- """ generate a new token for a partner, and return it
- :param partner_id: the partner id
+ def signup_prepare(self, cr, uid, ids, 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)
- :return: the token (string)
"""
- # generate a unique token
- token = random_token()
- while self._signup_retrieve_partner(cr, uid, token, context=context):
- token = random_token()
- self.write(cr, uid, [partner_id], {'signup_token': token, 'signup_expiration': expiration}, context=context)
- return token
+ 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_expiration': expiration})
+ return True
def _signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None):
""" find the partner corresponding to a token, and check its validity
@@ -79,7 +96,7 @@ class res_partner(osv.Model):
raise Exception("Signup token '%s' is not valid" % token)
return False
partner = self.browse(cr, uid, partner_ids[0], context)
- if partner.signup_expiration and partner.signup_expiration < now():
+ if not partner.signup_valid:
if raise_exception:
raise Exception("Signup token '%s' is no longer valid" % token)
return False
From 0d878dc99e93b61085d746456e422e73a8e23dec Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 28 Sep 2012 14:02:02 +0200
Subject: [PATCH 14/55] [FIX] orm: fix the method copy() when an 'inherits'
field is given in parameter default
bzr revid: rco@openerp.com-20120928120202-6fbayrwb1iszwfwv
---
openerp/osv/orm.py | 83 ++++++++++++++++++++--------------------------
1 file changed, 36 insertions(+), 47 deletions(-)
diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py
index f324ed67522..651f655617c 100644
--- a/openerp/osv/orm.py
+++ b/openerp/osv/orm.py
@@ -4744,56 +4744,45 @@ class BaseModel(object):
else:
raise IndexError( _("Record #%d of %s not found, cannot copy!") %( id, self._name))
- # TODO it seems fields_get can be replaced by _all_columns (no need for translation)
- fields = self.fields_get(cr, uid, context=context)
- for f in fields:
- ftype = fields[f]['type']
-
- if self._log_access and f in LOG_ACCESS_COLUMNS:
- del data[f]
+ # build a black list of fields that should not be copied
+ blacklist = set(MAGIC_COLUMNS + ['parent_left', 'parent_right'])
+ def blacklist_given_fields(obj):
+ # blacklist the fields that are given by inheritance
+ for other, field_to_other in obj._inherits.items():
+ blacklist.add(field_to_other)
+ if field_to_other in default:
+ # all the fields of 'other' are given by the record: default[field_to_other],
+ # except the ones redefined in self
+ blacklist.update(set(self.pool.get(other)._all_columns) - set(self._columns))
+ else:
+ blacklist_given_fields(self.pool.get(other))
+ blacklist_given_fields(self)
+ res = dict(default)
+ for f, colinfo in self._all_columns.items():
+ field = colinfo.column
if f in default:
- data[f] = default[f]
- elif 'function' in fields[f]:
- del data[f]
- elif ftype == 'many2one':
- try:
- data[f] = data[f] and data[f][0]
- except:
- pass
- elif ftype == 'one2many':
- res = []
- rel = self.pool.get(fields[f]['relation'])
- if data[f]:
- # duplicate following the order of the ids
- # because we'll rely on it later for copying
- # translations in copy_translation()!
- data[f].sort()
- for rel_id in data[f]:
- # the lines are first duplicated using the wrong (old)
- # parent but then are reassigned to the correct one thanks
- # to the (0, 0, ...)
- d = rel.copy_data(cr, uid, rel_id, context=context)
- if d:
- res.append((0, 0, d))
- data[f] = res
- elif ftype == 'many2many':
- data[f] = [(6, 0, data[f])]
+ pass
+ elif f in blacklist:
+ pass
+ elif isinstance(field, fields.function):
+ pass
+ elif field._type == 'many2one':
+ res[f] = data[f] and data[f][0]
+ elif field._type == 'one2many':
+ other = self.pool.get(field._obj)
+ # duplicate following the order of the ids because we'll rely on
+ # it later for copying translations in copy_translation()!
+ lines = [other.copy_data(cr, uid, line_id, context=context) for line_id in sorted(data[f])]
+ # the lines are duplicated using the wrong (old) parent, but then
+ # are reassigned to the correct one thanks to the (0, 0, ...)
+ res[f] = [(0, 0, line) for line in lines if line]
+ elif field._type == 'many2many':
+ res[f] = [(6, 0, data[f])]
+ else:
+ res[f] = data[f]
- del data['id']
-
- # make sure we don't break the current parent_store structure and
- # force a clean recompute!
- for parent_column in ['parent_left', 'parent_right']:
- data.pop(parent_column, None)
- # Remove _inherits field's from data recursively, missing parents will
- # be created by create() (so that copy() copy everything).
- def remove_ids(inherits_dict):
- for parent_table in inherits_dict:
- del data[inherits_dict[parent_table]]
- remove_ids(self.pool.get(parent_table)._inherits)
- remove_ids(self._inherits)
- return data
+ return res
def copy_translations(self, cr, uid, old_id, new_id, context=None):
if context is None:
From 3efe72ad6a012acc4d585abf9fa2098a613ae9ec Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 28 Sep 2012 14:09:44 +0200
Subject: [PATCH 15/55] [IMP] auth_signup: simplify user creation in signup,
thanks to the fix of method copy()
bzr revid: rco@openerp.com-20120928120944-a2hgusq8gg3qf83n
---
addons/auth_signup/res_users.py | 22 ++++++----------------
1 file changed, 6 insertions(+), 16 deletions(-)
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 0624b25b357..33538ca67bf 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -142,6 +142,7 @@ class res_users(osv.Model):
else:
# user does not exist: sign up invited user
self._signup_create_user(cr, uid, {
+ 'name': partner.name,
'login': values['login'],
'password': values['password'],
'email': values['login'],
@@ -165,22 +166,11 @@ class res_users(osv.Model):
template_user_id = safe_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'
- values['active'] = True
- if values.get('partner_id'):
- # create a copy of the template user attached to values['partner_id']
- # note: we do not include 'partner_id' here, as copy() does not handle it correctly
- safe_values = {'login': values['login'], 'password': values['password']}
- user_id = self.copy(cr, uid, template_user_id, safe_values, context=context)
- # problem: the res.partner part of the template user has been duplicated
- # solution: unlink it, and replace it by values['partner_id']
- user = self.browse(cr, uid, user_id, context=context)
- partner = user.partner_id
- user.write(values)
- partner.unlink()
- else:
- # check that uninvited users may sign up
+ # check that uninvited users may sign up
+ if 'partner_id' not in values:
if not safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')):
raise Exception('Signup is not allowed for uninvited users')
- user_id = self.copy(cr, uid, template_user_id, values, context=context)
- return user_id
+ # create a copy of the template user (attached to a specific partner_id if given)
+ values['active'] = True
+ return self.copy(cr, uid, template_user_id, values, context=context)
From 21de1f18ed1fc63069e205ffb6370140eff192ec Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 28 Sep 2012 14:21:10 +0200
Subject: [PATCH 16/55] [IMP] portal: remove password from welcome message, and
use auth_signup on portal users
bzr revid: rco@openerp.com-20120928122110-hspgzhxnulmal2mb
---
addons/portal/wizard/portal_wizard.py | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/addons/portal/wizard/portal_wizard.py b/addons/portal/wizard/portal_wizard.py
index 3cc706c2129..13253861d44 100644
--- a/addons/portal/wizard/portal_wizard.py
+++ b/addons/portal/wizard/portal_wizard.py
@@ -35,12 +35,14 @@ _logger = logging.getLogger(__name__)
WELCOME_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s")
WELCOME_EMAIL_BODY = _("""Dear %(name)s,
-You have been given access to %(portal)s at %(url)s.
+You have been given access to %(portal)s.
Your login account data is:
Database: %(db)s
-User: %(login)s
-Password: %(password)s
+Username: %(login)s
+
+In order to complete the signin process, click on the following url:
+%(url)s
%(welcome_message)s
@@ -164,6 +166,8 @@ class wizard_user(osv.osv_memory):
user = self._create_user(cr, SUPERUSER_ID, wizard_user, context)
if (not user.active) or (portal not in user.groups_id):
user.write({'active': True, 'groups_id': [(4, portal.id)]})
+ # prepare for the signup process
+ user.partner_id.signup_prepare()
wizard_user = self.browse(cr, SUPERUSER_ID, wizard_user.id, context)
self._send_email(cr, uid, wizard_user, context)
else:
@@ -219,7 +223,8 @@ class wizard_user(osv.osv_memory):
_('You must have an email address in your User Preferences to send emails.'))
# determine subject and body in the portal user's language
- url = self.pool.get('ir.config_parameter').get_param(cr, SUPERUSER_ID, 'web.base.url', context=this_context)
+ ir_config_parameter = self.pool.get('ir.config_parameter')
+ url = ir_config_parameter.get_param(cr, SUPERUSER_ID, 'web.base.url', context=this_context)
user = self._retrieve_user(cr, SUPERUSER_ID, wizard_user, context)
context = dict(this_context or {}, lang=user.lang)
data = {
@@ -227,11 +232,10 @@ class wizard_user(osv.osv_memory):
'portal': wizard_user.wizard_id.portal_id.name,
'welcome_message': wizard_user.wizard_id.welcome_message or "",
'goodbye_message': wizard_user.wizard_id.goodbye_message or "",
- 'url': url or _("(missing url)"),
'db': cr.dbname,
+ 'name': user.name,
'login': user.login,
- 'password': user.password,
- 'name': user.name
+ 'url': user.signup_url,
}
if wizard_user.in_portal:
subject = _(WELCOME_EMAIL_SUBJECT) % data
From ab930b9906ec0a80f6922b524b1d66fa2449533f Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 28 Sep 2012 14:37:04 +0200
Subject: [PATCH 17/55] [IMP] portal: remove goodbye message
bzr revid: rco@openerp.com-20120928123704-g6gd67v2t0ewqpm9
---
addons/portal/wizard/portal_wizard.py | 46 ++++-----------------
addons/portal/wizard/portal_wizard_view.xml | 2 -
2 files changed, 7 insertions(+), 41 deletions(-)
diff --git a/addons/portal/wizard/portal_wizard.py b/addons/portal/wizard/portal_wizard.py
index 13253861d44..c94d6509f63 100644
--- a/addons/portal/wizard/portal_wizard.py
+++ b/addons/portal/wizard/portal_wizard.py
@@ -30,7 +30,7 @@ from openerp import SUPERUSER_ID
from base.res.res_partner import _lang_get
_logger = logging.getLogger(__name__)
-# welcome/goodbye email sent to portal users
+# welcome email sent to portal users
# (note that calling '_' has no effect except exporting those strings for translation)
WELCOME_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s")
WELCOME_EMAIL_BODY = _("""Dear %(name)s,
@@ -51,28 +51,10 @@ OpenERP - Open Source Business Applications
http://www.openerp.com
""")
-GOODBYE_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s")
-GOODBYE_EMAIL_BODY = _("""Dear %(name)s,
-
-Your access to %(portal)s has been withdrawn.
-
-%(goodbye_message)s
-
---
-OpenERP - Open Source Business Applications
-http://www.openerp.com
-""")
-
-# character sets for passwords, excluding 0, O, o, 1, I, l
-_PASSU = 'ABCDEFGHIJKLMNPQRSTUVWXYZ'
-_PASSL = 'abcdefghijkmnpqrstuvwxyz'
-_PASSD = '23456789'
-
def random_password():
- # get 3 uppercase letters, 3 lowercase letters, 2 digits, and shuffle them
- chars = map(random.choice, [_PASSU] * 3 + [_PASSL] * 3 + [_PASSD] * 2)
- random.shuffle(chars)
- return ''.join(chars)
+ # temporary random stuff; user password is reset by signup process
+ chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
+ return ''.join(random.choice(chars) for i in xrange(12))
def extract_email(email):
""" extract the email address from a user-friendly email address """
@@ -94,8 +76,6 @@ class wizard(osv.osv_memory):
'user_ids': fields.one2many('portal.wizard.user', 'wizard_id', string='Users'),
'welcome_message': fields.text(string='Invitation Message',
help="This text is included in the email sent to new users of the portal."),
- 'goodbye_message': fields.text(string='Withdrawal Message',
- help="This text is included in the email sent to users withdrawn from the portal."),
}
def _default_portal(self, cr, uid, context):
@@ -178,8 +158,6 @@ class wizard_user(osv.osv_memory):
user.write({'groups_id': [(3, portal.id)], 'active': False})
else:
user.write({'groups_id': [(3, portal.id)]})
- wizard_user = self.browse(cr, SUPERUSER_ID, wizard_user.id, context)
- self._send_email(cr, uid, wizard_user, context)
def _retrieve_user(self, cr, uid, wizard_user, context=None):
""" retrieve the (possibly inactive) user corresponding to wizard_user.partner_id
@@ -212,7 +190,7 @@ class wizard_user(osv.osv_memory):
return res_users.browse(cr, uid, user_id, context)
def _send_email(self, cr, uid, wizard_user, context=None):
- """ send notification email to a new/former portal user
+ """ send notification email to a new portal user
@param wizard_user: browse record of model portal.wizard.user
@return: the id of the created mail.mail record
"""
@@ -223,33 +201,23 @@ class wizard_user(osv.osv_memory):
_('You must have an email address in your User Preferences to send emails.'))
# determine subject and body in the portal user's language
- ir_config_parameter = self.pool.get('ir.config_parameter')
- url = ir_config_parameter.get_param(cr, SUPERUSER_ID, 'web.base.url', context=this_context)
user = self._retrieve_user(cr, SUPERUSER_ID, wizard_user, context)
context = dict(this_context or {}, lang=user.lang)
data = {
'company': this_user.company_id.name,
'portal': wizard_user.wizard_id.portal_id.name,
'welcome_message': wizard_user.wizard_id.welcome_message or "",
- 'goodbye_message': wizard_user.wizard_id.goodbye_message or "",
'db': cr.dbname,
'name': user.name,
'login': user.login,
'url': user.signup_url,
}
- if wizard_user.in_portal:
- subject = _(WELCOME_EMAIL_SUBJECT) % data
- body = _(WELCOME_EMAIL_BODY) % data
- else:
- subject = _(GOODBYE_EMAIL_SUBJECT) % data
- body = _(GOODBYE_EMAIL_BODY) % data
-
mail_mail = self.pool.get('mail.mail')
mail_values = {
'email_from': this_user.email,
'email_to': user.email,
- 'subject': subject,
- 'body_html': '%s ' % body,
+ 'subject': _(WELCOME_EMAIL_SUBJECT) % data,
+ 'body_html': '%s ' % (_(WELCOME_EMAIL_BODY) % data),
'state': 'outgoing',
}
return mail_mail.create(cr, uid, mail_values, context=this_context)
diff --git a/addons/portal/wizard/portal_wizard_view.xml b/addons/portal/wizard/portal_wizard_view.xml
index c8e3f30b128..373b4ad8e7a 100644
--- a/addons/portal/wizard/portal_wizard_view.xml
+++ b/addons/portal/wizard/portal_wizard_view.xml
@@ -27,8 +27,6 @@
-
or
From fe3cd19a8e9b0ca5e198e414ccbe7e21d0b20ed7 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 28 Sep 2012 14:37:32 +0200
Subject: [PATCH 18/55] [IMP] portal: remove dependency on auth_anonymous
bzr revid: rco@openerp.com-20120928123732-99t5s4di1ztt21j0
---
addons/portal/__openerp__.py | 1 -
addons/portal/portal_view.xml | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/addons/portal/__openerp__.py b/addons/portal/__openerp__.py
index d7356fc9ce5..66a736c6048 100644
--- a/addons/portal/__openerp__.py
+++ b/addons/portal/__openerp__.py
@@ -25,7 +25,6 @@
'depends': [
'base',
'share',
- 'auth_anonymous',
'auth_signup',
],
'author': 'OpenERP SA',
diff --git a/addons/portal/portal_view.xml b/addons/portal/portal_view.xml
index 16e8a86b27e..5b39ea89525 100644
--- a/addons/portal/portal_view.xml
+++ b/addons/portal/portal_view.xml
@@ -5,7 +5,7 @@
From ad2d3c360fcab3cf9d5121dd9bcaf1303b71befd Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 28 Sep 2012 16:21:03 +0200
Subject: [PATCH 19/55] [IMP] auth_signup: fallback to login when token is no
longer valid but user exists
bzr revid: rco@openerp.com-20120928142103-3a4vbs7y3gw135qm
---
addons/auth_signup/controllers/main.py | 1 -
addons/auth_signup/res_users.py | 31 +++++++++++++------
.../auth_signup/static/src/js/auth_signup.js | 21 +++++++------
3 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py
index 6e053715c09..56e5e910212 100644
--- a/addons/auth_signup/controllers/main.py
+++ b/addons/auth_signup/controllers/main.py
@@ -40,7 +40,6 @@ class Controller(openerpweb.Controller):
with registry.cursor() as cr:
res_partner = registry.get('res.partner')
user_info = res_partner.signup_retrieve_info(cr, SUPERUSER_ID, token)
- user_info.update(db=dbname, token=token)
return user_info
@openerpweb.httprequest
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 33538ca67bf..d8ed50bcaf6 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -85,10 +85,13 @@ class res_partner(osv.Model):
partner.write({'signup_token': token, 'signup_expiration': expiration})
return True
- def _signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None):
- """ find the partner corresponding to a token, and check its validity
+ 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)
- :raise: when token not valid (if raise_exception is True)
"""
partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context)
if not partner_ids:
@@ -96,7 +99,7 @@ class res_partner(osv.Model):
raise Exception("Signup token '%s' is not valid" % token)
return False
partner = self.browse(cr, uid, partner_ids[0], context)
- if not partner.signup_valid:
+ if check_validity and not partner.signup_valid:
if raise_exception:
raise Exception("Signup token '%s' is no longer valid" % token)
return False
@@ -104,14 +107,23 @@ class res_partner(osv.Model):
def signup_retrieve_info(self, cr, uid, token, context=None):
""" retrieve the user info about the token
- :return: either {'name': ..., 'login': ...} if a user exists for that token,
- or {'name': ..., 'email': ...} otherwise
+ :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:
- return {'name': partner.name, 'login': partner.user_ids[0].login}
+ res['login'] = partner.user_ids[0].login
else:
- return {'name': partner.name, 'email': partner.email or ''}
+ res['email'] = partner.email or ''
+ return res
@@ -133,7 +145,8 @@ 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, raise_exception=True, context=None)
+ partner = res_partner._signup_retrieve_partner(cr, uid, token,
+ check_validity=True, raise_exception=True, context=None)
# invalidate signup token
partner.write({'signup_token': False, 'signup_expiration': False})
if partner.user_ids:
diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js
index 9bc775c8355..509fafa35cc 100644
--- a/addons/auth_signup/static/src/js/auth_signup.js
+++ b/addons/auth_signup/static/src/js/auth_signup.js
@@ -31,20 +31,23 @@ openerp.auth_signup = function(instance) {
return d;
},
on_token_loaded: function(result) {
- // switch to signup mode
- this.$el.addClass("oe_login_signup");
// select the right the database
this.selected_db = result.db;
this.on_db_loaded({db_list: [result.db]});
- // set the name and login of user
- this.$("form input[name=name]").val(result.name).attr("readonly", "readonly");
- if (result.login) {
- this.$("form input[name=login]").val(result.login).attr("readonly", "readonly");
+ if (result.token) {
+ // switch to signup mode, set user name and login
+ this.$el.addClass("oe_login_signup");
+ this.$("form input[name=name]").val(result.name).attr("readonly", "readonly");
+ if (result.login) {
+ this.$("form input[name=login]").val(result.login).attr("readonly", "readonly");
+ } else {
+ this.$("form input[name=login]").val(result.email);
+ }
} else {
- this.$("form input[name=login]").val(result.email);
+ // remain in login mode, set login if present
+ delete this.params.token;
+ this.$("form input[name=login]").val(result.login || "");
}
- this.$("form input[name=password]").val("");
- this.$("form input[name=confirm_password]").val("");
},
on_token_failed: function(result, ev) {
if (ev) {
From 05aaabdbddd42fe29e77406584ecf8fb7656ba8a Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Mon, 1 Oct 2012 11:14:41 +0200
Subject: [PATCH 20/55] [IMP] auth_signup, portal: include a correct
login/signup url in partner emails
bzr revid: rco@openerp.com-20121001091441-axamc9xp9nq5ibk2
---
addons/auth_signup/res_users.py | 14 ++++++++++----
addons/portal/mail_mail.py | 34 +++++++++++----------------------
2 files changed, 21 insertions(+), 27 deletions(-)
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index d8ed50bcaf6..234977823c1 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -27,6 +27,7 @@ from openerp.tools.safe_eval import safe_eval
import time
import random
+import urllib
import urlparse
def random_token():
@@ -51,16 +52,21 @@ class res_partner(osv.Model):
def _get_signup_url(self, cr, uid, ids, name, arg, context=None):
""" determine a signup url for a given partner """
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
- template_url = '#action=login&db=%s&token=%s'
- # if required, make sure that every partner has a valid signup token
+ # if required, make sure that every partner without user has a valid signup token
if context and context.get('signup_valid'):
- self.signup_prepare(cr, uid, ids, context=context)
+ unsigned_ids = [p.id for p in self.browse(cr, uid, ids, context) if not p.user_ids]
+ self.signup_prepare(cr, uid, unsigned_ids, context=context)
res = dict.fromkeys(ids, False)
for partner in self.browse(cr, uid, ids, context):
if partner.signup_token:
- res[partner.id] = urlparse.urljoin(base_url, template_url % (cr.dbname, partner.signup_token))
+ params = (urllib.quote(cr.dbname), urllib.quote(partner.signup_token))
+ res[partner.id] = urlparse.urljoin(base_url, "#action=login&db=%s&token=%s" % params)
+ elif partner.user_ids:
+ user = partner.user_ids[0]
+ params = (urllib.quote(cr.dbname), urllib.quote(user.login))
+ res[partner.id] = urlparse.urljoin(base_url, "#action=login&db=%s&login=%s" % params)
return res
_columns = {
diff --git a/addons/portal/mail_mail.py b/addons/portal/mail_mail.py
index a41c28f15e2..f57170ed67b 100644
--- a/addons/portal/mail_mail.py
+++ b/addons/portal/mail_mail.py
@@ -19,34 +19,22 @@
#
##############################################################################
-import tools
from osv import osv
+import tools
-
-class mail_mail_portal(osv.Model):
- """ Update of mail_mail class, to add the signin URL to notifications.
- """
- _name = 'mail.mail'
- _inherit = ['mail.mail']
-
- def _generate_signin_url(self, cr, uid, partner_id, portal_group_id, key, context=None):
- """ Generate the signin url """
- base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='', context=context)
- return base_url + '/login?action=signin&partner_id=%s&group=%s&key=%s' % (partner_id, portal_group_id, key)
+class mail_mail(osv.Model):
+ """ Update of mail_mail class, to add the signin URL to notifications. """
+ _inherit = 'mail.mail'
def send_get_mail_body(self, cr, uid, mail, partner=None, context=None):
- """ Return a specific ir_email body. The main purpose of this method
- is to be inherited by Portal, to add a link for signing in, in
- each notification email a partner receives.
-
+ """ add a signin link inside the body of a mail.mail
:param mail: mail.mail browse_record
:param partner: browse_record of the specific recipient partner
+ :return: the resulting body_html
"""
+ body = super(mail_mail, self).send_get_mail_body(cr, uid, mail, partner, context=context)
if partner:
- portal_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
- portal_id = portal_ref and portal_ref[1] or False
- url = self._generate_signin_url(cr, uid, partner.id, portal_id, 1234, context=context)
- body = tools.append_content_to_html(mail.body_html, url)
- return body
- else:
- return super(mail_mail_portal, self).send_get_mail_body(cr, uid, mail, partner=partner, context=context)
+ context = dict(context or {}, signup_valid=True)
+ partner = self.pool.get('res.partner').browse(cr, uid, partner.id, context)
+ body = tools.append_content_to_html(body, "Log in our portal at: %s" % partner.signup_url)
+ return body
From c2a1e7e15d3c6b3c369e682055955699b2c0d48e Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Mon, 1 Oct 2012 17:03:33 +0200
Subject: [PATCH 21/55] [FIX] auth_signup: fix condition to test validity of
signup token
bzr revid: rco@openerp.com-20121001150333-dcf9co7bpsau5x2k
---
addons/auth_signup/res_users.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 234977823c1..0d7fd3744df 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -46,7 +46,8 @@ class res_partner(osv.Model):
dt = now()
res = {}
for partner in self.browse(cr, uid, ids, context):
- res[partner.id] = bool(partner.signup_token) and (partner.signup_expiration or '') <= dt
+ res[partner.id] = bool(partner.signup_token) and \
+ (not partner.signup_expiration or dt <= partner.signup_expiration)
return res
def _get_signup_url(self, cr, uid, ids, name, arg, context=None):
From 07ba5a0ce234254964f7743cf8ac52caaf0f0f4a Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Mon, 1 Oct 2012 17:07:23 +0200
Subject: [PATCH 22/55] [IMP] auth_reset_password: new implementation based on
auth_signup
bzr revid: rco@openerp.com-20121001150723-hcr2gcqqt7jsytp6
---
addons/auth_reset_password/__init__.py | 22 +++
addons/auth_reset_password/__openerp__.py | 24 ++-
.../auth_reset_password.py | 159 +++++-------------
.../auth_reset_password.xml | 47 +-----
.../controllers/__init__.py | 24 +++
.../auth_reset_password/controllers/main.py | 52 ++++++
.../static/src/css/reset_password.css | 12 --
.../static/src/js/reset_password.js | 74 ++------
.../static/src/xml/reset_password.xml | 21 +--
9 files changed, 184 insertions(+), 251 deletions(-)
create mode 100644 addons/auth_reset_password/controllers/__init__.py
create mode 100644 addons/auth_reset_password/controllers/main.py
delete mode 100644 addons/auth_reset_password/static/src/css/reset_password.css
diff --git a/addons/auth_reset_password/__init__.py b/addons/auth_reset_password/__init__.py
index 28bbdd18161..665887c6ee7 100644
--- a/addons/auth_reset_password/__init__.py
+++ b/addons/auth_reset_password/__init__.py
@@ -1 +1,23 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+
+import controllers
import auth_reset_password
diff --git a/addons/auth_reset_password/__openerp__.py b/addons/auth_reset_password/__openerp__.py
index ccf5ca53a21..d3e5e6d4bcc 100644
--- a/addons/auth_reset_password/__openerp__.py
+++ b/addons/auth_reset_password/__openerp__.py
@@ -1,3 +1,24 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+
{
'name': 'Reset Password',
'description': """
@@ -9,9 +30,8 @@ Allow users to reset their password from the login page.
'category': 'Authentication',
'website': 'http://www.openerp.com',
'installable': True,
- 'depends': ['auth_anonymous', 'email_template'],
+ 'depends': ['auth_signup', 'email_template'],
'data': ['auth_reset_password.xml'],
'js': ['static/src/js/reset_password.js'],
- 'css': ['static/src/css/reset_password.css'],
'qweb': ['static/src/xml/reset_password.xml'],
}
diff --git a/addons/auth_reset_password/auth_reset_password.py b/addons/auth_reset_password/auth_reset_password.py
index cef7bc9c212..fe7d47e13d7 100644
--- a/addons/auth_reset_password/auth_reset_password.py
+++ b/addons/auth_reset_password/auth_reset_password.py
@@ -1,130 +1,55 @@
-import base64
-import hashlib
-import simplejson
-import time
-import urlparse
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
-from openerp.tools import config
from openerp.osv import osv, fields
-from openerp import SUPERUSER_ID
+from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
-TWENTY_FOUR_HOURS = 24 * 60 * 60
+from datetime import datetime, timedelta
-def message_sign(data, secret):
- src = simplejson.dumps([data, secret], indent=None, separators=(',', ':'), sort_keys=True)
- sign = hashlib.sha1(src).hexdigest()
- msg = simplejson.dumps([data, sign], indent=None, separators=(',', ':'), sort_keys=True)
- # pad message to avoid '='
- pad = (3 - len(msg) % 3) % 3
- msg = msg + " " * pad
- msg = base64.urlsafe_b64encode(msg)
- return msg, sign
+def now(**kwargs):
+ dt = datetime.now() + timedelta(**kwargs)
+ return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
-def message_check(msg, secret):
- msg = base64.urlsafe_b64decode(msg)
- l = simplejson.loads(msg)
- msg_data = l[0]
- msg_sign = l[1]
- tmp, sign = message_sign(msg_data, secret)
- if msg_sign == sign:
- return msg_data
class res_users(osv.osv):
_inherit = 'res.users'
- def _auth_reset_password_secret(self, cr, uid, context=None):
- uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
- res = {
- 'dbname': cr.dbname,
- 'uuid': uuid,
- 'admin_passwd': config['admin_passwd']
- }
- return res
+ def reset_password(self, cr, uid, login, context=None):
+ """ retrieve the user corresponding to login (login or email),
+ create a specific signup token, and send it to the user
+ """
+ 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:
+ raise Exception('Reset password: invalid username or email')
- def _auth_reset_password_host(self, cr, uid, context=None):
- return self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', '')
+ # prepare reset password signup
+ user = self.browse(cr, uid, user_ids[0], context)
+ user.partner_id.signup_prepare(expiration=now(days=+1))
- def _auth_reset_password_link(self, cr, uid, ids, context=None):
- assert len(ids) == 1
- host = self._auth_reset_password_host(cr, uid, context)
- secret = self._auth_reset_password_secret(cr, uid, context)
- msg_src = {
- 'time': time.time(),
- 'uid': ids[0],
- }
- msg, sign = message_sign(msg_src, secret)
- link = urlparse.urljoin(host, '/login?db=%s&login=anonymous&key=anonymous#action=reset_password&token=%s' % (cr.dbname, msg))
- return link
+ # send email to user with their signup url
+ user = self.browse(cr, uid, user.id, context)
+ template = self.pool.get('ir.model.data').get_object(cr, uid, 'auth_reset_password', 'reset_password_email')
+ assert template._name == 'email.template'
+ self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, force_send=True, context=context)
- def _auth_reset_password_check_token(self, cr, uid, token, context=None):
- secret = self._auth_reset_password_secret(cr, uid, context)
- data = message_check(token, secret)
- if data and (time.time() - data['time'] < TWENTY_FOUR_HOURS):
- return data
- return None
-
- def _auth_reset_password_send_email(self, cr, uid, email_to, tpl_name, res_id, context=None):
- model, tpl_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'auth_reset_password', tpl_name)
- assert model == 'email.template'
-
- msg_id = self.pool.get(model).send_mail(cr, uid, tpl_id, res_id, force_send=False, context=context)
- MailMessage = self.pool.get('mail.message')
- MailMessage.write(cr, uid, [msg_id], {'email_to': email_to}, context=context)
- MailMessage.send(cr, uid, [msg_id], context=context)
-
- def send_reset_password_request(self, cr, uid, email, context=None):
- # TODO reseting a password knowing only an email is not good enough (email can be shared between multiple logins).
- ids = self.pool.get('res.users').search(cr, SUPERUSER_ID, [('user_email', '=', email)], context=context)
- if ids:
- self._auth_reset_password_send_email(cr, SUPERUSER_ID, email, 'reset_password_email', ids[0], context=context)
- return True
- #else:
- # _m, company_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'main_company')
- # self._auth_reset_password_send_email(cr, uid, email, 'email_no_user', company_id, context=context)
- return False
-
-class auth_reset_password(osv.TransientModel):
- _name = 'auth.reset_password'
- _rec_name = 'password'
- _columns = {
- 'password': fields.char('Password', size=64),
- 'password_confirmation': fields.char('Confirm Password', size=64),
- 'token': fields.char('Token', size=128),
- 'state': fields.selection([(x, x) for x in 'draft done missmatch error'.split()], required=True),
- }
- _defaults = {
- 'state': 'draft',
- }
-
- def create(self, cr, uid, values, context=None):
- # NOTE here, invalid values raises exceptions to avoid storing
- # sensitive data into the database (which then are available to anyone)
-
- pw = values.get('password')
- if not pw or pw != values.get('password_confirmation'):
- raise osv.except_osv('Error', 'Passwords missmatch')
-
- Users = self.pool.get('res.users')
- data = Users._auth_reset_password_check_token(cr, uid, values.get('token', ''))
- if data:
- Users.write(cr, SUPERUSER_ID, data['uid'], {'password': pw}, context=context)
- else:
- raise osv.except_osv('Error', 'Invalid token')
-
- # Dont store password
- values = {'state': 'done'}
- return super(auth_reset_password, self).create(cr, uid, values, context)
-
- def change(self, cr, uid, ids, context=None):
return True
-
- def onchange_pw(self, cr, uid, ids, password, password_confirmation, context=None):
- if password != password_confirmation:
- return {'value': {'state': 'missmatch'}}
- return {'value': {'state': 'draft'}}
-
- def onchange_token(self, cr, uid, ids, token, context=None):
- Users = self.pool.get('res.users')
- if not Users._auth_reset_password_check_token(cr, uid, token, context=context):
- return {'value': {'state': 'error'}}
- return {}
diff --git a/addons/auth_reset_password/auth_reset_password.xml b/addons/auth_reset_password/auth_reset_password.xml
index bb75da34ce8..90f08a25899 100644
--- a/addons/auth_reset_password/auth_reset_password.xml
+++ b/addons/auth_reset_password/auth_reset_password.xml
@@ -7,56 +7,15 @@
Reset Password
]]>
-
+ ${object.email}
Password reset
A password reset was requested the OpenERP account linked to this email on ${object._auth_reset_password_host()}
+A password reset was requested for the OpenERP account linked to this email.
-You may change your password following this link ,
-or by copy-pasting the following URL in your browser: ${object._auth_reset_password_link()}
+You may change your password following this link .
Note: If you did not ask for a password reset, you can safely ignore this email.
]]>
-
-
- auth.reset_password.form
- auth.reset_password
-
-
-
-
-
-
- Reset Password
- ir.actions.act_window
- auth.reset_password
- form
- form
- new
-
-
-
diff --git a/addons/auth_reset_password/controllers/__init__.py b/addons/auth_reset_password/controllers/__init__.py
new file mode 100644
index 00000000000..f40f0ee976e
--- /dev/null
+++ b/addons/auth_reset_password/controllers/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+
+import main
+
+# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/auth_reset_password/controllers/main.py b/addons/auth_reset_password/controllers/main.py
new file mode 100644
index 00000000000..5185a65c3ce
--- /dev/null
+++ b/addons/auth_reset_password/controllers/main.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP SA ()
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+#
+##############################################################################
+
+from openerp import SUPERUSER_ID
+from openerp.modules.registry import RegistryManager
+import openerp.addons.web.common.http as openerpweb
+
+import werkzeug
+
+import logging
+_logger = logging.getLogger(__name__)
+
+class Controller(openerpweb.Controller):
+ _cp_path = '/auth_reset_password'
+
+ @openerpweb.httprequest
+ def reset_password(self, req, dbname, login):
+ """ retrieve user, and perform reset password """
+ url = '/'
+ registry = RegistryManager.get(dbname)
+ with registry.cursor() as cr:
+ try:
+ res_users = registry.get('res.users')
+ res_users.reset_password(cr, SUPERUSER_ID, login)
+ cr.commit()
+ message = 'An email has been sent with credentials to reset your password'
+ except Exception as e:
+ # signup error
+ _logger.exception('error when resetting password')
+ message = e.message
+ url = "/#action=login&error_message=%s" % werkzeug.urls.url_quote(message)
+ return werkzeug.utils.redirect(url)
+
+# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/auth_reset_password/static/src/css/reset_password.css b/addons/auth_reset_password/static/src/css/reset_password.css
deleted file mode 100644
index dd590365918..00000000000
--- a/addons/auth_reset_password/static/src/css/reset_password.css
+++ /dev/null
@@ -1,12 +0,0 @@
-.openerp .oe_login .oe_login_pane {
- height: 152px;
-}
-.openerp .oe_login .oe_login_pane ul.oe_login_switch a {
- color: #eeeeee;
- margin: 0 8px;
-}
-.openerp .oe_login .oe_login_pane ul.oe_login_switch a:hover {
- text-decoration: underline;
-}
-
-
diff --git a/addons/auth_reset_password/static/src/js/reset_password.js b/addons/auth_reset_password/static/src/js/reset_password.js
index 4e09b5c5318..13fe2a39ce5 100644
--- a/addons/auth_reset_password/static/src/js/reset_password.js
+++ b/addons/auth_reset_password/static/src/js/reset_password.js
@@ -1,72 +1,30 @@
openerp.auth_reset_password = function(instance) {
var _t = instance.web._t;
+
instance.web.Login.include({
start: function() {
- var $e = this.$el;
- $e.find('a.oe_login_switch').click(function() {
- $e.find('ul.oe_login_switch').toggle();
- var $m = $e.find('form input[name=is_reset_pw]');
- $m.attr('checked', !$m.is(':checked'));
- });
+ this.$('a.oe_reset_password').click(this.do_reset_password);
return this._super();
},
- on_submit: function(ev) {
- if(ev) {
+ do_reset_password: function(ev) {
+ if (ev) {
ev.preventDefault();
}
-
- var $e = this.$el;
- var db = $e.find("form [name=db]").val();
+ var db = this.$("form [name=db]").val();
+ var login = this.$("form input[name=login]").val();
if (!db) {
- this.do_warn(_t("Login"), _t("No database selected !"));
+ this.do_warn("Login", "No database selected !");
+ return false;
+ } else if (!login) {
+ this.do_warn("Login", "Please enter a username or email address.")
return false;
}
- var $m = $e.find('form input[name=is_reset_pw]');
- if ($m.is(':checked')) {
- var email = $e.find('form input[name=email]').val();
- return this.do_reset_password(db, email);
- } else {
- return this._super(ev);
- }
- },
- do_reset_password: function(db, email) {
- var self = this;
- instance.session.session_authenticate(db, 'anonymous', 'anonymous', true).pipe(function () {
- var func = new instance.web.Model("res.users").get_func("send_reset_password_request");
- return func(email).then(function(res) {
- // show message
- self.do_notify(_t('Reset Password'), _.str.sprintf(_t('We have sent an email to %s with further instructions'), email), true);
- }, function(error, event) {
- // no traceback please
- event.preventDefault();
- });
- }).fail(function(error, event) {
- // cannot log as anonymous or reset_password not installed
- self.do_warn(_t('Reset Password'), _.str.sprintf(_t('Reset Password functionnality is not available for database %s'), db), true);
- });
+ var params = {
+ dbname : db,
+ login: login,
+ };
+ var url = "/auth_reset_password/reset_password?" + $.param(params);
+ window.location = url;
}
});
-
- instance.reset_password = {};
- instance.reset_password.ResetPassword = instance.web.Widget.extend({
- init: function(parent, params) {
- this._super(parent);
- this.token = (params && params.token) || false;
- },
- start: function() {
- this.do_action({
- name: 'Reset Password',
- type: 'ir.actions.act_window',
- context: {default_token: this.token},
- res_model: 'auth.reset_password',
- target: 'new',
- views: [[false, 'form']]
- });
- }
- });
-
-
- instance.web.client_actions.add("reset_password", "instance.reset_password.ResetPassword");
-
-
};
diff --git a/addons/auth_reset_password/static/src/xml/reset_password.xml b/addons/auth_reset_password/static/src/xml/reset_password.xml
index e57334b9c15..841f3906622 100644
--- a/addons/auth_reset_password/static/src/xml/reset_password.xml
+++ b/addons/auth_reset_password/static/src/xml/reset_password.xml
@@ -1,26 +1,11 @@
-
+
-
- // addClass does not work :(
- this.attr('class', (this.attr('class') || '') + ' oe_login_switch');
-
-
- Forgot your password?
-
-
-
-
+ Reset password
+
From b3e7f809b2ff854beb99487fc3a9aa30d7d3d4a1 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Tue, 2 Oct 2012 09:53:04 +0200
Subject: [PATCH 23/55] [FIX] portal: adapt tests to new signup process
bzr revid: rco@openerp.com-20121002075304-62r768sg7vt4i9z3
---
addons/portal/tests/test_portal.py | 73 +++++++++++++++---------------
1 file changed, 36 insertions(+), 37 deletions(-)
diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py
index 653a05d40aa..fe26f3280a2 100644
--- a/addons/portal/tests/test_portal.py
+++ b/addons/portal/tests/test_portal.py
@@ -33,6 +33,7 @@ class test_portal(test_mail.TestMailMockups):
self.mail_group = self.registry('mail.group')
self.mail_mail = self.registry('mail.mail')
self.mail_message = self.registry('mail.message')
+ self.mail_invite = self.registry('mail.wizard.invite')
self.res_users = self.registry('res.users')
self.res_partner = self.registry('res.partner')
@@ -41,19 +42,17 @@ class test_portal(test_mail.TestMailMockups):
{'name': 'Pigs', 'description': 'Fans of Pigs, unite !'})
# Find Portal group
- group_portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
- self.group_portal_id = group_portal_ref and group_portal_ref[1] or False
+ group_portal = self.registry('ir.model.data').get_object(cr, uid, 'portal', 'group_portal')
+ self.group_portal_id = group_portal.id
# Create Chell (portal user)
self.user_chell_id = self.res_users.create(cr, uid, {'name': 'Chell Gladys', 'login': 'chell', 'groups_id': [(6, 0, [self.group_portal_id])]})
- self.user_chell = self.res_users.browse(cr, uid, self.user_chell_id)
- self.partner_chell_id = self.user_chell.partner_id.id
+ user_chell = self.res_users.browse(cr, uid, self.user_chell_id)
+ self.partner_chell_id = user_chell.partner_id.id
def test_00_access_rights(self):
""" Test basic mail_message and mail_group access rights for portal users. """
cr, uid = self.cr, self.uid
- partner_chell_id = self.partner_chell_id
- user_chell_id = self.user_chell_id
# Prepare group: Pigs (portal)
self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Message')
@@ -64,56 +63,56 @@ class test_portal(test_mail.TestMailMockups):
# ----------------------------------------
# Do: Chell reads Pigs messages, ok because restricted to portal group
- message_ids = self.mail_group.read(cr, user_chell_id, self.group_pigs_id, ['message_ids'])['message_ids']
- self.mail_message.read(cr, user_chell_id, message_ids)
+ message_ids = self.mail_group.read(cr, self.user_chell_id, self.group_pigs_id, ['message_ids'])['message_ids']
+ self.mail_message.read(cr, self.user_chell_id, message_ids)
+
# Do: Chell posts a message on Pigs, crash because can not write on group or is not in the followers
- self.assertRaises(except_orm,
- self.mail_group.message_post,
- cr, user_chell_id, self.group_pigs_id, body='Message')
+ with self.assertRaises(except_orm):
+ self.mail_group.message_post(cr, self.user_chell_id, self.group_pigs_id, body='Message')
+
# Do: Chell is added to Pigs followers
- self.mail_group.message_subscribe(cr, uid, [self.group_pigs_id], [partner_chell_id])
+ self.mail_group.message_subscribe(cr, uid, [self.group_pigs_id], [self.partner_chell_id])
+
# Test: Chell posts a message on Pigs, ok because in the followers
- self.mail_group.message_post(cr, user_chell_id, self.group_pigs_id, body='Message')
+ self.mail_group.message_post(cr, self.user_chell_id, self.group_pigs_id, body='Message')
def test_50_mail_invite(self):
cr, uid = self.cr, self.uid
user_admin = self.res_users.browse(cr, uid, uid)
- self.mail_invite = self.registry('mail.wizard.invite')
base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='')
- portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal')
- portal_id = portal_ref and portal_ref[1] or False
-
# 0 - Admin
- p_a_id = user_admin.partner_id.id
+ partner_admin_id = user_admin.partner_id.id
# 1 - Bert Tartopoils, with email, should receive emails for comments and emails
- p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
+ partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
# ----------------------------------------
- # CASE1: generated URL
+ # CASE: invite Bert to follow Pigs
# ----------------------------------------
- url = self.mail_mail._generate_signin_url(cr, uid, p_b_id, portal_id, 1234)
- self.assertEqual(url, base_url + '/login?action=signin&partner_id=%s&group=%s&key=%s' % (p_b_id, portal_id, 1234),
- 'generated signin URL incorrect')
-
- # ----------------------------------------
- # CASE2: invite Bert
- # ----------------------------------------
-
- _sent_email_subject = 'Invitation to follow Pigs'
- _sent_email_body = append_content_to_html('You have been invited to follow Pigs.
', url)
-
# Do: create a mail_wizard_invite, validate it
self._init_mock_build_email()
- mail_invite_id = self.mail_invite.create(cr, uid, {'partner_ids': [(4, p_b_id)]}, {'default_res_model': 'mail.group', 'default_res_id': self.group_pigs_id})
+ context = {'default_res_model': 'mail.group', 'default_res_id': self.group_pigs_id}
+ mail_invite_id = self.mail_invite.create(cr, uid, {'partner_ids': [(4, partner_bert_id)]}, context)
self.mail_invite.add_followers(cr, uid, [mail_invite_id])
- group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
# Test: Pigs followers should contain Admin and Bert
+ group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
follower_ids = [follower.id for follower in group_pigs.message_follower_ids]
- self.assertEqual(set(follower_ids), set([p_a_id, p_b_id]), 'Pigs followers after invite is incorrect')
- # Test: sent email subject, body
+ self.assertEqual(set(follower_ids), set([partner_admin_id, partner_bert_id]), 'Pigs followers after invite is incorrect')
+
+ # Test: partner must have been prepared for signup
+ partner_bert = self.res_partner.browse(cr, uid, partner_bert_id)
+ self.assertTrue(partner_bert.signup_valid, 'partner has not been prepared for signup')
+ self.assertTrue(base_url in partner_bert.signup_url, 'signup url is incorrect')
+ self.assertTrue(cr.dbname in partner_bert.signup_url, 'signup url is incorrect')
+ self.assertTrue(partner_bert.signup_token in partner_bert.signup_url, 'signup url is incorrect')
+
+ # Test: (pretend to) send email and check subject, body
self.assertEqual(len(self._build_email_kwargs_list), 1, 'sent email number incorrect, should be only for Bert')
for sent_email in self._build_email_kwargs_list:
- self.assertEqual(sent_email.get('subject'), _sent_email_subject, 'sent email subject incorrect')
- self.assertEqual(sent_email.get('body'), _sent_email_body, 'sent email body incorrect')
+ self.assertEqual(sent_email.get('subject'), 'Invitation to follow Pigs',
+ 'subject of invitation email is incorrect')
+ self.assertTrue('You have been invited to follow Pigs' in sent_email.get('body'),
+ 'body of invitation email is incorrect')
+ self.assertTrue(partner_bert.signup_url in sent_email.get('body'),
+ 'body of invitation email does not contain signup url')
From 295e96d5283b5271295c4604fb03d69b776c1e3a Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Tue, 2 Oct 2012 16:57:57 +0200
Subject: [PATCH 24/55] [IMP] auth_signup: add state (function field) on
res.users: new, active, resetting password
bzr revid: rco@openerp.com-20121002145757-2m4dj7t5zq2sztfc
---
addons/auth_signup/__openerp__.py | 1 +
addons/auth_signup/res_users.py | 9 +++++++++
addons/auth_signup/res_users_view.xml | 19 +++++++++++++++++++
3 files changed, 29 insertions(+)
create mode 100644 addons/auth_signup/res_users_view.xml
diff --git a/addons/auth_signup/__openerp__.py b/addons/auth_signup/__openerp__.py
index 5bb3aab4ec0..9cbeb60638f 100644
--- a/addons/auth_signup/__openerp__.py
+++ b/addons/auth_signup/__openerp__.py
@@ -34,6 +34,7 @@ Allow users to sign up.
'data': [
'auth_signup_data.xml',
'res_config.xml',
+ 'res_users_view.xml',
],
'js': ['static/src/js/auth_signup.js'],
'css' : ['static/src/css/base.css'],
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 0d7fd3744df..7b663bd0e33 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -137,6 +137,15 @@ class res_partner(osv.Model):
class res_users(osv.Model):
_inherit = 'res.users'
+ def _get_state(self, cr, uid, ids, name, arg, context=None):
+ return dict((user.id, 'new' if not user.login_date else 'reset' if user.signup_token else 'active')
+ for user in self.browse(cr, uid, ids, context))
+
+ _columns = {
+ 'state': fields.function(_get_state, string='State', type='selection',
+ selection=[('new', 'New'), ('active', 'Active'), ('reset', 'Resetting Password')]),
+ }
+
def signup(self, cr, uid, values, token=None, context=None):
""" signup a user, to either:
- create a new user (no token), or
diff --git a/addons/auth_signup/res_users_view.xml b/addons/auth_signup/res_users_view.xml
new file mode 100644
index 00000000000..c42e1ab5f41
--- /dev/null
+++ b/addons/auth_signup/res_users_view.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ user.form.state
+ res.users
+
+
+
+
+
+
+
+
+
+
From 224520c2cab5880362702608e56ae03fc84ac1ce Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Wed, 3 Oct 2012 10:38:31 +0200
Subject: [PATCH 25/55] [IMP] auth_signup: improve label in settings
bzr revid: rco@openerp.com-20121003083831-1tcla6w18cyazb1j
---
addons/auth_signup/res_config.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/auth_signup/res_config.py b/addons/auth_signup/res_config.py
index c69c01127f6..21fce19745c 100644
--- a/addons/auth_signup/res_config.py
+++ b/addons/auth_signup/res_config.py
@@ -26,7 +26,7 @@ class base_config_settings(osv.TransientModel):
_inherit = 'base.config.settings'
_columns = {
- 'auth_signup_uninvited': fields.boolean('Allow public users to sign up', help="If unchecked only invited users may sign up"),
+ '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', 'Template user for new users created through signup'),
}
From 8fbdc2a8253c7b32ad65473ab3ec02683033ba10 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Wed, 3 Oct 2012 15:21:52 +0200
Subject: [PATCH 26/55] [IMP] auth_reset_password: add button on user form view
to reset the user's password
bzr revid: rco@openerp.com-20121003132152-1vbzao6n34na2uw6
---
addons/auth_reset_password/__init__.py | 2 +-
addons/auth_reset_password/__openerp__.py | 5 ++++-
.../{auth_reset_password.py => res_users.py} | 16 ++++++++++------
addons/auth_reset_password/res_users_view.xml | 19 +++++++++++++++++++
4 files changed, 34 insertions(+), 8 deletions(-)
rename addons/auth_reset_password/{auth_reset_password.py => res_users.py} (73%)
create mode 100644 addons/auth_reset_password/res_users_view.xml
diff --git a/addons/auth_reset_password/__init__.py b/addons/auth_reset_password/__init__.py
index 665887c6ee7..8341ccd60c7 100644
--- a/addons/auth_reset_password/__init__.py
+++ b/addons/auth_reset_password/__init__.py
@@ -20,4 +20,4 @@
##############################################################################
import controllers
-import auth_reset_password
+import res_users
diff --git a/addons/auth_reset_password/__openerp__.py b/addons/auth_reset_password/__openerp__.py
index d3e5e6d4bcc..e620d515fa1 100644
--- a/addons/auth_reset_password/__openerp__.py
+++ b/addons/auth_reset_password/__openerp__.py
@@ -31,7 +31,10 @@ Allow users to reset their password from the login page.
'website': 'http://www.openerp.com',
'installable': True,
'depends': ['auth_signup', 'email_template'],
- 'data': ['auth_reset_password.xml'],
+ 'data': [
+ 'auth_reset_password.xml',
+ 'res_users_view.xml',
+ ],
'js': ['static/src/js/reset_password.js'],
'qweb': ['static/src/xml/reset_password.xml'],
}
diff --git a/addons/auth_reset_password/auth_reset_password.py b/addons/auth_reset_password/res_users.py
similarity index 73%
rename from addons/auth_reset_password/auth_reset_password.py
rename to addons/auth_reset_password/res_users.py
index fe7d47e13d7..b1039031812 100644
--- a/addons/auth_reset_password/auth_reset_password.py
+++ b/addons/auth_reset_password/res_users.py
@@ -34,22 +34,26 @@ class res_users(osv.osv):
def reset_password(self, cr, uid, login, context=None):
""" retrieve the user corresponding to login (login or email),
- create a specific signup token, and send it to the user
+ 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:
raise Exception('Reset password: invalid username or email')
+ return self.action_reset_password(cr, uid, user_ids, context=context)
+ def action_reset_password(self, cr, uid, ids, context=None):
+ """ create signup token for each user, and send their signup url by email """
# prepare reset password signup
- user = self.browse(cr, uid, user_ids[0], context)
- user.partner_id.signup_prepare(expiration=now(days=+1))
+ res_partner = self.pool.get('res.partner')
+ partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context)]
+ res_partner.signup_prepare(cr, uid, partner_ids, expiration=now(days=+1), context=context)
- # send email to user with their signup url
- user = self.browse(cr, uid, user.id, context)
+ # send email to users with their signup url
template = self.pool.get('ir.model.data').get_object(cr, uid, 'auth_reset_password', 'reset_password_email')
assert template._name == 'email.template'
- self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, force_send=True, context=context)
+ for user in self.browse(cr, uid, ids, context):
+ self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, context=context)
return True
diff --git a/addons/auth_reset_password/res_users_view.xml b/addons/auth_reset_password/res_users_view.xml
new file mode 100644
index 00000000000..9952162ae65
--- /dev/null
+++ b/addons/auth_reset_password/res_users_view.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ user.form.reset_password
+ res.users
+
+
+
+
+
+
+
+
+
+
+
+
From 40265731a095bfec36dad140a4ae3e2d6c1ddcc6 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 5 Oct 2012 12:11:38 +0200
Subject: [PATCH 27/55] [IMP] tests: add tests on model inheritance (_inherits)
bzr revid: rco@openerp.com-20121005101138-ifaon35nmytviu24
---
openerp/tests/test_orm.py | 67 +++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/openerp/tests/test_orm.py b/openerp/tests/test_orm.py
index bce5bc3903a..344bc29219f 100644
--- a/openerp/tests/test_orm.py
+++ b/openerp/tests/test_orm.py
@@ -6,6 +6,72 @@ import common
UID = common.ADMIN_USER_ID
DB = common.DB
+class TestInherits(common.TransactionCase):
+ """ test the behavior of the orm for models that use _inherits;
+ specifically: res.users, that inherits from res.partner
+ """
+
+ def setUp(self):
+ super(TestInherits, self).setUp()
+ self.partner = self.registry('res.partner')
+ self.user = self.registry('res.users')
+
+ def test_create(self):
+ """ creating a user should automatically create a new partner """
+ partners_before = self.partner.search(self.cr, UID, [])
+ foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
+ foo = self.user.browse(self.cr, UID, foo_id)
+
+ self.assertNotIn(foo.partner_id.id, partners_before)
+
+ def test_read(self):
+ """ inherited fields should be read without any indirection """
+ foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
+ foo_values, = self.user.read(self.cr, UID, [foo_id])
+ partner_id = foo_values['partner_id'][0]
+ partner_values, = self.partner.read(self.cr, UID, [partner_id])
+ self.assertEqual(foo_values['name'], partner_values['name'])
+
+ foo = self.user.browse(self.cr, UID, foo_id)
+ self.assertEqual(foo.name, foo.partner_id.name)
+
+ def test_copy(self):
+ """ copying a user should automatically copy its partner, too """
+ foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
+ foo_before, = self.user.read(self.cr, UID, [foo_id])
+ bar_id = self.user.copy(self.cr, UID, foo_id, {'login': 'bar', 'password': 'bar'})
+ foo_after, = self.user.read(self.cr, UID, [foo_id])
+
+ self.assertEqual(foo_before, foo_after)
+
+ foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
+ self.assertEqual(bar.login, 'bar')
+ self.assertNotEqual(foo.id, bar.id)
+ self.assertNotEqual(foo.partner_id.id, bar.partner_id.id)
+
+ def test_copy_with_ancestor(self):
+ """ copying a user with 'parent_id' in defaults should not duplicate the partner """
+ foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
+ par_id = self.partner.create(self.cr, UID, {'name': 'Bar'})
+
+ foo_before, = self.user.read(self.cr, UID, [foo_id])
+ partners_before = self.partner.search(self.cr, UID, [])
+ bar_id = self.user.copy(self.cr, UID, foo_id, {'partner_id': par_id, 'login': 'bar'})
+ foo_after, = self.user.read(self.cr, UID, [foo_id])
+ partners_after = self.partner.search(self.cr, UID, [])
+
+ self.assertEqual(foo_before, foo_after)
+ self.assertEqual(set(partners_before), set(partners_after))
+
+ foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
+ self.assertNotEqual(foo.id, bar.id)
+ self.assertEqual(bar.partner_id.id, par_id)
+ self.assertEqual(bar.login, 'bar', "login is given from copy parameters")
+ self.assertEqual(bar.password, foo.password, "password is given from original record")
+ self.assertEqual(bar.name, 'Bar', "name is given from specific partner")
+
+
+
CREATE = lambda values: (0, False, values)
UPDATE = lambda id, values: (1, id, values)
DELETE = lambda id: (2, id, False)
@@ -19,6 +85,7 @@ def sorted_by_id(list_of_dicts):
return sorted(list_of_dicts, key=lambda d: d.get('id'))
class TestO2MSerialization(common.TransactionCase):
+ """ test the orm method 'write' on one2many fields """
def setUp(self):
super(TestO2MSerialization, self).setUp()
From bc34a0b071fbd0d6ccd140f1da3fe6493ff1cff6 Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 5 Oct 2012 12:12:20 +0200
Subject: [PATCH 28/55] [FIX] res_users: fix method copy() to not break test
case
bzr revid: rco@openerp.com-20121005101220-jhnomko7woiuwf1f
---
openerp/addons/base/res/res_users.py | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py
index 8bd5c3a0179..a1c06a52494 100644
--- a/openerp/addons/base/res/res_users.py
+++ b/openerp/addons/base/res/res_users.py
@@ -341,14 +341,12 @@ class res_users(osv.osv):
def copy(self, cr, uid, id, default=None, context=None):
user2copy = self.read(cr, uid, [id], ['login','name'])[0]
- if default is None:
- default = {}
- copy_pattern = _("%s (copy)")
- copydef = dict(login=(copy_pattern % user2copy['login']),
- name=(copy_pattern % user2copy['name']),
- )
- copydef.update(default)
- return super(res_users, self).copy(cr, uid, id, copydef, context)
+ default = dict(default or {})
+ if ('name' not in default) and ('partner_id' not in default):
+ default['name'] = _("%s (copy)") % user2copy['name']
+ if 'login' not in default:
+ default['login'] = _("%s (copy)") % user2copy['login']
+ return super(res_users, self).copy(cr, uid, id, default, context)
def context_get(self, cr, uid, context=None):
user = self.browse(cr, SUPERUSER_ID, uid, context)
From 6c8756a5a22227096c9719fbde05c2919142c31e Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Fri, 5 Oct 2012 13:56:19 +0200
Subject: [PATCH 29/55] [IMP] test_orm: add test for create with given
inherited ancestor
bzr revid: rco@openerp.com-20121005115619-l9qsoofumwrs5kle
---
openerp/tests/test_orm.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/openerp/tests/test_orm.py b/openerp/tests/test_orm.py
index 344bc29219f..ef8c0cf19aa 100644
--- a/openerp/tests/test_orm.py
+++ b/openerp/tests/test_orm.py
@@ -24,6 +24,19 @@ class TestInherits(common.TransactionCase):
self.assertNotIn(foo.partner_id.id, partners_before)
+ def test_create_with_ancestor(self):
+ """ creating a user with a specific 'partner_id' should not create a new partner """
+ par_id = self.partner.create(self.cr, UID, {'name': 'Foo'})
+ partners_before = self.partner.search(self.cr, UID, [])
+ foo_id = self.user.create(self.cr, UID, {'partner_id': par_id, 'login': 'foo', 'password': 'foo'})
+ partners_after = self.partner.search(self.cr, UID, [])
+
+ self.assertEqual(set(partners_before), set(partners_after))
+
+ foo = self.user.browse(self.cr, UID, foo_id)
+ self.assertEqual(foo.name, 'Foo')
+ self.assertEqual(foo.partner_id.id, par_id)
+
def test_read(self):
""" inherited fields should be read without any indirection """
foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
From 33952153679e8227fcfa720c9dfc1e0573ce8fe8 Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 11:12:43 +0200
Subject: [PATCH 30/55] [IMP] crm: lead search view
bzr revid: qdp-launchpad@openerp.com-20121008091243-f0ktqoee67y6kb3x
---
addons/crm/crm_lead_view.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index f91a8756598..1a099fcb803 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -359,7 +359,7 @@
-
+
From 0cfb72791dd068f1cb349a024208d6b4b7c486b3 Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 12:00:32 +0200
Subject: [PATCH 31/55] [IMP] renamed filter 'Inbox' into 'Unread Messages'
which is clearer
bzr revid: qdp-launchpad@openerp.com-20121008100032-mh7sd740fhsbjemr
---
addons/base_calendar/crm_meeting_view.xml | 2 +-
addons/crm/crm_lead_view.xml | 6 +++---
addons/event/event_view.xml | 4 ++--
addons/hr_recruitment/hr_recruitment_view.xml | 2 +-
addons/project/project_view.xml | 4 ++--
addons/project_issue/project_issue_view.xml | 2 +-
addons/purchase/purchase_view.xml | 2 +-
addons/sale/sale_view.xml | 2 +-
8 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml
index adcfc23a520..3782141fd3f 100644
--- a/addons/base_calendar/crm_meeting_view.xml
+++ b/addons/base_calendar/crm_meeting_view.xml
@@ -279,7 +279,7 @@
-
+
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index 1a099fcb803..0c6d1d1fe96 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -356,7 +356,7 @@
-
+
@@ -569,10 +569,10 @@
-
+
-
+
diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml
index 003072616f7..a290172d499 100644
--- a/addons/event/event_view.xml
+++ b/addons/event/event_view.xml
@@ -339,7 +339,7 @@
-
+
@@ -529,7 +529,7 @@
-
+
diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml
index 08e5f31a2d2..d48086f5663 100644
--- a/addons/hr_recruitment/hr_recruitment_view.xml
+++ b/addons/hr_recruitment/hr_recruitment_view.xml
@@ -208,7 +208,7 @@
-
+
diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml
index bd63eec84c5..686ae08ee73 100644
--- a/addons/project/project_view.xml
+++ b/addons/project/project_view.xml
@@ -163,7 +163,7 @@
-
+
@@ -619,7 +619,7 @@
-
+
diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml
index 3214b93c8d3..4cd9e2c19ff 100644
--- a/addons/project_issue/project_issue_view.xml
+++ b/addons/project_issue/project_issue_view.xml
@@ -198,7 +198,7 @@
-
+
diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml
index 7702d182b09..bfff06f5eaf 100644
--- a/addons/purchase/purchase_view.xml
+++ b/addons/purchase/purchase_view.xml
@@ -323,7 +323,7 @@
-
+
diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml
index 5505a6a88b5..4323261a19d 100644
--- a/addons/sale/sale_view.xml
+++ b/addons/sale/sale_view.xml
@@ -337,7 +337,7 @@
-
+
From da443ff975c233d37126cda2d86d4d20a4f93226 Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 12:05:47 +0200
Subject: [PATCH 32/55] [IMP] hr: removed job and manager from hr.employee
search view
bzr revid: qdp-launchpad@openerp.com-20121008100547-nlos20k0xyz3f72e
---
addons/hr/hr_view.xml | 2 --
1 file changed, 2 deletions(-)
diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml
index 69567902763..89c327f4d13 100644
--- a/addons/hr/hr_view.xml
+++ b/addons/hr/hr_view.xml
@@ -112,8 +112,6 @@
-
-
From 109d418de05226bbd242745c6f55010b7f6a8cb7 Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 12:11:31 +0200
Subject: [PATCH 33/55] [IMP] hr_expense: search view
bzr revid: qdp-launchpad@openerp.com-20121008101131-r34boe2joemso1mu
---
addons/hr_expense/hr_expense_view.xml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml
index 388e5262f6e..2032d3f0567 100644
--- a/addons/hr_expense/hr_expense_view.xml
+++ b/addons/hr_expense/hr_expense_view.xml
@@ -155,11 +155,12 @@
-
+
+
+
-
From d058f68e2f9c2d301880219880ba9e32cb64dfd0 Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 12:11:44 +0200
Subject: [PATCH 34/55] [IMP] account: search view
bzr revid: qdp-launchpad@openerp.com-20121008101144-bx103sqfkhd03a21
---
addons/account/account_invoice_view.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml
index 26e18271dc2..3c2922ff141 100644
--- a/addons/account/account_invoice_view.xml
+++ b/addons/account/account_invoice_view.xml
@@ -458,7 +458,6 @@
-
From bc3009d590af243ee28bb1e6994bcb2fae6d2bba Mon Sep 17 00:00:00 2001
From: Olivier Dony
Date: Mon, 8 Oct 2012 14:03:51 +0200
Subject: [PATCH 35/55] [FIX] mail: default alias_domain should be web.base.url
bzr revid: odo@openerp.com-20121008120351-fqmjckrjmy52xdji
---
addons/mail/data/mail_demo.xml | 5 -----
addons/mail/res_config.py | 13 +++++++++++--
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/addons/mail/data/mail_demo.xml b/addons/mail/data/mail_demo.xml
index 1350fd33547..7bda32c002b 100644
--- a/addons/mail/data/mail_demo.xml
+++ b/addons/mail/data/mail_demo.xml
@@ -42,10 +42,5 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
comment
-
- mail.catchall.domain
- demo.openerp.com
-
-
diff --git a/addons/mail/res_config.py b/addons/mail/res_config.py
index ce2f75687ba..91606e1c720 100644
--- a/addons/mail/res_config.py
+++ b/addons/mail/res_config.py
@@ -19,6 +19,8 @@
#
##############################################################################
+import urlparse
+
from openerp.osv import osv, fields
class project_configuration(osv.TransientModel):
@@ -31,9 +33,16 @@ class project_configuration(osv.TransientModel):
}
def get_default_alias_domain(self, cr, uid, ids, context=None):
- return {'alias_domain': self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context)}
+ alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context)
+ if not alias_domain:
+ domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "web.base.url", context=context)
+ try:
+ alias_domain = urlparse.urlsplit(domain).netloc.split(':')[0]
+ except Exception:
+ pass
+ return {'alias_domain': alias_domain}
def set_alias_domain(self, cr, uid, ids, context=None):
config_parameters = self.pool.get("ir.config_parameter")
for record in self.browse(cr, uid, ids, context=context):
- config_parameters.set_param(cr, uid, "mail.catchall.domain", record.alias_domain or '', context=context)
\ No newline at end of file
+ config_parameters.set_param(cr, uid, "mail.catchall.domain", record.alias_domain or '', context=context)
From 43595fbeb1f2020ab7e1f545c0dbf6a579bf77b2 Mon Sep 17 00:00:00 2001
From: Olivier Dony
Date: Mon, 8 Oct 2012 14:04:21 +0200
Subject: [PATCH 36/55] [FIX] mail: default email From should consider user
email if there is no alias
bzr revid: odo@openerp.com-20121008120421-k9aj7g6vsr3zu5u2
---
addons/mail/mail_mail.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py
index e9a569a8192..5b4a096e969 100644
--- a/addons/mail/mail_mail.py
+++ b/addons/mail/mail_mail.py
@@ -63,10 +63,12 @@ class mail_mail(osv.Model):
}
def _get_default_from(self, cr, uid, context=None):
- cur = self.pool.get('res.users').browse(cr, uid, uid, context=context)
- if not cur.alias_domain:
- raise osv.except_osv(_('Invalid Action!'), _('Unable to send email, set an alias domain in your server settings.'))
- return cur.alias_name + '@' + cur.alias_domain
+ this = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+ if this.alias_domain:
+ return '%s@%s' % (this.alias, this.alias_domain)
+ elif this.email:
+ return this.email
+ raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias."))
_defaults = {
'state': 'outgoing',
From d54120565152cc0f1c8164bf7538802f7d527332 Mon Sep 17 00:00:00 2001
From: Olivier Dony
Date: Mon, 8 Oct 2012 15:02:45 +0200
Subject: [PATCH 37/55] [FIX] portal: add test email address for outgoing
emails sent during tests
bzr revid: odo@openerp.com-20121008130245-7n1ed3i2ez9bzniy
---
addons/portal/tests/test_portal.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py
index 653a05d40aa..10392be53ff 100644
--- a/addons/portal/tests/test_portal.py
+++ b/addons/portal/tests/test_portal.py
@@ -49,6 +49,9 @@ class test_portal(test_mail.TestMailMockups):
self.user_chell = self.res_users.browse(cr, uid, self.user_chell_id)
self.partner_chell_id = self.user_chell.partner_id.id
+ # Set an email address for the user running the tests, used as Sender for outgoing mails
+ self.res_users.write(cr, uid, uid, {'email': 'test@localhost'})
+
def test_00_access_rights(self):
""" Test basic mail_message and mail_group access rights for portal users. """
cr, uid = self.cr, self.uid
From a369130a35341779d0876da02aaa49b284f8f09f Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 15:08:31 +0200
Subject: [PATCH 38/55] [IMP] crm: remove 'sale team' from res.partner search
view
bzr revid: qdp-launchpad@openerp.com-20121008130831-hjm46vehb54tqc74
---
addons/crm/res_partner_view.xml | 16 ----------------
addons/hr_expense/hr_expense_view.xml | 2 +-
2 files changed, 1 insertion(+), 17 deletions(-)
diff --git a/addons/crm/res_partner_view.xml b/addons/crm/res_partner_view.xml
index 84a0bd4cf49..4b313bcef71 100644
--- a/addons/crm/res_partner_view.xml
+++ b/addons/crm/res_partner_view.xml
@@ -15,28 +15,12 @@
-
- view.res.partner.tree.crm.inherited2
- res.partner
-
-
-
-
-
-
-
-
-
view.res.partner.search.crm.inherited3
res.partner
-
-
-
-
diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml
index 2032d3f0567..67d629eee6a 100644
--- a/addons/hr_expense/hr_expense_view.xml
+++ b/addons/hr_expense/hr_expense_view.xml
@@ -158,7 +158,7 @@
-
+
From 635c3225dbf78dd6ff548b9f979f549a49a663da Mon Sep 17 00:00:00 2001
From: Olivier Dony
Date: Mon, 8 Oct 2012 16:08:57 +0200
Subject: [PATCH 39/55] [FIX] project_long_term: demo project with phases
should be marked as `use_phases`
bzr revid: odo@openerp.com-20121008140857-zd3kcyq8t2ewyfno
---
addons/project_long_term/project_long_term_demo.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/addons/project_long_term/project_long_term_demo.xml b/addons/project_long_term/project_long_term_demo.xml
index bca8231fdaa..fda481d5a11 100644
--- a/addons/project_long_term/project_long_term_demo.xml
+++ b/addons/project_long_term/project_long_term_demo.xml
@@ -7,6 +7,7 @@
+
From a9b9412f182c3bc7e53b7a001d5edfa0b21b24bf Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 16:26:19 +0200
Subject: [PATCH 40/55] [IMP] crm: remove 'country_id' from res.partner search
view
bzr revid: qdp-launchpad@openerp.com-20121008142619-mxqis0ubc8m3o4z9
---
addons/crm/res_partner_view.xml | 3 ---
1 file changed, 3 deletions(-)
diff --git a/addons/crm/res_partner_view.xml b/addons/crm/res_partner_view.xml
index 4b313bcef71..b7aa334a9c3 100644
--- a/addons/crm/res_partner_view.xml
+++ b/addons/crm/res_partner_view.xml
@@ -21,9 +21,6 @@
-
-
-
From 6210cf76a6fba0d3054dcbe837e0824858eeea4b Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 16:27:57 +0200
Subject: [PATCH 41/55] [IMP] base: module search view imp
bzr revid: qdp-launchpad@openerp.com-20121008142757-c3597to9h42e56tl
---
openerp/addons/base/module/module_view.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openerp/addons/base/module/module_view.xml b/openerp/addons/base/module/module_view.xml
index 729d3ef7b4c..c3b3684f07c 100644
--- a/openerp/addons/base/module/module_view.xml
+++ b/openerp/addons/base/module/module_view.xml
@@ -39,7 +39,7 @@
+ 'ilike', self)]" string="Module"/>
From 5db1b37797c2e2e8ed2743e89cac8dd7489b6d8d Mon Sep 17 00:00:00 2001
From: "Quentin (OpenERP)"
Date: Mon, 8 Oct 2012 16:47:22 +0200
Subject: [PATCH 42/55] [IMP] crm: opportunity search view imp
bzr revid: qdp-launchpad@openerp.com-20121008144722-r6th49qhfzq8jmbf
---
addons/crm/crm_lead_view.xml | 3 +--
addons/crm_partner_assign/crm_lead_view.xml | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index 0c6d1d1fe96..6a152f44b67 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -580,9 +580,8 @@
domain="['|', ('section_id.user_id','=',uid), ('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}"
help="Opportunities that are assigned to either me or one of the sale teams I manage" />
-
-
+
diff --git a/addons/crm_partner_assign/crm_lead_view.xml b/addons/crm_partner_assign/crm_lead_view.xml
index cd2e3f08c9a..2cffb6fb899 100644
--- a/addons/crm_partner_assign/crm_lead_view.xml
+++ b/addons/crm_partner_assign/crm_lead_view.xml
@@ -56,7 +56,7 @@
-
+
From a1922458f5eeafd67cf364dca2c003c4bc63c7ae Mon Sep 17 00:00:00 2001
From: Raphael Collet
Date: Mon, 8 Oct 2012 17:11:31 +0200
Subject: [PATCH 43/55] [FIX] mail: in record rule, use partner id in
mail_follower_ids instead of user id
bzr revid: rco@openerp.com-20121008151131-wi86wmjqxo44c7sq
---
addons/mail/security/mail_security.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/mail/security/mail_security.xml b/addons/mail/security/mail_security.xml
index b503565bd11..be3bb667628 100644
--- a/addons/mail/security/mail_security.xml
+++ b/addons/mail/security/mail_security.xml
@@ -7,7 +7,7 @@
Mail.group: access only public and joined groups
- ['|', '|', ('public', '=', 'public'), ('message_follower_ids', 'in', [user.id]), '&', ('public','=','groups'), ('group_public_id','in', [x.id for x in user.groups_id])]
+ ['|', '|', ('public', '=', 'public'), ('message_follower_ids', 'in', [user.partner_id.id]), '&', ('public','=','groups'), ('group_public_id','in', [g.id for g in user.groups_id])]