From a4cf2fd0a81ba3207761594dcd79cf9241a741c5 Mon Sep 17 00:00:00 2001 From: Lebbeous Fogle-Weekley Date: Wed, 28 Mar 2012 18:08:18 -0400 Subject: [PATCH] have editing working. at. last. create should be next. Signed-off-by: Lebbeous Fogle-Weekley --- Open-ILS/web/js/dojo/openils/FlattenerStore.js | 197 ++++++++++++++++----- .../web/js/dojo/openils/widget/FlattenerGrid.js | 38 ++-- 2 files changed, 169 insertions(+), 66 deletions(-) diff --git a/Open-ILS/web/js/dojo/openils/FlattenerStore.js b/Open-ILS/web/js/dojo/openils/FlattenerStore.js index c9ad647b95..4fbb547110 100644 --- a/Open-ILS/web/js/dojo/openils/FlattenerStore.js +++ b/Open-ILS/web/js/dojo/openils/FlattenerStore.js @@ -27,6 +27,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { "openils.FlattenerStore", null, { "_last_fetch": null, /* used internally */ + "_flattener_url": "/opac/extras/flattener", /* Everything between here and the constructor can be specified in * the constructor's args object. */ @@ -61,22 +62,36 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { }, "_prepare_flattener_params": function(req) { - var limit = (!isNaN(req.count) && req.count != Infinity) ? - req.count : this.limit; - var offset = (!isNaN(req.start) && req.start != Infinity) ? - req.start : this.offset; - var content = { "hint": this.fmClass, - "ses": openils.User.authtoken, - "where": dojo.toJson(req.query), - "slo": dojo.toJson({ - "sort": this._prepare_sort(req.sort), - "limit": limit, - "offset": offset - }) + "ses": openils.User.authtoken }; + /* If we're asked for a specific identity, we don't use + * any query or sort/count/start (sort/limit/offset). */ + if ("identity" in req) { + var where = {}; + where[this.fmIdentifier] = req.identity; + + content.where = dojo.toJson(where); + } else { + var limit = (!isNaN(req.count) && req.count != Infinity) ? + req.count : this.limit; + var offset = (!isNaN(req.start) && req.start != Infinity) ? + req.start : this.offset; + + dojo.mixin( + content, { + "where": dojo.toJson(req.query), + "slo": dojo.toJson({ + "sort": this._prepare_sort(req.sort), + "limit": limit, + "offset": offset + }) + } + ); + } + if (this.mapKey) { /* XXX TODO, get a map key */ content.key = this.mapKey; } else { @@ -89,8 +104,6 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { "_display_attributes": function() { var self = this; - /* XXX normalize map clause like ML does, - * so we can use short form */ return openils.Util.objectProperties(this.mapClause).filter( function(key) { return self.mapClause[key].display; } ); @@ -155,21 +168,24 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { if (typeof something != "object" || something === null) return false; - var fields = this._display_attributes() + var fields = this._display_attributes(); for (var i = 0; i < fields.length; i++) { var cur = fields[i]; - if (typeof something[cur] == "undefined") + if (!(cur in something)) return false; } return true; }, "isItemLoaded": function(/* anything */ something) { - console.warn("[unfinished] isItemLoaded(" + something + ")"); + //console.log("isItemLoaded(" + something + ")"); - /* This is assuming items are always loaded, which is probably not right for this particular store.*/ - return this.isItem(something); + /* XXX if 'something' is not an item at all, are we just supposed + * to return false or throw an exception? */ + return this.isItem(something) && ( + something[this.fmIdentifier] in this._current_items + ); }, "close": function(/* object */ request) { /* no-op */ return; }, @@ -189,7 +205,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { if (!this.isItem(keywordArgs.item)) throw new FlattenerStoreError("not an item; can't load it"); - keywordArgs.identity = this.getIdentity(item); + keywordArgs.identity = this.getIdentity(keywordArgs.item); return this.fetchItemByIdentity(keywordArgs); }, @@ -216,9 +232,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { console.info("fetch(" + dojo.toJson(req) + ")"); - /* XXX We're clearing this cache upon every fetch? Do we need/want - * to do that? Not sure. */ - this._current_items = {}; + // this._current_items={}; /* I'm pretty sure we don't want this */ var callback_scope = req.scope || dojo.global; var post_params = this._prepare_flattener_params(req); @@ -271,13 +285,15 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { }; req.abort = function() { - alert("The 'abort' operation is not supported"); + throw new FlattenerStoreError( + "The 'abort' operation is not supported" + ); }; var fetch_time = this._last_fetch = (new Date().getTime()); dojo.xhrPost({ - "url": "/opac/extras/flattener", + "url": this._flattener_url, "content": post_params, "handleAs": "json", "sync": false, @@ -294,7 +310,6 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { /* *** Begin dojo.data.api.Identity methods *** */ "getIdentity": function(/* object */ item) { - //console.log("getIdentity(" + item + ")"); if (!this.isItem(item)) throw new FlattenerStoreError("not an item"); @@ -306,42 +321,128 @@ if (!dojo._hasResource["openils.FlattenerStore"]) { return [this.fmIdentifier]; }, + /* not officially part of dojo.data.api.Read, and mostly probably + * what fetchItemByIdentity should do? */ + "refreshItem": function(/* object */ item) { + console.log("refreshItem(" + dojo.toJson(item) + ")"); + var post_params = this._prepare_flattener_params({"identity": this.getIdentity(item)}); + + var self = this; + var process_fetch_one = function(obj, when) { + if (when < self._last_fetch) /* Stale response. Discard. */ + return; + + /* don't call onBegin for this, even */ + if (dojo.isArray(obj)) { + if (obj.length == 1) { + obj = obj[0]; + for (var prop in obj) { + self.setValue(item, prop, obj[prop]); + } + } else if (obj.length > 1) { + throw new FlattenerStoreError("too many results"); + } else { + /* no results; not really an error from the + * store's point of view I suppose? */ + } + } else { + throw new FlattenerStoreError("bad response"); + } + }; + + console.log("post_params: " + dojo.toJson(post_params)); + + var fetch_time = this._last_fetch = (new Date().getTime()); + + dojo.xhrPost({ + "url": this._flattener_url, + "content": post_params, + "handleAs": "json", + "sync": false, + "preventCache": true, + "headers": {"Accept": "application/json"}, + "load": function(obj){ process_fetch_one(obj, fetch_time); } + }); + }, + "fetchItemByIdentity": function(/* object */ keywordArgs) { + console.warning("[unimplemented] fetchItemByIdentity() unneeded?"); + }, - /* XXX This almost certainly needs attention (unless DataGrid - * doesn't use it (or loadItem)?), and needs to be able to call - * fetch() or to talk to the web service directly. */ + /* dojo.data.api.Write */ - console.warn( - "[unfinished] fetchItemByIdentity(" + - dojo.toJson(keywordArgs) + ")" - ); - if (keywordArgs.identity == undefined) - return null; // Identity API spec unclear whether error callback - // would need to be run, so we won't. - var callback_scope = keywordArgs.scope || dojo.global; +/* to add: + * ------ + * newItem -> call onNew(newitem) + * deleteItem -> call onDelete(deleteditem) + * setValue -> call onSet(item, attr, oldval, newval) + * setValues -> ditto + */ - var item; - if (item = this._current_items[keywordArgs.identity]) { - if (typeof keywordArgs.onItem == "function") - keywordArgs.onItem.call(callback_scope, item); + "newItem": function(keywordArgs, parentInfo) { + if (parentInfo) + throw new FlattenerStoreError("not a hierarchical datastore"); - return item; - } else { - if (typeof keywordArgs.onError == "function") - keywordArgs.onError.call(callback_scope, E); - return null; - } + /* we need to get new item by calling a fetch or something, because + * the new fmobj as result of createpane or something is not enough + * (we need the data the flattener would return about it) + * + * finish when editing is figured out */ + //this.onNew(myNewItem, parentInfo) + }, + + "setValue": function(item, attribute, value) { + if (attribute == this.fmIdentifier) + throw new FlattenerStoreError("can't change item identifier"); + + var old_value = dojo.clone(item[attribute]); + + item[attribute] = dojo.clone(value); + this.onSet(item, attribute, old_value, value); + }, + + "setValues": function(item, attribute, values) { + console.warn("[unimplemented] setValues(); unneeded or TODO?"); }, + "deleteItem": function(item) { + console.warn("[unimplemented] deleteItem() XXX TODO"); + }, + + "unsetAttribute": function() { + console.warn("[unimplemented] unsetAttribute()"); + }, + + "save": function() { + console.warn("[unimplemented] save()"); + }, + + "revert": function() { + console.warn("[unimplemented] revert()"); + }, + + "isDirty": function() { /* I /think/ this will be ok for our purposes */ + console.info("[stub] isDirty() will always return false"); + + return false; + }, + + /* dojo.data.api.Notification */ + + "onNew" : function(item) { /* no-op, but keep */ }, + "onDelete" : function(item) { /* no-op, keep */ }, + "onSet": function(item, attr, oldval, newval) { /* no-op, but keep */ }, + /* *** Classes implementing any Dojo APIs do this to list which * APIs they're implementing. *** */ "getFeatures": function() { return { "dojo.data.api.Read": true, - "dojo.data.api.Identity": true + "dojo.data.api.Identity": true, + "dojo.data.api.Notification": true, + "dojo.data.api.Write": true /* well, only partly */ }; } }); diff --git a/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js b/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js index acac26633b..1aebd2ee40 100644 --- a/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js +++ b/Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js @@ -11,14 +11,15 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { dojo.declare( "openils.widget.FlattenerGrid", [dojox.grid.DataGrid], { - /* These potential constructor arguments are useful to - * lattenerGrid in their own right */ + /* These potential constructor arguments are useful to + * FlattenerGrid in their own right */ "columnReordering": true, "columnPersistKey": null, /* These potential constructor arguments maybe useful to * FlattenerGrid in their own right, and are passed to * FlattenerStore. */ + "editable": true, "fmClass": null, "fmIdentifier": null, "mapExtras": null, @@ -109,13 +110,16 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { "startup": function() { if (!this.store) { - this.store = new openils.FlattenerStore({ - "fmClass": this.fmClass, - "fmIdentifier": this.fmIdentifier, - "mapClause": (this.mapClause || this._generate_map()), - "baseSort": this.baseSort, - "defaultSort": this.defaultSort - }); + this._setStore( /* this exact method chosen intentionally */ + new openils.FlattenerStore({ + "fmClass": this.fmClass, + "fmIdentifier": this.fmIdentifier, + "mapClause": + (this.mapClause || this._generate_map()), + "baseSort": this.baseSort, + "defaultSort": this.defaultSort + }) + ); } if (!this.columnPicker) { @@ -193,7 +197,7 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { "_makeEditPane": function(storeItem, rowIndex, onPostSubmit, onCancel) { var grid = this; var fmObject = (new openils.PermaCrud()).retrieve( - this.fmClass, + this.fmClass, this.store.getIdentity(storeItem) ); @@ -208,16 +212,14 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) { "requiredFields": this.requiredFields, "suppressFields": this.suppressEditFields, "onPostSubmit": function() { - console.info("onPostSubmit"); - console.warn("[unfinished] would update store with fmObject values here"); - // for(var i in fmObject._fields) { - // var field = fmObject._fields[i]; - // if(idents.filter(function(j){return (j == field)})[0]) - // continue; // don't try to edit an identifier field - // grid.store.setValue(storeItem, field, fmObject[field]()); - // } + console.log("onPostSubmit"); + /* ask the store to call flattener specially to get + * the flat row related to only this fmobj */ + grid.store.refreshItem(storeItem); + if (grid.onPostUpdate) grid.onPostUpdate(storeItem, rowIndex); + setTimeout( function() { try { -- 2.11.0