have editing working. at. last. create should be next.
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Wed, 28 Mar 2012 22:08:18 +0000 (18:08 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Wed, 28 Mar 2012 22:08:18 +0000 (18:08 -0400)
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/web/js/dojo/openils/FlattenerStore.js
Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js

index c9ad647..4fbb547 100644 (file)
@@ -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 */
             };
         }
     });
index acac266..1aebd2e 100644 (file)
@@ -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 {