From b2097c513507c632738c59962d67c8259cc6a445 Mon Sep 17 00:00:00 2001 From: Lebbeous Fogle-Weekley Date: Wed, 18 Apr 2012 19:26:30 -0400 Subject: [PATCH] do not lose Signed-off-by: Lebbeous Fogle-Weekley --- .../src/perlmods/lib/OpenILS/Utils/Fieldmapper.pm | 2 + Open-ILS/src/templates/circ/hold_pull_list.tt2 | 15 +- .../web/js/dojo/openils/widget/FlattenerGrid.js | 273 +++++++++++++++------ .../web/js/dojo/openils/widget/GridColumnPicker.js | 43 +++- 4 files changed, 238 insertions(+), 95 deletions(-) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/Fieldmapper.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Fieldmapper.pm index df00277cbf..eddb944d48 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Utils/Fieldmapper.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Fieldmapper.pm @@ -139,11 +139,13 @@ sub load_links { my $reltype = get_attribute( $attribute_list, 'reltype' ); my $key = get_attribute( $attribute_list, 'key' ); my $class = get_attribute( $attribute_list, 'class' ); + my $map = get_attribute( $attribute_list, 'map' ); $$fieldmap{$fm}{links}{ $field } = { class => $class, reltype => $reltype, key => $key, + map => $map }; } } diff --git a/Open-ILS/src/templates/circ/hold_pull_list.tt2 b/Open-ILS/src/templates/circ/hold_pull_list.tt2 index db470bc6f0..ce8d338a4d 100644 --- a/Open-ILS/src/templates/circ/hold_pull_list.tt2 +++ b/Open-ILS/src/templates/circ/hold_pull_list.tt2 @@ -66,6 +66,8 @@ autoHeight="10" editOnEnter="false" hideSelector="true" + autoCoreFields="true" + autoFieldFields="['current_copy']" editStyle="pane" showLoadFilter="true" fmClass="'ahopl'" @@ -76,15 +78,12 @@ query="{}"> - - Title - Author - Shelving Location - Patron Name or Alias - - - + + Author + Title + + Parts diff --git a/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js b/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js index 0932970c4f..c70f52ccb4 100644 --- a/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js +++ b/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js @@ -16,6 +16,8 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { * FlattenerGrid in their own right */ "columnReordering": true, "columnPersistKey": null, + "autoCoreFields": false, + "autoFieldFields": null, "showLoadFilter": false, /* use FlattenerFilterDialog */ "fetchLock": false, @@ -55,12 +57,12 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { /* These are the fields defined in thead -> tr -> [th,th,...]. * For purposes of building the map, where each field has * three boolean attributes "display", "sort" and "filter", - * assume "display" and "sort" are always true for these. + * assume "display" is always true for these. * That doesn't mean that at the UI level we can't hide a * column later. * * If you need extra fields in the map for which display - * or sort should *not* be true, use mapExtras. + * should *not* be true, use mapExtras. */ dojo.forEach( fields, function(field) { @@ -70,7 +72,7 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { map[field.field] = { "display": true, "filter": (field.ffilter || false), - "sort": true, + "sort": field.fsort, "path": field.fpath || field.field }; /* The following attribute is not for the flattener @@ -135,85 +137,96 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { return clean; }, - /* The FlattenerStore doesn't need this, but it has at least two - * uses: 1) FlattenerFilterDialog, 2) setting column header labels - * to IDL defaults. - * - * To call these 'Terminii' can be misleading. In certain - * (actually probably common) cases, they won't really be the last - * field in a path, but the next-to-last. Read on. */ - "_calculateMapTerminii": function() { - function _fm_is_selector_for_class(hint, field) { - var cl = fieldmapper.IDL.fmclasses[hint]; + /* Given the hint of a class to start at, follow path to the end + * and return information on the last field. */ + "_followPathToEnd": function(hint, path, allow_selector_backoff) { + function _fm_is_selector_for_class(h, field) { + var cl = fieldmapper.IDL.fmclasses[h]; return (cl.field_map[cl.pkey].selector == field); } - function _follow_to_end(hint, path) { - var last_field, last_hint; - var orig_path = dojo.clone(path); - var field; - - while (field = path.shift()) { - /* XXX this assumes we have the whole IDL loaded. I - * guess we could teach this to work by loading classes - * on demand when we don't have the whole IDL loaded. */ - var field_def = - fieldmapper.IDL.fmclasses[hint].field_map[field]; - - if (!field_def) { - throw new Error( - "Lost our way in IDL at hint " + hint + - ", field " + field - ); - } - - if (field_def["class"] && path.length) { - last_field = field; - last_hint = hint; - - hint = field_def["class"]; - } else if (path.length) { - /* There are more fields left but we can't follow - * the chain via IDL any further. */ - throw new Error( - "_calculateMapTerminii can't parse path " + - orig_path + " (at " + field + ")" - ); - } else { - break; /* keeps field defined after loop */ - } + var last_field, last_hint; + var orig_path = dojo.clone(path); + var field, field_def; + + while (field = path.shift()) { + /* XXX this assumes we have the whole IDL loaded. I + * guess we could teach this to work by loading classes + * on demand when we don't have the whole IDL loaded. */ + field_def = + fieldmapper.IDL.fmclasses[hint].field_map[field]; + + if (!field_def) { + /* This can be ok in some cases. Columns following + * IDL paths involving links with a nonempty "map" + * attribute can be used for display only (no + * sort, no filter). */ + console.info( + "Lost our way in IDL at hint " + hint + + ", field " + field + "; may be ok" + ); + return null; } - var datatype = field_def.datatype; - var indirect = false; - /* Back off the last field in the path if it's a selector - * for its class, because the preceding field will be - * a better thing to hand to AutoFieldWidget. - */ - if (orig_path.length > 1 && - _fm_is_selector_for_class(hint, field)) { - hint = last_hint; - field = last_field; - datatype = "link"; - indirect = true; + if (field_def["class"] && path.length) { + last_field = field; + last_hint = hint; + + hint = field_def["class"]; + } else if (path.length) { + /* There are more fields left but we can't follow + * the chain via IDL any further. */ + throw new Error( + "_calculateMapTerminii can't parse path " + + orig_path + " (at " + field + ")" + ); + } else { + break; /* keeps field defined after loop */ } + } - return { - "fmClass": hint, - "name": field, - "label": field_def.label, - "datatype": datatype, - "indirect": indirect - }; + var datatype = field_def.datatype; + var indirect = false; + /* If allowed, back off the last field in the path if it's a + * selector for its class, because the preceding field will be + * a better thing to hand to AutoFieldWidget. + */ + if (orig_path.length > 1 && allow_selector_backoff && + _fm_is_selector_for_class(hint, field)) { + hint = last_hint; + field = last_field; + datatype = "link"; + indirect = true; } + return { + "fmClass": hint, + "name": field, + "label": field_def.label, + "datatype": datatype, + "indirect": indirect + }; + }, + + /* The FlattenerStore doesn't need this, but it has at least two + * uses: 1) FlattenerFilterDialog, 2) setting column header labels + * to IDL defaults. + * + * To call these 'Terminii' can be misleading. In certain + * (actually probably common) cases, they won't really be the last + * field in a path, but the next-to-last. Read on. */ + "_calculateMapTerminii": function() { this.mapTerminii = []; for (var column in this.mapClause) { + var end = this._followPathToEnd( + this.fmClass, + this.mapClause[column].path.split(/\./), + true /* allow selector backoff */ + ); + if (!end) + continue; var terminus = dojo.mixin( - _follow_to_end( - this.fmClass, - this.mapClause[column].path.split(/\./) - ), { + end, { "simple_name": column, "isfilter": this.mapClause[column].filter } @@ -226,8 +239,7 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { }, "_supplementHeaderNames": function() { - /* You'd be surprised how rarely this make sense in Flattener - * use cases, but if we didn't give a particular header cell + /* If we didn't give a particular header cell * () a display name (the innerHTML of that ), then * use the IDL to provide the label of the terminus of the * flattener path for that column. It may be better than using @@ -262,6 +274,97 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { return {"labels": labels, "columns": columns}; }, + "_getAutoFieldFields": function(fmclass) { + return fieldmapper.IDL.fmclasses[fmclass].fields.filter( + function(field) { + return !field.virtual && field.datatype != "link"; + } + ); + }, + + /* Take our core class (this.fmClass) and add table columns for + * any field we don't already have covered by actual hard-coded + * columns. */ + "_addAutoCoreFields": function() { + var cell_list = this.structure[0].cells[0]; + + dojo.forEach( + fieldmapper.IDL.fmclasses[this.fmClass].fields, + function(f) { + if (f.datatype == "link" || f.virtual) + return; + + if (cell_list.filter( + function(c) { + if (!c.fpath) return false; + return c.fpath.split(/\./)[0] == f.name; + } + ).length) + return; + + cell_list.push({ + "field": f.name, + "name": f.label, + "fsort": true, + "_visible": false + }); + } + ); + }, + + "_addAutoFieldFields": function(paths) { + var self = this; + var n = 0; + + dojo.forEach( + paths, function(path) { + /* The beginning is the end. */ + var beginning = self._followPathToEnd( + self.fmClass, path.split(/\./), false + ); + if (!beginning) { + return; + } else { + console.log(dojo.toJson(beginning)); + dojo.forEach( + self._getAutoFieldFields(beginning.fmClass), + function(field) { + var would_be_path = + path + "." + field.name; + var wbp_re = + new RegExp("^" + would_be_path); + if (!self.structure[0].cells[0].filter( + function(c) { + return c.fpath.match(wbp_re); + } + ).length) { + self.structure[0].cells[0].push({ + "field": "AUTO_" + beginning.name + + "_" + field.name, + "name": beginning.label + " - " + + field.label, + "fsort": true, + "fpath": would_be_path, + "_visible": false + }); + } + } + ); + } + } + ); + }, + + "_addAutoFields": function() { + if (this.autoCoreFields) + this._addAutoCoreFields(); + + if (dojo.isArray(this.autoFieldFields)) + this._addAutoFieldFields(this.autoFieldFields); + + this.setStructure(this.structure); + }, + "constructor": function(args) { dojo.mixin(this, args); @@ -270,9 +373,11 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { }, "startup": function() { - /* Save original query for further filtering later */ this._baseQuery = dojo.clone(this.query); + + this._addAutoFields(); + this._startupGridHelperColumns(); if (!this.columnPicker) { @@ -296,6 +401,17 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { this.inherited(arguments); }, + "canSort": function(idx, skip_structure /* API abuse */) { + var initial = this.inherited(arguments); + + /* idx is one-based instead of zero-based for a reason. */ + var view_idx = Math.abs(idx) - 1; + return initial && ( + skip_structure || + this.views.views[0].structure.cells[0][view_idx].fsort + ); + }, + /* Maps ColumnPicker sort fields to the correct format. If no sort fields specified, falls back to defaultSort */ "_mapCPSortFields": function(sortFields) { @@ -325,12 +441,12 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { }), this.query ); - if (!this.fetchLock) - this._refresh(true); - // pick up any column label changes this.columnPicker.reloadStructure(); + if (!this.fetchLock) + this._refresh(true); + this._showing_create_pane = false; this.overrideEditWidgets = {}; @@ -745,6 +861,11 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { cellDef[a] = value; } ); + + /* fsort is special. Assume true unless defined. */ + var fsort = dojo.attr(node, "fsort"); + cellDef.fsort = (typeof fsort == "undefined" || fsort === null) ? + true : dojo.fromJson(fsort); }; })(); diff --git a/Open-ILS/web/js/dojo/openils/widget/GridColumnPicker.js b/Open-ILS/web/js/dojo/openils/widget/GridColumnPicker.js index 9cc367d8f4..16e84b9c9a 100644 --- a/Open-ILS/web/js/dojo/openils/widget/GridColumnPicker.js +++ b/Open-ILS/web/js/dojo/openils/widget/GridColumnPicker.js @@ -63,8 +63,9 @@ if(!dojo._hasResource["openils.widget.GridColumnPicker"]) { * This is necessary if external forces alter the structure. */ reloadStructure : function() { - this.structure = this.grid.structure; this.cells = this.structure[0].cells[0].slice(); + this.pruneInvisibleFields(); + this.structure = this.grid.structure; this.grid.setStructure(this.structure); }, @@ -211,16 +212,24 @@ if(!dojo._hasResource["openils.widget.GridColumnPicker"]) { else this.dialogTable.appendChild(tr); - if ( this.grid.canSort(i+1) ) { // column index is 1-based - - // must be added after its parent node is inserted into the DOM. - var ns = new dijit.form.NumberSpinner( - { constraints : {places : 0}, - value : cell._sort || 0, - style : 'width:4em', - name : 'sort', - }, ipt3 - ); + if (this.grid.canSort( + i + 1, /* column index is 1-based */ + true /* skip structure test (API abuse) */ + )) { + + /* Ugly kludge. When using with FlattenerGrid the + * conditional is needed. Shouldn't hurt usage with + * AutoGrid. */ + if (typeof cell.fsort == "undefined" || cell.fsort) { + // must be added after its parent node is inserted into the DOM. + var ns = new dijit.form.NumberSpinner( + { constraints : {places : 0}, + value : cell._sort || 0, + style : 'width:4em', + name : 'sort', + }, ipt3 + ); + } } } }, @@ -366,6 +375,18 @@ if(!dojo._hasResource["openils.widget.GridColumnPicker"]) { this.grid.update(); }, + // *only* call this when no usr setting tells us what columns + // are visible or not. + pruneInvisibleFields : function() { + this.structure[0].cells[0] = dojo.filter( + this.structure[0].cells[0], + dojo.hitch(this, function(c) { + // keep true or undef, lose false + return typeof c._visible == "undefined" || c._visible; + }) + ); + }, + load : function() { var _this = this; -- 2.11.0