LP 1084732 TPAC copy location selector
authorBill Erickson <berick@esilibrary.com>
Tue, 13 Aug 2013 18:39:00 +0000 (14:39 -0400)
committerBen Shum <bshum@biblio.org>
Tue, 20 Aug 2013 14:22:56 +0000 (10:22 -0400)
Copy location filter selector for the TPAC advanced search interface.
The behavior is the same as the analogous JSPAC feature.  The interface
components are hidden by default, so non-JS browsers will be none the
wiser.

A number of small tweaks to related code were needed and are included in
this commit:

1. Make copy locations field_safe in the IDL so they can be retrieved
   and parsed without the IDL
2. Insert a (full) hash-based org unit tree as inline JS at the bottom
   of js.tt2 so JS will have access to the whole tree without having to
   load the IDL to understand it.
3. Fix a bug in openils.CGI : decodeURIComponent parameter names

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Ben Shum <bshum@biblio.org>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/templates/opac/parts/advanced/search.tt2
Open-ILS/src/templates/opac/parts/config.tt2
Open-ILS/src/templates/opac/parts/js.tt2
Open-ILS/web/js/dojo/openils/CGI.js
Open-ILS/web/js/ui/default/opac/copyloc.js [new file with mode: 0644]

index 2fe07f9..0064b76 100644 (file)
@@ -4216,7 +4216,7 @@ SELECT  usr,
                        <link field="notify_staff" reltype="has_a" key="id" map="" class="au"/>
                </links>
        </class>
-       <class id="acpl" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::copy_location" oils_persist:tablename="asset.copy_location" reporter:label="Copy/Shelving Location">
+       <class id="acpl" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::copy_location" oils_persist:tablename="asset.copy_location" reporter:label="Copy/Shelving Location"  oils_persist:field_safe="true">
                <fields oils_persist:primary="id" oils_persist:sequence="asset.copy_location_id_seq">
                        <field reporter:label="Can Circulate?" name="circulate"  reporter:datatype="bool"/>
                        <field reporter:label="Is Holdable?" name="holdable" reporter:datatype="bool"/>
index ad8cc44..56fd012 100644 (file)
             <tr>
 [%
         END; %]
-            <td valign='top'>
+            <td valign='top'[% IF adv_chunk.js_only %] 
+                id='adv_chunk_[% adv_chunk.adv_special %]' 
+                class='hidden'[% END %]>
                 <strong>[% adv_chunk.adv_label %]</strong><br />
 [%
         IF adv_chunk.adv_special;
             SWITCH adv_chunk.adv_special;
                 CASE "lib_selector";
                     PROCESS "opac/parts/org_selector.tt2";
-                        INCLUDE build_org_selector show_loc_groups=1; %]
+                        INCLUDE build_org_selector show_loc_groups=1 id="adv_org_selector" %]
                             <div style="position:relative;top:7px;">
                                 <input type='checkbox' name="modifier"
                                     value="available"[% CGI.param('modifier').grep('available').size ? ' checked="checked"' : '' %]
                 CASE "sort_selector";
                     INCLUDE "opac/parts/filtersort.tt2"
                         value=CGI.param('sort') class='results_header_sel';
+
+                CASE "copy_location" %]
+                    <select id="adv_copy_location_selector" 
+                        aria-label="[% l('Select Shelving Location') %]"
+                        name="fi:locations" size="3" multiple="multiple">
+                    </select>
+                [% 
             END;
         ELSIF adv_chunk.adv_attr;
             INCLUDE "opac/parts/coded_value_selector.tt2"
index bc046bd..d798fb9 100644 (file)
@@ -115,7 +115,8 @@ search.adv_config = [
     {adv_label => l("Audience"),  adv_attr => ["audience_group", "audience"], adv_break => 1},
     {adv_label => l("Video Format"), adv_attr => "vr_format"},
     {adv_label => l("Bib Level"), adv_attr => "bib_level"},
-    {adv_label => l("Literary Form"), adv_attr => "lit_form", adv_break => 1},
+    {adv_label => l("Literary Form"), adv_attr => "lit_form"},
+    {adv_label => l("Shelving Location"), adv_special => "copy_location", js_only => 1, adv_break => 1},
     {adv_label => l("Search Library"), adv_special => "lib_selector"},
     {adv_label => l("Publication Year"), adv_special => "pub_year"},
     {adv_label => l("Sort Results"), adv_special => "sort_selector"},
index 071ce21..67d09fb 100644 (file)
 
 [% INCLUDE "opac/parts/acjs.tt2" IF ctx.page == 'record' %]
 [% INCLUDE "opac/parts/ac_google_books.tt2" IF ctx.page == 'record' AND ctx.google_books_preview %]
+[% IF ctx.page == 'advanced' %]
+<script type="text/javascript" 
+    src="[% ctx.media_prefix %]/js/ui/default/opac/copyloc.js"></script>
+[% END %]
+
+<!-- provide a JS friendly org unit hash -->
+<script type="text/javascript">
+var aou_hash = {
+[% FOR org_unit IN ctx.aou_list %]
+    [% org_unit.id %] : {
+        id : "[% org_unit.id %]",
+        name : "[% org_unit.name | replace('"', '\"') %]",
+        parent_ou : "[% org_unit.parent_ou %]",
+        depth : "[% org_unit.ou_type.depth %]",
+        can_have_vols : "[% org_unit.ou_type.can_have_vols %]"
+    }[%- ',' UNLESS loop.last -%]
+[% END %]
+};
+</script>
 
 [%- END; # want_dojo -%]
index d79c825..e353e4e 100644 (file)
@@ -62,6 +62,7 @@ if(!dojo._hasResource["openils.CGI"]) {
                 if(c == "&" || c == ";") {
                     inkey = 1;
                     invalue = 0;
+                    key = decodeURIComponent(key);
                     if( ! this.data[key] ) this.data[key] = [];
                     this.data[key].push(decodeURIComponent(value));
                     this._keys.push(key);
diff --git a/Open-ILS/web/js/ui/default/opac/copyloc.js b/Open-ILS/web/js/ui/default/opac/copyloc.js
new file mode 100644 (file)
index 0000000..1eaae20
--- /dev/null
@@ -0,0 +1,107 @@
+dojo.require("DojoSRF");
+dojo.require("openils.CGI");
+
+// called on initial page load and when the advance search org unit
+// selector is changed.
+function apply_adv_copy_locations() {
+
+    // patron selected org
+    var sel = dojo.byId('adv_org_selector');
+    var selected_id = sel.options[sel.selectedIndex].getAttribute('value');
+    var org_unit = aou_hash[selected_id];
+
+    if (org_unit.can_have_vols != 't') {
+        dojo.addClass('adv_chunk_copy_location', 'hidden');
+        return;
+    }
+
+    var display_orgs = [];
+
+    // we want to display copy locations at the selected org,
+    // all parent orgs, and all child orgs.
+
+    function collect_child_orgs(org_id) {
+        display_orgs.push(org_id);
+        for (var id in aou_hash) { // for key in
+            if (aou_hash[id].parent_ou == org_id) 
+                collect_child_orgs(id);
+        }
+    }
+
+    function collect_parent_orgs(org_id) {
+        if (!org_id) return;
+        display_orgs.push(org_id);
+        collect_parent_orgs(aou_hash[org_id].parent_ou);
+    }
+
+    collect_child_orgs(org_unit.id);
+    collect_parent_orgs(org_unit.parent_ou);
+    fetch_adv_copy_locations(display_orgs);
+}
+
+function fetch_adv_copy_locations(org_ids) {
+
+    var params = [{
+        cache : 1, 
+        fields : ['name', 'id', 'owning_lib'],
+        query : {owning_lib : org_ids, opac_visible : 't'}
+    }];
+
+    new OpenSRF.ClientSession('open-ils.fielder').request({
+        method: 'open-ils.fielder.acpl.atomic',
+        params: params,
+        async: true,
+        oncomplete: function(r) {
+            var resp = r.recv();
+            if (resp) {
+                var list = resp.content();
+                if (list && list.length) {
+                    render_adv_copy_locations(list);
+                } else {
+                    dojo.addClass('adv_chunk_copy_location', 'hidden');
+                }
+            } else {
+                dojo.addClass('adv_chunk_copy_location', 'hidden');
+            }
+        }                                                              
+    }).send(); 
+}
+
+function render_adv_copy_locations(locations) {
+    var sel = dojo.byId('adv_copy_location_selector');
+    var cgi = new openils.CGI();
+
+    // collect any location values from the URL to re-populate the list
+    var url_selected = cgi.param('fi:locations');
+    if (url_selected) {
+        if (!dojo.isArray(url_selected)) 
+            url_selected = [url_selected];
+    }
+
+    dojo.removeClass('adv_chunk_copy_location', 'hidden');
+    
+    // sort by name
+    locations = locations.sort(
+        function(a, b) {return a.name < b.name ? -1 : 1}
+    );
+
+    // remove the previous list of locations
+    dojo.empty(sel);
+
+    // append the new list of locations
+    dojo.forEach(locations, function(loc) {
+        var attrs = {value : loc.id, innerHTML : loc.name};
+        if (url_selected && url_selected.indexOf(''+loc.id) > -1) {
+            attrs.selected = true;
+        }
+        sel.appendChild(dojo.create('option', attrs));
+    });
+}
+
+// load the locations on page load
+dojo.addOnLoad(function() {
+    apply_adv_copy_locations();
+    dojo.connect(dojo.byId('adv_org_selector'), 
+        'onchange', apply_adv_copy_locations);
+});
+