[register form] Updated the register form.
[register form] Updated the register form.

Updated to use the standard controller hooks that alter the behaviour of the form. In this
case, a fullname is required at registration. And that is maintained on the edit form too.

import sys import sys
from ckan.lib.base import request from ckan.lib.base import request
from ckan.lib.base import c, g, h from ckan.lib.base import c, g, h
from ckan.lib.base import model from ckan.lib.base import model
from ckan.lib.base import render from ckan.lib.base import render
from ckan.lib.base import _ from ckan.lib.base import _
   
  from ckan.lib.navl.validators import not_empty
   
from ckan.controllers.user import UserController from ckan.controllers.user import UserController
   
   
class CustomUserController(UserController): class CustomUserController(UserController):
"""This controller is an example to show how you might extend or """This controller is an example to show how you might extend or
override core CKAN behaviour from an extension package. override core CKAN behaviour from an extension package.
   
It duplicates functionality in the core CKAN UserController's It overrides 2 method hooks which the base class uses to create the
register function, but extends it to make an email address validation schema for the creation and editing of a user; to require
mandatory. that a fullname is given.
""" """
def custom_register(self):  
if request.method == 'POST':  
# custom validation that requires an email address  
error = False  
c.email = request.params.getone('email')  
c.login = request.params.getone('login')  
if not model.User.check_name_available(c.login):  
error = True  
h.flash_error(_("That username is not available."))  
if not c.email:  
error = True  
h.flash_error(_("You must supply an email address."))  
try:  
self._get_form_password()  
except ValueError, ve:  
h.flash_error(ve)  
error = True  
if error:  
return render('user/register.html')  
# now delegate to core CKAN register method  
return self.register()  
   
  new_user_form = 'user/register.html'
   
  def _add_requires_full_name_to_schema(self, schema):
  """
  Helper function that modifies the fullname validation on an existing schema
  """
  schema['fullname'] = [not_empty, unicode]
   
  def _new_form_to_db_schema(self):
  """
  Defines a custom schema that requires a full name to be supplied
   
  This method is a hook that the base class calls for the validation
  schema to use when creating a new user.
  """
  schema = super(CustomUserController, self)._new_form_to_db_schema()
  self._add_requires_full_name_to_schema(schema)
  return schema
   
  def _edit_form_to_db_schema(self):
  """
  Defines a custom schema that requires a full name cannot be removed
  when editing the user.
   
  This method is a hook that the base class calls for the validation
  schema to use when editing an exiting user.
  """
  schema = super(CustomUserController, self)._edit_form_to_db_schema()
  self._add_requires_full_name_to_schema(schema)
  return schema
   
   
import os import os
from logging import getLogger from logging import getLogger
   
from genshi.filters.transform import Transformer from genshi.filters.transform import Transformer
   
from ckan.plugins import implements, SingletonPlugin from ckan.plugins import implements, SingletonPlugin
from ckan.plugins import IConfigurer from ckan.plugins import IConfigurer
from ckan.plugins import IGenshiStreamFilter from ckan.plugins import IGenshiStreamFilter
from ckan.plugins import IRoutes from ckan.plugins import IRoutes
   
log = getLogger(__name__) log = getLogger(__name__)
   
   
class ExamplePlugin(SingletonPlugin): class ExamplePlugin(SingletonPlugin):
"""This plugin demonstrates how a theme packaged as a CKAN """This plugin demonstrates how a theme packaged as a CKAN
extension might extend CKAN behaviour. extension might extend CKAN behaviour.
   
In this case, we implement three extension interfaces: In this case, we implement three extension interfaces:
   
- ``IConfigurer`` allows us to override configuration normally - ``IConfigurer`` allows us to override configuration normally
found in the ``ini``-file. Here we use it to specify the site found in the ``ini``-file. Here we use it to specify the site
title, and to tell CKAN to look in this package for templates title, and to tell CKAN to look in this package for templates
and resources that customise the core look and feel. and resources that customise the core look and feel.
- ``IGenshiStreamFilter`` allows us to filter and transform the - ``IGenshiStreamFilter`` allows us to filter and transform the
HTML stream just before it is rendered. In this case we use HTML stream just before it is rendered. In this case we use
it to rename "frob" to "foobar" it to rename "frob" to "foobar"
- ``IRoutes`` allows us to add new URLs, or override existing - ``IRoutes`` allows us to add new URLs, or override existing
URLs. In this example we use it to override the default URLs. In this example we use it to override the default
``/register`` behaviour with a custom controller ``/register`` behaviour with a custom controller
""" """
implements(IConfigurer, inherit=True) implements(IConfigurer, inherit=True)
implements(IGenshiStreamFilter, inherit=True) implements(IGenshiStreamFilter, inherit=True)
implements(IRoutes, inherit=True) implements(IRoutes, inherit=True)
   
def update_config(self, config): def update_config(self, config):
"""This IConfigurer implementation causes CKAN to look in the """This IConfigurer implementation causes CKAN to look in the
```public``` and ```templates``` directories present in this ```public``` and ```templates``` directories present in this
package for any customisations. package for any customisations.
   
It also shows how to set the site title here (rather than in It also shows how to set the site title here (rather than in
the main site .ini file), and causes CKAN to use the the main site .ini file), and causes CKAN to use the
customised package form defined in ``package_form.py`` in this customised package form defined in ``package_form.py`` in this
directory. directory.
""" """
here = os.path.dirname(__file__) here = os.path.dirname(__file__)
rootdir = os.path.dirname(os.path.dirname(here)) rootdir = os.path.dirname(os.path.dirname(here))
our_public_dir = os.path.join(rootdir, 'ckanext', our_public_dir = os.path.join(rootdir, 'ckanext',
'example', 'theme', 'public') 'example', 'theme', 'public')
template_dir = os.path.join(rootdir, 'ckanext', template_dir = os.path.join(rootdir, 'ckanext',
'example', 'theme', 'example', 'theme',
'templates') 'templates')
# set our local template and resource overrides # set our local template and resource overrides
config['extra_public_paths'] = ','.join([our_public_dir, config['extra_public_paths'] = ','.join([our_public_dir,
config.get('extra_public_paths', '')]) config.get('extra_public_paths', '')])
config['extra_template_paths'] = ','.join([template_dir, config['extra_template_paths'] = ','.join([template_dir,
config.get('extra_template_paths', '')]) config.get('extra_template_paths', '')])
# add in the extra.css # add in the extra.css
config['ckan.template_head_end'] = config.get('ckan.template_head_end', '') +\ config['ckan.template_head_end'] = config.get('ckan.template_head_end', '') +\
'<link rel="stylesheet" href="/css/extra.css" type="text/css"> ' '<link rel="stylesheet" href="/css/extra.css" type="text/css"> '
# set the title # set the title
config['ckan.site_title'] = "Example CKAN theme" config['ckan.site_title'] = "Example CKAN theme"
# set the customised package form (see ``setup.py`` for entry point) # set the customised package form (see ``setup.py`` for entry point)
config['package_form'] = "example_form" config['package_form'] = "example_form"
   
def filter(self, stream): def filter(self, stream):
"""Conform to IGenshiStreamFilter interface. """Conform to IGenshiStreamFilter interface.
   
This example filter renames 'frob' to 'foobar' (this string is This example filter renames 'frob' to 'foobar' (this string is
found in the custom ``home/index.html`` template provided as found in the custom ``home/index.html`` template provided as
part of the package). part of the package).
""" """
stream = stream | Transformer('//p[@id="examplething"]/text()')\ stream = stream | Transformer('//p[@id="examplething"]/text()')\
.substitute(r'frob', r'foobar') .substitute(r'frob', r'foobar')
return stream return stream
   
def before_map(self, map): def before_map(self, map):
"""This IRoutes implementation overrides the standard """This IRoutes implementation overrides the standard
``/user/register`` behaviour with a custom controller. You ``/user/register`` behaviour with a custom controller. You
might instead use it to provide a completely new page, for might instead use it to provide a completely new page, for
example. example.
   
Note that we have also provided a custom register form Note that we have also provided a custom register form
template at ``theme/templates/user/register.html``. template at ``theme/templates/user/register.html``.
""" """
# Note that when we set up the route, we must use the form # Hook in our custom user controller at the points of creation
# that gives it a name (i.e. in this case, 'register'), so it # and edition.
# works correctly with the url_for helper:: map.connect('/user/register',
# h.url_for('register')  
map.connect('register',  
'/user/register',  
controller='ckanext.example.controller:CustomUserController', controller='ckanext.example.controller:CustomUserController',
action='custom_register') action='register')
  map.connect('/user/edit',
  controller='ckanext.example.controller:CustomUserController',
  action='edit')
  map.connect('/user/edit/{id:.*}',
  controller='ckanext.example.controller:CustomUserController',
  action='edit')
   
map.connect('/package/new', controller='package_formalchemy', action='new') map.connect('/package/new', controller='package_formalchemy', action='new')
map.connect('/package/edit/{id}', controller='package_formalchemy', action='edit') map.connect('/package/edit/{id}', controller='package_formalchemy', action='edit')
return map return map
   
<html xmlns:py="http://genshi.edgewall.org/" <form id="user-edit" action="" method="post"
xmlns:i18n="http://genshi.edgewall.org/i18n" py:attrs="{'class':'has-errors'} if errors else {}"
xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:i18n="http://genshi.edgewall.org/i18n"
py:strip=""> xmlns:py="http://genshi.edgewall.org/"
  xmlns:xi="http://www.w3.org/2001/XInclude">
<py:match path="primarysidebar">  
<li class="widget-container widget_text">  
<h2>Have an OpenID?</h2>  
<p>  
If you have an account with Google, Yahoo or one of many other  
OpenID providers, you can log in without signing up.  
</p>  
<ul>  
<li>${h.link_to(_('Log in now'), h.url_for(conroller='user', action='login'))}</li>  
</ul>  
</li>  
</py:match>  
   
<py:def function="page_title">Register - User</py:def>  
   
<div py:match="content"> <div class="error-explanation" py:if="error_summary">
<h2>Join the community</h2> <h2>Errors in form</h2>
  <p>The form contains invalid entries:</p>
<form action="/user/register" method="post" class="simple-form" id="register_form"> <ul>
<fieldset> <li py:for="key, error in error_summary.items()">${"%s: %s" % (key, error)}</li>
<legend i18n:msg="site_title">Register with CKAN</legend> </ul>
  </div>
   
<label for="login">Login:</label> <dl>
<input name="login" value="${c.login}" /> <dt><label class="field_opt" for="name">Login:</label></dt>
<br/> <dd><input type="text" name="name" value="${data.get('name','')}" /></dd>
<label for="fullname">Full name (optional):</label> <dd class="instructions basic">3+ chars, using only 'a-z0-9' and '-_'</dd>
<input name="fullname" value="${c.fullname}" /> <dd class="field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd>
<br/>  
<label for="email">E-Mail:</label>  
<input name="email" value="${c.email}" />  
<br/>  
<label for="password1">Password:</label>  
<input type="password" name="password1" value="" />  
<br/>  
<label for="password2">Password (repeat):</label>  
<input type="password" name="password2" value="" />  
<br/>  
</fieldset>  
${h.submit('s', _('Sign up'))}  
</form>  
</div>  
<xi:include href="layout.html" />  
</html>  
   
  <dt><label class="field_opt" for="fullname">Full name:</label></dt>
  <dd><input type="text" name="fullname" value="${data.get('fullname','')}" /></dd>
  <dd class="field_error" py:if="errors.get('fullname', '')">${errors.get('fullname', '')}</dd>
   
  <dt><label class="field_opt" for="email">E-Mail</label></dt>
  <dd><input type="text" name="email" value="${data.get('email','')}" /></dd>
   
  <dt><label class="field_opt" for="password1">Password:</label></dt>
  <dd><input type="password" name="password1" value="" /></dd>
  <dd class="field_error" py:if="errors.get('password1', '')">${errors.get('password1', '')}</dd>
   
  <dt><label class="field_opt" for="password2">Password (repeat):</label></dt>
  <dd><input type="password" name="password2" value="" /></dd>
   
  <dd py:if="g.recaptcha_publickey">
  <script type="text/javascript"
  src="http://www.google.com/recaptcha/api/challenge?k=${g.recaptcha_publickey}">
  </script>
  <noscript>
  <iframe src="http://www.google.com/recaptcha/api/noscript?k=${g.recaptcha_publickey}"
  height="300" width="500" frameborder="0"></iframe><br/>
  <textarea name="recaptcha_challenge_field" rows="3" cols="40"> </textarea>
  <input type="hidden" name="recaptcha_response_field" value="manual_challenge" />
  </noscript>
  </dd>
   
  </dl>
  <input id="save" name="save" type="submit" class="pretty-button primary" value="Register now &raquo;" />
  </form>