From 3b561fb28987c37f290ad9502b8866ceb073c05b Mon Sep 17 00:00:00 2001
From: Renaud Le Gac <renaud.legac@free.fr>
Date: Sat, 15 Jan 2011 17:18:34 +0000
Subject: [PATCH] Replace tab by space to follow PEP8 recommendation.

---
 controllers/default.py              |    6 +-
 controllers/plugin_dbui.py          |  464 ++++++------
 controllers/reports.py              |   69 +-
 models/db_database.py               |   80 +-
 models/db_defaults.py               |   56 +-
 models/db_languages.py              |    6 +-
 models/db_widgets.py                |  206 +++---
 models/plugin_dbui.py               |   30 +-
 modules/plugin_dbui/__init__.py     |   20 +-
 modules/plugin_dbui/basesvc.py      |   58 +-
 modules/plugin_dbui/cfgsvc.py       |  778 ++++++++++----------
 modules/plugin_dbui/dbsvc.py        | 1052 +++++++++++++--------------
 modules/plugin_dbui/foreignfield.py |  137 ++--
 modules/plugin_dbui/setfield.py     |    1 -
 modules/plugin_dbui/tools.py        |  382 +++++-----
 15 files changed, 1671 insertions(+), 1674 deletions(-)

diff --git a/controllers/default.py b/controllers/default.py
index f0b2d616..73676584 100644
--- a/controllers/default.py
+++ b/controllers/default.py
@@ -2,7 +2,7 @@
 
 
 def index():
-	"""Main Controller to run the application.
+    """Main Controller to run the application.
 
-	"""
-	return LOAD('plugin_dbui', 'index', vars=request.vars)
+    """
+    return LOAD('plugin_dbui', 'index', vars=request.vars)
diff --git a/controllers/plugin_dbui.py b/controllers/plugin_dbui.py
index d851ac1e..0fe90159 100644
--- a/controllers/plugin_dbui.py
+++ b/controllers/plugin_dbui.py
@@ -1,13 +1,13 @@
 """ plugin_dbui.py
 
-	Controllers expose by the plugin:
-	
-		index
-		database
-		configuration
+    Controllers expose by the plugin:
+    
+        index
+        database
+        configuration
 
-	Author: R. Le Gac
-	$Id$
+    Author: R. Le Gac
+    $Id$
 
 """
 
@@ -25,234 +25,234 @@ from pprint import pprint
 
 
 def index():
-	""" Main Controller to run the plugin
-
-	SYNOPSIS
-		http://localhost:8000/application/plugin_dbui[?options]
-
-	DESCRIPTION
-		Main controller to launch the plugin.
-		Load the extjs librairy and the associated css files.
-		Load the javascript librairy for the plugin.
-		Define view.
-
-	PUBLIC PARAMETERS WHICH CAN BE SET VIA THE PLUGINMANAGER
-		
-		page_view
-			The page view call by the plugin
-			By default view/plugin_dbui.html
-			
-		script_path
-			The relative path where scripts are located.
-			By default static/plugin_dbui/scripts
-		
-		with_grid_filter
-			Add the javascript librairy for gridFilter
-			
-	PRIVATE PARAMETERS
-	
-		combined_fields
-		field_sets
-		wizards
-		
-	OPTIONS
-
-		debug
-			activate the debug mode on the server side.
-
-		script foo
-			run the javascript foo in the framework defined by the plugin.
-			The scripts are stored in the directory application/static/script 
-			by default the javascript main.js is launch.
-
-	EXAMPLES
-
-		http://localhost:8000/application/plugin_dbui
-		http://localhost:8000/application/plugin_dbui?debug
-		http://localhost:8000/application/plugin_dbui?script=foo&debug
-
-	"""
-	# check the presence of the extjs plugin
-	server_path = os.path.join('applications', request.application)
-	path = os.path.join(server_path, 'static', 'plugin_extjs')
-	if not os.path.exists(path):
-		raise HTTP(500, 'The plugin extjs is not install !')
-
-	# is the plugin MatJax ?
-	path = os.path.join(server_path, 'static', 'plugin_mathjax')
-	withMathJax = (True if os.path.exists(path) else False)
-	
-	# option debug
-	debug = "debug" in request.get_vars
-
-	# option script
-	script = (request.vars.script if "script" in request.vars else "default")
-		
-	if not script.endswith(".js"):
-		script = '%s.js' % script
-
-	script_path = os.path.join(server_path, plugins.dbui.script_path)
-	if script not in os.listdir(script_path):
-		return 'Request script "%s" does not exist !!!' % script
-
-	# css files
-	base = os.path.join(os.path.sep, request.application)
-	extjs = os.path.join(base, 'static', 'plugin_extjs')
-	response.files.append(os.path.join(extjs, 'resources', 'css', 'ext-all.css')) 
-	response.files.append(os.path.join(extjs, 'examples', 'shared', 'icons', 'silk.css'))
-		
-	# javascript libraries
-	if debug:
-		response.files.append(os.path.join(extjs, 'adapter', 'ext', 'ext-base-debug.js'))
-		response.files.append(os.path.join(extjs, 'ext-all-debug.js'))
-
-	else:
-		response.files.append(os.path.join(extjs, 'adapter', 'ext', 'ext-base.js'))
-		response.files.append(os.path.join(extjs, 'ext-all.js'))
-
-	# internationalization or localization (fr, fr-fr, fr-ca, ...)
-	lg = T.accepted_language
-	if len(lg) == 5 and lg[0:2] == lg[3:5]: lg = lg[0:2]
-	response.files.append(os.path.join(extjs, 'src', 'locale', 'ext-lang-%s.js' % lg))
-	
-	# plugin library -- the minified version
-	path = os.path.join(server_path, 'static', 'plugin_dbui', 'dbui.min.js')
-	if not debug and os.path.exists(path):
-		response.files.append(os.path.join(base, 'static', 'plugin_dbui', 'dbui.min.js'))
-		
-	# plugin libraries -- full version -- respect the alphabetic order
-	else:
-		li = []
-		appjs = os.path.join(base, 'static', 'plugin_dbui', 'lib')
-		for file in os.listdir(os.path.join(server_path, 'static', 'plugin_dbui', 'lib')):
-			if file.endswith(".js"):
-				li.append(os.path.join(appjs, file))
-	
-		li.sort()
-		response.files.extend(li)
-	
-	# main application script
-	response.files.append(os.path.join(base, plugins.dbui.script_path, script))
-
-	# page view
-	response.view = plugins.dbui.page_view
-
-	return dict(debug=debug, withMathJax=withMathJax)    
+    """ Main Controller to run the plugin
+
+    SYNOPSIS
+        http://localhost:8000/application/plugin_dbui[?options]
+
+    DESCRIPTION
+        Main controller to launch the plugin.
+        Load the extjs librairy and the associated css files.
+        Load the javascript librairy for the plugin.
+        Define view.
+
+    PUBLIC PARAMETERS WHICH CAN BE SET VIA THE PLUGINMANAGER
+        
+        page_view
+            The page view call by the plugin
+            By default view/plugin_dbui.html
+            
+        script_path
+            The relative path where scripts are located.
+            By default static/plugin_dbui/scripts
+        
+        with_grid_filter
+            Add the javascript librairy for gridFilter
+            
+    PRIVATE PARAMETERS
+    
+        combined_fields
+        field_sets
+        wizards
+        
+    OPTIONS
+
+        debug
+            activate the debug mode on the server side.
+
+        script foo
+            run the javascript foo in the framework defined by the plugin.
+            The scripts are stored in the directory application/static/script 
+            by default the javascript main.js is launch.
+
+    EXAMPLES
+
+        http://localhost:8000/application/plugin_dbui
+        http://localhost:8000/application/plugin_dbui?debug
+        http://localhost:8000/application/plugin_dbui?script=foo&debug
+
+    """
+    # check the presence of the extjs plugin
+    server_path = os.path.join('applications', request.application)
+    path = os.path.join(server_path, 'static', 'plugin_extjs')
+    if not os.path.exists(path):
+        raise HTTP(500, 'The plugin extjs is not install !')
+
+    # is the plugin MatJax ?
+    path = os.path.join(server_path, 'static', 'plugin_mathjax')
+    withMathJax = (True if os.path.exists(path) else False)
+    
+    # option debug
+    debug = "debug" in request.get_vars
+
+    # option script
+    script = (request.vars.script if "script" in request.vars else "default")
+        
+    if not script.endswith(".js"):
+        script = '%s.js' % script
+
+    script_path = os.path.join(server_path, plugins.dbui.script_path)
+    if script not in os.listdir(script_path):
+        return 'Request script "%s" does not exist !!!' % script
+
+    # css files
+    base = os.path.join(os.path.sep, request.application)
+    extjs = os.path.join(base, 'static', 'plugin_extjs')
+    response.files.append(os.path.join(extjs, 'resources', 'css', 'ext-all.css')) 
+    response.files.append(os.path.join(extjs, 'examples', 'shared', 'icons', 'silk.css'))
+        
+    # javascript libraries
+    if debug:
+        response.files.append(os.path.join(extjs, 'adapter', 'ext', 'ext-base-debug.js'))
+        response.files.append(os.path.join(extjs, 'ext-all-debug.js'))
+
+    else:
+        response.files.append(os.path.join(extjs, 'adapter', 'ext', 'ext-base.js'))
+        response.files.append(os.path.join(extjs, 'ext-all.js'))
+
+    # internationalization or localization (fr, fr-fr, fr-ca, ...)
+    lg = T.accepted_language
+    if len(lg) == 5 and lg[0:2] == lg[3:5]: lg = lg[0:2]
+    response.files.append(os.path.join(extjs, 'src', 'locale', 'ext-lang-%s.js' % lg))
+    
+    # plugin library -- the minified version
+    path = os.path.join(server_path, 'static', 'plugin_dbui', 'dbui.min.js')
+    if not debug and os.path.exists(path):
+        response.files.append(os.path.join(base, 'static', 'plugin_dbui', 'dbui.min.js'))
+        
+    # plugin libraries -- full version -- respect the alphabetic order
+    else:
+        li = []
+        appjs = os.path.join(base, 'static', 'plugin_dbui', 'lib')
+        for file in os.listdir(os.path.join(server_path, 'static', 'plugin_dbui', 'lib')):
+            if file.endswith(".js"):
+                li.append(os.path.join(appjs, file))
+    
+        li.sort()
+        response.files.extend(li)
+    
+    # main application script
+    response.files.append(os.path.join(base, plugins.dbui.script_path, script))
+
+    # page view
+    response.view = plugins.dbui.page_view
+
+    return dict(debug=debug, withMathJax=withMathJax)    
 
 
 def database():
-	""" Controllers to perform operation on the database.
-	Act as a bridge between the web browser and the database service.
-
-	All information defining the operation are sent via the POST/GET methods.
-	It contains steering keyword defining the requested action.
-
-	Return a JSON string when the action is successful.
-	Otherwise return and HTTP error
-
-	"""
-	debug = "debug" in request.vars and (request.vars.debug == 'true')
-	
-	if debug:
-		print "\nSTART DATABASE CONTROLLER"
-		print "Method:", request.env.request_method
-		print "Arguments:", request.vars
-
-	try:
-
-		# decode the data dictionary
-		if "data" in request.vars and isinstance(request.vars.data, str):
-			request.vars.data = json.loads(request.vars.data)
-		
-		di = dbSvc.proceed(request.vars)
-		resp = json.dumps(di)
-
-	except BaseException, e:
-		print "Exception in the database controller:"
-		print "-"*72
-		traceback.print_exc(file=sys.stdout)
-		print "-"*72
-		raise HTTP(500, e)
-		
-	if debug:
-		print "\nDATABASE CONTROLLER RESPONSE IS:"
-		pprint(di)
-		print "END OF DATABASE CONTROLLER\n"
-
-	return resp
+    """ Controllers to perform operation on the database.
+    Act as a bridge between the web browser and the database service.
+
+    All information defining the operation are sent via the POST/GET methods.
+    It contains steering keyword defining the requested action.
+
+    Return a JSON string when the action is successful.
+    Otherwise return and HTTP error
+
+    """
+    debug = "debug" in request.vars and (request.vars.debug == 'true')
+    
+    if debug:
+        print "\nSTART DATABASE CONTROLLER"
+        print "Method:", request.env.request_method
+        print "Arguments:", request.vars
+
+    try:
+
+        # decode the data dictionary
+        if "data" in request.vars and isinstance(request.vars.data, str):
+            request.vars.data = json.loads(request.vars.data)
+        
+        di = dbSvc.proceed(request.vars)
+        resp = json.dumps(di)
+
+    except BaseException, e:
+        print "Exception in the database controller:"
+        print "-"*72
+        traceback.print_exc(file=sys.stdout)
+        print "-"*72
+        raise HTTP(500, e)
+        
+    if debug:
+        print "\nDATABASE CONTROLLER RESPONSE IS:"
+        pprint(di)
+        print "END OF DATABASE CONTROLLER\n"
+
+    return resp
 
 
 def configuration():
-	""" Controllers/router for the web service cfgSVc
-	Act as a bridge between the web browser and the configuration service.
-
-	Relies on Ext.Direct see specification:
-	http://www.extjs.com/products/extjs/direct.php
-
-	Act as a router for the cfgSvc service (see Ext.Direct Extjs 3.1.1)
-	Decode the raw HTTP post request:
-	{action: "cfgSvc", method: x, data: y, tip: i, type: "rpc"}
-	
-	Return a JSON string with the result of the request: 
-	{action: "cfgSvc", method: x, result: y, tip: i, type: "rpc"}
-	
-	Otherwise return and HTTP error.
-
-	For more detail on Ext.Direct see specification:
-	http://www.extjs.com/products/extjs/direct.php
-	
-
-	"""
-	debug = "debug" in request.vars and (request.vars.debug == 'true')
-	
-	# NOTE: the server cfgSvc is start at the beginning
-	# when building the model (db.py)
-	
-	# decode the raw HTTP post
-	# ExtJs.3.1.1 {action: "xx", method: "y", data: [], type:"rpc", tid:2}
-	
-	req = json.loads(request.body.read())
-
-	if debug:
-		print "\nSTART CONFIGURATION CONTROLLER:" 
-		pprint(req)
-	
-	# receive a list of dictionary for a multi-request
-	li = []
-	if isinstance(req, list):
-		li.extend(req)
-	else:
-		li.append(req)
-	
-	for di in li:
-		# construct the command to be executed
-		args = di["data"]
-		cmd = "%s.%s(*args)" % (di["action"], di["method"])
-		
-		if args == None:
-			cmd = "%s.%s()" % (di["action"], di["method"])
-			
-		# execute the command and return HTTP exception
-		# when an internal error occurred
-		try:
-			res = eval(cmd)
-		except BaseException, e:
-			print "Exception in the configuration controller:"
-			print "-"*72
-			traceback.print_exc(file=sys.stdout)
-			print "-"*72
-			raise HTTP(500, e)
-	
-		# NOTE: the client expect the following object:
-		# ExtJs.3.1.1 {action: "xx", method: "y", result: [], type:"rpc", tid:2}
-		
-		del di["data"]
-		di["result"] = res 
-
-	if debug:
-		print "\nCONFIGURATION CONTROLLER RESPONSE IS:"
-		pprint(li)
-		print "\nEND OF CONFIGURATION CONTROLLER\n"
-
-	# encode the data as a json string
-	return json.dumps(li)
\ No newline at end of file
+    """ Controllers/router for the web service cfgSVc
+    Act as a bridge between the web browser and the configuration service.
+
+    Relies on Ext.Direct see specification:
+    http://www.extjs.com/products/extjs/direct.php
+
+    Act as a router for the cfgSvc service (see Ext.Direct Extjs 3.1.1)
+    Decode the raw HTTP post request:
+    {action: "cfgSvc", method: x, data: y, tip: i, type: "rpc"}
+    
+    Return a JSON string with the result of the request: 
+    {action: "cfgSvc", method: x, result: y, tip: i, type: "rpc"}
+    
+    Otherwise return and HTTP error.
+
+    For more detail on Ext.Direct see specification:
+    http://www.extjs.com/products/extjs/direct.php
+    
+
+    """
+    debug = "debug" in request.vars and (request.vars.debug == 'true')
+    
+    # NOTE: the server cfgSvc is start at the beginning
+    # when building the model (db.py)
+    
+    # decode the raw HTTP post
+    # ExtJs.3.1.1 {action: "xx", method: "y", data: [], type:"rpc", tid:2}
+    
+    req = json.loads(request.body.read())
+
+    if debug:
+        print "\nSTART CONFIGURATION CONTROLLER:" 
+        pprint(req)
+    
+    # receive a list of dictionary for a multi-request
+    li = []
+    if isinstance(req, list):
+        li.extend(req)
+    else:
+        li.append(req)
+    
+    for di in li:
+        # construct the command to be executed
+        args = di["data"]
+        cmd = "%s.%s(*args)" % (di["action"], di["method"])
+        
+        if args == None:
+            cmd = "%s.%s()" % (di["action"], di["method"])
+            
+        # execute the command and return HTTP exception
+        # when an internal error occurred
+        try:
+            res = eval(cmd)
+        except BaseException, e:
+            print "Exception in the configuration controller:"
+            print "-"*72
+            traceback.print_exc(file=sys.stdout)
+            print "-"*72
+            raise HTTP(500, e)
+    
+        # NOTE: the client expect the following object:
+        # ExtJs.3.1.1 {action: "xx", method: "y", result: [], type:"rpc", tid:2}
+        
+        del di["data"]
+        di["result"] = res 
+
+    if debug:
+        print "\nCONFIGURATION CONTROLLER RESPONSE IS:"
+        pprint(li)
+        print "\nEND OF CONFIGURATION CONTROLLER\n"
+
+    # encode the data as a json string
+    return json.dumps(li)
\ No newline at end of file
diff --git a/controllers/reports.py b/controllers/reports.py
index 0ca48497..5a6e4bd7 100644
--- a/controllers/reports.py
+++ b/controllers/reports.py
@@ -2,45 +2,44 @@
 
 
 def index():
-	"""Main Controller handling report.
+    """Main Controller handling report.
 
-	"""
-	return 'Report section'
+    """
+    return 'Report section'
 
 
 def report_1():
-	"""First trial
-	
-	"""
-	# Resolve foreign key
-	query = db.publications.id_collaborations == db.collaborations.id
-	query = (query) & (db.publications.id_publishers == db.publishers.id)
-	query = (query) & (db.publications.id_categories_aeres == db.categories_aeres.id)
-
-	# publication with comite de lecture
-	query = (query) & (db.publications.conference_title == '')
-	query = (query) & ((db.categories_aeres.code  == 'ACL') | (db.categories_aeres.code  == 'ACLN'))
-	
-	# Interrogate the database
-	rows = db(query).select(db.publications.title,
-					   		db.publications.authors,
-					   		db.publications.id_collaborations,
-					   		db.collaborations.collaboration,
-					   		db.publishers.abbreviation,
-					   		db.publications.year,
-					   		db.publications.doi,
-					   		db.publications.volume,
-					   		db.publications.first_page,
-					   		db.publications.last_page,
-					   		db.publications.conference_title,
-					   		db.categories_aeres.code)
-	
-		
-	return {'title': 'Rapport 1', 'publis': rows}
+    """First trial
+    
+    """
+    # Resolve foreign key
+    query = db.publications.id_collaborations == db.collaborations.id
+    query = (query) & (db.publications.id_publishers == db.publishers.id)
+    query = (query) & (db.publications.id_categories_aeres == db.categories_aeres.id)
+
+    # publication with comite de lecture
+    query = (query) & (db.publications.conference_title == '')
+    query = (query) & ((db.categories_aeres.code  == 'ACL') | (db.categories_aeres.code  == 'ACLN'))
+    
+    # Interrogate the database
+    rows = db(query).select(db.publications.title,
+                            db.publications.authors,
+                            db.publications.id_collaborations,
+                            db.collaborations.collaboration,
+                            db.publishers.abbreviation,
+                            db.publications.year,
+                            db.publications.doi,
+                            db.publications.volume,
+                            db.publications.first_page,
+                            db.publications.last_page,
+                            db.publications.conference_title,
+                            db.categories_aeres.code)
+
+    return {'title': 'Rapport 1', 'publis': rows}
 
 
 def report_2():
-	""" Test jsmath on a standalone page
-	
-	"""
-	return {}
+    """ Test jsmath on a standalone page
+    
+    """
+    return {}
diff --git a/models/db_database.py b/models/db_database.py
index cdc82c69..34e848e1 100644
--- a/models/db_database.py
+++ b/models/db_database.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
 """ db_database
 
-	Defined the tables for your applications.
-	Initialized the web2py-dbui plugins
-	
-	$Id$
-	
+    Defined the tables for your applications.
+    Initialized the web2py-dbui plugins
+    
+    $Id$
+    
 """
 from datetime import datetime
 from gluon.tools import PluginManager
@@ -16,10 +16,10 @@ from gluon.tools import PluginManager
 #
 #-------------------------------------------------------------------------------
 plugins = PluginManager()
-plugins.dbui.script_path = 'static/scripts'		# js scripts for your applications
+plugins.dbui.script_path = 'static/scripts'        # js scripts for your applications
 #plugins.dbui.page_view = 'plugin_dbui.html'
 
-T.lazy = False		# immediate translation
+T.lazy = False        # immediate translation
 #-------------------------------------------------------------------------------
 #
 # connection to the database
@@ -33,8 +33,8 @@ db = DAL('sqlite://storage.sqlite')       # if not, use SQLite or other DB
 #
 #-------------------------------------------------------------------------------
 db.define_table("categories_aeres",
-	Field("code", "string", notnull=True),
-	Field("definition", "text", notnull=True))
+    Field("code", "string", notnull=True),
+    Field("definition", "text", notnull=True))
 
 #-------------------------------------------------------------------------------
 #
@@ -42,7 +42,7 @@ db.define_table("categories_aeres",
 #
 #-------------------------------------------------------------------------------
 db.define_table("collaborations",
-	Field("collaboration", "string", notnull=True))
+    Field("collaboration", "string", notnull=True))
 
 #-------------------------------------------------------------------------------
 #
@@ -50,8 +50,8 @@ db.define_table("collaborations",
 #
 #-------------------------------------------------------------------------------
 db.define_table("countries",
-	Field("country", "string", notnull=True),
-	Field("abbreviation", "string", notnull=True))
+    Field("country", "string", notnull=True),
+    Field("abbreviation", "string", notnull=True))
 
 #-------------------------------------------------------------------------------
 #
@@ -59,7 +59,7 @@ db.define_table("countries",
 #
 #-------------------------------------------------------------------------------
 db.define_table("projects",
-	Field("project", "string", notnull=True))
+    Field("project", "string", notnull=True))
 
 #-------------------------------------------------------------------------------
 #
@@ -67,8 +67,8 @@ db.define_table("projects",
 #
 #-------------------------------------------------------------------------------
 db.define_table("publishers",
-	Field("publisher", "string", notnull=True),
-	Field("abbreviation", "string", notnull=True))
+    Field("publisher", "string", notnull=True),
+    Field("abbreviation", "string", notnull=True))
 
 #-------------------------------------------------------------------------------
 #
@@ -76,7 +76,7 @@ db.define_table("publishers",
 #
 #-------------------------------------------------------------------------------
 db.define_table("reports",
-	Field("type", "string", notnull=True))
+    Field("type", "string", notnull=True))
 
 #-------------------------------------------------------------------------------
 #
@@ -84,7 +84,7 @@ db.define_table("reports",
 #
 #-------------------------------------------------------------------------------
 db.define_table("teams",
-	Field("team", "string", notnull=True))
+    Field("team", "string", notnull=True))
 
 #-------------------------------------------------------------------------------
 #
@@ -130,29 +130,29 @@ tp_speaker = \
 # the table
 #
 db.define_table("publications",
-	Field("title", "text", notnull=True),
-	Field("authors", "string", notnull=True, comment=tp_authors),
-	Field("id_collaborations", db.collaborations, default=1, label="Collaboration"),
-	Field("id_publishers", db.publishers, default=1,  label="Publisher"),
-	Field("year", "integer", notnull=True, default=year, requires=IS_INT_IN_RANGE(1900, year+1)),
-	Field("doi", "string"),
-	Field("volume", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))),
-	Field("first_page", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))),
-	Field("last_page", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))),
-	Field("e_print", "string", requires=IS_EMPTY_OR(IS_URL())),
-	Field("conference_title", "text", label='Title'),
-	Field("conference_url", "string", label='url', requires=IS_EMPTY_OR(IS_URL())),
-	Field("conference_start", "date", label='Start date'),
-	Field("conference_end", "date", label='End date'),
-	Field("conference_town", "string", label='Town'),
-	Field("id_countries", db.countries, default=1, label='Country'),
-	Field("conference_speaker", "string", label='Speaker', comment=tp_speaker),
-	Field("report_numbers", "string", comment=tr_report_numbers),
-	Field("id_reports", db.reports, default=1, label="Report type"),
-	Field("authors_cppm", "text", notnull=True, comment=tp_authors_cppm),
-	Field("id_teams", db.teams, default=1, label='Team'),
-	Field("id_projects", db.projects, default=1, label='Projects'),
-	Field("id_categories_aeres", db.categories_aeres, default=1, label='AERES'))
+    Field("title", "text", notnull=True),
+    Field("authors", "string", notnull=True, comment=tp_authors),
+    Field("id_collaborations", db.collaborations, default=1, label="Collaboration"),
+    Field("id_publishers", db.publishers, default=1,  label="Publisher"),
+    Field("year", "integer", notnull=True, default=year, requires=IS_INT_IN_RANGE(1900, year+1)),
+    Field("doi", "string"),
+    Field("volume", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))),
+    Field("first_page", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))),
+    Field("last_page", "integer", requires=IS_EMPTY_OR(IS_INT_IN_RANGE(1, None))),
+    Field("e_print", "string", requires=IS_EMPTY_OR(IS_URL())),
+    Field("conference_title", "text", label='Title'),
+    Field("conference_url", "string", label='url', requires=IS_EMPTY_OR(IS_URL())),
+    Field("conference_start", "date", label='Start date'),
+    Field("conference_end", "date", label='End date'),
+    Field("conference_town", "string", label='Town'),
+    Field("id_countries", db.countries, default=1, label='Country'),
+    Field("conference_speaker", "string", label='Speaker', comment=tp_speaker),
+    Field("report_numbers", "string", comment=tr_report_numbers),
+    Field("id_reports", db.reports, default=1, label="Report type"),
+    Field("authors_cppm", "text", notnull=True, comment=tp_authors_cppm),
+    Field("id_teams", db.teams, default=1, label='Team'),
+    Field("id_projects", db.projects, default=1, label='Projects'),
+    Field("id_categories_aeres", db.categories_aeres, default=1, label='AERES'))
 
 #
 # define foreign keys
diff --git a/models/db_defaults.py b/models/db_defaults.py
index 71ff3c24..7af1bf03 100644
--- a/models/db_defaults.py
+++ b/models/db_defaults.py
@@ -1,53 +1,53 @@
 # -*- coding: utf-8 -*-
 """ db_insert
 
-	Insert defaults values in the tables
-	
-	$Id$
-	
+    Insert defaults values in the tables
+    
+    $Id$
+    
 """
 
 if not db(db.categories_aeres.id).count():
-	db.categories_aeres.insert(id=1, code= T('undefined'), definition=T('undefined'))
+    db.categories_aeres.insert(id=1, code= T('undefined'), definition=T('undefined'))
 
 
 if not db(db.collaborations.id).count():
-	db.collaborations.insert(id=1, collaboration=T('undefined'))
-	db.collaborations.insert(collaboration='Atlas Collaboration')
-	db.collaborations.insert(collaboration='H1 Collaboration')
-	db.collaborations.insert(collaboration='LHCb Collaboration')
+    db.collaborations.insert(id=1, collaboration=T('undefined'))
+    db.collaborations.insert(collaboration='Atlas Collaboration')
+    db.collaborations.insert(collaboration='H1 Collaboration')
+    db.collaborations.insert(collaboration='LHCb Collaboration')
 
 
 if not db(db.countries.id).count():
-	db.countries.insert(id=1, country=T('undefined'), abbreviation=T('undefined'))
-	db.countries.insert(country="France", abbreviation="FR")
-	db.countries.insert(country="Allemagne", abbreviation="DE")
-	db.countries.insert(country="Etat-Unis", abbreviation="USA")
+    db.countries.insert(id=1, country=T('undefined'), abbreviation=T('undefined'))
+    db.countries.insert(country="France", abbreviation="FR")
+    db.countries.insert(country="Allemagne", abbreviation="DE")
+    db.countries.insert(country="Etat-Unis", abbreviation="USA")
 
 
 if not db(db.projects.id).count():
-	db.projects.insert(id=1, project=T('undefined'))
+    db.projects.insert(id=1, project=T('undefined'))
 
 
 if not db(db.publishers.id).count():
-	db.publishers.insert(id=1, publisher=T('undefined'), abbreviation=T('undefined'))
+    db.publishers.insert(id=1, publisher=T('undefined'), abbreviation=T('undefined'))
 
-	db.publishers.insert(publisher="Nuclear Instruments and Methods", 
-						 abbreviation="Nucl. Instrum. Methods Phys. Res.")	
-	db.publishers.insert(publisher="Physics Letter B", 
-						 abbreviation="Phys. Lett. B")
+    db.publishers.insert(publisher="Nuclear Instruments and Methods", 
+                         abbreviation="Nucl. Instrum. Methods Phys. Res.")    
+    db.publishers.insert(publisher="Physics Letter B", 
+                         abbreviation="Phys. Lett. B")
 
 
 if not db(db.reports.id).count():
-	db.reports.insert(id=1, type=T('undefined'))
-	db.reports.insert(type="LHCb Note")
+    db.reports.insert(id=1, type=T('undefined'))
+    db.reports.insert(type="LHCb Note")
 
 
 if not db(db.teams.id).count():
-  db.teams.insert(id=1, team=T('undefined'))
-  db.teams.insert(team="Atlas")
-  db.teams.insert(team="Antares")
-  db.teams.insert(team="D0")
-  db.teams.insert(team="H1")
-  db.teams.insert(team="LHCb")
-  db.teams.insert(team="Renoir")
+    db.teams.insert(id=1, team=T('undefined'))
+    db.teams.insert(team="Atlas")
+    db.teams.insert(team="Antares")
+    db.teams.insert(team="D0")
+    db.teams.insert(team="H1")
+    db.teams.insert(team="LHCb")
+    db.teams.insert(team="Renoir")
diff --git a/models/db_languages.py b/models/db_languages.py
index d72fb081..fb3fd361 100644
--- a/models/db_languages.py
+++ b/models/db_languages.py
@@ -1,9 +1,9 @@
 # -*- coding: utf-8 -*-
 """ db_languages
 
-	Defined the languages for your applications.
-	$Id$
-	
+    Defined the languages for your applications.
+    $Id$
+    
 """
 
 # Set the mother tongue of our application
diff --git a/models/db_widgets.py b/models/db_widgets.py
index 9f27a55d..88d1e9f6 100644
--- a/models/db_widgets.py
+++ b/models/db_widgets.py
@@ -1,9 +1,9 @@
 # -*- coding: utf-8 -*-
 """ db_widgets
 
-	Customize forms and grids widgets
-	$Id$
-	
+    Customize forms and grids widgets
+    $Id$
+    
 """
 from datetime import datetime
 pgm = local_import('plugin_dbui')
@@ -15,13 +15,13 @@ pgm = local_import('plugin_dbui')
 #
 #-------------------------------------------------------------------------------
 #pgm.hidden_tables('publishers', 
-#				  'categories_aeres',
-#				  'categories_commission',
-##				  'collaborations',
-#				  'countries',
-##				  'teams',
-#				  'projects',
-#				  'publishers')
+#                  'categories_aeres',
+#                  'categories_commission',
+##                  'collaborations',
+#                  'countries',
+##                  'teams',
+#                  'projects',
+#                  'publishers')
 
 #-------------------------------------------------------------------------------
 #
@@ -29,8 +29,8 @@ pgm = local_import('plugin_dbui')
 #
 #-------------------------------------------------------------------------------
 pgm.customize_grid(plugins=['pGridRowEditorContextMenu',
-						    'pGridPaging',
-						    'pGridMathJax'])
+                            'pGridPaging',
+                            'pGridMathJax'])
 
 
 #-------------------------------------------------------------------------------
@@ -45,65 +45,65 @@ pgm.customize_field('publications', 'year', maxValue=datetime.now().year)
 
 # add fieldset to form
 fields = ['title',
-		  'authors',
-		  'id_collaborations',
-		  'id_publishers',
-		  'year',
-		  'doi',
-		  'volume',
-		  'first_page',
-		  'last_page',
-		  'e_print']
+          'authors',
+          'id_collaborations',
+          'id_publishers',
+          'year',
+          'doi',
+          'volume',
+          'first_page',
+          'last_page',
+          'e_print']
 
 pgm.customize_form_with_field_set('publications', T('General'), fields, 
-															 	collapsible=True,
-															 	flex=1)
+                                                                 collapsible=True,
+                                                                 flex=1)
 
 fields = ['conference_title',
-		  'conference_url',
- 		  'conference_start',
-		  'conference_end',
-	 	  'conference_town',
-	 	  'id_countries',
- 	 	  'conference_speaker']
+          'conference_url',
+          'conference_start',
+          'conference_end',
+          'conference_town',
+          'id_countries',
+          'conference_speaker']
 
 pgm.customize_form_with_field_set('publications', T('Conference'), fields,
-																   spacer=78,
-												 				   collapsible=True,
-																   flex=1)
+                                                                   spacer=78,
+                                                                    collapsible=True,
+                                                                   flex=1)
 
 
 fields = ['authors_cppm',
-		  'id_teams',
-		  'id_projects',
-	   	  'id_categories_aeres']
+          'id_teams',
+          'id_projects',
+          'id_categories_aeres']
 
 pgm.customize_form_with_field_set('publications', 'CPPM', fields,
-												 		  collapsible=True,
-												 		  flex=1)
+                                                           collapsible=True,
+                                                           flex=1)
 
 fields = ['report_numbers', 'id_reports']
 
 pgm.customize_form_with_field_set('publications', T('Report'), fields,
-															   spacer=90,
-												               collapsible=True,
-												 			   flex=1)
+                                                               spacer=90,
+                                                               collapsible=True,
+                                                                flex=1)
 
 
 pgm.customize_form('publications',
-					buttonAlign='right', 
-					labelWidth=100,
-				    labelAlign='right',
-#				    layout={'type': 'hbox', 'padding': 0, 'align': 'stretchmax'},
-					layout={'type': 'table', 'columns': 2},
-				    layoutConfig={'tableAttrs': {'style': {'width': '100%', 'cellspacing': 10}}},
-#				    layout='table',
-#				    layoutConfig={'columns': 2,
-#								  'tableAttrs': {'style': {'width': '100%', 'cellspacing': 10}}},
-#				    default={'frame': True, 'width': 200, 'heigth': 200},
-#					width=700,
-#					anchor='100%'
-				    )
+                    buttonAlign='right', 
+                    labelWidth=100,
+                    labelAlign='right',
+#                    layout={'type': 'hbox', 'padding': 0, 'align': 'stretchmax'},
+                    layout={'type': 'table', 'columns': 2},
+                    layoutConfig={'tableAttrs': {'style': {'width': '100%', 'cellspacing': 10}}},
+#                    layout='table',
+#                    layoutConfig={'columns': 2,
+#                                  'tableAttrs': {'style': {'width': '100%', 'cellspacing': 10}}},
+#                    default={'frame': True, 'width': 200, 'heigth': 200},
+#                    width=700,
+#                    anchor='100%'
+                    )
 
 #-------------------------------------------------------------------------------
 #
@@ -113,55 +113,55 @@ pgm.customize_form('publications',
 
 # add a template column handling general information
 tpl = ['<b>{PublicationsTitle}</b><br>',
-	   '{PublicationsAuthors}',
-	   '<tpl if="PublicationsId_collaborations  &gt; 1">, {CollaborationsCollaboration}</tpl>',
-	   '<tpl if="PublicationsDoi"><br>{PublicationsDoi}</tpl>',
-	   '<tpl if="PublicationsId_publishers  &gt; 1"><br>{PublishersAbbreviation}',
-	       '<tpl if="PublicationsVolume"> {PublicationsVolume}</tpl>',
-	   	   '<tpl if="PublicationsYear"> ({PublicationsYear}) </tpl>',
-	   	   '<tpl if="PublicationsFirst_page"> {PublicationsFirst_page}</tpl>',
-	   	   '<tpl if="PublicationsLast_page">-{PublicationsLast_page}</tpl>',
-	   '</tpl>',
-	   '<tpl if="PublicationsE_print"><br>{PublicationsE_print}</tpl>',
-	   '<tpl if="PublicationsConference_title"><br><br>{PublicationsConference_title}',
-	   		'<tpl if="PublicationsConference_town"><br>{PublicationsConference_town}</tpl>',
-	   		'<tpl if="CountriesCountry">, {CountriesCountry}</tpl>',
-	   		'<tpl if="PublicationsConference_start">, {PublicationsConference_start}</tpl>',
-	   		'<tpl if="PublicationsConference_end"> - {PublicationsConference_end}</tpl>',
-	   		'<tpl if="PublicationsConference_url"><br>{PublicationsConference_url}</tpl>',
-	   		'<br>%s: {PublicationsConference_speaker}' % T('Speaker'),
-	   '</tpl>',
-	   '<tpl if="PublicationsReport_numbers"><br><br>',
-	   		'<tpl if="PublicationsId_reports &gt; 1">{ReportsType}, </tpl>',
-	   		'{PublicationsReport_numbers}',
-	   '</tpl>',
-	   '<br>Auteurs CPPM : {PublicationsAuthors_cppm}'
-	   ]
+       '{PublicationsAuthors}',
+       '<tpl if="PublicationsId_collaborations  &gt; 1">, {CollaborationsCollaboration}</tpl>',
+       '<tpl if="PublicationsDoi"><br>{PublicationsDoi}</tpl>',
+       '<tpl if="PublicationsId_publishers  &gt; 1"><br>{PublishersAbbreviation}',
+           '<tpl if="PublicationsVolume"> {PublicationsVolume}</tpl>',
+           '<tpl if="PublicationsYear"> ({PublicationsYear}) </tpl>',
+           '<tpl if="PublicationsFirst_page"> {PublicationsFirst_page}</tpl>',
+           '<tpl if="PublicationsLast_page">-{PublicationsLast_page}</tpl>',
+       '</tpl>',
+       '<tpl if="PublicationsE_print"><br>{PublicationsE_print}</tpl>',
+       '<tpl if="PublicationsConference_title"><br><br>{PublicationsConference_title}',
+           '<tpl if="PublicationsConference_town"><br>{PublicationsConference_town}</tpl>',
+           '<tpl if="CountriesCountry">, {CountriesCountry}</tpl>',
+           '<tpl if="PublicationsConference_start">, {PublicationsConference_start}</tpl>',
+           '<tpl if="PublicationsConference_end"> - {PublicationsConference_end}</tpl>',
+           '<tpl if="PublicationsConference_url"><br>{PublicationsConference_url}</tpl>',
+           '<br>%s: {PublicationsConference_speaker}' % T('Speaker'),
+       '</tpl>',
+       '<tpl if="PublicationsReport_numbers"><br><br>',
+           '<tpl if="PublicationsId_reports &gt; 1">{ReportsType}, </tpl>',
+           '{PublicationsReport_numbers}',
+       '</tpl>',
+       '<br>Auteurs CPPM : {PublicationsAuthors_cppm}'
+       ]
 
 hidden = ['title', 
-		  'authors', 
-		  'id_collaborations', 
-		  'id_publishers',
-		  'doi', 
-		  'volume',
-		  'first_page',
-		  'last_page',
-		  'e_print',
-		  'conference_title',
-		  'conference_url',
-		  'conference_start',
-		  'conference_end',
-		  'conference_town',
-		  'id_countries',
-		  'conference_speaker',
-		  'report_numbers',
-		  'id_reports',
-		  'authors_cppm']
+          'authors', 
+          'id_collaborations', 
+          'id_publishers',
+          'doi', 
+          'volume',
+          'first_page',
+          'last_page',
+          'e_print',
+          'conference_title',
+          'conference_url',
+          'conference_start',
+          'conference_end',
+          'conference_town',
+          'id_countries',
+          'conference_speaker',
+          'report_numbers',
+          'id_reports',
+          'authors_cppm']
 
 pgm.customize_grid_with_template_column('publications', T('Publication'), tpl, 
-									                                   	  1, 
-									                                   	  hidden,
-									                                      width=700)
+                                                                          1, 
+                                                                          hidden,
+                                                                          width=700)
 
 #-------------------------------------------------------------------------------
 #
@@ -169,10 +169,10 @@ pgm.customize_grid_with_template_column('publications', T('Publication'), tpl,
 #
 #-------------------------------------------------------------------------------
 filters = [('year', '==', T('select publication for a given year')),
-		   ('id_teams', '==', T('select publications for a given team')),
-		   ('id_projects', '==', T('select publications for a given project')),
-		   ('id_categories_aeres', '==', T('select publications with a given AERES code')),
-		   ('authors_cppm', 'contains', T('select publications for a given CPPM author')),
-		]
+           ('id_teams', '==', T('select publications for a given team')),
+           ('id_projects', '==', T('select publications for a given project')),
+           ('id_categories_aeres', '==', T('select publications with a given AERES code')),
+           ('authors_cppm', 'contains', T('select publications for a given CPPM author')),
+        ]
 
 pgm.define_grid_filter('publications', filters, width=300)
\ No newline at end of file
diff --git a/models/plugin_dbui.py b/models/plugin_dbui.py
index 158e09ca..a540a55f 100644
--- a/models/plugin_dbui.py
+++ b/models/plugin_dbui.py
@@ -1,4 +1,4 @@
-"""	$Id$ """
+"""    $Id$ """
 
 from gluon.tools import PluginManager
 pgmodule = local_import('plugin_dbui')
@@ -6,20 +6,20 @@ pgmodule = local_import('plugin_dbui')
 
 # define plugin parameters and their default value
 plugins = PluginManager('dbui',
-						combined_fields=None,
-						customized_fields={},
-						customized_forms={},
-						customized_grids={},
-						customize_grid_with_template_column={},
-						customized_viewport=None,
-						field_sets={},
-						grid_filters={},
-						hidden_columns={},
-						hidden_tables=[],
-						page_view='plugin_dbui.html', 
-						script_path='static/plugin_dbui/scripts',
-						template_columns={},
-						wizards=None)
+                        combined_fields=None,
+                        customized_fields={},
+                        customized_forms={},
+                        customized_grids={},
+                        customize_grid_with_template_column={},
+                        customized_viewport=None,
+                        field_sets={},
+                        grid_filters={},
+                        hidden_columns={},
+                        hidden_tables=[],
+                        page_view='plugin_dbui.html', 
+                        script_path='static/plugin_dbui/scripts',
+                        template_columns={},
+                        wizards=None)
 
 
 # Start common services
diff --git a/modules/plugin_dbui/__init__.py b/modules/plugin_dbui/__init__.py
index 603b36e8..7b4d04bf 100644
--- a/modules/plugin_dbui/__init__.py
+++ b/modules/plugin_dbui/__init__.py
@@ -1,16 +1,16 @@
 """ $Id$
-	Author: R. Le Gac
+    Author: R. Le Gac
 """
 
 from cfgsvc import CfgSvc
 from dbsvc import DbSvc
 from tools import (customize_field,
-				   customize_form,
-				   customize_form_with_field_set,
-				   customize_grid,
-				   customize_grid_with_template_column,
-				   customize_viewport,
-				   define_combined_field,
-				   define_grid_filter,
-				   define_wizard,
-				   hidden_tables)
+                   customize_form,
+                   customize_form_with_field_set,
+                   customize_grid,
+                   customize_grid_with_template_column,
+                   customize_viewport,
+                   define_combined_field,
+                   define_grid_filter,
+                   define_wizard,
+                   hidden_tables)
diff --git a/modules/plugin_dbui/basesvc.py b/modules/plugin_dbui/basesvc.py
index 0aa73f5c..7b8ab753 100644
--- a/modules/plugin_dbui/basesvc.py
+++ b/modules/plugin_dbui/basesvc.py
@@ -1,5 +1,5 @@
 """ $Id$
-	Author: R. Le Gac
+    Author: R. Le Gac
 """
 
 __version__ = "$Revision$"
@@ -9,40 +9,40 @@ import pprint
 KEYWORD_MISSING = "The keyword '%s' is missing or undefined."
 
 class BaseSvc:
-	
-	def __init__(self, db):
-		self._db = db
-		self._vars = None
+    
+    def __init__(self, db):
+        self._db = db
+        self._vars = None
 
 
-	def _debug(self, *args):
-		"""Print args if the keyword debug is in the request and
-		if the associate value is true.
+    def _debug(self, *args):
+        """Print args if the keyword debug is in the request and
+        if the associate value is true.
 
-		"""
-		if ("debug" in self._vars) and (self._vars.debug == 'true'):
-			for arg in args:
-				pprint.pprint(arg)
+        """
+        if ("debug" in self._vars) and (self._vars.debug == 'true'):
+            for arg in args:
+                pprint.pprint(arg)
 
 
-	def _is_keyword(self, keyword):
-		"""Raise an exception if the steering keyword is not defined."""
-		if not (keyword in self._vars and self._vars[keyword]):
-			raise Exception, KEYWORD_MISSING % keyword  
+    def _is_keyword(self, keyword):
+        """Raise an exception if the steering keyword is not defined."""
+        if not (keyword in self._vars and self._vars[keyword]):
+            raise Exception, KEYWORD_MISSING % keyword  
 
-	
-	def _list(self, val):
-		""" If val is a list return val.  
-		Otherwise, build a list containing val
-		
-		"""
-		if not isinstance(val, (list, tuple)):
-			 val = [val]
-		return val
+    
+    def _list(self, val):
+        """ If val is a list return val.  
+        Otherwise, build a list containing val
+        
+        """
+        if not isinstance(val, (list, tuple)):
+             val = [val]
+        return val
 
 
-	def proceed(self, vars):
-		"""Proceed the request sends via the POST/GET method.
+    def proceed(self, vars):
+        """Proceed the request sends via the POST/GET method.
 
-		"""
-		self._vars = vars
+        """
+        self._vars = vars
diff --git a/modules/plugin_dbui/cfgsvc.py b/modules/plugin_dbui/cfgsvc.py
index 4f22d32e..aec2d96b 100644
--- a/modules/plugin_dbui/cfgsvc.py
+++ b/modules/plugin_dbui/cfgsvc.py
@@ -1,5 +1,5 @@
 """ $Id$
-	Author: R. Le Gac
+    Author: R. Le Gac
 """
 
 __version__ = "$Revision$"
@@ -15,395 +15,395 @@ FIELD_IN_DBFIELD = "Field %s already in the dbFields list."
 # Convert a field type into a widget xtype
 # The dictionary contain the configuration parameters of the Ext JS widget
 FTYPE_TO_XTYPE = {'boolean':   {'xtype': 'checkbox'},\
-				  'date':      {'xtype': 'datefield', 'format': 'Y-m-d'},\
-				  'datetime':  {'xtype': 'datefield', 'format': 'Y-m-d H:i:s'},\
-				  'double':    {'xtype': 'numberfield'},\
-				  'id':   	   {'xtype': 'textfield'},\
-				  'integer':   {'xtype': 'numberfield'},\
-				  'password':  {'xtype': 'textfield', 'inputType': 'password'},\
-				  'reference': {'xtype': 'xcombobox'},\
-				  'string':    {'xtype': 'textfield'},\
-				  'text':      {'xtype': 'textarea'},\
-				  'time':      {'xtype': 'timefield'},\
-				  'upload':    {'xtype': 'textfield', 'inputType': 'file'}}
+                  'date':      {'xtype': 'datefield', 'format': 'Y-m-d'},\
+                  'datetime':  {'xtype': 'datefield', 'format': 'Y-m-d H:i:s'},\
+                  'double':    {'xtype': 'numberfield'},\
+                  'id':          {'xtype': 'textfield'},\
+                  'integer':   {'xtype': 'numberfield'},\
+                  'password':  {'xtype': 'textfield', 'inputType': 'password'},\
+                  'reference': {'xtype': 'xcombobox'},\
+                  'string':    {'xtype': 'textfield'},\
+                  'text':      {'xtype': 'textarea'},\
+                  'time':      {'xtype': 'timefield'},\
+                  'upload':    {'xtype': 'textfield', 'inputType': 'file'}}
 
 
 class CfgSvc(object):
 
-	
-	def __init__(self, db, translator, pluginManager):
-		
-		self._db = db
-		self._foreign = ForeignField(db)
-		self._set = SetField(db)
-		self._translate = translator
-		
-		for key in pluginManager.dbui:
-			setattr(self, '_%s' % key, pluginManager.dbui[key])
-
-
-	def _getColumn(self, tablename, fieldname):
-		""" Return a dictionary with the column model 
-		required by the gridPanel widget.
-		
-		"""
-		
-		col = {}
-		field = self._db[tablename][fieldname]
-
-		# column header
-		col["header"] = str(self._translate(field.label))
-		
-		# replace foreign key by the pointing column
-		if self._is_foreign_field(tablename, fieldname):
-			
-			k_tablename, k_fieldname, k_key = \
-			self._get_foreign_data(tablename, fieldname)
-			col["dataIndex"] = encode_field(k_tablename, k_fieldname)
-		
-		# standard column
-		else:
-			col["dataIndex"] = encode_field(tablename, fieldname)
-			
-		col["sortable"] = True
-		
-		# hide the primary key
-		# and field which are not readable and not writable
-		if fieldname == "id" or ((not field.readable) and (not field.writable)):
-			col["hidden"] = True
-
-		# Hide fields request customizing the grid
-		if tablename in self._hidden_columns:
-			if fieldname in self._hidden_columns[tablename]:
-				col["hidden"] = True
-				
-		return col
-
-
-	def _getColumnsModel(self, tablename):
-		""" Return the columns model required by the gridPanel widget
-		handling customize columns.
-
-		"""
-		
-		li= []
-
-		# standard column
-		for fieldname in self._db[tablename].fields:
-			li.append(self._getColumn(tablename, fieldname))
-		
-		# template column
-		if tablename in self._template_columns:
-			for el in self._template_columns[tablename]:
-				
-				col = {'xtype': 'templatecolumn',
-					   'header': el.header,
-					   'tpl': el.tpl,
-					   'sortable': True}
-				
-				col.update(el.extjs)
-				
-				li.insert(el.ipos, col)
-				
-		return li
-
-
-	def _get_foreign_data(self, tablename, fieldname):
-		""" Wrapper method returning the foreign data.
-		the foreign data is a tuple containing the name of the 
-		pointing table, the name of the pointing field 
-		and the join instruction.
-		
-		"""
-		return self._foreign.get_foreign_data(tablename, fieldname)
-
-	
-	def _get_set_data(self, tablename, fieldname):
-		""" Wrapper method
-		
-		"""
-		return self._set.get_set_data(tablename, fieldname)
-	
-	
-	def _getFormField(self, tablename, fieldname):
-		""" Return the configuration dictionary for a form field.
-		
-		"""
-		field = self._db[tablename][fieldname]
-		cfg= {"name": encode_field(tablename, fieldname)}
-					
-		# foreign key
-		if self._is_foreign_field(tablename, fieldname):
-				
-			k_tablename, k_fieldname, k_key = \
-			self._get_foreign_data(tablename, fieldname)
-			
-			cfg["xtype"] = "xcombobox"
-			cfg["hiddenName"] = encode_field(tablename, fieldname)
-			cfg["displayField"] = encode_field(k_tablename, k_fieldname)
-			cfg["valueField"] = encode_field(k_tablename, k_key)
-			
-			dbfields = [[k_tablename, k_fieldname], [k_tablename, k_key]]
-			fields = [encode_field(k_tablename, k_fieldname),
-					  encode_field(k_tablename, k_key)]
-			
-			cfg["model"] = {"store": {"table": k_tablename,
-									  "dbFields": dbfields,
-									  "fields": fields}}
-	
-		# set field
-		elif self._is_set_field(tablename, fieldname):
-			cfg["xtype"] = "xsetbox"
-			cfg["model"] = {"setData": self._get_set_data(tablename, fieldname)}
-			
-		# other types of fields
-		else:
-			cfg.update(FTYPE_TO_XTYPE[field.type])
-
-		# Exploit the argument of the SQLField consructor
-		# field label
-		cfg["fieldLabel"] = str(self._translate(field.label))
-
-		# default value and not null
-		# NOTE: the entryform doesn't work when both default and
-		#       notnull condition are defined.
-		if field.default:
-			cfg["value"] = str(field.default)
-		elif field.notnull:
-			cfg["allowBlank"] = False
-
-		# read only field
-		if field.readable and not field.writable:
-			cfg["readOnly"] = True
-
-		# tool tip
-		if field.comment:
-			cfg["tipText"] = field.comment
-			
-		# hide primary key
-		# and field which are not readable and not writable
-		if field.name == "id" or ((not field.readable) and (not field.writable)):
-			cfg["hidden"] = True
-			cfg["hideLabel"] = True
-			cfg["readOnly"] = True
-
-		# customized field
-		if tablename in self._customized_fields and \
-		   fieldname in self._customized_fields[tablename]:
-			cfg.update(self._customized_fields[tablename][fieldname])
-
-		return cfg
-
-
-	def _getFormModelItems(self, tablename):
-		""" Return the list of items appearing in a Form model.
-				
-		"""
-		li, id_is_not_used = [], True
-
-		# form with field sets
-		# NOTE: the field id is add in order to run this form
-		# in all context, create, update, destroy.
-		# It is add to the latest fieldset.
-		
-		if tablename in self._field_sets:
-			for fieldset in self._field_sets[tablename]:
-				
-				cfg = {'title': fieldset.title, 
-					   'xtype': 'fieldset',
-					   'items': []}
-				
-				for fieldname in fieldset.fields:
-					cfg['items'].append(self._getFormField(tablename, fieldname))
-					if fieldname == 'id':
-						id_is_not_used = False
-
-				# protection when applying Ext JS configuration parameters
-				# NOTE: if the user submit a list of items they are append at the
-				# end of the current list. 
-				# It is a way to add spacer and to fine tune the layout
-				di = dict(fieldset.extjs)
-				if 'items' in di:
-					cfg['items'].extend(di['items'])
-					del di['items']
-				
-				# Apply the remaining Ext JS configuration parameters	
-				cfg.update(di)
-				li.append(cfg)
-
-			if id_is_not_used:
-				li[-1]['items'].append(self._getFormField(tablename, 'id'))
-					
-		# the default form
-		else:
-			for fieldname in self._db[tablename].fields:
-				li.append(self._getFormField(tablename, fieldname))
-			
-		return li
-
-
-	def _getGridFilter(self, tablename):
-		""" Generate configuration object for the grid filter
-		
-		"""
-		di = {}
-		
-		if tablename in self._grid_filters:
-			
-			di = {'xtype':'xgridfilter',
-				  'items': [],
-				  'title': self._translate('Filter %s' % tablename)}
-			
-			filters = self._grid_filters[tablename].filters
-			for (fieldname, operator, comment) in filters:
-				
-				# get the standard form field
-				cfg = self._getFormField(tablename, fieldname)
-				
-				# remove default value for fields which are not combobox
-				if cfg['xtype'] != 'xcombobox':
-					cfg['value'] = ''
-					cfg['allowBlank'] = True
-				
-				# replace all textarea by textfield
-				if cfg['xtype'] == 'textarea':
-					cfg['xtype'] = 'textfield'
-				
-				# prepare the name for the where close
-				cfg['name'] = '[%s.%s]' % (tablename, fieldname)	
-					
-				# store information to customize the filter
-				cfg['filterComment'] = comment  
-				cfg['filterOperator'] = operator
-				cfg['filterType'] = self._db[tablename][fieldname].type
-				
-				di['items'].append(cfg)
-		
-			extjs = self._grid_filters[tablename].extjs
-			di.update(extjs)
-		
-		return di
-	
-	
-	def _getJsonStore(self, table):
-		""" Generate configuration object for JSON stores 
-		required by the gridPanel widget.
-		
-		"""
-
-		di = {"table": table,
-			  "idProperty": encode_field(table,'id'),
-			  "dbFields": [],
-			  "fields": []}
-
-		for field in self._db[table].fields:
-			if field in di["dbFields"]:
-				raise Exception, FIELD_IN_DBFIELD 
-			
-			di["dbFields"].append([table, field])
-			di["fields"].append(encode_field(table, field))
-			
-			if self._is_foreign_field(table, field):
-				if "where" not in di: di["where"] = []
-				k_table, k_field, k_key = self._get_foreign_data(table, field)
-				
-				if k_field in di["dbFields"]:
-					raise Exception, FIELD_IN_DBFIELD
-				
-				di["dbFields"].append([k_table, k_field])
-				di["fields"].append(encode_field(k_table, k_field))
-				di["where"].append("[%s.%s] == [%s.%s]" % (table, field, k_table, k_key))
-				
-		return di
-		
-		
-	def _getMethods(self):
-		""" Return the list of methods available and 
-		their number of arguments.
-		
-		"""
-		li = []
-		for el in dir(self):
-			if el.startswith("_"): continue
-			di = {"name": el,\
-				  "len": eval("self.%s.func_code.co_argcount" % el)-1}
-			li.append(di)
-
-		return li
-	
-
-	def _is_foreign_field(self, tablename, fieldname):
-		""" Wrapper method
-		
-		"""
-		return self._foreign.is_foreign_field(tablename, fieldname)
-
-
-	def _is_set_field(self, tablename, fieldname):
-		""" Wrapper method
-		
-		"""
-		return self._set.is_set_field(tablename, fieldname)
-
-		
-	def getFormModel(self, tablename):
-		""" Return the configuration dictionary for an entryForm widget.
-		
-		"""
-		model = {"title": str(self._translate(tablename.title())),
-				 "table": tablename,
-				 "items": self._getFormModelItems(tablename)}
-
-		di = {"xtype": "xentry", "model": model}
-		
-		for el in (ALL, tablename):
-			if el in self._customized_forms:
-				di.update(self._customized_forms[el])
-			
-		return di 
-
-		
-	def getGridModel(self, tablename):
-		""" Return the configuration dictionary for a gridPanel widget.
-		
-		"""
-		
-		formModel = {'xtype': 'xform',
-					 'items': self._getFormModelItems(tablename)}
-		
-		for el in (ALL, tablename):
-			if tablename in self._customized_forms:
-				formModel.update(self._customized_forms[tablename])
-					
-		model = {}	
-		model["colModel"] = self._getColumnsModel(tablename)
-		model["formModel"] = formModel
-		
-		filter = self._getGridFilter(tablename)
-		if filter:
-			model["filterModel"] = filter
-			 
-		model["store"] = self._getJsonStore(tablename)
-		model["table"] = tablename
-		model["title"] = str(self._translate(tablename.title()))
-
-		di = {"xtype": "xgrid", "model": model}
-
-		# handle the user customization
-		for el in (ALL, tablename):
-			if el in self._customized_grids:
-				di.update(self._customized_grids[el])
-			
-		return di
-
-
-	def getTables(self, *args):
-		""" Return a dictionary with list of tables 
-		and a list with the translation of their names
-		
-		"""
-		li_1 = [el for el in self._db.tables if el not in self._hidden_tables]			
-		li_1.sort()
-		li_2 = [str(self._translate(el)) for el in li_1]
-			
-		return {'tables': li_1, 'tablesText': li_2}
\ No newline at end of file
+    
+    def __init__(self, db, translator, pluginManager):
+        
+        self._db = db
+        self._foreign = ForeignField(db)
+        self._set = SetField(db)
+        self._translate = translator
+        
+        for key in pluginManager.dbui:
+            setattr(self, '_%s' % key, pluginManager.dbui[key])
+
+
+    def _getColumn(self, tablename, fieldname):
+        """ Return a dictionary with the column model 
+        required by the gridPanel widget.
+        
+        """
+        
+        col = {}
+        field = self._db[tablename][fieldname]
+
+        # column header
+        col["header"] = str(self._translate(field.label))
+        
+        # replace foreign key by the pointing column
+        if self._is_foreign_field(tablename, fieldname):
+            
+            k_tablename, k_fieldname, k_key = \
+            self._get_foreign_data(tablename, fieldname)
+            col["dataIndex"] = encode_field(k_tablename, k_fieldname)
+        
+        # standard column
+        else:
+            col["dataIndex"] = encode_field(tablename, fieldname)
+            
+        col["sortable"] = True
+        
+        # hide the primary key
+        # and field which are not readable and not writable
+        if fieldname == "id" or ((not field.readable) and (not field.writable)):
+            col["hidden"] = True
+
+        # Hide fields request customizing the grid
+        if tablename in self._hidden_columns:
+            if fieldname in self._hidden_columns[tablename]:
+                col["hidden"] = True
+                
+        return col
+
+
+    def _getColumnsModel(self, tablename):
+        """ Return the columns model required by the gridPanel widget
+        handling customize columns.
+
+        """
+        
+        li= []
+
+        # standard column
+        for fieldname in self._db[tablename].fields:
+            li.append(self._getColumn(tablename, fieldname))
+        
+        # template column
+        if tablename in self._template_columns:
+            for el in self._template_columns[tablename]:
+                
+                col = {'xtype': 'templatecolumn',
+                       'header': el.header,
+                       'tpl': el.tpl,
+                       'sortable': True}
+                
+                col.update(el.extjs)
+                
+                li.insert(el.ipos, col)
+                
+        return li
+
+
+    def _get_foreign_data(self, tablename, fieldname):
+        """ Wrapper method returning the foreign data.
+        the foreign data is a tuple containing the name of the 
+        pointing table, the name of the pointing field 
+        and the join instruction.
+        
+        """
+        return self._foreign.get_foreign_data(tablename, fieldname)
+
+    
+    def _get_set_data(self, tablename, fieldname):
+        """ Wrapper method
+        
+        """
+        return self._set.get_set_data(tablename, fieldname)
+    
+    
+    def _getFormField(self, tablename, fieldname):
+        """ Return the configuration dictionary for a form field.
+        
+        """
+        field = self._db[tablename][fieldname]
+        cfg= {"name": encode_field(tablename, fieldname)}
+                    
+        # foreign key
+        if self._is_foreign_field(tablename, fieldname):
+                
+            k_tablename, k_fieldname, k_key = \
+            self._get_foreign_data(tablename, fieldname)
+            
+            cfg["xtype"] = "xcombobox"
+            cfg["hiddenName"] = encode_field(tablename, fieldname)
+            cfg["displayField"] = encode_field(k_tablename, k_fieldname)
+            cfg["valueField"] = encode_field(k_tablename, k_key)
+            
+            dbfields = [[k_tablename, k_fieldname], [k_tablename, k_key]]
+            fields = [encode_field(k_tablename, k_fieldname),
+                      encode_field(k_tablename, k_key)]
+            
+            cfg["model"] = {"store": {"table": k_tablename,
+                                      "dbFields": dbfields,
+                                      "fields": fields}}
+    
+        # set field
+        elif self._is_set_field(tablename, fieldname):
+            cfg["xtype"] = "xsetbox"
+            cfg["model"] = {"setData": self._get_set_data(tablename, fieldname)}
+            
+        # other types of fields
+        else:
+            cfg.update(FTYPE_TO_XTYPE[field.type])
+
+        # Exploit the argument of the SQLField consructor
+        # field label
+        cfg["fieldLabel"] = str(self._translate(field.label))
+
+        # default value and not null
+        # NOTE: the entryform doesn't work when both default and
+        #       notnull condition are defined.
+        if field.default:
+            cfg["value"] = str(field.default)
+        elif field.notnull:
+            cfg["allowBlank"] = False
+
+        # read only field
+        if field.readable and not field.writable:
+            cfg["readOnly"] = True
+
+        # tool tip
+        if field.comment:
+            cfg["tipText"] = field.comment
+            
+        # hide primary key
+        # and field which are not readable and not writable
+        if field.name == "id" or ((not field.readable) and (not field.writable)):
+            cfg["hidden"] = True
+            cfg["hideLabel"] = True
+            cfg["readOnly"] = True
+
+        # customized field
+        if tablename in self._customized_fields and \
+           fieldname in self._customized_fields[tablename]:
+            cfg.update(self._customized_fields[tablename][fieldname])
+
+        return cfg
+
+
+    def _getFormModelItems(self, tablename):
+        """ Return the list of items appearing in a Form model.
+                
+        """
+        li, id_is_not_used = [], True
+
+        # form with field sets
+        # NOTE: the field id is add in order to run this form
+        # in all context, create, update, destroy.
+        # It is add to the latest fieldset.
+        
+        if tablename in self._field_sets:
+            for fieldset in self._field_sets[tablename]:
+                
+                cfg = {'title': fieldset.title, 
+                       'xtype': 'fieldset',
+                       'items': []}
+                
+                for fieldname in fieldset.fields:
+                    cfg['items'].append(self._getFormField(tablename, fieldname))
+                    if fieldname == 'id':
+                        id_is_not_used = False
+
+                # protection when applying Ext JS configuration parameters
+                # NOTE: if the user submit a list of items they are append at the
+                # end of the current list. 
+                # It is a way to add spacer and to fine tune the layout
+                di = dict(fieldset.extjs)
+                if 'items' in di:
+                    cfg['items'].extend(di['items'])
+                    del di['items']
+                
+                # Apply the remaining Ext JS configuration parameters    
+                cfg.update(di)
+                li.append(cfg)
+
+            if id_is_not_used:
+                li[-1]['items'].append(self._getFormField(tablename, 'id'))
+                    
+        # the default form
+        else:
+            for fieldname in self._db[tablename].fields:
+                li.append(self._getFormField(tablename, fieldname))
+            
+        return li
+
+
+    def _getGridFilter(self, tablename):
+        """ Generate configuration object for the grid filter
+        
+        """
+        di = {}
+        
+        if tablename in self._grid_filters:
+            
+            di = {'xtype':'xgridfilter',
+                  'items': [],
+                  'title': self._translate('Filter %s' % tablename)}
+            
+            filters = self._grid_filters[tablename].filters
+            for (fieldname, operator, comment) in filters:
+                
+                # get the standard form field
+                cfg = self._getFormField(tablename, fieldname)
+                
+                # remove default value for fields which are not combobox
+                if cfg['xtype'] != 'xcombobox':
+                    cfg['value'] = ''
+                    cfg['allowBlank'] = True
+                
+                # replace all textarea by textfield
+                if cfg['xtype'] == 'textarea':
+                    cfg['xtype'] = 'textfield'
+                
+                # prepare the name for the where close
+                cfg['name'] = '[%s.%s]' % (tablename, fieldname)    
+                    
+                # store information to customize the filter
+                cfg['filterComment'] = comment  
+                cfg['filterOperator'] = operator
+                cfg['filterType'] = self._db[tablename][fieldname].type
+                
+                di['items'].append(cfg)
+        
+            extjs = self._grid_filters[tablename].extjs
+            di.update(extjs)
+        
+        return di
+    
+    
+    def _getJsonStore(self, table):
+        """ Generate configuration object for JSON stores 
+        required by the gridPanel widget.
+        
+        """
+
+        di = {"table": table,
+              "idProperty": encode_field(table,'id'),
+              "dbFields": [],
+              "fields": []}
+
+        for field in self._db[table].fields:
+            if field in di["dbFields"]:
+                raise Exception, FIELD_IN_DBFIELD 
+            
+            di["dbFields"].append([table, field])
+            di["fields"].append(encode_field(table, field))
+            
+            if self._is_foreign_field(table, field):
+                if "where" not in di: di["where"] = []
+                k_table, k_field, k_key = self._get_foreign_data(table, field)
+                
+                if k_field in di["dbFields"]:
+                    raise Exception, FIELD_IN_DBFIELD
+                
+                di["dbFields"].append([k_table, k_field])
+                di["fields"].append(encode_field(k_table, k_field))
+                di["where"].append("[%s.%s] == [%s.%s]" % (table, field, k_table, k_key))
+                
+        return di
+        
+        
+    def _getMethods(self):
+        """ Return the list of methods available and 
+        their number of arguments.
+        
+        """
+        li = []
+        for el in dir(self):
+            if el.startswith("_"): continue
+            di = {"name": el,\
+                  "len": eval("self.%s.func_code.co_argcount" % el)-1}
+            li.append(di)
+
+        return li
+    
+
+    def _is_foreign_field(self, tablename, fieldname):
+        """ Wrapper method
+        
+        """
+        return self._foreign.is_foreign_field(tablename, fieldname)
+
+
+    def _is_set_field(self, tablename, fieldname):
+        """ Wrapper method
+        
+        """
+        return self._set.is_set_field(tablename, fieldname)
+
+        
+    def getFormModel(self, tablename):
+        """ Return the configuration dictionary for an entryForm widget.
+        
+        """
+        model = {"title": str(self._translate(tablename.title())),
+                 "table": tablename,
+                 "items": self._getFormModelItems(tablename)}
+
+        di = {"xtype": "xentry", "model": model}
+        
+        for el in (ALL, tablename):
+            if el in self._customized_forms:
+                di.update(self._customized_forms[el])
+            
+        return di 
+
+        
+    def getGridModel(self, tablename):
+        """ Return the configuration dictionary for a gridPanel widget.
+        
+        """
+        
+        formModel = {'xtype': 'xform',
+                     'items': self._getFormModelItems(tablename)}
+        
+        for el in (ALL, tablename):
+            if tablename in self._customized_forms:
+                formModel.update(self._customized_forms[tablename])
+                    
+        model = {}    
+        model["colModel"] = self._getColumnsModel(tablename)
+        model["formModel"] = formModel
+        
+        filter = self._getGridFilter(tablename)
+        if filter:
+            model["filterModel"] = filter
+             
+        model["store"] = self._getJsonStore(tablename)
+        model["table"] = tablename
+        model["title"] = str(self._translate(tablename.title()))
+
+        di = {"xtype": "xgrid", "model": model}
+
+        # handle the user customization
+        for el in (ALL, tablename):
+            if el in self._customized_grids:
+                di.update(self._customized_grids[el])
+            
+        return di
+
+
+    def getTables(self, *args):
+        """ Return a dictionary with list of tables 
+        and a list with the translation of their names
+        
+        """
+        li_1 = [el for el in self._db.tables if el not in self._hidden_tables]            
+        li_1.sort()
+        li_2 = [str(self._translate(el)) for el in li_1]
+            
+        return {'tables': li_1, 'tablesText': li_2}
\ No newline at end of file
diff --git a/modules/plugin_dbui/dbsvc.py b/modules/plugin_dbui/dbsvc.py
index 46f22fc6..19b188c0 100644
--- a/modules/plugin_dbui/dbsvc.py
+++ b/modules/plugin_dbui/dbsvc.py
@@ -1,5 +1,5 @@
 """ $Id$
-	Author: R. Le Gac
+    Author: R. Le Gac
 """
 
 __version__ = "$Revision$"
@@ -15,16 +15,16 @@ ACTIONS = ('create', 'destroy', 'read', 'update')
 
 # see gluon.dal.Field
 FUNC_FIELDS = ('count',
-			   'day',
-			   'lower',
-			   'max',
-			   'min',
-			   'minutes',
-			   'month',
-			   'upper',
-			   'seconds',
-			   'sum',
-			   'year')
+               'day',
+               'lower',
+               'max',
+               'min',
+               'minutes',
+               'month',
+               'upper',
+               'seconds',
+               'sum',
+               'year')
 
 ACTION_INVALID = "Requested action '%s' is not valid."
 FIELD_NOT_IN_DB = "the field '%s' doesn't exist in the database."
@@ -38,518 +38,518 @@ WHERE_INVALID = "Invalid syntax for a where keywords."
 
 
 class DbSvc(BaseSvc):
-	""" DbSvc
-
-	Interface the database and database model with http request.
-	It is the glue between wep2py and javascript.
-
-	Handle HTTP POST and HTTP GET request.
-	The selection of the action and its parameters are based on
-	steering keywords:  xaction, table, dbFields, debug,...
-	The xaction is mandatory.
-
-	Main actions are:
-
-		create
-			create a new record in a table.
-
-		read
-			read the content of a single table
-			and handle query with foreign key and where conditions
-
-		select
-			read the content of several table handling complex
-			query with join, group by, order by keywords.
-			
-		update
-			update the record of a table.
-
-		destroy
-			delete the record of a table.
-	
-	The steering keyword table is mandatory for the actions
-	create, read, update and destroy.
-	
-	the steering keyword for the select operation is xaction=read
-	but without table definition.
-	
-	Each action return a dictionary:
-	{success: True, data:{blabla}, msg: 'blalbla'}
-	
-	The keyword success is always present. It is mandatory for 
-	the FormPanel widget of the extjs library, but not strictly 
-	required since failure can be propagated through HTTP errors.
-	
-	In the data dictionary database field are encode as TableField.
-
-	If the action failed an Exception is raised.
-	The exception can be catch to generate an HTTP error.
-
-	The most important method are DbSvc.proceed(), DbSvc.create(),
-	DbSvc.destroy(), DbSvc.read(), DbSvc.select(), DbSvc.update()
-
-	"""
-
-	KEYWORDS = ['data',
-			    'debug',
-			    'dbFields',
-				'dir',
-				'groupby',
-				'leftjoin',
-				'limit',
-				'orderby',
-				'sort',
-				'start',
-				'table', 
-				'where',
-				'xaction']
-
-
-	def _encode_query(self, li):
-		""" helper method to encode the list of query as a web2py query.
-		
-				"[table1, field1] == [table2, field2]"
-				"[table1, field1] > n"
-				"[table1, field1] like 'm%'"
-				"[table1, field1] contains 'm%'"
-				"[table1, field1] startswith 'm%'"
-				"[table1, field1].lower() like 'm%'"
-				"[table1, field1] belongs (1, 2, 3)"
-				"([table1, field1] == [table2, field2]) | ([table1, field2] == [table2, field1])"
-				
-				all element of the list are ANDED
-				operator are ==, !=, <, >, <=, >=
-				boolean operator are &, |
-		""" 
-		query = ""
-		for el in li:
-			
-			s = el.replace(' ','')
-			s = s.replace(',', '.')
-			s = s.replace('[', 'db.')
-			s = s.replace(']', '')
-			
-			for op in ['belongs', 'contains', 'like', 'startswith']:
-				if op in s:
-					s = "%s)" % s.replace(op, '.%s(' % op )
-				
-			query = ("(%s) & (%s)" % (query, s) if query else s)
-
-		return eval(query, {}, {"db": self._db})	
-
-	
-	def _get_fields(self):
-		"""Helper method returning a dictionary with request fields
-		and values.
-		
-		The App.data.JsonStore send the field and their values
-		as steering keywords for FormPanel. 
-		
-		Since EXtJs 3.1, the fields and their values are 
-		encapsulated in the data dictionary for a GridPanel.
-		
-		This method allows to extract field values in all cases.
-		
-		"""
-		# New approach coming with ExtJs 3.1 where fields values are
-		# encapsulated in the data dictionary
-		if 'data' in self._vars:
-			return self._vars['data']
-		
-		# preserve backward compatibility for formPanel
-		fields = dict(self._vars)
-		for key in self.KEYWORDS:
-			if key in fields:
-				del fields[key]
-		return fields
-		
-		
-	def _is_action_valid(self, action):
-		"""Raise an Exception if the request action is not handle."""
-		if action not in ACTIONS:
-			raise Exception, ACTION_INVALID % action
-
-
-	def _is_field_in_table(self, table, field):
-		""" Return true is the field belongs to the table. """
-		return field in self._db[table].fields
-	
-	
-	def _is_fields_values_valid(self, table, fields):
-		"""Return a dictionary {field1: error1, field2: error2,...}
-		
-		The value of each field is check against validators defined in the
-		data model. The return dictionary contains field failing these checks.
-		
-		the dictionary is empty when everything is OK.
-		
-		"""
-		di = {}
-		for (field, val) in fields.items():
-			errmsg = self._db[table][field].validate(val)[1]
-			if val and self._db[table][field].notnull and errmsg:
-				di[field] = errmsg
-		return di
-
-
-	def _is_table_in_db(self, table):
-		"""Raise an Exception if the table is not defined in the database."""
-		if table not in self._db.tables:
-			raise Exception, TABLE_NOT_IN_DB % table
-
-
-	def create(self):
-		"""Create a new record in a table.
-
-			debug
-				boolean
-
-			table
-				the table name where data will be inserted.
-
-			data
-				a dictionary with field values {field1: x, field2: y,...}
-				approach use by App.data.JsonStore embedded in GridPanel
-				since ExtJs 3.1
-				
-			field1, field2,... 
-				To keep backward compatibility with formPanel
-				the field value.
-
-				
-		Return a dictionary with status, error message and id of the insert row:
-			{success: False, errors: {field: 'blabla',..}}
-			The keyword success and error are required by 
-			the Ext.FormPanel widget.
-		
-			or
-			
-			{success: True, msg: 'blalbla', data:{id:xx}}
-
-		"""
-		self._debug("Start DbSvc.insert")
-		
-		self._is_keyword("table")
-		
-		table = self._vars.table
-		self._is_table_in_db(table)
-
-		# Remove fields which do not belong to the table.
-		# The JsonStore send the foreign key and the the pointing field
-		# when manipulating table with foreign keys. The latter belongs
-		# to another table. 
-		
-		fields, di = {}, self._get_fields()
-		for table_field in di:
-			tablename, fieldname = decode_field(table_field)
-			if tablename == table:
-				fields[fieldname.encode('utf8')] = di[table_field]
-
-		# Validate field contents
-		di = self._is_fields_values_valid(table, fields)
-		if di:
-			return {"success": False, "errors": di}
-		
-		# Create the new record
-		self._debug('fields -->', fields)
-		id = self._db[table].insert (**fields)
-
-		self._debug("End DbSvc.insert.")
-		resp = {"success": True, 
-			    "msg": RECORD_INSERTED % id, 
-			    "data": {encode_field(table,'id'): id}}
-		return resp 
-
-
-	def destroy(self):
-		"""Destroy a record in a table.
-
-			debug
-				boolean
-
-			table
-				the table name where record will be removed.
-
-			data
-				dictionary with the identifier of the record (id)
-				
-		Return a dictionary with status and error message:
-		{success: True, msg: 'blalbla'}
-
-		"""
-		self._debug("Start DbSvc.destroy")
-
-		self._is_keyword("table")
-		self._is_keyword("data")
-		
-		table = self._vars.table
-		self._is_table_in_db(table)
-		
-		id = self._vars.data[encode_field(table, "id")]
-		del self._db[table][id]
-		
-		self._debug("End DbSvc.destroy")
-		return {"success": True, "msg": RECORD_DELETED % id}
-
-
-	def proceed(self, vars):
-		"""Proceed the request sends via the POST/GET method.
-
-		the input vars is a Storage object.
-
-		Return a dictionary with status, data and error message:
-		{success: True, data:{blabla}, msg: 'blalbla'}
-
-		"""
-		BaseSvc.proceed(self, vars)
-		self._debug("Start DbSvc.proceed")
-
-		self._is_keyword("xaction")
-		self._is_action_valid(vars.xaction)
-		
-		# create, destroy, update and read action related to a single table
-		# they are used to manipulate record within a table
-		if "table" in vars:
-			self._debug("self.%s()" % vars.xaction)
-			di = eval("self.%s()" % vars.xaction)
-
-		# complex query involving several tables and conditions
-		# they are use to build view or report
-		elif vars.xaction == 'read':
-			di = self.select()
-			
-		else:
-			raise Exception, ACTION_INVALID % ""
-		
-		self._debug("End DbSvc.proceed")
-		return di
-
-
-	def read(self):
-		"""Read the content of a single table resolving foreign keys.
-		
-			debug
-				A boolean
-			
-			table
-				name of the table containing records
-				
-			dbFields
-				A list of field to be red.
-				It also include pointing field resolving foreign keys.
-				[(table1, field1), (table1, field2), (table2, field3), ...]
-							
-			where (optional)
-				define the inner join when handling foreign key.
-				The syntax follows the web2py convention:
-				['db.table1.field1  == db.table2.id'].
-				An AND is performed between the different 
-				elements of the list.
-				
-				
-		This method also understand parameters useful for paging.
-		For more detail see  Ext.PagingToolbar.
-			start
-			limit
-			sort
-			dir
-			
-		Return a dictionary with selected data:
-		{success: True, data: [{field1: xxx,..},...]}
-
-		"""
-		self._debug("Start DbSvc.read")
-		self._is_keyword("dbFields")
-
-		self._is_keyword("table")		
-		table = self._vars.table
-		self._is_table_in_db(table)
-		
-		# build the list of SQLField object
-		# The JSON process sends a string when only one field is defined
-		# NOTE: The json encoding return the tuple [table, field] 
-		#       as a string 'table, field'
-		fields = []
-		for el in self._list(self._vars.dbFields):
-			(table, field) = el.replace(' ','').split(',')
-			if not self._is_field_in_table(table, field):
-				raise Exception, FIELD_NOT_IN_DB % ("%s.%s" % (table, field))
-			fields.append(self._db[table][field])
-
-		# build the inner join conditions
-		# The JSON process sends a string when only one field is defined
-		query = ''
-		if "where" in self._vars:
-			query = self._encode_query(self._list(self._vars["where"]))
-		
-		# handle paging options (see Ext.PagingToolbar)
-		# Count the number of record in the table --require for the paging
-		kwargs, results = {}, None
-		if 'start' in self._vars and 'limit' in self._vars:
-			start = int(self._vars.start)
-			limit = int(self._vars.limit)
-			kwargs['limitby'] = (start, start+limit)
-			results = self._db(self._db[table].id).count()
-		
-		rows = self._db(query).select(*fields, **kwargs)
-
-		self._debug("End DbSvc.read")
-		return {"success": True, "results": results, "data": rows_serializer(rows)}
-
-
-	def select(self):
-		""" Handle complex queries with inner/left join and with where
-		group by, order by conditions. 
-
-			debug
-				A boolean
-			
-			dbFields
-				A list of field to be red.
-				[(table1, field1), (table2, field1), ....]
-							
-			where (optional)
-				define the inner join when handling foreign key.
-				The syntax follows the web2py convention:
-				['db.table1.field1  == db.table2.id', 'db.table2.field3 > 3',...].
-				An AND is performed between the different 
-				elements of the list.
-				
-			left (optional)
-			
-			groupby (optional)
-			
-			orderby (optional)
-			
-			distinct (optional)
-			
-				
-		This method also understand parameters useful for paging.
-		For more detail see Ext.PagingToolbar.
-			start
-			limit
-			sort
-			dir
-			
-		Return a dictionary with selected data:
-		{success: True, data: [{Table1Field1: xxx,..},...]}
-		
-		"""
-		self._debug("Start DbSvc.read")
-		self._is_keyword("dbFields")
-		
-		# build the list of SQLField object
-		# The JSON process sends a string when only one field is defined
-		# but a list in the other case.
-		# NOTE: The json encoding return the tuple [table, field, func] 
-		# as a string 'table, field, func'
-		fields = []
-		for el in self._list(self._vars.dbFields):
-			li = el.replace(' ','').split(',')
-			(table, field) = li[:2]
-			if not self._is_field_in_table(table, field):
-				raise Exception, FIELD_NOT_IN_DB % ("%s.%s" % (table, field))
-			
-			if len(li) == 2:
-				fields.append(self._db[table][field])
-				
-			elif len(li) == 3:
-				func = li[2]
-				if func not in FUNC_FIELDS:
-					raise Exception, FUNC_FIELD_INVALID % func
-					
-				dbfield = "db.%s.%s.%s()" % (table, field, func)
-				fields.append(eval(dbfield, {}, {"db": self._db}))
-			
-		# build the innerJoins / where conditions
-		# The JSON process sends a string when only one field is defined
-		query = ''
-		if "where" in self._vars:
-			query = self._encode_query(self._list(self._vars["where"]))
-					
-		# handle left join
-		kwargs = {}
-		if "table" in self._vars and "left" in self._vars:
-			table = self._vars.table
-			self._is_table_in_db(table)
-			left_query = self._encode_query(self._list(self._vars["left"]))
-			kwargs["left"] = self._db[table].on(left_query)
-			
-		# handle orderby
-		if "orderby" in self._vars:
-			for el in self._list(self._vars.orderby):
-				(table, field) = el.replace(' ','').split(',')
-				if "orderby" in kwargs:
-					kwargs["orderby"] = kwargs["orderby"] | self._db[table][field]
-				else:
-					kwargs["orderby"] = self._db[table][field]
-						
-		# handle group by
-		if "groupby" in self._vars:
-			(table, field) = self._vars.groupby.replace(' ','').split(',')
-			kwargs["groupby"] = self._db[table][field]
-		
-		# handle distinct
-		if "distinct" in self._vars:
-			kwargs["distinct"] = True
-			
-		# handle paging options (see Ext.PagingToolbar)
-		if 'start' in self._vars and 'limit' in self._vars:
-			kwargs['limitby'] = (int(self._vars.start), int(self._vars.limit))
-		
-		# proceed the select
-		rows = self._db(query).select(*fields, **kwargs)
-
-		self._debug("End DbSvc.read")
-		return {"success": True, "data": rows_serializer(rows)}
-		
-		
-	def update(self):
-		"""Update a record in a table.
-
-		The steering keywords are:
-		
-			debug
-				boolean
-				
-			table
-				the table name where data will be updated
-				
-			data
-				A dictionary containing the update values for field
-				as well as the identifier of the record to be updated (id)
-				
-
-		Return a dictionary with status, message and id of the update row:
-		{success: True, msg: 'blalbla', data:{id:xx}}
-
-		"""
-		self._debug("Start DbSvc.update.")
-		
-		self._is_keyword("table")
-		self._is_keyword("data")
-		
-		table = self._vars.table
-		self._is_table_in_db(table)
-
-		# Remove fields which do not belong to the table.
-		# The JsonStore send the foreign key and the the pointing field
-		# when manipulating table with foreign keys. the latter belongs
-		# to another table.
-		
-		fields = {}
-		for table_field in self._vars.data:
-			tablename, fieldname = decode_field(table_field)
-			if tablename == table:
-				fields[fieldname.encode('utf8')] = self._vars.data[table_field]
-
-		# Validate field contents
-		di = self._is_fields_values_valid(table, fields)
-		if di:
-			return {"success": False, "errors": di}
-			
-		id = fields["id"]
-		self._db[table][id] = fields
-
-		self._debug("End DbSvc.update.")
-		return {"success": True, "msg": RECORD_UPDATED % id, "data": {"id": id}}
+    """ DbSvc
+
+    Interface the database and database model with http request.
+    It is the glue between wep2py and javascript.
+
+    Handle HTTP POST and HTTP GET request.
+    The selection of the action and its parameters are based on
+    steering keywords:  xaction, table, dbFields, debug,...
+    The xaction is mandatory.
+
+    Main actions are:
+
+        create
+            create a new record in a table.
+
+        read
+            read the content of a single table
+            and handle query with foreign key and where conditions
+
+        select
+            read the content of several table handling complex
+            query with join, group by, order by keywords.
+            
+        update
+            update the record of a table.
+
+        destroy
+            delete the record of a table.
+    
+    The steering keyword table is mandatory for the actions
+    create, read, update and destroy.
+    
+    the steering keyword for the select operation is xaction=read
+    but without table definition.
+    
+    Each action return a dictionary:
+    {success: True, data:{blabla}, msg: 'blalbla'}
+    
+    The keyword success is always present. It is mandatory for 
+    the FormPanel widget of the extjs library, but not strictly 
+    required since failure can be propagated through HTTP errors.
+    
+    In the data dictionary database field are encode as TableField.
+
+    If the action failed an Exception is raised.
+    The exception can be catch to generate an HTTP error.
+
+    The most important method are DbSvc.proceed(), DbSvc.create(),
+    DbSvc.destroy(), DbSvc.read(), DbSvc.select(), DbSvc.update()
+
+    """
+
+    KEYWORDS = ['data',
+                'debug',
+                'dbFields',
+                'dir',
+                'groupby',
+                'leftjoin',
+                'limit',
+                'orderby',
+                'sort',
+                'start',
+                'table', 
+                'where',
+                'xaction']
+
+
+    def _encode_query(self, li):
+        """ helper method to encode the list of query as a web2py query.
+        
+                "[table1, field1] == [table2, field2]"
+                "[table1, field1] > n"
+                "[table1, field1] like 'm%'"
+                "[table1, field1] contains 'm%'"
+                "[table1, field1] startswith 'm%'"
+                "[table1, field1].lower() like 'm%'"
+                "[table1, field1] belongs (1, 2, 3)"
+                "([table1, field1] == [table2, field2]) | ([table1, field2] == [table2, field1])"
+                
+                all element of the list are ANDED
+                operator are ==, !=, <, >, <=, >=
+                boolean operator are &, |
+        """ 
+        query = ""
+        for el in li:
+            
+            s = el.replace(' ','')
+            s = s.replace(',', '.')
+            s = s.replace('[', 'db.')
+            s = s.replace(']', '')
+            
+            for op in ['belongs', 'contains', 'like', 'startswith']:
+                if op in s:
+                    s = "%s)" % s.replace(op, '.%s(' % op )
+                
+            query = ("(%s) & (%s)" % (query, s) if query else s)
+
+        return eval(query, {}, {"db": self._db})    
+
+    
+    def _get_fields(self):
+        """Helper method returning a dictionary with request fields
+        and values.
+        
+        The App.data.JsonStore send the field and their values
+        as steering keywords for FormPanel. 
+        
+        Since EXtJs 3.1, the fields and their values are 
+        encapsulated in the data dictionary for a GridPanel.
+        
+        This method allows to extract field values in all cases.
+        
+        """
+        # New approach coming with ExtJs 3.1 where fields values are
+        # encapsulated in the data dictionary
+        if 'data' in self._vars:
+            return self._vars['data']
+        
+        # preserve backward compatibility for formPanel
+        fields = dict(self._vars)
+        for key in self.KEYWORDS:
+            if key in fields:
+                del fields[key]
+        return fields
+        
+        
+    def _is_action_valid(self, action):
+        """Raise an Exception if the request action is not handle."""
+        if action not in ACTIONS:
+            raise Exception, ACTION_INVALID % action
+
+
+    def _is_field_in_table(self, table, field):
+        """ Return true is the field belongs to the table. """
+        return field in self._db[table].fields
+    
+    
+    def _is_fields_values_valid(self, table, fields):
+        """Return a dictionary {field1: error1, field2: error2,...}
+        
+        The value of each field is check against validators defined in the
+        data model. The return dictionary contains field failing these checks.
+        
+        the dictionary is empty when everything is OK.
+        
+        """
+        di = {}
+        for (field, val) in fields.items():
+            errmsg = self._db[table][field].validate(val)[1]
+            if val and self._db[table][field].notnull and errmsg:
+                di[field] = errmsg
+        return di
+
+
+    def _is_table_in_db(self, table):
+        """Raise an Exception if the table is not defined in the database."""
+        if table not in self._db.tables:
+            raise Exception, TABLE_NOT_IN_DB % table
+
+
+    def create(self):
+        """Create a new record in a table.
+
+            debug
+                boolean
+
+            table
+                the table name where data will be inserted.
+
+            data
+                a dictionary with field values {field1: x, field2: y,...}
+                approach use by App.data.JsonStore embedded in GridPanel
+                since ExtJs 3.1
+                
+            field1, field2,... 
+                To keep backward compatibility with formPanel
+                the field value.
+
+                
+        Return a dictionary with status, error message and id of the insert row:
+            {success: False, errors: {field: 'blabla',..}}
+            The keyword success and error are required by 
+            the Ext.FormPanel widget.
+        
+            or
+            
+            {success: True, msg: 'blalbla', data:{id:xx}}
+
+        """
+        self._debug("Start DbSvc.insert")
+        
+        self._is_keyword("table")
+        
+        table = self._vars.table
+        self._is_table_in_db(table)
+
+        # Remove fields which do not belong to the table.
+        # The JsonStore send the foreign key and the the pointing field
+        # when manipulating table with foreign keys. The latter belongs
+        # to another table. 
+        
+        fields, di = {}, self._get_fields()
+        for table_field in di:
+            tablename, fieldname = decode_field(table_field)
+            if tablename == table:
+                fields[fieldname.encode('utf8')] = di[table_field]
+
+        # Validate field contents
+        di = self._is_fields_values_valid(table, fields)
+        if di:
+            return {"success": False, "errors": di}
+        
+        # Create the new record
+        self._debug('fields -->', fields)
+        id = self._db[table].insert (**fields)
+
+        self._debug("End DbSvc.insert.")
+        resp = {"success": True, 
+                "msg": RECORD_INSERTED % id, 
+                "data": {encode_field(table,'id'): id}}
+        return resp 
+
+
+    def destroy(self):
+        """Destroy a record in a table.
+
+            debug
+                boolean
+
+            table
+                the table name where record will be removed.
+
+            data
+                dictionary with the identifier of the record (id)
+                
+        Return a dictionary with status and error message:
+        {success: True, msg: 'blalbla'}
+
+        """
+        self._debug("Start DbSvc.destroy")
+
+        self._is_keyword("table")
+        self._is_keyword("data")
+        
+        table = self._vars.table
+        self._is_table_in_db(table)
+        
+        id = self._vars.data[encode_field(table, "id")]
+        del self._db[table][id]
+        
+        self._debug("End DbSvc.destroy")
+        return {"success": True, "msg": RECORD_DELETED % id}
+
+
+    def proceed(self, vars):
+        """Proceed the request sends via the POST/GET method.
+
+        the input vars is a Storage object.
+
+        Return a dictionary with status, data and error message:
+        {success: True, data:{blabla}, msg: 'blalbla'}
+
+        """
+        BaseSvc.proceed(self, vars)
+        self._debug("Start DbSvc.proceed")
+
+        self._is_keyword("xaction")
+        self._is_action_valid(vars.xaction)
+        
+        # create, destroy, update and read action related to a single table
+        # they are used to manipulate record within a table
+        if "table" in vars:
+            self._debug("self.%s()" % vars.xaction)
+            di = eval("self.%s()" % vars.xaction)
+
+        # complex query involving several tables and conditions
+        # they are use to build view or report
+        elif vars.xaction == 'read':
+            di = self.select()
+            
+        else:
+            raise Exception, ACTION_INVALID % ""
+        
+        self._debug("End DbSvc.proceed")
+        return di
+
+
+    def read(self):
+        """Read the content of a single table resolving foreign keys.
+        
+            debug
+                A boolean
+            
+            table
+                name of the table containing records
+                
+            dbFields
+                A list of field to be red.
+                It also include pointing field resolving foreign keys.
+                [(table1, field1), (table1, field2), (table2, field3), ...]
+                            
+            where (optional)
+                define the inner join when handling foreign key.
+                The syntax follows the web2py convention:
+                ['db.table1.field1  == db.table2.id'].
+                An AND is performed between the different 
+                elements of the list.
+                
+                
+        This method also understand parameters useful for paging.
+        For more detail see  Ext.PagingToolbar.
+            start
+            limit
+            sort
+            dir
+            
+        Return a dictionary with selected data:
+        {success: True, data: [{field1: xxx,..},...]}
+
+        """
+        self._debug("Start DbSvc.read")
+        self._is_keyword("dbFields")
+
+        self._is_keyword("table")        
+        table = self._vars.table
+        self._is_table_in_db(table)
+        
+        # build the list of SQLField object
+        # The JSON process sends a string when only one field is defined
+        # NOTE: The json encoding return the tuple [table, field] 
+        #       as a string 'table, field'
+        fields = []
+        for el in self._list(self._vars.dbFields):
+            (table, field) = el.replace(' ','').split(',')
+            if not self._is_field_in_table(table, field):
+                raise Exception, FIELD_NOT_IN_DB % ("%s.%s" % (table, field))
+            fields.append(self._db[table][field])
+
+        # build the inner join conditions
+        # The JSON process sends a string when only one field is defined
+        query = ''
+        if "where" in self._vars:
+            query = self._encode_query(self._list(self._vars["where"]))
+        
+        # handle paging options (see Ext.PagingToolbar)
+        # Count the number of record in the table --require for the paging
+        kwargs, results = {}, None
+        if 'start' in self._vars and 'limit' in self._vars:
+            start = int(self._vars.start)
+            limit = int(self._vars.limit)
+            kwargs['limitby'] = (start, start+limit)
+            results = self._db(self._db[table].id).count()
+        
+        rows = self._db(query).select(*fields, **kwargs)
+
+        self._debug("End DbSvc.read")
+        return {"success": True, "results": results, "data": rows_serializer(rows)}
+
+
+    def select(self):
+        """ Handle complex queries with inner/left join and with where
+        group by, order by conditions. 
+
+            debug
+                A boolean
+            
+            dbFields
+                A list of field to be red.
+                [(table1, field1), (table2, field1), ....]
+                            
+            where (optional)
+                define the inner join when handling foreign key.
+                The syntax follows the web2py convention:
+                ['db.table1.field1  == db.table2.id', 'db.table2.field3 > 3',...].
+                An AND is performed between the different 
+                elements of the list.
+                
+            left (optional)
+            
+            groupby (optional)
+            
+            orderby (optional)
+            
+            distinct (optional)
+            
+                
+        This method also understand parameters useful for paging.
+        For more detail see Ext.PagingToolbar.
+            start
+            limit
+            sort
+            dir
+            
+        Return a dictionary with selected data:
+        {success: True, data: [{Table1Field1: xxx,..},...]}
+        
+        """
+        self._debug("Start DbSvc.read")
+        self._is_keyword("dbFields")
+        
+        # build the list of SQLField object
+        # The JSON process sends a string when only one field is defined
+        # but a list in the other case.
+        # NOTE: The json encoding return the tuple [table, field, func] 
+        # as a string 'table, field, func'
+        fields = []
+        for el in self._list(self._vars.dbFields):
+            li = el.replace(' ','').split(',')
+            (table, field) = li[:2]
+            if not self._is_field_in_table(table, field):
+                raise Exception, FIELD_NOT_IN_DB % ("%s.%s" % (table, field))
+            
+            if len(li) == 2:
+                fields.append(self._db[table][field])
+                
+            elif len(li) == 3:
+                func = li[2]
+                if func not in FUNC_FIELDS:
+                    raise Exception, FUNC_FIELD_INVALID % func
+                    
+                dbfield = "db.%s.%s.%s()" % (table, field, func)
+                fields.append(eval(dbfield, {}, {"db": self._db}))
+            
+        # build the innerJoins / where conditions
+        # The JSON process sends a string when only one field is defined
+        query = ''
+        if "where" in self._vars:
+            query = self._encode_query(self._list(self._vars["where"]))
+                    
+        # handle left join
+        kwargs = {}
+        if "table" in self._vars and "left" in self._vars:
+            table = self._vars.table
+            self._is_table_in_db(table)
+            left_query = self._encode_query(self._list(self._vars["left"]))
+            kwargs["left"] = self._db[table].on(left_query)
+            
+        # handle orderby
+        if "orderby" in self._vars:
+            for el in self._list(self._vars.orderby):
+                (table, field) = el.replace(' ','').split(',')
+                if "orderby" in kwargs:
+                    kwargs["orderby"] = kwargs["orderby"] | self._db[table][field]
+                else:
+                    kwargs["orderby"] = self._db[table][field]
+                        
+        # handle group by
+        if "groupby" in self._vars:
+            (table, field) = self._vars.groupby.replace(' ','').split(',')
+            kwargs["groupby"] = self._db[table][field]
+        
+        # handle distinct
+        if "distinct" in self._vars:
+            kwargs["distinct"] = True
+            
+        # handle paging options (see Ext.PagingToolbar)
+        if 'start' in self._vars and 'limit' in self._vars:
+            kwargs['limitby'] = (int(self._vars.start), int(self._vars.limit))
+        
+        # proceed the select
+        rows = self._db(query).select(*fields, **kwargs)
+
+        self._debug("End DbSvc.read")
+        return {"success": True, "data": rows_serializer(rows)}
+        
+        
+    def update(self):
+        """Update a record in a table.
+
+        The steering keywords are:
+        
+            debug
+                boolean
+                
+            table
+                the table name where data will be updated
+                
+            data
+                A dictionary containing the update values for field
+                as well as the identifier of the record to be updated (id)
+                
+
+        Return a dictionary with status, message and id of the update row:
+        {success: True, msg: 'blalbla', data:{id:xx}}
+
+        """
+        self._debug("Start DbSvc.update.")
+        
+        self._is_keyword("table")
+        self._is_keyword("data")
+        
+        table = self._vars.table
+        self._is_table_in_db(table)
+
+        # Remove fields which do not belong to the table.
+        # The JsonStore send the foreign key and the the pointing field
+        # when manipulating table with foreign keys. the latter belongs
+        # to another table.
+        
+        fields = {}
+        for table_field in self._vars.data:
+            tablename, fieldname = decode_field(table_field)
+            if tablename == table:
+                fields[fieldname.encode('utf8')] = self._vars.data[table_field]
+
+        # Validate field contents
+        di = self._is_fields_values_valid(table, fields)
+        if di:
+            return {"success": False, "errors": di}
+            
+        id = fields["id"]
+        self._db[table][id] = fields
+
+        self._debug("End DbSvc.update.")
+        return {"success": True, "msg": RECORD_UPDATED % id, "data": {"id": id}}
diff --git a/modules/plugin_dbui/foreignfield.py b/modules/plugin_dbui/foreignfield.py
index c09fb45b..58d9b72e 100644
--- a/modules/plugin_dbui/foreignfield.py
+++ b/modules/plugin_dbui/foreignfield.py
@@ -1,5 +1,5 @@
 """ $Id$
-	Author: R. Le Gac
+    Author: R. Le Gac
 """
 
 __version__ = "$Revision$"
@@ -12,72 +12,71 @@ K_FIELD = "k_field"
 K_KEY = "k_key"
  
 class ForeignField(dict):
-	
-	def __init__(self, db):
-		""" Scan the database model to identify foreign key,
-		Store the pointing table and field.
-		
-		"""
-
-		for table in db.tables:
-			fields = [db[table][el] for el in db[table].fields]
-			
-			for field in fields:
-				#
-				# FOREIGN FIELD
-				#
-				# The reference to the foreign table is defined in the
-				# type: "reference foreignTable".
-				# The foreign table and the associated column are also
-				# defined in the validator IS_IN_DB.
-				#
-				if field.type.startswith("reference"):
-				
-					validators = field.requires
-					if not isinstance(validators, (list, tuple)):
-						validators = [field.requires]
-				
-					for vdt in validators:
-						if isinstance(vdt, IS_IN_DB):
-							
-							if table not in self:
-								self[table] = {}
-								
-							self[table][field.name] = {}
-							self[table][field.name][K_TABLE] = vdt.ktable
-							self[table][field.name][K_FIELD] = vdt.ks[0]
-							self[table][field.name][K_KEY] = vdt.ks[1]
-							
-	
-	def is_foreign_field(self, table, field):
-		""" Return true is the table.field is a foreign key.
-		
-		"""
-		if table in self:
-			return field in self[table]
-		
-		else:
-			return False
-		
-		
-	def is_table_with_foreign_fields(self, table):
-		return (True if table in self else False)
-	
-	 
-	def get_foreign_data(self, table, field):
-		""" Return a tuple containing the pointing table, the pointing field 
-		and key for the join:
-		
-			table.field --> k_table.k_field
-			table.field == k_table.k_key
-			
-		Otherwise return None.
-		
-		"""
-		if self.is_foreign_field(table, field):
-			di = self[table][field]
-			return (di[K_TABLE], di[K_FIELD], di[K_KEY])
-		
-		else:
-			return None
+    
+    def __init__(self, db):
+        """ Scan the database model to identify foreign key,
+        Store the pointing table and field.
+        
+        """
 
+        for table in db.tables:
+            fields = [db[table][el] for el in db[table].fields]
+            
+            for field in fields:
+                #
+                # FOREIGN FIELD
+                #
+                # The reference to the foreign table is defined in the
+                # type: "reference foreignTable".
+                # The foreign table and the associated column are also
+                # defined in the validator IS_IN_DB.
+                #
+                if field.type.startswith("reference"):
+                
+                    validators = field.requires
+                    if not isinstance(validators, (list, tuple)):
+                        validators = [field.requires]
+                
+                    for vdt in validators:
+                        if isinstance(vdt, IS_IN_DB):
+                            
+                            if table not in self:
+                                self[table] = {}
+                                
+                            self[table][field.name] = {}
+                            self[table][field.name][K_TABLE] = vdt.ktable
+                            self[table][field.name][K_FIELD] = vdt.ks[0]
+                            self[table][field.name][K_KEY] = vdt.ks[1]
+                            
+    
+    def is_foreign_field(self, table, field):
+        """ Return true is the table.field is a foreign key.
+        
+        """
+        if table in self:
+            return field in self[table]
+        
+        else:
+            return False
+        
+        
+    def is_table_with_foreign_fields(self, table):
+        return (True if table in self else False)
+    
+     
+    def get_foreign_data(self, table, field):
+        """ Return a tuple containing the pointing table, the pointing field 
+        and key for the join:
+        
+            table.field --> k_table.k_field
+            table.field == k_table.k_key
+            
+        Otherwise return None.
+        
+        """
+        if self.is_foreign_field(table, field):
+            di = self[table][field]
+            return (di[K_TABLE], di[K_FIELD], di[K_KEY])
+        
+        else:
+            return None
diff --git a/modules/plugin_dbui/setfield.py b/modules/plugin_dbui/setfield.py
index 23b39607..53aa3417 100644
--- a/modules/plugin_dbui/setfield.py
+++ b/modules/plugin_dbui/setfield.py
@@ -61,4 +61,3 @@ class SetField(dict):
         
         else:
             return None
-
diff --git a/modules/plugin_dbui/tools.py b/modules/plugin_dbui/tools.py
index 54d05350..e436d159 100644
--- a/modules/plugin_dbui/tools.py
+++ b/modules/plugin_dbui/tools.py
@@ -1,5 +1,5 @@
 """ $Id$
-	Author: R. Le Gac
+    Author: R. Le Gac
 """
 
 __version__ = "$Revision$"
@@ -12,222 +12,222 @@ ALL = '_all'
 
 
 def _init_tool(param, type='dict'):
-	""" Return the plugin manager with the parameter param
-	initialize as a dictionary, if it does not exist.
-	
-	If can be a list if the type is equal to list.
-	
-	"""
-	p = PluginManager('dbui')
-	if param not in p.dbui:
-		if type == 'dict':
-			p.dbui[param] = {}
-		elif type == 'list':
-			p.dbui[param] = []
-	return p
+    """ Return the plugin manager with the parameter param
+    initialize as a dictionary, if it does not exist.
+    
+    If can be a list if the type is equal to list.
+    
+    """
+    p = PluginManager('dbui')
+    if param not in p.dbui:
+        if type == 'dict':
+            p.dbui[param] = {}
+        elif type == 'list':
+            p.dbui[param] = []
+    return p
 
 
 def customize_field(tablename, fieldname, **extjs):
-	""" Helper tool to customize an existing field by applying the 
-	Ext JS configuration parameters.
-	
-	NOTE: If the xtype is defined as a parameter the default widget
-	will be replaced by the new one -- not recommand.
-	
-	"""
-	p = _init_tool('customized_fields')
-	if tablename not in p.dbui.customized_fields:
-		p.dbui.customized_fields[tablename] = {}
-	p.dbui.customized_fields[tablename][fieldname] = extjs	
+    """ Helper tool to customize an existing field by applying the 
+    Ext JS configuration parameters.
+    
+    NOTE: If the xtype is defined as a parameter the default widget
+    will be replaced by the new one -- not recommand.
+    
+    """
+    p = _init_tool('customized_fields')
+    if tablename not in p.dbui.customized_fields:
+        p.dbui.customized_fields[tablename] = {}
+    p.dbui.customized_fields[tablename][fieldname] = extjs    
 
 
 def customize_form(tablename=None, **extjs):
-	""" Helper tool to apply Ext JS configuration parameters on existing form.
-	For details see the documentation of Ext.form.formPanel.
-	
-	The most interesting parameters are labelwidth, labelAlign, 
-	labelSeparator, defaults, layout, layoutConfig and width.
-	
-	If tablename is not define, the parameters will be apply to
-	all forms. The order is the following apply first the table
-	parameters then the general ones.
-	
-	"""
-	p = _init_tool('customized_forms')
-	key = tablename
-	if key == None: key = ALL
-	p.dbui.customized_forms[key] = extjs
-	
+    """ Helper tool to apply Ext JS configuration parameters on existing form.
+    For details see the documentation of Ext.form.formPanel.
+    
+    The most interesting parameters are labelwidth, labelAlign, 
+    labelSeparator, defaults, layout, layoutConfig and width.
+    
+    If tablename is not define, the parameters will be apply to
+    all forms. The order is the following apply first the table
+    parameters then the general ones.
+    
+    """
+    p = _init_tool('customized_forms')
+    key = tablename
+    if key == None: key = ALL
+    p.dbui.customized_forms[key] = extjs
+    
 
 def customize_form_with_field_set(tablename, title, fields, spacer=0, **extjs):
-	""" Helper tool to customize an existing form with fieldset.
-
-	By default fields will occupy all the space in the fieldset panel.
-	this behavior can be superseded using the Ext JS 'defaults' configuration 
-	parameter.
-	
-
-			tablename
-				name of the tablename
-
-			title
-				title of the fieldset
-
-			fields
-				list of field name, belonging to the tablename,
-				groups in the fieldset.
-
-			spacer
-				add a Ext.Spacer at the end of the item list.
-				The value defines the height of the spacer.
-				Useful to tune the final height of the field set.
-				
-			extjs
-				additional keyword arguments corresponding
-				to Ext.form.FieldSet configuration parameters
-				(See the Ext JS APIdocumentation)
-	
-	"""
-	p = _init_tool('field_sets')
-	if tablename not in p.dbui.field_sets:
-		p.dbui.field_sets[tablename] = []
-	
-	# by default all fields occupy the full space in the fieldset panel
-	if 'defaults' in extjs:
-		if 'anchor' not in extjs['defaults']:
-			extjs['defaults'] = '100%'
-	else:
-		extjs['defaults'] = {'anchor': '100%'}
-		
-	# add the spacer component
-	if spacer:
-		di = {'xtype': 'spacer', 'height': spacer}
-		if 'items' in extjs:
-			items.append(di)
-		else:
-			extjs['items'] = [di]
-		
-	di = Storage(title=title, fields=fields, extjs= extjs)
-	p.dbui.field_sets[tablename].append(di)
+    """ Helper tool to customize an existing form with fieldset.
+
+    By default fields will occupy all the space in the fieldset panel.
+    this behavior can be superseded using the Ext JS 'defaults' configuration 
+    parameter.
+    
+
+            tablename
+                name of the tablename
+
+            title
+                title of the fieldset
+
+            fields
+                list of field name, belonging to the tablename,
+                groups in the fieldset.
+
+            spacer
+                add a Ext.Spacer at the end of the item list.
+                The value defines the height of the spacer.
+                Useful to tune the final height of the field set.
+                
+            extjs
+                additional keyword arguments corresponding
+                to Ext.form.FieldSet configuration parameters
+                (See the Ext JS APIdocumentation)
+    
+    """
+    p = _init_tool('field_sets')
+    if tablename not in p.dbui.field_sets:
+        p.dbui.field_sets[tablename] = []
+    
+    # by default all fields occupy the full space in the fieldset panel
+    if 'defaults' in extjs:
+        if 'anchor' not in extjs['defaults']:
+            extjs['defaults'] = '100%'
+    else:
+        extjs['defaults'] = {'anchor': '100%'}
+        
+    # add the spacer component
+    if spacer:
+        di = {'xtype': 'spacer', 'height': spacer}
+        if 'items' in extjs:
+            items.append(di)
+        else:
+            extjs['items'] = [di]
+        
+    di = Storage(title=title, fields=fields, extjs= extjs)
+    p.dbui.field_sets[tablename].append(di)
 
 
 def customize_grid(tablename=None, **extjs):
-	""" Helper tool to apply Ext JS configuration parameters on existing grid.
-	For details see the documentation of Ext.grid.GridPanel.
+    """ Helper tool to apply Ext JS configuration parameters on existing grid.
+    For details see the documentation of Ext.grid.GridPanel.
 
-	The most interesting parameter is plugins:
-	pGridRowEditorContextMenu, pGridPaging, .... 
+    The most interesting parameter is plugins:
+    pGridRowEditorContextMenu, pGridPaging, .... 
 
-	If tablename is not define, the parameters will be apply to
-	all grids. The order is the following apply first the table
-	parameters then the general ones.
-	
-	"""
-	p = _init_tool('customized_grids')
-	key = tablename
-	if key == None: key = ALL
-	p.dbui.customized_grids[key] = extjs
+    If tablename is not define, the parameters will be apply to
+    all grids. The order is the following apply first the table
+    parameters then the general ones.
+    
+    """
+    p = _init_tool('customized_grids')
+    key = tablename
+    if key == None: key = ALL
+    p.dbui.customized_grids[key] = extjs
 
 
 def customize_grid_with_template_column(tablename,
-									    header, 
-									    tpl, 
-									    ipos=0, 
-									    hidden=[],
-									    **extjs):
-	""" Helper tool to add a template column to an existing grid.
-	
-			tablename
-				name of the table associate to the grid
-				
-			header
-				Header of the column in the grid
-				
-			tpl
-				Ext.Template for the column
-			
-			ipos
-				position of the column in the grid
-				i.e 0 mean the first column, -1 inserts 
-				before the last element
-				
-			hidden
-				List of columns to be hidden in the grid since they
-				are aggregate in the column template. Columns are identified
-				via their fieldname.
-
-			extjs
-				additional keyword arguments corresponding
-				to Ext.grid.Column configuration parameters
-				(See the documentation of Ext.grid.Column)
-				
-	"""
-	p = _init_tool('template_columns')
-	p = _init_tool('hidden_columns')
-	
-	if tablename not in p.dbui.template_columns:
-		p.dbui.template_columns[tablename] = []
-		p.dbui.hidden_columns[tablename] = []
-	
-	di = Storage(header=header, tpl=tpl, ipos=ipos, extjs=extjs)
-	p.dbui.template_columns[tablename].append(di)
-	
-	for el in hidden:
-		if el not in p.dbui.hidden_columns[tablename]:
-			p.dbui.hidden_columns[tablename].append(el)
-		
-	
+                                        header, 
+                                        tpl, 
+                                        ipos=0, 
+                                        hidden=[],
+                                        **extjs):
+    """ Helper tool to add a template column to an existing grid.
+    
+            tablename
+                name of the table associate to the grid
+                
+            header
+                Header of the column in the grid
+                
+            tpl
+                Ext.Template for the column
+            
+            ipos
+                position of the column in the grid
+                i.e 0 mean the first column, -1 inserts 
+                before the last element
+                
+            hidden
+                List of columns to be hidden in the grid since they
+                are aggregate in the column template. Columns are identified
+                via their fieldname.
+
+            extjs
+                additional keyword arguments corresponding
+                to Ext.grid.Column configuration parameters
+                (See the documentation of Ext.grid.Column)
+                
+    """
+    p = _init_tool('template_columns')
+    p = _init_tool('hidden_columns')
+    
+    if tablename not in p.dbui.template_columns:
+        p.dbui.template_columns[tablename] = []
+        p.dbui.hidden_columns[tablename] = []
+    
+    di = Storage(header=header, tpl=tpl, ipos=ipos, extjs=extjs)
+    p.dbui.template_columns[tablename].append(di)
+    
+    for el in hidden:
+        if el not in p.dbui.hidden_columns[tablename]:
+            p.dbui.hidden_columns[tablename].append(el)
+        
+    
 def customize_viewport():
-	""" Helper tool to customize the viewport
-	
-	"""
-	p = _init_tool('customized_viewport')
+    """ Helper tool to customize the viewport
+    
+    """
+    p = _init_tool('customized_viewport')
 
 
 def define_combined_field():
-	""" Helper tool to define combined fields in form.
-	
-	"""
-	p = _init_tool('combined_fields')
+    """ Helper tool to define combined fields in form.
+    
+    """
+    p = _init_tool('combined_fields')
 
 
 def define_grid_filter(tablename, filters, **extjs):
-	""" Helper tool to define filter associate to grid.
-
-			tablename
-				name of the database table
-				
-			filters
-				a list of tuple.
-				Each tuple contains three columns:
-						-- a string with the database field
-						-- a string with the operator
-						   the list of valid operator is defined in
-						   the method dbsvc._encode_query
-						-- a string with a comment for the tool tip
-			extjs
-				additional keyword arguments corresponding
-				to the configuration parameter of Ext.form.FieldSet
-				(see the Ext JS API documentation)
-	"""
-	p = _init_tool('grid_filters')
-
-	di = Storage(filters=filters, extjs=extjs)
-	p.dbui.grid_filters[tablename] = di
+    """ Helper tool to define filter associate to grid.
+
+            tablename
+                name of the database table
+                
+            filters
+                a list of tuple.
+                Each tuple contains three columns:
+                        -- a string with the database field
+                        -- a string with the operator
+                           the list of valid operator is defined in
+                           the method dbsvc._encode_query
+                        -- a string with a comment for the tool tip
+            extjs
+                additional keyword arguments corresponding
+                to the configuration parameter of Ext.form.FieldSet
+                (see the Ext JS API documentation)
+    """
+    p = _init_tool('grid_filters')
+
+    di = Storage(filters=filters, extjs=extjs)
+    p.dbui.grid_filters[tablename] = di
 
 
 def define_wizard():
-	""" Helper tool to define wizard in form.
-	
-	"""
-	p = _init_tool('wizards')
+    """ Helper tool to define wizard in form.
+    
+    """
+    p = _init_tool('wizards')
 
 
 def hidden_tables(*args):
-	""" Helper tools to define a set of tables to be hidden in the viewport.
-	
-	"""
-	p = _init_tool('hidden_tables', 'list')
-	p.dbui.hidden_tables.extend(args)
-	
-	
\ No newline at end of file
+    """ Helper tools to define a set of tables to be hidden in the viewport.
+    
+    """
+    p = _init_tool('hidden_tables', 'list')
+    p.dbui.hidden_tables.extend(args)
+    
+    
\ No newline at end of file
-- 
GitLab