Commit 4184ebba authored by tux091's avatar tux091
Browse files

Major redesign of the configurator section:

- remove the service CvtSvc
- introduce a new module extjs containing a configurator class for each ExtJS component.
- introduce a new module converter containing helper function transforming web2py dal object into ExtJS configurator.
- deploy configurator in the rest of the code.
- define an xtype forl the App.Viewport class.
- Remove the widget class and transform the Spacer into a configurator.
- clean the navtree module. Helper function are replaced by configurator
parent 90224604
......@@ -119,7 +119,8 @@ def get_api():
"""
from gluon.contrib import simplejson as json
dbui = local_import('plugin_dbui')
di = {}
dbui = local_import('plugin_dbui')
......@@ -141,7 +142,7 @@ def get_api():
# the stores configuration for each table
storeCfgs = {}
for table in db:
storeCfgs[table._tablename] = cvtSvc.to_jsonstore(table)
storeCfgs[table._tablename] = dbui.to_jsonstore(table)
# fill the javascript template
app = request.application
......
......@@ -44,27 +44,25 @@ plugins = PluginManager('dbui',
# Start common services
dbSvc = dbui.DbSvc(globals())
cvtSvc = dbui.CvtSvc(globals())
directSvc = dbui.DirectSvc(globals())
# activate the debug mode
# this session variable is set by the control index or debug
dbSvc.debug = session.debug
cvtSvc.debug = session.debug
directSvc.debug = session.debug
# register method available on the client side
@directSvc.register
def getForm(tablename):
return cvtSvc.to_form_panel(db[tablename])
return dbui.to_formPanel(db[tablename])
@directSvc.register
def getGrid(tablename):
return cvtSvc.to_grid_panel(db[tablename])
return dbui.to_gridPanel(db[tablename])
@directSvc.register
def getTree(node):
return cvtSvc.to_tree(node)
return dbui.to_tree(node)
@directSvc.register
def create(data):
......
......@@ -184,24 +184,23 @@ gridModifier.set_filters(*filters,
# The navigation tree
#
formNode = dbui.Node(T('Forms'))
configurator = lambda tablename: cvtSvc.to_form_panel(db[tablename])
configurator = lambda tablename: dbui.to_formPanel(db[tablename])
formNode.add_children(db.tables, func=configurator)
gridNode = dbui.Node(T('Tables'))
configurator = lambda tablename: cvtSvc.to_grid_panel(db[tablename])
configurator = lambda tablename: dbui.to_gridPanel(db[tablename])
gridNode.add_children(db.tables, func=configurator)
reportNode = dbui.Node(T('Reports'))
node = dbui.to_panel(html='salut ma poule')
node = dbui.Panel(html='salut ma poule')
reportNode.add_child(T('report_1'), node)
panel = dbui.to_panel()
fields = cvtSvc.to_form_items(dummy.foo1)
selector = dbui.to_fieldset(fields, title=T('Select'))
node = dbui.to_panelWithUrlSelector(panel,
selector,
controller="reports",
function="report_2")
fields = dbui.to_fields(dummy.foo1)
selector = dbui.FieldSet(items=fields, title=T('Select'))
node = dbui.PanelWithUrlSelector(baseUrl=URL('reports', 'report_2'),
isMathJax=dbui.is_mathjax(),
panelCfg=dbui.Panel(),
selectorCfg=selector)
reportNode.add_child(T('report_2'), node)
viewportModifier = dbui.ViewportModifier()
......
......@@ -2,9 +2,42 @@
"""
from cvtsvc import CvtSvc
from converter import (to_field,
to_fields,
to_formPanel,
to_gridColumn,
to_gridColumnModel,
to_gridFilter,
to_gridPanel,
to_jsonstore,
to_tree)
from dbsvc import DbSvc
from directsvc import DBUI, DirectSvc
from extjs import (CheckBox,
ComboBox,
CompositeField,
DateField,
DirectStore,
Field,
FieldSet,
FormPanel,
GridColumn,
GridColumnModel,
GridFilter,
GridPanel,
GridRowNumberer,
GridTemplateColumn,
GridWithFilter,
NumberField,
Panel,
PanelWithUrlSelector,
SetBox,
Spacer,
TabPanel,
TextArea,
TextField,
TimeField,
Viewport)
from fieldsmodifier import FieldsModifier
from formmodifier import configure_forms, FormModifier
from gridmodifier import configure_grids, GridModifier
......@@ -27,10 +60,5 @@ from helper import (as_list,
is_table_with_foreign_fields,
rows_serializer)
from mapper import map_default, map_tabpanel
from modifier import Spacer, Widget
from navtree import (Node,
to_fieldset,
to_panel,
to_panelWithUrlSelector,
to_urlPanel)
from navtree import Node
from viewportmodifier import ViewportModifier
\ No newline at end of file
""" Converters transforming Web2py DAL objects into ExtJS widgets.
Author: R. Le Gac
"""
from extjs import *
from gluon import current
from helper import (encode_field,
get_field_validators,
get_foreign_field,
get_set_field,
is_foreign_field,
is_set_field)
from mapper import map_default
# Associated gluon.dal.field type with an ExtJS widget
# The dictionary contains configuration parameters of the Ext JS widget
FTYPE_TO_XTYPE = {'boolean': (CheckBox, {}),\
'date': (DateField, {'format': 'Y-m-d'}),\
'datetime': (DateField, {'format': 'Y-m-d H:i:s'}),\
'double': (NumberField, {'decimalPrecision':2,
'decimalSeparator': '.'}),\
'id': (TextField, {}),\
'integer': (NumberField, {}),\
'password': (TextField, {'inputType': 'password'}),\
'reference': (ComboBox, {}),\
'string': (TextField, {}),\
'text': (TextArea, {}),\
'time': (TimeField, {}),\
'upload': (TextField, {'inputType': 'file'})}
# constant defining a ExtJS store
ROOT = 'records'
STOREID = '%sStore'
SUCCESS= 'success'
TOTAL = 'count'
def _to_field(field, **kwargs):
"""Private converter. It is recommended to used to_field.
Convert a gluon.dal.Field into an ExtJS.form.Field and its
inherited class. It does not handle composite field.
The conversion takes into account the FieldsModifier instructions.
ExtJS configuration parameters are applied in the following order:
constructor, modifers, keyword arguments.
"""
T = current.T
fieldname = field.name
tablename = field.tablename
f_name = encode_field(tablename, fieldname)
# foreign key
if is_foreign_field(field):
k_tablename, k_fieldname, k_key = get_foreign_field(field)
cfg = ComboBox(name=f_name,
hiddenName=f_name,
displayField=encode_field(k_tablename, k_fieldname),
valueField=encode_field(k_tablename, k_key),
store=STOREID % k_tablename)
# set field
elif is_set_field(field):
cfg = SetBox(name=f_name,
model={"setData": get_set_field(field)})
# other types of fields
else:
cls, di = FTYPE_TO_XTYPE[field.type]
cfg = cls(name=f_name, **di)
# field label translate in the local languqge
cfg["fieldLabel"] = str(T(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
# validator constraints
cfg.update(get_field_validators(field))
# hide primary key
# hide field which are not readable and not writable
# hide field request by the form modifier
hidden = field.name == "id"
hidden = hidden or ((not field.readable) and (not field.writable))
modifier_forms = PluginManager('dbui').dbui.modifier_forms
if tablename in modifier_forms:
if fieldname in modifier_forms[tablename].hidden_fields:
hidden = True
if hidden:
cfg["hidden"] = True
cfg["hideLabel"] = True
cfg["readOnly"] = True
# configuration options set by the field_modifers
modifier_fields = PluginManager('dbui').dbui.modifier_fields
if tablename in modifier_fields:
if fieldname in modifier_fields[tablename].extjs_fields:
cfg.update(modifier_fields[tablename].extjs_fields[fieldname])
# configuration options from the keyword arguments
cfg.update(kwargs)
return cfg
def to_field(field, **kwargs):
"""Convert a gluon.dal.Field into an Ext.form.Field and its inherited class
as well as into a composite field, i.e Ext.form.CompositeField.
Composite fields are set using the FieldsModifer.
Return None if the field is comsumed by a CompositeField.
ExtJS configuration parameters are applied in the following order:
constructor, modifers, keyword arguments.
"""
table = field.table
tablename = field.tablename
modifier_fields = PluginManager('dbui').dbui.modifier_fields
# do we have composite field for this table ?
composite_fields = None
if tablename in modifier_fields:
modifier_field = modifier_fields[tablename]
composite_fields = modifier_field.composite_fields
# main field of the composite field
# it will consume the embedded field too
if composite_fields and field.name in composite_fields.main:
cfg = CompositeField()
for fieldname in composite_fields[field.name].fields:
cfg.append_items(_to_field(table[fieldname]))
# use configuration options from the modifier
cfg.update(composite_fields[field.name].extjs)
# an embedded field already used in a composite field
elif composite_fields and field.name in composite_fields.others:
cfg = None
# a basic field
else:
cfg = _to_field(field)
# configuration options from keyword arguments
if cfg:
cfg.update(kwargs)
return cfg
def to_fields(table):
"""Convert a gluon.dal.Table into a list of Ext.form.Field,
Ext.form.CompositeField or and Ext.form.FieldSet.
Individual fields can be organized in Fieldset and in CompositeField.
They are defined in the application model using modifiers namely
the FormModifier and the FieldsModifier respectively.
The conversion takes into account the modifier instructions.
"""
li = []
tablename = table._tablename
modifier_forms = PluginManager('dbui').dbui.modifier_forms
# do we have FieldSets
field_sets, modifier_form = None, None
if tablename in modifier_forms:
modifier_form = modifier_forms[tablename]
if modifier_form and modifier_form.field_sets:
field_sets = modifier_form.field_sets
# Table with Ext.form.FieldSet and/or Ext.form.CompositeFields
# and/or Ext.form.Field and/or Spacer
# 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.
id_is_used = False
if field_sets:
for fieldset in field_sets:
cfg = FieldSet()
for fieldname in fieldset.fields:
if isinstance(fieldname, Base):
cfg.append_items(fieldname)
continue
if fieldname == 'id':
id_is_used = True
field = table[fieldname]
item = to_field(field)
if item:
cfg.append_items(item)
# fieldset configuration options ser by the modifier
cfg.update(fieldset.extjs)
li.append(cfg)
# append the field Id the last field set
if not id_is_used:
li[-1].append_items(to_field(table['id']))
# Table with Ext.form.CompositeFields and/or Ext.form.Fields
else:
for field in table:
cfg = to_field(field)
if cfg:
li.append(cfg)
# map the list of fields/fieldSets on Ext.form.formPanel
mapper = map_default
if modifier_form and modifier_form.mapper:
mapper = modifier_form.mapper
li = mapper(li)
return li
def to_formPanel(table, **kwargs):
"""Convert a gluon.dal.Table into a ExtJS App.form.FormPanel.
The conversion takes into account the form modifier instructions.
ExtJS configuration parameters are applied in the following order:
constructor, modifers, keyword arguments.
"""
tablename = table._tablename
fields = to_fields(table)
cfg = FormPanel(items=fields)
# add a store for non dummy database
if table._db._uri:
cfg['store'] = STOREID % tablename
# configuration options from form modifier
modifier_forms = PluginManager('dbui').dbui.modifier_forms
if tablename in modifier_forms:
cfg.update(modifier_forms[tablename].extjs)
# configuration options form the keyword arguments
cfg.update(kwargs)
return cfg
def to_gridColumn(field, **kwargs):
"""Convert a gluon.dal.Field into an Ext.grid.Column.
The conversion takes into account the grid modifier instructions.
ExtJS configuration parameters are applied in the following order:
constructor, modifers, keyword arguments.
"""
T = current.T
fieldname = field.name
tablename = field.tablename
cfg = GridColumn(header=str(T(field.label)),
sortable=True)
# name of the field in the data store
# replace foreign key by the pointing column
if is_foreign_field(field):
k_tablename, k_fieldname, k_key = get_foreign_field(field)
index = encode_field(k_tablename, k_fieldname)
else:
index = encode_field(tablename, fieldname)
cfg["dataIndex"] = index
# hide the primary key
if fieldname == "id":
cfg["hidden"] = True
# hide field which are not readable and not writable
if (not field.readable) and (not field.writable):
cfg["hidden"] = True
# Hide fields request by the grid modifiers
modifier_grids = PluginManager('dbui').dbui.modifier_grids
if tablename in modifier_grids:
if fieldname in modifier_grids[tablename].hidden_columns:
cfg["hidden"] = True
# configuration options from the grid modifier
if tablename in modifier_grids:
if fieldname in modifier_grids[tablename].configure_columns:
cfg.update(modifier_grids[tablename].configure_columns[fieldname])
# configuration options from the keyword arguments
cfg.update(kwargs)
return cfg
def to_gridColumnModel(table):
"""Convert a gluon.dal.Table into an Ext.grid.ColumnModel.
The conversion takes into account the grid modifier instructions.
Currently the gridColumModel is implemented as a list of gridColumn.
"""
delete_columns = []
template_columns = []
tablename = table._tablename
# get modifier requirements
modifier_grids = PluginManager('dbui').dbui.modifier_grids
if tablename in modifier_grids:
delete_columns = modifier_grids[tablename].delete_columns
template_columns = modifier_grids[tablename].template_columns
# standard column
cfg = GridColumnModel()
for field in table:
fieldname = field.name
if fieldname not in delete_columns:
cfg.append(to_gridColumn(field))
# template column
for tpl in template_columns:
col = GridTemplateColumn(sortable=True)
col.update(tpl.extjs)
cfg.insert(tpl.position, col)
# row numbering in the first column
if modifier_grids[tablename].row_numbering:
cfg.insert(0, GridRowNumberer())
return cfg
def to_gridFilter(table, **kwargs):
"""Convert a gluon.dal.Table into an ExtJS App.grid.GridFilter.
The grid filter is set using the grid modifiers.
Return an empty dictionary if not defined
ExtJS configuration parameters are applied in the following order:
constructor, modifers, keyword arguments.
"""
T = current.T
tablename = table._tablename
modifier_grids = PluginManager('dbui').dbui.modifier_grids
if tablename not in modifier_grids:
return {}
grid_filters = modifier_grids[tablename].grid_filters
if not grid_filters:
return {}
di = GridFilter(title=T('Filter %s' % tablename))
for (fieldname, operator, comment) in grid_filters.filters:
field = table[fieldname]
# get the standard configuration for the field
cfg = to_field(field)
# 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'] = field.type
di.append_items(cfg)
# configuration options from the grid modifier
di.update(grid_filters.extjs)
# configuration option from keyword arguments
di.update(kwargs)
return di
def to_gridPanel(table, **kwargs):
"""Convert a gluon.dal.Table into an ExtJS App.grid.GridPanel.
The conversion takes into account the grid modifier instructions.
ExtJS configuration parameters are applied in the following order:
constructor, modifers, keyword arguments.
"""
tablename = table._tablename
cfg = GridPanel(columns=to_gridColumnModel(table),
frame=False,
viewConfig={'forceFit': True})
# add a store for non dummy database
if table._db._uri:
cfg['store'] = STOREID % tablename
# configuration option from the grid modifier
modifier_grids = PluginManager('dbui').dbui.modifier_grids
if tablename in modifier_grids:
cfg.update(modifier_grids[tablename].extjs)
# configuration option from the keyword arguments
cfg.update(kwargs)
# grid with filter
filter = to_gridFilter(table)
if filter:
cfg = GridWithFilter(panelCfg=dict(cfg),
selectorCfg=filter)
return cfg
def to_jsonstore(table, **kwargs):
"""Convert a gluon.dal.Table into an App.data.DirectStore.
ExtJS configuration parameters are applied in the following order:
constructor, modifers, keyword arguments.
"""
db = table._db
tablename = table._tablename
# NOTE: the configuration option baseParams defined the
# transactions parameters seen by the server
# This dictionary contains 3 keys:
# tablename: name of the table in the database
# dbFields: list of fields which should be red