From 0a5b672b72e9304c3a0c05d4073107cbfd2a31f1 Mon Sep 17 00:00:00 2001
From: legac <renaud.legac@free.fr>
Date: Sat, 21 Sep 2013 23:09:11 +0200
Subject: [PATCH] first implemmentation of the dictfield widget.

---
 languages/fr-fr.py                  |   5 ++
 models/db_core.py                   |  10 +++
 models/widgets_fields.py            |   8 +-
 modules/plugin_dbui/converter.py    |  33 ++++---
 modules/plugin_dbui/extjs.py        |   5 ++
 static/plugin_dbui/src/dictfield.js | 135 ++++++++++++++++++++++++++++
 6 files changed, 182 insertions(+), 14 deletions(-)
 create mode 100644 static/plugin_dbui/src/dictfield.js

diff --git a/languages/fr-fr.py b/languages/fr-fr.py
index f2f7ff72..c7f28d0e 100644
--- a/languages/fr-fr.py
+++ b/languages/fr-fr.py
@@ -20,6 +20,7 @@
 'Country': 'Pays',
 'Dates': 'Dates',
 'Definition': 'Définition',
+'dev': 'dev',
 'Doi': 'Doi',
 'domain': 'domaine',
 'E Print': 'E Print',
@@ -46,7 +47,10 @@
 'Help': 'Help',
 'Host': 'Host',
 'Id': 'Id',
+'invalid json': 'invalid json',
+'Json': 'Json',
 'Last Page': 'Dernière Page',
+'List': 'List',
 'Max Records': 'Max Records',
 'My Axes': 'My Axes',
 'My Axis': 'My Axis',
@@ -88,6 +92,7 @@
 'Speaker': 'Orateur',
 'Start date': 'Date de début',
 'Store': 'Store',
+'String': 'String',
 'Table': 'Table',
 'Tables': 'Tables',
 'team': 'équipe',
diff --git a/models/db_core.py b/models/db_core.py
index f43597a7..7f9bbfb4 100644
--- a/models/db_core.py
+++ b/models/db_core.py
@@ -88,6 +88,16 @@ db.define_table("teams",
     migrate="teams.table")
 
 db.teams.team.filter_in = dbui.CLEAN_SPACES
+
+db.define_table("dev",
+    Field("string", "string"),
+    Field("list", "list:string"),
+    Field("json", "json"),
+    migrate="dev.table")
+
+# NOTE: if we remove the json validator its seem to work ?
+db.dev.json.requires = None
+
 #-------------------------------------------------------------------------------
 #
 # Publications table
diff --git a/models/widgets_fields.py b/models/widgets_fields.py
index 1add4040..9ac81991 100644
--- a/models/widgets_fields.py
+++ b/models/widgets_fields.py
@@ -26,4 +26,10 @@ fieldsModifier.merge_fields('first_page',
 fieldsModifier.merge_fields('conference_start',
                             'conference_end',
                             fieldLabel=T('Dates'), 
-                            defaults={'flex': 1})
\ No newline at end of file
+                            defaults={'flex': 1})
+
+#
+# dev
+#
+fieldsModifier = dbui.FieldsModifier('dev')
+fieldsModifier.configure_field('json', dictCfg={"foo": "test", "faa": False, "fii": 0})
diff --git a/modules/plugin_dbui/converter.py b/modules/plugin_dbui/converter.py
index ba214f54..40ad90ec 100644
--- a/modules/plugin_dbui/converter.py
+++ b/modules/plugin_dbui/converter.py
@@ -19,19 +19,22 @@ 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'})}
+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, {}),\
+                  'json':         (DictField, {}),
+                  'list:string':  (TextArea, {}),\
+                  'list:integer': (TextArea, {}),\
+                  'password':     (TextField, {'inputType': 'password'}),\
+                  'reference':    (ComboBox, {}),\
+                  'string':       (TextField, {}),\
+                  'text':         (TextArea, {}),\
+                  'time':         (TimeField, {}),\
+                  'upload':       (TextField, {'inputType': 'file'})}
 
 
 # constant defining a ExtJS store
@@ -365,6 +368,10 @@ def to_gridColumn(field, **kwargs):
     if (not field.readable) and (not field.writable):
         cfg["hidden"] = True
 
+    # dedicated column render fo json field
+    if field.type == "json":
+        cfg["xtype"] = "jsoncolumn"
+        
     # Hide fields request by the grid modifiers
     modifier_grids = PluginManager('dbui').dbui.modifier_grids
     if tablename in modifier_grids:
diff --git a/modules/plugin_dbui/extjs.py b/modules/plugin_dbui/extjs.py
index 95ef3cbd..f4db72d9 100644
--- a/modules/plugin_dbui/extjs.py
+++ b/modules/plugin_dbui/extjs.py
@@ -91,6 +91,11 @@ class DateField(Base):
     xtype = 'datefield'
 
 
+class DictField(Base):
+    """Configurator for C{App.form.DictField}."""
+    xtype = 'xdictfield'
+
+
 class DirectStore(Base):
     """Configurator for C{App.data.DirectStore}."""
     xtype = 'xdirectstore'
diff --git a/static/plugin_dbui/src/dictfield.js b/static/plugin_dbui/src/dictfield.js
new file mode 100644
index 00000000..4a02b1a2
--- /dev/null
+++ b/static/plugin_dbui/src/dictfield.js
@@ -0,0 +1,135 @@
+/**
+ * The DictField is an Ext.grid.PropertyGrid with which is ran as a form.Field.
+ *
+ * NOTE: see Ect.form.SliderField a good example to understand
+ * how to build a custom field.
+ *
+ * The type of this component is xdictfield.
+ *
+ * @extends Ext.form.Field
+ * @version
+ *
+ */
+Ext.namespace('App.form');
+
+/**
+ * Extend the predefined renders of the Ext.grid.column by adding jsoncolumn.
+ * It render properly json object in the grid and well adapted for a DictField.
+ *
+ */
+Ext.grid.JsonColumn = Ext.extend(Ext.grid.Column, {
+
+    constructor: function (cfg) {
+
+        "use strict";
+
+        Ext.grid.JsonColumn.superclass.constructor.call(this, cfg);
+        this.renderer = JSON.stringify;
+    }
+});
+
+Ext.grid.Column.types.jsoncolumn = Ext.grid.JsonColumn;
+
+/**
+ * @class App.form.DictField
+ *
+ */
+App.form.DictField = Ext.extend(Ext.form.Field, {
+
+    /**
+     * default configuration of the dictionary
+     */
+    dictCfg: {},
+
+    /**
+     * private attributes
+     */
+    grid: null,
+    store: null,
+
+    /**
+     * private method requests by the component model of ExtJS
+     */
+    initComponent: function () {
+
+        "use strict";
+
+        // create the property grid
+        this.grid = new Ext.grid.PropertyGrid({
+            autoHeight: true,
+            hideHeaders: true,
+            source: Ext.apply({}, this.dictCfg),
+            viewConfig : {
+                forceFit: true,
+                scrollOffset: 2 // the grid will never have scrollbars
+            }
+        });
+
+        // inhibit the contextmenu in the PropertyGrid
+        this.grid.on('contextmenu', function (e) {e.stopEvent(); });
+
+        // instantiate the underlying class
+        App.form.DictField.superclass.initComponent.call(this);
+    },
+
+    /**
+     * private method requests by the component model of ExtJS
+     */
+    beforeDestroy: function () {
+
+        "use strict";
+
+        App.form.DictField.superclass.beforeDestroy.call(this);
+    },
+
+    /**
+     * Return the value of this field
+     */
+    getValue: function () {
+
+        "use strict";
+        return Ext.apply({}, this.grid.getSource());
+    },
+
+    /**
+     * private
+     * supersede the base class method to render the grid
+     *
+     * @param {Ext.Element} ct The container to render to.
+     * @param {Object} position The position in the container to render to.
+     */
+    onRender: function (ct, position) {
+
+        "use strict";
+
+        // hide the default input field
+        this.autoCreate = {
+            id: this.id,
+            name: this.name,
+            type: 'hidden',
+            tag: 'input'
+        };
+
+        App.form.DictField.superclass.onRender.call(this, ct, position);
+
+        this.wrap = this.el.wrap({cls: 'x-form-field'});
+        this.resizeEl = this.positionEl = this.wrap;
+        this.grid.render(this.wrap);
+    },
+
+    /**
+     * Set the value for this field
+     * @param {Object} value
+     */
+    setValue: function (value) {
+
+        "use strict";
+
+        var source  = Ext.apply({}, value || this.dictCfg);
+        this.grid.setSource(source);
+
+        return App.form.DictField.superclass.setValue.call(this, source);
+    }
+});
+
+Ext.reg('xdictfield', App.form.DictField);
\ No newline at end of file
-- 
GitLab