From 3ebff585f662cd57890113e02ef557e127facaf3 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Tue, 20 Mar 2012 15:16:09 -0400 Subject: [PATCH] Custom Org Tree : Admin UI New UI for managing custom org unit trees. (Only OPAC is supported for now). Custom trees default to the same shape as the full org unit tree. Staff can remove nodes and re-order nodes. Staff can also replace deleted nodes by dragging them from the reference tree on the left. Admin -> Server Admin -> Custom Org Unit Trees Signed-off-by: Bill Erickson Signed-off-by: Mike Rylander --- .../conify/global/actor/org_unit_custom_tree.tt2 | 73 ++++++ .../conify/global/actor/org_unit_custom_tree.js | 284 +++++++++++++++++++++ Open-ILS/web/opac/locale/en-US/lang.dtd | 1 + .../xul/staff_client/chrome/content/main/menu.js | 4 + .../chrome/content/main/menu_frame_menus.xul | 4 + 5 files changed, 366 insertions(+) create mode 100644 Open-ILS/src/templates/conify/global/actor/org_unit_custom_tree.tt2 create mode 100644 Open-ILS/web/js/ui/default/conify/global/actor/org_unit_custom_tree.js diff --git a/Open-ILS/src/templates/conify/global/actor/org_unit_custom_tree.tt2 b/Open-ILS/src/templates/conify/global/actor/org_unit_custom_tree.tt2 new file mode 100644 index 0000000000..9d259b026d --- /dev/null +++ b/Open-ILS/src/templates/conify/global/actor/org_unit_custom_tree.tt2 @@ -0,0 +1,73 @@ +[% WRAPPER base.tt2 %] +[% ctx.page_title = l('Org Unit Custom Tree') %] + + + + + + +

[% l('Org Unit Custom Tree') %]

+
    +
  • [% l('To add new nodes to the custom tree, drag them from the full tree on the left') %]
  • +
  • [% l('Changes to custom org trees may require web server (apache) reload before taking effect') %]
  • +
+
+ +
+
+

[% l('Full Org Unit Tree') %]

+ +
+
+
+ + + +
+

[% l('Custom Unit Tree: ') %]

+
+ + +
+ +
+
+
+ +
+[% END %] + + diff --git a/Open-ILS/web/js/ui/default/conify/global/actor/org_unit_custom_tree.js b/Open-ILS/web/js/ui/default/conify/global/actor/org_unit_custom_tree.js new file mode 100644 index 0000000000..cc835f79dc --- /dev/null +++ b/Open-ILS/web/js/ui/default/conify/global/actor/org_unit_custom_tree.js @@ -0,0 +1,284 @@ +dojo.require('dijit.form.Button'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dojo.data.ItemFileReadStore'); +dojo.require('dojo.data.ItemFileWriteStore'); +dojo.require('dijit.Tree'); +dojo.require('dijit.tree.TreeStoreModel'); +dojo.require("dijit._tree.dndSource"); +dojo.require('fieldmapper.Fieldmapper'); +dojo.require('fieldmapper.OrgUtils'); +dojo.require('openils.User'); +dojo.require('openils.Util'); +dojo.require('openils.PermaCrud'); +dojo.require('openils.widget.ProgressDialog'); + +var realTree; // dijit.Tree +var magicTree; // dijit.Tree +var mTree; // aouct object +var pcrud; +var virtId = -1; +var realOrgList = []; +var ctNodes = []; + +dojo.declare( + 'openils.actor.OrgUnitCustomTreeSource', dijit._tree.dndSource, { + itemCreator : function(nodes, etc) { + var items = this.inherited(arguments); + dojo.forEach(items, function(item) {item.shortname = item.name}); + return items; + } +}); + +dojo.declare( + 'openils.actor.OrgUnitCustomTreeStoreModel', dijit.tree.TreeStoreModel, { + mayHaveChildren : function(item) { return true }, +}); + +function drawPage() { + pcrud = new openils.PermaCrud({authtoken : openils.User.authtoken}); + + // real org unit list. Not write-able. Used only as a source. + realOrgList = openils.Util.objectValues( + fieldmapper.aou.OrgCache).map(function(obj) { return obj.org }); + + var store = new dojo.data.ItemFileReadStore( + {data : fieldmapper.aou.toStoreData(realOrgList)}); + + var model = new dijit.tree.TreeStoreModel({ + store: store, + query: {_top : 'true'} + }); + + realTree = new dijit.Tree( + { model: model, + expandAll: function() {treeDoAll(this)}, + collapseAll: function() {treeDoAll(this, true)}, + dndController : dijit._tree.dndSource, + persist : false, + }, + 'real-tree' + ); + + realTree.expandAll(); + drawMagicTree(); +} + + +// composed of org units. Write-able. +function drawMagicTree() { + var orgList = realOrgList; + var query = {_top : 'true'}; + + var mTreeRes = pcrud.search('aouct', + {purpose : treePurposeSelector.attr('value')}); + + if (mTreeRes.length) { + mTree = mTreeRes[0]; + if (openils.Util.isTrue(mTree.active())) { + openils.Util.hide(dojo.byId('activate-tree')); + openils.Util.show(dojo.byId('deactivate-tree'), 'inline'); + } + ctNodes = pcrud.search('aouctn', {tree : mTree.id()}); + if (ctNodes.length) { + orgList = []; + // create an org tree from the custom tree nodes + + dojo.forEach(ctNodes, + function(node) { + // deep clone to avoid globalOrgTree clobbering + var org = JSON2js(js2JSON( + fieldmapper.aou.findOrgUnit(node.org_unit()) + )); + org.parent_ou(null); + org.children([]); + if (node.parent_node()) { + org.parent_ou( + ctNodes.filter( + function(n) {return n.id() == node.parent_node()} + )[0].org_unit() + ); + } + orgList.push(org); + } + ); + var root = ctNodes.filter(function(n) {return n.parent_node() == null})[0]; + query = {id : root.org_unit()+''} + } + } else { + + mTree = new fieldmapper.aouct(); + mTree.isnew(true); + mTree.purpose(treePurposeSelector.attr('value')); + mTree.active(false); + } + + var store = new dojo.data.ItemFileWriteStore( + {data : fieldmapper.aou.toStoreData(orgList)}); + + var model = new openils.actor.OrgUnitCustomTreeStoreModel({ + store : store, + query : query + }); + + magicTree = new dijit.Tree( + { model: model, + expandAll: function() {treeDoAll(this)}, + collapseAll: function() {treeDoAll(this, true)}, + dndController : openils.actor.OrgUnitCustomTreeSource, + dragThreshold : 8, + betweenThreshold : 5, + persist : false, + }, + 'magic-tree' + ); + + magicTree.expandAll(); +} + +// 1. create the tree if necessary +// 2. translate the dijit.tree nodes into aouctn's +// 3. delete the existing aouctn's +// 4. create the new aouctn's +function applyChanges() { + progressDialog.show(); + + if (mTree.isnew()) { + + pcrud.create(mTree, { + oncomplete : function(r, objs) { + mTree = objs[0]; + applyChanges2(); + } + }); + + } else { + if (ctNodes.length) { + console.log('Deleting ' + ctNodes.length + ' nodes'); + pcrud.eliminate(ctNodes, {oncomplete : applyChanges2}); + } else { + applyChanges2(); + } + } +} + +function applyChanges2() { + ctNodes = []; + var newCtNodes = []; + var nodeList = []; + var sorder = 0; + var prevTn; + var progress = 0; + + function flatten(node) { + nodeList.push(node); + dojo.forEach(node.getChildren(), flatten); + } + flatten(magicTree.rootNode); + + // traverse the nodes, creating new aoucnt's as we go + function traverseAndCreate(node) { + var item = node.item; + + var tn = new fieldmapper.aouctn(); + tn.tree(mTree.id()); + tn.org_unit(item.id[0]) + + var pnode = node.getParent(); + if (pnode) { + // find the newly created parent node and extract the ID + var ptn = ctNodes.filter(function(n) { + return n.org_unit() == pnode.item.id[0]})[0]; + tn.parent_node(ptn.id()); + } + + // if the last node was our previous sibling + if (prevTn && prevTn.parent_node() == tn.parent_node()) { + tn.sibling_order(++sorder); + } else { sorder = 0; } + + console.log("Creating new node for org unit " + tn.org_unit()); + + // create the new node, then process the children + pcrud.create(tn, { + oncomplete : function(r, objs) { + var newTn = objs[0]; + ctNodes.push(newTn); + prevTn = newTn; + if (nodeList.length == 0) { + progressDialog.hide(); + location.href = location.href; + } else { + progressDialog.update({maximum : nodeList.length, progress : ++progress}); + traverseAndCreate(nodeList.shift()); + } + } + }); + } + traverseAndCreate(nodeList.shift()); +} + +function deleteSelected() { + var toDelete = []; + + function collectChildren(item) { + toDelete.push(item); + magicTree.model.store.fetch({ + query : {parent_ou : item.id[0]+''}, + onComplete : function(list) { + dojo.forEach(list, collectChildren) + } + }); + } + + magicTree.dndController.getSelectedItems().forEach( + function(item) { + if (item === magicTree.model.root) return + collectChildren(item); + // delete node plus children, starting at the leaf nodes + dojo.forEach(toDelete.reverse(), + function(i) { + console.log('Deleting item ' + i.id); + magicTree.model.store.deleteItem(i) + } + ); + } + ); +} + +function activateTree() { + mTree.active('t'); + if (mTree.isnew()) return; + pcrud.update(mTree, { + oncomplete : function() { + openils.Util.hide(dojo.byId('activate-tree')); + openils.Util.show(dojo.byId('deactivate-tree'), 'inline'); + } + }); +} + +function deactivateTree() { + mTree.active('f'); + if (mTree.isnew()) return; + pcrud.update(mTree, { + oncomplete : function() { + openils.Util.hide(dojo.byId('deactivate-tree')); + openils.Util.show(dojo.byId('activate-tree'), 'inline'); + } + }); +} + +// modified from +// http://stackoverflow.com/questions/2161032/expanding-all-nodes-in-dijit-tree +function treeDoAll(tree, collapse) { + function expand(node) { + if (collapse) tree._collapseNode(node); + else tree._expandNode(node); + var childBranches = dojo.filter(node.getChildren() || [], + function(node) { return node.isExpandable }); + var def = new dojo.Deferred(); + defs = dojo.map(childBranches, expand); + } + return expand(tree.rootNode); +} + +openils.Util.addOnLoad(drawPage); diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index 024419d9bf..e9385ddb22 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -770,6 +770,7 @@ + diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu.js b/Open-ILS/xul/staff_client/chrome/content/main/menu.js index 4ca96d828a..113ea937b5 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu.js @@ -750,6 +750,10 @@ main.menu.prototype = { ['oncommand'], function(event) { open_eg_web_page('conify/global/config/usr_activity_type', null, event); } ], + 'cmd_server_admin_actor_org_unit_custom_tree' : [ + ['oncommand'], + function(event) { open_eg_web_page('conify/global/actor/org_unit_custom_tree', null, event); } + ], 'cmd_local_admin_external_text_editor' : [ ['oncommand'], function() { diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul index bd98d4bb8d..5316bbf715 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul @@ -251,6 +251,9 @@ + @@ -550,6 +553,7 @@ + -- 2.11.0