Commit 8002f2de authored by Florent's avatar Florent
Browse files

adapt to new style forms and add tests

parent 9cb4f1bfcccd
import urllib, re, cgi, urlparse
from cubicweb import ValidationError
from cubicweb.web import Redirect
from cubicweb.devtools.testlib import WebTest
from cubes.registration.views import RegistrationSendMailController, deserialize, serialize
EMAILS = []
def my_send_email(self, to, msg):
EMAILS.append(msg)
RegistrationSendMailController.send_email = my_send_email
class RegistrationTC(WebTest):
data = {'firstname': u'Toto', 'surname': u'Toto', 'email': u'toto@secondweb.fr',
'login': u'toto', 'password': 'toto', 'password-confirm': 'toto'}
def setup_database(self):
self.config.set_option('registration_cypherkey', u'dummy cypher key')
super(RegistrationTC, self).setup_database()
def test_registration_form(self):
req = self.request()
req.form = {'firstname': u'Toto'}
pageinfo = self.view('registration', req=req, rset=None)
ns = {'ns': pageinfo.default_ns}
# check form field names
names = pageinfo.etree.xpath('//ns:form[@id="form"]//ns:input[@type!="hidden"]/@name',
namespaces=ns)
self.assertEquals(names, ['login', 'password', 'password-confirm',
'email', 'firstname', 'surname'])
# check form field value
firstname = pageinfo.etree.xpath('//ns:input[@name="firstname"]/@value', namespaces=ns)
self.assertEquals(firstname, [req.form['firstname']])
def _check_redirect(self, ctrl, urlpath, **urlparams):
try:
ctrl.publish()
assert False, 'should redirect to ' + urlpath
except Redirect, e:
url, params = (e.location.split('?')+[''])[:2]
self.assertEquals(url, self.config['base-url'] + urlpath)
self.assertDictEquals(dict(cgi.parse_qsl(params)), urlparams)
def test_send_mail_ok(self):
req = self.request()
req.form = self.data.copy()
ctrl = self.env.app.select_controller('registration_sendmail', req)
# check redirect
self._check_redirect(ctrl, '', __message='Your registration email has been sent.')
# check email contains activation url...
URL_RE = re.compile('(%s[^.]+)\.' % self.config['base-url'])
text = EMAILS[-1].get_payload(decode=True)
url = URL_RE.search(text).group(1)
# ... and the registration key contains all data
key = dict(cgi.parse_qsl(urlparse.urlsplit(url)[3]))['key']
d = self.data.copy()
d.pop('password-confirm')
self.assertDictEquals(deserialize(key, self.config['registration_cypherkey']), d)
def test_send_mail_failure(self):
req = self.request()
req.form = self.data.copy()
req.form.pop('firstname')
ctrl = self.env.app.select_controller('registration_sendmail', req)
try:
ctrl.publish()
assert False, 'should raise ValidationError'
except ValidationError, e:
self.assertDictEquals(e.errors, {'firstname': u'required attribute'})
def test_confirm_ok(self):
self.login('anon')
req = self.request()
req.form = {'key': serialize(self.data, self.config['registration_cypherkey'])}
ctrl = self.env.app.select_controller('registration_confirm', req)
self._check_redirect(ctrl, '', __message=u'Congratulations, your registration is complete. Welcome %(firstname)s %(surname)s !' % self.data)
req.cnx.commit()
req.cnx.close()
self.restore_connection()
rset = self.execute('Any U WHERE U login %(login)s, U firstname %(firstname)s, '
'U surname %(surname)s, U use_email M, M address %(email)s', self.data)
self.failUnless(rset.rowcount)
def test_confirm_failure_login_exists(self):
self.create_user(self.data['login'])
self.commit()
self.login('anon')
req = self.request()
req.form = {'key': serialize(self.data, self.config['registration_cypherkey'])}
ctrl = self.env.app.select_controller('registration_confirm', req)
# check user is redirected to a url without its password in the url params
d = self.data.copy()
d.pop('login'), d.pop('password'), d.pop('password-confirm')
d['__message'] = u'Login is already used. Please try another one.'
self._check_redirect(ctrl, 'register', **d)
def test_confirm_failure_invalid_data(self):
self.create_user(self.data['login'])
self.commit()
self.login('anon')
req = self.request()
req.form = {'key': u'dummykey'}
ctrl = self.env.app.select_controller('registration_confirm', req)
self._check_redirect(ctrl, 'register', __message=u'Invalid registration data. Please try registering again.')
......@@ -11,17 +11,19 @@ from Crypto.Cipher import Blowfish
from logilab.common.decorators import clear_cache
from cubicweb.common.mail import format_mail, addrheader
from cubicweb.common.selectors import match_user_groups, none_rset, match_form_params
from cubicweb.common.view import StartupView
from cubicweb.selectors import match_user_groups, none_rset, match_form_params
from cubicweb.view import StartupView
from cubicweb.web import Redirect, ValidationError, stdmsgs
from cubicweb.web.controller import Controller
from cubicweb.web.views.urlrewrite import SimpleReqRewriter, SchemaBasedRewriter, rgx_action
from cubicweb.web.form import FieldsForm, FormRenderer, FormViewMixIn
from cubicweb.web.formfields import StringField
from cubicweb.web.formwidgets import TextInput, PasswordInput, SubmitButton
REGISTRATION_FORM_URL = u'/register'
REGISTRATION_SUBMIT_URL = u'/register_sendemail'
REGISTRATION_CONFIRM_URL = u'/confirm'
REGISTRATION_DATA = ('login', 'password', 'email', 'firstname', 'surname')
REGISTRATION_FORM_URL = u'register'
REGISTRATION_SUBMIT_URL = u'register_sendemail'
REGISTRATION_CONFIRM_URL = u'confirm'
CYPHERFILLCHAR = '*'
CYPHERERS = {}
......@@ -44,71 +46,27 @@ def deserialize(s, cypherkey):
return loads(s)
class RegistrationFormView(StartupView):
id = 'registration_form'
template = u'''
<div class="formTitle">User registration</div>
<form method="post" enctype="application/x-www-form-urlencoded" action="%(action_url)s">
<table class="attributeForm">
<tr>
<th class="labelCol">
<label class="required" for="login">login</label>
</th>
<td style="width: 100%%;">
<input name="login" type="text" value="%(login)s" %(login_error)s/>
</td>
</tr>
<tr>
<th class="labelCol">
<label class="required" for="password">password</label>
</th>
<td style="width: 100%%;">
<input name="password" type="password" value="" %(password_error)s/>
<input name="password_conf" type="password" value="" %(password_conf_error)s/>
</td>
</tr>
<tr>
<th class="labelCol">
<label class="required" for="email">email</label>
</th>
<td style="width: 100%%;">
<input name="email" type="text" value="%(email)s" %(email_error)s/>
</td>
</tr>
<tr>
<th class="labelCol">
<label class="required" for="firstname">firstname</label>
</th>
<td style="width: 100%%;">
<input name="firstname" type="text" value="%(firstname)s" %(firstname_error)s/>
</td>
</tr>
<tr>
<th class="labelCol">
<label class="required" for="surname">surname</label>
</th>
<td style="width: 100%%;">
<input name="surname" type="text" value="%(surname)s" %(surname_error)s/>
</td>
</tr>
<tr>
<input class="validateButton" type="submit" value="Validate"/>
</tr>
</table>
</form>
'''
class RegistrationFormView(FormViewMixIn, StartupView):
id = 'registration'
def call(self):
self.req.add_css('cubicweb.form.css')
ctx = {}
for k in REGISTRATION_DATA:
ctx[k] = self.req.form.get(k, u'')
ctx[k+'_error'] = u''
ctx['password_conf_error'] = u''
for k in self.req.form.get('__errors', u'').split(','):
ctx[k+'_error'] = 'class="error"'
ctx['action_url'] = self.build_url(REGISTRATION_SUBMIT_URL[1:])
self.w(self.template % ctx)
form = self.vreg.select_object('forms', 'registration', self.req, rset=None)
self.w(form.form_render())
class RegistrationForm(FieldsForm):
id = 'registration'
action = REGISTRATION_SUBMIT_URL
login = StringField('login', widget=TextInput(), label=_('login'), required=True)
password = StringField('password', widget=PasswordInput(), label=_('password'), required=True)
email = StringField('email', widget=TextInput(), label=_('email'), required=True)
firstname = StringField('firstname', widget=TextInput(), label=_('firstname'), required=True)
surname = StringField('surname', widget=TextInput(), label=_('surname'), required=True)
form_buttons = [SubmitButton()]
class RegistrationSendMailController(Controller):
......@@ -126,8 +84,8 @@ See you soon on %(base_url)s !
subject = u'Confirm your registration on %(base_url)s'
def activation_url(self, data):
return self.build_url(REGISTRATION_CONFIRM_URL[1:],
key=serialize(data, self.config['registration_cypherkey']))
key = serialize(data, self.config['registration_cypherkey'])
return self.build_url(REGISTRATION_CONFIRM_URL, key=key)
def build_email(self, from_, recipient, data):
ctx = data.copy()
......@@ -147,18 +105,15 @@ See you soon on %(base_url)s !
smtp.close()
def checked_data(self):
form = self.req.form
data = dict((k, form.get(k, u'').strip()) for k in REGISTRATION_DATA)
error_params = [k for (k,v) in data.iteritems() if not v]
if data['password'] != form.get('password_conf'):
error_params += [u'password', u'password_conf']
if error_params:
url_params = dict((k,v) for (k,v) in data.iteritems()
if v and not k.startswith(u'password'))
url_params['__errors'] = u','.join(error_params)
url_params['__message'] = _(u'Please correct marked form fields')
self.debug("invalid registration form data: %s", url_params)
raise Redirect(self.build_url(REGISTRATION_FORM_URL[1:], **url_params))
'only basic data check here (required attributes and password confirmation check)'
fieldsform = self.vreg.select_object('forms', 'registration', self.req, rset=None)
required_fields = [f.name for f in fieldsform._fields_ if f.required]
data = dict((k, self.req.form.get(k, u'')) for k in required_fields)
errors = dict((k, _('required attribute')) for (k,v) in data.iteritems() if not v)
if data['password'] != self.req.form.get('password-confirm'):
errors[u'password'] = _('passwords are different')
if errors:
raise ValidationError(None, errors)
return data
def publish(self, rset=None):
......@@ -171,25 +126,15 @@ See you soon on %(base_url)s !
raise Redirect(self.build_url('', __message=u'Your registration email has been sent.'))
class RegistrationConfirmView(StartupView):
id = 'registration_confirm'
__select__ = match_user_groups('users') & none_rset() & match_form_params('firstname', 'surname')
success_msg = _(u'Successful registration. Welcome %(firstname)s %(surname)s !')
def call(self):
form = self.req.form
self.req.user.set_attributes(firstname=form['firstname'],
surname=form['surname'])
raise Redirect(self.build_url('', __message=self.success_msg % form))
class RegistrationConfirmController(Controller):
id = 'registration_confirm'
def failure_redirect_url(self, data):
# be sure not to get a password in your redirect url
url_params = data.copy()
url_params.pop('password', None), url_params.pop('password-confirm', None)
url_params['__message'] = _(u'Login is already used. Please try another one.')
return self.build_url(REGISTRATION_FORM_URL[1:], **url_params)
return self.build_url(REGISTRATION_FORM_URL, **url_params)
def success_redirect_url(self, data):
msg = u'Congratulations, your registration is complete. Welcome %(firstname)s %(surname)s !'
......@@ -202,7 +147,7 @@ class RegistrationConfirmController(Controller):
self.debug('registration data: %s', data)
except:
msg = _(u'Invalid registration data. Please try registering again.')
raise Redirect(req.build_url(REGISTRATION_FORM_URL[1:], __message=msg))
raise Redirect(req.build_url(REGISTRATION_FORM_URL, __message=msg))
login, password = data.pop('login'), data.pop('password')
if not self.appli.repo.register_user(login, password):
raise Redirect(self.failure_redirect_url(data))
......@@ -217,17 +162,20 @@ class RegistrationConfirmController(Controller):
except Redirect:
pass
assert req.user.login == login
req.execute('INSERT EmailAddress X: X address %(email)s, U use_email X '
'WHERE U eid %(ueid)s', {'email': data['email'], 'ueid': req.user.eid})
req.user.set_attributes(firstname=data['firstname'], surname=data['surname'])
raise Redirect(self.success_redirect_url(data))
## urls #######################################################################
class RegistrationSimpleReqRewriter(SimpleReqRewriter):
rules = [
(REGISTRATION_FORM_URL, dict(vid='registration_form')),
('/' + REGISTRATION_FORM_URL, dict(vid='registration')),
]
class RegistrationSchemaBasedRewrite(SchemaBasedRewriter):
rules = [
(REGISTRATION_SUBMIT_URL, rgx_action(controller='registration_sendmail')),
(REGISTRATION_CONFIRM_URL, rgx_action(controller='registration_confirm')),
('/' + REGISTRATION_SUBMIT_URL, rgx_action(controller='registration_sendmail')),
('/' + REGISTRATION_CONFIRM_URL, rgx_action(controller='registration_confirm')),
]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment