TPac: batch holds from on-the-fly lists and bookbags
authorBill Erickson <berick@esilibrary.com>
Thu, 18 Aug 2011 21:14:38 +0000 (17:14 -0400)
committerBill Erickson <berick@esilibrary.com>
Mon, 22 Aug 2011 20:06:47 +0000 (16:06 -0400)
* Enable the 'Place Hold' action in the drop-down for bookbag and
on-the-fly list items

* Place hold form now support lists of targets and will report error
conditions for each target

* Batch and individual hold overrides supported where appropriate

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Container.pm
Open-ILS/web/css/skin/default/opac/style.css
Open-ILS/web/templates/default/opac/myopac/lists.tt2
Open-ILS/web/templates/default/opac/parts/anon_list.tt2
Open-ILS/web/templates/default/opac/parts/place_hold.tt2
Open-ILS/web/templates/default/opac/parts/place_hold_result.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/opac/place_hold.tt2

index 698f467..547c578 100644 (file)
@@ -34,11 +34,10 @@ sub prepare_extended_user_info {
 # permission to override?  XXX Should the permission check be scoped to a
 # given org_unit context?
 sub test_could_override {
-    my ($self) = @_;
-    my $event = $self->ctx->{"hold_failed_event"};
+    my ($self, $event) = @_;
 
     return 0 unless $event;
-    return 1 if $self->editor->allowed($event . ".override");
+    return 1 if $self->editor->allowed($event->{textcode} . ".override");
     return 1 if $event->{"fail_part"} and
         $self->editor->allowed($event->{"fail_part"} . ".override");
     return 0;
@@ -46,7 +45,7 @@ sub test_could_override {
 
 # Find out whether we care that local copies are available
 sub local_avail_concern {
-    my ($self, $allowed, $hold_target, $hold_type, $pickup_lib) = @_;
+    my ($self, $hold_target, $hold_type, $pickup_lib) = @_;
 
     my $would_block = $self->ctx->{get_org_setting}->
         ($pickup_lib, "circ.holds.hold_has_copy_at.block");
@@ -56,7 +55,7 @@ sub local_avail_concern {
                 not $self->cgi->param("override")
     ) unless $would_block;
 
-    if ($allowed->{"success"} and ($would_block or $would_alert)) {
+    if ($would_block or $would_alert) {
         my $args = {
             "hold_target" => $hold_target,
             "hold_type" => $hold_type,
@@ -396,186 +395,211 @@ sub load_place_hold {
     my $gos = $ctx->{get_org_setting};
     my $e = $self->editor;
     my $cgi = $self->cgi;
-    $self->ctx->{page} = 'place_hold';
 
-    $ctx->{hold_target} = $cgi->param('hold_target');
+    $self->ctx->{page} = 'place_hold';
+    my @targets = $cgi->param('hold_target');
     $ctx->{hold_type} = $cgi->param('hold_type');
-
     $ctx->{default_pickup_lib} = $e->requestor->home_ou; # unless changed below
 
+    $logger->info("Looking at hold targets: @targets");
+
+    # if the staff client provides a patron barcode, fetch the patron
     if (my $bc = $self->cgi->cookie("patron_barcode")) {
-        # passed in from staff client
         $ctx->{patron_recipient} = $U->simplereq(
             "open-ils.actor", "open-ils.actor.user.fleshed.retrieve_by_barcode",
             $self->editor->authtoken, $bc
-        ) or return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
+        ) or return Apache2::Const::HTTP_BAD_REQUEST;
 
         $ctx->{default_pickup_lib} = $ctx->{patron_recipient}->home_ou;
     }
 
     my $request_lib = $e->requestor->ws_ou;
+    my @hold_data;
+    $ctx->{hold_data} = \@hold_data;
+
+    my $type_dispatch = {
+        T => sub {
+            my $recs = $e->batch_retrieve_biblio_record_entry(\@targets, {substream => 1});
+            for my $id (@targets) { # force back into the correct order
+                my ($rec) = grep {$_->id eq $id} @$recs;
+                push(@hold_data, {target => $rec, record => $rec});
+            }
+        },
+        V => sub {
+            my $vols = $e->batch_retrieve_asset_call_number([
+                \@targets, {
+                    "flesh" => 1,
+                    "flesh_fields" => {"acn" => ["record"]}
+                }
+            ], {substream => 1});
 
-    # XXX check for failure of the retrieve_* methods called below, and
-    # possibly replace all the if,elsif with a dispatch table (meh, elegance)
-
-    my $target_field;
-    if ($ctx->{hold_type} eq 'T') {
-        $target_field = "titleid";
-        $ctx->{record} = $e->retrieve_biblio_record_entry($ctx->{hold_target});
-    } elsif ($ctx->{hold_type} eq 'V') {
-        $target_field = "volume_id";
-        my $vol = $e->retrieve_asset_call_number([
-            $ctx->{hold_target}, {
-                "flesh" => 1,
-                "flesh_fields" => {"acn" => ["record"]}
+            for my $id (@targets) { 
+                my ($vol) = grep {$_->id eq $id} @$vols;
+                push(@hold_data, {target => $vol, record => $vol->record});
             }
-        ]);
-        $ctx->{record} = $vol->record;
-    } elsif ($ctx->{hold_type} eq 'C') {
-        $target_field = "copy_id";
-        my $copy = $e->retrieve_asset_copy([
-            $ctx->{hold_target}, {
-                "flesh" => 2,
-                "flesh_fields" => {
-                    "acn" => ["record"],
-                    "acp" => ["call_number"]
+        },
+        C => sub {
+            my $copies = $e->batch_retrieve_asset_copy([
+                \@targets, {
+                    "flesh" => 2,
+                    "flesh_fields" => {
+                        "acn" => ["record"],
+                        "acp" => ["call_number"]
+                    }
                 }
+            ], {substream => 1});
+
+            for my $id (@targets) { 
+                my ($copy) = grep {$_->id eq $id} @$copies;
+                push(@hold_data, {target => $copy, record => $copy->call_number->record});
             }
-        ]);
-        $ctx->{record} = $copy->call_number->record;
-    } elsif ($ctx->{hold_type} eq 'I') {
-        $target_field = "issuanceid";
-        my $iss = $e->retrieve_serial_issuance([
-            $ctx->{hold_target}, {
-                "flesh" => 2,
-                "flesh_fields" => {
-                    "siss" => ["subscription"], "ssub" => ["record_entry"]
+        },
+        I => sub {
+            my $isses = $e->batch_retrieve_serial_issuance([
+                \@targets, {
+                    "flesh" => 2,
+                    "flesh_fields" => {
+                        "siss" => ["subscription"], "ssub" => ["record_entry"]
+                    }
                 }
+            ], {substream => 1});
+
+            for my $id (@targets) { 
+                my ($iss) = grep {$_->id eq $id} @$isses;
+                push(@hold_data, {target => $iss, record => $iss->subscription->record_entry});
             }
-        ]);
-        $ctx->{record} = $iss->subscription->record_entry;
-    }
-    # ...
+        }
+        # ...
 
-    $ctx->{marc_xml} = XML::LibXML->new->parse_string($ctx->{record}->marc);
+    }->{$ctx->{hold_type}}->();
 
-    if (my $pickup_lib = $cgi->param('pickup_lib')) {
-        my $requestor = $e->requestor->id;
-        my $usr; 
+    # caller sent bad target IDs or the wrong hold type
+    return Apache2::Const::HTTP_BAD_REQUEST unless @hold_data;
 
-        if ((not $ctx->{"is_staff"}) or
-            ($cgi->param("hold_usr_is_requestor"))) {
-            $usr = $requestor;
-        } else {
-            my $actor = create OpenSRF::AppSession("open-ils.actor");
-            $usr = $actor->request(
-                "open-ils.actor.user.retrieve_id_by_barcode_or_username",
-                $e->authtoken, $cgi->param("hold_usr")
-            )->gather(1);
-
-            if (defined $U->event_code($usr)) {
-                $ctx->{hold_failed} = 1;
-                $ctx->{hold_failed_event} = $usr;
-            }
-            $actor->kill_me;
-        }
+    # generate the MARC xml for each record
+    $_->{marc_xml} = XML::LibXML->new->parse_string($_->{record}->marc) for @hold_data;
 
-        my $args = {
-            patronid => $usr,
-            $target_field => $ctx->{"hold_target"},
-            pickup_lib => $pickup_lib,
-            hold_type => $ctx->{"hold_type"},
-            depth => 0, # XXX
-        };
+    my $pickup_lib = $cgi->param('pickup_lib');
+    # no pickup lib means no holds placement
+    return Apache2::Const::OK unless $pickup_lib;
 
-        my $allowed = $U->simplereq(
-            'open-ils.circ',
-            'open-ils.circ.title_hold.is_possible',
-            $e->authtoken, $args
-        );
+    $ctx->{hold_attempt_made} = 1;
 
-        $logger->info('hold permit result ' . OpenSRF::Utils::JSON->perl2JSON($allowed));
+    # Give the original CGI params back to the user in case they
+    # want to try to override something.
+    $ctx->{orig_params} = $cgi->Vars;
+    delete $ctx->{orig_params}{submit};
+    delete $ctx->{orig_params}{hold_target};
 
-        my ($local_block, $local_alert) = $self->local_avail_concern(
-            $allowed, $args->{$target_field}, $args->{hold_type}, $pickup_lib
-        );
+    my $usr = $e->requestor->id;
 
-        # Give the original CGI params back to the user in case they
-        # want to try to override something.
-        $ctx->{orig_params} = $cgi->Vars;
+    if ($ctx->{is_staff} and !$cgi->param("hold_usr_is_requestor")) {
+        # find the real hold target
 
-        if ($local_block) {
+        $usr = $U->simplereq(
+            'open-ils.actor', 
+            "open-ils.actor.user.retrieve_id_by_barcode_or_username",
+            $e->authtoken, $cgi->param("hold_usr"));
+
+        if (defined $U->event_code($usr)) {
             $ctx->{hold_failed} = 1;
-            $ctx->{hold_local_block} = 1;
+            $ctx->{hold_failed_event} = $usr;
+        }
+    }
+
+    # First see if we should warn/block for any holds that 
+    # might have locally available items.
+    for my $hdata (@hold_data) {
+        my ($local_block, $local_alert) = $self->local_avail_concern(
+            $hdata->{target}->id, $ctx->{hold_type}, $pickup_lib);
+    
+        if ($local_block) {
+            $hdata->{hold_failed} = 1;
+            $hdata->{hold_local_block} = 1;
         } elsif ($local_alert) {
-            $ctx->{hold_failed} = 1;
-            $ctx->{hold_local_alert} = 1;
-        } elsif ($allowed->{success}) {
-            my $hold = Fieldmapper::action::hold_request->new;
-
-            $hold->pickup_lib($pickup_lib);
-            $hold->requestor($requestor);
-            $hold->usr($usr);
-            $hold->target($ctx->{hold_target});
-            $hold->hold_type($ctx->{hold_type});
-            # frozen, expired, etc..
-
-            my $method =  "open-ils.circ.holds.create";
-            $method .= ".override" if $cgi->param("override");
-
-            my $stat = $U->simplereq(
-                "open-ils.circ", $method, $e->authtoken, $hold
-            );
+            $hdata->{hold_failed} = 1;
+            $hdata->{hold_local_alert} = 1;
+        }
+    }
 
-            # The following did not cover all the possible return values of
-            # open-ils.circ.holds.create
-            #if($stat and $stat > 0) {
-            if ($stat and (not ref $stat) and $stat > 0) {
-                # if successful, return the user to the requesting page
-                $self->apache->log->info(
-                    "Redirecting back to " . $cgi->param('redirect_to')
-                );
-
-                # We also clear the patron_barcode (from the staff client)
-                # cookie at this point (otherwise it haunts the staff user
-                # later). XXX todo make sure this is best; also see that
-                # template when staff mode calls xulG.opac_hold_placed()
-                return $self->generic_redirect(
-                    undef,
-                    $self->cgi->cookie(
-                        -name => "patron_barcode",
-                        -path => "/",
-                        -secure => 1,
-                        -value => "",
-                        -expires => "-1h"
-                    )
-                );
 
-            } else {
-                $ctx->{hold_failed} = 1;
+    my $method = 'open-ils.circ.holds.test_and_create.batch';
+    $method .= '.override' if $cgi->param('override');
+
+    my @create_targets = map {$_->{target}->id} (grep { !$_->{hold_failed} } @hold_data);
 
-                delete $ctx->{orig_params}{submit};
+    if(@create_targets) {
+
+        my $bses = OpenSRF::AppSession->create('open-ils.circ');
+        my $breq = $bses->request( 
+            $method, 
+            $e->authtoken, 
+            {   patronid => $usr, 
+                pickup_lib => $pickup_lib, 
+                hold_type => $ctx->{hold_type}
+            }, 
+            \@create_targets
+        );
+
+        while (my $resp = $breq->recv) {
+
+            $resp = $resp->content;
+            $logger->info('batch hold placement result: ' . OpenSRF::Utils::JSON->perl2JSON($resp));
+
+            if ($U->event_code($resp)) {
+                $ctx->{general_hold_error} = $resp;
+                last;
+            }
 
-                if (ref $stat eq 'ARRAY') {
-                    $ctx->{hold_failed_event} = shift @$stat;
-                } elsif (defined $U->event_code($stat)) {
-                    $ctx->{hold_failed_event} = $stat;
+            my ($hdata) = grep {$_->{target}->id eq $resp->{target}} @hold_data;
+            my $result = $resp->{result};
+
+            if ($U->event_code($result)) {
+                # e.g. permission denied
+                $hdata->{hold_failed} = 1;
+                $hdata->{hold_failed_event} = $result;
+
+            } else {
+                
+                if(not ref $result and $result > 0) {
+                    # successul hold returns the hold ID
+
+                    $hdata->{hold_success} = $result; 
+    
                 } else {
-                    $self->apache->log->info(
-                        "attempt to create hold returned $stat"
-                    );
+                    # hold-specific failure event 
+                    $hdata->{hold_failed} = 1;
+                    $hdata->{hold_failed_event} = $result->{last_event};
+                    $hdata->{could_override} = $self->test_could_override($hdata->{hold_failed_event});
                 }
-
-                $ctx->{could_override} = $self->test_could_override;
             }
-        } else { # hold *check* failed
-            $ctx->{hold_failed} = 1; # XXX process the events, etc
-            $ctx->{hold_failed_event} = $allowed->{last_event};
         }
 
-        # hold permit failed
+        $bses->kill_me;
     }
 
+    # stay on the current page and display the results
+    return Apache2::Const::OK if 
+        (grep {$_->{hold_failed}} @hold_data) or $ctx->{general_hold_error};
+
+    # if successful, do some cleanup and return the 
+    # user to the requesting page.
+
+    # We also clear the patron_barcode (from the staff client)
+    # cookie at this point (otherwise it haunts the staff user
+    # later). XXX todo make sure this is best; also see that
+    # template when staff mode calls xulG.opac_hold_placed()
+    return $self->generic_redirect(
+        undef,
+        $self->cgi->cookie(
+            -name => "patron_barcode",
+            -path => "/",
+            -secure => 1,
+            -value => "",
+            -expires => "-1h"
+        )
+    );
+
     return Apache2::Const::OK;
 }
 
@@ -1164,10 +1188,10 @@ sub load_myopac_bookbags {
 }
 
 
-# actions are create, delete, show, hide, rename, add_rec, delete_item
+# actions are create, delete, show, hide, rename, add_rec, delete_item, place_hold
 # CGI is action, list=list_id, add_rec/record=bre_id, del_item=bucket_item_id, name=new_bucket_name
 sub load_myopac_bookbag_update {
-    my ($self, $action, $list_id) = @_;
+    my ($self, $action, $list_id, @hold_recs) = @_;
     my $e = $self->editor;
     my $cgi = $self->cgi;
 
@@ -1175,7 +1199,7 @@ sub load_myopac_bookbag_update {
     $list_id ||= $cgi->param('list');
 
     my @add_rec = $cgi->param('add_rec') || $cgi->param('record');
-    my @del_item = $cgi->param('del_item');
+    my @selected_item = $cgi->param('selected_item');
     my $shared = $cgi->param('shared');
     my $name = $cgi->param('name');
     my $success = 0;
@@ -1190,6 +1214,23 @@ sub load_myopac_bookbag_update {
         $success = $U->simplereq('open-ils.actor', 
             'open-ils.actor.container.create', $e->authtoken, 'biblio', $list)
 
+    } elsif($action eq 'place_hold') {
+
+        # @hold_recs comes from anon lists redirect; selected_itesm comes from existing buckets
+        unless (@hold_recs) {
+            if (@selected_item) {
+                my $items = $e->search_container_biblio_record_entry_bucket_item({id => \@selected_item});
+                @hold_recs = map { $_->target_biblio_record_entry } @$items;
+            }
+        }
+                
+        return Apache2::Const::OK unless @hold_recs;
+        $logger->info("placing holds from list page on: @hold_recs");
+
+        my $url = $self->ctx->{opac_root} . '/place_hold?hold_type=T';
+        $url .= ';hold_target=' . $_ for @hold_recs;
+        return $self->generic_redirect($url);
+
     } else {
 
         $list = $e->retrieve_container_biblio_record_entry_bucket($list_id);
@@ -1234,7 +1275,7 @@ sub load_myopac_bookbag_update {
         }
 
     } elsif($action eq 'del_item') {
-        foreach (@del_item) {
+        foreach (@selected_item) {
             $success = $U->simplereq(
                 'open-ils.actor',
                 'open-ils.actor.container.item.delete', $e->authtoken, 'biblio', $_
index 7f87104..be45ffc 100644 (file)
@@ -60,6 +60,10 @@ sub load_mylist_add {
 sub load_mylist_move {
     my $self = shift;
     my @rec_ids = $self->cgi->param('record');
+    my $action = $self->cgi->param('action') || '';
+
+    return $self->load_myopac_bookbag_update('place_hold', undef, @rec_ids)
+        if $action eq 'place_hold';
 
     my ($cache_key, $list) = $self->fetch_mylist;
     return $self->mylist_action_redirect unless $cache_key;
@@ -73,7 +77,7 @@ sub load_mylist_move {
         $cache_key, ANON_CACHE_MYLIST, \@keep
     );
 
-    if ($self->ctx->{user} and $self->cgi->param('action') =~ /^\d+$/) {
+    if ($self->ctx->{user} and $action =~ /^\d+$/) {
         # in this case, action becomes list_id
         $self->load_myopac_bookbag_update('add_rec', $self->cgi->param('action'));
         # XXX TODO: test for failure of above
index cd496ab..f5354ff 100644 (file)
@@ -1023,3 +1023,9 @@ a.dash-link:hover { text-decoration: underline !important; }
 
 .results-paginator-selected { color: red; }
 .inactive-hold { background: #e5e5e5; }
+
+#hold-items-list li { margin-bottom: 20px; }
+.hold-items-list-title { font-size: 120%; }
+.hold-items-list-problem { color: red; }
+
+.big-strong {font-weight: bold; font-size: 120%; }
index 87aec45..e94f94f 100644 (file)
                         <input type="checkbox" onclick="
                             var inputs=document.getElementsByTagName('input'); 
                             for (i = 0; i < inputs.length; i++) { 
-                                if (inputs[i].name == 'del_item' && !inputs[i].disabled && inputs[i].getAttribute('bbag') == [% bbag.id %]) 
+                                if (inputs[i].name == 'selected_item' && !inputs[i].disabled && inputs[i].getAttribute('bbag') == [% bbag.id %]) 
                                     inputs[i].checked = this.checked;}"/>
 
                         </td>
                         <td width="1%" class="nowrap">
                             <select class="opac-auto-179" name="action">
                                 <option>[% l('-- Actions for this list --') %]</option>
-                                <!-- XXX not ready yet<option value="hold">[% l('Place Hold') %]</option> -->
+                                <option value="place_hold">[% l('Place Hold') %]</option>
                                 <option value="del_item">[% l('Remove Items') %]</option>
                             </select>
                             <input type="submit" value="[% l('Go') %]" />
                         attrs = {marc_xml => ctx.bookbags_marc_xml.$rec_id};
                         PROCESS get_marc_attrs args=attrs %]
                     <tr>
-                        <td class="item_list_padding" style="padding-left: 10px;"><input type="checkbox" name="del_item" value="[% item.id %]" bbag='[% bbag.id %]'/></td>
+                        <td class="item_list_padding" style="padding-left: 10px;"><input type="checkbox" name="selected_item" value="[% item.id %]" bbag='[% bbag.id %]'/></td>
                         <td class="item_list_padding" style="padding-left: 5px;">[% attrs.title | html %]</td>
                         <td class="item_list_padding">[% attrs.author | html %]</td>
                     </tr>
index 6f91af7..89273a6 100644 (file)
@@ -31,7 +31,7 @@
                         <td width="1%" class="nowrap">
                             <select class="opac-auto-179" name="action">
                                 <option>[% l('-- Actions for this list --') %]</option>
-                                <!-- XXX not ready <option value="hold">[% l('Place Hold') %]</option> -->
+                                <option value="place_hold">[% l('Place Hold') %]</option>
                                 <option value="delete">[% l('Remove Items') %]</option>
                                 [% IF ctx.user AND ctx.bookbags.size %]
                                 <optgroup label="Move selected items to">
index 903cbb6..d9fb32c 100644 (file)
 [%  PROCESS "default/opac/parts/misc_util.tt2";
-    attrs = {marc_xml => ctx.marc_xml};
-    PROCESS get_marc_attrs args=attrs;
-
     PROCESS "default/opac/parts/hold_error_messages.tt2";
 %]
-<div>
-    <div id='holds_box' class='canvas' style='margin-top: 6px;'>
-        [% IF ctx.hold_success %]
-        <div><big><strong>[% l("Hold was successfully placed"); %]</strong></big></div>
-        [% ELSIF ctx.hold_failed %]
-        <div><big><strong>[% l("Hold was not successfully placed"); %]</strong></big></div>
-            [% IF ctx.hold_local_block %]
-            <div>[% l("There is already a copy available at your local library.") %]</div>
-            [% ELSIF ctx.hold_failed_event || ctx.hold_local_alert %]
-        <div>
-            <strong>[% l('Problem:') %]</strong>
-            <span title="[% ctx.hold_failed_event.textcode | html %]">
-                <em>[%
-                        fail_part_key = ctx.hold_failed_event.payload.fail_part;
-                        event_key = ctx.hold_failed_event.textcode;
-
-                        # display:
-                        l(FAIL_PART_MSG_MAP.$fail_part_key) ||
-                        l(EVENT_MSG_MAP.$event_key) ||
-                        l(ctx.hold_failed_event.desc) ||
-                        ctx.hold_failed_event.payload.fail_part ||
-                        ctx.hold_failed_event.textcode ||
-                        (ctx.hold_local_alert ?
-                            l("There is already a copy available at your local library.") :
-                            l("Unknown problem")) %]</em>
-            </span>
 
-            [% IF ctx.hold_copy_available %]<p>
-            [%  l('Find a copy in the shelving location, "[_1]."', locname) | html %]
-            </p>[% END %]
-
-            [% IF ctx.could_override || ctx.hold_local_alert %]
-            <p>
-                <big>[% l("You have permission to place this hold anyway.") %]</big>
-                <br />
-                [% l("Click submit below to override the system's objection and place your hold.") %]
-            </p>
-            <form method="POST">
-                <input type="hidden" type="name" name="override" value="1" />
-                [% FOR k IN ctx.orig_params.keys %]
-                <input type="hidden" name="[% k %]" value="[% ctx.orig_params.$k | uri %]" />
-                [% END %]
-                <input type="image" name="submit" value="submit" title="[% l('Submit') %]"
-                    alt="[% l('Submit') %]" src="[% ctx.media_prefix %]/images/btnSubmit.png" />
-            </form>
-            [% END %]
-        </div>
-            [% END;
-        ELSIF ctx.hold_local_block;
-            l("There is already a copy available at your local library");
-        ELSE %]
-        <form method="POST">
-            <br/>
-            <input type="hidden" name="hold_target"
-                value="[% CGI.param('hold_target') | html %]" />
-            <input type="hidden" name="hold_type"
-                value="[% CGI.param('hold_type') | html %]" />
-            [%
-                new_redirect_to = ctx.referer;
-                IF new_redirect_to.match('redirect_to');
-                    new_redirect_to = 'https://' _ ctx.hostname _ ctx.opac_root _ '/home';
-                ELSE;
-                    new_redirect_to = new_redirect_to | replace('^http:', 'https:');
-                END;
-            %]
-            <input type="hidden" name="redirect_to"
-                value="[% new_redirect_to | html %]" />
-            <h1>Place Hold</h1>
-            [% IF ctx.is_staff %]
-            <p class="staff-hold">
-                <input type="radio" id="hold_usr_is_requestor_not"
+<div id='holds_box' class='canvas' style='margin-top: 6px;'>
+    <h1>[% l('Place Hold') %]</h1>
+    <form method="POST">
+        <input type="hidden" name="hold_type" value="[% CGI.param('hold_type') | html %]" />
+        [%#
+            new_redirect_to = ctx.referer;
+            IF new_redirect_to.match('redirect_to');
+                new_redirect_to = 'https://' _ ctx.hostname _ ctx.opac_root _ '/home';
+            ELSE;
+                new_redirect_to = new_redirect_to | replace('^http:', 'https:');
+            END;
+        %]
+        <input type="hidden" name="redirect_to" value="[% CGI.param('redirect_to') || CGI.referer | html %]" />
+
+        [% IF ctx.is_staff %]
+        <p class="staff-hold">
+            <input type="radio" id="hold_usr_is_requestor_not"
+                onchange="staff_hold_usr_input_disabler(this);"
+                name="hold_usr_is_requestor" value="0"
+                [% IF ctx.patron_recipient; ' checked="checked"'; END %] />
+            <label for="hold_usr_is_requestor_not">
+                [% l("Place hold for patron by barcode:") %]
+            </label>
+            <input type="text" name="hold_usr" id="hold_usr_input" value="[% ctx.patron_recipient.card.barcode | html %]" /><br />[%# XXX multi-barcode users? %]
+            <span>
+                <input type="radio" id="hold_usr_is_requestor"
                     onchange="staff_hold_usr_input_disabler(this);"
-                    name="hold_usr_is_requestor" value="0"
-                    [% IF ctx.patron_recipient; ' checked="checked"'; END %]
-                    />
-                <label for="hold_usr_is_requestor_not">
-                    [% l("Place hold for patron by barcode:") %]
+                    name="hold_usr_is_requestor" value="1" />
+                <label for="hold_usr_is_requestor">
+                    [% l("Place this hold for me ([_1] [_2])", ctx.user.first_given_name, ctx.user.family_name) | html %]
                 </label>
-                <input type="text" name="hold_usr" id="hold_usr_input" value="[% ctx.patron_recipient.card.barcode | html %]" /><br />[%# XXX multi-barcode users? %]
-                <span>
-                    <input type="radio" id="hold_usr_is_requestor"
-                        onchange="staff_hold_usr_input_disabler(this);"
-                        name="hold_usr_is_requestor" value="1" />
-                    <label for="hold_usr_is_requestor">
-                        [% l("Place this hold for me ([_1] [_2])", ctx.user.first_given_name, ctx.user.family_name) | html %]
-                    </label>
-                </span>
-            </p>
-            [% END %]
-            <p>
-                [%  title = attrs.title | html; libname = ctx.get_aou(ctx.default_pickup_lib).name | html %]
-                [% | l(title, libname) %]
-                You would like to place a hold on <strong><q>[_1]</q></strong>.<br />
-                If this is correct, confirm your pickup location and click <strong>SUBMIT</strong>.
-                [% END %]
-            </p>
-            <p>
-                [% l('Pickup location:') %]
-                [% PROCESS "default/opac/parts/org_selector.tt2";
-                    PROCESS build_org_selector name='pickup_lib' value=ctx.default_pickup_lib id='pickup_lib' can_have_vols_only=1 %]
-            </p>
-            <p>
-                [% |l %]If you use the Traveling Library Center (TLC) and ABC Express
-                services, please select "Outreach" to have the item delivered
-                during your scheduled visit.[% END %]
-            </p>
-            <input type="image" name="submit" value="submit" title="[% l('Submit') %]"
-                alt="[% l('Submit') %]" src="[% ctx.media_prefix %]/images/btnSubmit.png" />
-            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-            <a href="javascript:history.go(-1);" id="holds_cancel"><img
-                alt="[% l('Cancel') %]" src="[% ctx.media_prefix %]/images/btnCancel.png" /></a>
-        </form>
-        <br /><br />
+            </span>
+        </p>
+        [% END %]
+
+        <!-- loop through the holds and display status of request where appropriate -->
+        <ul id='hold-items-list'>
+        [% FOR hdata IN ctx.hold_data;
+            attrs = {marc_xml => hdata.marc_xml};
+            PROCESS get_marc_attrs args=attrs %]
+            <li>
+                <input type="hidden" name="hold_target" value="[% hdata.target.id | html %]" />
+                <div class='hold-items-list-title'>[% attrs.title_extended | html %]</div>
+            </li>
+        [% END %]
+        </ul>
+
         <p>
-            [% |l %]* If you need your item today, and it is checked in at your
-            library, please place your hold and then call your library to set it
-            aside. Placing a hold without calling the library will increase your
-            wait time.[% END %]
-            <br /><a href="#">[% l('Library phone numbers.') %]</a>
+            [% l('Pickup location:') %]
+            [% PROCESS "default/opac/parts/org_selector.tt2";
+                PROCESS build_org_selector name='pickup_lib' value=ctx.default_pickup_lib id='pickup_lib' can_have_vols_only=1 %]
         </p>
         <p>
-            [% |l %]* For best possible service, we recommend keeping 
-            a printed copy of your most recent holds list.[% END %]
+            [% |l %]If you use the Traveling Library Center (TLC) and ABC Express
+            services, please select "Outreach" to have the item delivered
+            during your scheduled visit.[% END %]
         </p>
-        [% END %] <!-- ctx.hold_success -->
-        <table width='90%' border="1" class="hide_me">
-            <tbody>
-                <tr>
-                    <td class='holds_cell color_1' 
-                        align='center' colspan='2'>[% l("Create / Edit a Hold") %]</td>
-                </tr>
-                <tr>
-                    <td class='holds_cell'>[% l("Recipient") %]:</td>
-                    <td class='holds_cell' id='holds_recipient'> </td>
-                </tr>
-                <tr>
-                    <td class='holds_cell'>[% l("Title:") %]</td>
-                    <!-- <td class='holds_cell' id='holds_title'> </td> -->
-                </tr>
-                <tr>
-                    <td class='holds_cell'>[% l("Author") %]</td>
-                    <td class='holds_cell' id='holds_author'> </td>
-                </tr>
-                <tr>
-                    <td class='holds_cell'>[% l("Format") %]</td>
-                    <td class='holds_cell' id='holds_format'> </td>
-                </tr>
-                <tr id='hold_physical_desc_row'>
-                    <td class='holds_cell'>[% l("Physical Description:") %]</td>
-                    <td class='holds_cell' id='holds_physical_desc'> </td>
-                </tr>
-
-                <tr class='hide_me' id='holds_cn_row'>
-                    <td class='holds_cell'>[% l("Call Number:") %]</td>
-                    <td class='holds_cell'><b id='holds_cn'/> </td>
-                </tr>
-
-                <tr class='hide_me' id='holds_copy_row'>
-                    <td class='holds_cell'>[% l("Copy Barcode:") %]</td>
-                    <td class='holds_cell'><b id='holds_copy'/> </td>
-                </tr>
-
-                <tr class='hide_me' id='holds_type_row'>
-                    <td class='holds_cell'>[% l("Hold Type:") %]</td>
-                    <td class='holds_cell hide_me' id='holds_is_cn'>
-                        <b>[% l("Volume Hold") %]</b>
-                    </td>
-                    <td class='holds_cell hide_me' id='holds_is_copy'>
-                        <b>[% l("Copy Hold") %]</b>
-                    </td>
-                </tr>
-                <tr>
-                    <td class='holds_cell'>[% l("Contact telephone number") %]:</td>
-                    <td class='holds_cell'>
-                        <input id='holds_phone' size='13' maxlength='12'/>
-                        <span style='margin-left: 4px; font-size: 7pt;'>
-                            [% l("(XXX-YYY-ZZZZ)") %]
-                        </span>
-                    </td>
-                </tr>
-                <tr>
-                    <td class='holds_cell'>[% l("Enable phone notifications for this hold?") %]</td>
-                    <td class='holds_cell'>
-                        <input type='checkbox' id='holds_enable_phone'
-                            checked='checked' />
-                    </td>
-                </tr>
-                <tr>
-                    <td class='holds_cell'>[% l("Contact email address") %]:</td>
-                    <td class='holds_cell' id='holds_email'> 
-                        <span class='hide_me' id='holds.no_email'>
-                           ([% l("Patron has no configured email address") %])<br/>
-                           ([% l("See") %] <a class='classic_link' id='holds.no_email.my_account'>[% l("My Account") %]</a> [% l("for setting your email address") %])
-                        </span>
-                        <span class='hide_me' id='holds.no_email.xul'>
-                           [% l("(Patron has no configured email address)") %] 
-                        </span>
-                    </td>
-                </tr>
-                <tr>
-                    <td class='holds_cell'>[% l("Enable email notifications for this hold?") %]</td>
-                    <td class='holds_cell'>
-                        <input type='checkbox' id='holds_enable_email'
-                            checked='checked'/>
-                    </td>
-                </tr>
-                <!--
-                <tr id='holds_depth_selector_row' class='hide_me'>
-                    <td class='holds_cell'>Hold Range</td>
-                    <td class='holds_cell'>
-                        <select id='holds_depth_selector'></select>
-                    </td>
-                </tr>
-                -->
-                <tr>
-                    <td class='holds_cell'>[% l("Pickup location") %]</td>
-                    <td class='holds_cell'>
-                        <!-- <select id='holds_org_selector'> </select> -->
-                    </td>
-                </tr>
-
-                <tr>
-                    <td class='holds_cell'>[% l("Expiration date") %]</td>
-                    <td class='holds_cell'>
-                        <input size='10' maxlength='10'
-                         id='holds_expire_time' />
-                    </td>
-                </tr>
-
-                <tr>
-                    <td class='holds_cell'>
-                        [% l("Suspend this hold") %]
-                        <a class='classic_link'
-                            href='#'>[% l("(Help)") %]</a>
-                        </td>
-                    <td class='holds_cell'>
-                        <input type='checkbox' id='holds_frozen_chkbox' /> 
-                    </td>
-                </tr>
-                <tr id='hold_frozen_thaw_row' class='hide_me'>
-                    <td class='holds_cell'>
-                        <!-- XXX TODO there used to be script here dealing with
-                        frozen holds -->
-                        [% l("Automatically activate hold on:") %]
-                    </td>
-                    <td class='holds_cell'>
-                        <input size='10' maxlength='10'
-                            id='holds_frozen_thaw_input' />
-                    </td>
-                </tr>
-
-                <tr id='holds_alt_formats_row_extras' class='hide_me'>
-                    <td colspan='2' align='center'>
-                        <div style='padding: 8px;'>
-                            <a class='classic_link' href='#'
-                                style='padding: 5px;'>[% l("Advanced Hold Options") %]</a>
-                        </div>
-                    </td>
-                </tr>
-
-                <tr id='holds_alt_formats_row' class='hide_me'>
-
-                    <td class='holds_cell'>
-                        <div style='margin-bottom: 5px;'>
-                            <span>[% l("Acceptable Alternative Formats:") %] </span>
-                            <span><a class='classic_link red' href='#'>[% l("(Help)") %]</a></span>
-                        </div>
-                        <div>[% l("(control-click to select multiple formats)") %]</div>
-                    </td>
-
-                    <td class='holds_cell'>
-                        <select id='hold_alt_form_selector' multiple='multiple' style='width: 14em;'>
-                            <option value='at'    class='hide_me'>[% l("Books") %]</option>
-                            <option value='at-d' class='hide_me'>[% l("Large Print Books") %]</option>
-                            <option value='i'        class='hide_me'>[% l("Audiobooks") %]</option>
-                            <option value='g'        class='hide_me'>[% l("Video Recordings") %]</option>
-                            <option value='j'        class='hide_me'>[% l("Music") %]</option>
-                        </select>
-                    </td>
-                </tr>
-                <tr>
-                    <td class='holds_cell' align='center' colspan='2'>
-                        <!-- <button id='holds_submit'>[% l("Place Hold") %]</button> -->
-                        <button class='hide_me' id='holds_update'>[% l("Update Hold") %]</button>
-                        <span style='padding: 20px;'> </span>
-                        <!-- <button id='holds_cancel'>[% l("Cancel") %]</button> -->
-                    </td>
-                </tr>
-            </tbody>
-        </table>
-        <span class='hide_me' id='holds_bad_phone'>
-            [% l("The phone number does not have the correct format. The expected format is XXX-YYY-ZZZZ") %]
-        </span>
-        <span class='hide_me' id='hold_not_allowed'>
-            [% |l %]No items were found that could fulfill the requested holds.  
-                It's possible that choosing a different format will result in a successful hold.  
-                It is also possible that you have exceeded the number of allowable holds.  
-                For further information, please consult your local librarian.[% END %]
-        </span>
-    </div>
-    <div id="anonListTable" class="hide_me" style="margin-top: 6px;">
-        <select id="holdsCacheSel" class="hide_me"></select><br />
-        <a href="#">Place hold on selected</a><br />
-        <a href="#">Remove selected</a>
-        
-        <table id="temp_list_holds" cellpadding='0' cellspacing='0' border='0'
-            style="margin-top:10px;">
-            <tr>
-                <td width="1%" style="padding-left:10px;">
-                    <input type='checkbox' title='Select All'
-                        id='anon_selector' />
-                </td>
-                <td width="1%">
-                </td>
-                <td width="98%" style="padding-left:40px;">
-                    <strong>Title</strong>
-                </td>
-            </tr>
-        </table>
-        <table width='100%' style="margin-left:7px;margin-bottom:10px;">
-            <thead>
-                <tr><td width='20'></td><td width='30'></td><td></td></tr>
-            </thead>
-            <tbody id="anonListParent">
-                <tr id="anonListTemp">
-                  <td><input type='checkbox' name='anon_selector' /></td>
-                  <td name="curr_row"></td>
-                  <td name="title"></td>
-                </tr>
-            </tbody>
-        </table>
-        <a href="#">Back to search results</a>
-    </div>
-
-    <span class='hide_me' id='format_words'>
-        <span name='at'>[% l("Books") %]</span>
-        <span name='at-d'>[% l("Large Print Books") %]</span>
-        <span name='i'>[% l("Audiobooks") %]</span>
-        <span name='g'>[% l("Video Recordings") %]</span>
-        <span name='j'>[% l("Music") %]</span>
-        <span name='m'>[% l("Electronic Resources") %]</span>
-    </span>
-
-    <span class='hide_me' id='holds_explain_adv'>
-        [% |l %]If you wish to broaden the scope of your hold to include other versions of this title, 
-        select the formats that would be acceptable.  The first available copy will be sent to you.[% END %]
-    </span>
-
-    <span class='hide_me' id='holds_pick_good_org'>[% l("Please select a physical location where your hold can be delivered.") %]</span>
-    <span class='hide_me' id='hold_dup_exists'>[% l("A hold already exists on the requested item.") %]</span>
-    <span class='hide_me' id='hold_dup_exists_override'>[% l("A hold already exists on the requested item. Would you like to create the hold anyway?") %]</span>
-
-    <span id='hold_failed_patron_barred' class='hide_me'>
-        [% |l %]PATRON BARRED. Please see any notes in the "Staff Notes" section of your 
-        "My Account" page or contact your local library.[% END %]
-    </span>
-
-    <span id='invalid_hold' class='hide_me'>
-        [% |l %]This hold is no longer valid. It's likely that the target for the hold was 
-        deleted from the system.  Please cancel this hold and place a new one.[% END %]
-    </span>
-    <span id='holds_invalid_recipient' class='hide_me'>[% l("The patron barcode entered as the hold recipient is invalid.") %]</span>
+        <input type="image" name="submit" value="submit" title="[% l('Submit') %]"
+            alt="[% l('Submit') %]" src="[% ctx.media_prefix %]/images/btnSubmit.png" />
+        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+        <a href="javascript:history.go(-1);" id="holds_cancel"><img
+            alt="[% l('Cancel') %]" src="[% ctx.media_prefix %]/images/btnCancel.png" /></a>
+    </form>
+    <br /><br />
+    <p>
+        [% |l %]* If you need your item today, and it is checked in at your
+        library, please place your hold and then call your library to set it
+        aside. Placing a hold without calling the library will increase your
+        wait time.[% END %]
+        <br /><a href="#">[% l('Library phone numbers.') %]</a>
+    </p>
+    <p>
+        [% |l %]* For best possible service, we recommend keeping 
+        a printed copy of your most recent holds list.[% END %]
+    </p>
 </div>
+
diff --git a/Open-ILS/web/templates/default/opac/parts/place_hold_result.tt2 b/Open-ILS/web/templates/default/opac/parts/place_hold_result.tt2
new file mode 100644 (file)
index 0000000..0f03fa8
--- /dev/null
@@ -0,0 +1,103 @@
+[%  PROCESS "default/opac/parts/misc_util.tt2";
+    PROCESS "default/opac/parts/hold_error_messages.tt2";
+    override_ok = [];
+    fail_count = 0;
+%]
+
+<!-- TODO: CSS for big/strong-->
+
+<div id='holds_box' class='canvas' style='margin-top: 6px;'>
+    <h1>[% l('Place Hold') %]</h1>
+
+    <ul id='hold-items-list'>
+
+    [% FOR hdata IN ctx.hold_data;
+        attrs = {marc_xml => hdata.marc_xml};
+        PROCESS get_marc_attrs args=attrs %]
+        <li>
+            <div class='hold-items-list-title'>[% attrs.title_extended | html %]</div>
+            <div>
+                [% IF hdata.hold_success %]
+
+                <div>[% l("Hold was successfully placed"); %]</div>
+
+                [% ELSIF hdata.hold_failed;
+                    fail_count = fail_count + 1 %]
+
+
+                    <div><big><strong>[% l("Hold was not successfully placed"); %]</strong></big></div>
+                    [% IF hdata.hold_local_block %]
+                        <div>[% l("There is already a copy available at your local library.") %]</div>
+                    [% ELSIF hdata.hold_failed_event || hdata.hold_local_alert %]
+                        <div>
+                            <span class='hold-items-list-problem'>[% l('Problem:') %]</span>
+                            <span title="[% hdata.hold_failed_event.textcode | html %]">
+                                <em>[%
+                                        fail_part_key = hdata.hold_failed_event.payload.fail_part;
+                                        event_key = hdata.hold_failed_event.textcode;
+
+                                        # display:
+                                        l(FAIL_PART_MSG_MAP.$fail_part_key) ||
+                                        l(EVENT_MSG_MAP.$event_key) ||
+                                        l(hdata.hold_failed_event.desc) ||
+                                        hdata.hold_failed_event.payload.fail_part ||
+                                        hdata.hold_failed_event.textcode ||
+                                        (hdata.hold_local_alert ?
+                                            l("There is already a copy available at your local library.") :
+                                            l("Unknown problem")) | html
+                                    %]</em>
+                                    [% IF event_key == 'PERM_FAILURE' %]
+                                    <div>[% l('Permission: "[_1]"', hdata.hold_failed_event.ilsperm) | html %]</div>
+                                    [% END %]
+                            </span>
+
+                            [% IF hdata.hold_copy_available %]
+                                <p>[%  l('Find a copy in the shelving location, "[_1]."', locname) | html %]</p>
+                            [% END %]
+
+                            [% IF hdata.could_override || hdata.hold_local_alert %]
+                                [% override_ok.push(hdata.target.id) %]
+                                <p>
+                                    <big>[% l("You have permission to place this hold anyway.") %]</big>
+                                    <br />
+                                    [% l("Click submit below to override and place your hold.") %]
+                                </p>
+                                <form method="POST">
+                                    <input type="hidden" type="name" name="override" value="1" />
+                                    <input type="hidden" name="hold_target" value="[% hdata.target.id | html %]" />
+                                    [% FOR k IN ctx.orig_params.keys %]
+                                    <input type="hidden" name="[% k %]" value="[% ctx.orig_params.$k | html %]" />
+                                    [% END %]
+                                    <input type="image" name="submit" value="submit" title="[% l('Submit') %]"
+                                        alt="[% l('Submit') %]" src="[% ctx.media_prefix %]/images/btnSubmit.png" />
+                                </form>
+                            [% END %]
+                        </div>
+                    [% ELSIF hdata.hold_local_block;
+                        l("There is already a copy available at your local library");
+                    END;
+                END %]
+            </div>
+        </li>
+    [% END %]
+    <div>
+        [% IF fail_count > 1 AND fail_count == override_ok.size %]
+            <hr/>
+            <form method="POST">
+                <input type="hidden" type="name" name="override" value="1" />
+                [% FOR k IN ctx.orig_params.keys %]
+                <input type="hidden" name="[% k %]" value="[% ctx.orig_params.$k | html %]" />
+                [% END %]
+                [% FOR target IN override_ok %]
+                    <input type="hidden" name="hold_target" value="[% target | html %]" />
+                [% END %]
+                <div class='big-strong'>[% l('Override all holds') %]</div>
+                <br/>
+                <input type="image" name="submit" value="submit" title="[% l('Submit') %]"
+                    alt="[% l('Submit') %]" src="[% ctx.media_prefix %]/images/btnSubmit.png" />
+            </form>
+        [% END %]
+    </div>
+    </ul>
+</div>
+
index 3e6d2bd..0badd80 100644 (file)
@@ -9,7 +9,11 @@
     <div id="content-wrapper">
         <div id="main-content">
             <div class="common-full-pad"></div>        
-            [% INCLUDE "default/opac/parts/place_hold.tt2" %]
+            [% IF ctx.hold_attempt_made %]
+                [% INCLUDE "default/opac/parts/place_hold_result.tt2" %]
+            [% ELSE %]
+                [% INCLUDE "default/opac/parts/place_hold.tt2" %]
+            [% END %]
             <div class="common-full-pad"></div>        
         </div>
     </div>