Commit 33f19de0 authored by LE GAC Renaud's avatar LE GAC Renaud
Browse files

Merge branch '15-extjs-6' into 'master'

15 extjs 6

* Migrate to ExtJS 6.0.1
* Require a version of `plugin_dbui` 0.9.3 of higher
* Close #15 

See merge request !19
parents 7fb84320 86c5de4d
......@@ -125,4 +125,4 @@ def grid():
title = do_title(config, selector)
# delegate the grid rendering to the view
return dict(cfg_store=json.dumps(store), grid=grid, title=title)
return dict(cfg_store=store, grid=grid, title=title)
......@@ -59,6 +59,7 @@ All columns
``columnWidth`` or ``flex``
the column width can be specified as a number or a percentage.
The first syntax is the preferred one when running with ``ExtJS 6``.
``dataIndex``
the index of the column used by the ``DataFrame``, the ``Ext.data.store``
......@@ -85,7 +86,20 @@ All columns
``renderer``
A string pointing to the javascript function rendering the value or
containing the function itself. The possible value is ``reprDuration``.
containing the function itself. The possible value is ``reprDuration`` or
``(function(value) {Ext.util.Format.number(value, '0.0')})``. In the last
syntax, the parenthesis at the beginning and at the end are mandatory.
``summaryRenderer``
A string containing the function to render the summary value, *e.g*
``(function(value) {Ext.util.Format.number(value, '0.0')})``. The
parenthesis at the beginning and at the end are mandatory.
See the documentation for more complex strategy.
``summaryType``
Summary are computed over the column values.
Possible functions are ``"count"``, ``"sum"``, ``"min"``, ``"max"`` and
``"average"``.
``text``
the string containing the label of the column.
......@@ -121,11 +135,6 @@ Number column
``format``
the format string, *e.g.* ``"0,000.00"``.
``summaryType``
Summary are computed over the column values.
Possible functions are ``"count"``, ``"sum"``, ``"min"``, ``"max"`` and
``"average"``.
Row numbered column
-------------------
......
......@@ -16,7 +16,7 @@ db.define_table("history",
Field("start_date", "date", default=today, notnull=True),
Field("end_date", "date"),
Field("note", "text"),
Field("data", "json"),
Field("data", "json", default=dict()),
migrate="history.table")
# NOTE: we have to remove the json validator to work !!
......
......@@ -9,11 +9,13 @@
#-------------------------------------------------------------------------------
fieldsModifier = dbui.FieldsModifier('history')
fieldsModifier.configure_field('id_events', itemId='keyid_events')
fieldsModifier.configure_field('data',
comboBoxItemId='keyid_events',
dictField='EventsData',
dictFieldData='EventsData',
dictFieldEvent='EventsEvent',
dictFieldIdEvent='EventsId',
formFieldIdEvent='HistoryId_events',
store='eventsStore',
trUndefined=undef,
xtype='xuserdatamultidefault')
fieldsModifier.configure_field('start_date', flex=1)
......
......@@ -688,9 +688,9 @@ class List(BaseReport):
"""Build the configuration for the ``Dbui.grid.Panel``.
Returns:
plugin_dbui.Grid:
the configuration of the ``Dbui.grid.Panel``.
The properties columns and features are JSON encoded.
gluon.storage.Storage:
the keys are ``columns`` and ``features`` and the
corresponding values are list of dictionary.
"""
config = self.config
......@@ -707,14 +707,7 @@ class List(BaseReport):
grid.columns.append(cfg)
# features from the configuration
grid.features = config.features
# JSON encoding
# reference to javascript function or the function itself
# are surrounded by double quoted. removed them.
grid.columns = json.dumps(grid.columns)
grid.columns = \
re.sub('("renderer": *)(")(\w+)(")', r'\1\3', grid.columns)
grid.features = json.loads(config.features)
return grid
......
......@@ -4,15 +4,30 @@
*/
// Activate the dynamic loading for Ext JS and application classes
Dbui.setDynamicLoading(Dbui.debug);
if (Dbui.debug) {
Ext.Loader.setConfig({
disableCaching: false,
enabled: true,
paths: {
'Dbui': '/' + Dbui.name + '/static/plugin_dbui/src',
'Ext': '/' + Dbui.name + '/static/plugin_extjs/src',
'TrackEvents': '/' + Dbui.name + '/static/track_events/src'
}
});
}
Ext.require('Dbui.container.Viewport');
Ext.require('Dbui.plugin.MathJax');
Ext.require('Ext.direct.Manager');
Ext.require('Ext.direct.RemotingProvider');
Ext.require('Ext.EventManager');
Ext.require('Ext.tip.QuickTipManager');
Ext.require('TrackEvents.form.field.UserData');
Ext.require('TrackEvents.form.field.UserDataMultiDefault');
Ext.require('TrackEvents.form.plugin.UserDataConsistency');
Ext.onReady(function(){
"use strict";
......@@ -31,7 +46,8 @@ Ext.onReady(function(){
// Action trigger when the page is refreshed or when the tab is closed.
// NOTE: It should work when the browser is closed to.
// Not working with FireFox 17.0.8, web2py 2.4.5 and Ext JS 3.4.0.
Ext.EventManager.on(window, 'beforeunload', Dbui.closeUserSession);
// nor with Firefox 38, web2py 2.14.6, Ext JS 6.0.1
Ext.getWin().on('beforeunload', Dbui.closeUserSession);
// Instantiate the application
app = Ext.create('Dbui.container.Viewport', Dbui.config.viewport);
......
// require to load the base class when running in debug mode
Ext.Loader.setPath('Dbui', '../static/plugin_dbui/src');
/**
* A dictionary handling several user data block definition.
* It is linked to a ComboBox allowing to select the event and
* its user data block.
* It is linked to a ComboBox and to a store. The ComboBox allows to select
* the event while the definition of its data block is found in the store.
*
* The user data block is rendered as a Ext.grid.property.Grid.
* It is seen as a series of properties with their values.
......@@ -17,9 +14,75 @@ Ext.Loader.setPath('Dbui', '../static/plugin_dbui/src');
*/
Ext.define('TrackEvents.form.field.UserDataMultiDefault', {
extend: 'Dbui.form.field.DictMultiDefault',
extend: 'Dbui.form.field.Dict',
alias: 'widget.xuserdatamultidefault',
/**
* @cfg {Ext.data.Store/String} store (required)
* the store contains the definition of the event, namely a key
* and the configuration of the data block
*
*/
/**
* @cfg {String} formFieldIdEvent (required)
* The form containing this widget is loaded with a record.
* It contains the key associated to the select event.
* The formFieldEvent is the field containing this key.
* This also the name of the ComboBox field in the form.
*/
/**
* @cfg {String} dictFieldData (required)
* The field in the store which contains the data block configuration.
*/
/**
* @cfg {String} dictFieldEvent (required)
* The field in the store whihch contains the event name.
*/
/**
* @cfg {String} dictFieldIdEvent (required)
* The field in the store whihch contains the event key.
*/
/**
* @cfg {String} trUndefined (required)
* transaltion of the keyword "undefined" used in the database
*/
// private attributes
combo: null,
lastKey: undefined,
lastEvent: undefined,
// jshint strict: false
// private method requests by the component model of ExtJS
initComponent: function () {
var me = this;
// instantiate the base class
me.callParent(arguments);
// instantiate the store containing the relation
// between the key and the data block configuration
me.store = Dbui.getStore(me.store);
},
// private method requests by the component model of ExtJS
beforeDestroy: function () {
var me = this;
me.combo.un('select', me.onComboBoxSelect, me);
me.callParent(arguments);
},
// jshint strict: true
//
// build the source configuration for the Ext.grid.property.Grid.
// It defined the type for each property and the editor widget.
......@@ -40,7 +103,7 @@ Ext.define('TrackEvents.form.field.UserDataMultiDefault', {
"use strict";
var me = this,
model = record.get(me.dictField) || null,
model = record.get(me.dictFieldData) || null,
defaultValues = {},
sourceConfig = {};
......@@ -142,14 +205,13 @@ Ext.define('TrackEvents.form.field.UserDataMultiDefault', {
// supersede the base class method to deal with the data type
//
// @param {Ext.form.field.ComboBox} combo
// @param {Ext.data.Model[]} records
// @param {Ext.data.Model} record
//
//
onComboBoxSelect: function (combo, records) {
onComboBoxSelect: function (combo, record) {
"use strict";
var me = this,
record = records.length ? records[0] : null,
cfg;
cfg = me.buildSourceConfig(record);
......@@ -166,8 +228,9 @@ Ext.define('TrackEvents.form.field.UserDataMultiDefault', {
},
//
// supersede the base class method in order to deal with the
// data type of the user data block
// Load the value in the field.
// It is usualy call when the form is loaded.
// Reference key and data block are determined.
//
setValue: function (value) {
......@@ -175,35 +238,68 @@ Ext.define('TrackEvents.form.field.UserDataMultiDefault', {
var me = this,
combo = me.combo || null,
cfg,
record;
previousEvent = me.lastEvent,
undef = me.trUndefined,
cfg, form, formRecord, rawValue, record;
// link me to the ComboBox
if (value && !combo) {
if (!combo) {
combo = me.up('xform, form').down('#' + me.comboBoxItemId);
}
// The comboBox field is identidied by its name, e.g HistororyId_events
if (!combo) {
form = me.up('xform, form').getForm();
me.combo = form.findField(me.formFieldIdEvent);
me.combo.on('select', me.onComboBoxSelect, me);
}
// keep track of the value load in the ComboBox
// replace empty string by empty dictionary
if (combo) {
me.lastKey = combo.getValue();
// protection
// in the initialization phase rawValue is an empty string
rawValue = me.combo.getRawValue();
if (rawValue.length === 0) {
return;
}
if (!value) {
value = {};
}
formRecord = me.up('xform, form').getForm().getRecord();
// rather complicated logic to find the correct grid configuration
//
// NOTE ExtJS 6
// The ComboBox behave strangely in that configuration
// The method ComboBox.getValue() always return 1
// The property value and rawValue refer to was have been select previously
// Therefore a complexe logic is required to solve all case
//
if ((previousEvent === undef && rawValue === undef) || formRecord === undefined) {
me.lastKey = 1;
value = {};
} else if (previousEvent !== undefined && rawValue === undef) {
me.lastKey = 1;
value = {};
} else if (previousEvent === rawValue) {
me.lastKey = formRecord.get(me.formFieldIdEvent);
} else if (previousEvent === undefined && formRecord !== null) {
me.lastKey = formRecord.get(me.formFieldIdEvent);
// get the configuration for the Ext.grid.property.Grid
record = combo.findRecordByValue(combo.getValue());
cfg = me.buildSourceConfig(record);
} else if (previousEvent !== undefined && rawValue !== undefined) {
me.lastKey = formRecord.get(me.formFieldIdEvent);
// fill the property grid
// NOTE: let the editor take care of the data type conversion
// when the user data block definition change
me.grid.setSource(value, cfg.sourceConfig);
} else {
me.lastKey = 1;
value = {};
}
record = me.store.findRecord(me.dictFieldIdEvent, me.lastKey);
me.lastEvent = record.get(me.dictFieldEvent);
// build the configuration of the Ext.grid.property.Grid
cfg = me.buildSourceConfig(record);
// fill the property grid
// NOTE: let the editor take care of the data type conversion
// when the user data block definition change
me.grid.setSource(value, cfg.sourceConfig);
// the standard way to set the value for a field
// extract from Ext.form.field.Base
return me.mixins.field.setValue.call(me, value);
......
/**
* Convert a duration in nanosecond into a string.
*
* @param {Number} value
* The duration in seconds
*
* @return {String}
* The duration express as the number of year + the number of months e.g. "3y + 09m"
*
*/
function reprDuration(value) {
"use strict";
var duration = value,
month,
nd,
nm,
ny,
rep,
year;
// duration of year and month in seconds
year = 365 * 24 * 3600;
month = year / 12;
// number of years and months
ny = Math.floor(duration / year);
nm = Math.floor((duration - ny * year) / month);
// format the number of month
nm = Ext.String.leftPad(nm.toString(), 2, '0');
// format the response
if (ny > 0) {
rep = Ext.String.format("{0}y + {1}m", ny, nm);
} else if (nm > 0) {
rep = Ext.String.format("{0}m", nm);
} else {
nd = duration / (24 * 3600);
rep = Ext.String.leftPad(nd.toString(), 2, '0');
}
return rep;
}
......@@ -2,7 +2,7 @@
#--------------------------------------------------------------------------
#
# The python controller return the variables:
# - cfg_store (string)
# - cfg_store (Storage)
# - grid (Storage)
# - title (string)
#
......@@ -23,32 +23,109 @@
response.write(DIV(_id=divgrid))
#
# Export python variables to the javascript
# Convert store and grid configuration as string
#
jsvars = ["cfgStore = %s" % cfg_store,
"columns = %s" % grid.columns,
"divgrid = '%s'" % divgrid,
"features = %s" % grid.features]
jsvars = " var %s;" % ',\n'.join(jsvars)
response.write(SCRIPT(jsvars), escape=False)
cfg_grid = json.dumps(grid)
cfg_store = json.dumps(cfg_store)
}}
<script type="text/javascript">
var grid;
/**
* Helper function to convert a duration in nanosecond into a string.
*
* @param {Number} value
* The duration in seconds
*
* @return {String}
* The duration express as the number of year + the number of months
* e.g. "3y + 09m"
*
*/
function reprDuration(value) {
"use strict";
var duration = value,
month, nd, nm, ny, rep, year;
// duration of year and month in seconds
year = 365 * 24 * 3600;
month = year / 12;
// number of years and months
ny = Math.floor(duration / year);
nm = Math.floor((duration - ny * year) / month);
// format the number of month
nm = Ext.String.leftPad(nm.toString(), 2, '0');
// format the response
if (ny > 0) {
rep = Ext.String.format("{0}y + {1}m", ny, nm);
} else if (nm > 0) {
rep = Ext.String.format("{0}m", nm);
} else {
nd = duration / (24 * 3600);
rep = Ext.String.leftPad(nd.toString(), 2, '0');
}
return rep;
}
var cfgGrid = {{=XML(cfg_grid)}},
cfgStore = {{=XML(cfg_store)}},
columns = cfgGrid.columns,
features = cfgGrid.features,
divgrid = '{{=divgrid}}',
nColumns = columns.length,
i, grid;
// backward compatibility
// Ext JS 4.2.1 the width of the column is defined using the property flex
// Ext JS 6.0.1 this cause crash of the grid when a hidden column is activated
// The workaround is to destroy the flex property and to use the
// columnWidth instead. the latter is a percentage ranging from 0 to 1.
//
for (i = 0; i < nColumns; i += 1) {
column = columns[i];
if (column.flex !== undefined && column.columnWidth === undefined) {
if (column.flex <= 1) {
column.columnWidth = column.flex * 0.1;
} else {
column.columnWidth = 1;
}
}
delete column.flex;
}
// convert renderer / summaryRenderer properties
// typicaly a string to a function
//
for (i = 0; i < nColumns; i += 1) {
if (columns[i].renderer !== undefined) {
columns[i].renderer = eval(columns[i].renderer);
}
if (columns[i].summaryRenderer !== undefined) {
columns[i].summaryRenderer = eval(columns[i].summaryRenderer);
}
}
// instantiate the grid
//
grid = Ext.create('Dbui.grid.Panel', {
cfgGrid = Ext.apply(cfgGrid, {
plugins: ['pGridExport', 'pMathJax'],
store: cfgStore,
columns: columns,
features: features,
forceFit: true,
padding: "10 40 20 30",
renderTo: divgrid
});
Ext.create('Dbui.grid.Panel', cfgGrid);
</script>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment