Foundational work for temporary/anon lists and per-user lists (bookbags)
authorsenator <lebbeous@esilibrary.com>
Thu, 10 Mar 2011 22:24:08 +0000 (17:24 -0500)
committersenator <lebbeous@esilibrary.com>
Thu, 10 Mar 2011 22:24:08 +0000 (17:24 -0500)
There were already some features in EGCatLoader for adding and
deleting items from lists, but there's more to do, as that code only
dealt with numeric IDs, and we need records avaiable. Also, to avoid
search bots creating temporary/anon lists all the time, and just because
the following is good practice, our "add to my list" links need to be
forms that POST, not links that GET.

Etc., etc.; more to come.

12 files changed:
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Container.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
Open-ILS/web/css/skin/default/opac/semiauto.css
Open-ILS/web/css/skin/default/opac/style.css
Open-ILS/web/templates/default/opac/mylist.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/opac/myopac/lists.tt2
Open-ILS/web/templates/default/opac/parts/anon_list.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/opac/parts/result/table.tt2
Open-ILS/web/templates/default/opac/results.tt2

index 597bf96..941efa1 100644 (file)
@@ -84,12 +84,17 @@ sub load {
 
     my $path = $self->apache->path_info;
 
+    (undef, $self->ctx->{mylist}) = $self->fetch_mylist unless
+        $path =~ /opac\/my(opac\/lists|list)/;
+
     return $self->load_simple("home") if $path =~ /opac\/home/;
     return $self->load_simple("advanced") if $path =~ /opac\/advanced/;
     return $self->load_rresults if $path =~ /opac\/results/;
     return $self->load_record if $path =~ /opac\/record/;
+
     return $self->load_mylist_add if $path =~ /opac\/mylist\/add/;
     return $self->load_mylist_del if $path =~ /opac\/mylist\/del/;
+    return $self->load_mylist if $path =~ /opac\/mylist/;
     return $self->load_cache_clear if $path =~ /opac\/cache\/clear/;
 
     # ----------------------------------------------------------------
index 5adff47..7fcdfa9 100644 (file)
@@ -489,19 +489,31 @@ sub load_myopac_bookbags {
     my $self = shift;
     my $e = $self->editor;
     my $ctx = $self->ctx;
-    my $limit = $self->cgi->param('limit') || 0;
-    my $offset = $self->cgi->param('offset') || 0;
 
-    my $args = {order_by => {cbreb => 'name'}};
-    $args->{limit} = $limit if $limit;
-    $args->{offset} = $offset if $offset;
+    my $rv = $self->load_mylist;
+    return $rv if $rv ne Apache2::Const::OK;
 
-    (undef, $ctx->{mylist}) = $self->fetch_mylist;
+    my $args = {
+        order_by => {cbreb => 'name'},
+        limit => $self->cgi->param('limit') || 10,
+        offset => $self->cgi->param('limit') || 0
+    };
 
     $ctx->{bookbags} = $e->search_container_biblio_record_entry_bucket([
         {owner => $self->editor->requestor->id, btype => 'bookbag'},
-        $args
-    ]);
+        # XXX what to do about the possibility of really large bookbags here?
+        {"flesh" => 1, "flesh_fields" => {"cbreb" => ["items"]}, %$args}
+    ]) or return $e->die_event;
+
+    # get unique record IDs
+    my %rec_ids = ();
+    foreach my $bbag (@{$ctx->{bookbags}}) {
+        foreach my $item_id (map { $_->id } @{$bbag->items}) {
+            $rec_ids{$item_id} = 1;
+        }
+    }
+
+    $ctx->{bookbags_marc_xml} = $self->fetch_marc_xml_by_id(keys %rec_ids);
 
     return Apache2::Const::OK;
 }
index 49954e7..5a73fdb 100644 (file)
@@ -13,7 +13,7 @@ use constant ANON_CACHE_MYLIST => 'mylist';
 # Retrieve the users cached records AKA 'My List'
 # Returns an empty list if there are no cached records
 sub fetch_mylist {
-    my $self = shift;
+    my ($self, $with_marc_xml) = @_;
 
     my $list = [];
     my $cache_key = $self->cgi->cookie(COOKIE_ANON_CACHE);
@@ -32,8 +32,12 @@ sub fetch_mylist {
     }
 
     $self->apache->log->info("Found anon-cache list [@$list]");
+    my $marc_xml;
+    if ($with_marc_xml) {
+        $marc_xml = $self->fetch_marc_xml_by_id($list);
+    }
 
-    return ($cache_key, $list);
+    return ($cache_key, $list, $marc_xml);
 }
 
 
@@ -108,4 +112,12 @@ sub mylist_action_redirect {
     );
 }
 
+sub load_mylist {
+    my ($self) = shift;
+    (undef, $self->ctx->{mylist}, $self->ctx->{mylist_marc_xml}) =
+        $self->fetch_mylist(1);
+
+    return Apache2::Const::OK;
+}
+
 1;
index e2ea613..84c68a7 100644 (file)
@@ -26,6 +26,7 @@ sub _prepare_biblio_search_basics {
 
         # This stuff probably will need refined or rethought to better handle
         # the weird things Real Users will surely type in.
+        $contains = "" unless defined $contains; # silence warning
         if ($contains eq 'nocontains') {
             $query =~ s/"//g;
             $query = ('"' . $query . '"') if index $query, ' ';
@@ -138,43 +139,16 @@ sub load_rresults {
 
     return Apache2::Const::OK if @$rec_ids == 0;
 
-    my $cstore1 = OpenSRF::AppSession->create('open-ils.cstore');
-    my $bre_req = $cstore1->request(
-        'open-ils.cstore.direct.biblio.record_entry.search', {id => $rec_ids});
-
-    my $search = OpenSRF::AppSession->create('open-ils.search');
-    my $facet_req = $search->request('open-ils.search.facet_cache.retrieve', $results->{facet_key}, 10);
-
-    my @data;
-    while(my $resp = $bre_req->recv) {
-        my $bre = $resp->content; 
-
-        # XXX farm out to multiple cstore sessions before loop, then collect after
-        my $copy_counts = $e->json_query(
-            {from => ['asset.record_copy_count', 1, $bre->id, 0]})->[0];
-
-        push(@data,
-            {
-                bre => $bre,
-                marc_xml => XML::LibXML->new->parse_string($bre->marc),
-                copy_counts => $copy_counts
-            }
-        );
-    }
-
-    $cstore1->kill_me;
+    my ($facets, @data) = $self->get_records_and_facets($rec_ids, $results->{facet_key});
 
     # shove recs into context in search results order
-    for my $rec_id (@$rec_ids) { 
+    for my $rec_id (@$rec_ids) {
         push(
             @{$ctx->{records}},
             grep { $_->{bre}->id == $rec_id } @data
         );
     }
 
-    my $facets = $facet_req->gather(1);
-
-    $facets->{$_} = {cmf => $ctx->{find_cmf}->($_), data => $facets->{$_}} for keys %$facets;  # quick-n-dirty
     $ctx->{search_facets} = $facets;
 
     return Apache2::Const::OK;
index 63b5213..a6e408d 100644 (file)
@@ -137,4 +137,73 @@ sub generic_redirect {
     return Apache2::Const::REDIRECT;
 }
 
+sub get_records_and_facets {
+    my ($self, $rec_ids, $facet_key) = @_;
+
+    my $cstore = OpenSRF::AppSession->create('open-ils.cstore');
+    my $bre_req = $cstore->request(
+        'open-ils.cstore.direct.biblio.record_entry.search', {id => $rec_ids}
+    );
+
+    my $search = OpenSRF::AppSession->create('open-ils.search');
+    my $facet_req = $search->request(
+        'open-ils.search.facet_cache.retrieve', $facet_key, 10
+    ) if $facet_key;
+
+    my @data;
+    while (my $resp = $bre_req->recv) {
+        my $bre = $resp->content;
+
+        # XXX farm out to multiple cstore sessions before loop,
+        # then collect after
+        my $copy_counts = $self->editor->json_query(
+            {from => ['asset.record_copy_count', 1, $bre->id, 0]}
+        )->[0];
+
+        push @data, {
+            bre => $bre,
+            marc_xml => XML::LibXML->new->parse_string($bre->marc),
+            copy_counts => $copy_counts
+        };
+    }
+
+    $cstore->kill_me;
+
+    my $facets;
+    if ($facet_key) {
+        $facets = $facet_req->gather(1);
+
+        $facets->{$_} = {
+            cmf => $self->ctx->{find_cmf}->($_),
+            data => $facets->{$_}
+        } for keys %$facets;    # quick-n-dirty
+    } else {
+        $facets = undef;
+    }
+
+    return ($facets, @data);
+}
+
+sub fetch_marc_xml_by_id {
+    my ($self, $id_list) = @_;
+    $id_list = [$id_list] unless ref($id_list);
+    return {} if scalar(grep { defined $_ } @$id_list) < 1;
+
+    # I'm just sure there needs to be some more efficient way to get all of
+    # this.
+    my $results = $self->editor->json_query({
+        "select" => {"bre" => ["id", "marc"]},
+        "from" => {"bre" => {}},
+        "where" => {"id" => $id_list}
+    }) or return $self->editor->die_event;
+
+    my $marc_xml = {};
+    for my $r (@$results) {
+        $marc_xml->{$r->{"id"}} =
+            (new XML::LibXML)->parse_string($r->{"marc"});
+    }
+
+    return $marc_xml;
+}
+
 1;
index f652eaa..84748d0 100644 (file)
@@ -41,7 +41,7 @@
 .bold { font-weight: bold; }
 .opac-auto-057 { font-weight: bold; padding: 5px; margin: 5px; width: 100%; }
 .opac-auto-058 { font-weight: bold; padding-left: 10px; }
-.opac-auto-059 { font-weight: bold; padding-right: 10px; }
+#anon_list_name { font-weight: bold; padding-right: 10px; }
 .opac-auto-060 { font-weight: normal; }
 .opac-auto-061 { height: 0px; border-top: 1px solid #b7b7b7; border-bottom: 1px solid #d4d4d4; margin: 15px 0px; }
 .small-height { height: 10px; }
index 6780de3..324e2ff 100644 (file)
@@ -940,3 +940,12 @@ div.select-wrapper:hover {
     padding-left: 5em; background-color: #d7d7d7; margin: 2ex 0;
 }
 .row-remover { position: relative; top: 1px; vertical-align: middle; }
+.subtle-button {
+    background-color: #ffffff;
+    color: #003399; text-decoration: none;
+    font-size: 12px;
+    padding: 0; border: 0; margin: 0;
+    vertical-align: middle;
+}
+.subtle-button:hover { text-decoration: underline; cursor: pointer; }
+.no-dec:hover { text-decoration: none; }
diff --git a/Open-ILS/web/templates/default/opac/mylist.tt2 b/Open-ILS/web/templates/default/opac/mylist.tt2
new file mode 100644 (file)
index 0000000..f115441
--- /dev/null
@@ -0,0 +1,16 @@
+[%  PROCESS "default/opac/parts/header.tt2";
+    PROCESS "default/opac/parts/marc_misc.tt2";
+    WRAPPER "default/opac/parts/base.tt2";
+    INCLUDE "default/opac/parts/topnav.tt2";
+    ctx.page_title = l("Record Detail") %]
+    <div id="search-wrapper">
+        [% INCLUDE "default/opac/parts/utils.tt2" %]
+        [% INCLUDE "default/opac/parts/searchbar.tt2" %]
+    </div>
+    <div id="content-wrapper">
+        <div id="main-content">
+            [% INCLUDE "default/opac/parts/anon_list.tt2" %]
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[% END %]
index 6d1a6db..3da5062 100644 (file)
@@ -1,36 +1,10 @@
 [%  PROCESS "default/opac/parts/header.tt2";
+    PROCESS "default/opac/parts/marc_misc.tt2";
     WRAPPER "default/opac/parts/base.tt2" +
         "default/opac/parts/myopac/base.tt2";
     myopac_page = "lists"  %]
-<div style="margin-top: 6px;margin-left:20px;width:250px;padding:5px;" id="mylist_div">
-
-    <!-- new list creation -->
-    <div style="padding-bottom: 7px;">
-        <h2 style="font-weight:normal;">[% l('Create new list') %]</h2>
-        [% l('Enter the name of the new list:') %]<br/>
-        <input type="text" id="mylist_new" />
-    </div>
-
-    <table cellpadding="0" cellspacing="10" border="0">
-        <tr>
-            <td>
-                [% l('Share this list?') %]
-                <a href="#"><img alt="Sharing Help"     
-                    src="[% ctx.media_prefix %]/images/question-mark.png" /></a>
-            </td>
-            <td>
-                <input type="radio" value="0" name="shareList" checked="checked" />[% l('No') %]
-                <br/>
-                <input type="radio" value="1" name="shareList"/>[% l('Yes') %]
-            </td>
-        </tr>
-    </table>
-    <a href="#"><img alt="[% l('Submit') %]" src="[% ctx.media_prefix %]/images/btnSubmit.png"/></a>
-    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-    <a href="#"><img alt="[% l('Cancel') %]" src="[% ctx.media_prefix %]/images/btnCancel.png" /></a>
-</div>
 <div id='myopac_bookbag_div' style="padding:5px;">
-    <div class="header_middle">
+    <!-- <div class="header_middle">
         <span id="acct_holds_header" style="float:left;">[% l('My Lists') %]</span>
         <span style="float:right;"><a class="hide_me" href="#">[% l('Export List') %]</a></span>
     </div>
                 <a href="#"><img alt="[% l('Save') %]" src="[% ctx.media_prefix %]/images/save-btn.png"/></a>
             </div>
         </div>
-    </div>
-    <div class="clear-both pad-top-ten">
-        <a href="#">[% l('+ Add new list') %]</a>
-    </div>
+    </div> -->
     <div id="temp_wrapper">
-        <div id='acct_list_template2' class="hide_me">
-            <div style="width:100%">
-                <table cellpadding="0" cellspacing="0" border="0">
-                    <tr>
-                        <td style="font-weight:bold;padding-right:10px;" id='anon_list_name'>
-                            [% l('Temporary List') %]
-                        </td>
-                        <td>
-                            <a href="#"><img
-                                alt="[% l('Anonymous List Help') %]"
-                                src="[% ctx.media_prefix %]/images/question-mark.png" /></a>
-                        </td>
-                    </tr>
-                </table>
-                <div class="float-right"></div>
-                <div class="clear-both pad-bottom-five"></div>
-            </div>
-    
-            <table id="acct_list_header_anon" cellpadding='0' cellspacing='0' border='0'>
-                <tr>
-                    <td width="1%" style="padding-left:10px;"><input type="checkbox"/>
-                    </td>
-                    <td width="98%" style="padding-left:5px;">Title</td>
-                    <td width="1%">
-                        <select style='width:175px;margin-right:11px;'
-                            name="list_actions" id="sel_all_list_anon">
-                            <option value="0">[% l('-- Actions for this list --') %]</option>
-                            <option value="hold">[% l('Place Hold') %]</option>
-                            <option value="remove">[% l('Remove Items') %]</option>
-                        </select>
-                    </td>
-                </tr>
-            </table>
-            <table cellpadding='0' cellspacing='5' border='0'
-                width='91%' style='margin-left:5px;margin-top:5px;'>
-                <tbody id='anon_list_tbody'></tbody>
-            </table>
-            <br /><br />
-        </div>
+        [% INCLUDE "default/opac/parts/anon_list.tt2" %]
+        [% IF ctx.bookbags.size %]
         <div id='acct_lists_prime'>
             <div id='acct_list_template'>
                 <div style="width:100%">
                         </td>
                         <td width="98%" style="padding-left:5px;">Title</td>
                         <td width="1%">
-                            <select style='width:175px;margin-right:11px;' name="list_actions">
+                            <select class="opac-auto-179" name="list_actions">
                                 <option value="0">[% l('-- Actions for this list --') %]</option>
                                 <option value="hold">[% l('Place Hold') %]</option>
                                 <option value="remove">[% l('Remove Items') %]</option>
                 <br /><br />
             </div>
         </div>
+        [% END %]
     </div>
     <div id='myopac_delete_bookbag_warn' class='hide_me'>
         [% l("This will remove the selected bookbag and all items contained within the bookbag.  Are you sure you wish to continue?") %]
             </tr>
         </tbody>
     </table>
-    <table width='100%' class='data_grid data_grid_center hide_me' style='margin-top: 10px;'>
-        <thead>
-            <tr><td>[% l("Create a new Bookbag") %]</td></tr>
-        </thead>
-        <tbody>
-            <tr>
-                <td> 
-                    <span style='padding-right: 5px;'>
-                        [% l("Enter the name of the new Bookbag: ") %]
-                    </span>
-                    <input id='myopac_bookbag_new_name' type='text' /> 
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <span style='padding: 5px;'>[% l("Share this Bookbag") %]</span>
-                    <a class='classic_link' href='#'><b>[% l("(Help)") %]</b></a>
-                    <span>[% l("Yes") %]</span>
-                    <input type='radio' name='bb_public' id='bb_public_yes'/>
-                    <span>[% l("No") %]</span>
-                    <input type='radio' name='bb_public' id='bb_public_no' checked='checked'/>
-                    <input style='padding-left: 10px;' type='submit' value='[% l("Submit") %]' />
-                </td>
-            </tr>
-        </tbody>
-    </table>
     <div style='width: 99%; text-align: center'>
         <b id='myopac_bookbag_items_name'> </b>
     </div>
         [% l("Bookbag successfully updated") %]
     </span>
 </div>
+<div style="margin-top: 6px;margin-left:20px;width:250px;padding:5px;" id="mylist_div">
+
+    <!-- new list creation -->
+    <div style="padding-bottom: 7px;">
+        <h2 style="font-weight:normal;">[% l('Create new list') %]</h2>
+        [% l('Enter the name of the new list:') %]<br/>
+        <input type="text" id="mylist_new" />
+    </div>
+
+    <table cellpadding="0" cellspacing="10" border="0">
+        <tr>
+            <td>
+                [% l('Share this list?') %]
+                <a href="#"><img alt="[% l('Sharing Help') %]"
+                    src="[% ctx.media_prefix %]/images/question-mark.png" /></a>
+            </td>
+            <td>
+                <input type="radio" value="0" name="shareList" checked="checked" />[% l('No') %]
+                <br/>
+                <input type="radio" value="1" name="shareList"/>[% l('Yes') %]
+            </td>
+        </tr>
+    </table>
+    <a href="#"><img alt="[% l('Submit') %]" src="[% ctx.media_prefix %]/images/btnSubmit.png"/></a>
+    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+    <a href="#"><img alt="[% l('Cancel') %]" src="[% ctx.media_prefix %]/images/btnCancel.png" /></a>
+</div>
 [% END %]
diff --git a/Open-ILS/web/templates/default/opac/parts/anon_list.tt2 b/Open-ILS/web/templates/default/opac/parts/anon_list.tt2
new file mode 100644 (file)
index 0000000..2d1d1e3
--- /dev/null
@@ -0,0 +1,48 @@
+        [% IF ctx.mylist.size %]
+        <div id='acct_list_template2'>
+            <div style="width:100%">
+                <table cellpadding="0" cellspacing="0" border="0">
+                    <tr>
+                        <td id='anon_list_name'>
+                            [% l('Temporary List') %]
+                        </td>
+                        <td>
+                            <a href="#"><img
+                                alt="[% l('Anonymous List Help') %]"
+                                src="[% ctx.media_prefix %]/images/question-mark.png" /></a>
+                        </td>
+                    </tr>
+                </table>
+                <div class="float-right"></div>
+                <div class="clear-both pad-bottom-five"></div>
+            </div>
+            <table id="acct_list_header_anon" cellpadding='0' cellspacing='0' border='0'>
+                <tr>
+                    <td width="1%" style="padding-left:10px;"><input type="checkbox"/>
+                    </td>
+                    <td width="98%" style="padding-left:5px;">[% l('Title') %]</td>
+                    <td width="1%">
+                        <select class="opac-auto-179"
+                            name="list_actions" id="sel_all_list_anon">
+                            <option value="0">[% l('-- Actions for this list --') %]</option>
+                            <option value="hold">[% l('Place Hold') %]</option>
+                            <option value="remove">[% l('Remove Items') %]</option>
+                        </select>
+                    </td>
+                </tr>
+            </table>
+            <table cellpadding='0' cellspacing='5' border='0'
+                width='91%' style='margin-left:5px;margin-top:5px;'>
+                <tbody id='anon_list_tbody'>
+                    [% FOR item IN ctx.mylist;
+                        attrs = {marc_xml => ctx.mylist_marc_xml.$item};
+                        PROCESS get_marc_attrs args=attrs %]
+                    <tr>
+                        <td>[% attrs.title %]</td>
+                    </tr>
+                    [% END %]
+                </tbody>
+            </table>
+            <br /><br />
+        </div>
+        [% END %]
index 414c8ba..1358ecb 100644 (file)
                                                 <div style="width:250px;text-align:left;">
                                                     <div style="float:right;">
                                                         <div class="results_aux_utils opac-auto-010"><a
-                                                                href="[% ctx.opac_root %]/place_hold?hold_target=[% rec.bre.id %]&hold_type=T" name="place_hold_link"><img
+                                                                href="[% ctx.opac_root %]/place_hold?hold_target=[% rec.bre.id %]&hold_type=T" name="place_hold_link" class="no-dec"><img
                                                                 src="[% ctx.media_prefix %]/images/green_check.png"
                                                                 alt="place hold" /><span
                                                                     style="position:relative;top:-3px;left:3px;">Place Hold</span></a>
                                                         </div>
                                                         <div class="results_aux_utils opac-auto-011">
+                                                            <form action="[% ctx.opac_root %]/mylist/add" method="POST">
+                                                                <input type="hidden" name="record" value="[% rec.bre.id %]" />
                                                             <div style="position:absolute;">
                                                                 <div style="position:relative;top:5px; left: 25px;">
-                                                                    <a title="Add to my list"
-                                                                        name="result_my_list_link"
-                                                                        href="javascript:;">Add to my list</a>
+                                                                    <input type="submit" title="[% l('Add to my list') %]" value="[% l('Add to my list') %]" class="subtle-button" />
                                                                 </div>
                                                             </div>
-                                                            <a href="javascript:;"
-                                                                name="result_my_list_link_img"><img
-                                                                alt="add to my list"
-                                                                src="[% ctx.media_prefix %]/images/clipboard.png" /></a>
+                                                                <input type="image"
+                                                                alt="[% l('Add to my list') %]"
+                                                                src="[% ctx.media_prefix %]/images/clipboard.png" />
+                                                        </form>
                                                         </div>
                                                         <div style="padding-top:7px;" class="results_aux_utils">
-                                                            <a title="Reviews and More" target="_blank"
+                                                            <a title="Reviews and More" target="_blank" class="no-dec"
                                                                 name="reviews_and_more" href="javascript:;"><img
                                                                 alt="reviews &amp; more"
                                                                 src="[% ctx.media_prefix %]/images/starz.png" /> <span
index 682ef91..444995a 100644 (file)
                         onmouseover="this.src='[% ctx.media_prefix %]/images/adv_search_hover.png';"
                         onmouseout="this.src='[% ctx.media_prefix %]/images/adv_search.png';" /></a>
                 </div>
+                [% IF ctx.mylist.size %]
                 <div class="results_header_btns cached_list_div">
-                    <a href="#" class="hide_me" id="cacheListLink"><img
+                    <a href="[% ctx.opac_root; ctx.user ? '/myopac/lists' : '/mylist' %]"><img
                         alt="View My List"
                         src="[% ctx.media_prefix %]/images/view_my_list.png"
                         onmouseover="this.src='[% ctx.media_prefix %]/images/view_my_list_hover.png';"
                         onmouseout="this.src='[% ctx.media_prefix %]/images/view_my_list.png';" /></a>
                 </div>
+                [% END %]
                 <div class="results_header_div"></div>
                 [% UNLESS CGI.param('_adv') %]
                     <div class="results_header_lbl">Sort by</div>