Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Add support for filtering. #10

Closed
wants to merge 24 commits into from

1 participant

@jone
Owner

Usage: Install the grid-filter GS profile of collective.js.extjs and
define the filter configuration with the key filter in your
column definition.

Requires: 4teamwork/collective.js.extjs#1

jone added some commits
@jone jone Add support for filtering.
Install the ``grid-filter`` GS profile of ``collective.js.extjs`` and
define the filter configuration with the key ``filter`` in your
column definition.
8e7944a
@jone jone Ext / JSON: do not reconfigure metadata (column definitions) when not…
… needed.

A new "omit_metadata" request variable removes the metadata from the response.
This makes the client side faster and filters are not removed on load.
f61b66e
@jone jone Refactor static HTML to also work when not sending metadata (column d…
…efinitions).
a052604
@jone jone Fix tabbed view check. 9da893d
@jone jone Add a seperate goto_page table function.
This allows us to change the page in ExtJS withou recreating the table.
f222100
@jone jone ExtJS: show spinner on every request. 664475b
@jone jone Support for "groupable" key in column definitions (dict). d83bfb3
@jone jone linked helper: allow href customization. d7c6f6a
@jone jone Ignore eggs directory. f5f32d3
@jone jone ExtJS: Fix header width, so that last column is resizable with more t…
…han 7 columns.
402946d
@jone jone Fix grid header width on every change. 2af2887
@jone jone Fix grid header width initially. 70c89f2
@jone jone ExtJS: when pressing enter, open ".default-link" links in new tabs. 9539508
@jone jone Fix setting omit_metadata on grouping. e482f25
@jone jone Pass grid state profile name to tabbedview. e2ac668
@jone jone ExtJS: change grouping implementation, so that the complete tab is re…
…loaded

  when toggling grouping. Also prevent storing the state while grouping.
  This fixes issues when using grouping and filtering in combination.
837082e
@jone jone Grouping fixes: reload when disabling grouping. fc7898b
@jone jone ExtJS: when reloading browser page, go to the same page profile. b56638e
@jone jone Merge branch 'master' into jone-filtering
Conflicts:
	docs/HISTORY.txt
6c95076
@jone jone Fix filter condition so that ExtJS works without filters installed. db4944b
@jone jone ExtJS: cleanup draggable JS. 0cdd788
@jone jone Add cached_field_value helper for getting a field value of an object …
…with proper caching.
349c084
@jone jone Add raw-support to cached_field_value. 91ef5ae
@jone jone Refactor cached_field_value converter to match normal helpers. 0e7451f
@jone
Owner

I'm gonna cleanup the branch, re-base it on master and open a new pull-request..

@jone jone closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 6, 2012
  1. @jone

    Add support for filtering.

    jone authored
    Install the ``grid-filter`` GS profile of ``collective.js.extjs`` and
    define the filter configuration with the key ``filter`` in your
    column definition.
  2. @jone

    Ext / JSON: do not reconfigure metadata (column definitions) when not…

    jone authored
    … needed.
    
    A new "omit_metadata" request variable removes the metadata from the response.
    This makes the client side faster and filters are not removed on load.
  3. @jone
  4. @jone

    Fix tabbed view check.

    jone authored
  5. @jone

    Add a seperate goto_page table function.

    jone authored
    This allows us to change the page in ExtJS withou recreating the table.
  6. @jone
  7. @jone
Commits on Dec 8, 2012
  1. @jone
  2. @jone

    Ignore eggs directory.

    jone authored
Commits on Dec 10, 2012
  1. @jone
  2. @jone
  3. @jone

    Fix grid header width initially.

    jone authored
Commits on Dec 13, 2012
  1. @jone
Commits on Dec 21, 2012
  1. @jone
Commits on Jan 11, 2013
  1. @jone
Commits on Jan 12, 2013
  1. @jone

    ExtJS: change grouping implementation, so that the complete tab is re…

    jone authored
    …loaded
    
      when toggling grouping. Also prevent storing the state while grouping.
      This fixes issues when using grouping and filtering in combination.
  2. @jone
  3. @jone
  4. @jone

    Merge branch 'master' into jone-filtering

    jone authored
    Conflicts:
    	docs/HISTORY.txt
  5. @jone
Commits on Jan 13, 2013
  1. @jone

    ExtJS: cleanup draggable JS.

    jone authored
Commits on Jan 15, 2013
  1. @jone
Commits on Jan 16, 2013
  1. @jone
Commits on Jan 27, 2013
  1. @jone
This page is out of date. Refresh to see the latest.
View
3  .gitignore
@@ -11,4 +11,5 @@ develop-eggs/
dist/
parts/
src/
-var/
+var/
+eggs/
View
3  README.rst
@@ -99,8 +99,7 @@ Examples:
>>> columns = ['id', 'name', 'dates']
>>> print generator.generate(data, columns, output='json')
- {"totalCount": 2, "rows": [{"dates": "305-285 BC", "id": 1, "name": "Ptolemy I Soter"}, {"dates": "288-246 BC", "id": 2, "name": "Ptolemy II Philadelphos"}], "metaData": {"fields": [{"type": "string", "name": "id"}, {"type": "string", "name": "name"}, {"type": "string", "name": "dates"}], "translations": {"dragDropLocked": "dragDropLocked", "sortDescText": "sortDescText", "columnsText": "columnsText", "showGroupsText": "showGroupsText", "groupByText": "groupByText", "itemsSingular": "itemsSingular", "sortAscText": "sortAscText", "selectedRowen": "selectedRowen", "itemsPlural": "itemsPlural"}, "totalProperty": "totalCount", "root": "rows", "config": {"sort": null, "dir": "ASC", "gridstate": null}, "columns": [{"header": "id", "hidden": false, "sortable": true, "id": "id", "dataIndex": "id"}, {"header": "name", "hidden": false, "sortable": true, "id": "name", "dataIndex": "name"}, {"header": "dates", "hidden": false, "sortable": true, "id": "dates", "dataIndex": "dates"}]}}
-
+ {"totalCount": 2, "rows": [{"dates": "305-285 BC", "id": 1, "name": "Ptolemy I Soter"}, {"dates": "288-246 BC", "id": 2, "name": "Ptolemy II Philadelphos"}], "metaData": {"fields": [{"type": "string", "name": "id"}, {"type": "string", "name": "name"}, {"type": "string", "name": "dates"}], "translations": {"dragDropLocked": "dragDropLocked", "sortDescText": "sortDescText", "columnsText": "columnsText", "showGroupsText": "showGroupsText", "groupByText": "groupByText", "itemsSingular": "itemsSingular", "sortAscText": "sortAscText", "selectedRowen": "selectedRowen", "itemsPlural": "itemsPlural"}, "totalProperty": "totalCount", "root": "rows", "config": {"sort": null, "dir": "ASC", "gridstate": null}, "columns": [{"sortable": true, "groupable": true, "filter": null, "header": "id", "dataIndex": "id", "hidden": false, "id": "id"}, {"sortable": true, "groupable": true, "filter": null, "header": "name", "dataIndex": "name", "hidden": false, "id": "name"}, {"sortable": true, "groupable": true, "filter": null, "header": "dates", "dataIndex": "dates", "hidden": false, "id": "dates"}]}}
**Defining columns**
View
28 docs/HISTORY.txt
@@ -5,6 +5,34 @@ Changelog
1.8.3 (unreleased)
------------------
+- Add cached_field_value helper for getting a field value of an object
+ with proper caching.
+ [jone]
+
+- ExtJS: change grouping implementation, so that the complete tab is reloaded
+ when toggling grouping. Also prevent storing the state while grouping.
+ This fixes issues when using grouping and filtering in combination.
+ [jone]
+
+- ExtJS: when pressing enter, open ".default-link" links in new tabs.
+ [jone]
+
+- Support for "groupable" key in column definitions (dict).
+ [jone]
+
+- ExtJS: show spinner on every request.
+ [jone]
+
+- Add a seperate goto_page table function, so that we can
+ change the page in ExtJS withou recreating the table.
+ [jone]
+
+- Add support for filtering.
+ Install the ``grid-filter`` GS profile of ``collective.js.extjs`` and
+ define the filter configuration with the key ``filter`` in your
+ column definition.
+ [jone]
+
- Fix "linked" helper, pass href as attribute.
[mathias.leimgruber]
View
312 ftw/table/browser/ftwtable.extjs.js
@@ -3,36 +3,44 @@ Ext.Ajax.timeout = 120000; // 2 minutes
Ext.grid.FTWTableGroupingView = Ext.extend(Ext.grid.GroupingView, {
// private
onGroupByClick : function(){
- this.grid.store.baseParams['groupBy'] = this.cm.getDataIndex(this.hdCtxIndex);
- this.enableGrouping = true;
-
- // if we have a tabbedview, we need to tell it that we
- // are not grouping anymore
- if(typeof(tabbedview) != "undefined") {
- tabbedview.param('groupBy', store.baseParams['groupBy']);
- }
+ if(store.baseParams['groupBy'] && this.grid.store.sortInfo.field == 'draggable') {
+ // we are grouping and sorting by draggable - do not allow this.
+ // let's just sort by the groupBy-column
+ this.grid.store.sort(store.baseParams['groupBy'], 'ASC');
+ }
- if(store.baseParams['groupBy'] && this.grid.store.sortInfo.field == 'draggable') {
- // we are grouping and sorting by draggable - do not allow this.
- // let's just sort by the groupBy-column
- this.grid.store.sort(store.baseParams['groupBy'], 'ASC');
- }
+ if(typeof(tabbedview) != "undefined") {
+ tabbedview.param('groupBy', this.cm.getDataIndex(this.hdCtxIndex));
+ tabbedview.reload_view();
+ } else {
+ Ext.grid.GroupingView.superclass.onGroupByClick.call(this);
+ }
+ },
- this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups
- this.refresh();
- this.grid.store.reload();
- if(typeof(tabbedview) != "undefined") {
- tabbedview.show_spinner();
- }
+ // private
+ onShowGroupsClick : function(mi, checked){
+ if (typeof(tabbedview) != "undefined") {
+ this.enableGrouping = checked;
+ if (checked) {
+ this.onGroupByClick();
+ } else {
+ tabbedview.reload_page_keeping_profile();
+ }
+ } else {
+ Ext.grid.GroupingView.superclass.onShowGroupsClick.call(this, mi, checked);
+ }
},
+
// private
onColumnWidthUpdated : function(col, w, tw){
Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);
this.updateGroupWidths();
+
//set width of the header div to the same value as the table
//we need a few extra pixel to make the resizable handle draggable
var inner_width = $('.x-grid3-header table').width() + 5;
$('.x-grid3-header').width(inner_width);
+ $('.x-grid3-header-offset').width(inner_width);
}
});
@@ -43,10 +51,11 @@ function reset_grid_state() {
type: "POST",
data: {
gridstate: "{}",
- view_name: stateName()
+ view_name: stateName(),
+ "grid-state-profile": tabbedview.param('grid-state-profile')
},
success: function() {
- location.reload();
+ tabbedview.reload_page_keeping_profile();
}
});
}
@@ -60,6 +69,19 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
// private
set : function(name, value){
Ext.state.FTWPersistentProvider.superclass.set.call(this, name, value);
+
+ if(store.reader.meta.config.group != undefined ||
+ grid.colModel.getColumnById('groupBy') != undefined){
+ // store nothing on the server while grouping.
+ return;
+ }
+
+ //set width of the header div to the same value as the table
+ //we need a few extra pixel to make the resizable handle draggable
+ var inner_width = $('.x-grid3-header table').width() + 5;
+ $('.x-grid3-header').width(inner_width);
+ $('.x-grid3-header-offset').width(inner_width);
+
$.ajax({
url: '@@tabbed_view/setgridstate',
cache: false,
@@ -67,7 +89,8 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
data: {
// XXX does JSON.stringify work always?
gridstate: JSON.stringify(this.state[name]),
- view_name: stateName()
+ view_name: stateName(),
+ "grid-state-profile": tabbedview.param('grid-state-profile')
}
});
},
@@ -113,7 +136,8 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
baseParams: {
ext: 'json',
tableType: 'extjs', // lets the server know that this is a request from EXTJS ...
- mode: 'json' // ... and that we want JSON data to be returned
+ mode: 'json', // ... and that we want JSON data to be returned
+ omit_metadata: '0' // when 1, keep column defitions
},
proxy: new Ext.data.HttpProxy({
@@ -129,6 +153,20 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
listeners: {
+ beforeload: function(store, options) {
+ Ext.state.Manager.getProvider().state = {};
+ jQuery.tabbedview.show_spinner();
+ },
+
+ datachanged: function(store) {
+ if (typeof(store.reader.jsonData.static_html) != 'undefined'){
+ $.each(store.reader.jsonData.static_html, function(key, value) {
+ $('#'+key+'_container.ftwtable').html(value);
+ });
+ }
+ jQuery.tabbedview.hide_spinner();
+ },
+
// will be called if we get new metadata from the server. E.g. diffrent columns.
metachange : function(store, meta){
if(store.reader.meta.config.group != undefined){
@@ -136,7 +174,7 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
}
// On metadachange we have to create a new grid. Therefore destroy the old one
- if (grid){
+ if (grid && store.reader.meta.config.group == undefined){
// if the grid exists, let the state provider store
// our config
Ext.state.Manager.set(stateName(), grid.getState());
@@ -188,19 +226,7 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
}
});
- grid = new Ext.grid.GridPanel({
- //set up the GridPanel
- columnLines: true,
- store: store,
- cm: cm,
- stripeRows: true,
- autoHeight:true,
- stateful: true,
- stateId: stateName(),
- xtype: "grid",
-
- //XXX: GridDragDropRowOrder has to be the first plugin!
- plugins: [new Ext.ux.dd.GridDragDropRowOrder({
+ var grid_plugins = new Array(new Ext.ux.dd.GridDragDropRowOrder({
copy: false, // false by default
scrollable: true, // enable scrolling support (default is false)
targetCfg: {}, // any properties to apply to the actual DropTarget
@@ -221,7 +247,41 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
});
}
}
- })],
+ }));
+
+ if (Ext.ux.grid && typeof(Ext.ux.grid.GridFilters) != 'undefined') {
+ var filtered_columns = function() {
+ var cols = new Array();
+ for(var i=0; i<columns.length; i++) {
+ var col = columns[i];
+ if (col.filter) {
+ var filter = col.filter;
+ filter['dataIndex'] = col.dataIndex;
+ cols.push(filter);
+ }
+ }
+ return cols;
+ }();
+
+ grid_plugins.push(new Ext.ux.grid.GridFilters({
+ local: false,
+ filters: filtered_columns
+ }));
+ }
+
+ grid = new Ext.grid.GridPanel({
+ //set up the GridPanel
+ columnLines: true,
+ store: store,
+ cm: cm,
+ stripeRows: true,
+ autoHeight:true,
+ stateful: true,
+ stateId: stateName(),
+ xtype: "grid",
+
+ //XXX: GridDragDropRowOrder has to be the first plugin!
+ plugins: grid_plugins,
view: new Ext.grid.FTWTableGroupingView({
groupMode:'value',
@@ -241,31 +301,6 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
sm: sm,
listeners: {
- groupchange: function(grid, state) {
- if(!state) {
- // hide the groupBy column - which was just enabled
- // because grouping was disabled
- var groupByCol = grid.grid.colModel.getIndexById('groupBy');
- if(groupByCol !== -1) {
- grid.grid.colModel.setHidden(groupByCol, true);
- }
-
- // reload the store - this removes grouping and
- // reenables batching etc.
- store.baseParams['groupBy'] = '';
- store.reload();
- if(typeof(tabbedview) != "undefined") {
- tabbedview.show_spinner();
- }
- }
-
- // if we have a tabbedview, we need to tell it that we
- // are not grouping anymore
- if(typeof(tabbedview) != "undefined") {
- tabbedview.param('groupBy', store.baseParams['groupBy']);
- }
- },
-
beforerender: function(grid) {
// When the state is loaded, somehow the columns
// marked as hidden are not set to hidden
@@ -302,10 +337,6 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
//ugly hacks we need to use horizontal scrolling combined with autoHeight
//enable horizontal scrolling
$('.x-grid3-viewport').css('overflow', 'auto');
- //set width of the header div to the same value as the table
- //we need a few extra pixel to make the resizable handle draggable
- var inner_width = $('.x-grid3-header table').width() + 5;
- $('.x-grid3-header').width(inner_width);
// Checkboxes / radios are usually have the
// "selectable" css class. When using a extjs
@@ -340,64 +371,66 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
},
afterrender: function(panel){
- //drag 'n' drop reordering is only available if sort field is 'draggable'
- if(store.sortInfo.field == 'draggable'){
- unlockDragDrop();
- }else{
- lockDragDrop();
- }
+ this._update_dragndrop_state();
+ options.onLoad();
+ store.baseParams['omit_metadata'] = '1';
+ $this.trigger('gridRendered');
+ },
- /* meta.static contains plain html that we inject into the DOM using key+'_container' as selector.
- E.G.:
- "static":{
- "batching":"<!-- Navigation -->"
- [...]
- },
- $('#batching_container.ftwtable') will be replaced with "<!-- Navigation -->"
- */
- if(store.reader.meta['static'] != undefined){
- $.each(store.reader.meta['static'], function(key, value) {
- $('#'+key+'_container.ftwtable').html(value);
- });
+ sortchange: function(panel, sortInfo) {
+ this._update_dragndrop_state();
+ }
+ },
+
+ _update_dragndrop_state: function() {
+ if(!this.viewReady) {
+ return;
}
- options.onLoad();
-
- // We shouldn't be able to group and sort by "draggable" at the
- // same time.
- // So we need to disable sorting by "draggable" when
- // grouping is enabled, and enable it when grouping is disabled.
- var draggableCol = grid.colModel.getColumnById('draggable');
- if(draggableCol) {
- if(store.baseParams['groupBy']) {
- // grouping is enabled
- draggableCol.sortable = false;
- } else {
- // grouping is disabled
- draggableCol.sortable = true;
- }
+ // Dragndrop should only be available when sorting by 'draggable'.
+ var plugin = this._get_dragndrop_plugin();
+ if(!plugin) {
+ return;
}
- $this.trigger('gridRendered');
- },
- sortchange: function(panel, sortInfo) {
- // disable sorting on column "draggable" when sorting
- // by this column. This disables reversing the sort
- // order of "draggable", because it does not make
- // sense (since it's objectPositionInParent)
var col = grid.colModel.getColumnById('draggable');
- if(col) {
- if(sortInfo.field == 'draggable' && sortInfo.direction == 'ASC') {
+ if(!col) {
+ return;
+ }
+
+ if(store.sortInfo.field == 'draggable'){
+ // enable grouping
+ plugin.target.unlock();
+ grid.ddText = translate('selectedRowen', '{0} selected rowen{1}');
+ $this.removeClass('draglocked');
+
+ // do not allow to change the sort direction when sorting by 'draggable'
+ if(store.sortInfo.direction == 'DESC') {
+ store.sort(sortInfo.field, 'ASC');
+ }
col.sortable = false;
- } else if(sortInfo.field == 'draggable' && sortInfo.direction == 'DESC') {
- // not very nice: force to sort ascending, when
- // sorting on "draggable". Descending does not
- // make any sense..
- store.sort(sortInfo.field, 'ASC');
- } else {
+ }
+
+ else {
+ // desable grouping
+ plugin.target.lock();
+ grid.ddText = translate('dragDropLocked', "Drag 'n' Drop not possible");
+ $this.addClass('draglocked');
col.sortable = true;
- }
}
- }
+ },
+
+ _get_dragndrop_plugin: function() {
+ if (!Ext.ux || typeof(Ext.ux.dd.GridDragDropRowOrder) == 'undefined') {
+ return null;
+ }
+
+ for(var i=0; i<this.plugins.length; i++) {
+ if (this.plugins[i] instanceof Ext.ux.dd.GridDragDropRowOrder) {
+ return this.plugins[i];
+ }
+ }
+
+ return null;
}
});
// end grid=
@@ -429,21 +462,6 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
};
- unlockDragDrop = function(){
- //XXX: We assume that [0] is the GridDragDropRowOrder plugin
- grid.plugins[0].target.unlock();
- grid.ddText = translate('selectedRowen', '{0} selected rowen{1}');
- $this.removeClass('draglocked');
- };
-
- lockDragDrop = function(){
- //XXX: We assume that [0] is the GridDragDropRowOrder plugin
- grid.plugins[0].target.lock();
- grid.ddText = translate('dragDropLocked', "Drag 'n' Drop not possible");
- $this.addClass('draglocked');
- };
-
-
translate = function(key, defaultValue){
if(locales[key]){
return locales[key];
@@ -464,11 +482,6 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
};
$.fn.ftwtable.reloadTable = function(table, query, options){
- if(store.reader.meta['static'] != undefined){
- $.each(store.reader.meta['static'], function(key, value) {
- $('#'+key+'_container.ftwtable').html('');
- });
- }
$.fn.ftwtable.destroy();
$.fn.ftwtable.createTable(table, query, options);
};
@@ -485,6 +498,12 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
grid = null;
};
+ $.fn.ftwtable.goto_page = function(pagenumber) {
+ store.baseParams['pagenumber:int'] = pagenumber;
+ jQuery.tabbedview.show_spinner();
+ store.reload();
+ };
+
$.fn.ftwtable.select = function(start, end){
var sm = grid.getSelectionModel();
if (start=='all'){
@@ -506,6 +525,25 @@ Ext.state.FTWPersistentProvider = Ext.extend(Ext.state.Provider, {
sm.deselectRow(start);
}
};
+
+ $(document).keyup(function(event) {
+ /* on ENTER, open all selected issues in a new tab */
+ var src = $(event.srcElement);
+ if(event.keyCode == 13 && !src.is('input') && !src.is('textarea')) {
+ $('.x-grid3-row-selected .default-link').each(function() {
+ if($.browser.webkit === true) {
+ /* open link in new tab by simulating a mouse event with pressed ctrl / cmd keys */
+ var evt = document.createEvent("MouseEvents");
+ evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0,
+ true, false, false, true, 0, null);
+ this.dispatchEvent(evt);
+ } else {
+ window.open($(this).attr('href'), '_blank');
+ }
+ });
+ }
+ });
+
//
// end of closure
//
View
13 ftw/table/browser/jquery.ftwtable.js
@@ -66,6 +66,10 @@
destroy: function(start, end){
$.fn.ftwtable.destroy(start, end);
+ },
+
+ goto_page: function(pagenumber) {
+ $.fn.ftwtable.goto_page(pagenumber);
}
};
@@ -162,14 +166,21 @@
}
$this.load(query, null, function() {
- tabbedview.view_container.trigger('gridRendered');
if(typeof(tabbedview) != "undefined") {
+ tabbedview.view_container.trigger('gridRendered');
tabbedview.hide_spinner();
}
$o.onLoad();
});
};
+ $.fn.ftwtable.goto_page = function(pagenumber) {
+ if(typeof(tabbedview) != "undefined") {
+ tabbedview.param('pagenumber:int', pagenumber);
+ tabbedview.reload_view();
+ }
+ };
+
$.fn.ftwtable.select = function(start, end){
return get_checkbox_range(start, end).attr('checked', true);
};
View
57 ftw/table/helper.py
@@ -1,6 +1,7 @@
+from Products.CMFCore.utils import getToolByName
from datetime import datetime, timedelta
from plone.memoize import ram
-from Products.CMFCore.utils import getToolByName
+from time import time
from zope.app.component.hooks import getSite
from zope.globalrequest import getRequest
from zope.i18n import translate
@@ -251,3 +252,57 @@ def _translate(item, value):
return translate(
value, context=getRequest(), domain=domain)
return _translate
+
+
+def cached_field_value(fieldname, converter=None, cache_time=(60 * 60 * 24),
+ raw=False):
+ """A table helper for displaying a field value an object. It only works
+ when having a catalog source using brains and will get the object only
+ when necessary. The cache is based on the modified date of the object
+ and it's path.
+
+ Usage:
+ This function generates a regular helper on the fly, so it is called in
+ the table / tabbedview column definition.
+ The values are automatically converted to string (we should not cache
+ full-objects).
+ Pass a `converter` function (will get `obj` and `value`) if you need
+ to convert it first.
+
+ Using the `raw` option causes the value to be not converted to string.
+ Be adviced that the value is cached. It is strongly discuraged to put
+ full objects into the cache - thus the `raw` option should only be used
+ when we have no full-objects (only string, int, list, dict, etc).
+ """
+
+ def _cache_key(method, item, _nothing):
+ key = ['ftw.table.helper.cached_field_value',
+ fieldname,
+ item.getPath(),
+ item.modified,
+ time() // cache_time]
+ if converter is not None:
+ key.append(converter.__name__)
+ key.append(converter.__module__)
+ if raw:
+ key.append(raw)
+ return key
+
+ @ram.cache(_cache_key)
+ def _field_value_helper(item, _nothing):
+ obj = item.getObject()
+ field = obj.Schema().getField(fieldname)
+ value = field.get(obj)
+
+ if converter is not None:
+ value = converter(obj, value)
+
+ if not raw and value is None:
+ value = ''
+
+ if not raw and not isinstance(value, (str, unicode)):
+ value = str(value)
+
+ return value
+
+ return _field_value_helper
View
30 ftw/table/utils.py
@@ -105,7 +105,7 @@ def generate(self, contents, columns, sortable=False,
pass
table['rows'].append(row)
- if meta_data is None:
+ if meta_data is None and not self.options.get('omit_metadata', False):
#create metadata for oldstyle column definition
meta_data = deepcopy(METADATA)
for column in self.columns:
@@ -129,6 +129,8 @@ def generate(self, contents, columns, sortable=False,
col['id'] = key
col['sortable'] = column.get('sortable', True)
col['hidden'] = column.get('hidden', False)
+ col['filter'] = column.get('filter', False)
+ col['groupable'] = column.get('groupable', True)
if not column['title']:
if key == 'draggable':
@@ -174,17 +176,19 @@ def generate(self, contents, columns, sortable=False,
aecolumn = self.options['auto_expand_column']
meta_data['config']['auto_expand_column'] = aecolumn
- #add static html snippets. Eg batching, buttons, etc
- if 'static' in self.options:
- meta_data['static'] = deepcopy(self.options['static'])
-
#add translations for the table
- meta_data['translations'] = {}
- for msgid in msgids:
- meta_data['translations'][msgid] = translate(
- msgid, domain='ftw.table', context=self.request)
+ if meta_data is not None:
+ meta_data['translations'] = {}
+ for msgid in msgids:
+ meta_data['translations'][msgid] = translate(
+ msgid, domain='ftw.table', context=self.request)
+
if meta_data:
table['metaData'] = meta_data
+
+ if 'static' in self.options:
+ table['static_html'] = deepcopy(self.options['static'])
+
jsonstr = json.dumps(table)
return jsonstr
else:
@@ -258,6 +262,8 @@ def process_column(self, column):
sortable = True
hidden = False
+ groupable = True
+ filter_ = None
if isinstance(column, basestring):
attr = sort_index = column
@@ -290,6 +296,8 @@ def process_column(self, column):
width = column.get('width', None)
sortable = column.get('sortable', True)
hidden = column.get('hidden', False)
+ groupable = column.get('groupable', True)
+ filter_ = column.get('filter', None)
title = len(title) and title or attr
sort_index = len(sort_index) and sort_index or attr
@@ -301,4 +309,6 @@ def process_column(self, column):
'transform': transform,
'width': width,
'sortable': sortable,
- 'hidden': hidden}
+ 'hidden': hidden,
+ 'groupable': groupable,
+ 'filter': filter_}
Something went wrong with that request. Please try again.