Copy Location Search Groups : TPac org unit selector
authorBill Erickson <berick@esilibrary.com>
Fri, 17 Feb 2012 16:57:19 +0000 (11:57 -0500)
committerDan Scott <dan@coffeecode.net>
Sun, 11 Mar 2012 00:20:52 +0000 (19:20 -0500)
Adds support for viewing and searching on copy location groups in the
tpac.  Groups appear within the org unit selector, when the selector is
used in a search context.  Groups display below the owning org unit
similar to a child org unit.  Groups are displayed for all org units
that meet the following criteria:  search org unit, physical location,
patron home org unit, plus ancestors and descendents of each.

To support this, TPac gets a new "locg" CGI parameter, which contains
the org unit and copy location group.  It takes the form
org_id:group_id.  The TPac mod_perl code will extract this value and
popuplate the search_ou accordingly.  For consistency, we also use
ctx.search_ou instead of directly checking CGI.param('loc') within the
template environment.

This also includes a rewrite of the org_selector.tt2 template.  It
changes it from a recursive routine to a depth-first while loop.  I did
this mainly because the recursive approach was suffering from global
variable clobbering in the template environment.  In theory, this new
approach should be faster as well.

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Dan Scott <dan@coffeecode.net>
16 files changed:
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
Open-ILS/src/templates/opac/advanced.tt2
Open-ILS/src/templates/opac/myopac/prefs_notify.tt2
Open-ILS/src/templates/opac/parts/advanced/expert.tt2
Open-ILS/src/templates/opac/parts/advanced/search.tt2
Open-ILS/src/templates/opac/parts/org_selector.tt2
Open-ILS/src/templates/opac/parts/place_hold.tt2
Open-ILS/src/templates/opac/parts/preserve_params.tt2
Open-ILS/src/templates/opac/parts/record/copy_counts.tt2
Open-ILS/src/templates/opac/parts/record/copy_table.tt2
Open-ILS/src/templates/opac/parts/record/refworks.tt2
Open-ILS/src/templates/opac/parts/record/series.tt2
Open-ILS/src/templates/opac/parts/record/subjects.tt2
Open-ILS/src/templates/opac/parts/searchbar.tt2

index b1ffd40..76b3f63 100644 (file)
@@ -251,10 +251,13 @@ sub load_common {
             return $self->load_logout($self->apache->unparsed_uri);
         }
     }
-    $ctx->{search_ou} = $self->_get_search_lib();
 
+    $self->extract_copy_location_group_info;
+    $ctx->{search_ou} = $self->_get_search_lib();
     $self->staff_saved_searches_set_expansion_state if $ctx->{is_staff};
     $self->load_eg_cache_hash;
+    $self->load_copy_location_groups;
+    $self->staff_saved_searches_set_expansion_state if $ctx->{is_staff};
 
     return Apache2::Const::OK;
 }
@@ -303,8 +306,6 @@ sub get_physical_loc {
     return $self->cgi->cookie(COOKIE_PHYSICAL_LOC);
 }
 
-
-
 # -----------------------------------------------------------------------------
 # Log in and redirect to the redirect_to URL (or home)
 # -----------------------------------------------------------------------------
index b8907a6..ecf8ef0 100644 (file)
@@ -112,6 +112,10 @@ sub _prepare_biblio_search {
         $query .= " site($site)";
     }
 
+    if (my $grp = $ctx->{copy_location_group}) {
+        $query .= " location_groups($grp)";
+    }
+
     if(!$site) {
         ($site) = ($query =~ /site\(([^\)]+)\)/);
         $site ||= $ctx->{aou_tree}->()->shortname;
index f10ccb7..e672eac 100644 (file)
@@ -280,28 +280,35 @@ sub fetch_marc_xml_by_id {
 
 sub _get_search_lib {
     my $self = shift;
+    my $ctx = $self->ctx;
+
+    # avoid duplicate lookups
+    return $ctx->{search_ou} if $ctx->{search_ou};
+
+    my $loc = $ctx->{copy_location_group_org};
+    return $loc if $loc;
 
     # loc param takes precedence
-    my $loc = $self->cgi->param('loc');
+    $loc = $self->cgi->param('loc');
     return $loc if $loc;
 
-    if ($self->ctx->{user}) {
+    if ($ctx->{user}) {
         # See if the user has a search library preference
         my $lset = $self->editor->search_actor_user_setting({
-            usr => $self->ctx->{user}->id, 
+            usr => $ctx->{user}->id, 
             name => 'opac.default_search_location'
         })->[0];
         return OpenSRF::Utils::JSON->JSON2perl($lset->value) if $lset;
 
         # Otherwise return the user's home library
-        return $self->ctx->{user}->home_ou;
+        return $ctx->{user}->home_ou;
     }
 
     if ($self->cgi->param('physical_loc')) {
         return $self->cgi->param('physical_loc');
     }
 
-    return $self->ctx->{aou_tree}->()->id;
+    return $ctx->{aou_tree}->()->id;
 }
 
 # This is defensively coded since we don't do much manual reading from the
@@ -340,4 +347,51 @@ sub load_eg_cache_hash {
     }
 }
 
+# Extracts the copy location org unit and group from the 
+# "logc" param, which takes the form org_id:grp_id.
+sub extract_copy_location_group_info {
+    my $self = shift;
+    my $ctx = $self->ctx;
+    if (my $clump = $self->cgi->param('locg')) {
+        my ($org, $grp) = split(/:/, $clump);
+        $ctx->{copy_location_group_org} = $org;
+        $ctx->{copy_location_group} = $grp if $grp;
+    }
+}
+
+sub load_copy_location_groups {
+    my $self = shift;
+    my $ctx = $self->ctx;
+
+    # User can access to the search location groups at the current 
+    # search lib, the physical location lib, and the patron's home ou.
+    my @ctx_orgs = $ctx->{search_ou};
+    push(@ctx_orgs, $ctx->{physical_loc}) if $ctx->{physical_loc};
+    push(@ctx_orgs, $ctx->{user}->home_ou) if $ctx->{user};
+
+    my $grps = $self->editor->search_asset_copy_location_group([
+        {
+            opac_visible => 't',
+            owner => {
+                in => {
+                    select => {aou => [{
+                        column => 'id', 
+                        transform => 'actor.org_unit_full_path',
+                        result_field => 'id',
+                    }]},
+                    from => 'aou',
+                    where => {id => \@ctx_orgs}
+                }
+            }
+        },
+        {order_by => {acplg => 'pos'}}
+    ]);
+
+    my %buckets;
+    push(@{$buckets{$_->owner}}, $_) for @$grps;
+    $ctx->{copy_location_groups} = \%buckets;
+}
+
+
+
 1;
index 17cb74f..220c56f 100644 (file)
@@ -1,9 +1,9 @@
-[%-  PROCESS "opac/parts/header.tt2";
+[%- PROCESS "opac/parts/header.tt2";
     WRAPPER "opac/parts/base.tt2";
     INCLUDE "opac/parts/topnav.tt2";
     ctx.page_title = l("Advanced Search");
     pane = CGI.param("pane") || "advanced";
-    loc = CGI.param("loc");
+    loc = ctx.search_ou;
 -%]
     <div id="search-wrapper">
         <div id="search-box">
index 7d23021..aeaaa41 100644 (file)
@@ -51,7 +51,7 @@
                         [% IF ctx.user_setting_map.$setting; %] value='[% ctx.user_setting_map.$setting | html %]' [% END %]/>
                 </td>
             </tr>
-            [% IF ctx.get_org_setting(CGI.param('loc') OR ctx.aou_tree.id, 'sms.enable') == 1 %]
+            [% IF ctx.get_org_setting(ctx.search_ou, 'sms.enable') == 1 %]
             <tr>
                 <td>[% l('Notify by Text by default when a hold is ready for pickup?') %]</td>
                 <td>
index 24fbd1c..da8bba6 100644 (file)
@@ -1,4 +1,3 @@
-[% loc = CGI.param("loc") %]
 <form action="[% ctx.opac_root %]/results" method="GET">
     <div class="header_middle">[% l("Expert Search") %]</div>
     <input type="hidden" name="_special" value="1" />
index f14aaa8..6b62221 100644 (file)
@@ -67,7 +67,7 @@
                         <td valign='top'>
                             <strong>[% l("Search Library") %]</strong><br />
                             [% PROCESS "opac/parts/org_selector.tt2";
-                                PROCESS build_org_selector name='loc' value=ctx.search_ou %]
+                                PROCESS build_org_selector show_loc_groups=1 %]
                             <div style="position:relative;top:7px;">
                                 <input type='checkbox' name="modifier"
                                     value="available"[% CGI.param('modifier').grep('available').size ? ' checked="checked"' : '' %]
index 96bdcbf..a2c222e 100644 (file)
@@ -1,33 +1,66 @@
 [%
-    BLOCK build_org_selector_options;
-        disabled = '';
-        selected = '';
-        IF can_have_vols_only AND walker.ou_type.can_have_vols != 't';
-            disabled = 'disabled="disabled"';
-        ELSIF walker.id == value;
-            selected = 'selected="selected"';
-        END;
-        IF ctx.is_staff || walker.opac_visible == 't';
-%]
-            <option value='[% walker.id | uri %]' [% selected %] [% disabled %]>
-                [%
-                    pad = walker.ou_type.depth * 2;
-                    FOR idx IN [0..pad]; '&nbsp;'; END;
-                    walker.name | html;
-                %]
-            </option>
-            [% FOR child IN walker.children;
-                PROCESS build_org_selector_options walker=child value=value;
-            END;
+# Org Unit Selector Widget :
+#   PROCESS build_org_selector id='selector-id' name='selector-name' 
+#       value=org_id show_loc_groups=1/0 can_have_vols_only=1/0
+
+BLOCK build_org_selector;
+    node_stack = [{org => org_unit || ctx.aou_tree}];
+    IF !name; 
+        name = 'loc';
+        IF show_loc_groups; name = 'locg'; END;
+    END;
+    IF !value;
+        value = ctx.search_ou;
+        IF show_loc_groups; 
+            value = CGI.param('locg') || ctx.search_ou;
         END;
     END;
+    %]
 
-    # XXX TODO probably put this BLOCK somewhere else so it can be used widely.
-    # Org Unit Selector Widget :
-    #   PROCESS build_org_selector id='selector-id' name='selector-name'
-    BLOCK build_org_selector;
-%]
     <select [% IF id %] id='[% id %]' [% END %] name='[% name %]'>
-    [% PROCESS build_org_selector_options walker=(org_unit || ctx.aou_tree) value=value %]
+    [% 
+        WHILE node_stack.size > 0; 
+            node = node_stack.pop();
+            org_unit = node.org;
+            loc_grp = node.loc_grp;
+            ou_id = org_unit.id;
+            disabled = '';
+            selected = '';
+
+            NEXT UNLESS ctx.is_staff || org_unit.opac_visible == 't';
+
+            IF !loc_grp;
+                IF show_loc_groups;
+                    FOR grp IN ctx.copy_location_groups.$ou_id.reverse;
+                        node_stack.push({org => org_unit, loc_grp => grp});
+                    END;
+                END;
+                FOR child IN org_unit.children.reverse;
+                    node_stack.push({org => child});
+                END;
+            END;
+
+            node_value = ou_id;
+            IF loc_grp; node_value = node_value _ ':' _ loc_grp.id; END;
+
+            IF can_have_vols_only AND org_unit.ou_type.can_have_vols != 't';
+                disabled = 'disabled="disabled"';
+            ELSIF node_value == value;
+                selected = 'selected="selected"';
+            END %] 
+
+            <option value='[% node_value %]' [% selected %] [% disabled %]> 
+            [%
+                # loc_grp's are displayed as children of the current org
+                depth = org_unit.ou_type.depth;
+                IF loc_grp; depth = depth + 1; END;
+                pad = depth * 2;
+                FOR idx IN [0..pad]; '&nbsp;'; END;
+                loc_grp ? loc_grp.name : org_unit.name | html ;
+            %]
+            </option> 
+            [%
+        END;
+    %]
     </select>
-[%  END %]
+[% END %]
index bd95814..97ffe96 100644 (file)
@@ -83,7 +83,7 @@
                     [% l('Phone Number:') %]<input type="text" name="phone_notify" [% setting = 'opac.default_phone';
                     IF ctx.user_setting_map.$setting; %] value='[% ctx.user_setting_map.$setting | html %]' [% END %]/>
                 </blockquote>
-                [% IF ctx.get_org_setting(CGI.param('loc') OR ctx.aou_tree.id, 'sms.enable') == 1 %]
+                [% IF ctx.get_org_setting(ctx.search_ou, 'sms.enable') == 1 %]
                 <input type="checkbox" name="sms_notify_checkbox"
                     [% IF ctx.default_sms_notify %]checked="checked"[% END %]/>
                     [% l('Yes, by Text Messaging') %]<br/>
index 14fe5be..3517f58 100644 (file)
@@ -1,6 +1,6 @@
 [%- 
 UNLESS params;
-    params = ['loc', 'query', 'qtype', 'sort'];
+    params = ['locg', 'loc', 'query', 'qtype', 'sort'];
 END;
 FOR param IN params;
     IF CGI.param(param); %]
index 9d85ae7..defa0ae 100644 (file)
@@ -10,7 +10,7 @@
     <li>
     [% l('[quant,_1,copy,copies] at [_2].', ou_avail, ctx.get_aou(ou_id).name)
         | html %]
-    [%- IF ou_avail > 0 && ou_id != CGI.param('loc'); %]
+    [%- IF ou_avail > 0 && ou_id != ctx.search_ou; %]
     <a href="[% mkurl('', {loc => ou_id}); %]"
        title="[% l('Show copies at [_1]', ctx.get_aou(ou_id).name); %]">
        [%- l('(Show)'); %]</a>
index 8148dd9..240f970 100644 (file)
@@ -54,7 +54,7 @@ END;
                 org_name | html
             -%]
             </td>
-            <td header='copy_header_callnumber'>[% callnum | html %] [% IF ctx.get_org_setting(CGI.param('loc') OR ctx.aou_tree.id, 'sms.enable') == 1 %](<a href="[% mkurl(ctx.opac_root _ '/sms_cn', {copy_id => copy_info.id}) %]">Text</a>)[% END %]</td>
+            <td header='copy_header_callnumber'>[% callnum | html %] [% IF ctx.get_org_setting(ctx.search_ou, 'sms.enable') == 1 %](<a href="[% mkurl(ctx.opac_root _ '/sms_cn', {copy_id => copy_info.id}) %]">Text</a>)[% END %]</td>
             [%- IF has_parts == 'true' %]
             <td header='copy_header_part'>[% copy_info.part_label | html %]</td>
             [%- END %]
index 928ce90..ed02a74 100644 (file)
@@ -1,9 +1,6 @@
 [%
     # Default to the root of the org unit tree in the absence of a specific library
-    loc = ctx.aou_tree.id;
-    IF CGI.param('loc');
-        loc = CGI.param('loc');
-    END;
+    loc = ctx.search_ou;
 
     # Get the full name of the library
     ou_name = ctx.get_aou(loc).name | uri;
index 06def16..4695768 100644 (file)
@@ -1,6 +1,6 @@
 [% 
     series_tags = ['440', '490', '800', '810', '811', '830', '694']; 
-    loc = CGI.param('loc');
+    loc = ctx.search_ou;
 %]
 
 [% BLOCK render_series;
index a78bdef..59e26a5 100644 (file)
@@ -28,7 +28,6 @@
     ];
 
     BLOCK render_subject;
-        loc = CGI.param('loc') | uri;
         xpath = xpath || '//*[starts-with(@tag,"6")]';
         FOR node IN ctx.marc_xml.findnodes(xpath);
             all_terms = [];
index 3c12bd9..d0fa8ea 100644 (file)
@@ -28,7 +28,7 @@
                 [%- END # autosuggest enabled %] />
         </span>
         [%- INCLUDE "opac/parts/qtype_selector.tt2" id="qtype";
-            l(' in '); PROCESS build_org_selector name='loc' value=ctx.search_ou;
+            l(' in '); PROCESS build_org_selector show_loc_groups=1
     %]
     <span>
         <input id='search-submit-go' type="submit" value="[% l('Search') %]" alt="[% l('Search') %]" class="opac-button"