selected bib record or its associated copies and call_numbers/,
params => [
{ desc => 'Bib ID', type => 'number' },
+ { desc => q/Optional arguments. Supported arguments include:
+ "pickup_lib_descendant" -> limit holds to those whose pickup
+ library is equal to or is a child of the provided org unit/,
+ type => 'object'
+ }
],
return => {desc => 'Hold count', type => 'number'}
}
# XXX Need to add type I (and, soon, type P) holds to these counts
sub rec_hold_count {
- my($self, $conn, $target_id) = @_;
-
+ my($self, $conn, $target_id, $args) = @_;
+ $args ||= {};
my $mmr_join = {
mmrsm => {
}
+ if (my $pld = $args->{pickup_lib_descendant}) {
+ $query->{where}->{'+ahr'}->{pickup_lib} = {
+ in => {
+ select => {aou => [{
+ column => 'id',
+ transform => 'actor.org_unit_descendants',
+ result_field => 'id'
+ }]},
+ from => 'aou',
+ where => {id => $pld}
+ }
+ };
+ }
+
+
return new_editor()->json_query($query)->[0]->{count};
}
$self->load_copy_location_groups;
$self->staff_saved_searches_set_expansion_state if $ctx->{is_staff};
$self->load_search_filter_groups($ctx->{search_ou});
+ $self->load_org_util_funcs;
return Apache2::Const::OK;
}
sub get_hold_copy_summary {
my ($self, $rec_id, $org) = @_;
+ my $ctx = $self->ctx;
my $search = OpenSRF::AppSession->create('open-ils.search');
my $req1 = $search->request(
'open-ils.search.biblio.record.copy_count', $org, $rec_id);
+ # if org unit hiding applies, limit the hold count to holds
+ # whose pickup library is within our depth-scoped tree
+ my $count_args = {};
+ while ($org and $ctx->{org_within_hiding_scope}->($org)) {
+ $count_args->{pickup_lib_descendant} = $org;
+ $org = $ctx->{get_aou}->($org)->parent_ou;
+ }
+
$self->ctx->{record_hold_count} = $U->simplereq(
- 'open-ils.circ', 'open-ils.circ.bre.holds.count', $rec_id);
+ 'open-ils.circ', 'open-ils.circ.bre.holds.count',
+ $rec_id, $count_args);
$self->ctx->{copy_summary} = $req1->recv->content;
return $warn;
}
+sub load_org_util_funcs {
+ my $self = shift;
+ my $ctx = $self->ctx;
+
+ # evaluates to true if test_ou is within the same depth-
+ # scoped tree as ctx_ou. both ou's are org unit objects.
+ $ctx->{org_within_scope} = sub {
+ my ($ctx_ou, $test_ou, $depth) = @_;
+
+ return 1 if $ctx_ou->id == $test_ou->id;
+
+ if ($depth) {
+
+ # find the top-most ctx-org ancestor at the provided depth
+ while ($depth < $ctx_ou->ou_type->depth
+ and $ctx_ou->id != $test_ou->id) {
+ $ctx_ou = $ctx->{get_aou}->($ctx_ou->parent_ou);
+ }
+
+ # the preceeding loop may have landed on our org
+ return 1 if $ctx_ou->id == $test_ou->id;
+
+ } else {
+
+ return 1 if defined $depth; # $depth == 0;
+ }
+
+ for my $child (@{$ctx_ou->children}) {
+ return 1 if $ctx->{org_within_scope}->($child, $test_ou);
+ }
+
+ return 0;
+ };
+
+ # Returns true if the provided org unit is within the same
+ # org unit hiding depth-scoped tree as the physical location.
+ # Org unit hiding is based on the immutable physical_loc
+ # and is not meant to change as search/pref/etc libs change
+ $ctx->{org_within_hiding_scope} = sub {
+ my $org_id = shift;
+ my $ploc = $ctx->{physical_loc} or return 1;
+
+ my $depth = $ctx->{get_org_setting}->(
+ $ploc, 'opac.org_unit_hiding.depth');
+
+ return 1 unless $depth; # 0 or undef
+
+ return $ctx->{org_within_scope}->(
+ $ctx->{get_aou}->($ploc),
+ $ctx->{get_aou}->($org_id), $depth);
+
+ };
+
+ # Evaluates to true if the context org (defaults to get_library)
+ # is not within the hiding scope. Also evaluates to true if the
+ # user's pref_ou is set and it's out of hiding scope.
+ # Always evaluates to true when ctx.is_staff
+ $ctx->{org_hiding_disabled} = sub {
+ my $ctx_org = shift || $ctx->{search_ou};
+
+ return 1 if $ctx->{is_staff};
+
+ # beware locg values formatted as org:loc
+ $ctx_org =~ s/:.*//g;
+
+ return 1 if !$ctx->{org_within_hiding_scope}->($ctx_org);
+
+ return 1 if $ctx->{pref_ou} and $ctx->{pref_ou} != $ctx_org
+ and !$ctx->{org_within_hiding_scope}->($ctx->{pref_ou});
+
+ return 0;
+ };
+
+}
+
+
+
+
+
1;
);
END;
+ ou_hiding_disabled = ctx.org_hiding_disabled();
+
FOR volume IN xml.findnodes('//*[local-name()="volumes"]/*[local-name()="volume"]');
# Check volume visibility - could push this into XPath
status = copy.findnodes('./*[local-name()="status"]');
NEXT IF status.getAttribute('opac_visible') == 'false';
+ UNLESS ou_hiding_disabled;
+ # extract the circ_lib id from the circ_lib node
+ circ_lib = copy.findnodes('./*[local-name()="circ_lib"]');
+ circ_lib_id = circ_lib.getAttribute('id').replace('.*/', '');
+ NEXT UNLESS ctx.org_within_hiding_scope(circ_lib_id);
+ END;
+
holding = {
label => vol.label,
part_label => part_label,
IF !value;
value = loc_value;
END;
+
+ # if the selected org unit is out of hiding scope,
+ # disable the ou-hide scoping altogether.
+ hiding_disabled = ctx.org_hiding_disabled(value);
+
%]
<select [% IF id %] id='[% id %]' [% END %] name='[% name %]'>
END;
- # This org unit is not publicly visible (though its children may be).
+ # org is not publicly visible (though its children may be).
NEXT UNLESS ctx.is_staff OR visible;
+
+ # org is not within hiding scope (though its children may be).
+ NEXT UNLESS hiding_disabled OR ctx.org_within_hiding_scope(ou_id);
node_value = ou_id;
IF loc_grp;
[%- depths = ctx.copy_summary.size;
depth = 0;
displayed_ous = {};
+ ou_hiding_disabled = org_hiding_disabled();
WHILE depth < depths;
ou_avail = ctx.copy_summary.$depth.available;
ou_id = ctx.copy_summary.$depth.org_unit;
cp_org_unit = ctx.get_aou(ou_id);
- IF cp_org_unit.opac_visible == 'f' AND !ctx.is_staff;
+ skip_me = !ou_hiding_disabled AND !ctx.org_within_hiding_scope(ou_id);
+ IF (cp_org_unit.opac_visible == 'f' AND !ctx.is_staff) OR skip_me;
depth = depth + 1;
NEXT;
END;
<span id="rdetail_hold_counts">
<h2>[% l('Current holds') %]</h2>
<p>
- [%- l("[quant,_1,current hold,current holds] with [quant,_2,total copy,total copies].",
- ctx.record_hold_count, ctx.copy_summary.0.count) %]
+ [%
+ # If org hiding is enabled/relevant, only show
+ # counts for copies within the hiding scope.
+ count_entry = 0;
+ FOR count_chunk IN ctx.copy_summary;
+ IF ctx.org_within_hiding_scope(count_chunk.org_unit);
+ # always true when hiding is disabled
+ LAST;
+ END;
+ count_entry = count_entry + 1;
+ END;
+ l("[quant,_1,current hold,current holds] with [quant,_2,total copy,total copies].",
+ ctx.record_hold_count, ctx.copy_summary.$count_entry.count)
+ %]
</p>
</span>
[%- INCLUDE "opac/parts/record/copy_table.tt2" copies=ctx.copies %]
[%- depths = attrs.copy_counts.size;
depth = 0;
displayed_ous = {};
+ hiding_disabled = ctx.org_hiding_disabled();
WHILE depth < depths;
- ou_name = ctx.get_aou(attrs.copy_counts.$depth.org_unit).name;
+ org_unit = ctx.get_aou(attrs.copy_counts.$depth.org_unit);
+ ou_name = org_unit.name;
displayed_ous.$ou_name = 1;
- IF attrs.copy_counts.$depth.count > 0;
+ IF attrs.copy_counts.$depth.count > 0 AND (
+ hiding_disabled OR ctx.org_within_hiding_scope(org_unit.id));
%]
<div class="result_count">
[% IF ctx.get_aou(attrs.copy_counts.$depth.org_unit).opac_visible == 't' %]
END;
depth = attrs.plib_copy_counts.size - 1;
- ou_name = ctx.get_aou(attrs.plib_copy_counts.$depth.org_unit).name;
+ org_unit = ctx.get_aou(attrs.plib_copy_counts.$depth.org_unit);
+ ou_name = org_unit.name;
UNLESS displayed_ous.exists(ou_name);
%]
-[%- IF attrs.plib_copy_counts.$depth.count > 0; %]
+[%- IF attrs.plib_copy_counts.$depth.count > 0 AND (
+ hiding_disabled OR ctx.org_within_hiding_scope(org_unit.id)) %]
<div class="result_count preferred">[%
l('[_1] of [quant,_2,copy,copies] available at [_3].',
attrs.plib_copy_counts.$depth.available,
--- /dev/null
+TPAC Org Unit Hiding
+====================
+
+Adds support for the opac.org_unit_hiding.depth org unit setting to
+TPAC, which makes out-of-scope org units disappear (except when
+explicitly requested).
+
+Org unit hiding is based on the physical_loc (Physical Location) param /
+cookie, which is the closest analog to 'ol' (original location), from
+which it was based in the JSPAC.
+
+UI Changes
+----------
+
+ * All search org unit selectors
+ * Holds pickup lib selector
+ * Copy summary in search results page
+ * Copy list in search results page
+ * Copy summary in record detail page (which controls the copy grid).
+ * Hold summary in record detail page