LP#1778972: OPAC Redesign
authorChris Burton <cburton@nflibrary.ca>
Mon, 29 Jun 2020 14:51:51 +0000 (10:51 -0400)
committerJane Sandberg <sandbej@linnbenton.edu>
Sat, 12 Sep 2020 15:50:25 +0000 (08:50 -0700)
To test
-------
Rename opac to opac-old and opac-new to opac, replacing the framework
while keeping the old one as backup.

Signed-off-by: Chris Burton <cburton@nflibrary.ca>
Signed-off-by: Ruth Frasur <rfrasur@library.in.gov>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
148 files changed:
Open-ILS/src/templates/opac-new/advanced.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/browse.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/cnbrowse.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/css/style.css.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/home.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/i18n_strings.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/library.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/login.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/mylist.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/mylist/clear.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/mylist/email.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/mylist/print.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/circ_history.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/circ_history/export.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/circs.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/ebook_checkout.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/ebook_circs.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/ebook_holds.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/ebook_holds_ready.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/ebook_place_hold.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/hold_history.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/holds.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/holds/edit.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/list/print.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/list/update.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/lists.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/main.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/main_pay.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/main_pay_init.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/main_payment_form.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/main_payments.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/messages.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/messages/list.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/messages/single_message.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/prefs.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/prefs_my_lists.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/prefs_notify.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/prefs_settings.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/receipt_email.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/receipt_print.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/reservations.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/update_email.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/update_password.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/update_password_msg.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/myopac/update_username.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/acjs.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/advanced/expert.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/advanced/global_row.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/advanced/numeric.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/advanced/search.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/anon_list.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/base.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/bookbag_actions.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/cart.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/cart_nav.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/chilifresh.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/coded_value_selector.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/config-fresh.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/config.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/css/colors.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/css/fonts.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/ebook_api/avail.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/ebook_api/base_js.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/ebook_api/login_js.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/filter_group_selector.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/filtersort.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/footer.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/goog_analytics.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/header.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/hold_error_messages.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/hold_status.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/homesearch.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/js.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/library/core_info.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/library/hours.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/library_name_link.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/library_name_link_from_ou.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/locale_picker.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/location_name_link.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/login/form.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/login/help.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/login/login_modal.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/login/password_hint.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/login/username_hint.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/metarecord_hold_filters.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/misc_util.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/multi_hold_select.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/myopac/base.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/myopac/column_sort_support.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/myopac/main_base.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/myopac/main_refund_policy.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/myopac/prefs_base.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/myopac/prefs_hints.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/org_selector.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/place_hold.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/place_hold_result.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/pref_lib_display.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/pref_locale_picker.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/preserve_params.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/qtype_selector.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/addedcontent.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/authors.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/awards.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/body.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/cnbrowse.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/contents.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/copy_counts.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/copy_table.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/extras.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/issues-db.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/issues-mfhd.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/issues.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/navigation.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/refworks.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/series.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/subjects.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/summary.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/record/summaryplus.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/relators.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/result/adv_filter.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/result/copy_counts.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/result/facets-hz.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/result/facets.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/result/lowhits.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/result/lowhits_purchase.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/result/paginate.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/result/table.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/searchbar.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/sms_carrier_selector.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/sms_number_textbox.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/staff_saved_searches.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/stripe.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/tips.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/topnav.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/topnav_cannedsearch.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/topnav_links.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/topnav_logo.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/parts/topnav_subnav.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/password_reset.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/password_reset_msg.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/place_hold.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/record.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/record/email.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/record/print.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/register.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/results.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/sms_cn.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac-new/temp_warn.tt2 [new file with mode: 0644]

diff --git a/Open-ILS/src/templates/opac-new/advanced.tt2 b/Open-ILS/src/templates/opac-new/advanced.tt2
new file mode 100644 (file)
index 0000000..1d6b412
--- /dev/null
@@ -0,0 +1,57 @@
+[%- 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 = ctx.search_ou;
+    
+    ctx.metalinks.push('<meta name="robots" content="noindex,follow">');
+-%]
+    <span class="sr-only">[% l('Advanced Search') %]</span>
+       <div class="container">    
+        <div id="search-wrapper">    
+            [% IF pane == 'advanced' %]
+            <h1 class="h1 text-center pt-3">Advanced Search</h1>
+            <h2 class="text-center pt-3">Refine your search by filling out one or more fields to search by below.</h2>
+            [% ELSIF pane == 'numeric' %]
+            <h1 class="h1 text-center pt-3">Numeric Search</h1>
+            <h2 class="text-center pt-3">Search by a field and numeric identifier.</h2>
+            [% ELSIF pane == 'expert' %]
+            <h1 class="h1 text-center pt-3">Expert Search</h1>
+            <h2 class="text-center pt-3">Search the MARC record.</h2>
+            [% END %]
+            <div class="mt-3">
+                <ul class="nav nav-tabs">
+                <li class="nav-item">
+                    <a href="[% mkurl('', {pane => 'advanced'}) %]" 
+                        class="nav-link [% IF pane == 'advanced' %]active[% END %]"
+                        id="adv_search">[% l('Advanced Search') %]</a>
+                </li>
+                <li class="nav-item">
+                    <a href="[% mkurl('', {pane => 'numeric'}) %]"
+                       class="nav-link [% IF pane == 'numeric' %]active[% END %]"
+                        id="num_search">[% l('Numeric Search') %]</a>
+                </li>
+                <li class="nav-item">
+                    <a href="[% mkurl('', {pane => 'expert'}) %]"
+                       class="nav-link [% IF pane == 'expert' %]active[% END %]"
+                        id="expert_search">[% l('Expert Search') %]</a>
+                </li>
+            </div>
+        </div>
+        <div id="content-wrapper">
+            <div id="main-content">
+                <div class="advanced_div">
+                [% IF pane == 'advanced' %]
+                [% INCLUDE "opac/parts/advanced/search.tt2" %]
+                [% ELSIF pane == 'numeric' %]
+                [% INCLUDE "opac/parts/advanced/numeric.tt2" %]
+                [% ELSIF pane == 'expert' %]
+                [% INCLUDE "opac/parts/advanced/expert.tt2" %]
+                [% END %]
+                </div>
+                <div class="common-full-pad"></div>    
+            </div>
+        </div>
+    </div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/browse.tt2 b/Open-ILS/src/templates/opac-new/browse.tt2
new file mode 100644 (file)
index 0000000..bf34c23
--- /dev/null
@@ -0,0 +1,228 @@
+[%- # This is the bib and authority combined record browser.
+    ctx.page = "browse";
+
+    PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/org_selector.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+
+    ctx.page_title = l("Browse the Catalog");
+    
+    blimit = CGI.param('blimit') || ctx.opac_hits_per_page || 10;
+
+    depart_list = ['blimit', 'bterm', 'bpivot'];
+    ctx.metalinks.push('<meta name="robots" content="noindex,follow">');
+    MACRO heading_use_label BLOCK;
+        SWITCH use;
+            CASE 'variant';
+               l('See');
+            CASE 'broader';
+               l('Broader term');
+            CASE 'narrower';
+               l('Narrower term');
+            CASE 'other';
+               l('Related term');
+            CASE;
+               l('See');
+        END;
+    END;
+%]
+
+    <h2 class="sr-only">[% l('Catalog Browse') %]</h2>
+    
+    <div id="content-wrapper" class="my-5">
+        <div id="main-content" class="container mx-auto w-75">
+            <div id="browse-the-catalog">
+                <div id="browse-controls" class='row text-center'>
+                    <form method="get" onsubmit="$('browse-submit-spinner').className = ''; return true" class="mx-auto">
+                        <input type="hidden" name="blimit"
+                            value="[% blimit %]" />
+
+                        [% control_qtype = INCLUDE "opac/parts/qtype_selector.tt2"
+                            id="browse-search-class" browse_only=1 plural=1 %]
+                                                       
+                        [% control_bterm = BLOCK %]<input type="text" name="bterm" id="browse-term"
+                            aria-label="[% l('Browse term') %]"
+                            value="[% CGI.param('bterm') | html %]" class="form-control" />[% END %]
+                        [% control_locg = INCLUDE build_org_selector id='browse-context'
+                            show_loc_groups=1
+                            arialabel=l('Select holding library') %]
+                        [% l('Browse for [_1] starting with [_2] in [_3]', control_qtype, control_bterm, control_locg) %]
+
+                       <input id='search-submit-go' type="submit" value="[% l('Browse') %]" alt="[% l('Browse') %]" class="btn btn-confirm my-2"/>
+                        <img id="browse-submit-spinner" 
+                        src="[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]"
+                        class="hidden" style="width: 16px; height: 16px;" 
+                        alt="[% l('Search in progress icon') %]" />
+                    </form>
+                </div>
+                       <div class="row text-center my-5 border-top border-bottom">
+                [% BLOCK browse_pager %]
+                <div class="browse-pager col-12">
+                <div class="mx-auto">
+                    [% IF ctx.back_pivot %]
+                    <a class="btn btn-outline-primary btn-sm" href="[% mkurl('', {bpivot => ctx.back_pivot}) %]" onclick="$('browse-pager-spinner-[% id %]').className = '';">&larr; [%l ('Back') %]</a>
+                    [% END %]
+                    [% IF ctx.pager_shortcuts;
+                        current_qtype = CGI.param('qtype') || 'title' %]
+                    <span class="browse-shortcuts">
+                        [% FOR shortcut IN ctx.pager_shortcuts %]
+                            <a href="[% mkurl('', {qtype => current_qtype, bterm => shortcut.0}, ['bpivot','query']) %]">[% shortcut.1 %]</a>
+                        [% END %]
+                    </span>
+                    [% END %]
+                    <img id="browse-pager-spinner-[% id %]" 
+                        src="[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]"
+                        class="hidden" style="width: 16px; height: 16px;" 
+                        alt="[% l('Search in progress icon') %]" />
+                    [% IF ctx.forward_pivot %]
+                    <a class="btn btn-outline-primary btn-sm" href="[% mkurl('', {bpivot => ctx.forward_pivot}) %]" onclick="$('browse-pager-spinner-[% id %]').className = '';">[%l ('Next') %] &rarr;</a>
+                    [% END %]
+
+                   
+                        </div>
+                </div>
+                [% END %]
+
+                [% PROCESS browse_pager id=0 %]
+
+                <div id="browse-results" class="col-12">
+                [% IF ctx.browse_error %]
+                    <span class="browse-error">
+                        [% l("An error occurred browsing records. " _
+                        "Please try again in a moment or report the issue " _
+                        "to library staff.") %]
+                    </span>
+                [% ELSE %]
+                    [% IF ctx.browse_leading_article_warning %]
+                    <div class="browse-leading-article-warning">
+                            [% l("Your browse term seems to begin with an article (a, an, the). You might get better results by omitting the article.") %]
+                            [% IF ctx.browse_leading_article_alternative %]
+                            <p>
+                            [% alternative_link = BLOCK %]
+                            <a href="[% mkurl('', {bterm => ctx.browse_leading_article_alternative}, ['bpivot']) %]">[% ctx.browse_leading_article_alternative | html %]</a>
+                            [%-  END; # alternative_link BLOCK
+                                l("Did you mean [_1]?", alternative_link);
+                            END # IF %]
+                            </p>
+                    </div>
+                    [% END %]
+                    <ul class="browse-result-list">
+                    [% FOR result IN ctx.browse_results %]
+                        <li class="browse-result">
+                            <span class="browse-result-value[% result.row_number == 0 && !CGI.param('bpivot') ? ' browse-result-best-match' : '' %]">
+                                [% IF result.sources > 0 %] <!-- bib-linked browse value -->
+                                    <a href="[% mkurl(
+                                        ctx.opac_root _ '/results', {
+                                            'fi:has_browse_entry' => (result.browse_entry _ ',' _ result.fields)
+                                        }) %]">[% result.value | html %]</a>
+                                    <span class="browse-result-sources">([%
+                                        IF result.accurate == 'f';
+                                            l("At least"); " ";
+                                        END;
+                                    result.sources %])</span>
+                                [% ELSE %] <!-- only authority links -->
+                                    [% result.value | html %]
+                                [% END %]
+                            </span>
+
+                            [% FOR a IN result.authorities;
+                                    PROCESS authority_notes authority=a IF !sees.grep(a.id);
+                               END %]
+                            [% auth_headings_ul_added = 0; %]
+                                [% seenit = {}; # for headings we've rendered
+                                   FOR a IN result.sees;
+                                    # We can go no further sans control_set.
+                                    NEXT UNLESS a.control_set;
+
+                                    # get_authority_fields is fast and cache-y.
+                                    acs = ctx.get_authority_fields(a.control_set);
+                                    FOR field_group IN a.headings;
+                                        field_id = field_group.keys.0;
+                                        field = acs.$field_id;
+                                        headings = field_group.values.0;
+                                        FOR h IN headings;
+                                            # We could display headings without
+                                            # links here when h.target is
+                                            # undef, if we wanted to, but note
+                                            # that h.target_count is only
+                                            # defined when h.target is.
+
+                                            IF h.target AND h.target_count AND result.list_authorities.grep('^' _ h.target _ '$').size == 0 AND h.main_entry;
+                                                id = h.target; NEXT IF seenit.$id; seenit.$id = 1; 
+                                                IF !auth_headings_ul_added;
+                                                    # only add a <ul> if we have at least one <li> (WCAG 2.0)
+                                                    auth_headings_ul_added = 1; %]
+                                                <ul class="browse-result-authority-headings"> 
+                                                [% END %]
+
+                                                <li><span class="browse-result-authority-field-name">[% heading_use_label(use=h.type) %]</span>
+                                                <a href="[% mkurl(ctx.opac_root _ '/browse', {bterm => h.heading}, ['bpivot']) %]">[% h.heading | html %]</a>
+                                                <span class="browse-result-authority-bib-links">([% h.target_count %])</span>
+                                                </li>
+                                            [% END %]
+                                        [% END %]
+                                    [% END %]
+                                [% END %]
+                                [% FOR a IN result.authorities;
+                                    # We can go no further sans control_set.
+                                    NEXT UNLESS a.control_set;
+
+                                    # get_authority_fields is fast and cache-y.
+                                    acs = ctx.get_authority_fields(a.control_set);
+                                    FOR field_group IN a.headings;
+                                        field_id = field_group.keys.0;
+                                        field = acs.$field_id;
+                                        headings = field_group.values.0;
+                                        FOR h IN headings;
+                                            # We could display headings without
+                                            # links here when h.target is
+                                            # undef, if we wanted to, but note
+                                            # that h.target_count is only
+                                            # defined when h.target is.
+
+                                            IF h.target AND h.target_count AND result.list_sees.grep('^' _ h.target _ '$').size == 0 AND !h.main_entry AND ctx.get_cgf('opac.show_related_headings_in_browse').enabled == 't';
+                                                id = h.target; NEXT IF seenit.$id; seenit.$id = 1; 
+                                                IF !auth_headings_ul_added;
+                                                    # only add a <ul> if we have at least one <li> (WCAG 2.0)
+                                                    auth_headings_ul_added = 1; %]
+                                                <ul class="browse-result-authority-headings"> 
+                                                [% END %]
+                                                <li><span class="browse-result-authority-field-name">[% heading_use_label(use=h.type) %]</span>
+                                                <a href="[% mkurl(ctx.opac_root _ '/results', {query => 'identifier|authority_id[' _ h.target _ ']'}) %]">[% h.heading | html %]</a>
+                                                <span class="browse-result-authority-bib-links">([% h.target_count %])</span>
+                                                </li>
+                                            [% END %]
+                                        [% END %]
+                                    [% END %]
+                                [% END %]
+                            [% IF auth_headings_ul_added %]</ul>[% END %]
+                        </li>
+                    [% END %]
+                    </ul>
+                [% END %]
+                </div>
+
+                [% PROCESS browse_pager id=1 %]
+            </div>
+</div>
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+
+    [% BLOCK authority_notes;
+        # Displays public general notes (sometimes called "scope notes" ?)
+        FOR note IN authority.notes %]
+            <div class="browse-public-general-note">
+                <span class="browse-public-general-note-label">
+                    [% l("Note:") %]
+                </span>
+                <span class="browse-public-general-note-body">
+                [% FOR piece IN note; piece | html; END %]
+                </span>
+            </div>
+        [% END;
+    END;    # end of BLOCK authority_notes %]
+
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/cnbrowse.tt2 b/Open-ILS/src/templates/opac-new/cnbrowse.tt2
new file mode 100644 (file)
index 0000000..86454e9
--- /dev/null
@@ -0,0 +1,20 @@
+[%- # This is the stand-alone call-number browser.  This mainly wraps around
+    # the same guts as the "shelf browser" part of a record results page.
+
+    PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    ctx.metalinks.push('<meta name="robots" content="noindex,follow">');
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Call Number Browse"); %]
+    <span class="sr-only">[% l('Call Number Browse') %]</span>
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    <div id="content-wrapper">
+        <div id="main-content">
+            <div class="cnbrowse_div">
+                [% INCLUDE "opac/parts/record/cnbrowse.tt2" %]
+            </div>
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/css/style.css.tt2 b/Open-ILS/src/templates/opac-new/css/style.css.tt2
new file mode 100644 (file)
index 0000000..46e2ecc
--- /dev/null
@@ -0,0 +1,3792 @@
+[%-
+    PROCESS "opac/parts/css/colors.tt2";
+    PROCESS "opac/parts/css/fonts.tt2";
+    rtl = ctx.get_i18n_l(ctx.eg_locale).rtl;
+%]
+
+/*
+Mini Table Styling
+*/
+@media 
+only screen and (max-width: 650px)  {
+    .miniTable{
+        width: 100% !important;
+    }
+
+       /* Force table to not be like tables anymore */
+    .miniTable table, .miniTable thead, .miniTable tbody, .miniTable th, .miniTable td, .miniTable tr
+    { 
+               display: block; 
+    }
+    
+    .miniTable th{
+       display:none;
+    }
+       
+       /* Hide table headers (but not display: none;, for accessibility) */
+    .miniTable thead tr{ 
+               position: absolute;
+               top: -9999px;
+               left: -9999px;
+       }
+       
+    .miniTable tr
+     { 
+       border: 1px solid #ccc; 
+       margin: 10px 0px; 
+     }
+
+
+    .miniTable td
+    { 
+               border: none;
+               border-bottom: 1px solid #eee; 
+               position: relative;
+        padding-left: 50%; 
+        min-height:45px;
+        text-align: right;
+    }
+       
+    .miniTable td:before
+    { 
+               left: 6px;
+               width: 45%; 
+               padding-right: 10px; 
+               white-space: nowrap;
+        position: absolute;
+    }
+    .copyTable tr:nth-of-type(1):before { content: "Copy #1"; display: block; text-align:center; }
+       .copyTable tr:nth-of-type(2):before { content: "Copy #2"; display: block; text-align:center;}
+       .copyTable tr:nth-of-type(3):before { content: "Copy #3"; display: block; text-align:center;}
+       .copyTable tr:nth-of-type(4):before { content: "Copy #4"; display: block; text-align:center;}
+       .copyTable tr:nth-of-type(5):before { content: "Copy #5"; display: block; text-align:center;}
+       .copyTable tr:nth-of-type(6):before { content: "Copy #6"; display: block; text-align:center;}
+       .copyTable tr:nth-of-type(7):before { content: "Copy #7"; display: block; text-align:center;}
+       .copyTable tr:nth-of-type(8):before { content: "Copy #8"; display: block; text-align:center;}
+       .copyTable tr:nth-of-type(9):before { content: "Copy #9"; display: block; text-align:center;}
+       .copyTable tr:nth-of-type(10):before { content: "Copy #10"; display: block; text-align:center;}
+
+       .copyTable td:nth-of-type(1):before { content: "Library"; display: flex; }
+    .copyTable td:nth-of-type(2):before { content: "Call Number"; display: flex; }
+       .copyTable td:nth-of-type(3):before { content: "Barcode"; display: flex;}
+    .copyTable td:nth-of-type(4):before { content: "Shelving Location"; display: flex;}
+    .copyTable td:nth-of-type(5):before { content: "Status"; display: flex;}
+    .copyTable td:nth-of-type(6):before { content: "Due Date"; display: flex;}
+   
+    .holdingsTable tr:nth-of-type(1):before { content: "Copy #1"; display: block; text-align:center; }
+       .holdingsTable tr:nth-of-type(2):before { content: "Copy #2"; display: block; text-align:center;}
+       .holdingsTable tr:nth-of-type(3):before { content: "Copy #3"; display: block; text-align:center;}
+       .holdingsTable tr:nth-of-type(4):before { content: "Copy #4"; display: block; text-align:center;}
+       .holdingsTable tr:nth-of-type(5):before { content: "Copy #5"; display: block; text-align:center;}
+       .holdingsTable tr:nth-of-type(6):before { content: "Copy #6"; display: block; text-align:center;}
+       .holdingsTable tr:nth-of-type(7):before { content: "Copy #7"; display: block; text-align:center;}
+       .holdingsTable tr:nth-of-type(8):before { content: "Copy #8"; display: block; text-align:center;}
+       .holdingsTable tr:nth-of-type(9):before { content: "Copy #9"; display: block; text-align:center;}
+       .holdingsTable tr:nth-of-type(10):before { content: "Copy #10"; display: block; text-align:center;}
+
+
+    .holdingsTable td:nth-of-type(1):before { content: "Library"; display: flex; }
+    .holdingsTable td:nth-of-type(2):before { content: "Shelving location"; display: flex; }
+       .holdingsTable td:nth-of-type(3):before { content: "Call number"; display: flex;}
+    .holdingsTable td:nth-of-type(4):before { content: "Status"; display: flex;}
+
+       .chargesTable td:nth-of-type(1):before { content: "Select"; display: flex; }
+    .chargesTable td:nth-of-type(2):before { content: "Owed"; display: flex; }
+       .chargesTable td:nth-of-type(3):before { content: "Billing Type"; display: flex;}
+    .chargesTable td:nth-of-type(4):before { content: "Note"; display: flex;}
+    .chargesTable td:nth-of-type(5):before { content: "Date"; display: flex;}
+    
+    .chargesMainTable td:nth-of-type(1):before { content: "Select"; display: flex; }
+    .chargesMainTable td:nth-of-type(2):before { content: "Owed"; display: flex; }
+       .chargesMainTable td:nth-of-type(3):before { content: "Billing Type"; display: flex;}
+    .chargesMainTable td:nth-of-type(4):before { content: "Title"; display: flex;}
+    .chargesMainTable td:nth-of-type(5):before { content: "Checkout"; display: flex;}
+    .chargesMainTable td:nth-of-type(6):before { content: "Due"; display: flex;}
+    .chargesMainTable td:nth-of-type(7):before { content: "Returned/Renewed"; display: flex;}
+    
+    .activeHoldstable td:nth-of-type(1):before { content: "Select"; display: flex; }
+    .activeHoldstable td:nth-of-type(2):before { content: "Title"; display: flex; }
+       .activeHoldstable td:nth-of-type(3):before { content: "Author"; display: flex;}
+    .activeHoldstable td:nth-of-type(4):before { content: "Format"; display: flex;}
+    .activeHoldstable td:nth-of-type(5):before { content: "Pickup Location"; display: flex;}
+    .activeHoldstable td:nth-of-type(6):before { content: "Cancel If Not Filled By"; display: flex;}
+    .activeHoldstable td:nth-of-type(7):before { content: "Status"; display: flex;}
+    .activeHoldstable td:nth-of-type(8):before { content: "Notes"; display: flex;}
+    
+    .holdHistoryTable td:nth-of-type(1):before { content: "Title"; display: flex; }
+    .holdHistoryTable td:nth-of-type(2):before { content: "Author"; display: flex; }
+    .holdHistoryTable td:nth-of-type(3):before { content: "Format"; display: flex; }
+    .holdHistoryTable td:nth-of-type(4):before { content: "Pickup Location"; display: flex; }
+    .holdHistoryTable td:nth-of-type(5):before { content: "Status"; display: flex; }
+    
+    .paymentTable td:nth-of-type(1):before { content: "Payment Date"; display: flex; }
+    .paymentTable td:nth-of-type(2):before { content: "Payment For"; display: flex; }
+    .paymentTable td:nth-of-type(3):before { content: "Amount"; display: flex; }
+    .paymentTable td:nth-of-type(4):before { content: "Receipt"; display: flex; }
+    
+    .messagesTable td:nth-of-type(1):before { content: "Select"; display: flex; }
+    .messagesTable td:nth-of-type(2):before { content: "Date"; display: flex; }
+    .messagesTable td:nth-of-type(3):before { content: "Library"; display: flex; }
+    .messagesTable td:nth-of-type(4):before { content: "Subject"; display: flex; }
+    .messagesTable td:nth-of-type(5):before { content: ""; display: flex; }
+    
+    .circTable td:nth-of-type(1):before { content: "Select"; display: flex; }
+    .circTable td:nth-of-type(2):before { content: "Title"; display: flex; }
+    .circTable td:nth-of-type(3):before { content: "Author"; display: flex; }
+    .circTable td:nth-of-type(4):before { content: "Renewals Left"; display: flex; }
+    .circTable td:nth-of-type(5):before { content: "Due Date"; display: flex; }
+    .circTable td:nth-of-type(6):before { content: "Barcode"; display: flex; }
+    .circTable td:nth-of-type(7):before { content: "Call Number"; display: flex; }
+    
+    .circHistTable td:nth-of-type(1):before { content: "Select"; display: flex; }
+    .circHistTable td:nth-of-type(2):before { content: "Title"; display: flex; }
+    .circHistTable td:nth-of-type(3):before { content: "Author"; display: flex; }
+    .circHistTable td:nth-of-type(4):before { content: "Checkout Date"; display: flex; }
+    .circHistTable td:nth-of-type(5):before { content: "Due Date"; display: flex; }
+    .circHistTable td:nth-of-type(6):before { content: "Date Returned"; display: flex; }
+    .circHistTable td:nth-of-type(7):before { content: "Barcode"; display: flex; }
+    .circHistTable td:nth-of-type(8):before { content: "Call Number"; display: flex; }
+    
+    .bookbagTable td:nth-of-type(1):before { content: ""; display: flex; }
+    .bookbagTable td:nth-of-type(2):before { content: "Title"; display: flex; }
+    .bookbagTable td:nth-of-type(3):before { content: "Author(s)"; display: flex; }
+    .bookbagTable td:nth-of-type(4):before { content: "Local Call number"; display: flex; }
+    .bookbagTable td:nth-of-type(5):before { content: "Publication Date"; display: flex; }
+    .bookbagTable td:nth-of-type(6):before { content: "Format"; display: flex; }
+    .bookbagTable td:nth-of-type(7):before { content: "Notes"; display: flex; }
+    
+    .eholdsTable td:nth-of-type(1):before { content: "Title"; display: flex; }
+    .eholdsTable td:nth-of-type(2):before { content: "Author"; display: flex; }
+    .eholdsTable td:nth-of-type(3):before { content: "Expire Date"; display: flex; }
+    .eholdsTable td:nth-of-type(4):before { content: "Status"; display: flex; }
+    .eholdsTable td:nth-of-type(5):before { content: "Actions"; display: flex; }
+    
+    .eholdsreadyTable td:nth-of-type(1):before { content: "Title"; display: flex; }
+    .eholdsreadyTable td:nth-of-type(2):before { content: "Author"; display: flex; }
+    .eholdsreadyTable td:nth-of-type(3):before { content: "Expire Date"; display: flex; }
+    .eholdsreadyTable td:nth-of-type(4):before { content: "Actions"; display: flex; }
+    
+    .echeckoutTable td:nth-of-type(1):before { content: "Title"; display: flex; }
+    .echeckoutTable td:nth-of-type(2):before { content: "Author"; display: flex; }
+    .echeckoutTable td:nth-of-type(3):before { content: "Due Date"; display: flex; }
+    .echeckoutTable td:nth-of-type(4):before { content: "Actions"; display: flex; }
+    
+    .ecircsTable td:nth-of-type(1):before { content: "Title"; display: flex; }
+    .ecircsTable td:nth-of-type(2):before { content: "Author"; display: flex; }
+    .ecircsTable td:nth-of-type(3):before { content: "Due Date"; display: flex; }
+    .ecircsTable td:nth-of-type(4):before { content: "Actions"; display: flex; }
+
+    .bucketTable td:nth-of-type(2):before { content: "Title"; display: flex; text-align:left; }
+    .bucketTable td:nth-of-type(3):before { content: "Author"; display: flex; text-align:left; }
+    .bucketTable td:nth-of-type(4):before { content: "Call Number"; display: flex; text-align:left; }
+    
+    /*Media Style Additions*/
+    .fullRow{
+        padding: 5px !important;
+       text-align: center !important;
+       width: 100%;
+    }
+    
+    .paginateText{
+       display:none;
+       }
+}/*Micro Tables End*/
+
+/*
+Novelist Styling
+*/
+
+.NovGroupHeader{
+    font-size: 16pt !important;
+    text-align: center;
+    font-weight: 600 !important;
+}
+
+.NovSectionHeader{
+       font-size: 16pt !important;
+    font-weight: 600 !important;
+}
+
+.NovSelectImageHeadingToggle{
+           font-size: 20px;
+        
+}
+
+/*Novelist Styling End*/
+
+/* Buttons */
+
+.btn-confirm {
+    color: [% css_colors.button_confirm_text %];
+    background-color: [% css_colors.button_confirm %];
+    border-color: [% css_colors.button_confirm_border %];
+}
+
+.btn-confirm:hover {
+    color: [% css_colors.button_confirm_text %];
+    background-color: [% css_colors.button_confirm_hover %];
+    border-color: [% css_colors.button_confirm_border_hover %];
+}
+
+.btn-deny {
+    color: [% css_colors.button_deny_text %];
+    background-color: [% css_colors.button_deny %];
+    border-color: [% css_colors.button_deny_border_hover %];
+}
+
+.btn-deny:hover {
+    color: [% css_colors.button_deny_text %];
+    background-color: [% css_colors.button_deny_hover %];
+    border-color: [% css_colors.button_deny_border_hover %];
+}
+
+.btn-remove {
+    color: [% css_colors.button_remove_text %];
+    background-color: [% css_colors.button_remove %];
+    border-color: [% css_colors.button_remove_border %];
+}
+
+.btn-remove:hover {
+    color: [% css_colors.button_remove_text %];
+    background-color: [% css_colors.button_remove_hover %];
+    border-color: [% css_colors.button_remove_border_hover %];
+}
+
+.btn-opac{
+    color: [% css_colors.opac_button_text %] !important;
+    background-color: [% css_colors.opac_button %];
+    border-color: [% css_colors.opac_button_border %];
+}
+
+.btn-opac:hover{
+    color: [% css_colors.opac_button_text %];
+       background-color: [% css_colors.opac_button_hover %] !important;
+    border-color: [% css_colors.opac_button_border_hover %];
+}
+
+.btn-action{
+    color: [% css_colors.action_button_text %] !important;
+    background-color: [% css_colors.action_button %];
+    border-color: [% css_colors.action_button_border %];
+}
+
+.btn-action:hover{
+    color: [% css_colors.action_button_text %];
+       background-color: [% css_colors.action_button_hover %] !important;
+    border-color: [% css_colors.action_button_border_hover %];
+}
+.btn-action > a{
+    color: [% css_colors.action_button_text_anchors %];
+}
+
+
+.dash-link{
+       color: #fff;
+}
+
+.dash-clear{
+       color: [% css_colors.zero_count %] !important;
+}
+
+.dash-unclear{
+       color: [% css_colors.non_zero_count %] !important;
+}
+
+.remove_filter{
+    padding: .25rem .25rem !important;
+    font-size: .875rem;
+    line-height: 0.5;
+    border-radius: 1rem !important;
+}
+
+#cnbrowse{
+       width: 95%;
+    text-align: center;
+    padding: 15px;
+}
+.cn_browse_item .card{
+       padding: 15px;
+}
+#facets .card-header,#facets .card-header a {
+       background-color: [% css_colors.facets_back %];
+    color: [% css_colors.text_invert %]
+}
+
+.facet_template:hover{
+               background-color:#e8e8e8;
+}
+
+.red{
+       color:red;
+}
+
+.login:focus{
+       outline:none;
+}
+.browse-result-list{
+       list-style:none;
+}
+.not_active_pill{
+       border: 2px solid #007bff;
+    padding: 6px;
+    margin: 0 10px;
+}
+
+.mini-control{
+       max-width: 100px !important;
+}
+
+.card-body:empty{
+       display:none;
+}
+.min{
+ min-height:500px;
+}
+.container{
+       
+}
+html{
+       height:100%;
+    min-height: 100vh;
+}
+body {
+height:100%;
+min-height: 100vh;
+    margin:0;
+    font-family: [% css_fonts.font_main %] !important;
+    font-size: [% css_fonts.size_base %];
+  /*  background-color: [% css_colors.primary %]; */
+    [% IF rtl == 't' -%]
+    direction: rtl;
+    [%- END %]
+}
+
+button, input {
+    font-family: [% css_fonts.font_main %] !important;
+    font-size: [% css_fonts.size_base %];
+}
+
+img {
+    border: none;
+}
+
+#topnav_logo {
+    margin: 2em 0;
+}
+
+#homesearch_main_logo {
+    padding-top: 60px;
+    margin: auto;
+    width: 50%;
+}
+
+a {
+    color: [% css_colors.link %];
+}
+
+#search-wrapper input[type=text] {
+    margin:0;
+    padding:0;
+}
+
+#search-wrapper select, .results_header_lbl select {
+    border:1px solid [% css_colors.border_standard %];
+    margin:0;
+    padding:0;
+    width: 12em;
+}
+
+.searchbar {
+    font-weight: bold;
+    padding-top: 10px;
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    [%- ELSE %]
+    margin-left: 1em;
+    [%- END %]
+}
+
+.refine_search {
+    padding-bottom: 7px;
+    margin-left: 1em;
+}
+
+/*
+#search-wrapper select {
+    border:0px solid [% css_colors.border_dark %];
+    filter:alpha(opacity=0);
+    -khtml-opacity:0;
+    opacity:0;
+    padding:0;
+    margin:0;
+    height:18px;
+}
+*/
+h1 {
+    margin:0;
+    margin-bottom: 5px;
+    font-size: [% css_fonts.size_biggest %];
+    font-weight:normal;
+}
+
+h2 {
+    font-size: [% css_fonts.size_bigger %];
+    font-weight:bold;
+}
+
+h2.graphic880 {
+    font-size: [% css_fonts.size_bigger %];
+    font-weight:normal;
+}
+
+.hide_me, .hidden {
+    display: none;
+    visibility: hidden;
+}
+
+div.select-box-wrapper {
+    position:absolute;
+    padding-top:2px;
+    overflow:hidden;
+    [% IF rtl == 't' -%]
+    padding-right: 3px;
+    text-align:right;
+    [%- ELSE %]
+    padding-left:3px;
+    text-align:left;
+    [%- END %]
+}
+
+#dash_wrapper {
+    [% IF rtl == 't' -%]
+    margin-right: 0.5em;
+    [%- ELSE %]
+    margin-left: 0.5em;
+    [%- END %]
+}
+
+#dash_wrapper div {
+    background-color: [% css_colors.primary %];
+    border-radius: 5px;
+    padding: 0em 1em;
+}
+
+.dash_divider {
+    margin: 0 20px;
+    color: #fff;
+    display:inline-block;
+}
+
+#dashboard, #dashboard_e {
+    margin-top: 1em;
+    height: 2em;
+}
+
+#dashboard span.dash-align a, #dashboard_e span.dash-align a {
+    font-weight: bold;
+    text-decoration: none;
+}
+
+#dash_user {
+    font-weight: bold;
+}
+
+#logout_link {
+    [% IF rtl == 't' -%]
+    right: 1px;
+    [%- ELSE %]
+    left: 1px;
+    [%- END %]
+}
+
+
+/*  
+#dash_fines { color: [% css_colors.text_badnews %]; }
+the color contrast between "text_badnews" and "primary"
+is too low for WCAG compliance.  Use "text_attention" 
+for now until a better color is picked - if needed.
+*/
+
+#dash_wrapper #dash_user_message_button_container {
+    display: inline;
+    padding: 0;
+}
+#dash_wrapper #unread_message_count_floater {
+    position: absolute;
+    background-color: [% css_colors.text_alert %];
+    padding-left: 0.2em;
+    padding-right: 0.2em;
+    display: inline-block;
+    font-size: 95%;
+    border-radius: 0;
+    z-index: 2;
+    margin-top: 0.2em;
+    [% IF rtl == 't' -%]
+    margin-right: -1.5em;
+    [%- ELSE %]
+    margin-left: -1.5em;
+    [%- END %]
+}
+
+#header-wrap {
+       /* border-bottom: 5px solid [% css_colors.nav_separation %]; */
+    background-color: [% css_colors.header_primary_fade %];
+    background-image: -moz-linear-gradient([% css_colors.header_primary %], [% css_colors.header_primary_fade %]);
+    background-image: -o-linear-gradient([% css_colors.header_primary %], [% css_colors.header_primary_fade %]);
+    background-image: -webkit-linear-gradient([% css_colors.header_primary %], [% css_colors.header_primary_fade %]);
+    background-image: linear-gradient([% css_colors.header_primary %], [% css_colors.header_primary_fade %]);
+}
+#header {
+    color: [% css_colors.background %];
+    margin-left: 1em;
+    margin-right: 1em;
+    font-size: [% css_fonts.size_small %];
+}
+
+#header a {
+    color: [% css_colors.background %];
+}
+
+#header a:hover {
+    color: [% css_colors.text_invert %];
+    text-decoration: none;
+}
+
+#header-links {
+    color: [% css_colors.text_invert %];
+    font-size: [% css_fonts.size_small %];
+    font-weight: bold;
+    position: relative;
+    top:4px;
+    
+}
+
+#header-links a {
+    color: [% css_colors.text_invert %];
+    display: block;
+    text-decoration: none;
+    [% IF rtl == 't' -%]
+    float: right;
+    margin-left: 22px;
+    [%- ELSE %]
+    float:left;
+    margin-right:22px;
+    [%- END %]
+}
+
+#header-links a:hover {
+    color: [% css_colors.text_invert %];
+    text-shadow: 0 0 0.2em [% css_colors.primary %], 0 0 0.2em [% css_colors.primary %];
+    text-decoration: none;
+}
+
+#header #header-links2 {
+    position:relative;
+    top:-8px;
+    color: [% css_colors.text_invert %];
+    padding-bottom: 15px;
+}
+
+#header #header-links2 a {
+    color: [% css_colors.text_invert %];
+}
+
+#header #header-links2 a:hover {
+    text-decoration: underline;
+}
+
+#your-acct-login {
+    padding-top:2em;
+}
+
+#gold-links {
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    padding-right: 0px;
+    [%- ELSE %]
+    margin-left: 1em;
+    padding-left:0px;
+    [%- END %]
+}
+
+#gold-links-home {
+    margin:auto;
+    [% IF rtl == 't' -%]
+    padding-right: 0px;
+    [%- ELSE %]
+    padding-left:0px;
+    [%- END %]
+}
+
+#gold-links-holder {
+    height: 24px;
+    background-color: [% css_colors.background_invert %];
+}
+
+#util-bar {
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    padding-right: 0px;
+    [%- ELSE %]
+    margin-left: 1em;
+    padding-left:0px;
+    [%- END %]
+    height:0px;
+}
+
+#search-wrapper {
+    /*border-bottom: 1px solid [% css_colors.border_standard %];*/
+    padding-bottom: 5px;
+    background-color: [% css_colors.background %];
+}
+
+#search-wrapper #breadcrumb {
+    margin-top:0px;
+    font-size: [% css_fonts.size_smaller %];
+    [% IF rtl == 't' -%]
+    float: right;
+    [%- ELSE %]
+    float:left;
+    [%- END %]
+}
+
+#search-wrapper #search-within {
+    margin-top:10px;
+    position:relative;
+    [% IF rtl == 't' -%]
+    float: left;
+    right: -173px;
+    [%- ELSE %]
+    float:right;
+    left:-173px;
+    [%- END %]
+}
+
+#search-wrapper #breadcrumb a {
+    color: [% css_colors.text %];
+}
+
+#search-wrapper #search_frm label {
+    font-size: [% css_fonts.size_smaller %];
+}
+
+#search-wrapper #search-box {
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    padding-right: 0px;
+    [%- ELSE %]
+    margin-left: 1em;
+    padding-left: 0px;
+    [%- END %]
+    padding-top: 10px;
+    padding-bottom: 10px;
+}
+
+#adv_search_tabs, #acct_tabs, #acct_fines_tabs, #acct_checked_tabs, #acct_holds_tabs, #acct_prefs_tabs, #results_header_inner{
+    [% IF rtl == 't' -%]
+    margin-right:2px;
+    [%- ELSE %]
+    margin-left: 2px;
+    [%- END %]
+    overflow: auto;
+}
+
+#adv_search_tabs a, #acct_tabs a, #acct_fines_tabs a, #acct_checked_tabs a, #acct_holds_tabs a, #acct_prefs_tabs a {
+    [% IF rtl == 't' -%]
+    float: right;
+    margin: 10px 0px 0px 7px;
+    border-radius: 10px 10px 0px 0px;
+    [%- ELSE %]
+    float: left;
+    margin: 10px 7px 0px 0px;
+    border-radius: 10px 10px 0px 0px;
+    [%- END %]
+    text-align: center;
+    vertical-align: middle;
+    display: block;
+    padding: 10px 0px 10px 0px;
+    font-weight: bold;
+    color: [% css_colors.text_invert %];
+    background-color: [% css_colors.control %];
+    font-weight: bold;
+    text-decoration: none;
+}
+
+#adv_search_tabs a, #acct_tabs a, #acct_fines_tabs a {
+    padding: 1em 1em 0.5em;
+}
+
+#adv_search_tabs a:hover, #acct_tabs a:hover, #acct_fines_tabs a:hover, #acct_checked_tabs a:hover, #acct_holds_tabs a:hover, #acct_prefs_tabs a:hover {
+    background-color: [% css_colors.primary %];
+    color: [% css_colors.text_invert %];
+    text-decoration: none;
+}
+
+#adv_search_filters {
+    position: relative;
+}
+#adv_search_rows {
+    border-bottom: none;
+}
+.adv_filter_block {
+    [% IF rtl == 't' -%]
+    float: right;
+    [%- ELSE %]
+    float: left;
+    [%- END %]
+    padding: 15px;
+    clear: both;
+}
+.adv_filter_block_item {
+    [% IF rtl == 't' -%]
+    float: right;
+    [%- ELSE %]
+    float: left;
+    [%- END %]
+    padding: 5px;
+}
+
+#adv_special_block {
+    [% IF rtl == 't' -%]
+    float: right;
+    [%- ELSE %]
+    float: left;
+    [%- END %]
+    padding: 5px;
+    margin-top: 5px;
+}
+
+#adv_search_submit {
+    [% IF rtl == 't' -%]
+    margin-right: 10px;
+    [%- ELSE %]
+    margin-left: 10px;
+    [%- END %]
+    position: relative;
+}
+
+.checkbox_col {
+    width: 1%;
+    [% IF rtl == 't' -%]
+    padding-right: 10px !important;
+    [%- ELSE %]
+    padding-left: 10px !important;
+    [%- END %]
+}
+
+#adv_search.on, #num_search.on, #expert_search.on {
+    color: [% css_colors.accent_darker %];
+    background-color: [% css_colors.background %];
+    text-decoration: none;
+}
+
+#adv_search_tabs a.acct-tab-on, #acct_tabs a.acct-tab-on, #acct_fines_tabs a.acct-tab-on {
+    color: [% css_colors.accent_darker %];
+    background-color: [% css_colors.background %];
+    text-decoration: none;
+}
+
+.acct-tab-off {
+    background-color: [% css_colors.control %];
+}
+
+#acct_checked_tabs a, #acct_holds_tabs a, #acct_prefs_tabs a {
+    margin-top: 0px;
+    font-size: [% css_fonts.size_smaller %];
+    color: [% css_colors.accent_darker %];
+    padding: 10px 10px 10px 10px;
+}
+
+#acct_checked_tabs div.selected a, #acct_holds_tabs div.selected a, #acct_prefs_tabs div.selected a {
+    background-color: [% css_colors.accent_lightest %];
+    color: [% css_colors.accent_darker %];
+}
+
+#acct_checked_tabs, #acct_holds_tabs, #acct_prefs_tabs {
+    padding-bottom: 12px;
+    color: [% css_colors.accent_medium %];
+}
+
+#acct_select, #acct_prefs_select {
+     display: none;
+}
+
+#mobile_acct_search_links {
+      background-color: [% css_colors.primary_fade %];
+}
+
+.rdetail_header {
+    [% IF rtl == 't' -%]
+    padding: 5px 0px 6px 7px;
+    margin-right: 1em;
+    [%- ELSE %]
+    padding: 5px 7px 6px 0px;
+    margin-left: 1em;
+    [%- END %]
+    border-bottom: 1px dotted [% css_colors.accent_light %];
+}
+
+.rdetail_results a {
+    color:[% css_colors.primary_fade %];
+    font-weight:bold;
+    font-size: [% css_fonts.size_bigger %];
+}
+
+.rdetail_result_count {
+    color: [% css_colors.text %];
+    font-weight: normal;
+    display: inline-block;
+    [% IF rtl == 't' -%]
+    padding-right: 1em;
+    [%- ELSE %]
+    padding-left: 1em;
+    [%- END %]
+}
+
+.rdetail_result_nav {
+    font-weight:normal;
+    display: inline-block;
+    [% IF rtl == 't' -%]
+    padding-right: 1em;
+    [%- ELSE %]
+    padding-left: 1em;
+    [%- END %]
+}
+
+#rdetail_details_table {
+    margin-top: 15px;
+}
+
+#rdetail_image { border: none; }
+#rdetail_image_cell {
+    padding-top: 3px;
+    [% IF rtl == 't' -%]
+    padding-left: 10px;
+    [%- ELSE %]
+    padding-right: 10px;
+    [%- END %]
+}
+
+h2.rdetail_uris {
+    margin-top: 1em;
+}
+div.rdetail_uris {
+    padding: 0.5em 1em 0.5em 1em;
+    background-color: [% css_colors.accent_lighter2 %];
+}
+div.rdetail_uris ul li {
+    position: relative;
+    [% IF rtl == 't' -%]
+    right: 1em;
+    [%- ELSE %]
+    left: 1em;
+    [%- END %]
+}
+
+div.rdetail_show_copies {
+    margin-top: 1em;
+}
+
+div#rdetail_actions_div {
+    background-color: [% css_colors.background %];
+    [% IF rtl == 't' -%]
+    float: left;
+    [%- ELSE %]
+    float: right;
+    [%- END %]
+}
+
+span#rdetail_copy_counts {
+    display: inline-block;
+    vertical-align: top;
+    [% IF rtl == 't' -%]
+    border-left: thin;
+    margin-left: 1em;
+    padding-left: 1em;
+    [%- ELSE %]
+    border-right: thin;
+    margin-right: 1em;
+    padding-right: 1em;
+    [%- END %]
+}
+
+span#rdetail_hold_counts {
+    display: inline-block;
+    vertical-align: top;
+}
+span#rdetail_hold_counts p {
+    [% IF rtl == 't' -%]
+    padding-right: 2em;
+    [%- ELSE %]
+    padding-left: 2em;
+    [%- END %]
+}
+
+#rdetail_image_div {
+    [% IF rtl == 't' -%]
+    float: right;
+    margin-left: 1em;
+    [%- ELSE %]
+    float: left;
+    margin-right: 1em;
+    [%- END %]
+}
+
+.rdetail_aux_utils {
+    padding-bottom: 6px;
+    [% IF rtl == 't' -%]
+    border-right: 1px dotted [% css_colors.accent_light %];
+    padding-right:17px;
+    padding-left:70px;
+    [%- ELSE %]
+    border-left:1px dotted [% css_colors.accent_light %];
+    padding-left: 17px;
+    padding-right: 70px;
+    [%- END %]
+}
+
+div.place_hold {
+    border-bottom: 1px dotted [% css_colors.accent_light %];
+    padding-top: 10px;
+}
+
+div.toggle_list { padding-top: 6px; }
+
+div.share_record {
+    padding-top: 6px;
+    border-top: 1px dotted [% css_colors.accent_light %];
+}
+
+div.format_icon {
+    [% IF rtl == 't' -%]
+    float: left;
+    margin-left: 17px;
+    [%- ELSE %]
+    float: right;
+    margin-right: 17px;
+    [%- END %]
+}
+
+#metarecord_population {
+    overflow: hidden;
+    width: 40%;
+    padding-bottom: 10px;
+}
+
+.metarecord_population_span_link {
+    line-height: 20px;
+}
+
+.metarecord_population_format {
+    border-right: thin;
+    display: inline-block;
+    margin-right: 1em;
+    padding-right: 1em;
+    vertical-align: top;
+}
+
+.metarecord_population_item_lang {
+    display: inline-block;
+    vertical-align: top;
+}
+
+.metarecord_population_all {
+    padding-top:10px;
+}
+
+.results_aux_utils {
+    display: table-cell;
+}
+
+.result_util {
+    border-bottom: 1px dotted [% css_colors.accent_light %];
+    padding-top: 6px;
+    [% IF rtl == 't' -%]
+    padding-right: 1em;
+    [%- ELSE %]
+    padding-left: 1em;
+    [%- END %]
+}
+
+.results_reviews {
+    top: -5px;
+}
+
+#rdetail_copies {
+    padding-top: 1.5em;
+}
+
+#rdetails_status td {
+    [% IF rtl == 't' -%]
+    padding: 7px 13px 3px 0px;
+    [%- ELSE %]
+    padding: 7px 0px 3px 13px;
+    [%- END %]
+}
+
+#rdetails_status td[headers=copy_header_library], 
+#rdetails_status td[headers=copy_header_shelfloc] {
+    white-space: normal;
+}
+
+#rdetails_status thead th {
+    [% IF rtl == 't' -%]
+    padding: 13px 13px 13px 0px;
+    text-align: right;
+    [%- ELSE %]
+    padding: 13px 0px 13px 13px;
+    text-align: left;
+    [%- END %]
+    background-color: [% css_colors.accent_lighter2 %];
+    font-size: [% css_fonts.size_smaller %];
+    font-weight: bold;
+}
+
+#rdetails_status tbody td {
+    [% IF rtl == 't' -%]
+    padding-right: 13px;
+    text-align: right;
+    [%- ELSE %]
+    padding-left: 13px;
+    text-align: left;
+    [%- END %]
+}
+#rdetails_status tbody td.copy_note {
+    color: [% css_colors.primary %];
+    text-wrap:normal;
+    white-space:pre-wrap !important;
+    word-wrap:normal;
+}
+
+#rdetails_status tbody td.copy_tag {
+    border-color: [% css_colors.primary %];
+    border-style: dashed;
+    border-width: 2px;
+}
+#rdetails_status tbody .copy_tag_value {
+    font-weight: bolder;
+}
+
+.rdetail_extras {
+    background-color: [% css_colors.primary_fade %];
+    border: 1px solid [% css_colors.primary %];
+    padding-top:1px;
+    clear:both;
+}
+
+#rdetail_extras_div {
+    margin: 1em 0;
+}
+
+.rdetail_extras_hr {
+    [% IF rtl == 't' -%]
+    margin-right: 1px;
+    margin-left: 1px;
+    [%- ELSE %]
+    margin-left: 1px;
+    margin-right: 1px;
+    [%- END %]
+    height: 1px;
+    background-color: [% css_colors.accent_light %];
+}
+
+.rdetail_extras_link {
+    padding: 6px 12px;
+    font-size: [% css_fonts.size_smaller %];
+    font-weight: bold;
+}
+
+.rdetail_extras_lbl {
+    color: [% css_colors.text_invert %];
+    text-decoration: none;
+}
+
+#rdetail_extras_expand, #rdetail_extras_collapse, #rdetail_locs_collapse {
+    [% IF rtl == 't' -%]
+    margin-right: 13px;
+    [%- ELSE %]
+    margin-left: 13px;
+    [%- END %]
+}
+
+#rdetail_locs_expand, #rdetail_locs_collapse {
+    [% IF rtl == 't' -%]
+    margin-right: 13px;
+    [%- ELSE %]
+    margin-left:13px;
+    [%- END %]
+    padding-bottom:3px;
+    margin-top:15px;
+}
+
+#rdetail_anotes_div .biography {
+    margin:0;
+}
+
+#gbp_extra, #gbp_arrow_down_link {
+    display: none;
+}
+
+.almost-content-wrapper {
+    background-color: [% css_colors.background %];
+}
+
+#content-wrapper {
+    background-color: [% css_colors.background %];
+    min-height: 600px;
+        height: auto;
+    display: grid;
+}
+
+.content-wrapper-record-page { top: -15px; position: relative; }
+
+#main-content-home {
+    [% IF rtl == 't' -%]
+    padding-right: 17px;
+    margin-right: 1em;
+    [%- ELSE %]
+    padding-left: 17px;
+    margin-left: 1em;
+    [%- END %]
+}
+#main-content {
+    [% IF rtl == 't' -%]
+    padding-right: 0px;
+    [%- ELSE %]
+    padding-left: 0px;
+    [%- END %]
+    margin: 0 1em;
+    clear: both;   
+}
+
+#main-content-after-bar {
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    padding-right: 4px;
+    [%- ELSE %]
+    margin-left: 1em;
+    padding-left: 4px;
+    [%- END %]
+}
+
+#results-side-bar {
+    [% IF rtl == 't' -%]
+    float: right; 
+    margin-left: 5px;
+    [%- ELSE %]
+    float: left;
+    margin-right: 5px; 
+    [%- END %]
+    width: 174px;
+    background-color: [% css_colors.background %];
+}
+
+#main-content .login_boxes {
+    border: 1px solid [% css_colors.accent_lighter %];
+    background-color: [% css_colors.accent_lightest %];
+    background-image: -moz-linear-gradient(bottom, [% css_colors.accent_ultralight %] 15%, [% css_colors.accent_lightest %] 55%, [% css_colors.accent_ultralight %] 85%);
+    background-image: -ms-linear-gradient(bottom, [% css_colors.accent_ultralight %] 15%, [% css_colors.accent_lightest %] 55%, [% css_colors.accent_ultralight %] 85%);
+    background-image: -o-linear-gradient(bottom, [% css_colors.accent_ultralight %] 15%, [% css_colors.accent_lightest %] 55%, [% css_colors.accent_ultralight %] 85%);
+    background-image: -webkit-linear-gradient(bottom, [% css_colors.accent_ultralight %] 15%, [% css_colors.accent_lightest %] 55%, [% css_colors.accent_ultralight %] 85%);
+    background-image: linear-gradient(to bottom, [% css_colors.accent_ultralight %] 15%, [% css_colors.accent_lightest %] 55%, [% css_colors.accent_ultralight %] 85%);
+    color: [% css_colors.accent_darker %];
+}
+
+#main-content .login_boxes h1 {
+    font-weight: normal;
+    font-size: [% css_fonts.size_biggest %];
+    margin:0;
+}
+
+#main-content .left_brain {
+    [% IF rtl == 't' -%]
+    padding-right: 28px;
+    [% ELSE -%]
+    padding-left:28px;
+    [% END -%]
+    padding-top:25px;
+}
+
+#main-content .left_brain input[type=text], #main-content .left_brain input[type=password] {
+    width:167px;
+    margin:0;
+    padding:0;
+    background-color: [% css_colors.background %];
+    font-size: [% css_fonts.size_bigger %];
+    color: [% css_colors.text %];
+}
+
+#main-content .left_brain .input_bg {
+    [% IF rtl == 't' -%]
+    padding: 10px 13px 0px 10px;
+    [% ELSE -%]
+    padding:10px 10px 0px 13px;
+    [% END -%]
+    width:167px;
+    height:29px;
+}
+
+.login-help-box {
+    [% IF rtl == 't' -%]
+    float: right;
+    margin-right: 2em;
+    [% ELSE -%]
+    float: left;
+    margin-left: 2em;
+    [% END -%]
+    width: 200px;
+    text-align: center;
+}
+
+.login-help-button {
+    font-size: [% css_fonts.size_biggest %];
+    padding: 1.5em;
+}
+
+#login-failed-message {
+    font-size: [% css_fonts.size_bigger %];
+    font-weight: bold;
+    color: [% css_colors.text_alert %];
+    padding-top: 1em;
+}
+
+#holds_temp_parent td {
+    border-bottom:1px solid [% css_colors.border_standard %];
+}
+
+#holds_temp_parent input, #holds_temp_parent select {
+    margin:0;
+}
+
+#holds_temp_parent tr td div {
+    margin-top: 10px;
+    margin-bottom: 10px;
+}
+
+#holds_temp_parent tr td div.format_icon {
+    margin-top: 6px;
+    margin-bottom: 6px;
+    float: none;
+}
+
+#results_header_bar {
+    background-color: [% css_colors.accent_medium %];
+    border-top:1px solid [% css_colors.accent_mediumdark %];
+    border-bottom:1px solid [% css_colors.accent_mediumdark %];
+}
+
+.results_header_lbl {
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float: left;
+    [% END -%]
+    font-weight: bold;
+    color: [% css_colors.text %];
+    /* this border is not visible, but it keeps these labels the same size
+    as the buttons */
+    border: 1px solid [% css_colors.accent_medium %];
+    background-color: [% css_colors.accent_medium %];
+    margin: 0.5em 0.3em;
+    padding: 0.3em;
+}
+/* we need a negative margin on the select to allow the containing <label>
+to determine the actual size of the element.  By doing this, we can increase
+the odds that the buttons and the select will be the same size, and therefore
+collapse correctly when the window width decreases */
+.results_header_lbl select {
+    margin: -0.5em 0;
+}
+
+#limit_to_available {
+    vertical-align: middle;
+}
+
+.results_header_sel {
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+    margin:0;
+}
+
+
+
+.results_header_nav1 .h1 {
+    font-size: [% css_fonts.size_bigger %];
+    font-weight:bold;
+    color:[% css_colors.primary_fade %];
+}
+
+.table_no_border_space {
+  border-spacing: 0px;
+}
+.table_no_cell_pad td {
+  padding: 0px;
+}
+.table_no_border {
+  border-collapse: collapse;
+}
+
+#result_table_table {
+  margin-top:10px;
+}
+
+#result_table_div {
+    margin-top: 1em;
+}
+
+tr.result_table_row > td.results_row_count,
+tr.result_table_row > td.result_table_pic_header,
+tr.result_table_row > td.result_table_title_cell {
+    vertical-align: top;
+}
+
+.result_metadata {
+    [% IF rtl == 't' -%]
+    float: right;
+    padding-left: 2em;
+    [% ELSE -%]
+    float: left;
+    padding-right: 2em;
+    [% END -%]
+    width: 30em;
+}
+
+tr.result_table_row:nth-child(n+2) > td {
+    border-top: 1px solid [% css_colors.accent_lighter2 %];
+}
+
+tr.result_table_row > td.result_table_pic_header {
+    [% IF rtl == 't' -%]
+    padding-right: 1em;
+    [% ELSE -%]
+    padding-left: 1em;
+    [% END -%]
+    white-space: nowrap;
+    width: 78px;
+}
+
+/* styles for selecting records in the results set */
+.result_table_row_selected {
+    background-color: [% css_colors.item_selected %];
+}
+#selected_records_summary, #clear_basket {
+    margin-left: 5em;
+}
+
+/* styles for the basket */
+#record_basket {
+    [% IF rtl == 't' -%]
+    float: left;
+    margin-left: 5em;
+    [% ELSE; %]
+    float: right;
+    margin-right: 5em;
+    [% END; %]
+}
+#record_basket_icon {
+    [% IF rtl == 't' -%]
+    float: left;
+    margin-left: 2em;
+    [% ELSE; %]
+    float: right;
+    margin-right: 2em;
+    [% END; %]
+    position: relative;
+}
+#record_basket_count_floater {
+    background-color: [% css_colors.accent_lighter %];
+    position: absolute;
+    top: -3px;
+    right: -3px; /* relative to icon, so don't want to adjust for RTL */
+    z-index: 2;
+    border-radius: 50%;
+}
+#record_basket_count_floater a {
+    text-decoration: none;
+}
+#basket_actions {
+    [% IF rtl == 't' -%]
+    float: left;
+    [% ELSE; %]
+    float: right;
+    [% END; %]
+}
+#basket_actions select {
+    border-color: rgb(169, 169, 169);
+}
+
+.result_number {
+    [% IF rtl == 't' -%]
+    padding-right: 1em;
+    [% ELSE -%]
+    padding-left: 1em;
+    [% END -%]
+    white-space: nowrap;
+}
+
+.result_count_number {
+   font-weight: bold;
+}
+
+.result_table_subtable {
+    width: 100%;
+    padding-top: 1em;
+}
+
+div.result_table_utils_cont {
+    [% IF rtl == 't' -%]
+    text-align: right;
+    float: right;
+    [% ELSE -%]
+    text-align:left;
+    float: left;
+    [% END -%]
+}
+
+#myopac_summary_div p {
+    margin:0;
+    margin-bottom: 10px;
+}
+
+#acct_sum_checked_table td {
+    padding-bottom:5px;
+}
+
+.zero_search_hits_main {
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+    width:300px;
+    margin-top: 2ex;
+}
+.zero_search_hits_saved {
+    [% IF rtl == 't' -%]
+    float: right;
+    margin-left: 2em;
+    [% ELSE -%]
+    float:left;
+    margin-right: 2em;
+    [% END -%]
+    width:200px;
+    margin-top: 2ex;
+}
+
+#zero_search_hits p {
+    margin-top:0;
+}
+
+#zero_hits_term {
+    font-weight: bold;
+}
+
+#zero_search_hits #spell_check_link {
+}
+
+#zero_search_hits #zero_hits_suggestions {
+}
+
+#lowhits_help {
+    [% IF rtl == 't' -%]
+    float: left;
+    [% ELSE -%]
+    float: right;
+    [% END-%]
+    width: 353px;
+    background-color: [% css_colors.accent_light %];
+    padding: 10px;
+    margin-top: 7px;
+}
+
+.results_info_table td {
+    [% IF rtl == 't' -%]
+    padding-left: 10px;
+    [% ELSE -%]
+    padding-right: 10px;
+    [% END-%]
+}
+
+#myopac_holds_main_table {
+    border-collapse: collapse;
+}
+
+#myopac_holds_main_table td {
+    border: 1px solid [% css_colors.border_dark %];
+    
+}
+
+.myopac-hold-available {
+    color: [% css_colors.text_greatnews %];
+    font-weight: bold;
+}
+
+.myopac-hold-suspended {
+    color: [% css_colors.text_badnews %];
+    font-weight: bold;
+}
+
+#myopac_prefs_div .data_grid {
+    border-collapse:collapse;
+}
+
+#myopac_prefs_div .data_grid td {
+    [% IF rtl == 't' -%]
+    padding: 6px 17px 7px 0px;
+    [% ELSE -%]
+    padding:6px 0px 7px 17px;
+    [% END -%]
+    background-color:[% css_colors.accent_ultralight %];
+    border-bottom:3px solid [% css_colors.background %];
+}
+
+.header_middle {
+    [% IF rtl == 't' -%]
+    padding: 0px 0px 0px 7px;
+    [% ELSE -%]
+    padding: 0px 7px 0px 0px;
+    [% END -%]
+    height:22px;
+    font-size: [% css_fonts.size_bigger %];
+    font-weight:bold;
+    color:[% css_colors.primary_header %];
+    /*border-bottom: 1px dotted [% css_colors.accent_light %];*/
+    clear: both;
+}
+
+.header_middle a {
+    font-weight: normal;
+}
+
+#acct_sum_block {
+    [% IF rtl == 't' -%]
+    float: right;
+    clear: right;
+    [% ELSE -%]
+    float: left;
+    clear: left;
+    [% END -%]
+}
+
+.acct_sum_table {
+    border-collapse: collapse;
+    background-color: [% css_colors.accent_ultralight %];
+}
+
+.acct_sum_table tr {
+    border-bottom: 2px solid white;
+}
+
+.acct_sum_table td {
+    padding: 1em;
+}
+
+.acct_sum_table a {
+    text-transform: none;
+    position:relative;
+    top:-1px;
+}
+
+.acct_sum_table .view_link {
+    font-weight: normal;
+}
+
+#myopac_sum_fines {
+    [% IF rtl == 't' -%]
+    float: left;
+    padding: 15px 23px 0px 0px;
+    [% ELSE -%]
+    float:right;
+    padding: 15px 0px 0px 23px;
+    [% END -%]
+    width: 177px;
+}
+
+.cc_logo_img
+{
+   width: 79%;
+    [% IF rtl == 't' -%]
+      padding-right: .5em;
+    [% ELSE -%]
+       padding-left: .5em;
+    [% END -%]
+}
+
+#myopac_sum_fines_placehold {
+    [% IF rtl == 't' -%]
+    float: left;
+    [% ELSE -%]
+    float: right;
+    [% END -%]
+    width: 177px;
+    height: 166px;
+}
+
+.acct_holds_temp td {
+    [% IF rtl == 't' -%]
+    text-align: right;
+    [% ELSE -%]
+    text-align: left;
+    [% END -%]
+}
+
+#acct_checked_tabs .align, #acct_holds_tabs .align, #acct_prefs_tabs .align {
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+}
+
+
+
+.acct_fines_header
+{
+   width: 100%;
+   padding-bottom:1em;
+   [% IF rtl == 't' -%]
+   float: right;
+   [% ELSE -%]
+   float:left;
+   [% END -%]
+}
+
+/* tables for entering payment information */
+#acct_fines_main_header
+{
+   width: 100%;
+}
+
+/* tables for entering payment information */
+#billing_info_table
+{
+    width: auto;
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+    display:inline;
+    padding-left: 1em;
+    padding-right: 1em;
+}
+
+/* tables for entering payment information */
+#credit_card_info_table
+{
+    width: auto;
+    display: block;
+}
+
+/* tables for entering payment information */
+#billing_info_table td,
+#credit_card_info_table td
+{
+   [% IF rtl == 't' -%]
+    padding-right: .5em;
+    [% ELSE -%]
+    padding-left: .5em;
+    [% END -%]
+   
+}
+
+/* div for the payment buttons with the total */
+#payment_actions
+{
+   width: auto;
+   display: block;
+   padding: 1em 0 0 1em;
+   text-align:center;
+}
+
+/* text to state what is seen on cc statement */
+.cc_disclaimer
+{
+   font-style:italic;
+   font-size: 90%;
+   color:[% css_colors.primary_fade %];
+}
+
+.hold_notes {
+    text-transform: none;
+    font-weight: normal;
+}
+
+
+
+#acct_list_header select, #acct_list_header_anon select {
+    font-weight:normal;
+    text-transform:none;
+}
+
+.search_catalog_lbl {
+    font-size: [% css_fonts.size_bigger %];
+}
+.adv_search_catalog_lbl {
+    font-size: [% css_fonts.size_bigger %];
+}
+
+.browse_the_catalog_lbl {
+    font-size: [% css_fonts.size_bigger %];
+}
+
+.lbl1 {
+    font-size: [% css_fonts.size_bigger %];
+    font-weight:bold;
+}
+
+.lbl2 {
+    font-size: [% css_fonts.size_smaller %];
+    font-weight:normal;
+    position:relative;
+    top:3px;
+    max-width:300px;
+}
+
+#fines_payments_wrapper {
+    padding-left: 5px; 
+    padding-right: 5px;
+}
+
+.myopac_payments_table th {
+    [% IF rtl == 't' -%]
+    text-align: right; }
+    [% ELSE -%]
+    text-align: left;
+    [% END -%]
+}
+
+.myopac_payments_table thead th { border-bottom: 1px dashed [% css_colors.accent_darker %]; }
+.myopac_payments_table thead th:first-child { width: 8em; }
+.myopac_payments_table tbody tr:nth-child(odd) { background-color: [% css_colors.accent_lighter %]; }
+.myopac_payments_table form { display: inline; }
+.myopac_payments_table input[type="submit"] { padding: 1px; }
+
+.cc_header
+{
+   padding-top:1em;
+   background-color: [% css_colors.accent_lighter %];
+}
+
+.payment-error {
+    font-weight: bold; color: [% css_colors.text_alert %];
+    padding: 10px; border: 1px solid [% css_colors.accent_medium_dark %];
+}
+
+.payment-processing {
+    font-weight: bold;
+    color: [% css_colors.text_greatnews %];
+    font-size: [% css_fonts.size_bigger %];
+    padding: 10px; border: 1px solid [% css_colors.accent_medium_dark %];
+    text-align: center;
+}
+#adv_search_input { width: 100%; }
+#adv_search_parent {
+    margin-bottom:0px;
+}
+#search-submit-spinner {
+    height: 16px;
+    width: 16px;
+}
+div.adv_search_available {
+    margin-top: 1em;
+}
+
+#myopac_loading {
+    width:100%;
+    text-align:center;
+    padding-top:20px;
+    font-size: [% css_fonts.size_bigger %];
+    font-weight:bold;
+}
+
+.chili_link {
+    width:100px !important;
+    text-align: center !important;
+}
+
+.chili_review div.chili_link div {
+    margin: auto;
+}
+
+/* styling for advanced search filters that display with searchbar */
+
+#adv_filter_results_block h4 { display: inline; }
+
+#adv_filter_results_block {
+    margin-top: 6px;
+    text-align: center;
+}
+
+.adv_filter_results_block_label {
+    font-weight:bold;
+    [% IF rtl == 't' -%]
+    padding: 4px 12px 4px 0px;
+    [% ELSE -%]
+    padding: 4px 0px 4px 12px;
+    [% END -%]
+}
+
+.adv_filter_results_group {
+    font-size: 12px;
+    display: inline-block;
+    background-color:  [% css_colors.accent_lighter2 %];
+    border-style: solid;
+    border-color: [% css_colors.accent_medium %];
+    border-width: 1px;
+    border-radius: 4px;
+    padding: 2px 4px 2px 4px;
+}
+
+.adv_filter_results_group_wrapper {
+    display: inline-block;
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    [% ELSE -%]
+    margin-left: 1em;
+    [% END -%]
+    margin-bottom: 8px;
+}
+
+.adv_filter_results_group_header {
+    display: inline-block;
+}
+
+.adv_filter_results_group_values {
+    display: inline;
+}
+
+.adv_search_result_filter {
+    display: inline-block;
+    font-size: 16px;
+}
+
+.remove_filter {
+    font-size: 16px;
+    [% IF rtl == 't' -%]
+    margin-right: 3px;
+    [% ELSE -%]
+    margin-left: 3px;
+    [% END -%]
+}
+
+a.remove_filter {
+    text-decoration: none;
+    color: [% css_colors.accent_dark %];
+}
+
+#filter_hits {
+     display: inline-block;
+}
+
+#refine_search_link {
+    display: inline-block;
+}
+
+[%- IF we_want_to_turn_on_facet_styling.defined; %]
+/* some facet styling */
+.facetClassContainer { margin: 2px; border: 1px solid [% css_colors.accent_light %]; }
+.facetClassLabelContainer { border: 1px solid [% css_colors.accent_light %]; }
+.facetClassLabel { font-weight: bold; text-align: center; }
+.facetFieldContainer {  }
+.facetFieldLabel { padding-left: 2px; margin-top: 5px; margin-bottom: 5px; font-weight: bold; text-align: left; }
+.extraFacetFieldsWrapper { }
+.toggleExtraFacetFieldsButton { float: right; margin: 0px; padding: 0px; }
+.facetFieldLineCount {
+    display: inline-block;
+    border-right: 1px solid [% css_colors.accent_light %];
+    color: [% css_colors.accent_mediumdark %];
+    width: 3em;
+    margin-right: 3px
+}
+.facetField { border-top: 1px solid [% css_colors.accent_light %]; }
+.facetFields { padding-left: 5px; }
+.facetFieldLineValue { overflow: hidden; text-overflow: ellipsis; }
+[%- END -%]
+
+div#facet_sidebar {
+    [% IF rtl == 't' -%]
+    float: right;
+    margin-left: 1em;
+    [% ELSE -%]
+    float: left;
+    margin-right: 1em;
+    [% END-%]
+}
+
+.facet_box_temp {
+    padding-bottom:3px;
+}
+
+.facet_box_temp .header {
+    height: 2.3em;
+    background-color:[% css_colors.primary %];
+    border-top-left-radius: 5px;
+    border-top-right-radius: 5px;
+    font-weight:bold;
+    color:[% css_colors.text_invert %];
+    padding-top:4px;
+}
+
+.facet_box_temp.filter_box_temp .header {
+    background-color: [% css_colors.background_invert %] !important;
+}
+
+.filter_box_label {
+    color: [% css_colors.background_invert %];
+    font-weight:bold;
+    padding-top:4px;
+    padding-bottom:4px;
+    padding-left:12px;
+}
+
+.facet_box_temp .header .title {
+    [% IF rtl == 't' -%]
+    float: right;
+    padding-right: 12px;
+    [% ELSE -%]
+    float:left;
+    padding-left:12px;
+    [% END -%]
+    padding-top:6px;
+}
+
+/* in this context, where h4 is primarily for structure, 
+   avoid the normal large font and margin for h4's */
+.facet_box_temp h4 {
+    font-size : 100%; 
+    margin: 0px;
+}
+
+.facet_box_temp .header a.button {
+    [% IF rtl == 't' -%]
+    float: left;
+    padding-left: 6px;
+    [% ELSE -%]
+    float:right;
+    padding-right:6px;
+    [% END -%]
+    padding-top:6px;
+    color:[% css_colors.text_invert %];
+}
+
+.facet_box_wrapper .box_wrapper .box {
+    border-top:1px solid [% css_colors.border_standard %];
+    border-left:1px solid [% css_colors.border_standard %];
+    border-right:1px solid [% css_colors.border_standard %];
+    padding: 0 0.5em;
+    overflow: hidden;
+}
+
+.filter_box_wrapper {
+    margin-bottom: 3px;
+    padding: 2px;
+    border: 1px solid [% css_colors.background_invert %];
+    border-radius: 3px;
+    font-weight:bold;
+    padding-top:4px;
+}
+
+.facet_template {
+    box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    display: table;
+    margin: 0.5em 0;
+    width: 100%;
+}
+
+.facet_template div {
+    display: table-cell;
+    padding: 2px;
+}
+
+.facet_template.filter_template div {
+    padding: 0px !important;
+}
+
+.facet_template .count {
+    text-align: right;
+    color: [% css_colors.accent_mediumdark %];
+}
+
+.facet_template_selected {
+    background-color: [% css_colors.accent_lighter2 %];
+    border: 1px solid [% css_colors.accent_medium %];
+}
+
+#footer-wrap {
+       /* border-top: 5px solid [% css_colors.nav_separation %]; */
+    background-color: [% css_colors.footer_primary %];
+    background-image: -moz-linear-gradient([% css_colors.footer_primary_fade %], [% css_colors.footer_primary %]);
+    background-image: -o-linear-gradient([% css_colors.footer_primary_fade %], [% css_colors.footer_primary %]);
+    background-image: -webkit-linear-gradient([% css_colors.footer_primary_fade %], [% css_colors.footer_primary %]);
+    background-image: linear-gradient([% css_colors.footer_primary_fade %], [% css_colors.footer_primary %]);
+    bottom: 0px;
+    width: 100%;
+}
+
+#footer {
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    [% ELSE -%]
+    margin-left: 1em;
+    [% END -%]
+    padding-top:5px;
+    padding-bottom: 10px;
+    font-size: [% css_fonts.size_small %];
+}
+
+
+#copyright_text, #footer_logo {
+    color: [% css_colors.footer_link %];
+    font-size: [% css_fonts.size_bigger %];
+}
+
+.color_4 {
+    font-weight: bold;
+}
+
+.advanced_div { padding-top: 15px; }
+#adv_global_search { width: 100%; }
+#adv_global_addrow td { padding-top: 7px; }
+[% IF rtl == 't' -%]
+#adv_global_addrow.td-search-left{text-align:right;}
+[% END -%]
+#adv_global_input_table { width: 100%; }
+#adv_global_input_table select { width: 7em; }
+.adv_adv_link {
+    font-size: [% css_fonts.size_smaller %];
+    color: [% css_colors.text_alert %];
+}
+#acct_prefs_header {
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float: left;
+    [% END -%]
+}
+.search_page_nav_link {
+    cursor: pointer;
+    [% IF rtl == 't' -%]
+    padding-right: 1em;
+    [% ELSE -%]
+    padding-left: 1em;
+    [% END -%]
+}
+#opac.result.sort { width: 160px; }
+.renew-summary, .message-update-summary { font-size: [% css_fonts.size_bigger %]; font-style: italic; margin: 0.5ex 0; }
+.failure-text {
+    [% IF rtl == 't' -%]
+    margin-right: 4em;
+    [% ELSE -%]
+    margin-left: 4em;
+    [% END -%]
+    font-style: italic;
+    color: [% css_colors.text_alert %];
+}
+.refine-controls { font-size: [% css_fonts.size_bigger %]; padding: 0.5ex 0; }
+#adv_search_refine input[type=text] { border: 1px inset [% css_colors.accent_light %] !important; }
+#adv_search_refine select { border: 1px inset [% css_colors.accent_light %] !important; }
+#adv_search_refine {
+    [% IF rtl == 't' -%]
+    padding-right: 5em;
+    [% ELSE -%]
+    padding-left: 5em;
+    [% END -%]
+    background-color: [% css_colors.accent_lighter2 %];
+    margin: 2ex 0;
+}
+.row-remover { position: relative; top: 1px; vertical-align: middle; }
+.subtle-button {
+    background-color: [% css_colors.background %];
+    color: [% css_colors.primary %]; text-decoration: none;
+    padding: 0; border: 0; margin: 0;
+    vertical-align: middle;
+}
+.subtle-button:hover { text-decoration: underline; cursor: pointer; }
+.no-dec:hover { text-decoration: none; }
+.pending-addr td { background-color: [% css_colors.background_alert %] !important; border: 0 !important; }
+
+#account-update-email table { text-align: center; padding: 20px; margin-top: 18px; border-collapse: collapse; }
+#account-update-email table td {
+    [% IF rtl == 't' -%]
+    text-align: right;
+    [% ELSE -%]
+    text-align: left;
+    [% END -%]
+    padding: 5px 15px 5px 15px;
+    border-bottom: 1px solid [% css_colors.accent_lighter %];
+}
+#account-update-email-error { font-size: [% css_fonts.size_biggest %]; padding: 10px; border:1px solid [% css_colors.border_standard %];}
+a.dash-link:hover { text-decoration: underline !important; }
+#list_create_table td { vertical-align: middle; padding: 0 8px; }
+#list_create_table {
+    background-color: [% css_colors.accent_light %];
+    padding-bottom: 4px;
+    margin-bottom: 10px;
+    border-bottom: 1px dotted [% css_colors.accent_medium %];
+    width: 100%;
+}
+.list_create_table_label {
+    width: 30%;
+}
+#list_description, #list_create_name {
+        width: 500px;
+}
+.list-create-table-buttons input[type=image] { margin-top: 2px; }
+.result_table_format_cell { padding: 0px 10px; text-align: center; }
+.results_row_count { font-weight: bold; }
+
+.fmt-note {
+    [% IF rtl == 't' -%]
+    padding-right: 1em !important;
+    [% ELSE -%]
+    padding-left: 1em !important;
+    [% END -%]
+    vertical-align: middle;
+}
+.hold-editor-controls { text-align: center; padding-top: 1em !important; }
+.hold-editor-controls a { padding-left: 2em; }
+
+.text-right {
+    [% IF rtl == 't' -%]
+    text-align: left;
+    [% ELSE -%]
+    text-align: right;
+    [% END -%]
+}
+.text-right-top {
+    [% IF rtl == 't' -%]
+    text-align: right;
+    [% ELSE -%]
+    vertical-align: top;
+    [% END -%]
+}
+.rdetail-author-div {
+    padding-bottom: 10px;
+    display: inline-block;
+}
+
+.invisible { visibility: hidden; }
+.rdetail-extras-summary { margin: 10px; }
+.staff-hold { background-color: [% css_colors.accent_lightest %]; }
+.expert-search tbody tr th {
+    [% IF rtl == 't' -%]
+    text-align: left;
+    padding-right: 2em;
+    [% ELSE -%]
+    text-align: right;
+    padding-left: 2em;
+    [% END -%]
+}
+.expert-search-row { padding-top: 10px; }
+#adv_expert_row label { font-weight: bold; }
+
+.bookshelf tr.browse_border td {
+    border-bottom: 1px dashed [% css_colors.accent_dark %];
+    font-size: 1px;
+    height: 1px;
+    padding-bottom: 1ex;
+}
+.cn_browse_item { padding: 2ex; }
+.results-paginator-list {
+    [% IF rtl == 't' -%]
+    padding-right: 1em;
+    [% ELSE -%]
+    padding-left: 1em;
+    [% END -%]
+}
+.results-paginator-selected { color: [% css_colors.text_alert %]; }
+.inactive-hold { background-color: [% css_colors.inactive_hold %]; }
+.unread-patron-message { font-weight: bold; }
+
+#hold-items-list td { padding: 5px; margin-bottom: 20px; }
+.hold-items-list-title { 
+  font-size: [% css_fonts.size_bigger %];
+  margin-bottom: 20px;
+}
+.hold-items-list-problem { color: [% css_colors.text_alert %]; }
+
+.hold_success_links > span { margin: 0 2em; }
+
+.radio-parts-selection { 
+   width: 75%;
+   margin-bottom: 20px;
+}
+
+.parts-radio-option { 
+  display: inline-block;
+  width:15em;
+}
+
+.mr_holds_no_formats { 
+  [% IF rtl == 't' -%]
+  margin-right: 25px;
+  [% ELSE -%]
+  margin-left: 25px;
+  [% END -%]
+  padding: 5px;
+  font-size: 110%;
+  font-weight: bold;
+  color: [% css_colors.text_invert %]; 
+  background-color: [% css_colors.primary %];
+}
+.holds_item_row_separator td {
+  border-top: 2px dashed [% css_colors.accent_medium %];
+}
+
+.big-strong {font-weight: bold; font-size: [% css_fonts.size_bigger %]; }
+
+.results_header_btns, .results_header_sel {
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+}
+
+/*
+ * .various_containers a = shortcut to putting .opac-button on every 'a' with
+ *     the tradeoff of increased stylesheet complexity (TODO: rethink?)
+ */
+
+.opac-multiline-button > a,
+.opac-button, .results_header_btns a, #simple-detail-view-links a, .dash_account_buttons a {
+    color: [% css_colors.button_text %];
+    font-weight: bold; 
+    text-decoration: none;
+    cursor: pointer !important;
+    border-radius: 5px;
+    border: 1px solid [% css_colors.primary %];
+    background-color:  [% css_colors.primary_fade %];
+    margin: 0.5em;
+    padding: 0.3em;
+    display: inline-block;
+}
+
+.opac-multiline-button > a:hover,
+.opac-button:hover, .results_header_btns a:hover, #simple-detail-view-links a:hover, #dash_wrapper a.opac-button:hover {
+    background-color: [% css_colors.primary %];
+}
+
+.opac-button:disabled {
+    color: [% css_colors.accent_medium %];
+    cursor: pointer !important;
+    border: 1px solid [% css_colors.accent_light %];
+    background-color: [% css_colors.accent_lighter %];
+}
+
+/* Firefox adds its own special space to inputs; this gets us closer */
+button.opac-button::-moz-focus-inner, input.opac-button::-moz-focus-inner {
+    padding: 0;
+    border: 0;
+}
+
+.opac-button-header, #dash_wrapper .opac-button {
+    background-color: [% css_colors.control %];
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
+    font-size: [% css_fonts.size_base %];
+}
+a.opac-button-header:hover, #dash_wrapper a.opac-button:hover {
+    border-color: [% css_colors.control %];
+}
+
+.opac-multiline-button > a {
+    display: inline-block;
+}
+
+#myopac_checked_div {
+    padding: 0px;
+}
+
+.rdetail-mfhd-head {
+    margin-top: 5px;
+    padding-top: 5px;
+    background-color: [% css_colors.table_heading %];
+}
+
+.rdetail-mfhd-type {
+    [% IF rtl == 't' -%]
+    padding-right: 1em;
+    [% ELSE -%]
+    padding-left: 1em;
+    [% END -%]
+}
+
+.rdetail-mfhd-bottom {
+    border-bottom: thin solid [% css_colors.border_dark %];
+    width: 100%;
+}
+
+#rdetail_record_details {
+    clear: both;
+    margin-top: 1em;
+}
+
+.rdetail_content {
+    [% IF rtl == 't' -%]
+    margin-right: 1.5em;
+    padding-right: 1.5em;
+    [% ELSE -%]
+    margin-left: 1.5em;
+    padding-left: 1.5em;
+    [% END -%]
+}
+
+.rdetail_content_type, .rdetail_subject_type {
+    vertical-align: top;
+    font-weight: bold;
+}
+
+.bookbag-item-row td { vertical-align: top; }
+
+.error { color: [% css_colors.text_alert %]; font-weight: bold; }
+.success {
+    color: [% css_colors.text_greatnews %];
+    font-weight: bold;
+}
+
+.rdetail_related_subjects {
+    margin-top: 1.5em;
+}
+
+.rdetail_related_series {
+    margin-top: 1.5em;
+}
+
+#rdetail_openurl {
+    margin-top: 1em;
+}
+
+.rdetail_openurl_entry {
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    padding-right: 1em;
+    [% ELSE -%]
+    margin-left: 1em;
+    padding-left: 1em;
+    [% END -%]
+}
+.bookbag-controls-holder { width: 100%; }
+.bookbag-controls-holder:nth-child(odd) { background-color: [% css_colors.accent_lighter2 %]; }
+.bookbag-controls-holder:nth-child(even) { background-color: [% css_colors.accent_lightest %]; }
+.bookbag-controls-holder .most {
+    [% IF rtl == 't' -%]
+    padding-right: 0;
+    margin-left: 5em;
+    [% ELSE -%]
+    padding-left: 0;
+    margin-right: 5em;
+    [% END -%]
+}
+.bookbag-controls-title-block {
+    [% IF rtl == 't' -%]
+    float:right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+    width:40%;
+}
+.bookbag-controls-button-block {
+    [% IF rtl == 't' -%]
+    float:right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+    width:50%;
+}
+h2.bookbag-name { margin-bottom: 0em; padding-bottom: 0em; }
+.bookbag-share .fixed { min-width: 4em; }
+.bookbag-specific {
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    [% ELSE -%]
+    margin-left: 1em;
+    [% END -%]
+}
+.bookbag-specific div.sort {
+    [% IF rtl == 't' -%]
+    float: right;
+    text-align: right;
+    margin-right: 15px;
+    [% ELSE -%]
+    float: left;
+    text-align: left;
+    margin-left: 15px;
+    [% END -%]
+    width: 40%;
+}
+.bookbag-specific div.meta {
+    background-color: [% css_colors.accent_light %];
+    [% IF rtl == 't' -%]
+    float: left;
+    text-align: left;
+    [% ELSE -%]
+    float: right;
+    text-align: right;
+    [% END -%]
+    width: 54%;
+    border: 1px solid [% css_colors.accent_light %];
+    padding: 5px;
+    margin-top: 5px;
+}
+#bbag-name-desc-form tr th { vertical-align: middle; }
+#bbag-name-desc-form .saver { vertical-align: middle; text-align: center; }
+.bookbag-description {
+    padding-top: 0em;
+    font-style: italic;
+    max-width: 40em;
+}
+.bbag-edit-desc-label {
+    [% IF rtl == 't' -%]
+    float:right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+    width:8em;
+}
+.bbag-edit-desc-save {
+    clear:both;
+    margin-bottom:10px;
+}
+#bbag-edit-description {
+    width: 20em;
+    [% IF rtl == 't' -%]
+    float:right;
+    [% ELSE -%]
+    float:left;
+    [% END -%]
+}
+#bbag-edit-name {
+    width: 20em;
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float: left;
+    [% END -%]
+}
+.bbag-action {
+    [% IF rtl == 't' -%]
+    margin-right: 15px;
+    [% ELSE -%]
+    margin-left: 15px;
+    [% END -%]
+}
+.bbag-navigate-list {
+    [% IF rtl == 't' -%]
+    padding-right: 32px;
+    float:right;
+    [% ELSE -%]
+    padding-left: 32px;
+    float:left;
+    [% END -%]
+}
+.bbag-navigate-list-pages {
+    [% IF rtl == 't' -%]
+    padding-right: 10px;
+    float:right;
+    [% ELSE -%]
+    padding-left: 10px;
+    float:left;
+    [% END -%]
+}
+textarea { font-family: sans-serif; }
+table.bookbag-specific {
+    border-right: 1px solid [% css_colors.accent_dark %];
+    border-bottom: 1px solid [% css_colors.accent_medium %];
+    margin-bottom: 2ex;
+    width: 100%;
+}
+.bookbag-share {
+    [% IF rtl == 't' -%]
+    float: right;
+    padding: 0px 10px 0px 0px;
+    [% ELSE -%]
+    float: left;
+    padding: 0px 0px 0px 10px;
+    [% END -%]
+}
+.bookbag-share .fixed { min-width: 6em; }
+.bookbag-controls {
+    [% IF rtl == 't' -%]
+    float: right;
+    padding: 0px 10px 0px 0px;
+    [% ELSE -%]
+    float: left;
+    padding: 0px 0px 0px 10px;
+    [% END -%]
+}
+.bookbag-specific td.list_checkbox {
+    [% IF rtl == 't' -%]
+    padding-right: 10px !important;
+    [% ELSE -%]
+    padding-left: 10px !important;
+    [% END -%]
+}
+.bookbag-specific td.list_entry {
+    min-width: 10em;
+    [% IF rtl == 't' -%]
+    padding-right: 5px !important;
+    [% ELSE -%]
+    padding-left: 5px !important;
+    [% END -%]
+}
+.bookbag-specific td.list_actions {
+    white-space: nowrap !important;
+}
+.bookbag-paginator-selected { color: [% css_colors.text_alert %]; }
+
+.list_is_empty {
+    padding: 8px 0px 6px 0px;
+    width: 100%;
+    border: 0;
+    font-size: [% css_fonts.size_bigger %];
+    text-align: center;
+    font-style: italic;
+}
+.save-notes { padding-bottom: 1.5ex; }
+
+.nonbreaking-wrapper {
+    display: inline-block;
+}
+
+/* Moved from semiauto.css */
+.adv_global_input_container {
+    border-bottom: none;
+    clear: both;
+}
+.opac-auto-013 {
+    border-bottom: none;
+    *height: 0px;
+}
+.adv_global_filter_sort {
+    border: none;
+    width: 100%;
+}
+.clear-both { clear: both; }
+.common-no-pad {
+    clear: both;
+    height: 0px;
+    margin: 0px;
+    padding: 0px;
+}
+.common-full-pad {
+    clear: both;
+    height: 15px;
+}
+.opac-alert { color: [% css_colors.text_alert %]; }
+.float-left {
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float: left;
+    [% END -%]
+}
+.float-right {
+    [% IF rtl == 't' -%]
+    float: left;
+    [% ELSE -%]
+    float: right;
+    [% END -%]
+}
+
+.saved-searches-header { width: 100%; font-weight: bold; font-size: [% css_fonts.size_bigger %]; }
+.saved-searches-header .button {
+    [% IF rtl == 't' -%]
+    float: left;
+    [% ELSE -%]
+    float: right;
+    width: 28px;
+    [% END -%]
+}
+.saved-searches-header .text {
+    [% IF rtl == 't' -%]
+    float: right;
+    padding-left: 1em;
+    [% ELSE -%]
+    float: left;
+    padding-right: 1em;
+    [% END -%]
+    margin: 0.5ex 0;
+}
+.saved-searches-header {font-weight: bold; font-size: [% css_fonts.size_bigger %]; }
+.saved-searches { border-bottom: 1px solid [% css_colors.accent_medium %]; padding-right: 1em; }
+#staff-saved-search { /* wraps .saved-searches-header and .saved-searches on the record page */
+    [% IF rtl == 't' -%]
+    border-left: 1px solid [% css_colors.accent_darker %];
+    [% ELSE -%]
+    border-right: 1px solid [% css_colors.accent_darker %];
+    [% END -%]
+}
+.result_item_circulated {
+    padding-top: 4px;
+}
+
+.result_item_circulated span {
+    position: relative;
+    top:-3px;
+    [% IF rtl == 't' -%]
+    right: 3px;
+    [% ELSE -%]
+    left:3px;
+    [% END -%]
+}
+
+#search-only-bookbag-container { margin: 2ex 0; font-weight: bold; }
+#result-bookbag-heading { text-align: center; margin: 2ex; }
+
+.result-bookbag-name { font-size: [% css_fonts.size_bigger %]; font-weight: bold; }
+.result-bookbag-description { font-size: [% css_fonts.size_bigger %]; font-style: italic; }
+.result-bookbag-item-note { font-style: italic; }
+.lowhits-bookbag-name { font-weight: bold; }
+.oils_AS { font-weight: bold; color: [% css_colors.text_match %]; }
+.oils_AS_match_term {
+    [% IF rtl == 't' -%]
+    text-align: right;
+    [% ELSE -%]
+    text-align: left;
+    [% END -%]
+    color: [% css_colors.text %];
+}
+.oils_AS_match_field {
+    font-size: [% css_fonts.size_smallest %]; padding: 0.65em 0;
+    [% IF rtl == 't' -%]
+    text-align: left;
+    [% ELSE -%]
+    text-align: right;
+    [% END -%]
+    color: [% css_colors.accent_medium %];
+}
+table.result_holdings_table {
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
+table.result_holdings_table thead tr {
+    background-color: [% css_colors.table_heading %];
+}
+table.result_holdings_table thead tr th {
+    font-weight: bold;
+}
+span.preflib {
+    margin: 0 2em 0 2em;
+}
+a.preflib_change {
+  vertical-align: super;
+  font-size: [% css_fonts.size_smaller %];
+  line-height: normal;
+  text-decoration: none;
+}
+.rdetail-holding-group {
+    [% IF rtl == 't' -%]
+    margin-right: 1.5em;
+    [% ELSE -%]
+    margin-left: 1.5em;
+    [% END -%]
+}
+.rdetail-holding-group span {
+    [% IF rtl == 't' -%]
+    margin-right: 1.5em;
+    [% ELSE -%]
+    margin-left: 1.5em;
+    [% END -%]
+}
+.rdetail-holding-group .paging {
+    [% IF rtl == 't' -%]
+    margin-right: 1.5em;
+    [% ELSE -%]
+    margin-left: 1.5em;
+    [% END -%]
+}
+#rdetail_deleted_exp {
+    font-weight: bold;
+    padding: 1em;
+    margin: 1em;
+    border: thick solid [% css_colors.border_alert %];
+}
+
+#ac_tab_wrapper { width : 100%; }
+.ac_tab {
+    [% IF rtl == 't' -%]
+    float: right;
+    padding-left: 10px;
+    [% ELSE -%]
+    float: left;
+    padding-right: 10px;
+    [% END -%]
+    font-size: [% css_fonts.size_big %];
+    padding: 5px;
+    border: 1px solid [% css_colors.primary_offset %];
+}
+.ac_tab_selected { background-color: [% css_colors.primary_offset %]; }
+.ac_tab_selected a { color: [% css_colors.text_invert %]; }
+#ac_content { clear: both; width: 100%; margin-top: 10px; }
+
+/* Popmenu styles used for making css menus. */
+.popmenu {
+    margin: 0;
+    padding: 0;
+}
+.popmenu li {
+    list-style: none;
+}
+.popmenu li a {
+    display: block;
+    padding: 3px 5px;
+}
+.popmenu li ul {
+    display: none; 
+    width: 10em; /* Width to help Opera out */
+    background-color: [% css_colors.primary %];
+}
+.popmenu li:hover ul {
+    display: block;
+    position: absolute;
+    margin: 0;
+    padding: 0;
+    border-color: [% css_colors.border_dark %];
+    border-width: 1px;
+    border-style: solid;
+}
+.popmenu li:hover li {
+    float: none;
+}
+.popmenu li:hover li a {
+    background-color: [% css_colors.primary %]; 
+    color: [% css_colors.accent_ultralight %];
+}
+.popmenu li li a:hover {
+    background-color: [% css_colors.accent_ultralight %]; 
+    color: [% css_colors.primary %];
+}
+/* Styles for the basket entry. */
+.popmenu li:hover li[class~="temporary"] a {
+    background-color: [% css_colors.primary %]; 
+    color: [% css_colors.accent_ultralight %];
+}
+.popmenu li li[class~="temporary"] a:hover {
+    background-color: [% css_colors.accent_ultralight %]; 
+    color: [% css_colors.primary %];
+}
+/* Styles for the default list entry. */
+.popmenu li:hover li[class~="default"] a {
+    background-color: [% css_colors.primary %]; 
+    color: [% css_colors.accent_ultralight %];
+}
+.popmenu li li[class~="default"] a:hover {
+    background-color: [% css_colors.accent_ultralight %]; 
+    color: [% css_colors.primary %];
+}
+/* Styles for the new list entry. */
+.popmenu li:hover li[class~="new"] a {
+    background-color: [% css_colors.primary %]; 
+    color: [% css_colors.accent_ultralight %];
+}
+.popmenu li li[class~="new"] a:hover {
+    background-color: [% css_colors.accent_ultralight %]; 
+    color: [% css_colors.primary %];
+}
+/* Style to add a divider on the menu. */
+.popmenu li li[class~="divider"] {
+    border-bottom-width: 1px;
+    border-bottom-color: [% css_colors.border_dark %];
+    border-bottom-style: solid;
+}
+    
+
+
+
+#patron_usr_barcode_not_found {
+    font-weight: bold; color: [% css_colors.text_alert %];
+}
+
+.record_title {
+    font-weight: bold;
+}
+
+.record_author {
+    font-style: italic;
+}
+
+.password_message {
+    padding-top: 1em;
+    padding-bottom: 0.5em;
+       font-style: italic;
+}
+
+#maintenance_message {
+    padding: 5px;
+    width: 100%;
+    background-color: [% css_colors.text_alert %];
+    color: [% css_colors.text_invert %];
+    text-align: center;
+}
+
+#search-box > span {
+    margin: 0 1em;
+}
+.browse-error {
+    font-weight: bold;
+    font-color: #c00;
+}
+.browse-result-sources, .browse-result-authority-bib-links {
+    [% IF rtl == 't' -%]
+    margin-right: 1em;
+    [% ELSE -%]
+    margin-left: 1em;
+    [% END -%]
+}
+.browse-result-best-match {
+    font-weight: bold;
+}
+.browse-pager {
+    margin: 2ex 0;
+}
+.browse-result-list {
+    padding-bottom: 0.5ex;
+}
+.browse-shortcuts {
+    font-size: [% css_fonts.size_bigger %];
+}
+.browse-result-authority-field-name {
+    font-style: italic;
+    [% IF rtl == 't' -%]
+    margin-left: 1em;
+    [% ELSE -%]
+    margin-right: 1em;
+    [% END -%]
+}
+.browse-leading-article-warning {
+    font-style: italic;
+    font-size: [% css_fonts.size_big %];
+}
+.browse-public-general-note {
+    font-size: [% css_fonts.size_big %];
+}
+.browse-public-general-note-label { }
+.browse-public-general-note-institution {
+    font-style: normal;
+    font-weight: bold;
+}
+.browse-public-general-note-body {
+    font-style: italic;
+}
+
+.bib_peer_type {
+    font-weight: bold;
+}
+
+#main-content-register {
+    [% IF rtl == 't' -%]
+    margin-right: 40px;
+    [% ELSE -%]
+    margin-left: 40px;
+    [% END -%]
+    font-size: [% css_fonts.size_bigger %];
+}
+
+#main-content-register table { 
+    padding: 20px; 
+    margin-top: 18px; 
+    border-collapse: collapse;
+}
+
+#main-content-register td {
+    [% IF rtl == 't' -%]
+    text-align: right;
+    [% ELSE -%]
+    text-align: left;
+    [% END -%]
+}
+
+#main-content-register td:not(:first-child) {
+    [% IF rtl == 't' -%]
+    padding-right: 20px;
+    [% ELSE -%]
+    padding-left: 20px;
+    [% END -%]
+}
+
+.patron-reg-invalid {
+    font-weight: bold;
+    color: red;
+    [% IF rtl == 't' -%]
+    padding-left: 10px;
+    [% ELSE -%]
+    padding-right: 10px;
+    [% END -%]
+}
+
+.result_footer_nav1 {
+    clear: both;
+}
+
+.small_view_only, #filter_hits, #refine_hits, #return_to_hits {
+    display: none;
+}
+
+.rdetail_authors_div {
+    margin-bottom: 1em;
+}
+
+#search_query_label, #search_qtype_label, #search_itype_label, #search_locg_label {
+    white-space: nowrap;
+    display: inline-block;
+}
+
+.result_table_title_cell {
+    padding-top: 4px;
+    padding-bottom: 4px;
+}
+
+.record_title {
+    font-size: [% css_fonts.size_bigger %];
+}
+
+/* styling for sms text call number */
+.sms_text pre {
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: [% css_fonts.size_medium %];
+    background-color: [% css_colors.accent_lightest %];
+    padding: .5%;
+    /* Allow text to wrap */
+    white-space: pre-wrap;       /* css-3 */
+    white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
+    white-space: -pre-wrap;      /* Opera 4-6 */
+    white-space: -o-pre-wrap;    /* Opera 7 */
+    word-wrap: break-word;       /* Internet Explorer 5.5+ */
+}
+
+.mobile_view {
+   display:none;
+}
+
+/* patron message center */
+#myopac_message_tbody {
+    vertical-align: top;
+}
+.myopac_message_message {
+    white-space: pre-wrap;
+}
+
+@media only screen and (min-width: 992px) {
+       .nav-fr{
+       float: right;
+    }
+    .nav-fl{
+       float: left;
+    }
+    #nav-divide{
+       display:none !important;
+    }
+}
+@media only screen and (max-width: 800px) {
+    .facet_sidebar_hidden, .result_block_hidden {
+        display: none;
+    }
+    .facet_sidebar_visible, .result_block_visible {
+        display: inline ! important;
+    }
+    #acct_select, #acct_prefs_select {
+        display: inline-block;
+    }
+    #acct_tabs, #acct_prefs_tabs {
+         display:none;
+     }
+    .radio-parts-selection { width: 90%; }
+    #list_description, #list_create_name {
+        width: 300px;
+    }
+}
+
+@media only screen and (max-width: 600px) {
+    input, select {
+        font-size: [% css_fonts.size_big %];
+    }
+    span .nav_arrow_fix {
+        display: none;
+    }
+    #header {
+        padding: 0px;
+        margin: 0px;
+    }
+    #homesearch_main_logo img {
+        width:75%;
+    }
+    #format_selector {
+        display:none;
+    }
+    #your-acct-login {
+        padding: 0px;
+        padding-top: 5px;
+    }
+    #your-acct-login a {
+        margin: 0px;
+        padding: 5px;
+    }
+    #topnav_logo {
+        margin: 0;
+    }
+    #topnav_logo img {
+        width: 200px;
+    }
+    #gold-links-holder {
+        display: none;
+    }
+    #simple-detail-view-links {
+        display: none;
+    }
+    #acct_tabs a, #acct_fines_tabs a {
+       [% IF rtl == 't' -%]
+       border-radius: 6px 0px 0px 6px;
+       margin: 0px 0px 0px 5px;
+       [% ELSE -%]
+       border-radius: 6px 6px 0px 0px;
+       margin: 0px 5px 0px 0px;
+       [% END -%]
+       padding: 2px 4px 3px 4px;
+       font-size: [% css_fonts.size_base %];
+    }
+    .bookbag-controls-title-block {
+        [% IF rtl == 't' -%]
+        clear:right;
+        [% ELSE -%]
+        clear:left;
+        [% END -%]
+        width:90%;
+    }
+    .bookbag-controls-button-block {
+        [% IF rtl == 't' -%]
+        clear:right;
+        [% ELSE -%]
+        clear:left;
+        [% END -%]
+        width;90%;
+    }
+    .bookbag-specific {
+        [% IF rtl == 't' -%]
+        margin-right: 0px;
+        [% ELSE -%]
+        margin-left: 0px;
+        [% END -%]
+    }
+    .bookbag-specific div.sort {
+        [% IF rtl == 't' -%]
+        float: right;
+        text-align: right;
+        [% ELSE -%]
+        float: left;
+        text-align: left;
+        [% END -%]
+        width: 95%;
+        margin: 5px 0px 5px 0px;
+        border: 1px solid [% css_colors.accent_light %];
+        padding:5px;
+    }
+    .bookbag-specific div.meta {
+        [% IF rtl == 't' -%]
+        float: right;
+        margin-right:0px;
+        text-align: right;
+        [% ELSE -%]
+        float: left;
+        margin-left:0px;
+        text-align: left;
+        [% END -%]
+        width: 95%;
+        margin-bottom:5px;
+        padding:5px;
+    }
+    #bbag-edit-name {
+        [% IF rtl == 't' -%]
+        float: right;
+        [% ELSE -%]
+        float: left;
+        [% END -%]
+        width: 220px;
+    }
+    #bbag-edit-description {
+        width: 220px;
+        margin-top:5px;
+    }
+    .bbag-content {
+        padding:5px;
+        border:1px solid [% css_colors.accent_light %];
+    }
+    .bbag-action {
+        [% IF rtl == 't' -%]
+        margin-right:0px;
+        [% ELSE -%]
+        margin-left:0px;
+        [% END -%]
+        margin-bottom:5px;
+    }
+    .bbag-action-field {
+        width:230px;
+    }
+    .bookbag-specific div.sort select {
+        width:180px;
+    }
+    .bookbag-specific tr {
+        display: block;
+        border-bottom: 1px solid [% css_colors.border_standard %];
+    }
+    .bookbag-specific td.list_checkbox {
+        [% IF rtl == 't' -%]
+        padding-right: 0px !important;
+        [% ELSE -%]
+        padding-left: 0px !important;
+        [% END -%]
+    }
+    .bookbag-specific td.list_entry {
+        display: inline-block;
+        min-width: 5em;
+        [% IF rtl == 't' -%]
+        padding-right: 40% !important;
+        [% ELSE -%]
+        padding-left: 40% !important;
+        [% END -%]
+    }
+    .bookbag-specific td.list_entry:before {
+        content: attr(data-label);
+        position: absolute;
+        [% IF rtl == 't' -%]
+        right: 10px;
+        [% ELSE -%]
+        left: 10px;
+        [% END -%]
+    }
+    .bbag-navigate-list {
+        display: none;
+    }
+    .bbag-navigate-list-pages {
+        [% IF rtl == 't' -%]
+        text-align:left;
+        float:left;
+        [% ELSE -%]
+        text-align:right;
+        float:right;
+        [% END -%]
+    }
+    #dash_wrapper div {
+        background-color: transparent;
+        padding: 0px;
+    }
+    #dash_wrapper {
+        position: static;
+        top: auto;
+    }
+    #dash_wrapper .opac-button {
+        top: 0px;
+    }
+    .small_view_only, #filter_hits {
+        display: inline !important;
+    }
+    #dash_identity a {
+        [% IF rtl == 't' -%]
+        float:right;
+        [% ELSE -%]
+        float:left;
+        [% END -%]
+    }
+    #dashboard, #dashboard_e {
+        display: none;
+    }
+    #holds_box form blockquote {
+        [% IF rtl == 't' -%]
+        margin-right: 10px;
+        margin-left: 2px;
+        [% ELSE -%]
+        margin-left: 10px;
+        margin-right: 2px;
+        [% END -%]
+    }
+    #holds_box form blockquote select {
+        width: 100%;
+    }
+    #myopac_sum_fines_placehold {
+        display:none;
+    }
+    #myopac_sum_fines {
+        display: none;
+    }
+    #list_description, #list_create_name {
+        width: 170px;
+    }
+    .results_header_lbl {
+        display: none;
+    }
+    .results_header_nav1 span.h1 {
+        display: none;
+    }
+    .preflib {
+        display: none;
+    }
+    .start_end_links_span {
+        display: block;
+    }
+    .invisible {
+        display: none;
+    }
+    .result_table_pic_header {
+        [% IF rtl == 't' -%]
+        padding-right: 0px !important;
+        padding-left: 5px;
+        [% ELSE -%]
+        padding-left: 0px !important;
+        padding-right: 5px;
+        [% END -%]
+        width: 0px !important;
+        margin: 0px;
+    }
+    .result_table_pic {
+        width: 55px;
+        padding: 0px;
+        margin: 0px;
+    }
+    tr[name=results_isbn_tr], tr[name=results_phys_desc_tr], tr[name=results_pub_tr] strong, .result_count {
+         display: none;
+    }
+    tr.result_table_title_cell[name=bib_cn_list] .result_holdings_table th:nth-child(4),
+    tr.result_table_title_cell[name=bib_cn_list] .result_holdings_table td:nth-child(4) {
+        display:none;
+    }
+    #results_header_bar {
+        background-color: inherit;
+    }
+    .results_header_btns a {
+        margin: 0.3em;
+    }
+    .adv_filter_results_hide {
+        display: none;
+    }
+    .adv_filter_results_show {
+        display: block;
+    }
+    .adv_filter_results_block_label {
+        display: block;
+    }
+    .adv_filter_results_group_wrapper {
+        display: block;
+    }
+    #main-content {
+        margin: 0 1px;        
+    }
+    #rdetails_status thead {
+        display: none;
+    }
+    #rdetails_status tr {
+       display: block;
+       margin-top: 3px;
+    }
+    #rdetails_status td {
+       display: block;
+       padding: 1px;
+    }
+    .copy_details_row {
+       background-color: [% css_colors.accent_lightest %];
+    }
+    .copy_details_offers_row {
+       background-color: [% css_colors.accent_lightest %];
+    }
+    select#pickup_lib.search-wrapper-locg {
+        width: 100%;
+    }
+    #search-wrapper #search-box {
+        width: 85%;
+        padding-top: 5px;
+    }
+    #main-content-home {
+        padding: 0px;
+        margin: 0px;
+    }
+    /* Make use of full width in mobile mode */
+    .facet_box_wrapper .box_wrapper .box,
+    .facet_template .facet,
+    .facet_box_temp {
+        width: inherit;
+    }
+    .facet_template .count {
+        [% IF rtl == 't' -%]
+        padding-right: 1em;
+        [% ELSE -%]
+        padding-left: 1em;
+        [% END -%]
+    }
+    #facet_sidebar {
+        margin-top: 0.5em;
+    }
+    #adv_search_parent {
+        font-size: [% css_fonts.size_smaller %];
+    }
+    #adv_search_filters {                                                                                                                             
+        position: relative;                                                                                                                           
+        width: 300px;
+    }
+    #format_actions {
+        [% IF rtl == 't' -%]
+        float: right;
+        [% ELSE -%]
+        float: left;
+        [% END -%]
+    }
+    .rdetail_aux_utils {
+        padding: 0px;
+        border: none;
+    }
+    .result_metadata {
+        width: inherit;
+    }
+    div#rdetail_actions_div {
+        float: none;
+    }
+    h2.rdetail_uris {
+        clear: both;
+    }
+    #metarecord_population {
+        overflow: hidden;
+        width: 100%;
+    }
+    .metarecord_population_span_link {
+    }
+    .metarecord_population_item_lang {
+        float: none;
+    }
+    .search_catalog_lbl {
+        [% IF rtl == 't' -%]
+       margin-right: 0;
+        [% ELSE -%]
+       margin-left: 0;
+        [% END -%]
+       white-space: nowrap;
+    }
+    .adv_search_catalog_lbl { 
+        margin-top: 0;
+       white-space: nowrap;
+    }
+    .browse_the_catalog_lbl {
+        white-space: nowrap;
+    }
+    .mobile_hide {
+       display: none;
+    } 
+    #dash_user {
+        display: block;
+        padding: 0.5em;    
+        font-size: 18px;
+        text-align: center;
+    }
+    .dash_divider {
+       display: none;
+    }
+    .dash_account_buttons {
+        display: block;
+    } 
+    .searchbar { line-height: 1.5em; }
+    #browse-controls { line-height: 1.5em; }
+    #search_query_label, #search_qtype_label, #search_itype_label, #search_locg_label {
+        display: block;
+    }
+    .bookshelf td {
+       display: block;
+       width: 100%;
+    }
+    .bookshelf table thead tr {
+       display: block;
+    }
+    #lowhits_help { width: inherit; }
+    #adv_search_tabs a{                                                                                                                           
+        font-size: [% css_fonts.size_small %];
+        margin: 2px 2px 0px 2px;
+        padding: 2px 2px 5px 2px; 
+        [% IF rtl == 't' -%]
+        border-radius: 7px 0px 0px 7px;
+        [% ELSE -%]
+        border-radius: 7px 7px 0px 0px;
+        [% END -%]
+    }
+    #adv_global_tbody td {
+        border-bottom: thin solid [% css_colors.accent_light %];
+    }
+    #adv_global_addrow td {
+        border-bottom: none;
+    }
+        /* Force table to not be like tables anymore */
+        table#acct_checked_main_header thead tr th, table#acct_holds_main_header thead tr th, table#acct_checked_hist_header thead tr th, table#acct_holds_hist_header thead tr th, table#ebook_circs_main_table thead tr th, table#ebook_holds_main_table thead tr th {
+                display: none;
+        }
+        table#acct_checked_main_header tbody tr td, table#acct_holds_main_header tbody tr td, table#acct_checked_hist_header tbody tr td, table#acct_holds_hist_header tbody tr td, table#ebook_circs_main_table tbody tr td, table#ebook_holds_main_table tbody tr td {
+                display: block;
+        }
+
+        /* Hide table headers (but not display: none;, for accessibility) */
+       /* thead tr {
+                position: absolute;
+                top: -9999px;
+                [% IF rtl == 't' -%]
+                right: -9999px;
+                [% ELSE -%]
+                left: -9999px;
+                [% END -%]
+        }*/
+
+        table#acct_checked_main_header, table#acct_holds_main_header, table#acct_checked_hist_header, table#acct_holds_hist_header, table#ebook_circs_main_table, table#ebook_holds_main_table {
+                width: 90%;
+        }
+
+        table#acct_checked_main_header tr, table#acct_holds_main_header tr, table#acct_checked_hist_header tr { border: 1px solid #ddd; }
+
+        /* Holds history gets large white border to mimic header cell on other
+           account screens that provide visual cue for next title. We should do
+           the same for ebook tables too since we have no actions on those
+           tables. If actions get added, we should move those tables out of
+           here. */
+
+        table#ebook_circs_main_table tr, table#ebook_holds_main_table tr { border-top: 25px solid #fff; }
+               table#acct_holds_hist_header tr{ border-top: 2px solid #bbb; }
+
+        table#acct_checked_main_header td, table#acct_holds_main_header td, table#acct_checked_hist_header td, table#acct_holds_hist_header td, table#ebook_circs_main_table td, table#ebook_holds_main_table td {
+                /* Behave  like a "row" */
+                border: none;
+                border-bottom: 1px solid #eee;
+                position: relative;
+                [% IF rtl == 't' -%]
+                padding-right: 40%;
+                [% ELSE -%]
+                padding-left: 40%;
+                [% END -%]
+        }
+
+         table#acct_checked_main_header td:before, table#acct_holds_main_header td:before, table#acct_checked_hist_header td:before, table#acct_holds_hist_header td:before, table#ebook_circs_main_table td:before, table#ebook_holds_main_table td:before {
+                /* Now like a table header */
+                position: absolute;
+                /* Top/left values mimic padding */
+                top: 2px;
+                width: 40%;
+                [% IF rtl == 't' -%]
+                right: 2px;
+                padding-left: 10px;
+                [% ELSE -%]
+                left: 2px;
+                padding-right: 10px;
+                [% END -%]
+                white-space: nowrap;
+        }
+
+        table#acct_checked_main_header td:nth-of-type(1) { border-top: 5px solid #aaa; padding-top: 15px; background-color: #fff;}
+        table#acct_checked_main_header td:nth-of-type(2):before { content: "[% l('Title') %]";}
+        table#acct_checked_main_header td:nth-of-type(3):before { content: "[% l('Author') %]"; }
+        table#acct_checked_main_header td:nth-of-type(4):before { content: "[% l('Renewals Left') %]"; }
+        table#acct_checked_main_header td:nth-of-type(5):before { content: "[% l('Due Date') %]"; }
+        table#acct_checked_main_header td:nth-of-type(6):before { content: "[% l('Barcode') %]"; }
+        table#acct_checked_main_header td:nth-of-type(7):before { content: "[% l('Call number') %]"; }
+
+     table#acct_checked_hist_header td:nth-of-type(1) { border-top: 5px solid #aaa; padding-top: 15px; background-color: #fff;}
+        table#acct_checked_hist_header td:nth-of-type(2):before { content: "[% l('Title') %]";}
+        table#acct_checked_hist_header td:nth-of-type(3):before { content: "[% l('Author') %]"; }
+        table#acct_checked_hist_header td:nth-of-type(4):before { content: "[% l('Checkout Date') %]"; }
+        table#acct_checked_hist_header td:nth-of-type(5):before { content: "[% l('Due Date') %]"; }
+        table#acct_checked_hist_header td:nth-of-type(6):before { content: "[% l('Date Returned') %]"; }
+        table#acct_checked_hist_header td:nth-of-type(7):before { content: "[% l('Barcode') %]"; }
+        table#acct_checked_hist_header td:nth-of-type(8):before { content: "[% l('Call number') %]"; }
+
+        table#acct_holds_main_header td:nth-of-type(1) { border-top: 5px solid #aaa; padding-top: 15px; background-color: #fff;}
+        table#acct_holds_main_header td:nth-of-type(2):before { content: "[% l('Title') %]";}
+        table#acct_holds_main_header td:nth-of-type(3):before { content: "[% l('Author') %]"; }
+        table#acct_holds_main_header td:nth-of-type(4):before { content: "[% l('Format') %]"; }
+        table#acct_holds_main_header td:nth-of-type(5):before { content: "[% l('Pickup Location') %]"; }
+        table#acct_holds_main_header td:nth-of-type(6):before { content: "[% l('Cancel on') %]"; }
+        table#acct_holds_main_header td:nth-of-type(7):before { content: "[% l('Status') %]"; }
+        table#acct_holds_main_header td.hold_notes:before { content: "[% l('Notes') %]"; }
+
+        table#acct_holds_hist_header td:nth-of-type(1):before { content: "[% l('Title') %]";}
+        table#acct_holds_hist_header td:nth-of-type(2):before { content: "[% l('Author') %]"; }
+        table#acct_holds_hist_header td:nth-of-type(3):before { content: "[% l('Format') %]"; }
+        table#acct_holds_hist_header td:nth-of-type(4):before { content: "[% l('Pickup Location') %]"; }
+        table#acct_holds_hist_header td:nth-of-type(8):before { content: "[% l('Status') %]"; }
+
+        table#ebook_circs_main_table td:nth-of-type(1):before { content: "[% l('Title') %]"; }
+        table#ebook_circs_main_table td:nth-of-type(2):before { content: "[% l('Author') %]"; }
+        table#ebook_circs_main_table td:nth-of-type(3):before { content: "[% l('Due Date') %]"; }
+        table#ebook_circs_main_table td:nth-of-type(4):before { content: "[% l('Actions') %]"; }
+
+        table#ebook_holds_main_table td:nth-of-type(1):before { content: "[% l('Title') %]"; }
+        table#ebook_holds_main_table td:nth-of-type(2):before { content: "[% l('Author') %]"; }
+        table#ebook_holds_main_table td:nth-of-type(3):before { content: "[% l('Due Date') %]"; }
+        table#ebook_holds_main_table td:nth-of-type(4):before { content: "[% l('Actions') %]"; }
+
+
+
+       /*Want to see these in mobile ONLY */
+       .mobile_view{
+          display:block;
+       }
+
+       .mobile_search_lbl_clr{
+          color:[% css_colors.mobile_header_text %];
+       }
+}
+
+
+/* 
+For text which is visible only to screen readers.
+Borrowed from http://getbootstrap.com/css/#helper-classes-screen-readers 
+See also http://webaim.org/techniques/css/invisiblecontent/
+*/
+.sr-only {                                                                     
+    position: absolute;                                                          
+    width: 1px;                                                                  
+    height: 1px;                                                                 
+    padding: 0;                                                                  
+    margin: -1px;                                                                
+    overflow: hidden;                                                            
+    clip: rect(0, 0, 0, 0);                                                      
+    border: 0;                                                                   
+}
+
+/* Make added rows in Expert Search have bold labels like the initial row */
+label[for*=expert_]
+{
+    font-weight: bold;
+}  
+
+.sort_deemphasize {
+    font-weight: lighter;
+    font-size: 70%;
+}
+
+#results-page-depth-hint {
+    text-align: center;
+    font-style: italic;
+}
+
+#clear-history-confirm {
+  font-weight: bold;
+  color: [% css_colors.text_badnews %]; 
+  padding: 10px;
+}
+
+/*Inline rules from other templates files*/
+
+.td-left{
+    [% IF rtl == 't' -%]
+    text-align:right;
+    [% ELSE -%]
+    text-align:left;
+    [% END -%]
+    width:100%;
+}
+.td-search-left{
+    [% IF rtl == 't' -%]
+    text-align:right;
+    [% ELSE -%]
+    text-align:left;
+    [% END -%]
+}
+#myopac_summary_div{padding:0px;}
+.div-left{
+    [% IF rtl == 't' -%]
+    float: right;
+    [% ELSE -%]
+    float: left;
+    [% END -%]
+}
+.td-right{
+    [% IF rtl == 't' -%]
+    text-align:left;
+    [% ELSE -%]
+    text-align:right;
+    [% END -%]
+}
+.login-form-left{
+    [% IF rtl == 't' -%]
+    float: right;
+    margin-left: 40px;
+    [% ELSE -%]
+    float: left;
+    margin-right: 40px;
+    [% END -%]
+    padding-bottom: 10px;
+}
+.hold-div{
+    [% IF rtl == 't' -%]
+    padding-right: 10px;
+    [% ELSE -%]
+    padding-left: 10px;
+    [% END -%]
+    padding-bottom: 15px;
+}
+.hold-span{font-weight: bold;}
+.padding-left-6{
+    [% IF rtl == 't' -%]
+    padding-right: 6px;
+    [% ELSE -%]
+    padding-left: 6px;
+    [% END -%]
+}
+.padding-left-10{
+    [% IF rtl == 't' -%]
+    padding-right:10px;
+    [% ELSE -%]
+    padding-left:10px;
+    [% END -%]
+}
+.padding-left-5{
+    [% IF rtl == 't' -%]
+    padding-right: 5px;
+    [% ELSE -%]
+    padding-left: 5px;
+    [% END -%]
+}
+
+/* Rules from metarecord_hold_filters.tt2 */
+
+.metarecord_filters{
+      padding: 5px;
+      margin-top: 5px;
+      border-bottom: 1px solid #333;
+      border-top: 1px solid #333; }
+.metarecord_filter_container{
+    [% IF rtl == 't' -%]
+    float : right;
+    margin-left: 10px;
+    [% ELSE -%]
+    float : left;
+    margin-right: 10px;
+    [% END -%]
+}
+.metarecord_filter_container select{padding: 2px;width: 13em; /* consistent w/ adv search selectors */}
+.metarecord_filter_header{padding-bottom: 5px;}
+
+/* Toggled items. */
+/* A block that is initially hidden and shown by clicking an anchor. */
+[id^="toggled-block-"]{
+    display: none;
+}
+[id^="toggled-block-"]:target{
+    display: block;
+}
+/* Inline text that is initially hidden and shown by clicking an anchor. Useful on a span. */
+[id^="toggled-inline-"]{
+    display: none;
+}
+[id^="toggled-inline-"]:target{
+    display: inline;
+}
+
+.oils_SH {
+    font-weight: bolder;
+    background-color: #99ff99;
+}
+
+.oils_SH.identifier {
+    font-weight: bolder;
+    background-color: #42b0f4;
+}
+.hold_note_title { font-weight: bold; }
+
+/*
+#acct_checked_main_header td, #acct_holds_main_header td, #acct_fines_main_header td, #acct_fines_confirm_header td, #acct_fees_main_header td,#acct_checked_hist_header td, #acct_holds_hist_header td, #acct_list_header td, #acct_list_header_anon td, #temp_list_holds td, #acct_messages_main_header td, #ebook_circs_main_table td, #ebook_holds_main_table td {
+    background-color: [% css_colors.background %]; 
+    padding: 10px;
+        border: 1px solid #d3d3d3; 
+} 
+
+#acct_checked_main_header th, #acct_holds_main_header th, #acct_fines_main_header th, #acct_fines_confirm_header th, #acct_fees_main_header th, #acct_checked_hist_header th, #acct_holds_hist_header th, #acct_list_header th, #acct_list_header_anon th, #temp_list_holds th, #acct_messages_main_header th, #ebook_holds_main_table th {
+    [% IF rtl == 't' -%]
+    text-align: right;
+    [% ELSE -%]
+    text-align: left;
+    [% END -%]
+    padding: 0px 10px 0px 10px;
+}
+#acct_checked_main_header, #acct_holds_main_header, #acct_fines_main_header, #acct_checked_hist_header, #acct_holds_hist_header, #acct_list_header, #acct_list_header_anon, #temp_list_holds, #acct_messages_main_header, #ebook_circs_main_table, #ebook_holds_main_table {
+    border-collapse: collapse;
+}
+
+#hold_editor h1 { font-size: [% css_fonts.size_bigger %]; font-weight: bold; }
+#hold_editor h2 { font-size: [% css_fonts.size_big %]; font-weight: normal; text-indent: 2em; font-style: italic; }
+#hold_editor h1, #hold_editor h2 { margin: 2px 0; }
+#hold_editor_table { background-color: [% css_colors.accent_lighter %]; padding: 0.5em; }
+#hold_editor_table th {
+    [% IF rtl == 't' -%]
+    text-align: left;
+    padding-left: 1em;
+    [% ELSE -%]
+    text-align: right;
+    padding-right: 1em;
+    [% END -%]
+}
+#hold_editor_table td { padding: 0.25em 0; }
+*/
+
+
+/*
+  Carousels
+*/
+
+.carousel {
+    width: 40%;
+    margin: 0 auto;
+}
+.carousel-title {
+    font-size: 150%;
+    font-weight: bold;
+    text-align: center;
+}
+.carousel-entry {
+    max-width: 150px;
+}
+.carousel-entry-image {
+    width: 100%;
+    height: auto;
+}
+.carousel .glide__arrow {
+    color: [% css_colors.button_text %];
+    font-weight: bold !important;
+    text-decoration: none !important;
+    cursor: pointer;
+    border-radius: 5px;
+    border: 1px solid [% css_colors.primary %];
+    background-color:  [% css_colors.primary_fade %];
+    margin: 0.5em;
+    padding: 0.3em;
+    display: inline-block;
+}
+.carousel .glide__arrow--right {
+    right: -5em;
+}
+.carousel .glide__arrow--left {
+    left: -5em;
+}
+
+.search-filter{
+    display: inline;
+}
+
+
+
+.search-option{
+    display: inline-block;
+    margin: 0px 10px;
+}
+
+.title{
+    font-size: large;
+}
+
+#homeLogo{
+    opacity: 0.5; 
+    filter: alpha(opacity=50); 
+    background-color: #fff; /* Used if the image is unavailable */
+    height: 500px; /* You must set a specified height */
+    background-position: center; /* Center the image */
+    background-repeat: no-repeat; /* Do not repeat the image */
+    background-size: contain; /* Resize the background image to cover the entire container */
+    background-image: url("https://evergreen-ils.org/wp-content/uploads/2018/09/Evergreen-Final-Logo.png");
+
+}
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/home.tt2 b/Open-ILS/src/templates/opac-new/home.tt2
new file mode 100644 (file)
index 0000000..b362fe6
--- /dev/null
@@ -0,0 +1,13 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.metalinks.push('<link rel="canonical" href="' _ mkurl('', {}, 1) _ '" />');
+    ctx.page_title = l("Home") %]
+    <h2 class="sr-only">[% l('Catalog Home') %]</h2>
+    [% INCLUDE "opac/parts/searchbar.tt2" is_home_page=1 %]
+    <div>
+        <div>
+            [% INCLUDE "opac/parts/homesearch.tt2" %]
+        </div>
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/i18n_strings.tt2 b/Open-ILS/src/templates/opac-new/i18n_strings.tt2
new file mode 100644 (file)
index 0000000..7d3b831
--- /dev/null
@@ -0,0 +1,24 @@
+<!--
+This file allows us to bring TT2 i18n'ized strings
+to js source files, via js blob.
+-->
+<script>
+    // Add a boost-style format function to JavaScript string.
+    // Implementation stolen from StackOverflow:
+    // https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format
+    String.prototype.format = function() {
+        var args = arguments;
+        return this.replace(/{(\d+)}/g, function(match, number) {
+        return typeof args[number] != 'undefined'
+            ? args[number]
+            : match;
+        });
+    };
+
+    var eg_opac_i18n = {};
+
+    eg_opac_i18n.EG_MISSING_REQUIRED_INPUT = "[% l('Please fill out all required fields') %]";
+    eg_opac_i18n.EG_INVALID_DATE = "[% l('That is not a valid date in the future.') %]";
+    // For multiple holds placement confirmation dialog. {0} is replaced by number of copies requested.
+    eg_opac_i18n.EG_MULTIHOLD_MESSAGE = "[% l('Do you really want to place {0} holds for this title?') %]";
+</script>
diff --git a/Open-ILS/src/templates/opac-new/library.tt2 b/Open-ILS/src/templates/opac-new/library.tt2
new file mode 100644 (file)
index 0000000..702f30e
--- /dev/null
@@ -0,0 +1,12 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Library details: [_1]", ctx.library.name);
+    canon = ctx.proto _ '://' _ ctx.hostname _ mkurl('', {}, 1);
+    ctx.metalinks.push('<link rel="canonical" href="' _ canon  _ '" />');
+    ctx.metalinks.push('<meta property="og:url" content="' _ canon  _ '" />');
+    ctx.metalinks.push('<meta property="og:type" content="http://schema.org/Library" />');
+-%]
+    [%- INCLUDE "opac/parts/searchbar.tt2" %]
+    [%- INCLUDE "opac/parts/library/core_info.tt2"; %]
+[%- END; %]
diff --git a/Open-ILS/src/templates/opac-new/login.tt2 b/Open-ILS/src/templates/opac-new/login.tt2
new file mode 100644 (file)
index 0000000..0e82edf
--- /dev/null
@@ -0,0 +1,17 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    basic_search = "f";
+    ctx.page_title = l("Account Login") %]
+    <h2 class="sr-only">[% l('Account Login') %]</h2>
+    <div class='mobile_hide'>
+        [% INCLUDE "opac/parts/searchbar.tt2" %]
+    </div>
+    <div id="content-wrapper">
+        <div id="main-content">
+            <h3 class="sr-only">[% l('Account Login Form') %]</h3>
+            [% INCLUDE "opac/parts/login/form.tt2" %]
+            <div class="clear-both very-big-height"></div>     
+        </div>
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/mylist.tt2 b/Open-ILS/src/templates/opac-new/mylist.tt2
new file mode 100644 (file)
index 0000000..fe94c6d
--- /dev/null
@@ -0,0 +1,22 @@
+[%- PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.metalinks.push('<meta name="robots" content="noindex,follow">');
+    ctx.page_title = l("Record Detail") %]
+    <h2 class="sr-only">[% l('Basket') %]</h2>
+    <div class="mobile_hide">
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    </div>
+    <div id="content-wrapper">
+        <div id="main-content">
+            [%  IF ctx.mylist.size;
+                    INCLUDE "opac/parts/anon_list.tt2";
+                ELSE %]
+                <div class="warning_box"><h2>[% l("The basket is empty."); %]</h2></div>
+                <button type="button" class="btn btn-confirm" onclick="window.location='[% ctx.referer | html %]'">[% l('Return') %]</button>
+                [% END %]
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/mylist/clear.tt2 b/Open-ILS/src/templates/opac-new/mylist/clear.tt2
new file mode 100644 (file)
index 0000000..8e2596a
--- /dev/null
@@ -0,0 +1,21 @@
+[%- PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Confirm Clearing of Basket") %]
+    <h2 class="sr-only">[% l('Confirm Clearing of Basket') %]</h2>
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    <div id="content-wrapper">
+        <div id="main-content">
+             <p class="big-strong">[% l('Please confirm that you want to remove all [_1] titles from the basket.', ctx.mylist.size) %]
+             <form method="post" action="[% mkurl(ctx.opac_root _ '/cache/clear', {}, 1) %]">
+             <input type="hidden" name="redirect_to" value="[% ctx.referer %]" />
+             <input id="print_cart_submit" type="submit" name="submit"
+               value="[% l('Confirm') %]" title="[% l('Confirm') %]"
+               alt="[% l('Confirm') %]" class="btn btn-confirm" />
+             <input type="reset" name="cancel" onclick="window.location='[% ctx.referer | html %]'" value="[% l('Cancel') %]" id="clear_basket_cancel" class="btn btn-deny" />
+             </form>
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/mylist/email.tt2 b/Open-ILS/src/templates/opac-new/mylist/email.tt2
new file mode 100644 (file)
index 0000000..9eeaa27
--- /dev/null
@@ -0,0 +1,29 @@
+[%- PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Confirm Basket Email") %]
+    <h2 class="sr-only">[% l('Confirm Basket Email') %]</h2>
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    <div id="content-wrapper">
+        <div id="main-content">
+          [% IF ctx.mylist.size %]
+             <p class="big-strong">[% l('Please confirm that you want to email the [_1] titles in the basket.', ctx.mylist.size) %]
+             <form method="post" action="[% mkurl(ctx.opac_root _ '/mylist/doemail', {}, 1) %]">
+             <input type="hidden" name="redirect_to" value="[% ctx.referer %]" />
+             <input type="checkbox" name="clear_basket" value="on" />
+             <label for="clear_basket">[% l('Clear basket after emailing it.') %]</label>
+             <br />
+             <input id="print_cart_submit" type="submit" name="submit"
+               value="[% l('Confirm') %]" title="[% l('Confirm') %]"
+               alt="[% l('Confirm') %]" class="btn btn-confirm" />
+             <input type="reset" name="cancel" onclick="window.location='[% ctx.referer | html %]'" value="[% l('Cancel') %]" id="clear_basket_cancel" class="btn btn-deny" />
+             </form>
+          [% ELSE %]
+            <div class="warning_box">[% l("The basket is empty."); %]</div>
+            <button type="button" class="btn btn-confirm" onclick="window.location='[% ctx.referer | html %]'">[% l('Return') %]</button>
+          [% END %]
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/mylist/print.tt2 b/Open-ILS/src/templates/opac-new/mylist/print.tt2
new file mode 100644 (file)
index 0000000..08722c1
--- /dev/null
@@ -0,0 +1,29 @@
+[%- PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Confirm Basket Printing") %]
+    <h2 class="sr-only">[% l('Confirm Basket Printing') %]</h2>
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    <div id="content-wrapper">
+        <div id="main-content">
+          [% IF ctx.mylist.size %]
+             <p class="big-strong">[% l('Please confirm that you want to print the [_1] titles in the basket.', ctx.mylist.size) %]
+             <form method="post" action="[% mkurl(ctx.opac_root _ '/mylist/doprint', {}, 1) %]">
+             <input type="hidden" name="redirect_to" value="[% ctx.referer %]" />
+             <input type="checkbox" name="clear_cart" value="on" />
+             <label for="clear_basket">[% l('Clear basket after printing it.') %]</label>
+             <br />
+             <input id="print_cart_submit" type="submit" name="submit"
+               value="[% l('Confirm') %]" title="[% l('Confirm') %]"
+               alt="[% l('Confirm') %]" class="btn btn-confirm" />
+             <input type="reset" name="cancel" onclick="window.location='[% ctx.referer | html %]'" value="[% l('Cancel') %]" id="clear_basket_cancel" class="btn btn-deny" />
+             </form>
+          [% ELSE %]
+            <div class="warning_box"><h2>[% l("The basket is empty."); %]</h2></div>
+            <button type="button" class="btn btn-confirm" onclick="window.location='[% ctx.referer | html %]'">[% l('Return') %]</button>
+          [% END %]
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/circ_history.tt2 b/Open-ILS/src/templates/opac-new/myopac/circ_history.tt2
new file mode 100644 (file)
index 0000000..dc1d90f
--- /dev/null
@@ -0,0 +1,214 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "circs"
+    limit = ctx.circ_history_limit;
+    offset = ctx.circ_history_offset;
+%]
+
+<h3 class="sr-only">[% l('History of Checked Out Items') %]</h3>
+<div style="padding:0px;">
+   <div>
+     <ul class="nav nav-pills nav-fill">
+        <li class="nav-item m-2">
+            <a class="nav-link not_active_pill" href='[% mkurl('circs',{},1) %]'>[% l("Current Items Checked Out") %]</a>
+        </li>
+        [%- IF ebook_api.enabled == 'true' %]
+        <li class="nav-item m-2">
+            <a class="nav-link not_active_pill" href="[% mkurl('ebook_circs',{},1) %]">[% l("E-Items Currently Checked Out") %]</a>
+        </li>
+        [%- END %]
+        <li class="nav-item m-2">
+            <a class="nav-link active" href="#">[% l("Check Out History") %]</a>
+        </li>
+    </ul>
+       </div>
+
+    [%
+    # In the sorting case, the size is the size of ALL the circ items.  In the non-sorting case,
+    # the size is simply the size of the chunk passed in.  See the TODO below for the still-lingering
+    # bug.
+    sort_field = CGI.param('sort');
+    IF (sort_field);
+        no_next = ctx.circs.size - offset <= limit;
+    ELSE;
+        no_next = ctx.circs.size < limit;
+    END;
+    %]
+<div class="my-4">
+    <div class="header_middle">
+        <span class="float-left">[% l('Previously Checked Out Items') %]</span>
+        <span class='float-left' style='padding-left: 10px;'>
+            <a href='[% mkurl('circ_history', {limit => limit, offset => (offset - limit)}) %]'
+                [% IF offset == 0 %] class='invisible' [% END %]><span class="nav_arrow_fix">&#9668;</span>[% l('Previous') %]</a>
+            [%# TODO: get total to prevent paging off then end of the list.. %]
+            <a href='[% mkurl('circ_history', {limit => limit, offset => (offset + limit)}) %]'
+            [% IF no_next %] class='invisible' [% END %] >[% l('Next') %]<span class="nav_arrow_fix">&#9658;</span></a>
+        </span>
+    </div>
+    <div class="clear-both"></div>
+
+    [% IF ctx.circs.size < 1 %]
+    <div class="warning_box">[% l('There are no items in your circulation history.') %]</div>
+    [% ELSE %]
+
+        <form method="post" id="circ-form"
+            onsubmit="return confirm('[% l("Are you sure you wish to delete the selected item(s)?") %]');">
+        <table cellpadding='0' cellspacing='0' class="item_list_padding">
+            <tr>
+                <td style="width:90%;">
+                    <select name="action" class="form-control">
+                        <option value="delete">[% l('Delete Selected Titles') %]</option>
+                    </select>
+                </td>
+               
+                <td style="padding-left:9px;">
+                    <input type="submit"
+                        value="[% l('Go') %]"
+                        alt="[% l('Go') %]" title="[% l('Go') %]"
+                        class="btn btn-sm btn-confirm" />
+                </td>
+                <!--
+                <td style="padding-left:5px;">
+                    <a href="#"><img alt="[% l('Deleting Help') %]"
+                        src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]" /></a>
+                </td>
+                -->
+            </tr>
+        </table>
+    <div id='checked_main'>
+        <table title="[% l('History of Items Checked Out') %]" id="acct_checked_hist_header" class="table table-hover miniTable circHistTable my-3">
+            <thead>
+                <tr>
+                    <th class="checkbox_column">
+                        <input type="checkbox" onclick="var inputs=document.getElementsByTagName('input'); for (i = 0; i < inputs.length; i++) { if (inputs[i].name == 'circ_id' && !inputs[i].disabled) inputs[i].checked = this.checked;}"/>
+                    </th>
+                    <th>[% sort_head("sort_title", l("Title")) %]</th>
+                    <th>[% sort_head("author", l("Author")) %]</th>
+                    <th>[% sort_head("callnum", l("Call Number")) %]</th>
+                    <th>[% sort_head("checkout", l("Checkout Date")) %]</th>
+                    <th>[% sort_head("due", l("Due Date")) %]</th>
+                    <th>[% sort_head("returned", l("Date Returned")) %]</th>
+                    <th>[% sort_head("barcode", l("Barcode")) %]</th>
+                </tr>
+            </thead>
+            <tbody>
+                [%# Copy the ctx.circs into a local array, then add a SORT field
+                    that contains the value to sort on.  Since we need the item attrs,
+                    invoke it and save the result in ATTRS.
+        %]
+        [%
+                circ_items = ctx.circs;  # Array assignment
+
+                FOR circ IN circ_items;
+                    circ.ATTRS = {marc_xml => circ.marc_xml};
+                    PROCESS get_marc_attrs args=circ.ATTRS;
+
+                    SWITCH sort_field;
+
+                    CASE "sort_title";
+                        circ.SORTING = circ.ATTRS.sort_title;
+
+                    CASE "author";
+                        circ.SORTING = circ.ATTRS.author;
+
+                    CASE "checkout";
+                        circ.SORTING = circ.circ.xact_start;
+
+                    CASE "due";
+                        circ.SORTING = circ.circ.due_date;
+
+                    CASE "returned";
+                        circ.SORTING = circ.circ.checkin_time;
+
+                    CASE "barcode";
+                        circ.SORTING = circ.circ.target_copy.barcode;
+
+                    CASE "callnum";
+                        circ.SORTING = circ.circ.target_copy.call_number.label;
+
+                    CASE;
+                        sort_field = "";
+                    END; # SWITCH
+                END; #FOR circ
+
+                IF (sort_field != "sort_title");
+                deemphasize_class = "";
+                ELSE;
+                deemphasize_class = " class=\"sort_deemphasize\"";
+                END;
+
+                # Apply sorting to circ_items
+                IF (sort_field);
+                circ_items = circ_items.sort("SORTING");
+                IF (CGI.param("sort_type") == "desc");
+                    circ_items = circ_items.reverse;
+                END;
+
+                # Shorten the circ_items list per offset/limit/cout
+                hi = offset + limit - 1;
+                hi = hi > circ_items.max ? circ_items.max : hi;
+
+                circ_items = circ_items.slice(offset, hi);
+                END;
+
+                # circ_items list is now sorted.  Traverse and dump the information.
+
+                FOR circ IN circ_items; %]
+                    <tr>
+            <td class="checkbox_column">
+                <input type="checkbox" name="circ_id" value="[% circ.circ.id %]" />
+            </td>
+                        <td>
+                            <a href="[% mkurl(ctx.opac_root _ '/record/' _
+                                circ.circ.target_copy.call_number.record.id, {}, 1) %]"
+                                name="[% l('Catalog record') %]"><span[%- deemphasize_class -%]>
+                                [%- circ.ATTRS.title.substr(0,circ.ATTRS.nonfiling_characters) | html %]</span>
+                                [%- circ.ATTRS.title.substr(circ.ATTRS.nonfiling_characters) | html %]</a>
+                        </td>
+                        <td>
+                            <a href="[% mkurl(ctx.opac_root _ '/results',
+                                {qtype => 'author', query => circ.ATTRS.author.replace('[,\.:;]', '')},
+                                1
+                            ) %]">[% circ.ATTRS.author | html %]</a>
+                        </td>
+                        <td>[% circ.circ.target_copy.call_number.label | html %]</td>
+                        <td>
+                            [% date.format(ctx.parse_datetime(circ.circ.xact_start),DATE_FORMAT); %]
+                        </td>
+                        <td>
+                            [% IF circ.circ.source_circ;
+                                date.format(ctx.parse_datetime(circ.circ.due_date, circ.circ.source_circ.circ_lib),DATE_FORMAT);
+                               ELSE;
+                                date.format(ctx.parse_datetime(circ.circ.due_date, ctx.user.home_ou),DATE_FORMAT);
+                               END;
+                            %]
+                        </td>
+                        <td>
+                            [% IF circ.circ.checkin_time;
+                                    date.format(ctx.parse_datetime(circ.circ.checkin_time),DATE_FORMAT);
+                                ELSE; %]
+                                <span style='color:blue;'>*</span><!-- meh -->
+                            [% END; %]
+                        </td>
+                        <td>[% circ.circ.target_copy.barcode | html %]</td>
+                        
+                    </tr>
+                [% END %]
+            </tbody>
+        </table>
+    </div>
+    </form>
+    <form action="[% mkurl(ctx.opac_root _ '/myopac/circ_history/export') %]" method="post">
+                <div>
+                    [%- INCLUDE "opac/parts/preserve_params.tt2" %]
+                    [% IF ctx.circs.size > 0 %]
+                    <input type="hidden" name="filename" value="[% l('circ_history.csv') %]"/>
+                    <button type="submit" class="btn btn-sm btn-primary">[% l('Download CSV') %]</button>
+                    [% END %]
+                </div>
+            </form>
+    [% END %]
+</div></div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/circ_history/export.tt2 b/Open-ILS/src/templates/opac-new/myopac/circ_history/export.tt2
new file mode 100644 (file)
index 0000000..55059cd
--- /dev/null
@@ -0,0 +1,38 @@
+[%- PROCESS "opac/parts/misc_util.tt2";
+    USE CSVFilter 'csv';
+    USE date;
+    SET DATE_FORMAT = l('%m/%d/%Y'); -%]
+[%- l('Title') | csv -%]
+[%- l('Author') | csv -%]
+[%- l('Checkout Date') | csv -%]
+[%- l('Due Date') | csv -%]
+[%- l('Date Returned') | csv -%]
+[%- l('Barcode') | csv -%]
+[%- l('Call Number') | csv -%]
+[%- l('Format') | csv 'last' %]
+[%  FOREACH circ IN ctx.csv.circs;
+    attrs = { marc_xml => circ.marc_xml };
+    PROCESS get_marc_attrs args=attrs;
+    formats = [];
+    FOR format IN attrs.all_formats;
+        formats.push(format.label);
+    END;
+-%]
+[%- IF circ.circ.target_copy.call_number.id == -1 -%]
+    [%- circ.circ.target_copy.dummy_title | csv -%]
+    [%- circ.circ.target_copy.dummy_author | csv -%]
+[%- ELSIF attrs.title -%]
+    [%- attrs.title | csv -%]
+    [%- attrs.author | csv -%]
+[%- END -%]
+[%- date.format(ctx.parse_datetime(circ.circ.xact_start), DATE_FORMAT) | csv-%]
+[%- date.format(ctx.parse_datetime(circ.circ.due_date), DATE_FORMAT) | csv -%]
+[%- IF circ.circ.checkin_time;
+       date.format(ctx.parse_datetime(circ.circ.checkin_time), DATE_FORMAT) | csv;
+    ELSE; -%]
+,
+[%- END -%]
+[%- circ.circ.target_copy.barcode | csv -%]
+[%- circ.circ.target_copy.call_number.label | csv -%]
+[%- formats.join('+') | csv 'last' %]
+[%  END -%]
diff --git a/Open-ILS/src/templates/opac-new/myopac/circs.tt2 b/Open-ILS/src/templates/opac-new/myopac/circs.tt2
new file mode 100644 (file)
index 0000000..760674b
--- /dev/null
@@ -0,0 +1,238 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "circs";
+%]
+<h3 class="sr-only">[% l('Current Items Checked Out') %]</h3>
+<div id='myopac_checked_div'>
+       <div>
+     <ul class="nav nav-pills nav-fill">
+        <li class="nav-item m-2">
+            <a class="nav-link active" href='#'>[% l("Current Items Checked Out") %]</a>
+        </li>
+        [%- IF ebook_api.enabled == 'true' %]
+        <li class="nav-item m-2">
+            <a class="nav-link not_active_pill" href="[% mkurl('ebook_circs',{},1) %]">[% l("E-Items Currently Checked Out") %]</a>
+        </li>
+        [%- END %]
+        <li class="nav-item active m-2">
+            <a class="nav-link not_active_pill" href="[% mkurl('circ_history',{},1) %]">[% l("Check Out History") %]</a>
+        </li>
+    </ul>
+       </div>
+    <div class="my-4">
+    <div class="header_middle">
+        <span class="float-left">[% l('Current Items Checked Out') %]</span>
+    </div>
+    <div class="clear-both"></div>
+    [% IF ctx.circs.size < 1 %]
+    <div class="warning_box">[% l('You have no items checked out.') %]</div>
+    [% ELSE %]
+    [% IF ctx.success_renewals %]
+    <div class="renew-summary">
+        [% l("Successfully renewed [_1] item(s)", ctx.success_renewals) %]
+    </div>
+    [% END %]
+    [% IF ctx.failed_renewals %]
+    <div class="renew-summary alert">
+        [% l("Failed to renew [_1] item(s)", ctx.failed_renewals) %]
+    </div>
+    [% END %]
+    <div id='checked_main'>
+        <form method="post" id="circ-form"
+            onsubmit="return confirm('[% l("Are you sure you wish to renew the selected item(s)?") %]');">
+         <div class="row">
+                <span class="col-md-11 my-2">
+                <select class="form-control" name="action" title="[% l('Select Action') %]">
+                    <option value="renew">[% l('Renew Selected Titles') %]</option>
+                </select>
+            </span>
+            <span class="col-md-1 my-2">
+                <input type="submit"
+                    value="[% l('Go') %]"
+                    title="[% l('Go') %]"
+                    class="btn btn-confirm" />
+            </span>
+           <!-- <span style="padding-left:5px;">
+                <a href="#"><img 
+                    alt="[% l('Renewing Help') %]"
+                    title="[% l('Actions for checked out items') %]"
+                    src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]" /></a>
+            </span> -->
+        </div>
+
+        <table  id="acct_checked_main_header"  title="[% l('Items Checked Out') %]"
+            class="table table-hover miniTable circTable">
+            <thead>
+            <tr>
+                <th class="checkbox_column">
+                    <input type="checkbox" id="check_all_checked"
+                        title="[% l('Select All Items') %]"
+                        onclick="var inputs=document.getElementsByTagName('input'); for (i = 0; i < inputs.length; i++) { if (inputs[i].name == 'circ' &amp;&amp; !inputs[i].disabled) inputs[i].checked = this.checked;}"
+                    />
+                </th>
+                <th>[% sort_head("sort_title", l("Title")) %]</th>
+                <th>[% sort_head("author", l("Author")) %]</th>
+                <th>[% sort_head("renews", l("Renewals Left")) %]</th>
+                <th>[% sort_head("due", l("Due Date")) %]</th>
+                <th>[% sort_head("barcode", l("Barcode")) %]</th>
+                <th>[% sort_head("callnum", l("Call Number")) %]</th>
+                 [% IF ctx.get_org_setting(ctx.user.home_ou, 'opac.show_owning_lib_column'); %]
+                   <th>[% sort_head("lib", l("Owning Library")) %]</th>
+                   [% column_count = 8;
+                ELSE ;
+                   column_count = 7;
+                END; %]
+            </tr>
+            </thead>
+            <tbody>
+                [%# Copy the ctx.circs into a local array, then add a SORT field
+                    that contains the value to sort on.  Since we need the item attrs,
+                    invoke it and save the result in ATTRS.
+               %]
+               [% 
+                circ_items = ctx.circs;  # Array assignment
+
+                sort_field = CGI.param('sort');  # unless changed below...
+
+                FOR circ IN circ_items;
+                    circ.ATTRS = {marc_xml => circ.marc_xml};
+                    PROCESS get_marc_attrs args=circ.ATTRS;
+               
+                    SWITCH sort_field;
+
+                       CASE "sort_title";
+                          circ.SORTING = circ.ATTRS.sort_title;
+
+                       CASE "author";
+                          circ.SORTING = circ.ATTRS.author;
+
+                       CASE "renews";
+                          circ.SORTING = circ.circ.renewal_remaining;
+                       
+                       CASE "due";
+                          circ.SORTING = circ.circ.due_date;
+                       
+                       CASE "barcode";
+                          circ.SORTING = circ.circ.target_copy.barcode;
+
+                       CASE "callnum";
+                          circ.SORTING = circ.circ.target_copy.call_number.label;
+                        CASE "lib";
+                          circ.SORTING = circ.circ.target_copy.call_number.owning_lib.name;
+                       
+                       CASE;
+                          sort_field = "";
+                    END; # SWITCH
+                END; #FOR circ
+
+                IF (sort_field != "sort_title");
+                   deemphasize_class = "";
+                ELSE;
+                   deemphasize_class = " class=\"sort_deemphasize\"";
+                END;
+
+                # Apply sorting to circ_items
+               IF (sort_field);
+                   circ_items = circ_items.sort("SORTING");
+                    IF (CGI.param("sort_type") == "desc");
+                        circ_items = circ_items.reverse;
+                    END;
+                END;
+
+               # circ_items list is now sorted.  Traverse and dump the information.
+
+                FOR circ IN circ_items; %]
+                    <tr>
+                        <td class="checkbox_column" valign="top">
+                            <input type="checkbox" name="circ"
+                                title="[% l('Item Selected') %]"
+                                [% IF circ.circ.renewal_remaining < 1 %] disabled="disabled" [% END %]
+                                value="[% circ.circ.id %]" />
+                        </td>
+                        <td name="title">
+                            <span class="sr-only">Title</span>
+                            [% IF circ.circ.target_copy.call_number.id == -1 %]
+                                [% circ.circ.target_copy.dummy_title | html %]
+                            [% ELSIF circ.ATTRS.title %]
+                                <a href="[% mkurl(ctx.opac_root _ '/record/' _ 
+                                    circ.circ.target_copy.call_number.record.id, {}, 1) %]"
+                                    name="[% l('Catalog record') %]"><span[%- deemphasize_class -%]>
+                                    [%- circ.ATTRS.title.substr(0,circ.ATTRS.nonfiling_characters) | html %]</span>
+                                    [%- circ.ATTRS.title.substr(circ.ATTRS.nonfiling_characters) | html %]</a>
+                            [% END %]
+                        </td>
+                        <td name="author">
+                            [% IF circ.circ.target_copy.call_number.id == -1 %]
+                                <span class="sr-only">Author</span>
+                                [% circ.circ.target_copy.dummy_author | html %]
+                            [% ELSIF circ.ATTRS.author %]
+                                <span class="sr-only">Author</span>
+                                <a href="[% mkurl(ctx.opac_root _ '/results',
+                                    {qtype => 'author', query => circ.ATTRS.author.replace('[,\.:;]', '')},
+                                    1
+                                ) %]">[% circ.ATTRS.author | html %]</a>
+                            [% ELSE %]
+
+                            [% END %]
+                        </td>
+                        <td name="renewals">
+                            <span class="sr-only">Renewals Remaining</span>
+                            [% circ.circ.renewal_remaining %]
+                        </td>
+                        [%
+                            due_date = ctx.parse_datetime(circ.circ.due_date, circ.circ.circ_lib);
+                            due_class = (date.now > date.format(due_date, '%s')) ? 'error' : '';
+                        %]
+                        <td name="due_date" class='[% due_class %]'>
+                            <span class="sr-only">Due Date</span>
+                            [% date.format(due_date, DATE_FORMAT) %]
+                        </td>
+                        <td name="barcode">
+                            <span class="sr-only">Barcode</span>
+                            <span class="speak:spell-out;">[% circ.circ.target_copy.barcode | html %]</span>
+                        </td>
+                        <td name="call_number">
+                            <span class="sr-only">Call Number</span>
+                            [% circ.circ.target_copy.call_number.label | html %]
+                        </td>
+                         [% IF ctx.get_org_setting(ctx.user.home_ou, 'opac.show_owning_lib_column'); %]
+                          <td name="lib">
+                                [%- fleshed_ou = circ.circ.target_copy.call_number.owning_lib;
+                                                INCLUDE "opac/parts/library_name_link_from_ou.tt2";
+                                %]
+                          </td>
+                        [% END; %]
+                    </tr>
+                    [%  IF circ.renewal_response AND
+                            circ.renewal_response.textcode != 'SUCCESS' %]
+                    <tr>
+                        <td colspan="[% column_count %]">[%# XXX colspan="0" does not work in IE %]
+                            <span class="failure-text" title="[% circ.renewal_response.textcode | html %] / [% circ.renewal_response.fail_part | html %]">
+                                [%
+                                    renew_fail_msg = '';
+                                    IF circ.renewal_response.textcode == 'TOTAL_HOLD_COPY_RATIO_EXCEEDED' OR
+                                        circ.renewal_response.textcode == 'AVAIL_HOLD_COPY_RATIO_EXCEEDED';
+                                        # the stock hold ratio message may not be patron friendly
+                                        renew_fail_msg = l('Item is needed for a hold');
+                                    ELSE;
+                                        renew_fail_msg = circ.renewal_response.desc || 
+                                            circ.renewal_response.fail_part || 
+                                            circ.renewal_response.textcode;
+                                    END;
+                                    renew_fail_msg | html;
+                                %]
+                            </span>
+                        </td>
+                    </tr>
+                    [% END; %]
+
+                [% END; %]
+                </tbody>
+            </table>
+        </form>
+    </div>
+    [% END %]
+</div></div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/ebook_checkout.tt2 b/Open-ILS/src/templates/opac-new/myopac/ebook_checkout.tt2
new file mode 100644 (file)
index 0000000..ca5bfe2
--- /dev/null
@@ -0,0 +1,34 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "ebook_checkout";
+%]
+<h3 class="sr-only">[% l('Check Out E-Item') %]</h3>
+<div id='myopac_checked_div'>
+
+    <div class="header_middle">
+        <span class="float-left">[% l('Check Out E-Item') %]</span>
+    </div>
+    <div class="clear-both"></div>
+    <div id="ebook_checkout_failed" class="warning_box hidden">[% l('E-item could not be checked out.') %]</div>
+    <div id="ebook_checkout_succeeded" class="success hidden">[% l('E-item successfully checked out.') %]</div>
+    <div id="ebook_spinner"><img src="[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]" alt="[% l("Loading...") %]"/></div>
+    <div id='ebook_circs_main' class="hidden">
+        <table id="ebook_circs_main_table"
+            title="[% l('Check Out E-Item') %]"
+            class="table table-hover miniTable w-100 echeckoutTable">
+            <thead>
+            <tr>
+                <th>[% l("Title") %]</th>
+                <th>[% l("Author") %]</th>
+                <th>[% l("Due Date") %]</th>
+                <th>[% l("Actions") %]</th>
+            </tr>
+            </thead>
+            <tbody id="ebook_circs_main_table_body"></tbody>
+        </table>
+    </div>
+</div>
+[% END %]
+
diff --git a/Open-ILS/src/templates/opac-new/myopac/ebook_circs.tt2 b/Open-ILS/src/templates/opac-new/myopac/ebook_circs.tt2
new file mode 100644 (file)
index 0000000..eb40563
--- /dev/null
@@ -0,0 +1,48 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "ebook_circs";
+%]
+<h3 class="sr-only">[% l('E-Items Currently Checked Out') %]</h3>
+<div id='myopac_checked_div'>
+
+   <div class="mb-3">
+     <ul class="nav nav-pills nav-fill">
+        <li class="nav-item m-2">
+            <a class="nav-link not_active_pill" href='[% mkurl('circs',{},1) %]'>[% l("Current Items Checked Out") %]</a>
+        </li>
+        [%- IF ebook_api.enabled == 'true' %]
+        <li class="nav-item m-2">
+            <a class="nav-link active" href="[% mkurl('ebook_circs',{},1) %]">[% l("E-Items Currently Checked Out") %]</a>
+        </li>
+        [%- END %]
+        <li class="nav-item active m-2">
+            <a class="nav-link not_active_pill" href="[% mkurl('circ_history',{},1) %]">[% l("Check Out History") %]</a>
+        </li>
+    </ul>
+       </div>
+
+    <div class="header_middle">
+        <span class="float-left">[% l('E-Items Currently Checked Out') %]</span>
+    </div>
+    <div class="clear-both"></div>
+    <div id="no_ebook_circs" class="warning_box hidden">[% l('You have no e-items checked out.') %]</div>
+    <div id="ebook_spinner"><img src="[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]" alt="[% l("Loading...") %]"/></div>
+    <div id='ebook_circs_main' class="hidden">
+        <table id="ebook_circs_main_table"
+            title="[% l('E-Items Currently Checked Out') %]"
+            class="table table-hover miniTable w-100 ecircsTable">
+            <thead>
+            <tr>
+                <th>[% l("Title") %]</th>
+                <th>[% l("Author") %]</th>
+                <th>[% l("Due Date") %]</th>
+                <th>[% l("Actions") %]</th>
+            </tr>
+            </thead>
+            <tbody id="ebook_circs_main_table_body"></tbody>
+        </table>
+    </div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/ebook_holds.tt2 b/Open-ILS/src/templates/opac-new/myopac/ebook_holds.tt2
new file mode 100644 (file)
index 0000000..737475c
--- /dev/null
@@ -0,0 +1,57 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_status.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+     myopac_page = "ebook_holds";
+    limit = (ctx.holds_limit.defined) ? ctx.holds_limit : 0;
+    offset = (ctx.holds_offset.defined) ? ctx.holds_offset : 0;
+    count = (ctx.holds_ids.size.defined) ? ctx.holds_ids.size : 0;
+%]
+<h3 class="sr-only">[% l('My E-Item Holds') %]</h3>
+<div id='myopac_holds_div'>
+
+       <div class="mb-3">
+       <ul class="nav nav-pills nav-fill">
+        <li class="nav-item m-2">
+            <a href='[% mkurl('holds', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("Items on Hold") %]</a>
+        </li>
+        [% IF ebook_api.enabled == 'true' %]
+        <li class="nav-item m-2">
+            <a href='#' class="nav-link active">[% l("E-Items on Hold") %]</a>
+        </li>
+        <li class="nav-item m-2">
+            <a href='[% mkurl('ebook_holds_ready', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("E-Items Ready for Checkout") %]</a>
+        </li>
+        [% END %]
+        <li class="nav-item m-2">
+            <a href='[% mkurl('hold_history', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("Holds History") %]</a>
+        </li>
+        </ul>
+    </div>
+    <div class="header_middle">
+        <span class="float-left">[% l('E-Items on Hold') %]</span>
+    </div>
+    <div class="clear-both"></div>
+    <div id="no_ebook_holds" class="warning_box hidden">[% l('You have no e-item holds.') %]</div>
+    <div id="ebook_cancel_hold_failed" class="warning_box hidden">[% l('Hold could not be canceled.') %]</div>
+    <div id="ebook_cancel_hold_succeeded" class="success hidden">[% l('Your hold has been canceled.') %]</div>
+    <div id="ebook_spinner"><img src="[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]" alt="[% l("Loading...") %]"/></div>
+    <div id='ebook_holds_main' class="hidden">
+        <table id="ebook_holds_main_table"
+            title="[% l('E-Items on Hold') %]"
+            class="table table-hover miniTable w-100 eholdsTable">
+            <thead>
+            <tr>
+                <th>[% l("Title") %]</th>
+                <th>[% l("Author") %]</th>
+                <th>[% l("Expire Date") %]</th>
+                <th>[% l("Status") %]</th>
+                <th>[% l("Actions") %]</th>
+            </tr>
+            </thead>
+            <tbody id="ebook_holds_main_table_body"></tbody>
+        </table>
+    </div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/ebook_holds_ready.tt2 b/Open-ILS/src/templates/opac-new/myopac/ebook_holds_ready.tt2
new file mode 100644 (file)
index 0000000..83fd8b9
--- /dev/null
@@ -0,0 +1,54 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_status.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "ebook_holds_ready";
+    limit = (ctx.holds_limit.defined) ? ctx.holds_limit : 0;
+    offset = (ctx.holds_offset.defined) ? ctx.holds_offset : 0;
+    count = (ctx.holds_ids.size.defined) ? ctx.holds_ids.size : 0;
+%]
+<h3 class="sr-only">[% l('E-Items Ready for Checkout') %]</h3>
+<div id='myopac_holds_div'>
+       <div class="mb-3">
+       <ul class="nav nav-pills nav-fill">
+        <li class="nav-item m-2">
+            <a href='[% mkurl('holds', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("Items on Hold") %]</a>
+        </li>
+        [% IF ebook_api.enabled == 'true' %]
+        <li class="nav-item m-2">
+            <a href='[% mkurl('ebook_holds', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("E-Items on Hold") %]</a>
+        </li>
+        <li class="nav-item m-2">
+            <a href='#' class="nav-link active">[% l("E-Items Ready for Checkout") %]</a>
+        </li>
+        [% END %]
+        <li class="nav-item m-2">
+            <a href='[% mkurl('hold_history', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("Holds History") %]</a>
+        </li>
+        </ul>
+    </div>
+    
+    <div class="header_middle">
+        <span class="float-left">[% l('E-Items Ready for Checkout') %]</span>
+    </div>
+    <div class="clear-both"></div>
+    <div id="no_ebook_holds" class="warning_box hidden">[% l('You have no e-item holds ready to be checked out.') %]</div>
+    <div id="ebook_spinner"><img src="[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]" alt="[% l("Loading...") %]"/></div>
+    <div id='ebook_holds_main' class="hidden">
+        <table id="ebook_holds_main_table"
+            title="[% l('E-Items Ready for Checkout') %]"
+            class="table table-hover miniTable w-100 eholdsreadyTable">
+            <thead>
+            <tr>
+                <th>[% l("Title") %]</th>
+                <th>[% l("Author") %]</th>
+                <th>[% l("Expire Date") %]</th>
+                <th>[% l("Actions") %]</th>
+            </tr>
+            </thead>
+            <tbody id="ebook_holds_main_table_body"></tbody>
+        </table>
+    </div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/ebook_place_hold.tt2 b/Open-ILS/src/templates/opac-new/myopac/ebook_place_hold.tt2
new file mode 100644 (file)
index 0000000..cefea21
--- /dev/null
@@ -0,0 +1,36 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_status.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "ebook_place_hold";
+%]
+<h3 class="sr-only">[% l('Place Hold on E-Item') %]</h3>
+<div id='myopac_holds_div'>
+
+    <div class="header_middle">
+        <span class="float-left">[% l('Place Hold on E-Item') %]</span>
+    </div>
+    <div class="clear-both"></div>
+    <div id="ebook_place_hold_failed" class="warning_box hidden">[% l('Hold could not be placed.') %]</div>
+    <div id="ebook_place_hold_succeeded" class="success hidden">[% l('E-item is now on hold.') %]</div>
+    <div id="ebook_spinner"><img src="[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]" alt="[% l("Loading...") %]"/></div>
+    <div id='ebook_holds_main' class="hidden">
+        <table id="ebook_holds_main_table"
+            title="[% l('Place Hold on E-Item') %]"
+            class="table_no_border_space table_no_cell_pad item_list_padding">
+            <thead>
+            <tr>
+                <th>[% l("Title") %]</th>
+                <th>[% l("Author") %]</th>
+                <th>[% l("Expire Date") %]</th>
+                <th>[% l("Status") %]</th>
+                <th>[% l("Actions") %]</th>
+            </tr>
+            </thead>
+            <tbody id="ebook_holds_main_table_body"></tbody>
+        </table>
+    </div>
+</div>
+[% END %]
+
diff --git a/Open-ILS/src/templates/opac-new/myopac/hold_history.tt2 b/Open-ILS/src/templates/opac-new/myopac/hold_history.tt2
new file mode 100644 (file)
index 0000000..1ee3589
--- /dev/null
@@ -0,0 +1,125 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_status.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "holds";
+    limit = ctx.hold_history_limit;
+    offset = ctx.hold_history_offset;
+    count = ctx.hold_history_ids.size;
+%]
+
+<h3 class="sr-only">[% l('Holds History') %]</h3>
+<div id='myopac_holds_div'>
+
+
+       <div>
+       <ul class="nav nav-pills nav-justified">
+        <li class="nav-item m-2">
+            <a href='[% mkurl('holds',{},['limit','offset']) %]' class="nav-link not_active_pill">[% l("Items on Hold") %]</a>
+        </li>
+        [% IF ebook_api.enabled == 'true' %]
+        <li class="nav-item m-2">
+            <a href='[% mkurl('ebook_holds', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("E-Items on Hold") %]</a>
+        </li>
+        <li class="nav-item m-2">
+            <a href='[% mkurl('ebook_holds_ready', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("E-Items Ready for Checkout") %]</a>
+        </li>
+        [% END %]
+        <li class="nav-item m-2">
+            <a href='#' class="nav-link active">[% l("Holds History") %]</a>
+        </li>
+        </ul>
+    </div>
+    
+  
+<div class="my-4">
+    <div class="header_middle">
+        <span style="float:left;">[% l("Previously Held Items") %]</span>
+        <span class='float-left' style='padding-left: 10px;'>
+            <a href='[% mkurl('hold_history', {limit => limit,offset => (offset - limit)}) %]'
+                [% IF offset == 0 %] class='invisible' [% END %]><span class="nav_arrow_fix">&#9668;</span>[% l('Previous') %]</a>
+            [% IF offset > 0 || count > limit;
+                curpage = 0;
+                WHILE curpage * limit < count;
+                    IF curpage * limit == offset;
+            %]
+            [% curpage + 1 %]
+                    [%- ELSE %]
+            <a href='[% mkurl('hold_history', {limit => limit, offset => (curpage * limit)}) %]'>[% curpage + 1 %]</a>
+                    [%- END;
+                    curpage = curpage + 1;
+                END;
+            END %]
+            <a href='[% mkurl('hold_history', {limit => limit, offset => (offset + limit)}) %]'
+               [% IF count <= limit + offset %] class='invisible' [% END %] >[% l('Next') %]<span class="nav_arrow_fix">&#9658;</span></a>
+        </span>
+        <span style="float:right;">
+            <a class="hide_me" href="#">[% l('Export List') %]</a>
+        </span>
+    </div>
+    <div class="clear-both"></div>
+
+    <div id='holds_main'>
+        [% IF ctx.holds.size && ctx.holds.size < 1 %]
+        <div class="warning_box">
+            <big><strong>[% l('No holds found.') %]</strong></big>
+        </div>
+        [% ELSE %]
+        <table id='acct_holds_hist_header' class='table table-hover miniTable holdHistoryTable' title="[% l('History of items on hold') %]">
+            <thead>
+                <tr>
+                    <th><span>[% l('Title') %]</span></th>
+                    <th><span>[% l('Author') %]</span></th>
+                    <th> <span>[% l('Format') %]</span></th>
+                    <th><span>[% l('Pickup Location') %]</span> </th>
+                    <th><span>[% l('Status') %]</span></th>
+                </tr>
+            </thead>
+            <tbody>
+                [% FOR hold IN ctx.holds;
+                    attrs = {marc_xml => hold.marc_xml};
+                    PROCESS get_marc_attrs args=attrs;
+                    ahr = hold.hold.hold %]
+
+                <tr name="acct_holds_temp">
+                   
+                    <td>
+                    <span class="sr-only">Title</span>
+                        <div>
+                            <a href="[% mkurl(ctx.opac_root _ '/record/' _ hold.hold.bre_id) %]">[% attrs.title | html %]</a>
+                        </div>
+                    </td>
+                    <td>
+                    <span class="sr-only">Author</span>
+                        <div>
+                            <a href="[% mkurl(ctx.opac_root _ '/results',
+                                {qtype => author, query => attrs.author.replace('[,\.:;]', '')}
+                            ) %]">[% attrs.author | html %]</a>
+                        </div>
+                    </td>
+                    <td>
+                        <div class='format_icon'>
+                            [% IF attrs.format_icon %]
+                            <img title="[% attrs.format_label | html %]" alt="[% attrs.format_label | html %]" src="[% attrs.format_icon %][% ctx.cache_key %]" />
+                            [% END %]
+                        </div>
+                         <span class="sr-only">Format [% attrs.format_label | html %]</span>
+                    </td>
+                    <td>
+                    <span class="sr-only">Pickup Location</span>
+                        [% ctx.get_aou(ahr.pickup_lib).name | html %]
+                    </td>
+                    <td>
+                    <span class="sr-only">Hold Status</span>
+                        <div name="acct_holds_status">
+                            [% PROCESS get_hold_status hold=hold; %]
+                        </div>
+                    </td>
+                </tr>
+                [% END %]
+            </tbody>
+        </table>
+        [% END %]
+    </div></div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/holds.tt2 b/Open-ILS/src/templates/opac-new/myopac/holds.tt2
new file mode 100644 (file)
index 0000000..960d67c
--- /dev/null
@@ -0,0 +1,275 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_status.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "holds";
+    limit = (ctx.holds_limit.defined) ? ctx.holds_limit : 0;
+    offset = (ctx.holds_offset.defined) ? ctx.holds_offset : 0;
+    count = (ctx.holds_ids.size.defined) ? ctx.holds_ids.size : 0; 
+%]
+<h3 class="sr-only">[% l('My Holds') %]</h3>
+<div id='myopac_holds_div'>
+    <div class="mb-3">
+       <ul class="nav nav-pills nav-fill">
+        <li class="nav-item m-2">
+            <a href='#' class="nav-link active">[% l("Items on Hold") %]</a>
+        </li>
+        [% IF ebook_api.enabled == 'true' %]
+        <li class="nav-item m-2">
+            <a href='[% mkurl('ebook_holds', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("E-Items on Hold") %]</a>
+        </li>
+        <li class="nav-item m-2">
+            <a href='[% mkurl('ebook_holds_ready', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("E-Items Ready for Checkout") %]</a>
+        </li>
+        [% END %]
+        <li class="nav-item m-2">
+            <a href='[% mkurl('hold_history', {}, ['limit','offset','available','sort','sort_type']) %]' class="nav-link not_active_pill">[% l("Holds History") %]</a>
+        </li>
+        </ul>
+    </div>
+       <div class="my-4">
+    <div class="header_middle">
+        <span id="acct_holds_header" style="float:left;">
+            [%  IF CGI.param("available");
+                    l("Items Ready for Pickup");
+                ELSE;
+                    l("Current Items on Hold");
+                END
+            %]
+        </span>
+    </div>
+    <div class="clear-both"></div>
+    <div id='holds_main'>
+    <form method="post">
+           <div class="row">
+                <span class="col-md-11 my-2">
+                       <select class="form-control" name="action" id="acct_holds_actions" 
+                      title="[% l('Select your action for the selected holds') %]">
+                        <option id='myopac_holds_actions_none' value=''>
+                        -- [% l("Actions for selected holds") %] --
+                        </option>
+                        <option value='suspend'>[% l("Suspend") %]</option>
+                        <option value='activate'>[% l("Activate") %]</option>
+                        <!-- XXX maybe later <option value='thaw_date'>[% l("Set Active Date") %]</option> -->
+                        <option value='cancel'>[% l("Cancel") %]</option>
+                    </select>
+                </span>
+                <span class="col-md-1 my-2">
+                    <input type="submit" value="[% l('Go') %]"
+                        title="[% l('Go') %]" class="btn btn-confirm" />
+                </span>
+          </div>
+
+          <div class="row">
+            <div class="col-3 text-left">
+                <a href='[% mkurl('holds', {limit => limit,offset => (offset - limit)}) %]'
+                [% IF offset <= 0 %] class='invisible' [% END %]><span class="np_nav_link classic_link btn btn-outline-primary">&#9668;[% l('Previous') %]</span></a>
+                    [% IF offset > 0 || count > limit; curpage = 0; WHILE curpage * limit < count; IF curpage * limit == offset; %]
+                        [% curpage + 1 %]
+                    [%- ELSE %]
+                <a href='[% mkurl('holds', {limit => limit, offset => (curpage * limit)}) %]' class="np_nav_link classic_link btn btn-outline-primary">[% curpage + 1 %]</a>
+                    [%- END; curpage = curpage + 1; END; END %]
+            </div>
+            <div class="col-6 text-center">
+                <span style="padding-left:5px;" class='error'>
+                    [%  IF ctx.hold_suspend_post_capture; l('One or more holds could not be suspended because the item is at (or en route to) the pickup library.'); END; %]
+                </span>
+            
+               <span>
+                    [% IF CGI.param("available") -%]
+                    <a href="[% mkurl('holds', {}, ['limit','offset','available']) %]">[% l('Show all holds') %]</a> |
+                    <strong>[% l("Show only available holds") %]</strong>
+                    [% ELSE -%]
+                    <strong>[% l("Show all holds") %]</strong> |
+                    <a href="[% mkurl('holds',{available => 1},['limit','offset']) %]">[% l("Show only available holds") %]</a>
+                    [% END -%]
+               </span>
+                <a href="#">
+                    <img alt="[% l('Holds Help') %]" title="[% l('Actions for selected holds') %]" src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]" />
+                </a>
+            </div>
+
+            <div class="col-3 text-right">  
+                <a href='[% mkurl('holds', {limit => limit, offset => (offset + limit)}) %]'
+               [% IF count <= limit + offset %] class='invisible' [% END %] ><span class="np_nav_link classic_link btn btn-outline-primary">[% l('Next') %]&#9658;</span></a>
+            </div>
+          </div>
+                    
+        [% IF ctx.holds.size && ctx.holds.size < 1 %]
+        <div class="warning_box">[% l('No holds found.') %]</div>
+        [% ELSE %]
+        <table title="[% l('Items on Hold') %]"
+        class="table table-hover miniTable activeHoldstable w-100 my-3">
+            <thead>
+            <tr>
+                <th class="checkbox_column"> 
+                    <input type="checkbox" title="[% l('Select All Holds') %]"
+                      onclick="var inputs=document.getElementsByTagName('input'); for (i = 0; i < inputs.length; i++) { if (inputs[i].name == 'hold_id' &amp;&amp; !inputs[i].disabled) inputs[i].checked = this.checked;}"/>
+                </th>
+                <th>[% sort_head("sort_title", l('Title')) %]</th>
+                <th>[% sort_head("author", l('Author')) %]</th>
+                <th>[% sort_head("format", l('Format')) %]</th>
+                <th>[% l('Pickup Location') %]</th>
+                <th>[% l('Cancel if not filled by') %]</th>
+                <th>[% l('Status') %]</th>
+                <th>[% l('Notes') %]</th>
+            </tr>
+            </thead>
+            <tbody id="holds_temp_parent">
+
+                [%# Copy the ctx.holds into a local array, then add a SORT field
+                    that contains the value to sort on.  Since we need the item attrs,
+                    invoke it and save the result in ATTRS.
+               %]
+               [% 
+                hold_items = ctx.holds;
+
+                sort_field = CGI.param('sort');
+
+                FOR hold IN hold_items;
+                    hold.ATTRS = {marc_xml => hold.marc_xml};
+                    PROCESS get_marc_attrs args=hold.ATTRS;
+
+                    SWITCH sort_field;
+
+                       CASE "sort_title";
+                          hold.SORTING = hold.ATTRS.sort_title;
+
+                       CASE "author";
+                          hold.SORTING = hold.ATTRS.author;
+
+                       CASE "format";
+                          hold.SORTING = hold.ATTRS.format_label;
+                       
+                       CASE;
+                          sort_field = "";
+                    END; # SWITCH
+                END; #FOR hold
+
+                IF (sort_field != "sort_title");
+                   deemphasize_class = "";
+                ELSE;
+                   deemphasize_class = " class=\"sort_deemphasize\"";
+                END;
+
+                # Apply sorting to hold_items
+               IF (sort_field != "");
+                   hold_items = hold_items.sort("SORTING");
+                    IF (CGI.param("sort_type") == "desc");
+                        hold_items = hold_items.reverse;
+                    END;
+
+                    # Shorten the hold_items list per offset/limit/count 
+                    hi = offset + limit - 1;
+                    hi = hi > hold_items.max ? hold_items.max : hi;
+
+                    hold_items = hold_items.slice(offset, hi);
+                END;
+
+               # hold_items list is now sorted.  Traverse and dump the information.
+                cnt = 0;
+                FOR hold IN hold_items;
+                    ahr = hold.hold.hold;
+                    cnt = cnt + 1;
+                     %]
+                <tr name="acct_holds_temp"
+                    class="[% ahr.frozen == 't' ? ' inactive-hold' : '' %]">
+                    <td class="checkbox_column">
+
+                    <span class="sr-only">Hold Number [%  cnt; %]</span>
+                        <input type="checkbox" name="hold_id" value="[% ahr.id %]" 
+                            [% html_text_attr('title', l('Select hold [_1]', attrs.title)) %]/>
+                    </td>
+                    <td>
+                    <span class="sr-only">Title</span>
+                        <div>
+                            [% title = hold.ATTRS.title;
+                            IF ahr.hold_type == 'P';
+                               title = l('[_1] ([_2])', title, hold.hold.part.label);
+                            END; %]
+
+                            <a href="[% mkurl(ctx.opac_root _ '/record/' _ 
+                                hold.hold.bre_id, {}, 1) %]"
+                                name="[% l('Catalog record') %]"><span[%- deemphasize_class -%]>
+                                [%- title.substr(0,hold.ATTRS.nonfiling_characters) | html %]</span>
+                                [%- title.substr(hold.ATTRS.nonfiling_characters)   | html %]</a>
+                        </div>
+                    </td>
+                    <td>
+                    <span class="sr-only">Author</span>
+                        <div>
+                            <a href="[% mkurl(ctx.opac_root _ '/results',
+                                {qtype => 'author', query => hold.ATTRS.author.replace('[,\.:;]', '')},
+                                1
+                            ) %]">[% hold.ATTRS.author | html %]</a>
+                        </div>
+                    </td>
+                    <td>
+                    <span class="sr-only">Format</span>
+                        <div class="format_icon">
+                          [% 
+                            formats = hold.ATTRS.all_formats;
+                            IF ahr.hold_type == 'M';
+                              # only show selected formats for metarecords
+                              formats = [];
+                              FOR ccvm IN hold.metarecord_selected_filters.icons;
+                                NEXT IF ccvm.opac_visible == 'f';
+                                format = {};
+                                format.label = ccvm.search_label || ccvm.value;
+                                format.icon = PROCESS get_ccvm_icon ccvm=ccvm;
+                                formats.push(format);
+                              END;
+                            END;
+                            FOR format IN formats 
+                          %]
+                          <span class="sr-only">[% format.label | html %]</span>
+                            <img title="[% format.label | html %]" 
+                              alt="[% format.label | html %]" src="[% format.icon %][% ctx.cache_key %]" />
+                            [% END %]
+                        </div>
+                    </td>
+                    <td>
+                    <span class="sr-only">Pickup Location</span>
+                        [% ctx.get_aou(ahr.pickup_lib).name | html %]
+                    </td>
+                    <td>
+                    <span class="sr-only">Cancel if not filled by</span>
+                        [% IF ahr.expire_time;
+                            date.format(ctx.parse_datetime(ahr.expire_time), DATE_FORMAT);
+                        END %]
+                    </td>
+                    <td>
+                    <span class="sr-only">Status</span>
+                        <div name="acct_holds_status">
+                            [% PROCESS get_hold_status hold=hold; %]
+                        </div>
+                    </td>
+                    <td class="hold_notes">
+                    <span class="sr-only">Notes</span>
+                    [%- FOREACH pubnote IN ahr.notes;
+                        IF pubnote.pub == 't';
+                    %]
+                        <div class="hold_note">
+                            <span class="hold_note_title">[% pubnote.title | html %]</span>
+                            <br />
+                            <span class="hold_note_body">[% pubnote.body | html %]</span>
+                        </div>
+                    [%- END; END; %]
+                    </td>
+                    <td class="fullRow">
+                        <a class="btn btn-confirm btn-sm" href="[% mkurl(ctx.opac_root _ '/myopac/holds/edit', {hid => ahr.id}) %]"
+                            [% html_text_attr('title', l('Edit hold for item [_1]', attrs.title)) %]>
+                            [% l('Edit') %]
+                        </a>
+                    </td>
+                </tr>
+                [% END %]
+            </tbody>
+        </table>
+        </div>
+        [% END %]
+        </form>
+    </div></div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/holds/edit.tt2 b/Open-ILS/src/templates/opac-new/myopac/holds/edit.tt2
new file mode 100644 (file)
index 0000000..eeff1fb
--- /dev/null
@@ -0,0 +1,123 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_status.tt2";
+    PROCESS "opac/parts/org_selector.tt2";
+    PROCESS "opac/parts/metarecord_hold_filters.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "holds"; # in this case, just for tab coloring.
+
+    hold = ctx.holds.0;
+    ahr = hold.hold.hold;
+    attrs = {marc_xml => hold.marc_xml};
+    PROCESS get_marc_attrs args=attrs;
+    hold.human_status = PROCESS get_hold_status hold=hold;
+
+    # Do this up front to avoid verbosity later
+    expire_time = ahr.expire_time ? date.format(ctx.parse_datetime(ahr.expire_time), DATE_FORMAT) : '';
+    thaw_date = ahr.thaw_date ? date.format(ctx.parse_datetime(ahr.thaw_date), DATE_FORMAT) : '';
+%]
+<h3 class="sr-only">[% l('Edit Hold') %]</h3>
+<div class="pad-bottom-five">
+    <div class="header_middle">
+        <span id="acct_holds_header float-left">[% l('Editing Hold') %]</span>
+        &nbsp; &nbsp; &nbsp; &nbsp;
+        <a href="[% ctx.opac_root %]/myopac/holds" class="float-right">[% l('List all holds') %]</a>
+    </div>
+    <div id="hold_editor" class="mt-4">
+        [%  IF hold;
+                title = attrs.title;
+                IF ahr.hold_type == 'P';
+                    title = l('[_1] ([_2])', title, hold.hold.part.label);
+                END 
+        %]<div class="my-4">
+            <h1>[% title | html %]</h1>
+            <h2>[% attrs.author | html %]</h2>
+            [% IF attrs.format_icon %]<p>
+             <strong>[% l('Format:') %]</strong>
+                <img src="[% attrs.format_icon %]" alt="[% attrs.format_label | html %]" title="[% attrs.format_label | html %]" />
+            </p>[% END %]</div>
+                <p>
+                <strong>[% l('Status') %]</strong>: [% hold.human_status %]
+                </p>
+            <form method="post">
+                <table id="hold_editor_table" >
+                    <tr>
+                        <th>
+                            <input type="hidden" name="action" value="edit" />
+                            <input type="hidden" name="hold_id"
+                                value="[% ahr.id %]" />
+                            [% l('Pickup library') %]
+                        </th>
+                        <td>
+                            [% INCLUDE build_org_selector
+                                name='pickup_lib' value=ahr.pickup_lib 
+                                can_have_vols_only=1 hold_pickup_lib=1 %]
+                        </td>
+                    </tr>
+                    [% IF hold.hold.status < 3 OR hold.hold.status == 7 %]
+                    [%# The following actions cannot be performed on holds that 
+                        have already been captured... %]
+                    <tr>
+                        <th>
+                            [% l('Cancel unless filled by') %]
+                        </th>
+                        <td>
+                            <input class="form-control" type="text" name="expire_time"
+                                value="[% expire_time | html %]" />
+                               <p><em>[% l('Enter date in MM/DD/YYYY format') %]</em></p>
+                            <!-- XXX TODO pick out a minimal, simple, reliable
+                            calendar widget that's not part of some giant,
+                            bloated framework and doesn't do anything at onload.
+                            -->
+                           
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            [% l('Active?') %]
+                        </th>
+                        <td>
+                            <select class="form-control" name="frozen">
+                                <option value="f"[% ahr.frozen == 't' ? '' :' selected="selected"' %]>
+                                    [% l('Yes, this hold is active now') %]
+                                </option>
+                                <option value="t"[% ahr.frozen == 't' ? ' selected="selected"' : '' %]>
+                                    [% l('No, this hold is suspended') %]
+                                </option>
+                            </select>
+                        </td>
+                    </tr>
+                    <tr>
+                        <th>
+                            [% l('If suspended, activate on') %]
+                        </th>
+                        <td>
+                            <input class="form-control" type="text" name="thaw_date"
+                                value="[% thaw_date | html %]" />
+                                <p><em>[% l('Enter date in MM/DD/YYYY format') %]</em></p>
+                        </td>
+                    </tr>
+                    [% END %]
+                      
+                    <tr><td colspan='4'>
+                    [% IF hold.metarecord_filters.formats.size OR
+                        (hold.metarecord_filters.langs.size.defined && hold.metarecord_filters.langs.size > 1);
+                            PROCESS metarecord_hold_filters_selector 
+                                hold_data=hold; END %]
+                    </td></tr>
+                    <tr>
+                        <td colspan="2" class="hold-editor-controls">
+                            
+                            <input type="submit" value="[% l('Submit') %]" class="btn btn-confirm" />
+                            <a href="[% ctx.opac_root %]/myopac/holds"><button 
+                                class="btn btn-deny">[% l('Go Back') %]</button></a>
+                        </td>
+                    </tr>
+                </table>
+            </form>
+        [% ELSE;
+            l('Hold not found');
+        END %]
+    </div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/list/print.tt2 b/Open-ILS/src/templates/opac-new/myopac/list/print.tt2
new file mode 100644 (file)
index 0000000..fa3988f
--- /dev/null
@@ -0,0 +1 @@
+[%- ctx.csv.template_output.data -%]
diff --git a/Open-ILS/src/templates/opac-new/myopac/list/update.tt2 b/Open-ILS/src/templates/opac-new/myopac/list/update.tt2
new file mode 100644 (file)
index 0000000..2275df6
--- /dev/null
@@ -0,0 +1,44 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "lists/update"  
+%]
+
+<!-- we should never see this page on success -->
+
+[% IF ctx.bucket_action_failed %]
+<div id='bookbag_udpate_failures'>
+
+  <div>
+    <strong>[% l("Problem with list management:") %]</strong>
+  </div>
+
+  <div>
+    <ul>
+    [% IF ctx.bucket_action == 'create' %]
+      [% IF ctx.bucket_failure_noname %]         
+        <li>[% l('A list name is required') %]</li>
+      [% END %]   
+    [% END %]   
+    </ul>
+  </div>
+
+  <div>
+    [% url = ctx.referer;
+      # The return link should return the user to the page where the edit
+      # failure occurred.
+      # mkurl() does not support 'page' params w/ existing CGI params.
+      # build the URL manually.
+      IF ctx.where_from;
+        from = ctx.where_from | uri;
+        IF url.match('\?');
+          url = url _ ';where_from=' _ from;
+        ELSE;
+          url = url _ '?where_from=' _ from;
+        END;
+      END; %]
+    <a href="[% url %]">[% l('Return') %]</a>
+  </div>
+</div>
+[% END %]
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/lists.tt2 b/Open-ILS/src/templates/opac-new/myopac/lists.tt2
new file mode 100644 (file)
index 0000000..e9e2738
--- /dev/null
@@ -0,0 +1,518 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "lists"  
+    limit = ctx.bookbags_limit;
+    offset = ctx.bookbags_offset;
+    item_page = ctx.bookbags_item_page;
+    bb_publish_text = l(
+"Sharing a list means that the contents " _
+"of the list will be visible to others. " _
+"To see the public view of a shared list, " _
+"click on the HTML View link in the Saved Lists section.");
+%]
+<h3 class="sr-only">[% l('My Lists') %]</h3>
+<div id='myopac_bookbag_div' style="padding:5px;">
+
+    <!-- new list creation -->
+    <form action="[% mkurl(ctx.opac_root _ '/myopac/list/update') %]" method="post" id="create_form">
+        <h1>[% l('Create New List') %]</h1><a name="createnewlist"></a>
+        <table class="table" >
+            <tr>
+                <td class="list_create_table_label">
+                    <label for="list_create_name">[% l('Enter the name of the new list:') %]</label>
+                </td>
+                <td class="px-3 my-2">
+                    [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                    <input id="list_create_name" type="text" name="name" class="form-control"/>
+                    <input type="hidden" name="action" value="create" class="form-control" />
+                    [% IF ctx.add_rec %]
+                    <input type="hidden" name="add_rec" value="[% ctx.add_rec %]" class="form-control" />
+                    [% END %]
+                    [% IF ctx.where_from %]
+                    <input type="hidden" name="where_from" value="[% ctx.where_from %]" class="form-control" />
+                    [% END %]
+                </td>
+            </tr>
+            <tr>
+                <td class="list_create_table_label">
+                    <label for="list_description">[% l("List description (optional):") %]</label>
+                </td>
+                <td class="px-3">
+                    <textarea name="description" id="list_description" class="form-control"></textarea>
+                </td>
+            </tr>
+            <tr>
+                <td class="list_create_table_label">
+                    <label for="list_create_shared">[% l('Share this list?') %]</label>
+                </td>
+                <td class="px-3">
+                    <select class="form-control mini-control" name="shared" id="list_create_shared">
+                        <option value="0">[% l('No') %]
+                        <option value="1">[% l('Yes') %]
+                    </select>
+                   
+                    <a href="javascript:void(0);" 
+                        onclick="alert('[% bb_publish_text %]')" >
+                        <img data-toggle="tooltip" data-placement="top" alt="[% l('Sharing Help') %]"
+                            [% html_text_attr('title', bb_publish_text) %]
+                            src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]" />
+                    </a> 
+                </td>
+            </tr>
+            [% IF ctx.mylist.size %]
+            <tr>
+                <td class="list_create_table_label">
+                    <label for="list_move_cart">[% l('Move contents of basket to this list?') %]</label>
+                </td>
+                <td>
+                    <select name="move_cart" id="list_move_cart">
+                        <option value="0">[% l('No') %]
+                        <option value="1" [% IF CGI.param('move_cart_by_default') %]selected="selected"[% END%]>[% l('Yes') %]
+                    </select>
+                </td>
+            </tr>
+            [% END %]
+            <tr>
+                <td>&nbsp;</td>
+                <td class="list-create-table-buttons">
+                    <input type="reset"
+                        value="[% l('Cancel') %]"
+                        alt="[% l('Cancel') %]"
+                        class="btn btn-deny" />
+                    &nbsp;&nbsp;&nbsp;
+                    <input type="submit"
+                        value="[% l('Submit') %]"
+                        alt="[% l('Submit') %]"
+                        class="btn btn-confirm"/>
+                </td>
+            </tr>
+        </table>
+    </form>
+
+      [% IF CGI.param('from_basket'); %]
+    <h1>[% l("... from basket") %]</h1>
+    [% INCLUDE "opac/parts/anon_list.tt2" %]
+    [% ELSE %]
+    <h1>[% l("My Existing Basket and Lists") %]</h1>
+    [% INCLUDE "opac/parts/anon_list.tt2" %]
+    
+    [% IF ctx.bookbags.size %]
+    <div class="header_middle">
+        <span class="float-left">[% l('Saved Lists') %]</span>
+        [% IF limit < ctx.bookbag_count; %]
+        <span class='float-left' style='padding-left: 10px;'>
+            [%- IF offset > 0 -%]
+                <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                    offset => (offset - limit)
+                }) %]'><span class="nav_arrow_fix">&#9668;</span>[% l('Previous') %]</a>
+            [%- END; -%]
+            [%- IF (offset - (limit * 3)) >= 0 -%]
+                <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                    offset => (offset - (limit * 3))
+                }) %]'>[% ((offset - (limit * 3)) / limit) + 1 %]</a>
+            [%- END; -%]
+            [%- IF (offset - (limit * 2)) >= 0 -%]
+                <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                    offset => (offset - (limit * 2))
+                }) %]'>[% ((offset - (limit * 2)) / limit) + 1 %]</a>
+            [%- END; -%]
+            [%- IF (offset - limit) >= 0 -%]
+                <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                    offset => (offset - limit)
+                }) %]'>[% ((offset - limit) / limit) + 1 %]</a>
+            [%- END; -%]
+            <span class="bookbag-paginator-selected" >[% (offset / limit) + 1 %]</span>
+            [%- IF (offset + limit) < ctx.bookbag_count -%]
+                <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                    offset => (offset + limit)
+                }) %]'>[% ((offset + limit) / limit) + 1 %]</a>
+            [%- END; -%]
+            [%- IF (offset + (limit * 2)) < ctx.bookbag_count -%]
+                <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                    offset => (offset + (limit * 2))
+                }) %]'>[% ((offset + (limit * 2)) / limit) + 1 %]</a>
+            [%- END; -%]
+            [%- IF (offset + (limit * 3)) < ctx.bookbag_count -%]
+                <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                    offset => (offset + (limit * 3))
+                }) %]'>[% ((offset + (limit * 3)) / limit) + 1 %]</a>
+            [%- END; -%]
+            [%- IF (ctx.bookbag_count - offset) > limit; -%] 
+                <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                    offset => (offset + limit)
+                }) %]'>[% l('Next') %]<span class="nav_arrow_fix">&#9658;</span></a>
+            </span>
+            [%- END; -%]
+        [% END %]
+    </div>
+    <div class="clear-both"></div>
+
+    <div id='acct_lists_prime'>
+        [% FOR bbag IN ctx.bookbags %]
+        <div>
+         <table class="table"><tr>
+            <td width="30%">
+            <div class="bookbag-controls-title-block">
+                [% baseurl = ctx.opac_root _ '/myopac/lists';
+                IF bbag.id != CGI.param("bbid");
+                    url = mkurl(baseurl,{bbid => bbag.id, item_page => 1},['edit_notes','sort']);
+                    ltitle = l("Show items in list");
+                ELSE;
+                    url = mkurl(baseurl, {}, ['bbid', 'edit_notes', 'sort']);
+                    ltitle = l("Hide items in list");
+                END %]
+                <h2 class="bookbag-name"><a title="[% ltitle %]" href="[% url %]">[% bbag.name | html %]</a></h2>
+                [% IF bbag.description %]<div class="bookbag-description">[% bbag.description | html %]</div>[% END %]
+            </div>
+            </td>
+           
+            <td>
+            [% IF ctx.add_rec %]
+            <form action="[% mkurl(ctx.opac_root _ '/myopac/list/update', {}, 1) %]" method="post">
+                <div class="bookbag-controls">
+                    <input type="hidden" name="action" value="add_rec" />
+                    <input type="hidden" name="list" value="[% bbag.id %]" />
+                    <input type="hidden" name="add_rec" value="[% ctx.add_rec %]" />
+                    [% IF ctx.where_from %]
+                    <input type="hidden" name="where_from" value="[% ctx.where_from %]" />
+                    [% END %]
+                    <input class="fixed btn btn-secondary btn-sm m-1" type="submit" value="[% l('Add to this list') %]" />
+                </div>
+            </form>
+            [% END %]
+            <form action="[% mkurl(ctx.opac_root _ '/myopac/list/update') %]" method="post">
+                <div class="bookbag-share">
+                    <input type="hidden" name="list" value="[% bbag.id %]" />
+                    [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                    [% IF bbag.pub != 't' %]
+                    <input type="hidden" name="action" value="show" />
+                    <input class="class=fixed btn btn-secondary btn-sm m-1" type="submit"  value="[% l('Share') %]" />
+                    [% ELSE %]
+                    <input type="hidden" name="action" value="hide" />
+                    <input class="fixed btn btn-secondary btn-sm m-1" type="submit" value="[% l('Hide') %]" />
+                    [% END %]
+                </div>
+            </form>
+            <form action="[% mkurl(ctx.opac_root _ '/myopac/list/update') %]" method="post" onsubmit="return confirm('[% l('Do you really want to delete this list?') %]')">
+                <div class="bookbag-controls">
+                    <input type="hidden" name="list" value="[% bbag.id %]" />
+                    <input type="hidden" name="action" value="delete" />
+                    [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                    <input type="submit" value="[% l('Delete List') %]" class="btn btn-secondary btn-sm m-1"/>
+                </div>
+            </form>
+            <form action="[% mkurl(ctx.opac_root _ '/myopac/list/print') %]" method="post">
+                <div class="bookbag-controls">
+                    <input type="hidden" name="list" value="[% bbag.id %]" />
+                    <input type="hidden" name="sort" value="[% CGI.param('sort') | html %]" />
+                    [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                    <input type="submit" value="[% l('Download CSV') %]" class="btn btn-secondary btn-sm m-1"/>
+                </div>
+            </form>
+            [% setting = 'opac.default_list'; %]
+            <form action="[% mkurl(ctx.opac_root _ '/myopac/list/update') %]" method="post">
+                <div class="bookbag-controls">
+                    <input type="hidden" name="list" value="[% bbag.id %]" />
+                    [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                    [% IF ctx.user_setting_map.$setting == bbag.id %]
+                    <input type="hidden" name="action" value="remove_default" />
+                    <input type="submit" class="btn btn-secondary btn-sm m-1" value="[% l('Remove Default List') %]" />
+                    [% ELSE %]
+                    <input type="hidden" name="action" value="make_default" />
+                    <input type="submit" class="btn btn-secondary btn-sm m-1" value="[% l('Make Default List') %]" />
+                    [% END %]
+                </div>
+            </form>
+            [% IF ctx.is_staff %]
+            <div class="bookbag-controls">
+                <input 
+                    type="submit" 
+                    onclick='
+                       var path = 
+                          "oils://remote/xul/server/cat/bucketz39_dialog.xul";
+                        window.openDialog(
+                            xulG.url_prefix(path),
+                            "bucketz39_dialog",
+                            "width=800,height=500",
+                            "[% ctx.user.id %]",
+                            "[% ctx.authtoken %]",
+                            "[% ctx.user.ws_ou %]",
+                            "[% bbag.id %]",
+                            xulG
+                        )'
+                    value="[% l('Locate Z39.50 Matches') %]" 
+                />
+            </div>
+            [% END %]
+            <div class="bookbag-controls">
+                [% IF bbag.pub == 't'; %]
+                <a class="btn btn-sm btn-secondary m-1" href='[%-
+                    mkurl(
+                        ctx.opac_root _ '/results',
+                        {page => '0', bookbag => bbag.id, depth => 0, locg => ctx.search_ou},
+                        1
+                    )
+                -%]'>[% l('View in Catalogue') %]</a>
+                [% END %]
+                [% IF bbag.pub == 't'; %]
+                <a target='_blank' href='/opac/extras/feed/bookbag/rss2-full/[% bbag.id %]'><img
+                    alt="[% l('RSS Feed') %]" border="0"
+                    src="[% ctx.media_prefix %]/images/small-rss.png[% ctx.cache_key %]"/></a>
+                [% END %]
+            </div>
+            </td>
+            </tr>
+            </table>
+            <div class="clear-both pad-bottom-five"></div>
+        </div>
+        [% IF CGI.param("bbid") == bbag.id %]
+        <div>
+            [% IF bbag.items.size %]
+                <div class="d-none">
+                    <form method="get" class="form-row">
+                        <label for="opac.result.sort" class="form-row">[% l("Sort list items by: ") %]
+                        [%- INCLUDE "opac/parts/preserve_params.tt2" params=['loc', 'query', 'qtype']; %]
+                        [% INCLUDE "opac/parts/filtersort.tt2"
+                            value=CGI.param('sort') mode='bookbag' class='form-control my-2' %]
+                        <input type="hidden" name="bbid"
+                            value="[% CGI.param('bbid') | html %]"/>
+                        <input type="submit" class="btn btn-confirm btn-sm my-2" value="[% l('Sort') %]" />
+                        </label>
+                    </form>
+                </div>
+            [% END %]
+            <div>
+                <form method="post">
+                    <input type="hidden" name="bbid" value="[% bbag.id %]" />
+                    <input type="hidden" name="action" value="editmeta" />
+                    <input type="hidden" name="limit" value="[% limit %]" />
+                    <input type="hidden" name="offset" value="[% offset %]" />
+                    [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                       <table title="Edit List Description" class="mx-auto">
+                               <th colspan="2" class="text-center"><strong>Edit List Description</strong></th>
+                               <tr>
+                            <td> 
+                                <label for="bbag-edit-name">[% l('Name:') %]</label>
+                            </td>
+                            <td class="px-3">
+                               <input name="name" type="text" value="[% bbag.name | html %]" id="bbag-edit-name" class="form-control m-2" /></td>
+                            </tr>
+                            <tr>
+                            <td>
+                               <label for="bbag-edit-description">[% l('Description:') %]</label>
+                            </td>
+                            <td class="px-3">
+                                <textarea name="description" id="bbag-edit-description" class="form-control m-2">[% bbag.description | html %]</textarea>
+                            </td>
+                            </tr>
+                            <tr>
+                               <td colspan="2" >
+                                 [% l("Save changes?") %]
+                                <input type="submit" class="btn btn-confirm btn-sm mx-3" value="[% l('Save') %]" />
+                                </td>
+                            </tr>
+                        </table>
+                </form>
+            </div>
+        </div>
+        <br class="clear-both" />
+        <div>
+        <h5 class="text-center my-2">List Items</h5>
+        <form action="[% mkurl(ctx.opac_root _ '/myopac/list/update') %]" method="post" >
+        <input type="hidden" name="list" value="[% bbag.id %]" />
+        <input type="hidden" name="sort" value="[% CGI.param('sort') | uri %]" />
+        <div>
+        [% IF bbag.items.size %]
+            <div class="my-2">
+                <select name="action"  class="form-control float-left w-75">
+                    <option disabled="disabled" selected="selected">[% l('-- Actions for these items --') %]</option>
+                    <option value="place_hold">[% l('Place hold') %]</option>
+                     <option value="print">[% l('Print title details') %]</option>
+                    <option value="email">[% l('Email title details') %]</option>
+                    <option value="del_item">[% l('Remove from list') %]</option>
+                </select>
+                [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                <input class="btn btn-confirm btn-sm m-1" type="submit" value="[% l('Go') %]" />
+                 [% IF CGI.param('list_none_selected') %]
+                    <span class="error">[% l('No items were selected') %]</span>
+                [% END %]
+            </div>
+        [% END %]
+        
+        <div>
+        <table class="table table-hover miniTable w-100 bookbagTable">
+            <thead>
+                <tr>
+                    <th class="list_checkbox">
+                    <input type="checkbox" 
+                      onclick="var inputs=document.getElementsByTagName('input'); 
+                        for (i = 0; i < inputs.length; i++) { 
+                            if (inputs[i].name == 'selected_item' &amp;&amp; !inputs[i].disabled &amp;&amp; inputs[i].getAttribute('bbag') == [% bbag.id %]) 
+                                inputs[i].checked = this.checked;}"/>
+
+                    </th>
+                    <th class="list_entry">
+                        <a href="[% mkurl(ctx.opac_root _ '/myopac/lists', {sort=> (CGI.param('sort') == 'titlesort' ? 'titlesort.descending' : 'titlesort')}) %]">[% l('Title') %]</a>
+                    </th>
+                    <th class="list_entry">
+                        <a href="[% mkurl(ctx.opac_root _ '/myopac/lists', {sort=>(CGI.param('sort') == 'authorsort' ? 'authorsort.descending' : 'authorsort')}) %]">[% l('Author(s)') %]</a>
+                    </th>
+                    <th class='list_entry'>
+                        [% l('Local Call Number') %]
+                    </th>
+                    <th class="list_entry">
+                        <a href="[% mkurl(ctx.opac_root _ '/myopac/lists', {sort=>(CGI.param('sort') == 'pubdate' ? 'pubdate.descending' : 'pubdate')}) %]">[% l('Publication Date') %]</a>
+                    </th>
+                    <th class="list_entry">
+                         [% l('Format') %]
+                    </th>
+                    <th class="list_entry">
+                        [% l('Notes') %]
+                        [% IF CGI.param("edit_notes") != bbag.id %]
+                        | <a href="[% mkurl(ctx.opac_root _ '/myopac/lists', {edit_notes=> bbag.id}) %]">[% l('Edit') %]</a>
+                        [% END %]
+                    </th>
+                </tr>
+            </thead>
+            <tbody>
+                [% UNLESS bbag.items.size %]
+                <tr><td colspan="6" class="list_is_empty">
+                    [% l("This list contains no items.") %]
+                </td></tr>
+                [% END %]
+                [% FOR item IN bbag.items;
+                    rec_id = item.target_biblio_record_entry.id;
+                    attrs = {marc_xml => ctx.bookbags_marc_xml.$rec_id};
+                    PROCESS get_marc_attrs args=attrs %]
+                <tr class="bookbag-item-row">
+                    <td class="list_checkbox">
+                        <input type="checkbox" name="selected_item" value="[% item.id %]" bbag='[% bbag.id %]'/>
+                    </td>
+                    <td class="list_entry" data-label="[% l('Title') %]">
+                        <a href="[% mkurl(ctx.opac_root _ '/record/' _ rec_id, {}, ['edit_notes', 'bbid']) %]">[% attrs.title | html %]</a>
+                    </td>
+                    <td class="list_entry" data-label="[% l('Author(s)') %]">
+                        <a href="[%-
+                            authorquery = attrs.author | replace('[,\.:;]', '');
+                            mkurl(ctx.opac_root _ '/results', {qtype => 'author', query => authorquery}, ['page', 'bbid', 'edit_notes'])
+                            -%]">[% attrs.author | html %]</a>
+                    </td>
+                    <td class="list_entry" data-label="[% l('Local Call Number') %]">
+                        [% 
+                            copy = attrs.holdings.0;
+                            IF copy;
+                                # only show a relevant call number
+                                copy_org = ctx.get_aou_by_shortname(copy.owner);
+                                FOR ctx_org IN [ctx.pref_ou, ctx.search_ou, ctx.home_ou, ctx.physical_loc];
+                                    NEXT UNLESS ctx_org;
+                                    ctx_org = ctx.get_aou(ctx_org);
+                                    IF ctx.org_within_scope(ctx_org, copy_org, ctx_org.ou_type.depth);
+                                        l('[_1] ([_2])', copy.label, copy_org.name) | html;
+                                        LAST;
+                                    END;
+                                END;
+                            END;
+                        %]
+                    </td>
+                    <td class="list_entry" data-label="[% l('Publication Date') %]">
+                          [% attrs.pubdate | html %]
+                    </td>
+                    <td class="list_entry" data-label="[% l('Format') %]">
+                          [% attrs.format_label | html %]
+                    </td>
+                    [% IF CGI.param("edit_notes") == bbag.id %]
+                    <td class="list_entry" data-label="[% l('Notes') %]">
+                        [% FOR note IN item.notes %]
+                        <input type="text" name="note-[% note.id %]" value="[% note.note | html %]" />
+                        [% END %]
+                        <input type="text" name="item-[% item.id %]" />
+                    </td>
+                    [% ELSE %]
+                    <td class="list_entry" data-label="[% l('Notes') %]">
+                        [% FOR note IN item.notes %]
+                        <div>[% note.note | html %]</div>
+                        [% END %]
+                    </td>
+                    [% END %]
+                </tr>
+                [% END %]
+                [% IF CGI.param("edit_notes") == bbag.id %]
+                <tr class="mobile_hide">
+                    <td colspan="6"><!-- All space left of notes column --></td>
+                    <td class="save-notes">
+                        [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                        <input type="hidden" name="bbid" value="[% CGI.param('bbid') | html %]" />
+                        <input type="submit" class="btn btn-confirm" name="save_notes" value="[% l('Save Notes') %]" />
+                    </td>
+                </tr>
+                [% END %]
+            </tbody>
+        </table>
+        </div>
+        
+        
+        
+        </div>
+        [% IF ctx.bb_page_count > 1; %]
+            <div class="header_middle" style="padding-top:7px;">
+                <div class="bbag-navigate-list">[% l('Navigate Selected List ') %]</div>
+                <div class="bbag-navigate-list-pages">
+                    [%- IF item_page > 1 -%]
+                        <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                            item_page => item_page - 1
+                        }) %]'><span class="nav_arrow_fix">&#9668;</span>[% l('Previous') %]</a>
+                    [%- END; -%]
+
+                    [%- IF (item_page - 3) >= 1 -%]
+                        <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                            item_page => item_page - 3
+                        }) %]'>[% item_page - 3 %]</a>
+                    [%- END; -%]
+                    [%- IF (item_page - 2) >= 1 -%]
+                        <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                            item_page => item_page - 2
+                        }) %]'>[% item_page - 2 %]</a>
+                    [%- END; -%]
+                    [%- IF (item_page - 1) >= 1 -%]
+                        <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                            item_page => item_page - 1
+                        }) %]'>[% item_page - 1%]</a>
+                    [%- END; -%]
+                    <span class="bookbag-paginator-selected" >[% item_page %]</span>
+                    [%- IF (item_page + 1) <= ctx.bb_page_count -%]
+                        <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                            item_page => item_page + 1
+                        }) %]'>[% item_page + 1 %]</a>
+                    [%- END; -%]
+                    [%- IF (item_page + 2) <= ctx.bb_page_count -%]
+                        <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                            item_page => item_page + 2
+                        }) %]'>[% item_page + 2 %]</a>
+                    [%- END; -%]
+                    [%- IF (item_page + 3) <= ctx.bb_page_count -%]
+                        <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                            item_page => item_page + 3
+                        }) %]'>[% item_page + 3 %]</a>
+                    [%- END; -%]
+
+                    [%- IF (item_page + 1) <= ctx.bb_page_count; -%]
+                        <a href='[% mkurl(ctx.opac_root _ '/myopac/lists', {
+                            item_page => item_page + 1
+                        }) %]'>[% l('Next') %]<span class="nav_arrow_fix">&#9658;</span></a>
+                    [%- END; -%]
+                 </div>
+            </div>
+            <div class="clear-both"></div>
+        [% END %]
+        <br/>
+
+        </form>
+        </div>
+        [% END %]
+        [% END %]
+    </div>
+    [% END %][% END %]
+</div></div>
+[% END %]
+
diff --git a/Open-ILS/src/templates/opac-new/myopac/main.tt2 b/Open-ILS/src/templates/opac-new/myopac/main.tt2
new file mode 100644 (file)
index 0000000..99ed0ea
--- /dev/null
@@ -0,0 +1,232 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/main_base.tt2";
+    myopac_page = "main";
+    myopac_main_page = "main";
+    myopac_cc_allowed = 0;
+    IF (ctx.fines.grocery.size OR ctx.fines.circulation.size) AND ctx.get_org_setting(ctx.user.home_ou, 'credit.payments.allow') == 1;
+        myopac_cc_allowed = 1;
+    END
+%]
+<h3 class="sr-only">[% l('My Account Summary') %]</h3>
+[%  IF myopac_cc_allowed;
+    # http://www.w3.org/TR/WCAG20-TECHS/H32.html
+    # avoid forms w/ no submit action %]
+<form action="[% ctx.opac_root %]/myopac/main_payment_form#payment" method="get" id="selected_fines" class="my-3">
+[% END %]
+
+    [% IF ctx.fines.circulation.size > 0 %]
+    <div id='myopac_circ_trans_div'>
+      <!--moved tables heading to outside of the table itself - took internal styling -->
+        <div class="header_middle">
+           <span class="acct_fines_header">
+           <!-- Different heading when not able to pay -->
+             [% IF myopac_cc_allowed %]
+                [% l("Pay Selected Circulation Charges") %]
+            [% ELSE %] 
+                [% l("Circulation Charges") %]
+             [% END %]
+           </span>
+           
+        </div>
+    
+        <table id="acct_fines_main_header" class="table table-hover miniTable chargesMainTable" title="[% l('Items Checked Out') %]">
+            <thead>
+               
+                <tr>
+
+                   [% IF myopac_cc_allowed %]
+                    <th nowrap="nowrap" style="white-space:nowrap;">
+                        <input id="pay_fines_box1" checked="checked"
+                            type="checkbox" onclick="select_all_checkboxes('xact', this.checked)"
+                            title="[% l('Click to (un)select all charges') %]" />
+                    </th>
+                    [% END %]
+                    <th>[% l("Owed") %]</th>
+                    <th class="mobile_hide">[% l("Billing Type") %]</th>
+                    <th>[% l("Title") %]</th>
+                    <th class='mobile_hide'>[% l("Checkout") %]</th> 
+                    <th>[% l(" Due ") %]</th> 
+                    <th>
+                       <span class="mobile_hide">[% l(" Returned/Renewed") %]</span>
+                       <span class="mobile_view">[% l(" Returned&#42;") %]</span>
+                    </th>
+                    
+                </tr>
+            </thead>
+            <tbody id='myopac_circ_trans_tbody'>
+                [% FOR f IN ctx.fines.circulation;
+                    attrs = {marc_xml => f.marc_xml};
+                    IF f.marc_xml;
+                        PROCESS get_marc_attrs args=attrs;
+                    ELSIF f.xact.reservation;
+                        attrs.title = f.xact.reservation.target_resource_type.name;
+                    END %]
+                <tr id='myopac_circ_trans_row'>
+                    [% IF myopac_cc_allowed %]
+                    <td>
+                        <input type="checkbox" checked="checked" 
+                            title="[% l('Pay this fine') %]" name="xact"
+                            value="[% f.xact.id %]" class="fineCheck"/>
+                    </td>
+                    [% END %]
+                    
+                    <td>
+                        <strong class="fineAmount" value="[% f.xact.balance_owed %]">
+                            [% money(f.xact.balance_owed) %]
+                        </strong>
+                    </td>
+                    
+                    <td class="mobile_hide">
+                       [% f.xact.last_billing_type %]
+                    </td>
+                                  
+                    <td>
+                        [% recid = f.xact.circulation.target_copy.call_number.record.id || f.xact.reservation.target_resource_type.record.id;
+                        IF recid; %]
+                        <a href="[% mkurl(ctx.opac_root _ '/record/' _ recid,
+                            {loc => ctx.search_ou}) %]">[% attrs.title | html %]</a>
+                        [% ELSE %]
+                        [% attrs.title | html %]
+                        [% END %]
+                    </td>
+                    
+                   <td name='myopac_circ_trans_start' class='mobile_hide'>
+                        [% ts = f.xact.circulation.xact_start || f.xact.reservation.start_time || 0;
+                        IF ts;
+                            date.format(ctx.parse_datetime(ts), DATE_FORMAT);
+                        END %]
+                    </td>
+                    
+                    <td name='myopac_circ_trans_due' >
+                        [% ts = f.xact.circulation.due_date || f.xact.reservation.end_time || 0;
+                           due_org = f.xact.circulation.circ_lib || f.xact.reservation.pickup_lib;
+                        IF ts;
+                            date.format(ctx.parse_datetime(ts, due_org), DATE_FORMAT);
+                        END %]
+                    </td>
+                    
+                    <td name='myopac_circ_trans_finished' >
+                        [%  ts = f.xact.circulation.checkin_time || f.xact.reservation.return_time || 0;
+                            IF ts;
+                                date.format(ctx.parse_datetime(ts), DATE_FORMAT);
+                            ELSE %]
+                            <!-- XXX TODO fines aren't really accruing
+                                if circ has hit maxfines. more clarity
+                                here? --> 
+                            <span class="alert">[% l('(fines accruing)') %]</span>
+                        [%  END %]
+                    </td>
+                   
+                </tr>
+                [% END %]
+            </tbody>
+        </table>
+        <!-- Disclaimer for mobile -->
+        <span class="mobile_view cc_disclaimer"> <strong>&#42;</strong>Items may have been renewed. </span>
+    </div>
+    [% END %]
+
+    [% IF ctx.fines.grocery.size > 0 %]
+    <!-- Table for all non-circulation transactions -->
+    <div id='myopac_trans_div'>
+        <h2 class="acct_fines_header text-center">
+                [% l("Charges On Your Account") %]
+        </h2>
+        
+        
+         <table id="acct_fees_main_header" class="table table-hover miniTable chargesTable my-3" title="[% l('Other Fines and Fees') %]">
+            <thead>
+                <tr>
+                
+                     [% IF myopac_cc_allowed %]
+                    <th  nowrap="nowrap" style="white-space:nowrap;">
+                        <input id="pay_fines_box2" checked="checked"
+                            type="checkbox" onclick="select_all_checkboxes('xact_misc', this.checked)"
+                            title="[% l('Click to (un)select all fines') %]" />
+                    </th>
+                    [% END %]
+                    <th >[% l("Owed") %]</th>
+                    <th >[% l("Billing Type") %]</th>
+                    <th >[% l("Note") %]</th>
+                    <th >[% l("Date") %]</th>
+                    
+
+                </tr>
+            </thead>
+            <tbody id='myopac_trans_tbody'>
+                [% c = 0; %]
+                [% FOR f IN ctx.fines.grocery %]
+                <tr id='myopac_trans_row'>
+                [% c = c + 1; %]
+                    [% IF myopac_cc_allowed %]
+                    <td>
+                        <span class="sr-only">Billing Number [% c %]</span>
+                        <input class="fineCheck" type="checkbox" title='[% l("Pay this fine") %]' name="xact_misc" value="[% f.xact.id %]" checked="checked" />
+                    </td>
+                    [% END %]
+                    
+                    <td class="red">
+                        <span class="sr-only">Amount Owed</span>
+                        <strong class="fineAmount" value="[% f.xact.balance_owed %]">
+                            [% money(f.xact.balance_owed) %]
+                        </strong>
+                    </td>
+                    
+                    <td>
+                        <span class="sr-only">Billing For</span>
+                        [% f.xact.last_billing_type %]
+                    </td>
+                    
+                    <td>
+                        [% IF f.xact.last_billing_note; %]
+                            <span class="sr-only">Note</span>
+                            [% f.xact.last_billing_note %]
+                        [% END %]
+                    </td>
+                    
+                    <td>
+                        <span class="sr-only">Date of Billing</span>
+                        [% date.format(ctx.parse_datetime(f.xact.xact_start),DATE_FORMAT) %]
+                    </td>
+
+                </tr>
+                [% END %]
+            </tbody>
+        </table>
+   
+    </div>
+    [% END %]
+    [% UNLESS ctx.fines.grocery.size OR ctx.fines.circulation.size %]
+    <div>[% l('You have no current fines.') %]</div>
+    [% ELSIF myopac_cc_allowed %]
+   
+    <div class="my-3">
+        <input type="submit"
+            value="[% l('Pay selected charges') %] - [% money(ctx.user_stats.fines.balance_owed) %]"
+            title="[% l('Pay selected charges') %]"
+            class="btn btn-confirm my-1"
+            id="selectPay"/>
+    <form action="[% ctx.opac_root %]/myopac/main_payment_form" method="get"><input
+            type="submit" title="[% l('Pay All Charges') %] "
+            value="[% l('Pay All Charges') %] - [% money(ctx.user_stats.fines.balance_owed) %]" class="btn btn-confirm my-1" /></form>
+    </div>
+    [% END %]
+[% IF myopac_cc_allowed %]
+</form>
+[% END %]
+[% END %]
+
+<!--Table functions ~ Running total of selected items for use in a Paypal module or where running total is needed-->
+<script>
+jQuery("input[type=checkbox]").click(function () {
+       var amount = 0;
+    jQuery("td input:checked").each(function () {
+               var item = jQuery( this ).parent().parent().children("td").children(".fineAmount").attr("value");
+        item = parseFloat(item);
+        amount += item;
+    });
+    jQuery("#total").val(amount);
+    jQuery("#selectPay").val('Pay selected charges - $' + amount.toFixed(2));
+});
+</script>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/myopac/main_pay.tt2 b/Open-ILS/src/templates/opac-new/myopac/main_pay.tt2
new file mode 100644 (file)
index 0000000..5f59e53
--- /dev/null
@@ -0,0 +1,63 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "main";
+    myopac_main_page = "pay" %]
+<div id="myopac_summary_div">
+
+    [% IF ctx.payment_response.textcode %]
+        <div class="payment-error">
+            <span title="[% ctx.payment_response.textcode %]">
+                [% ctx.payment_response.desc || ctx.payment_response.textcode %]
+            </span><br />
+            [% ctx.payment_response.note %]
+            [% ctx.payment_response.payload.error_message | html %]
+            [% ctx.payment_response.payload.message | html %]
+        </div>
+        <p>
+            [%
+                url_args = {xact => [], xact_misc => []};
+                FOR k IN ['xact', 'xact_misc'];
+                    FOR val IN CGI.param(k);
+                        url_args.$k.push(val);
+                    END;
+                END;
+                retry_url =  mkurl(ctx.opac_root _ '/myopac/main_payment_form', url_args, 1);
+            %]
+            <br/>
+            
+             <a href="[% mkurl(ctx.opac_root _ '/myopac/main', {}, 1) %]" class="btn btn-confirm">[% l('Account Home') %]</a> 
+             <a href="[% retry_url %]" class="btn btn-confirm">[% l('Try Payment Again') %]</a>
+        </p>
+    [% ELSE %]
+        <p><big>[% l('Your payment has been approved.') %]</big>
+        [% IF ctx.printable_receipt.template_output;
+            print_args = [];
+            FOR p IN ctx.payment_response.payments;
+                print_args.push('payment=' _ p);
+            END %]
+               <a href="[% ctx.opac_root %]/myopac/receipt_print?[% print_args.join('&amp;') %]"
+            target="_egrecpt"
+            class="btn-confirm btn btn-sm"
+            onclick="try { print_node('printable-receipt'); } catch (e) { window.print(); } return false;">[% l('Print receipt') %]</a></p>
+            <hr>
+        <tt id="printable-receipt">
+            [% ctx.printable_receipt.template_output.data %]
+            <br />
+        </tt>
+               <hr>
+        [% ELSE %]
+        <div class="payment-error">
+            [% l(
+                'Error creating receipt: [_1]',
+                    (ctx.printable_receipt.textcode ? ctx.printable_receipt.textcode _ ' / ' _ ctx.printable_receipt.desc : 0) ||
+                    ctx.printable_receipt.error_output.data ||
+                    l('No receipt data returned from server')
+                ) | html %]
+        </div>
+        [% END %]
+        <p><a href="[% ctx.opac_root %]/myopac/main" class="btn btn-confirm">[%
+            l("Back to Account Summary") %]</a></p>
+    [% END %]
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/main_pay_init.tt2 b/Open-ILS/src/templates/opac-new/myopac/main_pay_init.tt2
new file mode 100644 (file)
index 0000000..8355710
--- /dev/null
@@ -0,0 +1,15 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "main";
+    myopac_main_page = "pay" %]
+<div id="myopac_summary_div">
+
+    <div class="payment-processing">
+        [% l('Processing...') %] <br/><br/>
+        [% l('Processing your payment may take some time.') %]<br/>
+        [% l("Please do not Refresh or use your browser's Back button or your credit card may be charged more than once.") %]<br/>
+    </div>
+
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/main_payment_form.tt2 b/Open-ILS/src/templates/opac-new/myopac/main_payment_form.tt2
new file mode 100644 (file)
index 0000000..de357f5
--- /dev/null
@@ -0,0 +1,251 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/main_base.tt2";
+    myopac_page = "main";
+    myopac_main_page = "payment_form";
+
+    last_chance = CGI.param("last_chance");
+
+    IF myopac_main_page == "payment_form" AND
+        ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.stripe.enabled') AND ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.default') == 'Stripe';
+        ctx.use_stripe = 1;
+    END %]
+
+<a name="payment"> </a>    
+<h3 class="sr-only">[% l('Pay Charges') %]</h3>
+[% IF ctx.fines.balance_owed <= 0 %]
+<div>
+    [% l("The minimum amount you can pay is \$0.01.") %]
+</div>
+[% ELSE %]
+[% IF ctx.use_stripe %]
+<noscript>
+    [% l("Your browser does not have Javascript enabled, and we cannot " _
+        "process credit card payments without it.  Please change your " _
+        "browser settings and try again.") %]
+</noscript>
+[% END %]
+<div id="pay_fines_now"[% IF ctx.use_stripe %] class="hide_me"[% END %]>
+     [% IF last_chance %]
+    
+    <p><big>[% l("Are you sure you are ready to charge ") %]
+         <strong> [% l("[_1] ", money(ctx.fines.balance_owed))%]</strong>
+        [% l("to your credit card?") %]</big></p> 
+    <form action="[% ctx.opac_root %]/myopac/main_pay_init" method="post">
+        [% FOR k IN CGI.Vars;
+            NEXT UNLESS k;
+            FOR val IN CGI.param(k) %]
+        <input type="hidden" name="[% k | html %]" value="[% val | html %]" /> 
+        [% END; END %]
+
+        <input type="submit" value="[% l('Submit') %]" class="btn btn-confirm"/>
+        <a href="[% mkurl(ctx.opac_root _ '/myopac/main#selected_fines', {}, 1) %]" class="btn btn-deny">[% l('Cancel') %]</a>
+       
+     <table title="[% l('List of Transactions') %]" id="acct_fines_confirm_header"
+        class="table_no_border_space table_no_cell_pad my-2">
+   <thead>
+      <tr>
+        <th>[% l('Charge/Fee') %]</th>
+        <th>[% l('Amount') %]</th>
+     </tr>
+   </thead>
+   <tbody>
+      [%
+       FOR f IN ctx.fines.circulation;
+         NEXT IF CGI.param('xact').size &&
+            !CGI.param('xact').grep(f.xact.id).size;
+         attrs = {marc_xml => f.marc_xml};
+         IF f.marc_xml;
+             PROCESS get_marc_attrs args=attrs;
+         ELSIF f.xact.reservation;
+              attrs.title = f.xact.reservation.target_resource_type.name;
+         END %]
+         <tr>
+            <td>[% attrs.title | html %]</td>
+            <td class="text-right">[% money(f.xact.balance_owed) %]</td>
+         </tr>
+          [%
+          END;
+          FOR f IN ctx.fines.grocery;
+              NEXT IF CGI.param('xact_misc').size &&
+                  !CGI.param('xact_misc').grep(f.xact.id).size %]
+              <tr>
+                 <td>[% f.xact.last_billing_type | html %]</td>
+                 <td class="text-right">[% money(f.xact.balance_owed) %]</td>
+            </tr>
+        [% END %]
+     </tbody>
+   </table> 
+     
+    <p style="padding-top: 2em;">
+    <big>[% l("Are you sure you are ready to charge ") %]
+         <strong> [% l("[_1] ", money(ctx.fines.balance_owed))%]</strong>
+        [% l("to your credit card?") %]</big></p>        
+        <input type="submit" value="[% l('Submit') %]" class="btn btn-confirm"/>
+        <a href="[% mkurl(ctx.opac_root _ '/myopac/main#selected_fines', {}, 1) %]" class="btn btn-deny">[% l('Cancel') %]</a>
+    [% ELSE %]
+    
+    <form method="post" id="payment_form" action='#payment'
+    [% IF ctx.use_stripe %]
+    onsubmit="return stripe_onsubmit();"
+    [% END %]
+    >
+        <input type="hidden" name="last_chance" value="1" />
+        [% FOR xact IN CGI.param('xact') %]
+        <input type="hidden" name="xact" value="[% xact | html %]" />
+        [% END %]
+        [% FOR xact IN CGI.param('xact_misc') %]
+        <input type="hidden" name="xact_misc" value="[% xact | html %]" />
+        [% END %]
+        [% IF ctx.use_stripe %]
+        <input type="hidden" name="stripe_token" id="stripe_token" />
+        [% END %]
+
+         <table id="billing_info_table" class="table table-hover">
+         <thead>
+          <th colspan='2'><strong>[% l('Billing Information') %]</strong></th>
+         </thead>
+          <tbody>
+               
+               <tr> 
+                <td><label for="payment-first-name">[% l('First Name') %]</label></td>
+                    <td><input type="text"  name="billing_first" id="payment-first-name" 
+                        value="[% ctx.user.first_given_name | html %]" class="form-control"/></td>
+                </tr>
+                <tr>
+                    <td><label for="payment-last-name">[% l('Last Name') %]</label></td>
+                    <td><input type="text" name="billing_last" id="payment-last-name" 
+                        value="[% ctx.user.family_name | html %]" class="form-control"/></td>
+                </tr>
+
+                <tr>
+                    <td><label for="payment-email-addr">[% l('Email Address') %]</label></td>
+                    <td>
+                        
+                        <input id="payment-email-addr" type="text" 
+                             value="[% ctx.user.email | html %]" disabled="disabled" 
+                            readonly="readonly" class="form-control"/>   
+                        <a title="[% l('Update Email Address') %]"
+                            href="[% ctx.opac_root %]/myopac/update_email?return_to_referer=1">[% l("Update") %]</a>
+                    </td
+                </tr>
+                <tr>
+                    <td><label for="payment-billing-address">[% l('Street Address') %]</label></td>
+                    <td><input type="text" name="billing_address" id="payment-billing-address" 
+                        value="[% ctx.user.billing_address.street1 _ ctx.user.billing_address.street2 | html %]" class="form-control"/></td>
+                </tr>
+                <tr>
+                    <td><label for="payment-billing-city">[% l('City' )%]</label></td>
+                    <td><input type="text" name="billing_city" id="payment-billing-city" 
+                        value="[% ctx.user.billing_address.city | html %]" class="form-control"/></td>
+                </tr>
+                <tr>
+                    <td><label for="payment-billing-state">[% l('State or Province') %]</label></td>
+                    <td><input type="text" name="billing_state" id="payment-billing-state"
+                        value="[% ctx.user.billing_address.state | html %]" class="form-control" /></td>
+                </tr>
+                <tr>
+                    <td><label for="paymenet-billing-zip">[% l('Postal Code') %]</label></td>
+                    [% USE zip=String(ctx.user.billing_address.post_code) %]
+                    <td><input type="tel" pattern="[/^[a-zA-Z0-9 _-]{7}$/]*" maxlength="7" size="7" name="billing_zip" id="paymenet-billing-zip"
+                        value="[% zip.truncate(5)  %]" class="form-control"/></td>
+                </tr>
+               </tbody>
+               </table>
+               
+               <table id="credit_card_info_table" class="table">
+               <thead>
+                       <th colspan='2'><strong>[% l('Credit Card Information') %]</strong></th>
+               </thead>
+               <tbody>
+               
+                <tr>
+                    <td><label for="payment-credit-card">[% l('Credit Card #') %]</label></td>
+                    
+                     <!-- Make type tel, which prompts for numbers in mobile -->
+                    <td><input class="form-control" type="tel" pattern="[0-9]*" maxlength="16" id="payment-credit-card" required 
+                    [% IF ctx.use_stripe %]
+                    data-stripe="number"
+                    [% ELSE %]
+                    name="number"
+                    [% END %]
+                    /></td>
+                </tr>
+                <tr>
+                    <td><label for="payment-security-code">[% l('Security Code') %]</label></td>
+                    <td>
+                         <!-- Make type tel, which prompts for numbers in mobile -->
+                        <input class="form-control" type="tel" pattern="[0-9]*" size="4" maxlength="5" id="payment-security-code"
+                        [% IF ctx.use_stripe %]
+                        data-stripe="cvc"
+                        [% ELSE %]
+                        name="cvv2"
+                        [% END %]
+                        /></td>
+                </tr>
+                <tr>
+                    <td><label for="payment-expire-month">[% l('Expiration Month') %]</label></td>
+                    <td>
+                        <select class="form-control" id="payment-expire-month" required
+                        [% IF ctx.use_stripe %]
+                        data-stripe="exp_month"
+                        [% ELSE %]
+                        name="expire_month"
+                        [% END %]
+                        >
+                            <option value="-1"></option>
+                            <option value="01">[% l("January (1)") %]</option>
+                            <option value="02">[% l("February (2)") %]</option>
+                            <option value="03">[% l("March (3)") %]</option>
+                            <option value="04">[% l("April (4)") %]</option>
+                            <option value="05">[% l("May (5)") %]</option>
+                            <option value="06">[% l("June (6)") %]</option>
+                            <option value="07">[% l("July (7)") %]</option>
+                            <option value="08">[% l("August (8)") %]</option>
+                            <option value="09">[% l("September (9)") %]</option>
+                            <option value="10">[% l("October (10)") %]</option>
+                            <option value="11">[% l("November (11)") %]</option>
+                            <option value="12">[% l("December (12)") %]</option>
+                        </select>
+                    </td>
+                </tr>
+                <tr>
+                    <td><label for="payment-expire-year">[% l('Expiration Year') %]</label></td>
+                    <td>
+                        <select class="form-control" id="payment-expire-year"
+                        [%- IF ctx.use_stripe %]
+                        data-stripe="exp_year"
+                        [% ELSE %]
+                        name="expire_year"
+                        [% END -%]
+                        >
+                        [% year = date.format(date.now, '%Y');
+                        y = year;
+                        WHILE y < year + 10; # show ten years starting now %]
+                            <option value="[% y %]">[% y %]</option>
+                        [% y = y + 1; END %]
+                        </select>
+                    </td>
+                </tr>
+                <tr>
+                   <td colspan='2'>
+                       <div id="payment_actions">
+                          [% l('Total amount:') %]
+                           <strong>[% money(ctx.fines.balance_owed) %]</strong><br />
+          
+                         <input type="submit" id="payment_submit" value="[% l('Next') %]" class="btn btn-confirm" />
+                         <a href="[% mkurl(ctx.opac_root _ '/myopac/main', {}, 1) %]" class="btn btn-deny">[% l('Cancel') %]</a> 
+                         <br/>
+                         </div>
+                   </td>
+                </tr>
+                
+          </tbody>
+        </table>
+         [% INCLUDE "opac/parts/myopac/main_refund_policy.tt2" %]
+        
+    [% END %]
+    </form>
+</div>
+[% END %]
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/main_payments.tt2 b/Open-ILS/src/templates/opac-new/myopac/main_payments.tt2
new file mode 100644 (file)
index 0000000..f865e3b
--- /dev/null
@@ -0,0 +1,70 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/main_base.tt2";
+    myopac_page = "main";
+    myopac_main_page = "payments";
+    limit = ctx.payment_history_limit;
+    offset = ctx.payment_history_offset;
+%]
+
+<h3 class="sr-only">[% l('Transaction Payments') %]</h3>
+<div class="my-3">
+    <div class="row my-2">
+        <div class="col-3 text-left"> 
+            <a href='main_payments?limit=[% limit %]&amp;offset=[% offset - limit %]'
+                [% IF offset == 0 %] class='invisible' [% END %]><span class="np_nav_link classic_link btn btn-outline-primary">&#9668;[% l('Previous') %]</span>
+            </a>
+        </div>
+        <div class="col-6">
+            <h2 class="w-50 mx-auto">[% l('Payment History') %]</h2>
+        </div>
+        <div class="col-3 text-right"> 
+            <a href='main_payments?limit=[% limit %]&amp;offset=[% offset + limit %]'
+                [% IF ctx.payments.size < limit %] class='invisible' [% END %]><span class="np_nav_link classic_link btn btn-outline-primary">[% l('Next') %] &#9658;</span>
+            </a>
+        </div>
+    </div>
+    <div class="clear-both"></div>
+
+    [% IF ctx.payments.size %]
+    <table title="[% l('Payments') %]" 
+        class='table table-hover miniTable paymentTable' width='100%'>
+        <thead><tr>
+            <th>[% l('Payment Date') %]</th>
+            <th>[% l('Payment For') %]</th>
+            <th>[% l('Amount') %]</th>
+            <th>[% l('Receipt') %]</th> 
+        </tr></thead>
+        <tbody>
+            [% FOR payment IN ctx.payments %]
+            <tr>
+                <td><span class="sr-only">Payment Date: </span>[% date.format(ctx.parse_datetime(payment.mp.payment_ts), DATE_FORMAT) %]</td>
+                <td>
+                <span class="sr-only">Payment For: </span>
+                [% 
+                    btype = payment.last_billing_type | html;
+                    ptitle = payment.title | html;
+                    (payment.xact_type == 'grocery') ? btype : ptitle
+                %]
+                </td>
+                <td><span class="sr-only">Amount: </span>[% money(payment.mp.amount) %]</td>
+                <td>
+                <span class="sr-only">Receipt Options: </span>
+                    <form action="[% ctx.opac_root %]/myopac/receipt_print" method="post" class="d-inline m-1">
+                        <input class="btn btn-sm btn-confirm my-1" type="hidden" name="payment" value="[% payment.mp.id %]" />
+                        <input class="btn btn-sm btn-confirm my-1" type="submit" value="[% l('Print') %]" />
+                    </form>
+                    <form action="[% ctx.opac_root %]/myopac/receipt_email" method="post" class="d-inline m-1">
+                        <input class="btn btn-sm btn-confirm my-1" type="hidden" name="payment" value="[% payment.mp.id %]" />
+                        <input class="btn btn-sm btn-confirm my-1" type="submit" value="[% l('Email') %]" />
+                    </form>
+                </td>
+            </tr>
+            [% END %]
+        </tbody>
+    </table>
+    [% ELSE %]
+    <div>[% l('You have no historical payments to display.') %]</div>
+    [% END %]
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/messages.tt2 b/Open-ILS/src/templates/opac-new/myopac/messages.tt2
new file mode 100644 (file)
index 0000000..1cecd70
--- /dev/null
@@ -0,0 +1,16 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "messages";
+    limit = (ctx.patron_messages_limit.defined) ? ctx.patron_messages_limit : 20;
+    offset = (ctx.patron_messages_offset.defined) ? ctx.patron_messages_offset : 0;
+    count = (ctx.patron_messages_count.defined) ? ctx.patron_messages_count : 0;
+    display_single = (ctx.display_single_message.defined) ? ctx.display_single_message : 0;
+%]
+<h3 class="sr-only">[% l('My Messages') %]</h3>
+[% IF display_single;
+    PROCESS "opac/myopac/messages/single_message.tt2";
+ELSE;
+    PROCESS "opac/myopac/messages/list.tt2";
+END %]
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/messages/list.tt2 b/Open-ILS/src/templates/opac-new/myopac/messages/list.tt2
new file mode 100644 (file)
index 0000000..9b0dbd8
--- /dev/null
@@ -0,0 +1,128 @@
+<div id="myopac_messages_div">
+    <div class="header_middle">
+        <span id="acct_messages_header" style="float:left;">[% l("Messages") %]</div>
+        <span class='float-left' style='padding-left: 10px;'>
+            <a href='[% mkurl('messages', { limit => limit, offset => (offset - limit)} ) %]'
+                [% IF offset <= 0 %] class='invisible' [% END %]><span class="nav_arrow_fix">&#9668;</span>[% l('Previous') %]</a>
+            [% IF offset > 0 || count > limit;
+                curpage = 0;
+                WHILE curpage * limit < count;
+                    IF curpage * limit == offset;
+            %]
+            [% curpage + 1 %]
+                    [%- ELSE %]
+            <a href='[% mkurl('messages', {limit => limit, offset => (curpage * limit)}) %]'>[% curpage + 1 %]</a>
+                    [%- END;
+                    curpage = curpage + 1;
+                END;
+            END %]
+            <a href='[% mkurl('messages', {limit => limit, offset => (offset + limit)}) %]'
+               [% IF count <= limit + offset %] class='invisible' [% END %] >[% l('Next') %]<span class="nav_arrow_fix">&#9658;</span></a>
+        </span>
+    </div>
+    <div class="clear-both"></div>
+    [% IF ctx.message_update_action.defined %]
+        [% IF ctx.message_update_changed > 0 %]
+        <div class="message-update-summary">
+            [% IF ctx.message_update_action == 'mark_read';
+                l('Marked [_1] message(s) as read.', ctx.message_update_changed);
+               ELSIF ctx.message_update_action == 'mark_unread';
+                l('Marked [_1]  message(s) as unread.', ctx.message_update_changed);
+               ELSIF ctx.message_update_action == 'mark_deleted';
+                l('Deleted [_1] message(s).', ctx.message_update_changed);
+               END
+            %]
+        </div>
+        [% END %]
+        [% IF ctx.message_update_failed > 0 %]
+        <div class="message-update-summary alert">
+            [% IF ctx.message_update_action == 'mark_read';
+                l('Failed to mark [_1] message(s) as read.', ctx.message_update_failed);
+               ELSIF ctx.message_update_action == 'mark_unread';
+                l('Failed to mark [_1]  message(s) as unread.', ctx.message_update_failed);
+               ELSIF ctx.message_update_action == 'mark_unread';
+                l('Failed to delete [_1] message(s).', ctx.message_update_failed);
+               END
+            %]
+        </div>
+        [% END %]
+    [% END %]
+    <div class="clear-both"></div>
+    <div id="messages_main">
+        <form method="post" id="messages-form"
+         onsubmit="if (document.getElementById('acct_messages_actions').value == 'mark_deleted') { return confirm('[% l("Are you sure you wish to permanently delete the selected message(s)?") %]') } else { return true; }">
+            <div class="row">
+                <span class="col-md-11 my-2">
+                    <select class="form-control" name="action" id="acct_messages_actions"
+                        title="[% l('Select your action for the selected messages') %]">
+                        <option id="acct_messages_actions_none" value="">
+                            -- [% l('Actions for selected messages') %] --
+                        </option>
+                        <option value="mark_read">[% l('Mark As Read') %]</option>
+                        <option value="mark_unread">[% l('Mark As Unread') %]</option>
+                        <option value="mark_deleted">[% l('Delete') %]</option>
+                    </select>
+                </span>
+                <span class="col-md-1 my-2">
+                    <input type="submit"
+                        value="[% l('Go') %]"
+                        title="[% l('Go') %]"
+                        class="btn btn-confirm " />
+                </span>
+               <!-- <span style="padding-left:5px;">
+                    <a href="#"><img
+                        alt="[% l('Messages Help') %]"
+                        title="[% l('Actions for messages') %]"
+                        src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]" /></a>
+                </span> -->
+            </div>
+            [% IF count < 1 %]
+            <div class="warning_box">[% l('No messages found.') %]</div>
+            [% ELSE %]
+            <table id="acct_messages_main_header" title="[% l('Messages') %]"
+                class="table table-hover miniTable messagesTable">
+                <thead>
+                <tr>
+                    <th align="center" class="text-center">
+                        <input type="checkbox" title="[% l('Select All Messages') %]"
+                        onclick="var inputs=document.getElementsByTagName('input'); for (i = 0; i < inputs.length; i++) { if (inputs[i].name == 'message_id' &amp;&amp; !inputs[i].disabled) inputs[i].checked = this.checked;}"/>
+                    </th>
+                    <th>[% l('Date') %]</th>
+                    <th>[% l('Library') %]</th>
+                    <th>[% l('Subject') %]</th>
+                    <th></th>
+                </tr>
+                </thead>
+                <tbody>
+                [% FOR message IN ctx.patron_messages; %]
+                    <tr name="acct_message_row"
+                        [% IF !message.is_read %]class="unread-patron-message"[% END %]>
+                        <td align="center">
+                            <span class="sr-only">Message Number [% count; %]</span>
+                        <input type="checkbox" name="message_id" value="[% message.id %]"
+                            [% html_text_attr('title', l('Select message [_1]', message.title)) %]/>
+                        </td>
+                        <td>
+                            <span class="sr-only">Date of Message</span>
+                            [% date.format(ctx.parse_datetime(message.create_date), DATE_FORMAT); %]
+                        </td>
+                        <td>
+                            <span class="sr-only">Library</span>
+                            [% message.library | html %]
+                        </td>
+                        <td>
+                            <span class="sr-only">Title of Message</span>
+                            <a href="[% mkurl('messages', { single => 1, message_id => message.id } ) %]">[% message.title | html %]</a>
+                        </td>
+                        <td>
+                        <span class="sr-only">Message Options</span>
+                            <a href="[% mkurl('messages', { single => 1, message_id => message.id } ) %]" title="Read [% message.title | html %]">Read</a>
+                        </td>
+                    </tr>
+                [% END %]
+                </tbody>
+            </table>
+            [% END %]
+        </form>
+    </div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/myopac/messages/single_message.tt2 b/Open-ILS/src/templates/opac-new/myopac/messages/single_message.tt2
new file mode 100644 (file)
index 0000000..f79f009
--- /dev/null
@@ -0,0 +1,51 @@
+<div id="myopac_messages_div">
+    <div class="header_middle">
+        <span id="acct_messages_header" style="float:left;">[% l("Message") %]</div>
+    </div>
+    <div class="clear-both"></div>
+    <div id="single_message_main">
+        <form method="post" id="messages-form" action="[% ctx.opac_root %]/myopac/messages"  class="my-2">
+            <input type="hidden" name="message_id" value="[% ctx.patron_message_id %]" />
+            <input type="hidden" name="offset" value="[% offset %]" />
+            <input type="hidden" name="limit" value="[% limit %]" />
+            <span>
+                <a href="[% mkurl('messages', {}, ['single', 'message_id']) %]" class="btn btn-confirm m-1">
+                [% l('Return to Message List') %]
+                </a>
+                <button type="submit" name="action" value="mark_deleted" class="btn btn-deny m-1"
+                    onclick="return confirm('[% l("Are you sure you wish to permanently delete this message?") %]')">
+                    [% l('Delete') %]
+                </button>
+                <button type="submit" name="action" value="mark_unread" class="btn btn-opac m-1">
+                    [% l('Mark Unread') %]
+                </button>
+            </span>
+        </form>
+
+        <table title="[% l('Message') %]"
+            class='light_border table'>
+            <tbody id='myopac_message_tbody'>
+            <tr>
+                <td width='30%'
+                    class='color_4 light_border'>[% l("Date") %]</td>
+                <td class='myopac_message_date'>[% date.format(ctx.parse_datetime(ctx.patron_messages.0.create_date), DATE_FORMAT); %]</td>
+            </tr>
+            <tr>
+                <td width='30%'
+                    class='color_4 light_border'>[% l("Library") %]</td>
+                <td class='myopac_message_library'>[% ctx.patron_messages.0.library | html %]</td>
+            </tr>
+            <tr>
+                <td width='30%'
+                    class='color_4 light_border'>[% l("Subject") %]</td>
+                <td class='myopac_message_subject'>[% ctx.patron_messages.0.title | html %]</td>
+            </tr>
+            <tr>
+                <td width='30%'
+                    class='color_4 light_border'>[% l("Message") %]</td>
+                <td class='myopac_message_message'>[% ctx.patron_messages.0.message | html %]</td>
+            </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/myopac/prefs.tt2 b/Open-ILS/src/templates/opac-new/myopac/prefs.tt2
new file mode 100644 (file)
index 0000000..849f836
--- /dev/null
@@ -0,0 +1,258 @@
+[%  PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/myopac/prefs_base.tt2";
+    myopac_page = "prefs";
+    prefs_page = 'prefs' %]
+
+<h3 class="sr-only">[% l('Account Preferences') %]</h3>
+<div id="acct_info_main">
+    <table width='70%' title="[% l('Account preference') %]"
+        class='light_border table table-hover'>
+        <tbody id='myopac_summary_tbody'>
+            <tr>
+                <td class='color_4 light_border'>[% l("Name") %]</td>
+
+                <td width='50%' class='light_border'>[% l(
+                    HUMAN_NAME_FORMAT,
+                    ctx.user.prefix, 
+                    ctx.user.first_given_name,
+                    ctx.user.second_given_name, 
+                    ctx.user.family_name,
+                    ctx.user.suffix
+                ) | html %]</td>
+
+                <td></td>
+            </tr>
+             [% IF ctx.user.pref_first_given_name || ctx.user.pref_second_given_name || ctx.user.pref_family_name %]
+            <tr>
+                <td width='30%'
+                    class='color_4 light_border'>[% l("Preferred Name") %]</td>
+
+                <td class='light_border'>[% l(
+                    HUMAN_NAME_FORMAT,
+                    ctx.user.pref_prefix, 
+                    (ctx.user.pref_first_given_name || ctx.user.first_given_name),
+                    ctx.user.pref_second_given_name, 
+                    (ctx.user.pref_family_name || ctx.user.family_name),
+                    ctx.user.pref_suffix
+                ) | html %]</td>
+
+                <td></td>
+            </tr>
+            [%  END %]
+
+            <tr>
+                <td class='color_4 light_border'>[% l("Day Phone") %]</td>
+                <td class='light_border'>[% ctx.user.day_phone | html %]</td>
+                <td></td>
+            </tr>
+
+            <tr>
+                <td class='color_4 light_border'>[% l("Evening Phone") %]</td>
+                <td class='light_border'>[% ctx.user.evening_phone | html %]</td>
+                <td></td>
+            </tr>
+
+            <tr>
+                <td class='color_4 light_border'>[% l("Other Phone") %]</td>
+                <td class='light_border'>[% ctx.user.other_phone | html %]</td>
+                <td></td>
+            </tr>
+
+            <tr>
+                <td class='color_4 light_border'>
+                    <div style="position:absolute">
+                   </div>[% l("Username") %]
+                </td>
+                <td class='light_border'>[% ctx.user.usrname | html %]</td>
+                [%- IF ctx.username_change_disallowed %]
+                <td></td>
+                [%- ELSE %]
+                <td class='light_border'><a href='update_username'
+                    title="[% l('Change Username') %]">[% l("Change") %]</a></td>
+                [%- END %]
+            </tr>
+            <tr>
+                <td class='color_4 light_border'>[% l("Password") %]</td>
+                <td class='light_border'>[% l("(not shown)") %]</td>
+                <td>
+                [%- IF disable_password_change == 'true' %]
+                <span></span>
+                [%- ELSE %]
+                <span class='light_border'><a href='update_password'
+                    title="[% l('Change Password') %]">[% l("Change") %]</a></span>
+                [%- END %]
+                </td>
+            </tr>
+            <tr>
+                <td class='color_4 light_border'>[% l("Email Address") %]</td>
+                <td class='light_border'>[% ctx.user.email | html %]</td>
+                <td>
+                [%- IF disable_email_change == 'true' %]
+                <span></span>
+                [%- ELSE %]
+                <span class='light_border'><a href='update_email'
+                    title="[% l('Update Email Address') %]">[% l('Change') %]</a></span>
+                [%- END %]
+                </td>
+            </tr>
+
+            [% IF ctx.user.ident_value %]<tr class="hide_me">
+                <td class='color_4 light_border'>
+                [% l("Primary Identification") %]</td>
+
+                <td class='light_border'>
+                    [%
+                    # l('[_1] ([_2])', ctx.user.ident_value, ctx.user.ident_type.name) | html
+                    # XXX uncomment the above line to show primary
+                    # identification. With a minor tweak it could
+                    # alternatively be shown but partially obscured. 
+                    %]
+                </td>
+                <td></td>
+            </tr>[% END %]
+
+            <tr>
+                <td class='color_4 light_border'>[% l("Active Barcode") %]</td>
+                <td class='light_border'>[% ctx.user.card.barcode %]</td>
+                <td></td>
+            </tr>
+
+            <tr>
+                <td class='color_4 light_border'>[% l("Home Library") %]</td>
+                <td class='light_border'>[% ctx.user.home_ou.name | html %]</td>
+                <td></td>
+            </tr>
+            <tr>
+                <td class='color_4 light_border'>[% l("Account Creation Date") %]</td>
+                <td class='light_border'>[% date.format(ctx.parse_datetime(ctx.user.create_date), DATE_FORMAT) %]</td>
+                <td></td>
+            </tr>
+            <tr>
+                <td class='color_4 light_border'>[% l("Account Expiration Date") %]</td>
+                <td class='light_border'>
+                    [%- IF date.format(ctx.parse_datetime(ctx.user.expire_date), '%s') < date.format(date.now , '%s'); -%]
+                        <span class="alert" style="font-weight:bold;">[% date.format(ctx.parse_datetime(ctx.user.expire_date), DATE_FORMAT) %]</span>
+                        </td>
+                    [% ELSE %]
+                        [% date.format(ctx.parse_datetime(ctx.user.expire_date), DATE_FORMAT) %]
+                    [% END %]
+                </td>
+                <td></td>
+            </tr>
+            <tr>
+                <td colspan="3">
+                 [%- INCLUDE "opac/parts/locale_picker.tt2" %]
+                </td>
+            </tr>
+            
+        </tbody>
+    </table>
+    
+    <br />
+    <hr class='opac-auto-013' color="#DCDBDB" />
+    <br />
+    
+       <div class="header_middle mb-3">
+        <span>Addresses</span>
+    </div>
+
+<div class="row">
+            [% 
+                # cycle through the non-pending address.  For each non-pending addr
+                # that has a corresponding pending addr, render the pending addr 
+                # along the right inside it's own edit form
+                pending = {};
+                regular = [];
+                edit_addr_id = CGI.param('edit_address');
+                FOR addr IN ctx.user.addresses;
+                    IF addr.pending == 't';
+                        replaces = addr.replaces;
+                        pending.$replaces = addr;
+                    ELSE;
+                        regular.push(addr);
+                    END;
+                END;
+            FOR addr IN regular;
+                cur_id = addr.id; 
+                pending_addr = pending.$cur_id;
+            %]
+            <div class="col-lg-6 col-12">
+             <table width='100%' id='address_table' class='light_border' title="[% l('Addresses') %]">
+       
+        <tbody>
+            <tr>
+                <!-- pending address form -->
+                [% IF pending_addr OR edit_addr_id == cur_id;
+                    update_addr = pending_addr || addr %]
+                <td>
+                    <form method='post'>
+                        [% IF pending_addr %]
+                            <input type='hidden' name='pending_addr' value='[% pending_addr.id %]'/>
+                        [% ELSE %]
+                            <input type='hidden' name='replace_addr' value='[% edit_addr_id | html %]'/>
+                        [% END %]
+                        <table class='pending-addr table' title="[% l('Pending Addresses') %]">
+                        <tr><td colspan="2"><h5 class="text-center">Pending Address Change</h5></td></tr>
+                        [% addr_fields = [
+                            {label => l("Address Type"), name => 'address_type', value => update_addr.address_type},
+                            {label => l("Street"), name => 'street1', value => update_addr.street1},
+                            {label => l("Street"), name => 'street2', value => update_addr.street2},
+                            {label => l("City"), name => 'city', value => update_addr.city},
+                            {label => l("County"), name => 'county', value => update_addr.county},
+                            {label => l("State"), name => 'state', value => update_addr.state},
+                            {label => l("Country"), name => 'country', value => update_addr.country},
+                            {label => l("ZIP"), name => 'post_code', value => update_addr.post_code},
+                        ];
+                        FOR field IN addr_fields %]
+                        
+                            <tr>
+                               
+                                <td><label for="addr-form-[% field.name %]">[% field.label %]</label></td>
+                                <td><input name="[% field.name %]" 
+                                    id="addr-form-[% field.name %]"
+                                    value="[% field.value | html %]" class="form-control">
+                                </td>
+                            </tr>
+                        [% END %]
+                            <tr>
+                                <td><input type='submit' value="[% l('Save Changes') %]" class="btn btn-confirm btn-sm"/></td>
+                                <td>
+                                    [% params = (pending_addr) ? {delete_pending => pending_addr.id} : {} %]
+                                    <a href="[% mkurl('', params, 1) %]" class="btn btn-deny btn-sm">[% l('Discard Pending Address') %]</a>
+                                </td>
+                            </tr>
+                            <tr><td colspan='2'><i>[% l('This address has a pending change.') %]</i></td></tr>
+                            <tr><td colspan='2'><i>[% l('Address changes will be verified by staff') %]</i></td></tr>
+                        </table>
+                    </form>
+                </td>
+                </tr>
+            </tbody>
+        </table>
+     </div>
+                [% ELSE %]
+                               <div class="col-lg-6 col-12">
+                    <!-- read-only address display -->
+                    <table class='table' title="[% l('Active Addresses') %]">
+                        <tr><td>[% l("Address Type") %]</td><td>[% addr.address_type | html %]</td></tr>
+                        <tr><td>[% l("Street") %]</td><td>[% addr.street1 | html %]</td></tr>
+                        <tr><td>[% l("Street") %]</td><td>[% addr.street2 | html %]</td></tr>
+                        <tr><td>[% l("City") %]</td><td>[% addr.city | html %]</td></tr>
+                        <tr><td>[% l("County") %]</td><td>[% addr.county| html  %]</td></tr>
+                        <tr><td>[% l("State") %]</td><td>[% addr.state | html %]</td></tr>
+                        <tr><td>[% l("Country") %]</td><td>[% addr.country | html %]</td></tr>
+                        <tr><td>[% l("Zip") %]</td><td>[% addr.post_code | html %]</td></tr>
+                        [% IF ctx.get_org_setting(ctx.user.home_ou.id, 'opac.allow_pending_address') 
+                                AND !pending_addr AND edit_addr_id != cur_id %]
+                        <tr><td colspan='2'><a href='?edit_address=[% addr.id %]'>[% l('Edit Address') %]</a></td></tr>
+                        [% END %]
+                    </table>
+                 </div>
+                [% END %] <!-- if pending_addr -->
+            [% END %]
+       
+    </div>
+    </div>
+</div>
+
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/prefs_my_lists.tt2 b/Open-ILS/src/templates/opac-new/myopac/prefs_my_lists.tt2
new file mode 100644 (file)
index 0000000..0458c5d
--- /dev/null
@@ -0,0 +1,67 @@
+[%  PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/myopac/prefs_base.tt2";
+    myopac_page = "prefs";
+    prefs_page = 'prefs_my_lists' %]
+
+
+    <h3 class="sr-only">[% l('List Preferences') %]</h3>
+    <form method='post'>
+
+       <table class="full-width table" id="acct_search_main"
+            title="[% l('List Preferences') %]">
+            <tbody>
+
+                [% IF ctx.updated_user_settings %]
+                <tr><td colspan='2'>
+                    <div class='renew-summary'>
+                        [% l('Account Successfully Updated') %]
+                    </div>
+                </td></tr>
+                [% END %]
+
+                [%- setting = 'opac.lists_per_page' -%]
+                <tr >
+                    <td width='20%'><label for='[% setting %]'>[%l("Lists per page") %]</label></td>
+                    <td>
+                        <select class="form-control mini-control" id='[% setting %]' name='[% setting %]'>
+                            [%  UNLESS ctx.user_setting_map.$setting;
+                                    ctx.user_setting_map.$setting = 10;
+                                END;
+                                FOR val IN [10, 15, 20, 25] %]
+                                <option value='[% val | uri %]' 
+                                    [% IF ctx.user_setting_map.$setting == val %]
+                                        selected='selected'[% END %]>[% val | html %]</option>
+                            [% END %]
+                        </select>
+                       <img alt="[% l('List Items Help') %]"
+                            src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]"
+                            title="[% l('The number of lists displayed per page.') %]"
+                        />
+                    </td>
+                </tr>
+                [%- setting = 'opac.list_items_per_page' -%]
+                <tr >
+                    <td width='20%'><label for='[% setting %]'>[%l("List items per page") %]</label></td>
+                    <td>
+                        <select class="form-control mini-control" id='[% setting %]' name='[% setting %]'>
+                            [%  UNLESS ctx.user_setting_map.$setting;
+                                    ctx.user_setting_map.$setting = 10;
+                                END;
+                                FOR val IN [10, 15, 20, 25, 50] %]
+                                <option value='[% val | uri %]' 
+                                    [% IF ctx.user_setting_map.$setting == val %]
+                                        selected='selected'[% END %]>[% val | html %]</option>
+                            [% END %]
+                        </select>
+                        <img alt="[% l('List Items Help') %]"
+                            src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]"
+                            title="[% l('The number of list items displayed per page when viewing a selected list.') %]"
+                        /> 
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+        <input type="submit" value="[% l('Save') %]" class="btn btn-confirm"/>
+    </form>
+    [% INCLUDE "opac/parts/myopac/prefs_hints.tt2" %]
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/prefs_notify.tt2 b/Open-ILS/src/templates/opac-new/myopac/prefs_notify.tt2
new file mode 100644 (file)
index 0000000..de1dbbb
--- /dev/null
@@ -0,0 +1,106 @@
+[%  PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/myopac/prefs_base.tt2";
+    myopac_page = "prefs";
+    prefs_page = 'prefs_notify' %]
+
+<h3 class="sr-only">[% l('Notification Preferences') %]</h3>
+<form method='post'>
+    [% setting = 'opac.hold_notify' %]
+    <input name='[% setting %]' type="hidden"
+        [% IF ctx.user_setting_map.$setting; %] value='[% ctx.user_setting_map.$setting | html %]' [% END %]/>
+
+    <table class="full-width table" id="acct_search_main" 
+        title="[% l('Notification Preferences') %]">
+        <tbody>
+
+            [% IF ctx.updated_user_settings %]
+            <tr><td colspan='2'>
+                <div class='renew-summary'>
+                    [% l('Account Successfully Updated') %]
+                </div>
+            </td></tr>
+            [% END %]
+
+            [% setting = 'opac.hold_notify' %]
+            <tr>
+                [%# WCAG insists that labels for checkboxes contain the input
+                    or directly follow the input, which would not look right
+                    with the rest of the table.  As an alternative, we can
+                    repeat the label as a title attr.
+                    http://www.w3.org/TR/WCAG20-TECHS/H44.html %]
+                [% email_label = l('Notify by Email by default when a hold is ready for pickup?') %]
+
+                <td><label for='[% setting %].email'>[% email_label %]</label></td>
+                <td>
+                    <input id='[% setting %].email' name='[% setting %].email' 
+                        type="checkbox" title="[% email_label %]"
+                        [% IF (matches = ctx.user_setting_map.$setting.match('email')); %] checked='checked' [% END %]/>
+                </td>
+            </tr>
+            [%- IF allow_phone_notifications == 'true';
+                setting = 'opac.hold_notify'; 
+            -%]
+            <tr>
+                [% phone_label = l('Notify by Phone by default when a hold is ready for pickup?') %]
+                <td><label for='[% setting %].phone'>[% phone_label %]</label></td>
+                <td>
+                    <input id='[% setting %].phone' name='[% setting %].phone' 
+                        type="checkbox" title="[% phone_label %]"
+                        [% IF (matches = ctx.user_setting_map.$setting.match('phone')); %] checked='checked' [% END %]/>
+                </td>
+            </tr>
+            [% setting = 'opac.default_phone' %]
+            <tr>
+                <td><label for='[% setting %]'>[% l('Default Phone Number') %]</label></td>
+                <td>
+                    <input id='[% setting %]' name='[% setting %]' type="text"
+                        [% IF ctx.user_setting_map.$setting; %] value='[% ctx.user_setting_map.$setting | html %]' [% END %] class="form-control"/>
+                </td>
+            </tr>
+            [%- END %]
+            [%- IF ctx.get_org_setting(ctx.search_ou, 'sms.enable') == 1;
+               setting = 'opac.hold_notify';
+            -%]
+            <tr>
+                [% sms_label = l('Notify by Text by default when a hold is ready for pickup?') %]
+                <td><label for='[% setting %].sms'>[% sms_label %]</label></td>
+                <td>
+                    <input id='[% setting %].sms' name='[% setting %].sms' 
+                        type="checkbox" title="[% sms_label %]"
+                        [% IF (matches = ctx.user_setting_map.$setting.match('sms')); %] checked='checked' [% END %]/>
+                </td>
+            </tr>
+            <tr>
+                <td>[% l('Default Mobile Carrier') %]</td>
+                <td>[% INCLUDE "opac/parts/sms_carrier_selector.tt2" sms_carrier_hide_label="true" %]</td>
+            </tr>
+            [% setting = 'opac.default_sms_notify' %]
+            <tr>
+                <td><label for='[% setting %]'>[% l('Default Mobile Number') %]</label></td>
+                <td>
+                    <input id='[% setting %]' name='[% setting %]' type="text"
+                        [% IF ctx.user_setting_map.$setting; %] value='[% ctx.user_setting_map.$setting | html %]' [% END %]/>
+                    [% l('Hint: use the full 10 digits of your phone #, no spaces, no dashes'); %]
+                </td>
+            </tr>
+            [% END %]
+            [% FOR optin IN ctx.opt_in_settings %]
+            <tr>
+                <td><label for='[% optin.cust.name | uri %]'>[% optin.cust.label | html %]</label></td>
+                <td>
+                    <input type='checkbox' name='setting' 
+                        value='[% optin.cust.name | uri %]' 
+                        id='[% optin.cust.name | uri %]' 
+                        title="[% optin.cust.label | html %]"
+                        [% IF optin.value %] checked='checked' [% END %]/>
+                </td>
+            </tr>
+            [% END %]
+        </tbody>
+    </table>
+
+    <input type='submit' value="[% l('Save') %]" class="btn btn-confirm" />
+</form>
+[% END %]
+
+
diff --git a/Open-ILS/src/templates/opac-new/myopac/prefs_settings.tt2 b/Open-ILS/src/templates/opac-new/myopac/prefs_settings.tt2
new file mode 100644 (file)
index 0000000..853c65f
--- /dev/null
@@ -0,0 +1,185 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/org_selector.tt2";
+    WRAPPER "opac/parts/myopac/prefs_base.tt2";
+    myopac_page = "prefs";
+    prefs_page = 'prefs_settings' %]
+
+    <h3 class="sr-only">[% l('Search and History Preferences') %]</h3>
+
+    <form method='post' action="prefs_settings">
+
+       <table class="full-width table" id="acct_search_main"
+            title="[% l('Search and History Preferences') %]">
+            <tbody>
+
+                [% IF ctx.confirm_history_delete %]
+                <tr><td colspan='2'>
+                  <div id='clear-history-confirm' class='renew-summary text-center'>
+                  [% l('Disabling checkout or holds history will permanently remove all items from your history.') %]
+                  <br/>
+                  [% l('Are you sure you wish to continue?') %]
+                  <br>
+                  <a href="[% mkurl() %]" class="btn btn-deny">[% l('Cancel') %]</a>
+                  <input type='hidden' name='history_delete_confirmed' value='1'/>
+                  <input type="submit" value="[% l('Confirm') %]" class="btn btn-confirm"/>
+                  </div>
+                </td></tr>
+                [% ELSIF ctx.updated_user_settings OR ctx.updated_waiver_entries %]
+                <tr><td colspan='2'>
+                    <div class='renew-summary'>
+                        [% l('Account Successfully Updated') %]
+                    </div>
+                </td></tr>
+                [% END %]
+
+                [%- setting = 'opac.hits_per_page' -%]
+                <tr >
+                    <td width='20%'><label for='[% setting %]'>[% l("Search hits per page") %]</label></td>
+                    <td>
+                        <select class="form-control mini-control" id='[% setting %]' name='[% setting %]'>
+                            [%  UNLESS ctx.user_setting_map.$setting;
+                                    ctx.user_setting_map.$setting = 10;
+                                END;
+                                FOR val IN [5, 8, 10, 15, 20, 25, 50] %]
+                                <option value='[% val | uri %]' 
+                                    [% IF ctx.user_setting_map.$setting == val %]
+                                        selected='selected'[% END %]>[% val | html %]</option>
+                            [% END %]
+                        </select>
+                       <a href="#"><img alt="[% l('Search Hits Help') %]" 
+                            src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]"
+                        /></a>
+                    </td>
+                </tr>
+                [%- setting = 'opac.default_search_location'; -%]
+                <tr >
+                    <td width='20%'><label for='[% setting %]'>[% l("Preferred search location") %]</label></td>
+                    <td>
+                        [%- thang = ctx.user.home_ou.id;
+                            IF ctx.user_setting_map.$setting;
+                                thang = ctx.user_setting_map.$setting;
+                            END;
+                            id = setting;
+                            INCLUDE build_org_selector name=setting value=thang;
+                        %]
+                    </td>
+                </tr>
+                [%- setting = 'opac.default_pickup_location'; -%]
+                <tr>
+                    <td width='20%'><label for='[% setting %]'>[% l("Preferred pickup location") %]</label></td>
+                    <td>
+                        [%- thang = ctx.user.home_ou.id;
+                            IF ctx.user_setting_map.$setting;
+                                thang = ctx.user_setting_map.$setting;
+                            END;
+                            id = setting;
+                            INCLUDE build_org_selector name=setting value=thang can_have_vols_only=1;
+                        %]
+                    </td>
+                </tr>
+                [%- setting = 'history.circ.retention_start' -%]
+                <tr>
+                    [% circ_name = l('Keep history of checked out items?') %]
+                    <td><label for='[% setting %]'>[% circ_name%]</label></td>
+                    <td>
+                        <input id='[% setting %]' name='[% setting %]' 
+                            type="checkbox" title="[% circ_name %]"
+                            [% IF ctx.user_setting_map.$setting 
+                              AND !ctx.clear_circ_history; %] checked='checked' [% END %]/>
+                    </td>
+                </tr>
+                [%- setting = 'history.hold.retention_start' -%]
+                <tr>
+                    [% hold_name =  l('Keep history of holds?') %]
+                    <td><label for='[% setting %]'>[% hold_name %]</label></td>
+                    <td>
+                        <input id='[% setting %]' name='[% setting %]' 
+                            type="checkbox" title="[% hold_name %]"
+                            [% IF ctx.user_setting_map.$setting
+                              AND !ctx.clear_hold_history; %] checked='checked' [% END %]/>
+                    </td>
+                </tr>
+                [%- setting = 'opac.temporary_list_no_warn' -%]
+                <tr>
+                    [% skip_warn =  l('Skip warning when adding to temporary book list?') %]
+                    <td><label for='[% setting %]'>[% skip_warn %]</label></td>
+                    <td>
+                        <input id='[% setting %]' name='[% setting %]' 
+                            type="checkbox" title="[% skip_warn %]"
+                            [% IF ctx.user_setting_map.$setting %] checked='checked' [% END %]/>
+                    </td>
+                </tr>
+                [%- setting = 'circ.holds_behind_desk'; IF ctx.behind_desk_supported -%]
+                <tr>
+                    [% behind_desk = l('Pickup holds from behind the desk when possible?') %]
+                    <td><label for='[% setting %]'>[% behind_desk %]</label></td>
+                    <td>
+                        <input id='[% setting %]' name='[% setting %]' 
+                            type="checkbox" title="[% behind_desk %]"
+                            [% IF ctx.user_setting_map.$setting %] checked='checked' [% END %]/>
+                    </td>
+                </tr>
+                [% END %]
+
+  [%- IF ctx.get_org_setting(ctx.user.home_ou.id, 'circ.privacy_waiver'); %]
+                <tr>
+                    <td>[% l('Allow others to use my account') %]</td>
+                    <td>
+                        [% FOR waiver IN ctx.user.waiver_entries %]
+                        <div id="waiver_[% waiver.id %]">
+                            <input type="hidden" name="waiver_id" value="[% waiver.id %]"/>
+                            [% l('Name:') %] <input type="textbox" name="waiver_name_[% waiver.id %]" value="[% waiver.name | html %]"/><br/>
+                            <label>
+                                <input type="checkbox" name="waiver_place_holds" 
+                                    value="[% waiver.id %]" [% waiver.place_holds == 't' ? 'checked="checked"' : '' %]/>
+                                [% l('Place Holds') %]
+                            </label>
+                            <label>
+                                <input type="checkbox" name="waiver_pickup_holds"
+                                    value="[% waiver.id %]" [% waiver.pickup_holds == 't' ? 'checked="checked"' : '' %]/>
+                                [% l('Pick Up Holds') %]
+                            </label>
+                            <label>
+                                <input type="checkbox" name="waiver_checkout_items"
+                                    value="[% waiver.id %]" [% waiver.checkout_items == 't' ? 'checked="checked"' : '' %]/>
+                                [% l('Check Out Items') %]
+                            </label>
+                            <label>
+                                <input type="checkbox" name="waiver_view_history"
+                                    value="[% waiver.id %]" [% waiver.view_history == 't' ? 'checked="checked"' : '' %]/>
+                                [% l('View Borrowing History') %]
+                            </label>
+                        </div>
+                        [% END %]
+                        <br/>
+                        <div>
+                            [% l('Name:') %] <input type="textbox" name="waiver_name_new"/><br/>
+                            <label><input type="checkbox" name="waiver_place_holds" value="new"/>[% l('Place Holds') %]</label>
+                            <label><input type="checkbox" name="waiver_pickup_holds" value="new"/>[% l('Pick Up Holds') %]</label>
+                            <label><input type="checkbox" name="waiver_checkout_items" value="new"/>[% l('Check Out Items') %]</label>
+                            <label><input type="checkbox" name="waiver_view_history" value="new"/>[% l('View Borrowing History') %]</label>
+                        </div>
+                    </td>
+                </tr>
+                [% END %]
+
+                <!--
+                <tr>
+                    <td><label for='prefs_def_font'>[% l("Default Font Size") %]</label></td>
+                    <td>
+                        <select id='prefs_def_font'>
+                            <option value='regular'>[% l("Regular Font") %]</option>
+                            <option value='large'>[% l("Large Font") %]</option>
+                        </select>
+                    </td>
+                </tr>
+                -->
+
+            </tbody>
+        </table>
+        [% IF !ctx.confirm_history_delete %]
+        <input type="submit" value="[% l('Save') %]" class="btn btn-confirm"/>
+        [% END %]
+    </form>
+    [% INCLUDE "opac/parts/myopac/prefs_hints.tt2" %]
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/receipt_email.tt2 b/Open-ILS/src/templates/opac-new/myopac/receipt_email.tt2
new file mode 100644 (file)
index 0000000..15b744f
--- /dev/null
@@ -0,0 +1,21 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "prefs"  %]
+    [% IF ctx.email_receipt_result; # result should be undef on success %]
+    <div class="payment-error">
+        [% l('Error preparing receipt:') %]
+        <span title="[% ctx.email_receipt_result.textcode | html %]">
+            [% ctx.email_receipt_result.desc | html %]
+        </span>
+    </div>
+    [% ELSE %]
+    <div>
+        [% l('Your receipt will be emailed to [_1]', ctx.user.email) | html %]
+    </div>
+    [% END %]
+    <p>
+        [ <a href="[% ctx.opac_root %]/myopac/main_payments">[%
+            l("Back to Payments History") %]</a> ]
+    </p>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/receipt_print.tt2 b/Open-ILS/src/templates/opac-new/myopac/receipt_print.tt2
new file mode 100644 (file)
index 0000000..64237e2
--- /dev/null
@@ -0,0 +1,26 @@
+[%# sic! no wrapper %]
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>[% l('Receipt') %]</title>
+    </head>
+    <body onload="if (document.getElementById('printable-receipt')) window.print();">
+        [% IF ctx.printable_receipt.template_output %]
+        <tt id="printable-receipt">
+            [% ctx.printable_receipt.template_output.data %]
+        </tt>
+        [% ELSE %]
+        <div class="payment-error">
+            [% l(
+                'Error preparing receipt: [_1]',
+                    (ctx.printable_receipt.textcode ? ctx.printable_receipt.textcode _ ' / ' _ ctx.printable_receipt.desc : 0) ||
+                    ctx.printable_receipt.error_output.data ||
+                    l('No receipt data returned from server')
+                ) | html %]
+        </div>
+        [% END %]
+        <hr />
+        <p>[ <a href="[% ctx.opac_root %]/myopac/main_payments">[%
+            l("Back to Payments History") %]</a> ]</p>
+    </body>
+</html>
diff --git a/Open-ILS/src/templates/opac-new/myopac/reservations.tt2 b/Open-ILS/src/templates/opac-new/myopac/reservations.tt2
new file mode 100644 (file)
index 0000000..ff995fc
--- /dev/null
@@ -0,0 +1,39 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "reservations";
+%]
+<h3 class="sr-only">[% l('Reservations') %]</h3>
+[% IF ctx.reservations.size %]
+    <table id="acct_reservations_main_header" class="table_no_border_space table_no_cell_pad item_list_padding">
+        <tr>
+            <th scope="col">[% l('Resource type') %]</th>
+            <th scope="col">[% l('Start time') %]</th>
+            <th scope="col">[% l('End time') %]</th>
+            <th scope="col">[% l('Pickup location') %]</th>
+            <th scope="col">[% l('Status') %]</th>
+        </tr>
+    [% FOREACH r IN ctx.reservations %]
+        <tr>
+            <td>[% r.resource_type_name %]</td>
+            <td>[% date.format(ctx.parse_datetime(r.start_time, r.pickup_lib), DATE_FORMAT _ ' %I:%M %p') %]</td>
+            <td>[% date.format(ctx.parse_datetime(r.end_time, r.pickup_lib), DATE_FORMAT _ ' %I:%M %p') %]</td>
+            <td><a href="../library/[% r.shortname %]">[% r.pickup_name %]</a></td>
+            <td>
+            [% IF r.cancel_time %]
+                [% l('Canceled') %]
+            [% ELSIF r.pickup_time %]
+                [% l('Checked Out') %]
+            [% ELSIF r.capture_time %]
+                [% l('Ready for Pickup') %]
+            [% ELSE %]
+                [% l('Reserved') %]
+            [% END %]
+            </td>
+        </tr>
+    [% END %]
+    </table>
+[% ELSE %]
+    [% l('You have no current reservations') %]
+[% END %]
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/update_email.tt2 b/Open-ILS/src/templates/opac-new/myopac/update_email.tt2
new file mode 100644 (file)
index 0000000..8f3787d
--- /dev/null
@@ -0,0 +1,37 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "prefs"  %]
+<h3 class="sr-only">[% l('Edit Email Address') %]</h3>
+<div id='myopac_summary_div' style="padding:0px;">
+
+    <div class="header_middle">
+       <span class="float-left">[% l('Update Email') %]</span>
+    </div>
+       
+[% IF ctx.invalid_email %]
+    <div id='account-update-email-error'>
+        [% bad_email = ctx.invalid_email | html %]
+        [% l('The email address "[_1]" is invalid.  Please try a different email address.', '<b>' _ bad_email _ '</b>') %]
+    </div>
+
+[% ELSIF ctx.password_incorrect %]
+    <div id='account-update-email-error'>
+        [% |l %]Your current password was not correct.[% END %]
+    </div>
+
+[% END %]
+
+<form method='post' id='account-update-email' autocomplete='off'>
+    [% IF CGI.param("return_to_referer") %]
+    <input type="hidden" name="redirect_to" value="[% ctx.referer | html %]" />
+    [% END %]
+    <table> 
+        <tr><td>[% l('Current Email') %]</td><td>[% ctx.user.email | html %]</td></tr>
+        <tr><td>[% l('Current Password') %]</td><td><input type='password' class="form-control" name='current_pw'/></td></tr>
+        <tr><td>[% l('New Email') %]</td><td><input type='text' name='email' class="form-control" value='[% ctx.invalid_email | html %]'/></td></tr>
+    </table>
+    <input value="[% l('Submit') %]" type='submit' class="btn btn-confirm m-2"/>
+</form>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/update_password.tt2 b/Open-ILS/src/templates/opac-new/myopac/update_password.tt2
new file mode 100644 (file)
index 0000000..1e66141
--- /dev/null
@@ -0,0 +1,40 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "prefs"  %]
+
+<h3 class="sr-only">[% l('Change Password') %]</h3>
+<div id='myopac_summary_div' style="padding:0px;">
+
+    <div class="header_middle">
+        <span class="float-left">[% l('Update Password') %]</span>
+    </div>
+
+[%- INCLUDE "opac/myopac/update_password_msg.tt2" -%]
+
+[% IF ctx.password_invalid %]
+    <div id='account-update-email-error'> <!-- borrow css from update-email page -->
+        [% |l %]New password is invalid.  Please try a different password.[% END %]
+    </div>
+
+[% ELSIF ctx.password_nomatch %]
+    <div id='account-update-email-error'>
+        [% |l %]Passwords do not match.[% END %]
+    </div>
+
+[% ELSIF ctx.password_incorrect %]
+    <div id='account-update-email-error'>
+        [% |l %]Your current password was not correct.[% END %]
+    </div>
+[% END %]
+
+<form method='post' id='account-update-email' autocomplete='new-password' >
+    <table class="table"> 
+        <tr><td>[% l('Current Password') %]</td><td><label><input class="form-control" type='password' name='current_pw' autocomplete="new-password" autocapitalize="off" /></label></td></tr>
+        <tr><td>[% l('New Password') %]</td><td><label><input class="form-control" type='password' name='new_pw' autocomplete='off'/></label></td></tr>
+        <tr><td>[% l('New Password Again') %]</td><td><label><input class="form-control" type='password' name='new_pw2'/></label></td></tr>
+    </table>
+    <input value="[% l('Submit') %]" type='submit' class="btn btn-confirm"/>
+</form>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/myopac/update_password_msg.tt2 b/Open-ILS/src/templates/opac-new/myopac/update_password_msg.tt2
new file mode 100644 (file)
index 0000000..23d5914
--- /dev/null
@@ -0,0 +1,3 @@
+<div class="password_message">
+[% l('Note: The password must be at least 7 characters in length, contain at least one letter (a-z/A-Z), and contain at least one number.'); %]
+</div>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/myopac/update_username.tt2 b/Open-ILS/src/templates/opac-new/myopac/update_username.tt2
new file mode 100644 (file)
index 0000000..6c01af0
--- /dev/null
@@ -0,0 +1,39 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+    myopac_page = "prefs"  %]
+<h3 class="sr-only">[% l('Update Username') %]</h3>
+<div id='myopac_summary_div' style="padding:0px;">
+
+    <div class="header_middle">
+        <span class="float-left">[% l('Update Username') %]</span>
+    </div>
+       
+[% IF ctx.invalid_username %]
+    <div id='account-update-email-error'> <!-- borrow css from update-email page -->
+        [% bad_user = ctx.invalid_username | html %]
+        [% l('"[_1]" is not a valid username.  Usernames cannot have any spaces or look like a barcode, and may be restricted by policy.  Please try a different username.', '<b>' _ bad_user _ '</b>') %]
+    </div>
+
+[% ELSIF ctx.username_exists %]
+    <div id='account-update-email-error'>
+        [% l('The username "[_1]" is taken.  Please try a different username.', '<b>' _ bad_user _ '</b>') %]
+    </div>
+
+[% ELSIF ctx.password_incorrect %]
+    <div id='account-update-email-error'>
+        [% |l %]Your current password was not correct.[% END %]
+    </div>
+
+[% END %]
+
+<form method='post' id='account-update-email' autocomplete='off'> 
+    <table> 
+        <tr><td>[% l('Current Username') %]</td><td>[% ctx.user.usrname | html %]</td></tr>
+        <tr><td>[% l('Current Password') %]</td><td><input type='password' name='current_pw' class="form-control"/></td></tr>
+        <tr><td>[% l('New Username') %]</td><td><input type='text' name='username' value='[% ctx.invalid_username | html %]' class="form-control"/></td></tr>
+        <tr><td colspan='2' align='center'><input value="[% l('Submit') %]" class="btn btn-confirm m-2" type='submit'/></td></tr>
+    </table>
+</form>
+
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/acjs.tt2 b/Open-ILS/src/templates/opac-new/parts/acjs.tt2
new file mode 100644 (file)
index 0000000..9d4eaa2
--- /dev/null
@@ -0,0 +1,76 @@
+
+<script type="text/javascript">
+
+    /* Checks to see if a given type of added content has data to show.
+     * The first arg to callback() is boolean indicating the presence of data */
+    function acIsAvailable(bre_id, type, callback) {
+        var url = '/opac/extras/ac/' + type + '/html/r/' + bre_id;
+        dojo.xhr('HEAD', {
+            url : url,
+            failOk : true, // http://bugs.dojotoolkit.org/ticket/11568
+            error : function(err) { callback(false, bre_id, type); },
+            load : function(result) { callback(true, bre_id, type); }
+        });
+    }
+
+    [%-
+        FOR type IN ctx.added_content.keys;
+            IF ctx.added_content.$type.status == '3' # status unknown %]
+
+                dojo.addOnLoad(function() {
+                    var bre_id = '[% ctx.bre_id %]';
+                    var type = '[% type %]';
+
+                    acIsAvailable(bre_id, type, function(avail, bre_id, type) {
+                        if (avail) {
+
+                            [% IF CGI.param('expand') == 'addedcontent' %]
+
+                                // if the content is available, un-hide the tab
+                                dojo.removeClass(dojo.byId('ac:' + type), 'hidden');
+
+                            [% ELSE %]
+                                // if no default type is selected on the main tab link
+                                // set one here, since we have available content
+
+                                var link = dojo.query('[name=addedcontent]')[0];
+                                var href = link.getAttribute('href');
+                                if (!href.match('[\&;]ac=')) {
+                                    href = href.replace('#addedcontent', ';ac=' + type + '#addedcontent');
+                                    dojo.attr(link, 'href', href);
+                                    dojo.attr(dojo.query('[name=addedcontent_lbl]')[0], 'href', href);
+                                }
+
+                            [% END %]
+                        }
+                    });
+                });
+            [% END; # IF status unknown
+        END; # FOR type
+
+        ident = ctx.record_attrs.isbn_clean || ctx.record_attrs.upc;
+        IF ident;
+    -%]
+
+[%- IF ENV.OILS_NOVELIST_URL -%]
+            /* Load novelist content */
+            setTimeout( function() {
+            novSelect.loadContentForQuery(
+                {
+                    ClientIdentifier : '[% ident %]',
+                    ISBN : '[% ident %]',
+                    version : '2.1'
+                },
+                '[% ENV.OILS_NOVELIST_PROFILE %]',
+                '[% ENV.OILS_NOVELIST_PASSWORD %]',
+                function(d){
+                    // note if d.length == 0, there is no content to display
+                    // hide the Loading... text
+                    dojo.byId('novelist-loading').innerHTML = '';
+                }
+            )}, 100);
+[%- END; # Novelist -%]
+
+        [% END; # IF ident
+    %]
+</script>
diff --git a/Open-ILS/src/templates/opac-new/parts/advanced/expert.tt2 b/Open-ILS/src/templates/opac-new/parts/advanced/expert.tt2
new file mode 100644 (file)
index 0000000..bb88c53
--- /dev/null
@@ -0,0 +1,64 @@
+<form action="[% ctx.opac_root %]/results" method="get">
+    <div class="header_middle">[% l("Expert Search") %]</div>
+    <input type="hidden" name="_special" value="1" />
+    <input id="detail" type="hidden" name="detail_record_view"
+           value="[% show_detail_view %]"/>
+    <table class="expert-search">
+        <tbody id="adv_expert_rows_here">
+            <tr id="adv_expert_row">
+                <td class="py-3">
+                   <div class="row">
+                        <div class="col-2">
+                            <span class="d-inline-block" style="position:absolute;bottom:10px;">
+                                <a href="javascript:;" class="btn btn-remove btn-sm"
+                                title="[% l('Remove row') %]" alt="[% l('Remove row') %]"
+                                onclick='return killRowIfAtLeast(1, this.parentNode.parentNode);'>
+                                <span>[% l('X') %]</span></a>
+                            </span>
+                        </div>
+                        <div class="col-3">
+                        <span class="d-inline-block">
+                            <label for="expert_tag">[% l("Tag:") %]</label>
+                            <input class="form-control" id="expert_tag" type="text" name="tag" size="3" autofocus />
+                        </span>
+                        </div>
+                        <div class="col-3">
+                        <span class="d-inline-block">
+                            <label for="expert_subfield">[% l("Subfield:") %]</label>
+                            <input class="form-control" id="expert_subfield" type="text" name="subfield" size="1" />
+                        </span>
+                        </div>
+                        <div class="col-4">
+                        <span class="d-inline-block">
+                            <label for="expert_term">[% l("Value:") %]</label>
+                            <input class="form-control" id="expert_term" type="text" name="term" size="16" />
+                        </span>
+                        </div>
+                    </div>
+                </td>
+            </tr>
+        </tbody>
+        <tfoot>
+            <tr>
+               <td class="py-3" colspan="7"><a class="btn btn-opac btn-sm" href="javascript:addExpertRow();">[% l("Add Search Row") %]</a></td>
+            </tr>
+            <tr>
+               [%- lib_select_id = "expert_search_library"; -%]
+               <td  class="py-3" colspan="5" class="expert-search-row">
+                   <label for="[% lib_select_id %]"><strong>[% l("Search Library:") %]</strong>
+                       [%- PROCESS "opac/parts/org_selector.tt2";
+                           INCLUDE build_org_selector id=lib_select_id show_loc_groups=1 
+                       %]
+                   </label>
+               </td>
+            </tr>
+            <tr>
+                <td  colspan="2" class="expert-search-row">
+                <input type="submit" value="[% l('Search') %]"
+                    alt="[% l('Search') %]" title="[% l('Search') %]"
+                    class="btn btn-confirm" />
+                </td>
+            </tr>
+        </tfoot>
+    </table>
+</form>
diff --git a/Open-ILS/src/templates/opac-new/parts/advanced/global_row.tt2 b/Open-ILS/src/templates/opac-new/parts/advanced/global_row.tt2
new file mode 100644 (file)
index 0000000..2846ef7
--- /dev/null
@@ -0,0 +1,66 @@
+[%
+    contains_options = [
+        {value => 'contains', label => l('Contains')},
+        {value => 'nocontains', label => l('Does not contain')},
+        {value => 'phrase', label => l('Contains phrase')},
+        {value => 'exact', label => l('Matches exactly')},
+        {value => 'starts', label => l('Starts with')}
+    ];
+    contains = CGI.param('contains');
+    queries = CGI.param('query');
+    bools = CGI.param('bool');
+    qtypes = CGI.param('qtype');
+    rowcount = 3;
+
+    # scalar.merge treats the scalar as a 1-item array
+    WHILE queries.size < rowcount; queries = queries.merge(['']); END;
+    WHILE bools.size < rowcount; bools = bools.merge(['and']); END;
+    WHILE qtypes.size < rowcount; qtypes = qtypes.merge(search.default_qtypes.${qtypes.size} ? [search.default_qtypes.${qtypes.size}] : ['keyword']); END;
+
+    FOR qtype IN qtypes;
+        c = contains.shift;
+        b = bools.shift;
+        q = queries.shift; %]
+
+<!-- tag the second row so the bool column won't be hidden -->
+<tr[% IF loop.index == 1 %] id="adv_global_row"[% END %]>
+
+    <td class="td-left py-3">
+               <div class="row">
+            <div class="col-2">
+            <!-- bool selector.  hide for first row.  safe to ignore first bool value in form submission -->
+                <select title="[% l('Boolean search operator') %]" 
+                name='bool' [% IF loop.first %] class='d-none' [% ELSE %] class='form-control' [% END %]>
+                    <option value='and' [% b == 'and' ? 'selected="selected"' : '' %]>[% l('And') %]</option>
+                    <option value='or' [% b == 'or' ? 'selected="selected"' : '' %]>[% l('Or') %]</option>
+                </select>
+            </div>
+            <div class="col-3">
+                <!-- keyword, subject, etc. selector -->
+                <span>
+                    [% INCLUDE "opac/parts/qtype_selector.tt2"
+                        query_type=qtype %]
+                </span>
+            </div>
+            <div class="col-3">
+                <select class='form-control' title="[% l('Search phrase match strictness') %]"
+                name='contains'>
+                [% FOR o IN contains_options; -%]
+                <option value="[% o.value %]" [% c == o.value ? ' selected="selected"' : '' %]>[% o.label %]</option>
+                [% END %]
+                </select>
+            </div>
+            <div class="col-4">
+                <input class="form-control" title="[% l('Search term') %]" aria-label="[% l('Search term') %]"
+                type='text' size='18' name='query' value="[% q | html %]" x-webkit-speech [% IF loop.index == 0 %] autofocus [% END %] />
+            </div>
+        </div>
+    </td>
+    <td class="px-2">
+        <a href="javascript:;" class="btn btn-remove btn-sm"
+            title="[% l('Remove row') %]" alt="[% l('Remove row') %]"
+            onclick='return killRowIfAtLeast(2, this);'>
+                <span>[% l('X') %]</span>
+            </a></td>
+</tr>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/advanced/numeric.tt2 b/Open-ILS/src/templates/opac-new/parts/advanced/numeric.tt2
new file mode 100644 (file)
index 0000000..3fe91fe
--- /dev/null
@@ -0,0 +1,38 @@
+<form action="[% ctx.opac_root %]/results" method="get">
+    <div class="header_middle">[% l("Numeric Search") %]</div>
+    <input type="hidden" name="contains" value="contains" />
+    <input type="hidden" name="_special" value="1" />
+    <input id="detail" type="hidden" name="detail_record_view"
+           value="[% show_detail_view %]"/>
+    <div id='adv_numeric_block' class='row'>
+       <div class="col-md-6 col-xs-12 my-3">
+            <label for="numeric_qtype"><strong>[% l("Field:") %]</strong></label>
+                <select id="numeric_qtype" name="qtype" class="form-control">
+                    <!-- TODO: Pull labels from config.metabib_field.label -->
+                    <option value="identifier|isbn">[% l('ISBN') %]</option>
+                     <option value="identifier|upc">[% l('UPC') %]</option>
+                    <option value="identifier|issn">[% l('ISSN') %]</option>
+                    <option value="cnbrowse">[% l('Call Number (Shelf Browse)') %]</option>
+                    <option value="identifier|lccn">[% l('LCCN') %]</option>
+                    <option value="identifier|tcn">[% l('TCN') %]</option>
+                    <option value="item_barcode">[% l('Item Barcode') %]</option>
+                </select>
+               </div>
+        <div class="col-md-6 col-xs-12 my-3">
+                       <label for="numeric_qtype"><strong>[% l("Identifier:") %]</strong></label>
+                <input class="form-control" type="text" name="query" size="16" autofocus placeholder='[% l("Identifier") %]' aria-label='[% l("Identifier") %]' />
+        </div>
+           <br/>
+        <div class="col-12 my-3 text-center">
+            [%- lib_select_id="numeric_search_library" -%]
+            <label for="[% lib_select_id %]"><strong>[% l("Search Library:") %]</strong>
+                  [% PROCESS "opac/parts/org_selector.tt2";
+                     INCLUDE build_org_selector id=lib_select_id show_loc_groups=1
+                  %]
+            </label>
+           <br/>
+                <input type="submit" alt="[% l('Search') %]"
+                    value="[% l('Search') %]" class="btn btn-confirm mt-3" />
+          </div>
+    </div>
+</form>
diff --git a/Open-ILS/src/templates/opac-new/parts/advanced/search.tt2 b/Open-ILS/src/templates/opac-new/parts/advanced/search.tt2
new file mode 100644 (file)
index 0000000..a1d161c
--- /dev/null
@@ -0,0 +1,174 @@
+[%
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS get_library;
+%]
+<form action="[% ctx.opac_root %]/results" method="get">
+<div id='adv_global_search' class='data_grid data_grid_center'>
+    <div id='adv_search_rows' class="container mb-3">
+        <!-- Contains the user-adda
+        ble(?) rows to define search class, containment and text -->
+         <div class="header_middle">[% l('Search Input') %]</div>
+               <div valign='top' class='adv_global_input_container row'>
+            <table role="presentation" class="mx-auto mb-5 col-lg-8">
+                <tbody id="adv_global_tbody">
+                    [% INCLUDE "opac/parts/advanced/global_row.tt2" %]
+                    <!-- add a new row -->
+                    <tr id='adv_global_addrow' >
+                        <td class="td-search-left" colspan="2">
+                            <a class="btn btn-opac btn-sm" href="javascript:;" id="myopac_new_global_row" onclick='addSearchRow();'>[% l('Add Search Row') %]</a>
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+         <div id='adv_search_submit' class="text-center">
+        <input type="hidden" name="_adv" value="1" />
+        <input id="detail" type="hidden" name="detail_record_view"
+               value="[% show_detail_view %]"/>
+        <span>
+            <input id='search-submit-go' type="submit" value="[% l('Search') %]" title="[% l('Search') %]" class="btn btn-confirm"
+                onclick='setTimeout(function(){$("search-submit-spinner").className=""; $("search-submit-go").className="hidden"}, 2000)'/>
+            <img id='search-submit-spinner' src='/opac/images/progressbar_green.gif[% ctx.cache_key %]'
+                class='hidden' alt="[% l('Search in progress icon') %]"/>
+        </span>
+
+        <a href="[% mkurl(ctx.opac_root _ '/advanced', {$loc_name => loc_value}, 1) %]"
+            class="btn btn-deny">[% l('Clear Form') %]</a>
+    </div>
+    </div>
+
+   
+
+    <div class="container">
+        <div class="header_middle">[% l('Search Filters') %]</div>
+<div class='row'>
+[% FOR adv_chunk IN search.adv_config; NEXT IF adv_chunk.adv_hide || (adv_chunk.adv_special && adv_chunk.adv_special != 'copy_location'); %]
+            <div class='col-xl-3 col-sm-6 col-md-4 col-xs-12 pt-3'>
+            <div valign='top'[% IF adv_chunk.js_only %]
+                id='adv_chunk_[% adv_chunk.adv_special %]'
+                class='hidden  mx-auto' [%ELSE%] class='mx-auto' [% END %]>
+                <strong><label for="[% adv_chunk.id %]">
+                  [% adv_chunk.adv_label %] </label></strong><br />
+                  
+[% IF adv_chunk.adv_attr;
+            INCLUDE "opac/parts/coded_value_selector.tt2"
+                id=adv_chunk.id
+                attr=adv_chunk.adv_attr multiple="multiple"
+                size=adv_chunk.adv_size || search.default_adv_select_height || "4";
+ ELSIF adv_chunk.adv_filter;
+            INCLUDE "opac/parts/filter_group_selector.tt2"
+                id=adv_chunk.id
+                filter_group=adv_chunk.adv_filter multiple="multiple"
+                size=adv_chunk.adv_size || search.default_adv_select_height || "4"; %]
+         [% END; %]       
+[% IF adv_chunk.adv_special == 'copy_location' %]
+<select id="adv_copy_location_selector"
+                        title="[% l('Select Shelving Location') %]"
+                        name="fi:locations" size="4" multiple="multiple" class="form-control">
+                    </select>
+         [%END;%]</div></div>[%END;%]</div>       
+         
+         
+         
+<!--Bottom Content (Library/Pub Year/Sort and Limit/Format Checkboxes -->
+                <div class="row">
+[% FOR adv_chunk IN search.adv_config; NEXT IF adv_chunk.adv_hide || adv_chunk.adv_special == 'copy_location';%]                  
+[%     IF adv_chunk.adv_special;%]
+ <div class='col-md-4 col-sm-6 col-xs-12 pt-3'>
+            <div valign='top'[% IF adv_chunk.js_only %]
+                id='adv_chunk_[% adv_chunk.adv_special %]'
+                class='hidden mx-auto' [%ELSE%] class='mx-auto' [% END %]>
+                <strong><label for="[% adv_chunk.id %]">
+                  [% adv_chunk.adv_label %] </label></strong><br />
+                  
+           [% IF adv_chunk.adv_special == "lib_selector";
+                    PROCESS "opac/parts/org_selector.tt2";
+                        INCLUDE build_org_selector show_loc_groups=1 id=adv_chunk.id %]
+                        
+                          
+                            
+[% ELSIF adv_chunk.adv_special == "pub_year"; %]
+                            <select name='pubdate' class='form-control'
+                                id="[% adv_chunk.id %]"
+                                onchange='if(this.selectedIndex == 3)
+                                    unHideMe($("adv_global_pub_date_2_span"));
+                                else
+                                    hideMe($("adv_global_pub_date_2_span"));'>
+                                    
+                                    [%  FOR opt IN [
+                                            {"code" => "is", "label" => l("Is")},
+                                            {"code" => "before", "label" => l("Before")},
+                                            {"code" => "after", "label" => l("After")},
+                                            {"code" => "between", "label" => l("Between")} ] %]
+                                        <option value="[% opt.code %]"[% CGI.param('pubdate') == opt.code ? ' selected="selected"' : '' %]>[% opt.label | html %]</option>
+                                    [%  END %]
+                                    
+                            </select>
+                            
+                            [% IF !ctx.exclude_electronic_checkbox %]
+                            <br/>
+                            <div id='adv_special_block' class="text-center">
+                            [% END %]
+                                <input title="[% l('Search date') %]" class='form-control' placeholder='Year'
+                                  name='date1' type='text' size='4' maxlength='4' value="[% CGI.param('date1') | html %]" />
+                                <span id='adv_global_pub_date_2_span' class='[% CGI.param("pubdate") == "between" ? "" : "hide_me" %]'>
+                                   [% l("AND") %] <input name='date2' class='form-control' placeholder='Year'
+                                    title="[% l('Secondary search date') %]" type='text' size='4' maxlength='4' value="[% CGI.param('date2') | html %]" />
+                                </span>
+                            [% IF !ctx.exclude_electronic_checkbox %]
+                            </div>
+                            [% END %]
+                            [% IF ctx.exclude_electronic_checkbox %]
+                            <div class="adv_search_available">
+                                <input type='checkbox' name="fi:-search_format" value="electronic"
+                                [%- CGI.param('fi:-search_format').grep('electronic').size ?
+                                    ' checked="checked"' : '' %]
+                                id='opac.result.excludeelec' />
+                                <label for='opac.result.excludeelec'>
+                                    [% l("Exclude Electronic Resources") %]
+                                </label>
+                            </div>
+                            [% END %]
+                            
+[% ELSIF adv_chunk.adv_special == "sort_selector"; default_sort=ctx.default_sort;
+                    IF CGI.param('sort');
+                        default_sort=CGI.param('sort');
+                    END;
+                    INCLUDE "opac/parts/filtersort.tt2"
+                        id=adv_chunk.id
+                        value=default_sort class='results_header_sel form-control'; %]
+
+<div class="mt-5">
+                    [% IF NOT metarecords.disabled %]
+                        <br/><!-- <br> may seem redundant, but it allows the
+                            <input> (below) to drop down inline w/ its label -->
+                        <div>
+                            <input type='checkbox' name="modifier" value="metabib"
+                              [%- CGI.param('modifier').grep('metabib').size || search.metarecord_default ?
+                                    ' checked="checked"' : '' %]
+                                id='opac.result.ismetabib' />
+                            <label for='opac.result.ismetabib'>
+                                [% l("Group Formats and Editions") %]</label>
+                        </div>
+                    [% END %]
+                      <div>
+                                <input type='checkbox' name="modifier"
+                                    value="available"[% CGI.param('modifier').grep('available').size ? ' checked="checked"' : '' %]
+                                    id='opac.result.limit2avail' />
+                                <label for='opac.result.limit2avail'>
+                                    [% l("Limit to Available") %]</label>
+                            </div>
+</div>
+                [% END; %]
+         </div></div>
+     [%   END; %]
+            
+        
+
+            
+[% END; %]
+</div>
+        </div>
+    </div>
+</div>
+</form>
diff --git a/Open-ILS/src/templates/opac-new/parts/anon_list.tt2 b/Open-ILS/src/templates/opac-new/parts/anon_list.tt2
new file mode 100644 (file)
index 0000000..27172ae
--- /dev/null
@@ -0,0 +1,114 @@
+        [% IF ctx.mylist.size %]
+        <div>
+        <p class="big-strong">[% l('Basket') %]</p>
+        <div class="sort my-3">
+            <form method="get">
+            <div class="input-group">
+                <label for="anonsort">[% l("Sort basket items by: ") %]</label>
+                [% INCLUDE "opac/parts/filtersort.tt2" mode='bookbag'
+                    id="anonsort" name="anonsort" value=CGI.param("anonsort") class="form-control form-control-sm"%]
+                <input type="hidden" name="id"
+                    value="[% CGI.param('id') | html %]" />
+                [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+                <input class="btn btn-confirm btn-sm" type="submit" value="[% l('Sort') %]" />
+            </div>
+            </form>
+        </div>
+        <form action="[% mkurl(ctx.opac_root _ '/mylist/move') %]" method="post">
+        <input type="hidden" name="orig_referrer" value="[% CGI.referer | html %]" />
+        <input type="hidden" name="redirect_to" value="[% mkurl('', {}, ['list_none_selected', 'cart_none_selected']) %]" />
+        <div class="bbag-action" style="clear:both;">
+        <div class="input-group my-3">
+            <select class="form-control form-control-sm" name="action" aria-label="[% l('Actions for these items') %]">
+                <option>[% l('-- Actions for these items --') %]</option>
+                <option value="place_hold">[% l('Place hold') %]</option>
+                <option value="print">[% l('Print title details') %]</option>
+                <option value="email">[% l('Email title details') %]</option>
+                <option value="delete">[% l('Remove from basket') %]</option>
+                <option value="new_list">[% l('Add to new list') %]</option>
+                [% IF ctx.user AND ctx.bookbags.size %]
+                    <optgroup label="[% l('Move selected items to list:') %]">
+                    [% FOR bbag IN ctx.bookbags %]]
+                        <option value="[% bbag.id %]" class="selector_actions_for_list_inner_option">[% bbag.name | html %]</option>
+                    [% END %]
+                    </optgroup>
+                [% END %]
+            </select>
+            [%- INCLUDE "opac/parts/preserve_params.tt2"; %]
+            <input class="btn btn-confirm btn-sm" type="submit" value="[% l('Go') %]" />
+        </div>
+
+
+        
+        <div class="input-group">
+           <label for="clear_cart"><input class="form-control form-control-sm" type="checkbox" name="clear_cart" id="clear_cart" />[% l('Clear entire basket when action complete') %]</label>
+        </div>
+            [% IF CGI.param('cart_none_selected') %]
+                <span class="error">[% l('No items were selected') %]</span>
+            [% END %]
+        </div>
+        <div class="bbag-content">
+
+
+
+            <table class="container-fluid table table-hover mt-4 miniTable bucketTable">
+                <thead id="acct_list_header_anon">
+                    <tr>
+                        <th class='list_checkbox'>
+                            <input type="checkbox" checked="checked" 
+                            aria-label="[% l('Select all records') %]" onclick="
+                                var inputs=document.getElementsByTagName('input'); 
+                                for (i = 0; i < inputs.length; i++) { 
+                                    if (inputs[i].name == 'record' && !inputs[i].disabled) inputs[i].checked = this.checked;}"/>
+                        </th>
+                        <th><a href="[% mkurl('', {anonsort=>(CGI.param('anonsort') == 'titlesort' ? 'titlesort.descending' : 'titlesort')}) %]">[% l('Title') %]</a></th>
+                        <th><a href="[% mkurl('', {anonsort=>(CGI.param('anonsort') == 'authorsort' ? 'authorsort.descending' : 'authorsort')}) %]">[% l('Author(s)') %]</a></th>
+                        <th>[% l('Local Call Number') %]</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    [% FOR item IN ctx.mylist;
+                        attrs = {marc_xml => ctx.mylist_marc_xml.$item};
+                        PROCESS get_marc_attrs args=attrs %]
+                    <tr>
+                        <td class="list_checkbox">
+                            <input type="checkbox" checked="checked" name="record" value="[% item %]" aria-label="[% l('Select record') %]" />
+                        </td>
+                        <td class="list_entry" data-label="[% l('Title') %]"><a href="[% mkurl(ctx.opac_root _ '/record/' _ item, {}, ['edit_notes', 'id']) %]">[% attrs.title | html %]</a></td>
+                        <td class="list_entry" data-label="[% l('Author(s)') %]"><a href="[%-
+                            authorquery = attrs.author | replace('[,\.:;]', '');
+                            mkurl(
+                                ctx.opac_root _ '/results',
+                                {qtype => 'author', query => authorquery},
+                                ['page', 'id', 'edit_notes']
+                            )
+                        -%]">[% attrs.author | html %]</a></td>
+                        <td class="list_entry" data-label="[% l('Local Call Number') %]">
+                        [% 
+                            copy = attrs.holdings.0;
+                            IF copy;
+                                copy_org = ctx.get_aou_by_shortname(copy.owner);
+                                FOR ctx_org IN [ctx.pref_ou, ctx.search_ou, ctx.home_ou, ctx.physical_loc];
+                                    NEXT UNLESS ctx_org;
+                                    ctx_org = ctx.get_aou(ctx_org);
+                                    IF ctx.org_within_scope(ctx_org, copy_org, ctx_org.ou_type.depth);
+                                        l('[_1] ([_2])', copy.label, copy_org.name) | html;
+                                        LAST;
+                                    END;
+                                END;
+                            END;
+                        %]
+                        </td>
+                    </tr>
+                    [% END %]
+                </tbody>
+            </table>
+
+
+
+
+            <br /><br />
+        </div>
+        </form>
+        </div>
+        [% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/base.tt2 b/Open-ILS/src/templates/opac-new/parts/base.tt2
new file mode 100644 (file)
index 0000000..b492b80
--- /dev/null
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html lang='[% ctx.eg_locale %]'>
+    <head prefix="og: http://ogp.me/ns#">
+        <meta charset="utf-8" />
+        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+        [% IF ctx.refresh %]
+        <meta http-equiv="refresh" content="[% ctx.refresh %]" />
+        [% ELSIF ctx.authtime AND !ctx.is_staff %]
+        <meta http-equiv="refresh" content="[% ctx.authtime %]; url=[% ctx.home_page %]" />
+        [% END %]
+        <meta name = "viewport" content = "initial-scale = 1.0">
+        <!--Added bootstrap dependancies-->
+        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
+               <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
+               <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
+               <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
+        <link rel="stylesheet" type="text/css" href="[% ctx.media_prefix %]/css/skin/default/opac/semiauto.css[% ctx.cache_key %]" />
+        <link rel="stylesheet" type="text/css" href="[% ctx.opac_root %]/css/style.css[% ctx.cache_key %]&amp;dir=[%
+          IF ctx.get_i18n_l(ctx.eg_locale).rtl == 't' %]rtl[%
+          ELSE %]ltr[% END %]" />
+        [% IF ctx.get_i18n_l(ctx.eg_locale).rtl == 't' %]
+        <link rel="stylesheet" type="text/css" href="[% ctx.media_prefix %]/css/skin/default/opac/semiauto-rtl.css[% ctx.cache_key %]" />
+        [% END %]
+        
+           [% IF ctx.get_org_setting(ctx.search_ou, 'opac.patron.custom_css') %]
+            [% custom_css = ctx.get_org_setting(ctx.search_ou, 'opac.patron.custom_css') %]
+        [% '<style>'; custom_css ; '</style>' %]                                          
+        [% END %]
+
+        [%- libname = ctx.get_aou(ctx.search_ou).name;
+            libname = libname | html;
+            libsname = ctx.get_aou(ctx.search_ou).shortname; %]
+        <title>[%- IF ctx.user_stats.messages.unread;
+                      l('([_1])', ctx.user_stats.messages.unread);
+                   END;
+               -%]
+               [% l('[_1] - [_2]', ctx.page_title, libname) %]</title>
+        <link rel="unapi-server" type="application/xml" title="unAPI" href="/opac/extras/unapi" />
+        <link type="application/opensearchdescription+xml" rel='search' title="[% l('[_1] OpenSearch', libname) %]" href="/opac/extras/opensearch/1.1/[% libsname | uri %]/-/osd.xml" />
+        [%-# Hook for page-specific metadata such as <meta name="robots" content="noindex"> %]
+        [%- ctx.metalinks.push('<meta property="og:title" content="' _ ctx.page_title _ '" />'); %]
+        [%- ctx.metalinks.push('<meta property="og:site_name" content="' _ libname _ '" />'); %]
+        [% FOREACH meta IN ctx.metalinks; meta _ "\n"; END; -%]
+        [% IF want_dojo %]
+        <style type="text/css">
+            @import "[% ctx.media_prefix %]/js/dojo/dijit/themes/tundra/tundra.css[% ctx.cache_key %]";
+        </style>
+        [% END %]
+               
+        [% IF !ctx.is_staff %]
+            [% INCLUDE 'opac/parts/goog_analytics.tt2' %]
+        [% END %]
+        [% PROCESS 'opac/parts/stripe.tt2' %]
+    </head>
+    <body[% IF want_dojo; ' class="tundra"'; END %]>
+        <h1 class="sr-only">[% l('Catalog') %]</h1>
+        [%#Each content page needs (at minimum) an <h2> describing the content%]
+        <div style="min-height:100%;">
+        [% content %]
+        </div>
+        [% INCLUDE 'opac/parts/login/login_modal.tt2' %]
+        <h2 class="sr-only">[% l('Additional Resources') %]</h2>
+        [% INCLUDE 'opac/parts/footer.tt2' %]
+        [% INCLUDE 'opac/parts/js.tt2' %]
+        [%- IF ENV.OILS_CHILIFRESH_ACCOUNT %]
+            [%- INCLUDE 'opac/parts/chilifresh.tt2' %]
+        [%- END %]
+    </body>
+</html>
diff --git a/Open-ILS/src/templates/opac-new/parts/bookbag_actions.tt2 b/Open-ILS/src/templates/opac-new/parts/bookbag_actions.tt2
new file mode 100644 (file)
index 0000000..b14f7ee
--- /dev/null
@@ -0,0 +1,88 @@
+[%
+    dsetting = "opac.default_list";
+    tclass = "temporary divider";
+
+    # Wrap a url to open in a new tab in staff client.
+    MACRO opac_wrap(url) BLOCK;
+         IF ctx.is_staff AND NOT ctx.is_browser_staff;
+              # void(0) to return false and not go to new page in current tab.
+              "javascript:xulG.new_tab(xulG.urls.XUL_OPAC_WRAPPER, {}, {'opac_url' : 'oils://remote" _ url _ "'});void(0);";
+          ELSE;
+              url;
+          END;
+    END;
+
+    IF rec.id;
+        # Results table context
+        ctx.bre_id = rec.id;
+        stop_parms = 1;
+        rec_anchor = 'record_' _ ctx.bre_id;
+    ELSE;
+        # Record details context
+        rec_anchor => ctx.bre_id;
+    END;
+
+    record_title = attrs.title; # same for either context
+
+    IF ctx.user_setting_map.defined(dsetting);
+        default_list = ctx.user_setting_map.$dsetting;
+    END;
+
+    IF (!default_list) && ctx.bookbags.size;
+        tclass = "temporary";
+    END;
+
+    href = opac_wrap(mkurl(ctx.opac_root _ '/mylist/add', {
+        record => ctx.bre_id, anchor => rec_anchor
+    }, stop_parms));
+%]
+
+ <button id="btnGroupDrop1" type="button" class="btn btn-action" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><a href="#" class="no-dec">
+          <img src="[% ctx.media_prefix %]/images/clipboard.png[% ctx.cache_key %]"
+              [% html_text_attr('alt', 
+                l('Add item to a list: [_1]', record_title)) %]/>
+          [% l("Add to my list") %]
+        </a></button>
+    <!--Removal-->    
+    <div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
+    <a class="dropdown-item" href="[% href %]">[% l('Temporary List') %]</a>
+    
+    [% IF default_list;
+       label = (ctx.default_bookbag) ? ctx.default_bookbag : l('Default List');
+       class = (ctx.bookbags.size) ? "default divider" : "default";
+       href = opac_wrap(mkurl(ctx.opac_root _ '/myopac/list/update', {
+            action => 'add_rec', list => default_list,
+            record => ctx.bre_id, anchor => rec_anchor
+        }, stop_parms));
+    %]
+    <a href="[% href %]" class="dropdown-item">[% label %]</a>
+    [% END %]
+    [% IF ctx.bookbags.size;
+       i = 0;
+       FOREACH bag IN ctx.bookbags;
+           href = opac_wrap(mkurl(ctx.opac_root _ '/myopac/list/update', {
+                action => 'add_rec', list => bag.id, record => ctx.bre_id,
+                anchor => rec_anchor
+           }, stop_parms));
+           i = i + 1;
+           IF i == ctx.bookbags.size;
+    %]
+    <div class="dropdown-divider"></div>
+        [%- END -%]
+    <a class="dropdown-item" href="[% href %]">[% bag.name %]</a>
+    [%
+       END;
+       END
+    %]
+        <a class="dropdown-item" href="[% opac_wrap(mkurl(ctx.opac_root _ '/myopac/lists', {
+            limit => ctx.bookbag_count, add_rec => ctx.bre_id,
+            anchor => rec_anchor })) %]"
+           title="[% l('See all of my lists') %]">
+        [% l('See All') %]
+        </a>
+    <a class="dropdown-item" href="[% opac_wrap(mkurl(ctx.opac_root _ '/myopac/lists', {
+        add_rec => ctx.bre_id, anchor => rec_anchor
+    }, stop_parms)) %]">
+    [% l('Add to new list') %]
+    </a>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/cart.tt2 b/Open-ILS/src/templates/opac-new/parts/cart.tt2
new file mode 100644 (file)
index 0000000..8451413
--- /dev/null
@@ -0,0 +1,30 @@
+<div id="record_basket">
+  <div id="basket_actions">
+    <select id="select_basket_action" aria-label="[% l('Select basket action') %]">
+      <option value="">[% l('-- Basket Actions --') %]</option>
+      <option value="[% mkurl(ctx.opac_root _ '/mylist', {}) %]">[% l('View Basket') %]</option>
+      <option value="[% mkurl(ctx.opac_root _ '/mylist/move', { action => 'place_hold', entire_list => 1 }) %]">[% l('Place Holds') %]</option>
+      <option value="[% mkurl(ctx.opac_root _ '/mylist/print', {}) %]">[% l('Print Title Details') %]</option>
+      <option value="[% mkurl(ctx.opac_root _ '/mylist/email', {}) %]">[% l('Email Title Details') %]</option>
+      [% IF !ctx.is_browser_staff %]
+      <option value="[% mkurl(ctx.opac_root _ '/myopac/lists', { move_cart_by_default => 1, from_basket => 1 }) %]">[% l('Add Basket to Saved List') %]</option>
+      [% END %]
+      [% IF ctx.is_browser_staff %]
+      <option value="add_cart_to_bucket">[% l('Add Basket to Bucket') %]</option>
+      [% END %]
+      <option value="[% mkurl(ctx.opac_root _ '/mylist/clear', {}) %]">[% l('Clear Basket') %]</option>
+    </select>
+    <input class="opac-button" type="button" id="do_basket_action" value="[% l('Go') %]" />
+  </div>
+  <div id="record_basket_icon">
+     <a href="[% mkurl(ctx.opac_root _ '/mylist') %]" class="no-dec" rel="nofollow" vocab="">
+       <img src="[% ctx.media_prefix %]/images/cart-sm.png[% ctx.cache_key %]" alt="[% l('View Basket') %]">
+     </a>
+     <div id="record_basket_count_floater">
+       <a href="[% mkurl(ctx.opac_root _ '/mylist') %]" class="no-dec" rel="nofollow" vocab="">
+         <span id="record_basket_count">[% ctx.mylist.size %]</span>
+         <span class="sr-only">[% l('records in basket') %]</span>
+       </a>
+     </div>
+  </div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/cart_nav.tt2 b/Open-ILS/src/templates/opac-new/parts/cart_nav.tt2
new file mode 100644 (file)
index 0000000..32a1d62
--- /dev/null
@@ -0,0 +1,26 @@
+<div>
+  <div id="basket_actions">
+  <div class="dropdown">
+      <a href="#" id="cartDropdown" data-toggle="dropdown">
+        <img src="[% ctx.media_prefix %]/images/cart-sm.png[% ctx.cache_key %]" alt="[% l('View Basket') %]">
+         <div id="record_basket_count_floater">
+            <span id="record_basket_count">[% ctx.mylist.size %]</span>
+            <span class="sr-only">[% l('Records In Basket') %]</span>
+        </div>
+      </a>
+      <ul class="dropdown-menu dropdown-menu-left" role="menu" aria-labelledby="cartDropdown" style="right:0px !important;left:unset !important;">
+        <a role="menuitem" tabindex="-1" href="[% mkurl(ctx.opac_root _ '/mylist', {}) %]"><li role="presentation" class="dropdown-item">[% l('View Basket') %]</li></a>
+        <a role="menuitem" tabindex="-1" href="[% mkurl(ctx.opac_root _ '/mylist/move', { action => 'place_hold', entire_list => 1 }) %]"><li role="presentation" class="dropdown-item">[% l('Place Holds') %]</li></a>
+        <a role="menuitem" tabindex="-1" href="[% mkurl(ctx.opac_root _ '/mylist/print', {}) %]"><li role="presentation" class="dropdown-item">[% l('Print Title Details') %]</li></a>
+        <a role="menuitem" tabindex="-1" href="[% mkurl(ctx.opac_root _ '/mylist/email', {}) %]"><li role="presentation" class="dropdown-item">[% l('Email Title Details') %]</li></a>
+        [% IF !ctx.is_browser_staff %]
+        <a role="menuitem" tabindex="-1" href="[% mkurl(ctx.opac_root _ '/myopac/lists', { move_cart_by_default => 1, from_basket => 1 }) %]"><li role="presentation" class="dropdown-item">[% l('Add Basket to Saved List') %]</li></a>
+        [% END %]
+        [% IF ctx.is_browser_staff %]
+        <a role="menuitem" tabindex="-1" href="add_cart_to_bucket"><li role="presentation" class="dropdown-item">[% l('Add Basket to Bucket') %]</li></a>
+        [% END %]
+        <a role="menuitem" tabindex="-1" href="[% mkurl(ctx.opac_root _ '/mylist/clear', {}) %]"><li role="presentation" class="dropdown-item">[% l('Clear Basket') %]</li></a>
+      </ul>
+    </div>
+  </div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/chilifresh.tt2 b/Open-ILS/src/templates/opac-new/parts/chilifresh.tt2
new file mode 100644 (file)
index 0000000..937e20f
--- /dev/null
@@ -0,0 +1,17 @@
+[%-  IF ENV.OILS_CHILIFRESH_URL AND ENV.OILS_CHILIFRESH_ACCOUNT 
+            AND (ctx.page == 'rresult' OR ctx.page == 'record');
+        chili_url = ENV.OILS_CHILIFRESH_URL;
+        IF CGI.https AND ENV.OILS_CHILIFRESH_HTTPS_URL;
+            chili_url = ENV.OILS_CHILIFRESH_HTTPS_URL;
+        END; 
+%]
+    <!-- Set up the chilifresh account information and load the JS -->
+    <input type="hidden" id="chilifresh_account" name="chilifresh_account" value="[% ENV.OILS_CHILIFRESH_ACCOUNT %]"/>
+    <!--
+    <input type="hidden" id="chilifresh_profile" name="chilifresh_profile" value="[% ENV.OILS_CHILIFRESH_PROFILE %]"/>
+    -->
+    <input type="hidden" id="chilifresh_version" name="chilifresh_version" value="onsite_v1" />
+    <input type="hidden" id="chilifresh_type" name="chilifresh_type" value="search" />
+    <script type="text/javascript" src="[% chili_url %]"></script>
+    <script>try { setTimeout(chili_init, 0); } catch(E) {}</script>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/coded_value_selector.tt2 b/Open-ILS/src/templates/opac-new/parts/coded_value_selector.tt2
new file mode 100644 (file)
index 0000000..35d8e37
--- /dev/null
@@ -0,0 +1,47 @@
+
+[%- 
+    # If caller passes a list of possible attribute types, 
+    # search all until we find some values
+
+    IF !attr.size; attr = [attr]; END;
+    all_values = [];
+    attr_class = '';
+    FOR attr_class IN attr;
+        all_values = ctx.search_ccvm('ctype', attr_class, 'opac_visible', 't');
+        IF all_values.size > 0; LAST; END;
+    END;
+    name = name || "fi:" _ attr_class;
+    id = id || attr_class _ "_selector";
+    values = values || CGI.param(name); 
+    IF size AND size < 1; size = all_values.size; END;
+-%]
+
+<select class="form-control w-100" id='[% id %]' name='[% name %]'[%
+    multiple ? ' multiple="multiple"' : '';
+    size ? (' size="' _ size _ '"') : '';
+%] title="[% filter_label || l('Select item type:') %]">
+[% IF none_ok %]
+    <option value=''>[% none_label ? none_label : l('-- Any --') %]</option>
+[% END;
+# turn the list of objects into a list of hashes to 
+# leverage TT's array.sort('<hashkey>') behavior
+simple_sorter = [];
+sorter = [];
+FOR o IN all_values;
+    IF o.is_simple == 't';
+        simple_sorter.push({code => o.code, value => (o.search_label ? o.search_label : o.value)});
+    ELSE;
+        sorter.push({code => o.code, value  => (o.search_label ? o.search_label : o.value)}); 
+    END;
+END;
+FOR o IN simple_sorter.sort('value') %]
+    <option value='[% o.code | html | replace("'","&#39;") %]'[% values.grep('^' _ o.code _ '$').size ? ' selected="selected"' : '' %]>[% o.value | html %]</option>
+[%  END;
+IF simple_sorter.size && sorter.size %]
+    <option disabled='true'>-----</option>
+[%  END;
+FOR o IN sorter.sort('value') %]
+    <option value='[% o.code | html | replace("'","&#39;") %]'[% values.grep('^' _ o.code _ '$').size ? ' selected="selected"' : '' %]>[% o.value | html %]</option>
+[%  END -%]
+</select>
+
diff --git a/Open-ILS/src/templates/opac-new/parts/config-fresh.tt2 b/Open-ILS/src/templates/opac-new/parts/config-fresh.tt2
new file mode 100644 (file)
index 0000000..08f3ae8
--- /dev/null
@@ -0,0 +1,281 @@
+[%
+
+##############################################################################
+# This file contains skin-level configuration settings.
+# Many of these settings will probably migrate into actor.org_unit_settings.
+##############################################################################
+
+##############################################################################
+# Holds blocking
+##############################################################################
+# Prevent the "Place hold" link from being displayed if a copy is available.
+# This is not perfect, given the umpteen different types of holds that are
+# possible, but addresses the major use case for libraries that don't want
+# to fetch copies from the shelves.
+ctx.holds_block.enabled = 'false';
+
+##############################################################################
+# RefWorks configuration
+##############################################################################
+# RefWorks is a Web-based citation manager
+ctx.refworks.enabled = 'false';
+
+# Base URL for RefWorks
+ctx.refworks.url = 'http://www.refworks.com';
+
+# Some sites have a hosted RefWorks instance at a different URL;
+# in addition, you might want to proxy access to RefWorks - for example:
+# ctx.refworks.url = 'http://librweb.laurentian.ca/login?url=http://refworks.scholarsportal.info';
+
+##############################################################################
+# OpenURL resolution
+##############################################################################
+# Evergreen provides the ability to point at an OpenURL resolver to find
+# electronic resources for a given ISSN or ISBN. Currently, only the SFX
+# resolver is supported.
+#
+# You must enable the open-ils.resolver instance in opensrf.xml to use
+# this feature.
+##############################################################################
+
+openurl.enabled = 'false';
+openurl.baseurl = 'http://sfx.example.com/instance';
+
+##############################################################################
+# Google Analytics support
+##############################################################################
+# You can enable Google Analytics support in Evergreen by entering a
+# valid Google Analytics code and changing 'false' to 'true'
+##############################################################################
+google_analytics.enabled = 'false';
+google_analytics.code = 'UA-9999999-99';
+
+##############################################################################
+# Ebook API integration
+##############################################################################
+ebook_api.enabled = 'false';
+ebook_api.ebook_test.enabled = 'false';
+ebook_api.ebook_test.base_uris = [ 'http://example.com/ebookapi/t/' ];
+ebook_api.oneclickdigital.enabled = 'false';
+ebook_api.oneclickdigital.base_uris = [ 'http://example.oneclickdigital.com/Products/ProductDetail.aspx' ];
+ebook_api.overdrive.enabled = 'false';
+ebook_api.overdrive.base_uris = [ 'http://elm.lib.overdrive.com/' ];
+
+##############################################################################
+# Enable "Forgot your password?" prompt at login
+##############################################################################
+reset_password = 'true';
+
+##############################################################################
+# Hide various options from user preferences that you might not want to expose
+# if you rely on centralized authentication via open-ils.auth_proxy, like LDAP
+#
+# Username changes can be disabled by the opac.lock_usernames OU setting.
+##############################################################################
+disable_password_change = 'false';
+disable_email_change = 'false';
+
+##############################################################################
+# Some libraries do not do notifications by phone; if not true, then this
+# hides the user preference for phone notifications as well as the phone
+# notification portion of the hold dialogue
+##############################################################################
+allow_phone_notifications = 'true';
+
+##############################################################################
+# Format of parts selection on Place Holds screen
+# Set to true to use radio buttons for parts selection on the Place Holds
+# screen. The default behavior is to to display them in a select menu.
+#############################################################################
+enable.radio.parts = 'false';
+
+##############################################################################
+# Misc. UI Settings
+##############################################################################
+# Option for full details as a default, esp. impt. for e-content
+# that uses resolver plumbing. Valid values are 'true', 'false' and 'hide'.
+# Setting this to 'true' shows full details by default but allows the link
+# to appear for 'Show Fewer Details'. The 'hide' option shows full details
+# and also suppresses the link from displaying at all.
+show_more_details.default = 'false';
+
+##############################################################################
+# Size of the jacket image to display on the record detail summary.
+# Sizes vary depending on added content provider.
+# Options are "small", "medium", and "large"
+record.summary.jacket_size = 'medium';
+
+##############################################################################
+# Define the order in which facets are displayed.  Only facets listed here
+# will be displayed.  To show all facets sorted by name, comment out this
+# setting.
+# facet.display = [] # show no facets
+facet.display = [
+    {facet_class => 'author',  facet_order => ['personal', 'corporate']},
+    {facet_class => 'subject', facet_order => ['topic']},
+    {facet_class => 'identifier', facet_order => ['genre']},
+    {facet_class => 'series',  facet_order => ['seriestitle']},
+    {facet_class => 'subject', facet_order => ['name', 'geographic']}
+];
+facet.default_display_count = 5;
+
+##############################################################################
+# Define the advanced search limiters and labels.
+# Each entry is put into a table cell.
+# adv_label is the (translated) label for the limiter
+# adv_attr is an array of possible limiters, the first one that has any
+#   values will be used
+# adv_filter is the same as adv_attr, but for search filter groups
+# adv_size lets you set the height of the adv_attr or adv_filter select box.
+# if adv_size < 1, the box height is set to the number of options in it.
+# adv_break will end the current row. If specified with a label/attr it
+#   will do so *after* that limiter.
+# adv_special will drop in a special entry:
+#   lib_selector will put the search library box (with limit to available)
+#   pub_year will put the publication year box
+#   sort_selector will put the sort results selector
+# id DOM id used for linking labels to form controls.  They are pinned
+#   here instead of auto-generated (from the attr type, for example)
+#   for consistency.
+
+search.adv_config = [
+    {adv_label => l("Item Type"), adv_attr => ["mattype", "item_type"], id => 'adv_selector_item_type'},
+    {adv_label => l("Item Form"), adv_attr => "item_form", id => 'adv_selector_item_form'},
+    {adv_label => l("Language"),  adv_attr => "item_lang", id => 'adv_selector_item_lang'},
+    {adv_label => l("Audience"),  adv_attr => ["audience_group", "audience"], id => 'adv_selector_audience', adv_break => 1},
+    {adv_label => l("Video Format"), adv_attr => "vr_format", id => 'adv_selector_video_format'},
+    {adv_label => l("Bib Level"), adv_attr => "bib_level", id => 'adv_selector_bib_level'},
+    {adv_label => l("Literary Form"), adv_attr => "lit_form", id => 'adv_selector_lit_form'},
+    {adv_label => l("Shelving Location"), adv_special => "copy_location", id => 'adv_copy_location_selector', js_only => 1, adv_break => 1},
+    {adv_label => l("Search Library"), adv_special => "lib_selector", id => 'adv_org_selector'},
+    {adv_label => l("Publication Year"), adv_special => "pub_year", id => 'adv_selector_pub_year'},
+    {adv_label => l("Sort Results"), adv_special => "sort_selector", id => 'adv_selector_sort_results'},
+];
+
+# Set the default height of the select boxes. Defaults to 4.
+#search.default_adv_select_height = 4;
+
+##############################################################################
+# For each search box the default "query type" value can be specified here
+# This is the actual backend value, not the label
+# Also note that including more than the row count entries won't add rows
+# The first entry should be used as a default for "basic" search as well
+
+search.default_qtypes = ['keyword','title','author'];
+
+##############################################################################
+# Basic Search Box definition
+# This allows selection of what, exactly, basic search uses for a selection
+# box. Previously it was hardcoded to use an attr box of mattype or item_type.
+#
+# type can be "attr" or "filter"
+# group is the attr or filter entries you want to check for
+# none_label is the label for the default nothing selected entry.
+
+search.basic_config = {
+    type => 'attr',
+    group => [ctx.get_cgf('opac.format_selector.attr').value, 'item_type'],
+    none_label => l("All Formats"),
+};
+
+# Make metarecord search the default.
+#search.metarecord_default = 1;
+
+# Disable search term highlighting
+#search.no_highlight = 1;
+
+##############################################################################
+# Show Google Book Previews
+# Set to 1 or 'true' to enable
+ctx.google_books_preview = 0;
+
+##############################################################################
+
+# Set a maintenance message to display in the catalogue. Supports HTML encoding
+ctx.maintenance_message = "<b>This OPAC will be unavailable on Month ##, #### at #pm for planned upgrades.</b>";
+# Maintenance types follow Bootstrap4 alert options as follows
+# success = green; info = blue; warning = yellow; danger = red
+# Other colors would require re-coding the alert 
+ctx.maintenance_type = l('info');
+
+##############################################################################
+# Depth Button/Checkbox
+# Recommendation: Do not enable button for basic search without enabling the
+# checkbox for the results page
+ctx.depth_sel_checkbox = 1; # Results Page Checkbox Toggle
+ctx.depth_sel_button = 1; # Basic Search Submit Button
+ctx.depth_sel_depth = 0; # Depth to set to
+ctx.depth_sel_button_label = l('All Libraries');
+ctx.depth_sel_button_class = 'opac-button';
+ctx.depth_sel_checkbox_label = l('Show Results from All Libraries');
+ctx.depth_sel_tooltip = l('Select this option to expand your results to all libraries while retaining the priority of your selected library\'s holdings.');
+ctx.depth_sel_resultshint = l('Showing results from all libraries');
+
+##############################################################################
+# Exclude Electronic Resources Checkbox
+# One setting for both the advanced search page and the results bar.
+# Off by default; set to 1 to display.
+ctx.exclude_electronic_checkbox = 0;
+
+##############################################################################
+# Metarecords configuration
+# metarecords.disabled = 1; # disable all metarecord access points
+##############################################################################
+
+##############################################################################
+# Local date format (uses POSIX strftime() formatting)
+# See http://www.template-toolkit.org/docs/modules/Template/Plugin/Date.html
+# DATE_FORMAT = '%Y-%m-%d'; # for 2014-06-31 format
+##############################################################################
+
+##############################################################################
+# Local time format (uses POSIX strftime() formatting)
+# See http://www.template-toolkit.org/docs/modules/Template/Plugin/Date.html
+# TIME_FORMAT = '%H:%M:%S'; # for 16:32:32 (24 hour) format
+# TIME_FORMAT = '%H:%M'; # for 16:32 (24 hour) format
+##############################################################################
+
+##############################################################################
+# Obalkyknih.cz support
+##############################################################################
+# Obalkyknihy.cz is free Czech service providing added content and book covers
+# Set to 'true' to enable
+##############################################################################
+# obalkyknih_cz.enabled = 'false';
+
+##############################################################################
+# JQuery support
+##############################################################################
+# Include JQuery in the TPAC?
+# Set to a true value to enable
+##############################################################################
+# ctx.want_jquery = 1;
+
+##############################################################################
+# Browser cache-busting key
+##############################################################################
+# Links to assets like stylesheets, JavaScript, and images will have this
+# key appended so that browsers will be forced to request a new version.
+# Enables you to set a cache max-date of years.
+#
+# If unset, it falls back to eg_cache_hash (which is set every time you run
+# "autogen", which may or may not be when you change styles etc)
+##############################################################################
+# ctx.cache_key = '001'
+
+##############################################################################
+# Display of badge scores
+##############################################################################
+ctx.hide_badge_scores = 'false';
+
+##############################################################################
+# Maximum number of items allowed to be stored in a basket
+##############################################################################
+ctx.max_cart_size = 500;
+
+##############################################################################
+# Display booking reservations tab in myopac 
+##############################################################################
+ctx.show_reservations_tab = 'false';
+
+%]
diff --git a/Open-ILS/src/templates/opac-new/parts/config.tt2 b/Open-ILS/src/templates/opac-new/parts/config.tt2
new file mode 100644 (file)
index 0000000..aab1add
--- /dev/null
@@ -0,0 +1,282 @@
+[%
+
+##############################################################################
+# This file contains skin-level configuration settings.
+# Many of these settings will probably migrate into actor.org_unit_settings.
+##############################################################################
+
+
+##############################################################################
+# Holds blocking
+##############################################################################
+# Prevent the "Place hold" link from being displayed if a copy is available.
+# This is not perfect, given the umpteen different types of holds that are
+# possible, but addresses the major use case for libraries that don't want
+# to fetch copies from the shelves.
+ctx.holds_block.enabled = 'false';
+
+##############################################################################
+# RefWorks configuration
+##############################################################################
+# RefWorks is a Web-based citation manager
+ctx.refworks.enabled = 'false';
+
+# Base URL for RefWorks
+ctx.refworks.url = 'http://www.refworks.com';
+
+# Some sites have a hosted RefWorks instance at a different URL;
+# in addition, you might want to proxy access to RefWorks - for example:
+# ctx.refworks.url = 'http://librweb.laurentian.ca/login?url=http://refworks.scholarsportal.info';
+
+##############################################################################
+# OpenURL resolution
+##############################################################################
+# Evergreen provides the ability to point at an OpenURL resolver to find
+# electronic resources for a given ISSN or ISBN. Currently, only the SFX
+# resolver is supported.
+#
+# You must enable the open-ils.resolver instance in opensrf.xml to use
+# this feature.
+##############################################################################
+
+openurl.enabled = 'false';
+openurl.baseurl = 'http://sfx.example.com/instance';
+
+##############################################################################
+# Google Analytics support
+##############################################################################
+# You can enable Google Analytics support in Evergreen by entering a
+# valid Google Analytics code and changing 'false' to 'true'
+##############################################################################
+google_analytics.enabled = 'false';
+google_analytics.code = 'UA-9999999-99';
+
+##############################################################################
+# Ebook API integration
+##############################################################################
+ebook_api.enabled = 'true';
+ebook_api.ebook_test.enabled = 'true';
+ebook_api.ebook_test.base_uris = [ 'http://example.com/ebookapi/t/' ];
+ebook_api.oneclickdigital.enabled = 'false';
+ebook_api.oneclickdigital.base_uris = [ 'http://example.oneclickdigital.com/Products/ProductDetail.aspx' ];
+ebook_api.overdrive.enabled = 'false';
+ebook_api.overdrive.base_uris = [ 'http://elm.lib.overdrive.com/' ];
+
+##############################################################################
+# Enable "Forgot your password?" prompt at login
+##############################################################################
+reset_password = 'true';
+
+##############################################################################
+# Hide various options from user preferences that you might not want to expose
+# if you rely on centralized authentication via open-ils.auth_proxy, like LDAP
+#
+# Username changes can be disabled by the opac.lock_usernames OU setting.
+##############################################################################
+disable_password_change = 'false';
+disable_email_change = 'false';
+
+##############################################################################
+# Some libraries do not do notifications by phone; if not true, then this
+# hides the user preference for phone notifications as well as the phone
+# notification portion of the hold dialogue
+##############################################################################
+allow_phone_notifications = 'true';
+
+##############################################################################
+# Format of parts selection on Place Holds screen
+# Set to true to use radio buttons for parts selection on the Place Holds
+# screen. The default behavior is to to display them in a select menu.
+#############################################################################
+enable.radio.parts = 'false';
+
+##############################################################################
+# Misc. UI Settings
+##############################################################################
+# Option for full details as a default, esp. impt. for e-content
+# that uses resolver plumbing. Valid values are 'true', 'false' and 'hide'.
+# Setting this to 'true' shows full details by default but allows the link
+# to appear for 'Show Fewer Details'. The 'hide' option shows full details
+# and also suppresses the link from displaying at all.
+show_more_details.default = 'false';
+
+##############################################################################
+# Size of the jacket image to display on the record detail summary.
+# Sizes vary depending on added content provider.
+# Options are "small", "medium", and "large"
+record.summary.jacket_size = 'medium';
+
+##############################################################################
+# Define the order in which facets are displayed.  Only facets listed here
+# will be displayed.  To show all facets sorted by name, comment out this
+# setting.
+# facet.display = [] # show no facets
+facet.display = [
+    {facet_class => 'author',  facet_order => ['personal']},
+    {facet_class => 'subject', facet_order => ['topic']},
+    #{facet_class => 'identifier', facet_order => ['genre']},
+    {facet_class => 'series',  facet_order => ['seriestitle']},
+    {facet_class => 'subject', facet_order => ['name']}
+];
+facet.default_display_count = 4;
+
+##############################################################################
+# Define the advanced search limiters and labels.
+# Each entry is put into a table cell.
+# adv_label is the (translated) label for the limiter
+# adv_attr is an array of possible limiters, the first one that has any
+#   values will be used
+# adv_filter is the same as adv_attr, but for search filter groups
+# adv_size lets you set the height of the adv_attr or adv_filter select box.
+# if adv_size < 1, the box height is set to the number of options in it.
+# adv_break will end the current row. If specified with a label/attr it
+#   will do so *after* that limiter.
+# adv_special will drop in a special entry:
+#   lib_selector will put the search library box (with limit to available)
+#   pub_year will put the publication year box
+#   sort_selector will put the sort results selector
+# id DOM id used for linking labels to form controls.  They are pinned
+#   here instead of auto-generated (from the attr type, for example)
+#   for consistency.
+
+search.adv_config = [
+    #{adv_label => l("Item Type"), adv_attr => ["mattype", "item_type"], id => 'adv_selector_item_type'},
+    {adv_label => l("Item Form"), adv_attr => "item_form", id => 'adv_selector_item_form'},
+    {adv_label => l("Language"),  adv_attr => "item_lang", id => 'adv_selector_item_lang'},
+    {adv_label => l("Audience"),  adv_attr => ["audience_group", "audience"], id => 'adv_selector_audience', adv_break => 1},
+    {adv_label => l("Video Format"), adv_attr => "vr_format", id => 'adv_selector_video_format'},
+    #{adv_label => l("Bib Level"), adv_attr => "bib_level", id => 'adv_selector_bib_level'},
+    {adv_label => l("Literary Form"), adv_attr => "lit_form", id => 'adv_selector_lit_form'},
+    {adv_label => l("Shelving Location"), adv_special => "copy_location", id => 'adv_copy_location_selector', js_only => 101, adv_break => 1},
+    {adv_label => l("Search Library"), adv_special => "lib_selector", id => 'adv_org_selector'},
+    {adv_label => l("Publication Year"), adv_special => "pub_year", id => 'adv_selector_pub_year'},
+    {adv_label => l("Sort Results"), adv_special => "sort_selector", id => 'adv_selector_sort_results'},
+];
+
+# Set the default height of the select boxes. Defaults to 4.
+#search.default_adv_select_height = 4;
+
+##############################################################################
+# For each search box the default "query type" value can be specified here
+# This is the actual backend value, not the label
+# Also note that including more than the row count entries won't add rows
+# The first entry should be used as a default for "basic" search as well
+
+search.default_qtypes = ['keyword','title','author'];
+
+##############################################################################
+# Basic Search Box definition
+# This allows selection of what, exactly, basic search uses for a selection
+# box. Previously it was hardcoded to use an attr box of mattype or item_type.
+#
+# type can be "attr" or "filter"
+# group is the attr or filter entries you want to check for
+# none_label is the label for the default nothing selected entry.
+
+search.basic_config = {
+    type => 'attr',
+    group => [ctx.get_cgf('opac.format_selector.attr').value, 'item_type'],
+    none_label => l("All Formats"),
+};
+
+# Make metarecord search the default.
+#search.metarecord_default = 1;
+
+# Disable search term highlighting
+#search.no_highlight = 1;
+
+##############################################################################
+# Show Google Book Previews
+# Set to 1 or 'true' to enable
+ctx.google_books_preview = 0;
+
+##############################################################################
+
+# Set a maintenance message to display in the catalogue
+#ctx.maintenance_message = "<b>This OPAC will be unavailable part of March 15, 2019 for planned upgrades.</b>";
+# Maintenance types follow Bootstrap4 alert options as follows
+# success = green; info = blue; warning = yellow; danger = red
+# Other colors would require re-coding the alert 
+#ctx.maintenance_type = l('warning');
+
+##############################################################################
+# Depth Button/Checkbox
+# Recommendation: Do not enable button for basic search without enabling the
+# checkbox for the results page
+ctx.depth_sel_checkbox = 1; # Results Page Checkbox Toggle
+ctx.depth_sel_button = 1; # Basic Search Submit Button
+ctx.depth_sel_depth = 0; # Depth to set to
+ctx.depth_sel_button_label = l('All Libraries');
+ctx.depth_sel_button_class = 'opac-button';
+ctx.depth_sel_checkbox_label = l('Show Results from All Libraries');
+ctx.depth_sel_tooltip = l('Select this option to expand your results to all libraries while retaining the priority of your selected librarys holdings.');
+ctx.depth_sel_resultshint = l('Showing results from all libraries');
+
+##############################################################################
+# Exclude Electronic Resources Checkbox
+# One setting for both the advanced search page and the results bar.
+# Off by default; set to 1 to display.
+ctx.exclude_electronic_checkbox = 1;
+
+##############################################################################
+# Metarecords configuration
+# metarecords.disabled = 1; # disable all metarecord access points
+##############################################################################
+
+##############################################################################
+# Local date format (uses POSIX strftime() formatting)
+# See http://www.template-toolkit.org/docs/modules/Template/Plugin/Date.html
+# DATE_FORMAT = '%Y-%m-%d'; # for 2014-06-31 format
+##############################################################################
+
+##############################################################################
+# Local time format (uses POSIX strftime() formatting)
+# See http://www.template-toolkit.org/docs/modules/Template/Plugin/Date.html
+# TIME_FORMAT = '%H:%M:%S'; # for 16:32:32 (24 hour) format
+# TIME_FORMAT = '%H:%M'; # for 16:32 (24 hour) format
+##############################################################################
+
+##############################################################################
+# Obalkyknih.cz support
+##############################################################################
+# Obalkyknihy.cz is free Czech service providing added content and book covers
+# Set to 'true' to enable
+##############################################################################
+# obalkyknih_cz.enabled = 'false';
+
+##############################################################################
+# JQuery support
+##############################################################################
+# Include JQuery in the TPAC?
+# Set to a true value to enable
+##############################################################################
+# ctx.want_jquery = 1;
+
+##############################################################################
+# Browser cache-busting key
+##############################################################################
+# Links to assets like stylesheets, JavaScript, and images will have this
+# key appended so that browsers will be forced to request a new version.
+# Enables you to set a cache max-date of years.
+#
+# If unset, it falls back to eg_cache_hash (which is set every time you run
+# "autogen", which may or may not be when you change styles etc)
+##############################################################################
+#ctx.cache_key = '001'
+
+##############################################################################
+# Display of badge scores
+##############################################################################
+ctx.hide_badge_scores = 'false';
+
+##############################################################################
+# Maximum number of items allowed to be stored in a basket
+##############################################################################
+ctx.max_cart_size = 500;
+
+##############################################################################
+# Display booking reservations tab in myopac 
+##############################################################################
+ctx.show_reservations_tab = 'false';
+
+%]
diff --git a/Open-ILS/src/templates/opac-new/parts/css/colors.tt2 b/Open-ILS/src/templates/opac-new/parts/css/colors.tt2
new file mode 100644 (file)
index 0000000..d758124
--- /dev/null
@@ -0,0 +1,93 @@
+[%
+    css_colors = {
+        
+        
+#The counts that appear in the navigation when signed into the OPAC -EX. X Ready For Pickup where x is the modified number
+
+       zero_count = "#a0ff9e", #Logged in item/charges count at 0
+        non_zero_count = "#ff0016", #Logged in item/charges count NOT at 0
+
+       facets_back = "#00593d", #search list facet backgrounds
+       footer_link = "#fff",
+
+       nav_separation = "#00593d",
+        header_primary = "#00593d", #Primary Header Color
+        header_primary_fade = "#007a54", #Fade Header Color, make equal to header_primary for no fade
+        footer_primary = "#00593d", #Primary Footer Color
+        footer_primary_fade = "#007a54", #Fade Footer Color, make equal to footer_primary for no fade
+
+        background = "#fff", # white
+        background_alert = "#ffcccc", # pink
+        background_invert = "#252525", # charcoal
+        text = "black", # black 
+        text_alert = "red", # red
+        text_attention = "#ffcc33", # pumpkin orange
+        text_badnews = "#f41d36", # cherry red
+        text_goodnews = "#1dd93c", # lime green
+        text_greatnews = "green", # green
+        text_invert = "#fff", # white
+        text_match = "#c00", # deep red
+        primary = "#00593d", # dark green
+        primary_fade = "#007a54", # medium green
+        primary_offset = "#417860", # light_green
+        control = "#69A088", # lighter green
+        accent_light = "#ccc", #  grey (light)
+        accent_lighter = "#ddd", #  grey (lighter)
+        accent_lighter2 = "#d8d8d8", #  grey (lighter again)
+        accent_lightest = "#eee", #  grey (lightest)
+        accent_ultralight = "#f0f0f0", # ivory
+        accent_medium = "#999", # gun metal grey (medium)
+        accent_mediumdark = "#888", # gun metal grey (medium dark)
+        accent_dark = "#666", # gun metal grey (dark)
+        accent_darker = "#333", # gun metal grey (darker)
+        border_standard = "#e9ebf3", # light grey-blue
+        border_dark = "black", # black
+        border_alert = "red", # red
+        button_text = "#fbf9f9", # off-white 
+        button_text_shadow = "#555555", # medium grey
+        table_heading = "#d8d8d8", # grey-blue
+        mobile_header_text = "#fff", # white
+        item_selected = "#ddd", # grey (lighter)
+        link = "#00593d", # dark green
+
+#Buttons are using default Bootstrap styling colors by default with this scheme, changable to match your library colours
+
+#Submit buttons and confirmations likle login. Make all 4 the same color for no hover effect
+        button_confirm = "#28a745",
+        button_confirm_hover = "#218838",
+        button_confirm_border = "#28a745",
+        button_confirm_border_hover = "#1e7e34",
+        button_confirm_text = "#fff",
+
+#Denial buttons like Cancel. Make all 4 the same color for no hover effect    
+        button_deny = "#dc3545",
+        button_deny_hover = "#c82333",
+        button_deny_border = "#dc3545",
+        button_deny_border_hover = "#bd2130",
+        button_deny_text = "#fff",
+
+#Removal buttons like removing a search row in the advanced search. Make all 4 the same color for no hover effect
+        button_remove = "#dc3545",
+        button_remove_hover = "#c82333",
+        button_remove_border = "#dc3545",
+        button_remove_border_hover = "#bd2130",
+        button_remove_text = "#fff",
+
+#Main search buttons and buttons that are not a confirm or deny such as the Less and More details on the record. Make all 4 the same color for no hover effect 
+        opac_button = "#2e3b91",
+        opac_button_hover = "#949cc8",
+        opac_button_border = "#2e3b91",
+        opac_button_border_hover = "#949cc8",
+        opac_button_text = "#fff",
+
+#Action buttons such as Place Hold and Add to List seen in record and results. Make all 4 the same color for no hover effect 
+        action_button = "#f8f9fa",
+        action_button_hover = "#e2e6ea",
+        action_button_border = "#f8f9fa",
+        action_button_border_hover = "#dae0e5",
+        action_button_text = "#00593d",
+        #Many action buttons use link coloring on the anchor tags. This setting will change the color to match your color schema if necessary
+        #action_button_text_anchors = "",
+};
+    
+%]
diff --git a/Open-ILS/src/templates/opac-new/parts/css/fonts.tt2 b/Open-ILS/src/templates/opac-new/parts/css/fonts.tt2
new file mode 100644 (file)
index 0000000..eaf2e7b
--- /dev/null
@@ -0,0 +1,13 @@
+[%-
+    css_fonts = {
+        size_base = "14px",
+        size_small = "92%",
+        size_smaller = "83%",
+        size_smallest = "75%",
+        size_medium = "95%",
+        size_big = "110%",
+        size_bigger = "125%",
+        size_biggest = "150%"
+       font_main = "Arial, Helvetica, sans-serif",
+    }
+%]
diff --git a/Open-ILS/src/templates/opac-new/parts/ebook_api/avail.tt2 b/Open-ILS/src/templates/opac-new/parts/ebook_api/avail.tt2
new file mode 100644 (file)
index 0000000..9df0465
--- /dev/null
@@ -0,0 +1,41 @@
+[%-
+# Display holdings/availability info from ebook API
+#
+# We require the following info:
+# - rec_id: internal ID for this record (rec.id in search results, ctx.bre_id in record summary)
+# - ebook_id: external ID for title (ISBN for OneClickdigital, unique identifier for OverDrive)
+# - vendor (oneclickdigital, overdrive)
+
+IF args.ebook;
+
+    IF ctx.page == 'rresult';
+        ebook.rec_id = rec.id;
+    ELSE;
+        ebook.rec_id = ctx.bre_id;
+    END;
+
+    ebook.ebook_id = args.ebook.ebook_id;
+    ebook.vendor = args.ebook.vendor;
+
+# This div is hidden by default. The JS layer will unhide it, use the ebook_id
+# to retrieve holdings/availability info via the appropriate vendor API, and
+# overwrite the div's contents with that information.
+-%]
+<div id="[% ebook.rec_id %]" class="ebook_avail hidden">
+    <div id="[% ebook.ebook_id %]" class="[% ebook.vendor %]_avail">
+        <table id="[% ebook.rec_id %]_ebook_holdings" class="result_holdings_table hidden">
+            <thead>
+                <tr>
+                    <th>[% l('Available Formats') %]</th>
+                    <th>[% l('Status') %]</th>
+                </tr>
+            <tbody>
+                <tr>
+                    <td id="[% ebook.rec_id %]_formats"></td>
+                    <td id="[% ebook.rec_id %]_status"></td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/ebook_api/base_js.tt2 b/Open-ILS/src/templates/opac-new/parts/ebook_api/base_js.tt2
new file mode 100644 (file)
index 0000000..668b5fa
--- /dev/null
@@ -0,0 +1,95 @@
+[%
+# HTML display chunks
+progress_icon = '<img id="ebook_avail_spinner" src="/opac/images/progressbar_green.gif' _ ctx.cache_key _ '" alt="' _ l("Checking availability for this item...") _ '"/>'
+%]
+
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/ebook_api/session.js[% ctx.cache_key %]"></script>
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/ebook_api/ebook.js[% ctx.cache_key %]"></script>
+<script type="text/javascript">
+
+// translatable strings as JS variables
+var l_strings = {};
+l_strings.download = '[% l('Download') %]';
+l_strings.ready_for_checkout = '[% l('Ready for Checkout') %]';
+l_strings.suspended = '[% l('Suspended') %]';
+l_strings.checkout = '[% l('Checkout') %]';
+l_strings.place_hold = '[% l('Place Hold') %]';
+l_strings.cancel_hold = '[% l('Cancel Hold') %]';
+
+// give us cookies!
+dojo.require("dojo.cookie");
+
+// context org unit
+[% IF !ctx.page OR ctx.page != 'rresult';
+    PROCESS get_library;
+END %]
+var ou = [% loc_value %];
+
+// list of enabled vendors
+var vendor_list = [];
+[% IF ebook_api.ebook_test.enabled == 'true' %]
+vendor_list.push('ebook_test');
+[% END %]
+[% IF ebook_api.oneclickdigital.enabled == 'true' %]
+vendor_list.push('oneclickdigital');
+[% END %]
+[% IF ebook_api.overdrive.enabled == 'true' %]
+vendor_list.push('overdrive');
+[% END %]
+
+var cookie_registry = [ 'ebook_xact_cache' ];
+dojo.forEach(vendor_list, function(v) {
+    cookie_registry.push(v);
+});
+
+// essential info for performing a transaction
+var ebook_action = {};
+[%- IF CGI.param("action").defined %]
+ebook_action.type = '[% CGI.param("action") | html %]';
+[%- END -%]
+[%- IF CGI.param("title").defined %]
+ebook_action.title_id = '[% CGI.param("title") | html %]';
+[%- END -%]
+[%- IF CGI.param("vendor").defined %]
+ebook_action.vendor = '[% CGI.param("vendor") | html %]';
+[%- END -%]
+
+[% IF ctx.user %]
+// user- or login-specific vars
+var authtoken = '[% ctx.authtoken %]';
+var patron_id = '[% ctx.active_card %]'; // using barcode of active card as patron ID
+[%- IF ctx.user.email %]
+var patron_email = '[% ctx.user.email | html %]';
+[%- ELSE %]
+var patron_email = null;
+[%- END %]
+
+var myopac_page;
+[% IF myopac_page %]
+myopac_page = "[% myopac_page %]";
+[% END %]
+
+[% END %]
+
+// enforce removal of ebook API cookies on logout
+dojo.addOnLoad(function() {
+    var logout_handle = dojo.connect(dojo.byId('#logout_link'), 'onclick', function() {
+        dojo.forEach(cookie_registry, function(cookie) {
+            dojo.cookie(cookie, '', {path: '/', expires: '-1h'});
+        });
+        // When we switch to jQuery, use .one()
+        // instead of dojo's .connect() and .disconnect()
+        dojo.disconnect(logout_handle);
+    });
+});
+</script>
+
+[%- IF ctx.user %]
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/ebook_api/relation.js[% ctx.cache_key %]"></script>
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/ebook_api/loggedin.js[% ctx.cache_key %]"></script>
+[%- END %]
+
+[%- IF (ctx.page == 'rresult' OR ctx.page == 'record') %]
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/ebook_api/avail.js[% ctx.cache_key %]"></script>
+[%- END %]
+
diff --git a/Open-ILS/src/templates/opac-new/parts/ebook_api/login_js.tt2 b/Open-ILS/src/templates/opac-new/parts/ebook_api/login_js.tt2
new file mode 100644 (file)
index 0000000..23ed256
--- /dev/null
@@ -0,0 +1,41 @@
+<script type="text/javascript">
+var vendors_requiring_password = [];
+
+[% IF !loc_value; PROCESS get_library; END; %]
+[% IF ebook_api.overdrive.enabled == 'true'
+    AND loc_value
+    AND ctx.get_org_setting(loc_value, 'ebook_api.overdrive.password_required') %]
+vendors_requiring_password.push('overdrive');
+[% END %]
+
+dojo.addOnLoad(function() {
+    var handle = dojo.connect(dojo.byId('#login-form-box'), 'onclick', function(evt) {
+        // disconnect this event since it's one-time-only
+        // (when we switch to jQuery, we can use .one() here)
+        dojo.disconnect(handle);
+
+        // we cache the username (and password) for now, but will
+        // replace that with the patron's active barcode later
+        vendors_requiring_password.forEach(function(v) {
+            if (vendor_list.includes(v)) {
+                checkSession(v, function(v,ses) {
+                    var username = dojo.byId('#username_field').value;
+                    var password = dojo.byId('#password_field').value;
+                    new OpenSRF.ClientSession('open-ils.ebook_api').request({
+                        method: 'open-ils.ebook_api.patron.cache_password',
+                        params: [ ses, password ],
+                        async: true,
+                        oncomplete: function(r) {
+                            var resp = r.recv();
+                            if (resp) {
+                                console.log('patron password has been cached');
+                                return;
+                            }
+                        }
+                    }).send();
+                });
+            }
+        });
+    });
+});
+</script>
diff --git a/Open-ILS/src/templates/opac-new/parts/filter_group_selector.tt2 b/Open-ILS/src/templates/opac-new/parts/filter_group_selector.tt2
new file mode 100644 (file)
index 0000000..e03033e
--- /dev/null
@@ -0,0 +1,28 @@
+[%- 
+    # If caller passes a list of possible filter_groups
+    # search all until we find some values
+    IF !filter_group.size; filter_group = [filter_group]; END;
+    group = '';
+    FOR code IN filter_group;
+        group = ctx.search_filter_groups.$code;
+        LAST IF group AND group.entries.size;
+    END;
+    name = name || "fg:" _ group.code;
+    id = id || group.code _ "_selector";
+    class = class || '';
+    values = values || CGI.param(name); 
+    IF size AND size < 1; size = group.entries.size; END;
+-%]
+
+<select id='[% id %]' class='[% class %]' name='[% name %]' title="[% filter_label || l('Search Filter') %]"[%
+    multiple ? ' multiple="multiple"' : '';
+    size ? (' size="' _ size _ '"') : ''; %]>
+[% IF none_ok %]
+    <option value=''>[% none_label ? none_label : l('-- Any --') %]</option>
+[% END;
+
+FOR o IN group.entries %]
+    <option value='[% o.id %]'[% values.grep(o.id).size ? ' selected="selected"' : '' %]>[% o.query.label | html %]</option>
+[%  END -%]
+</select>
+
diff --git a/Open-ILS/src/templates/opac-new/parts/filtersort.tt2 b/Open-ILS/src/templates/opac-new/parts/filtersort.tt2
new file mode 100644 (file)
index 0000000..2990352
--- /dev/null
@@ -0,0 +1,29 @@
+<select title="[% l('Search sorting') %]" 
+    [% class ? ('class="' _ class _ '"') : '' %] id='[% id || "opac.result.sort" %]' 
+    name="[% name || 'sort' %]" [% IF submit_on_change %]onchange='this.form.submit()'[% END %]>
+    [% IF mode != 'bookbag' %]<option value=''>[% l("Sort by Relevance") %]</option>[% END %]
+    <optgroup label='[% l("Sort by Title") %]'>
+        <option value='titlesort'[% value == 'titlesort' ? ' selected="selected"' : '' %]>[% l("Title: A to Z") %]</option>
+        <option value='titlesort.descending'[% value == 'titlesort.descending' ? ' selected="selected"' : '' %]>[% l("Title: Z to A") %]</option>
+    </optgroup>
+    <optgroup label='[% l("Sort by Author") %]'>
+        <option value='authorsort'[% value == 'authorsort' ? ' selected="selected"' : '' %]>[% l("Author: A to Z") %]</option>
+        <option value='authorsort.descending'[% value == 'authorsort.descending' ? ' selected="selected"' : '' %]>[% l("Author: Z to A") %]</option>
+    </optgroup>
+    <optgroup label='[% l("Sort by Publication Date") %]'>
+        <option value='pubdate.descending'[% value == 'pubdate.descending' ? ' selected="selected"' : '' %]>[% l("Date: Newest to Oldest") %]</option>
+        <option value='pubdate'[% value == 'pubdate' ? ' selected="selected"' : '' %]>[% l("Date: Oldest to Newest") %]</option>
+    </optgroup>
+    <optgroup label='[% l("Sort by Popularity") %]'>
+        <option value='popularity'[% value == 'popularity' ? ' selected="selected"' : '' %]>[% l("Most Popular") %]</option>
+<!-- Sorting by least popular items first is probably not an expected
+     choice in production, but could be useful in cases where every
+     record has at least one badge value assigned and you want
+     to investigate the long tail.
+-->
+<!--
+        <option value='popularity.descending'[% value == 'popularity.descending' ? ' selected="selected"' : '' %]>[% l("Least Popular") %]</option>
+-->
+        <option value='poprel'[% value == 'poprel' ? ' selected="selected"' : '' %]>[% l("Popularity Adjusted Relevance") %]</option>
+    </optgroup>
+</select>
diff --git a/Open-ILS/src/templates/opac-new/parts/footer.tt2 b/Open-ILS/src/templates/opac-new/parts/footer.tt2
new file mode 100644 (file)
index 0000000..7b9f463
--- /dev/null
@@ -0,0 +1,68 @@
+[% IF !ctx.is_staff %]
+<div id="footer-wrap">
+<div id="footer" class="text-center">
+<nav class="navbar navbar-expand-lg navbar-dark ">
+<!--Home-->
+  
+  <div class="collapse navbar-collapse" id="navbarNav" style="height:auto !important;">
+  <!--Left Links-->
+  <div class="row w-100">
+  <div class="col-12">
+  
+   <ul class="navbar-nav justify-content-center ">
+       <!--Link 1-->
+      [% IF ctx.get_org_setting(ctx.physical_loc || ctx.aou_tree.id, 'opac.allow_pending_user') && !ctx.user %]
+       <li class="nav-item"><a class="nav-link" href="[% mkurl(ctx.opac_root _ '/register') %]">[% 
+        l('Request Library Card') %]</a></li>
+    [% ELSE %]
+       <li class="nav-item"><a class="nav-link" href="#">[% l('Link 1') %]</a></li> 
+    [% END %]
+       <li class="nav-item"><a class="nav-link" href="#">[% l('Link 2') %]</a></li> 
+       <li class="nav-item"><a class="nav-link" href="#">[% l('Link 3') %]</a></li>
+       <li class="nav-item"><a class="nav-link" href="#">[% l('Link 4') %]</a></li>
+    </ul>
+    <div id="nav-divide" class="dropdown-divider"></div>
+    
+   
+       </div>
+       
+       
+               
+           </div>
+       </div>
+   </nav>
+    [% IF ctx.timing %]
+    <div id="timing">
+        [% FOR event IN ctx.timing %]
+        At <span class="timing-time">[% event.0 | format("%0.4f") %]</span>:
+        <span class="timing-event">[% event.1 %]</span><br />
+        [% END %]
+    </div>
+    [% END %]
+    
+ <!--   <div id="footer_logo my-2">
+        <a href="http://evergreen-ils.org">
+            [% INCLUDE "opac/parts/topnav_logo.tt2" %]
+        </a>
+    </div> -->
+    
+      <div id="copyright_text" class="mt-3">
+        [% l('Copyright &copy; 2006-[_1] Georgia Public Library Service, and others', date.format(date.now, '%Y')) %]
+    <div id="footer_logo">
+        [% l('Powered by') %]
+        <a href="http://evergreen-ils.org">
+            <img src="[% ctx.media_prefix %]/opac/images/eg_tiny_logo.png[% ctx.cache_key %]"
+                style="border:none; width: 94px; height: 16px;"
+                alt="[% l('Evergreen') %]"
+            />
+        </a>
+    </div>
+   
+    </div>
+    
+</div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/goog_analytics.tt2 b/Open-ILS/src/templates/opac-new/parts/goog_analytics.tt2
new file mode 100644 (file)
index 0000000..5070537
--- /dev/null
@@ -0,0 +1,18 @@
+[%- PROCESS "opac/parts/header.tt2";
+    IF google_analytics.enabled == 'true' %]
+
+<!-- http://www.google.com/support/googleanalytics/bin/answer.py?answer=174090 -->
+<script type="text/javascript">
+    var _gaq = _gaq || [];
+    _gaq.push(['_setAccount', '[% google_analytics.code %]']);
+    _gaq.push(['_trackPageview']);
+
+    (function() {
+        var ga = document.createElement('script'); 
+        ga.type = 'text/javascript'; ga.async = true;
+        ga.src = ('oils:' == document.location.protocol || 'https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+        var s = document.getElementsByTagName('script')[0]; 
+        s.parentNode.insertBefore(ga, s);
+    })();
+</script>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/header.tt2 b/Open-ILS/src/templates/opac-new/parts/header.tt2
new file mode 100644 (file)
index 0000000..e3a0eda
--- /dev/null
@@ -0,0 +1,165 @@
+[%- USE money = format(l('$%.2f'));
+    USE date;
+    USE CGI = CGI_utf8;
+    USE POSIX;
+    USE HTML;
+    SET DATE_FORMAT = l('%m/%d/%Y');
+    PROCESS "opac/parts/config.tt2";
+
+    # Don't wrap in l() here; do that where this format string is actually used.
+    SET HUMAN_NAME_FORMAT = '[_1] [_2] [_3] [_4] [_5]';
+
+    is_advanced = CGI.param("_adv").size || CGI.param("query").size;
+    is_special = CGI.param("_special").size;
+
+    # Check if we want to show the detail record view.  Doing this
+    # here because we don't want to repeat logic in multiple other
+    # files, nor do we want to add a new tt2 file just for this.  The
+    # below is currently needed for parts/result/table.tt2,
+    # parts/searchbar.tt2, and results.tt2.
+    show_detail_view = 0;
+    IF CGI.param("detail_record_view").defined;
+        show_detail_view = CGI.param("detail_record_view") | html;
+    ELSIF show_more_details.default == "true" OR
+          show_more_details.default == "hide";
+        show_detail_view = 1;
+    END;
+
+    #variables to use to remove parameters via mkurk
+    expert_search_parms = ['tag','subfield','term','_special'];
+    general_search_parms = ['page','sort','query','bool','contains','pubdate'];
+    browse_search_parms = ['fi:has_browse_entry','bterm','blimit','bpivot'];
+    facet_search_parms = ['facet'];
+
+
+    # -----------------------------------------------------------------------------
+    # mkurl( destination_page, params_to_set, params_to_clear )
+    #
+    # Current page, updated params:
+    # mkurl('', {foo => 'bar', boo => 'baz'});
+    #
+    # New page, one param is a list:
+    # mkurl('http://flarg.baz/squz', {foo => 'bar', boo => ['baz', 'faz']});
+    #
+    # New page, clear all existing params before applying new ones:
+    # mkurl('/fuz/buster', {foo => 'bar', boo => 'baz'}, 1); 
+    #
+    # Current page, clear 'some_param' from the existing params:
+    # mkurl('', {foo => 'bar', boo => 'baz'}, ['some_param']);
+    #
+    # Current page to a named anchor 'copies'
+    # mkurl('', {}, [], 'copies');
+    MACRO mkurl(page, params, clear_params, named_anchor) BLOCK;
+
+        # clone the query string to avoid clobberation
+        cgi = CGI.new(CGI.query_string);
+
+        # remove requested params
+        IF clear_params.0; # array
+            FOR p IN clear_params; cgi.delete(p); END;
+        ELSIF clear_params;
+            cgi.delete_all();
+        END;
+
+        # some standing, hardcoded parameters to always clear
+        # because they're used for specific, transitory purposes
+        cgi.delete('move_cart_by_default');
+        cgi.delete('cart_none_selected');
+        cgi.delete('list_none_selected');
+        
+        # x and y are artifacts of using <input type="image" /> tags 
+        # instead of true submit buttons, and their values are never used.
+        cgi.delete('x', 'y'); 
+
+        # apply user params
+        FOR k IN params.keys;
+            encoded = [];
+            max = params.$k.max;
+
+            # The following commented-out line can be fooled. Its replacement
+            # below is what you really mean.
+            # list = (params.$k.0 OR max == -1) ? params.$k : [params.$k];
+            list = params.$k.list;
+
+            IF list.size == 0; NEXT; END;
+            # CGI croaks on already-decoded strings.  force-encode to be safe.
+            FOR p IN list; encoded.push(ctx.encode_utf8(p)); END;
+            foo = cgi.param("-name", k, "-values", encoded);
+        END;
+
+        # for url brevity, remove any params that have no value
+        FOR p IN cgi.param;
+            val = cgi.param(p);
+            IF val == ''; cgi.delete(p); END;
+
+            # Delete POST vars unless we asked for them
+            UNLESS CGI.url_param(p).defined OR params.defined(p);
+                cgi.delete(p);
+            END;
+        END;
+
+        final = named_anchor ? '#' _ named_anchor : '';
+
+        IF page;
+            IF cgi.query_string;
+                page _ '?' _ cgi.query_string _ final;
+            ELSE;
+                page _ final;
+            END;
+        ELSE;
+            # staying on the current page
+            cgi.url("-absolute" => 1, "-path" => 1, "-query" => 1) _ final;
+        END;
+    END;
+
+    # Dojo is required for the copy locations advanced search filter
+    IF ctx.page == 'advanced';
+        want_dojo = 1;
+    END;
+
+    # ... and for code that tweaks visibility of types on the added
+    # content tab
+    IF ctx.page == 'record' AND (ctx.expand_addedcontent OR ctx.expand_all);
+        want_dojo = 1;
+    END;
+
+    use_autosuggest = ctx.get_cgf("opac.use_autosuggest");
+
+    IF use_autosuggest.enabled == "t";
+        want_dojo = 1;
+    END;
+
+    IF ENV.OILS_NOVELIST_URL;
+        want_dojo = 1;
+    END;
+
+    IF ebook_api.enabled == 'true';
+        want_dojo = 1;
+    END;
+
+    # Especially useful for image 'alt' tags and link title tags,
+    # where the content may need to be unique (making it longer)
+    # but should not exceed 75 chars for ideal screen reader support.
+    # usage: html_text_attr('title', 'Link to item I Have A Super Long Title')
+    # the full HTML attribute key="value" is produced
+    MACRO html_text_attr(name, value) BLOCK;
+        IF value.length >= 75;
+            value = value.substr(71, value.length, '...');                        
+        END;                                                                   
+        value = value.replace('\s*$', ''); # remove trailing whitespace          
+        HTML.attributes($name => value); 
+    END;
+
+    MACRO img_alt(text) BLOCK;
+        html_text_attr('alt', text);
+    END;
+
+    # Browser cache-busting key
+    # Fall back to the eg_cache_hash (set by autogen) so that we don't have to
+    # add conditionals into the rest of the templates
+    IF ctx.cache_key AND ctx.cache_key != "?" _ ctx.eg_cache_hash;
+        ctx.cache_key = "?v=" _ ctx.cache_key;
+    ELSE;
+        ctx.cache_key = "?" _ ctx.eg_cache_hash;
+    END;
+%]
diff --git a/Open-ILS/src/templates/opac-new/parts/hold_error_messages.tt2 b/Open-ILS/src/templates/opac-new/parts/hold_error_messages.tt2
new file mode 100644 (file)
index 0000000..407ea52
--- /dev/null
@@ -0,0 +1,40 @@
+[%
+    # XXX It might be cleaner to move this into its own file or something.
+    #
+    # The following hash should have fail_parts as keys and user/patron-friendly
+    # strings as values.  If the system returns an event with a fail_part when
+    # you attempt to place a hold and the fail part matches something in this
+    # hash, this is the message that will be displayed to the user. Wait until
+    # these are used to pass them through l() for i18n.
+
+    FAIL_PART_MSG_MAP = {
+        "actor.usr.barred" => l("The patron is barred"),
+        "asset.copy.circulate" => l("The item does not circulate"),
+        "asset.copy_location.circulate" => l("Items from this shelving location do not circulate"),
+        "asset.copy.status" => l("The item cannot circulate at this time"),
+        "circ.holds.target_skip_me" => l("The item's circulation library does not fulfill holds"),
+        "config.circ_matrix_circ_mod_test" => l("The patron has too many items of this type checked out"),
+        "config.circ_matrix_test.circulate" => l("Circulation rules reject this item as non-circulatable"),
+        "config.hold_matrix_test.holdable" => l("Hold rules reject this item as unholdable"),
+        "config.hold_matrix_test.max_holds" => l("The patron has reached the maximum number of holds"),
+        "config.rule_age_hold_protect.prox" => l("The item is too new to transit this far"),
+        "item.holdable" => l("The item is not holdable"),
+        "location.holdable" => l("The item's location is not holdable"),
+        "status.holdable" => l("The item is not in a holdable status"),
+        "no_item" => l("The system could not find this item"),
+        "no_ultimate_items" => l("The system could not find any items to match this hold request"),
+        "no_matchpoint" => l("System rules do not define how to handle this item"),
+        "no_user" => l("The system could not find this patron"),
+        "transit_range" => l("The item cannot transit this far")
+    };
+
+
+    # This works like the above has, but instead of fail_parts for keys, use
+    # the textcode of the event.  This will only be consulted for events
+    # that do not have a fail_part matching something in the above hash.
+    # Wait until these are used to pass them through l() for i18n.
+
+    EVENT_MSG_MAP = {
+        "PATRON_EXCEEDS_FINES" => l("Patron has reached the maximum fine amount")
+    };
+%]
diff --git a/Open-ILS/src/templates/opac-new/parts/hold_status.tt2 b/Open-ILS/src/templates/opac-new/parts/hold_status.tt2
new file mode 100644 (file)
index 0000000..f2eb5e6
--- /dev/null
@@ -0,0 +1,62 @@
+[% BLOCK get_hold_status %]
+<div>   
+    [% IF hold.hold.status == 4 %]
+            <span class="myopac-hold-available">[% l("Available") %] </span>
+              [% IF ahr.shelf_expire_time %]
+                <br/>
+                [% l("Expires") %]
+                 <span class="myopac-shelf-expire">
+                     [% date.format(ctx.parse_datetime(ahr.shelf_expire_time), DATE_FORMAT) %]
+                 </span>
+              [% END %]
+
+
+     [% ELSIF hold.hold.status == 6 %]
+               <span class="myopac-hold-canceled"> [% l("Canceled") %] </span>
+                  <span class="myopac-cancel-time">
+                      [% date.format(ctx.parse_datetime(ahr.cancel_time), DATE_FORMAT) %]
+                  </span>
+
+     [%  ELSIF hold.hold.status == 7 %]
+               <span class="myopac-hold-suspended"> [% l("Suspended") %] </span>
+                 [% IF ahr.thaw_date %]
+                   <br/>
+                   [% l("Activate On") %]
+                      <span class="my-account-activate-time">
+                        [% date.format(ctx.parse_datetime(ahr.thaw_date), DATE_FORMAT) %]
+                      </span>
+                 [% END %]
+
+     [% ELSIF hold.hold.status == 9 %]
+               <span class="myopac-hold-fulfilled">[% l("Fulfilled") %]</span>
+                  <span class="myopac-fulfill-time">
+                     [% date.format(ctx.parse_datetime(ahr.fulfillment_time), DATE_FORMAT) %]
+                  </span>
+
+
+     [% ELSIF hold.hold.estimated_wait AND hold.hold.estimated_wait > 0;
+            # estimated wait is delivered as seconds.
+            SET hwait = POSIX.ceil(hold.hold.estimated_wait / 86400) %]
+              <span class="myopac-hold-wait"> [% l("Estimated wait:") %] </span>
+                <span class="myopac-hold-wait-time">
+                  [% l("[quant,_1,day,days]", hwait) | html %]
+                </span>
+
+     [% ELSIF hold.hold.status == 3 OR hold.hold.status == 8 %]
+            <span class="myopac-transit"> [% l("In Transit") %] </span>
+
+     [% ELSIF hold.hold.status < 3 %]
+            <span class="myopac-uncaptured-hold"> [% l("Waiting for copy") %] </span>
+     [% END %]
+   </div>
+    <div>
+        [% # Only show the count for holds that have not been captured
+           IF hold.hold.status < 3 OR hold.hold.status == 7 %]
+             <span class="myopac-hold-count">
+                [% l('You are Hold #[_1] on [quant,_2, copy, copies]',
+                   hold.hold.queue_position, hold.hold.potential_copies) | html %]
+             </span>
+         [% END %]
+    </div>
+[% END %]
+
diff --git a/Open-ILS/src/templates/opac-new/parts/homesearch.tt2 b/Open-ILS/src/templates/opac-new/parts/homesearch.tt2
new file mode 100644 (file)
index 0000000..721c060
--- /dev/null
@@ -0,0 +1,4 @@
+<div id="homeLogo">
+    
+</div>
+[% PROCESS carousels %]
diff --git a/Open-ILS/src/templates/opac-new/parts/js.tt2 b/Open-ILS/src/templates/opac-new/parts/js.tt2
new file mode 100644 (file)
index 0000000..28b043d
--- /dev/null
@@ -0,0 +1,176 @@
+<!-- JS imports, etc.  -->
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/simple.js[% ctx.cache_key %]"></script>
+[% INCLUDE "opac/i18n_strings.tt2" %]
+
+[% IF ctx.want_jquery || ctx.readonly %]
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/common/build/js/jquery.min.js[% ctx.cache_key %]"></script>
+[% END; # ctx.want_jquery %]
+
+[% IF ctx.readonly %]
+<script type="text/javascript">
+  $(document).ready(function() {
+    $('a').removeAttr('href');
+    $('a').click(function(event) { event.preventDefault(); });
+    $('button').prop('disabled', true);
+    $('button').click(function(event) { event.preventDefault(); });
+    $('input').prop('disabled', true);
+    $('input').click(function(event) { event.preventDefault(); });
+    $('select').prop('disabled', true);
+    $('select').click(function(event) { event.preventDefault(); });
+    $('textarea').prop('disabled', true);
+    $('textarea').click(function(event) { event.preventDefault(); });
+  });
+</script>
+[% END %]
+
+<!-- DOB validation for Patron Registration in OPAC -->
+<script type="text/javascript">
+    function dobValidate(input) {
+        var validformat = /^(19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])$/
+        var returnval = false
+        if (!validformat.test(input.value))
+            alert("[% l('You have entered an invalid date, or an improperly formatted date.  Please enter Date of Birth in YYYY-MM-DD or YYYY/MM/DD format and try again.') %]")
+        else
+            returnval = true
+        if (returnval == false) input.select()
+        return returnval
+}
+</script>
+
+[%- IF ctx.use_stripe %]
+<script type="text/javascript">unHideMe($("pay_fines_now"));[%# the DOM is loaded now, right? %]</script>
+[% END -%]
+
+[%- IF ctx.is_staff %]
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/staff.js[% ctx.cache_key %]"></script>
+    [% IF ctx.page == 'record' %]
+        [% IF ctx.search_result_index >= 0 %]
+        <script>
+            rdetail_next_prev_actions(
+                "[% ctx.search_result_index %]",
+                "[% ctx.hit_count %]",
+                "[% ctx.prev_rec_url || '' %]",
+                "[% ctx.next_rec_url || '' %]",
+                "[% mkurl(ctx.first_search_record, {page => 0}) %]",
+                "[% mkurl(ctx.opac_root _ '/results', {find_last => 1, page => POSIX.floor(ctx.hit_count / ctx.page_size)}) %]",
+                "[% mkurl(ctx.opac_root _ '/results', {}, ['expand','cnoffset']) %]"
+            );
+        </script>
+        [% END %]
+        [% IF ctx.mfhd_summaries.size %]
+        <script>
+            window.mfhdDetails = [
+                [% FOR summary IN ctx.mfhd_summaries %]
+                    [% IF summary.sre_id != -1 %]
+                {
+                    'id' : '[% summary.sre_id %]', 
+                    'label' : '[% summary.location | replace("'", "\\'") %]',
+                    'entryNum' : [% loop.index %],
+                    'owning_lib' : '[% summary.owning_lib %]'
+                },
+                    [% END %]
+                [% END %]
+            ];
+        </script>
+        [% END %]
+    [% END %]
+[%- END %]
+
+[%- IF ENV.OILS_NOVELIST_URL AND ctx.page == 'record';
+    url = ENV.OILS_NOVELIST_URL;
+    IF CGI.https; url = url.replace('^http:', 'https:'); END; %]
+<script type='text/javascript' id='EIT' src='[% url %]'></script>
+[%- END %]
+[%- IF ctx.page == 'record' AND ctx.google_books_preview %]
+<script src='[% ctx.media_prefix %]/js/ui/default/opac/ac_google_books.js[% ctx.cache_key %]' async defer></script>
+[%- END %]
+
+<script>
+    window.egStrings = [];
+    window.egStrings['CONFIRM_BASKET_EMPTY'] = "[% l('Remove all records from basket?') %]";
+</script>
+<script src='[% ctx.media_prefix %]/js/ui/default/opac/record_selectors.js[% ctx.cache_key %]' async defer></script>
+
+<!-- Require some inputs and selections for browsers that don't support required form field element -->
+[% IF ctx.page == 'place_hold' %]
+  <script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/holds-validation.js[% ctx.cache_key %]">
+  </script>
+[% END %]
+
+[%- IF want_dojo; -%]
+<script type="text/javascript">
+     var djConfig = {
+         locale: "[% ctx.locale | lower | replace('_', '-') %]",
+         parseOnLoad: true,
+         isDebug: false
+     }, lang, bidi;
+</script>
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/dojo/dojo.js?[% ctx.eg_cache_hash %]"></script>
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/dojo/openils_dojo.js?[% ctx.eg_cache_hash %]"></script>
+
+[%- # So the following works in Mozilla and Chrome, but not in IE8.
+    # Seems like it /should/ work anywhere, though, and obviate the
+    # next three script tags: %]
+<!-- <script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/DojoSRF.js?[% ctx.eg_cache_hash %]"></script> -->
+
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/opensrf/opensrf.js?[% ctx.eg_cache_hash %]"></script>
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/opensrf/opensrf_xhr.js?[% ctx.eg_cache_hash %]"></script>
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/opensrf/JSON_v1.js?[% ctx.eg_cache_hash %]"></script>
+
+[% IF use_autosuggest.enabled == "t" AND basic_search != "f"; %]
+<script type="text/javascript">
+    dojo.require("openils.widget.AutoSuggest");
+
+    /* Set focus, and place the cursor at the end of the input string */
+    dojo.addOnLoad(function() {
+        /* Don't error out if the object doesn't exist, like on advanced search pages */
+        if (dojo.byId('search_box')) {
+            dijit.byId('search_box').focus();
+
+            var sb_value = dijit.byId('search_box').value;
+            /* Dojo won't trigger a change if the value doesn't change */
+            if (sb_value) {
+                dijit.byId('search_box').setValue(sb_value + ' ');
+                dijit.byId('search_box').setValue(sb_value);
+            }
+        }
+    });
+</script>
+[% END; # use_autosuggest %]
+
+[% INCLUDE "opac/parts/acjs.tt2" IF ctx.page == 'record' %]
+[% IF ctx.page == 'advanced' %]
+<script type="text/javascript" 
+    src="[% ctx.media_prefix %]/js/ui/default/opac/copyloc.js[% ctx.cache_key %]"></script>
+[% END %]
+
+[% IF ebook_api.enabled == 'true' %]
+    [% INCLUDE "opac/parts/ebook_api/base_js.tt2" %]
+    [% INCLUDE "opac/parts/ebook_api/login_js.tt2" IF (ctx.page == 'login') %]
+[% END %]
+
+<!-- provide a JS friendly org unit hash -->
+<script type="text/javascript">
+var aou_hash = {
+[% FOR org_unit IN ctx.aou_list %]
+    [% org_unit.id %] : {
+        id : "[% org_unit.id %]",
+        name : "[% org_unit.name | replace('"', '\"') %]",
+        parent_ou : "[% org_unit.parent_ou %]",
+        depth : "[% org_unit.ou_type.depth %]",
+        can_have_vols : "[% org_unit.ou_type.can_have_vols %]"
+    }[%- ',' UNLESS loop.last -%]
+[% END %]
+};
+</script>
+
+<script type="text/javascript">if ($('client_tz_id')) { $('client_tz_id').value = OpenSRF.tz }</script>
+[%- END; # want_dojo -%]
+
+[%- IF ctx.max_cart_size; %]
+<script type="text/javascript">var max_cart_size = [% ctx.max_cart_size %];</script>
+[%- END; %]
+
+
+<link rel="stylesheet" href="[% ctx.media_prefix %]/js/ui/default/common/build/js/glide/css/glide.core.min.css[% ctx.cache_key %]">
+<link rel="stylesheet" href="[% ctx.media_prefix %]/js/ui/default/common/build/js/glide/css/glide.theme.min.css[% ctx.cache_key %]">
diff --git a/Open-ILS/src/templates/opac-new/parts/library/core_info.tt2 b/Open-ILS/src/templates/opac-new/parts/library/core_info.tt2
new file mode 100644 (file)
index 0000000..cdaf047
--- /dev/null
@@ -0,0 +1,56 @@
+[%-
+    opac_root = ctx.opac_root;
+    IF ctx.kpac_root;
+        opac_root = ctx.kpac_root;
+    END;
+-%]
+<div id="content-wrapper">
+    <div id="main-content" class="content-wrapper-library-page" vocab="http://schema.org/" typeof="Library">
+    <h1 property="name">[% ctx.library.name | html %]</h1>
+
+    [%-
+        lib_url = ctx.get_org_setting(ctx.library.id, 'lib.info_url');
+        IF lib_url;
+            '<div id="library-url"><a href="'; lib_url | html; '" property="url">'; l('Library web site'); '</a></div>';
+        END;
+    -%]
+
+    [%- IF ctx.hours; %]
+        [%- INCLUDE "opac/parts/library/hours.tt2"; %]
+    [% END; -%]
+
+    [%- IF (ctx.library.email OR ctx.library.phone); %]
+    <h2 id="contact-info">[% l('Contact information') %]</h2>
+    [%- IF ctx.library.email; %]
+        <div>[% l('Email address: ') %]<a href="mailto:[% ctx.library.email | html %]" property="email">[% ctx.library.email | html %]</a></div>
+    [%- END; %]
+    [%- IF ctx.library.phone; %]
+        <div>[% l('Telephone: ') %]<a href="tel:[% ctx.library.phone | html %]" property="telephone">[% ctx.library.phone | html %]</a></div>
+    [% END; %]
+    [% END; %]
+
+    [%- IF ctx.library.mailing_address; %]
+    <div id="addresses">
+        <div id="mailing" property="location address" typeof="PostalAddress">
+            <h3 property="contactType">[% l('Mailing address') %]</h3>
+            <span property="streetAddress">[% ctx.mailing_address.street1 | html %]
+            [%- IF ctx.mailing_address.street2; "<br />"; ctx.mailing_address.street2 | html; END; %]
+            </span><br />
+            <span property="addressLocality">[% ctx.mailing_address.city | html %]</span><br />
+            <span property="addressRegion">[% ctx.mailing_address.state | html %]</span><br />
+            <span property="addressCountry">[% ctx.mailing_address.country | html %]</span><br />
+            <span property="postalCode">[% ctx.mailing_address.post_code | html %]</span><br />
+        </div>
+    </div>
+    [%- END; %]
+
+    [%- IF ctx.library.parent_ou; %]
+    <h2>[% l('Branch relationship') %]</h2>
+    <div id="branch-info">[% l('Parent library: ') %]
+        <a property="branchOf" href="[% mkurl(opac_root _ '/library/' _ ctx.parent.shortname, {}, 1) %]">[% ctx.parent.name | html %]</a>
+    </div>
+    [%  END; -%]
+
+    <div class="common-full-pad"></div>
+    </div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/library/hours.tt2 b/Open-ILS/src/templates/opac-new/parts/library/hours.tt2
new file mode 100644 (file)
index 0000000..d0bf5b9
--- /dev/null
@@ -0,0 +1,103 @@
+[%
+   UNLESS TIME_FORMAT;
+       TIME_FORMAT = '%l:%M %p';
+   END;
+
+   time_format = TIME_FORMAT;
+
+   USE date (format = time_format);
+   today = date.format(format = '%Y-%m-%d ');
+   # We need to add "today" to the opening/closing hours for Date input
+-%]
+<h2>[% l('Opening hours') %]</h2>
+
+[%-
+    open = today _ ctx.hours.dow_0_open;
+    close = today _ ctx.hours.dow_0_close;
+    IF open == close;
+%]
+<div class="opening-hours">[% l('Monday: closed') %]</div>
+[%- ELSE %]
+<div class="opening-hours" property="openingHoursSpecification" typeof="OpeningHoursSpecification"><link property="dayOfWeek" href="http://purl.org/goodrelations/v1#Monday" />[%
+    l('Monday: [_1] - [_2]', '<time property="opens" content="' _ date.format(open, format => '%H:%M:%S') _ '">' _ date.format(open) _ '</time>',
+     '<time property="closes" content="' _ date.format(close, format => '%H:%M:%S') _ '">' _ date.format(close) _ '</time>') -%]
+</div>
+[%- END %]
+
+[%-
+    open = today _ ctx.hours.dow_1_open;
+    close = today _ ctx.hours.dow_1_close;
+    IF open == close;
+%]
+<div class="opening-hours">[% l('Tuesday: closed') %]</div>
+[%- ELSE %]
+<div class="opening-hours" property="openingHoursSpecification" typeof="OpeningHoursSpecification"><link property="dayOfWeek" href="http://purl.org/goodrelations/v1#Tuesday" />[%
+    l('Tuesday: [_1] - [_2]', '<time property="opens" content="' _ date.format(open, format => '%H:%M:%S') _ '">' _ date.format(open) _ '</time>',
+     '<time property="closes" content="' _ date.format(close, format => '%H:%M:%S') _ '">' _ date.format(close) _ '</time>') -%]
+</div>
+[%- END %]
+
+[%-
+    open = today _ ctx.hours.dow_2_open;
+    close = today _ ctx.hours.dow_2_close;
+    IF open == close;
+%]
+<div class="opening-hours">[% l('Wednesday: closed') %]</div>
+[%- ELSE %]
+<div class="opening-hours" property="openingHoursSpecification" typeof="OpeningHoursSpecification"><link property="dayOfWeek" href="http://purl.org/goodrelations/v1#Wednesday" />[%
+    l('Wednesday: [_1] - [_2]', '<time property="opens" content="' _ date.format(open, format => '%H:%M:%S') _ '">' _ date.format(open) _ '</time>',
+     '<time property="closes" content="' _ date.format(close, format => '%H:%M:%S') _ '">' _ date.format(close) _ '</time>') -%]
+</div>
+[%- END %]
+
+[%-
+    open = today _ ctx.hours.dow_3_open;
+    close = today _ ctx.hours.dow_3_close;
+    IF open == close;
+%]
+<div class="opening-hours">[% l('Thursday: closed') %]</div>
+[%- ELSE %]
+<div class="opening-hours" property="openingHoursSpecification" typeof="OpeningHoursSpecification"><link property="dayOfWeek" href="http://purl.org/goodrelations/v1#Thursday" />[%
+    l('Thursday: [_1] - [_2]', '<time property="opens" content="' _ date.format(open, format => '%H:%M:%S') _ '">' _ date.format(open) _ '</time>',
+     '<time property="closes" content="' _ date.format(close, format => '%H:%M:%S') _ '">' _ date.format(close) _ '</time>') -%]
+</div>
+[%- END %]
+
+[%-
+    open = today _ ctx.hours.dow_4_open;
+    close = today _ ctx.hours.dow_4_close;
+    IF open == close;
+%]
+<div class="opening-hours">[% l('Friday: closed') %]</div>
+[%- ELSE %]
+<div class="opening-hours" property="openingHoursSpecification" typeof="OpeningHoursSpecification"><link property="dayOfWeek" href="http://purl.org/goodrelations/v1#Friday" />[%
+    l('Friday: [_1] - [_2]', '<time property="opens" content="' _ date.format(open, format => '%H:%M:%S') _ '">' _ date.format(open) _ '</time>',
+     '<time property="closes" content="' _ date.format(close, format => '%H:%M:%S') _ '">' _ date.format(close) _ '</time>') -%]
+</div>
+[%- END %]
+
+[%-
+    open = today _ ctx.hours.dow_5_open;
+    close = today _ ctx.hours.dow_5_close;
+    IF open == close;
+%]
+<div class="opening-hours">[% l('Saturday: closed') %]</div>
+[%- ELSE %]
+<div class="opening-hours" property="openingHoursSpecification" typeof="OpeningHoursSpecification"><link property="dayOfWeek" href="http://purl.org/goodrelations/v1#Saturday" />[%
+    l('Saturday: [_1] - [_2]', '<time property="opens" content="' _ date.format(open, format => '%H:%M:%S') _ '">' _ date.format(open) _ '</time>',
+     '<time property="closes" content="' _ date.format(close, format => '%H:%M:%S') _ '">' _ date.format(close) _ '</time>') -%]
+</div>
+[%- END %]
+
+[%-
+    open = today _ ctx.hours.dow_6_open;
+    close = today _ ctx.hours.dow_6_close;
+    IF open == close;
+%]
+<div class="opening-hours">[% l('Sunday: closed') %]</div>
+[%- ELSE %]
+<div class="opening-hours" property="openingHoursSpecification" typeof="OpeningHoursSpecification"><link property="dayOfWeek" href="http://purl.org/goodrelations/v1#Sunday" />[%
+    l('Sunday: [_1] - [_2]', '<time property="opens" content="' _ date.format(open, format => '%H:%M:%S') _ '">' _ date.format(open) _ '</time>',
+     '<time property="closes" content="' _ date.format(close, format => '%H:%M:%S') _ '">' _ date.format(close) _ '</time>') -%]
+</div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/library_name_link.tt2 b/Open-ILS/src/templates/opac-new/parts/library_name_link.tt2
new file mode 100644 (file)
index 0000000..c3d26f3
--- /dev/null
@@ -0,0 +1,27 @@
+[%-
+    opac_root = ctx.opac_root;
+    IF ctx.kpac_root;
+        opac_root = ctx.kpac_root;
+    END;
+
+  # Allow fleshed circ_libs
+    IF copy_info.circ_lib.name; 
+       org_id = copy_info.circ_lib.id;
+       org_name = copy_info.circ_lib.name; 
+       org_sname = copy_info.circ_lib.shortname; 
+    ELSE;
+       org_id = copy_info.circ_lib;
+       org_name = ctx.get_aou(org_id).name;
+       org_sname = ctx.get_aou(org_id).shortname;
+    END;
+
+     lib_url = ctx.get_org_setting(org_id, 'lib.info_url');
+    prefer_external_url = ctx.get_org_setting(org_id, 'lib.prefer_external_url');
+    UNLESS lib_url && prefer_external_url;
+        lib_url = mkurl(opac_root _ '/library/' _ org_sname, {}, 1);
+    END; 
+    IF lib_url; '<a target="_blank" property="offeredBy" typeof="Library" href="'; lib_url | html; '">'; END;
+    '<span property="name">'; org_name | html; '</span>';
+    IF lib_url; '</a>'; END;
+-%]
+
diff --git a/Open-ILS/src/templates/opac-new/parts/library_name_link_from_ou.tt2 b/Open-ILS/src/templates/opac-new/parts/library_name_link_from_ou.tt2
new file mode 100644 (file)
index 0000000..54e085b
--- /dev/null
@@ -0,0 +1,22 @@
+[%-
+    opac_root = ctx.opac_root;
+    IF ctx.kpac_root;
+        opac_root = ctx.kpac_root;
+    END;
+
+    # Requires a "fleshed_ou" (aou) object defined.
+    # Allow fleshed circ_libs
+    org_id = fleshed_ou.id;
+    org_name = fleshed_ou.name; 
+    org_sname = fleshed_ou.shortname;
+
+    lib_url = ctx.get_org_setting(org_id, 'lib.info_url');
+    prefer_external_url = ctx.get_org_setting(org_id, 'lib.prefer_external_url');
+    UNLESS lib_url && prefer_external_url;
+        lib_url = mkurl(opac_root _ '/library/' _ org_sname, {}, 1);
+    END; 
+    IF lib_url; '<a property="offeredBy" typeof="Library" href="'; lib_url | html; '">'; END;
+    '<span property="name">'; org_name | html; '</span>';
+    IF lib_url; '</a>'; END;
+-%]
+
diff --git a/Open-ILS/src/templates/opac-new/parts/locale_picker.tt2 b/Open-ILS/src/templates/opac-new/parts/locale_picker.tt2
new file mode 100644 (file)
index 0000000..87cdce7
--- /dev/null
@@ -0,0 +1,31 @@
+[%- IF ctx.locales.keys.size > 1;
+
+    # Ensure the context locale is in xx_yy format
+    base_locale = ctx.locale FILTER lower;
+    base_locale = base_locale.replace('-','_');
+
+    set_locale = CGI.param('set_eg_locale') || CGI.cookie('eg_locale') || base_locale;
+%]
+<form id="locale_picker_form" class="row mx-2" action="[% mkurl() %]" method="post">
+    <label for="locale_picker" class="color_4 light_border col-3 text-left">[% l("Language:") %]</label>
+    [%- FOREACH param IN CGI.params(); -%]
+        [%- NEXT IF param.key == 'set_eg_locale'; -%]
+        <input type="hidden" name="[% param.key | html %]" value="[% param.value | html %]" />
+    [%- END; -%]
+   
+    <select id="locale_picker" name="set_eg_locale" class="form-control form-control-sm col-6">
+    [%- FOREACH locale IN ctx.locales.keys %]
+        [%- IF set_locale == locale;
+                selected = 'selected="selected"';
+            ELSE;
+                selected = '';
+            END;
+        %]
+        <option value="[% locale | html %]" [% selected %]>[% ctx.locales.$locale | html %]</option>
+    [%- END %]
+    </select>
+    <div class="col-3">
+     <input type="submit" class="btn btn-sm btn-confirm float-right" value="[% l("Change") %]" />
+    </div>
+</form>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/location_name_link.tt2 b/Open-ILS/src/templates/opac-new/parts/location_name_link.tt2
new file mode 100644 (file)
index 0000000..06df235
--- /dev/null
@@ -0,0 +1,14 @@
+[%-
+    opac_root = ctx.opac_root;
+    IF ctx.kpac_root;
+        opac_root = ctx.kpac_root;
+    END;
+
+    loc_name = copy_info.copy_location;
+    loc_url = copy_info.location_url;
+
+    IF loc_url; '<a property="offeredBy" typeof="Library" href="'; loc_url | html; '">'; END;
+    '<span property="name">'; loc_name | html; '</span>';
+    IF loc_url; '</a>'; END;
+-%]
+
diff --git a/Open-ILS/src/templates/opac-new/parts/login/form.tt2 b/Open-ILS/src/templates/opac-new/parts/login/form.tt2
new file mode 100644 (file)
index 0000000..999c69b
--- /dev/null
@@ -0,0 +1,67 @@
+
+
+<div id='login-form-box' class='container'>
+    <h2 class="h2 text-center">[% l('Log in to Your Account') %]</h1>
+    <form method='post'>
+   <div class="row text-center">[% INCLUDE "opac/parts/login/help.tt2" %]</div>
+    [% IF ctx.login_failed_event %]
+<div id='login-failed-message'>
+[%
+    IF ctx.login_failed_event.textcode == 'PATRON_CARD_INACTIVE';
+        l("The barcode used to login is marked as inactive.  <br>Please contact your local library.");
+    ELSIF ctx.login_failed_event.textcode == 'PATRON_INACTIVE';
+        l("This account has been deactivated.  <br>Please contact your local library.");
+    ELSE;
+        l("Login failed. <br> The username or password provided was not valid. <br>" _
+           "Passwords are case-sensitive.  <br>Check your Caps-Lock key and try again or contact your local library.");
+    END;
+%]
+</div>
+[% END %]
+    
+    <div class="mx-auto row w-75 my-5">
+        <div class='col-md-6 text-center'>
+            <label for='username_field' class="lbl1" >[% l('Library Card Number') %]</label>
+            <div class="input_bg">
+                <input type='text' id="username_field" name="username" autofocus />
+            </div>
+            [% INCLUDE "opac/parts/login/username_hint.tt2" %]
+        </div>
+        <div class='col-md-6 text-center'>
+            <label for="password_field" class="lbl1" >[% l('PIN') %]</label>
+            <div class="input_bg">
+                <input id="password_field" name="password" type="password" />
+            </div>
+            [% INCLUDE "opac/parts/login/password_hint.tt2" %]
+            [% IF reset_password == 'true' %]
+            <a style="font-size: 80%" href='[% mkurl(ctx.opac_root _ '/password_reset', {}, 1) %]'>[% l('Forgot your password?') %]</a>
+            [% END %]
+        </div>
+         <div class="col-12 pt-4 text-center">
+            <input type="checkbox" name="persist" id="login_persist" /><label for="login_persist"> [% l('Stay logged in?') %]</label><br>
+            <input type="submit" value="[% l('Log in') %]" alt="[% l('Log in') %]" class="btn btn-confirm" />       
+        </div>
+        <div style="clear: both; padding-top: 15px;" class="col-12 text-center">
+        [%
+            redirect = CGI.param('redirect_to');
+            # Don't use referer unless we got here from elsewhere within the TPAC
+            IF !redirect AND ctx.referer.match('^https?://' _ ctx.hostname _ ctx.opac_root);
+                redirect = ctx.referer;
+            END;
+            # If no redirect is offered or it's leading us back to the
+            # login form, redirect the user to My Account
+            IF !redirect OR redirect.match(ctx.path_info _ '$');
+                redirect = CGI.url('-full' => 1) _ '/opac/myopac/main';
+            END;
+                redirect = redirect  | replace('^http:', 'https:');
+            %]
+            <input type='hidden' name='redirect_to' value='[% redirect | html %]'/>
+          
+        </div>
+        <input id="client_tz_id" name="client_tz" type="hidden" />
+        </div>
+    </form>
+    
+</div>
+
diff --git a/Open-ILS/src/templates/opac-new/parts/login/help.tt2 b/Open-ILS/src/templates/opac-new/parts/login/help.tt2
new file mode 100644 (file)
index 0000000..7fd77b3
--- /dev/null
@@ -0,0 +1,17 @@
+<div class="col-12">
+    <p>
+        [% l('Visit our FAQs section for answers to common questions about how to use your account.') %]
+    </p>
+</div>
+<div class="col-12 text-center">
+    <div class="mx-3 d-inline-block">
+        <a href="http://example.com" class="btn btn-confirm">[% l('Questions?') %]</a>
+    </div>
+    
+    <div class="mx-3 d-inline-block">
+        <div>
+            <a href="http://example.com" class="btn btn-confirm">[% l('FAQs') %]</a>
+           </div>
+    </div>
+
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/login/login_modal.tt2 b/Open-ILS/src/templates/opac-new/parts/login/login_modal.tt2
new file mode 100644 (file)
index 0000000..37c52e0
--- /dev/null
@@ -0,0 +1,85 @@
+<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
+  <div class="modal-dialog modal-dialog-centered" role="document">
+    <div class="modal-content">
+     <form method='post' action="/eg/opac/login?redirect_to=%2Feg%2Fopac%2Fmyopac%2Fmain">
+      <div class="modal-header">
+        <h5 class="modal-title" id="exampleModalLabel">Login To Evergreen</h5>
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+          <span aria-hidden="true">&times;</span>
+        </button>
+      </div>
+      <div class="modal-body">
+      
+       [% IF ctx.login_failed_event %]
+               <div id='login-failed-message'>
+               [%
+               IF ctx.login_failed_event.textcode == 'PATRON_CARD_INACTIVE';
+                       l("The barcode used to login is marked as inactive.  Please contact your local library.");
+               ELSIF ctx.login_failed_event.textcode == 'PATRON_INACTIVE';
+                       l("This account has been deactivated.  Please contact your local library.");
+               ELSE;
+                       l("Login failed. The username or password provided was not valid. " _
+                       "Passwords are case-sensitive.  Check your Caps-Lock key and try again or contact your local library.");
+               END;
+               %]
+               </div>
+               [% END %]
+
+<div id='login-form-box' class='container text-center'>
+    <div class="mx-auto row py-4">
+        <div class='col-md-6'>
+            <label for='username_field' class="lbl1" >[% l('Library Card Number') %]</label>
+            <div class="input_bg mb-2">
+                <input class="form-control" type='text' id="username_field" name="username" autofocus />
+            </div>
+            <div>[% l('Use the full number.') %]</div>
+            <div>[% l('Example: 280800001234567') %]</div>
+            <br>
+            <a href='[% mkurl(ctx.opac_root _ '/register', {}, 1) %]'>[% l('Request A Card') %]</a>
+        </div>
+        <div class='col-md-6'>
+            <label for="password_field" class="lbl1" >[% l('PIN') %]</label>
+            <div class="input_bg mb-2">
+                <input class="form-control" id="password_field" name="password" type="password" />
+            </div>
+            [% INCLUDE "opac/parts/login/password_hint.tt2" %]
+            <br>
+             [% IF reset_password == 'true' %]
+            <a href='[% mkurl(ctx.opac_root _ '/password_reset', {}, 1) %]'>[% l('Forgot Your Password?') %]</a>
+            [% END %]
+        </div>
+        <div style="clear: both; padding-top: 15px;" class="col-12">
+        [%
+            redirect = CGI.param('redirect_to');
+            # Don't use referer unless we got here from elsewhere within the TPAC
+            IF !redirect AND ctx.referer.match('^https?://' _ ctx.hostname _ ctx.opac_root);
+                redirect = ctx.referer;
+            END;
+            # If no redirect is offered or it's leading us back to the
+            # login form, redirect the user to My Account
+            IF !redirect OR redirect.match(ctx.path_info _ '$');
+                redirect = CGI.url('-full' => 1) _ '/opac/myopac/main';
+            END;
+                redirect = redirect  | replace('^http:', 'https:');
+            %]
+        </div>
+       
+        <input id="client_tz_id" name="client_tz" type="hidden" />
+        </div>
+       </div>
+               [%# INCLUDE "opac/parts/login/help.tt2" %]
+
+      </div>
+      <div class="modal-footer">
+      <div class="mr-auto">
+       <input type='hidden' name='redirect_to' value='[% redirect | html %]'/>
+        <input type="checkbox" name="persist" id="login_persist" class="mr-1"/><label for="login_persist"> [% l('Stay logged in?') %]</label>
+      </div>
+       
+        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+        <input type="submit" value="[% l('Log in') %]" alt="[% l('Log in') %]" class="btn btn-confirm" />
+      </div>
+      </form>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/parts/login/password_hint.tt2 b/Open-ILS/src/templates/opac-new/parts/login/password_hint.tt2
new file mode 100644 (file)
index 0000000..3c39b05
--- /dev/null
@@ -0,0 +1,3 @@
+<div>
+[%- l('First time logging in?<br> Use the last 4 digits of your phone number. <br> Example: 0926'); %]
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/login/username_hint.tt2 b/Open-ILS/src/templates/opac-new/parts/login/username_hint.tt2
new file mode 100644 (file)
index 0000000..8c96277
--- /dev/null
@@ -0,0 +1,5 @@
+<div>
+[% l('Please include leading zeros.') %]
+<br>
+[% l('Example: 0026626051') %]
+</div>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/parts/metarecord_hold_filters.tt2 b/Open-ILS/src/templates/opac-new/parts/metarecord_hold_filters.tt2
new file mode 100644 (file)
index 0000000..b8b8772
--- /dev/null
@@ -0,0 +1,77 @@
+[%#
+Draws the format multi-select and the language multi-select for
+limiting the set of desired records for a given metarecord.
+%]
+
+<style>
+  /* The rules has been moved to style.css  */
+</style>
+
+[% BLOCK metarecord_hold_filters_selector;
+    # in edit mode, pull the target from the existing hold
+    target_id = hold_data.hold.hold.target || hold_data.target.id;
+
+    selected_formats = {};
+    selected_langs = {};
+    FOR fmt IN hold_data.metarecord_selected_filters.formats;
+        code = fmt.code;
+        selected_formats.$code = fmt;
+    END;
+    FOR lang IN hold_data.metarecord_selected_filters.langs;
+        code = lang.code;
+        selected_langs.$code = lang;
+    END;
+%]
+
+<div class="metarecord_filters">
+  <div class="metarecord_filter_container">
+    <div class="metarecord_filter_header">
+      <div>[% l('Select your desired format(s).') %]</div>
+      <div>[% l('If none are selected, any available formats may be used.') %]</div>
+    </div>
+    <select multiple='multiple' class="form-control"
+      name="metarecord_formats_[% target_id %]">
+      [% FOR ccvm IN
+          hold_data.metarecord_filters.formats.sort('search_label');
+          NEXT IF ccvm.opac_visible == 'f' %]
+        <option value="[% ccvm.code %]"[%- code = ccvm.code; 
+            IF selected_formats.$code %] selected='selected'[% END -%]>
+          [% ccvm.search_label | html %]
+        </option>
+      [% END %]
+    </select>
+  </div>
+  [% IF hold_data.metarecord_filters.langs.size;
+        my_lang = ctx.get_i18n_l(ctx.eg_locale).marc_code;
+  %]
+  <div class="metarecord_filter_container">
+    <div class="metarecord_filter_header">
+      <div>[% l('Select your desired language(s).') %]</div>
+      <div>[% l('If none are selected, any available languages may be used.') %]</div>
+    </div>
+    <select multiple='multiple' class="form-control"
+      name="metarecord_langs_[% target_id %]">
+      [% FOR lang_ccvm IN hold_data.metarecord_filters.langs.sort('value');
+            NEXT IF lang_ccvm.opac_visible == 'f';
+            selected = 0; 
+            code = lang_ccvm.code;
+            IF selected_langs.size;
+                # user has already selected their preferred language(s)
+                SET selected = 1 IF selected_langs.$code;
+
+            ELSIF !hold_data.hold.hold.target; # new hold
+                # no prefered language selected; use current locale
+                SET selected = 1 IF code == my_lang;
+            END;
+        %]
+        <option value="[% lang_ccvm.code %]"[%- 
+            IF selected %] selected='selected'[%- END %]>
+          [% lang_ccvm.value | html %]
+        </option>
+      [% END %]
+    </select>
+  </div>
+  [% END %]
+  <div class="clear-both">&nbsp;</div>
+</div>
+[% END # metarecord_hold_filters_selector %]
diff --git a/Open-ILS/src/templates/opac-new/parts/misc_util.tt2 b/Open-ILS/src/templates/opac-new/parts/misc_util.tt2
new file mode 100644 (file)
index 0000000..1a9e683
--- /dev/null
@@ -0,0 +1,868 @@
+[%- 
+    # Support multiscript records via alternate graphic 880 fields
+    # get_graphic_880s(target_field='100')
+    # See "Model A" in http://www.loc.gov/marc/bibliographic/ecbdmulti.html
+    # and $6 description in http://www.loc.gov/marc/bibliographic/ecbdcntf.html
+    MACRO get_graphic_880s BLOCK;
+        FOR node IN xml.findnodes('//*[@tag="' _ target_field _ '"]');
+            raw_vals = [];
+            core_val = '';
+            FOR subnode IN node.findnodes('./*[not(contains("e w 0 4 5 6 7 8 9", @code))]');
+                raw_vals.push(subnode.textContent());
+            END;
+            core_val = raw_vals.join(" ");
+            raw_vals = [];
+
+            linked_fields = [];
+            FOR sub IN node.findnodes('./*[@code="6"]');
+                linked_fields.push(sub.textContent);
+            END;
+            graphics = [];
+            get_linked_880s;
+            graphic_880s.push({
+                primary => {"occur" => occurrence, "value" => core_val},
+                graphic => graphics
+            });
+        END;
+    END;
+
+    MACRO get_linked_880s BLOCK;
+        FOR link_field IN linked_fields;
+            target = target_field _ link_field.substr(3);
+            # Get the linked 880 value
+            raw_val = '';
+            dir = '';
+            occurrence = '';
+            script = '';
+            FOR node IN xml.findnodes('//*[@tag="880"]');
+                # Operate only on the target linked fields
+                FOR linknode IN node.findnodes('./*[@code="6"]');
+                    lf = linknode.textContent();
+                    IF lf.substr(0, target.length) == target;
+                        occurrence = lf.substr(4, 2);
+                        rawscript = lf.substr(7, 2);
+                        SWITCH rawscript;
+                        CASE '(3';
+                            script = 'Arabic';
+                        CASE '(B';
+                            script = 'Latin';
+                        CASE '$1';
+                            script = 'CJK';
+                        CASE '(N';
+                            script = 'Cyrillic';
+                        CASE '(S';
+                            script = 'Greek';
+                        CASE '(2';
+                            script = 'Hebrew';
+                        END;
+
+                        rawdir = lf.substr(9, 1);
+                        SWITCH rawdir;
+                        CASE 'r';
+                            dir = 'rtl';
+                        END;
+
+                        raw_vals = [];
+                        FOR subnode IN node.findnodes('./*[not(contains("w 0 5 6 8 9", @code))]');
+                            raw_vals.push(subnode.textContent());
+                        END;
+                        raw_val = raw_vals.join(" ");
+                    END;
+                END;
+            END;
+            graphics.push({
+                occur => occurrence,
+                value => raw_val,
+                script => script,
+                dir => dir
+            });
+        END;
+    END;
+
+    BLOCK get_ccvm_icon;
+        ctx.media_prefix _ '/images/format_icons/' _ ccvm.ctype _ '/' _ ccvm.code _ '.png' _ ctx.cache_key;
+    END;
+
+    # Extract MARC fields from XML
+    #   get_marc_attrs( args = { marc_xml => doc } )
+    BLOCK get_marc_attrs;
+        USE Dumper;
+        xml = args.marc_xml;
+
+        args.bibid = [];
+        FOR bibid IN xml.findnodes('//*[@tag="901"]/*[@code="c"]');
+            args.bibid.push(bibid.textContent);
+        END;
+
+        args.df_bib_list = args.bibid;
+        args.bibid = args.bibid.0; 
+
+        IF args.mr_constituent_ids.size && !args.df_bib_list.size;
+            args.df_bib_list = args.mr_constituent_ids;
+        END;
+
+
+        # Gather display field data for this record and map it
+        # to a display field map.  Hopefully, one day, this can
+        # replace the XPath below entirely.
+
+        args.display_fields = {};
+        args.hl = {};
+
+        args.hl_field = 'highlight';
+        IF CGI.param('no_highlight') || search.no_highlight;
+            args.hl_field = 'value';
+        END;
+        hl_field = args.hl_field;
+
+        junk = ctx.timelog('Fetching display fields and Using data from "' _ hl_field _ '" for rendering'); 
+        args.display_field_list = ctx.fetch_display_fields(args.df_bib_list.list);
+
+        junk = ctx.timelog('Mapping display fields for bib(s) ' _ args.df_bib_list.list.join(', ')); 
+        FOR df IN args.display_field_list;
+            df_map = ctx.search_cdfm('field', df.field).0;
+            df_name = df_map.name();
+            IF df_map.multi() == 't';
+                IF NOT args.hl_display_fields.$df_name;
+                    args.hl_display_fields.$df_name = [];
+                    args.hl.$df_name = [];
+                END;
+                args.hl_display_fields.$df_name.push(df);
+                args.hl.$df_name.push(df.$hl_field || df.value);
+            ELSIF !args.hl_display_fields.$df_name.defined;
+                args.hl_display_fields.$df_name = df;
+                args.hl.$df_name = df.$hl_field || df.value;
+            END;
+        END;
+        junk = ctx.timelog('Finished mapping display fields for bib(s) ' _ args.df_bib_list.list.join(', ')); 
+
+        # Map item types to schema.org types; impedance mismatch :(
+        args.schema.itemtype = {};
+        schema_typemap = {};
+
+        schema_typemap.bluray = 'Movie'; # Movie could also be TVSeries
+        schema_typemap.book = 'Book';
+        schema_typemap.braille = 'Book';
+        schema_typemap.casaudiobook = 'Book AudioObject';
+        schema_typemap.casmusic = 'MusicAlbum';
+        schema_typemap.cdaudiobook = 'Book AudioObject';
+        schema_typemap.cdmusic = 'MusicAlbum';
+        schema_typemap.dvd = 'Movie';
+        schema_typemap.eaudio = 'AudioObject';
+        schema_typemap.ebook = 'Book';
+        # schema_typemap.equip = '';
+        schema_typemap.evideo = 'Movie';
+        # schema_typemap.kit = '';
+        schema_typemap.lpbook = 'Book';
+        schema_typemap.map = 'Map';
+        # schema_typemap.microform = '';
+        schema_typemap.music = 'MusicAlbum';
+        schema_typemap.phonomusic = 'MusicAlbum';
+        # schema_typemap.phonospoken = '';
+        # schema_typemap.picture = ''; Painting or Photograph?
+        schema_typemap.score = 'Book'; # schema.org has no generic Music type
+        schema_typemap.serial = 'Periodical';
+        schema_typemap.software = 'SoftwareApplication';
+        schema_typemap.vhs = 'Movie';
+
+        schema_typemap.a = 'Book';
+        schema_typemap.e = 'Map';
+        schema_typemap.j = 'MusicAlbum';
+
+        # Hard-coded to match defaults in config.copy_status for all OPAC-visible statuses
+        schema_copy_status = {};
+        schema_copy_status.0 = '<link property="availability" href="http://schema.org/InStock" />'; # Available
+        schema_copy_status.1 = '<link property="availability" href="http://schema.org/OutOfStock" />'; # Checked out
+        schema_copy_status.5 = '<link property="availability" href="http://schema.org/PreOrder" />'; # In process
+        schema_copy_status.6 = '<link property="availability" href="http://schema.org/PreOrder" />'; # In transit
+        schema_copy_status.7 = '<link property="availability" href="http://schema.org/InStock" />'; # Reshelving
+        schema_copy_status.8 = '<link property="availability" href="http://schema.org/OutOfStock" />'; # On holds shelf
+        schema_copy_status.9 = '<link property="availability" href="http://schema.org/PreOrder" />'; # On order
+        schema_copy_status.12 = '<link property="availability" href="http://schema.org/InStoreOnly" />'; # Reserves
+
+        args.isbns = [];
+        FOR isbn IN xml.findnodes('//*[@tag="020"]/*[@code="a"]');
+            args.isbns.push(isbn.textContent);
+        END;
+
+        # UPCs can be identified either by ind1="1" or subfield 2 of "upc"
+        args.upcs = [];
+        FOR upc IN xml.findnodes('//*[@tag="024" and @ind="1"]/*[@code="a"]');
+            args.upcs.push(upc.textContent);
+        END;
+        FOR upcfield IN xml.findnodes('//*[@tag="024"]/*[@code="2" and text()="upc"]');
+            my_parent = upcfield.parentNode();
+            FOR upc IN my_parent.findnodes('./*[@code="a"]');
+                args.upcs.push(upc.textContent);
+            END;
+        END;
+        args.upc = args.upcs.0; # use first UPC as the default
+
+        # These are URIs that link out to related works for linked data purposes,
+        # such as OCLC Work Entities like http://worldcat.org/entity/work/id/415491
+
+        # We differentiate between http://schema.org/sameAs relations, which
+        # are roughly the same as the work we're describing here, and
+        # http://schema.org/exampleOfWork which point to a broader work, of which
+        # this is a more specific edition or manifestation.
+        args.links.sameAs = [];
+        args.links.exampleOfWork = [];
+        FOR link_node IN xml.findnodes('//*[@tag="024"]/*[@code="2" and text()="uri"]');
+            my_parent = link_node.parentNode();
+            FOR link IN my_parent.findnodes('./*[@code="a"]');
+                link_uri = link.textContent | trim;
+                IF link_uri.search('worldcat.org/entity/work');
+                    args.links.exampleOfWork.push(link_uri);
+                ELSE;
+                    args.links.sameAs.push(link_uri);
+                END;
+            END;
+        END;
+        FOR lccn IN xml.findnodes('//*[@tag="010"]/*[@code="a"]');
+            lccn_num = lccn.textContent | trim;
+            args.links.sameAs.push('http://lccn.loc.gov/' _ lccn_num);
+        END;
+        FOR oclcnum IN xml.findnodes('//*[@tag="035"]/*[@code="a"]');
+            oclcnum = oclcnum.textContent | trim;
+            NEXT UNLESS oclcnum.search('^\(OCoLC\)');
+            oclcnum = oclcnum | remove('\(OCoLC\)');
+            # These prefixes are often included in the 035, even though they should not be
+            oclcnum = oclcnum | remove('ocm');
+            oclcnum = oclcnum | remove('ocn');
+            oclcnum = oclcnum | remove('on') | trim;
+            args.links.sameAs.push('http://www.worldcat.org/oclc/' _ oclcnum);
+        END;
+        args.links.sameAs = args.links.sameAs.unique;
+
+        args.issns = [];
+        FOR sub IN xml.findnodes('//*[@tag="022"]/*[@code="a"]');
+            args.issns.push(sub.textContent);
+        END;
+        args.issn = (args.issns.size) ? args.issn.0 : '';
+
+        graphic_880s = [];
+        get_graphic_880s(target_field='100');
+        get_graphic_880s(target_field='110');
+        get_graphic_880s(target_field='111');
+        args.graphic_authors = graphic_880s;
+        args.authors = [];
+        FOR author IN args.graphic_authors;
+            args.authors.push(author.primary.value);
+        END;
+        args.author = (args.authors.size) ? args.authors.0 : '';
+
+        # Include subfields 'abnp' to generate a more comprehensive title display in search results
+        titresults = xml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b" or @code="n" or @code="p"]');
+        titresults_content = [];
+            FOR sub IN titresults; titresults_content.push(sub.textContent); END;
+
+        args.title = titresults_content.join(" ");
+        # Avoid ugly trailing syntax on brief titles
+        args.title = args.title | replace('[:;/]$', '');
+
+        graphic_880s = [];
+        get_graphic_880s(target_field='245');
+        args.graphic_titles = graphic_880s;
+        args.titles = [];
+        FOR title IN args.graphic_titles;
+            args.titles.push(title.primary.value);
+        END;
+        args.title_extended = (args.titles.size) ? args.titles.0 : '';
+
+        # Create a version of the title designed for sorted displays.
+        args.sort_title = args.title | upper;
+
+        # If the title has a "non-filing chaaracters" 
+        # (to logically remove leading "The " for example)
+        # chop the title. Otherwise, chop until the first alphanumeric.
+        # BTW: Template Toolkit folds 1-element arrays to scalars!
+        title_node = xml.findnodes('//*[@tag="245"]');
+
+        args.nonfiling_characters = title_node.findvalue('@ind2');
+      
+        IF (args.nonfiling_characters > 0);
+             args.sort_title = args.sort_title.substr(args.nonfiling_characters); 
+        ELSE;
+             args.sort_title = args.sort_title.replace('^[^A-Z0-9]*','');
+        END;
+      
+        args.pubplaces = [];
+        pubplace_hunt = xml.findnodes('//*[@tag="260"]/*[@code="a"]') ||
+            xml.findnodes('//*[@tag="264" and @ind2="1"]/*[@code="a"]');
+        FOR sub IN pubplace_hunt;
+            args.pubplaces.push(sub.textContent);
+        END;
+        args.pubplace = (args.pubplaces.size) ? args.pubplaces.0 : '';
+
+        args.publishers = [];
+        publishers_hunt = xml.findnodes('//*[@tag="260"]/*[@code="b"]') ||
+            xml.findnodes('//*[@tag="264" and @ind2="1"]/*[@code="b"]');
+        FOR sub IN publishers_hunt;
+            args.publishers.push(sub.textContent);
+        END;
+        args.publisher = (args.publishers.size) ? args.publishers.0 : '';
+
+        args.pubdates = [];
+        pubdates_hunt = xml.findnodes('//*[@tag="260"]/*[@code="c"]') || 
+            xml.findnodes('//*[@tag="264" and @ind2="1"]/*[@code="c"]');
+        FOR sub IN pubdates_hunt;
+            args.pubdates.push(sub.textContent);
+        END;
+        args.pubdate = (args.pubdates.size) ? args.pubdates.0 : '';
+
+        # Get RDA Copyright Info.
+        args.copyrights = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="4"]/*[@code="c"]');
+            args.copyrights.push(sub.textContent);
+        END;
+        args.copyright = (args.copyrights.size) ? args.copyrights.0 : '';
+
+        IF args.copyright.length >= 4;
+            args.copyrightYear = args.copyright.match('(\d{4})');
+            IF args.copyrightYear;
+                args.copyrightYear = args.copyrightYear.0;
+            END;
+        END;
+
+        # Get the RDA Production info.
+        args.producers = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="0"]/*[@code="b"]');
+            args.producers.push(sub.textContent);
+        END;
+        args.producer = (args.producers.size) ? args.producers.0 : '';
+
+        args.prodplaces = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="0"]/*[@code="a"]');
+            args.prodplaces.push(sub.textContent);
+        END;
+        args.prodplace = (args.prodplaces.size) ? args.prodplaces.0 : '';
+
+        args.proddates = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="0"]/*[@code="c"]');
+            args.proddates.push(sub.textContent);
+        END;
+        args.proddate = (args.proddates.size) ? args.proddates.0 : '';
+
+        # Get the RDA Distribution args.
+        args.distributors = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="2"]/*[@code="b"]');
+            args.distributors.push(sub.textContent);
+        END;
+        args.distributor = (args.distributors.size) ? args.distributors.0 : '';
+
+        args.distplaces = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="2"]/*[@code="a"]');
+            args.distplaces.push(sub.textContent);
+        END;
+        args.distplace = (args.distplaces.size) ? args.distplaces.0 : '';
+
+        args.distdates = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="2"]/*[@code="c"]');
+            args.distdates.push(sub.textContent);
+        END;
+        args.distdate = (args.distdates.size) ? args.distdates.0 : '';
+
+        # Get the RDA Manufacture args.
+        args.manufacturers = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="3"]/*[@code="b"]');
+            args.manufacturers.push(sub.textContent);
+        END;
+        args.manufacturer = (args.manufacturers.size) ? args.manufacturers.0 : '';
+
+        args.manplaces = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="3"]/*[@code="a"]');
+            args.manplaces.push(sub.textContent);
+        END;
+        args.manplace = (args.manplaces.size) ? args.manplaces.0 : '';
+
+        args.mandates = [];
+        FOR sub IN xml.findnodes('//*[@tag="264" and @ind2="3"]/*[@code="c"]');
+            args.mandates.push(sub.textContent);
+        END;
+        args.mandate = (args.mandates.size) ? args.mandates.0 : '';
+
+        # RDA adds 264 to the pubinfo 880 consideration mix
+        graphic_880s = [];
+        get_graphic_880s(target_field='260');
+        get_graphic_880s(target_field='264');
+        args.graphic_pubinfos = graphic_880s;
+        args.pubinfos = [];
+        FOR pubinfo IN args.graphic_pubinfos;
+            args.pubinfos.push(pubinfo.primary.value);
+        END;
+        args.pubinfo = (args.pubinfos.size) ? args.pubinfos.0 : '';
+
+        args.summaries = [];
+        FOR sub IN xml.findnodes('//*[@tag="520"]/*[@code="a"]');
+            args.summaries.push(sub.textContent);
+        END;
+        args.summary = (args.summaries.size) ? args.summaries.0 : '';
+
+        # 250 gets pride of place for edition statement, and is the
+        # only logical choice for 880 graphic fields
+        graphic_880s = [];
+        get_graphic_880s(target_field='250');
+        args.graphic_editions = graphic_880s;
+        args.editions = [];
+        FOR edition IN args.graphic_editions;
+            args.editions.push(edition.primary.value);
+        END;
+
+        ed_hunt = xml.findnodes('//*[@tag="250"]/*[@code="a"]');
+        FOR sub IN ed_hunt;
+            args.editions.push(sub.textContent);
+        END;
+        args.edition = (args.editions.size) ? args.editions.0 : '';
+
+        phys_content = [];
+        FOR sub IN xml.findnodes(
+            '//*[@tag="300"]/*[@code="a" or @code="b" or @code="c" or @code="e"]'
+        );
+            phys_content.push(sub.textContent);
+        END;
+        args.phys_desc = phys_content.join(" ");
+
+        graphic_880s = [];
+        get_graphic_880s(target_field='505');
+        args.graphic_contents = graphic_880s;
+        FOR content IN args.graphic_contents;
+            args.contents.push(content.primary.value);
+        END;
+        args.content = (args.contents.size) ? args.contents.0 : '';
+
+        # Maintain contents_list in case any custom use was made of it
+        args.contents_list = [];
+        FOR sub IN xml.findnodes('//*[@tag="505"]');
+            args.contents_list.push(sub.textContent);
+        END;
+
+        # MARC Callnumber
+        args.marc_cns = [];
+        FOR sub IN xml.findnodes('//*[@tag="092" or @tag="099"]/*');
+            args.marc_cns.push(sub.textContent);
+        END;
+        args.marc_cn = (args.marc_cns.size ) ? args.marc_cns.0 : '';
+            
+
+        # clean up the ISBN
+        args.isbn_clean = args.isbns.0.replace('\ .*', '');
+        FOR isbn IN args.isbns;
+            clean_isbn = isbn.replace('\ .*', '');
+            clean_isbn = clean_isbn.replace('-', '');
+            IF clean_isbn.length == 13;
+                args.gtin13 = clean_isbn;
+                LAST;
+            END;
+        END;
+
+        IF ebook_api.overdrive.enabled == 'true';
+            FOR marc037 IN xml.findnodes('//*[@tag="037"]');
+                marc037_id = marc037.findnodes('./*[@code="a"]').textContent;
+                marc037_source = marc037.findnodes('./*[@code="b"]').textContent;
+                IF marc037_source.match('OverDrive') AND marc037_id;
+                    args.overdrive_id = marc037_id;
+                    LAST;
+                END;
+            END;
+        END;
+
+        # Extract the 856 URLs that are not otherwise represented by asset.uri's
+        args.online_res = [];
+        FOR node IN xml.findnodes('//*[@tag="856" and @ind1="4" and (@ind2="0" or @ind2="1")]');
+            IF node.findnodes('./*[@code="9" or @code="w" or @code="n"]'); NEXT; END; # asset.uri's
+            label = node.findnodes('./*[@code="y"]');
+            notes = node.findnodes('./*[@code="z" or @code="3"]');
+            FOR href IN node.findnodes('./*[@code="u"]');
+                NEXT UNLESS href;
+                # it's possible for multiple $u's to exist within 1 856 tag.
+                # in that case, honor the label/notes data for the first $u, but
+                # leave any subsequent $u's as unadorned href's. 
+                # use href/link/note keys to be consistent with args.uri's
+                args.online_res.push({
+                    href => href.textContent, 
+                    link => (loop.first AND label) ? label.textContent : href.textContent,
+                    note => (loop.first) ? notes.textContent : ''
+                });
+            END;
+        END;
+        args.holdings = [];
+        args.uris = [];
+        args.issns = [];
+        args.resolver_isbns = [];
+        args.resolver_issns = [];
+
+        # we use $9 of ISBN and ISSN as a flag for e-version
+        FOR resolver_isbn IN xml.findnodes('//*[@tag="020"]/*[@code="9"]');
+            IF resolver_isbn.textContent == "SFX" || resolver_isbn.textContent == "CUFTS";
+                my_parent = resolver_isbn.parentNode();
+                FOR resolver_isbn_val IN my_parent.findnodes('./*[@code="a"]');
+                    args.resolver_isbns.push(
+                        resolver_isbn_val.textContent.replace('-', '').replace('\ .*', '')
+                    );
+                END;
+            END;
+        END;
+
+        FOR resolver_issn IN xml.findnodes('//*[@tag="022"]/*[@code="9"]');
+            IF resolver_issn.textContent == "SFX" || resolver_issn.textContent == "CUFTS";
+                my_parent = resolver_issn.parentNode();
+                FOR resolver_issn_val IN my_parent.findnodes('./*[@code="a"]');
+                    args.resolver_issns.push(
+                        resolver_issn_val.textContent.replace('[^\d\-X]', '')
+                    );
+                END;
+            END;
+        END;
+
+        # now snag all issns 
+        FOR rawissn IN xml.findnodes('//*[@tag="022"]/*[@code="a"]');
+            args.issns.push(
+                rawissn.textContent.replace('[^\d\-X]', '')
+            );
+        END;
+
+        ou_hiding_disabled = ctx.org_hiding_disabled();
+
+        # explicitly grabbing the first bib_source node, though there should be only one anyway
+        bib_source = xml.findnodes('//*[local-name()="bib_source"][1]');
+        args.bib_source.id = bib_source.getAttribute('ident');
+        args.bib_source.source = bib_source.textContent;
+
+        FOR volume IN xml.findnodes('//*[local-name()="volumes"]/*[local-name()="volume"]');
+
+            # Check volume visibility - could push this into XPath
+            vol.label = volume.getAttribute('label');
+
+            # Prepend prefix, if any
+            prefix = volume.findnodes('./*[local-name()="call_number_prefix"][@ident!="-1"]');
+            IF prefix.getAttribute('label') != '';
+                vol.label = prefix.getAttribute('label') _ " " _ vol.label;
+            END;
+
+            # Append prefix, if any
+            suffix = volume.findnodes('./*[local-name()="call_number_suffix"][@ident!="-1"]');
+            IF suffix.getAttribute('label') != '';
+                vol.label = vol.label _ " " _ suffix.getAttribute('label');
+            END;
+
+            vol.id = volume.getAttribute('id');
+            NEXT IF volume.getAttribute('opac_visible') == 'false';
+            NEXT IF volume.getAttribute('deleted') == 'true';
+
+            IF vol.label == '##URI##';
+                FOR uri IN volume.findnodes('./*[local-name()="uris"]/*[local-name()="uri"]');
+                    res = {};
+                    res.href = uri.getAttribute('href');
+                    res.link = uri.getAttribute('label');
+                    res.note = uri.getAttribute('use_restriction');
+                    # Avoid displaying the href as the link text if we can display the note instead
+                    IF res.link == res.href AND res.note;
+                        res.link = res.note;
+                        res.note = '';
+                    END;
+                    args.uris.push(res);
+
+                    IF ebook_api.ebook_test.enabled == 'true';
+                        IF !args.ebook_test_id;
+                            FOR base_uri IN ebook_api.ebook_test.base_uris;
+                                IF res.href.match(base_uri);
+                                    args.ebook_test_id = res.href.remove(base_uri);
+                                    LAST;
+                                END;
+                            END;
+                        END;
+                    END;
+
+                    IF ebook_api.oneclickdigital.enabled == 'true';
+                        # A record might conceivably have multiple OneClickdigital URIs,
+                        # but we use (the same) ISBN as the ebook ID in each case.
+                        IF !args.oneclickdigital_id;
+                            FOR base_uri IN ebook_api.oneclickdigital.base_uris;
+                                IF res.href.match(base_uri);
+                                    # found a OneClickdigital URI, let's grab our ID and move on
+                                    args.oneclickdigital_id = clean_isbn;
+                                    LAST;
+                                END;
+                            END;
+                        END;
+                    END;
+
+                    IF ebook_api.overdrive.enabled == 'true';
+                        # Ideally we already have an OverDrive record ID from MARC 037 (see above).
+                        # But for older records, it will be embedded in the URI in MARC 856.
+                        IF !args.overdrive_id;
+                            FOR base_uri IN ebook_api.overdrive.base_uris;
+                                IF res.href.match(base_uri);
+                                    args.overdrive_id = res.href.remove('^.*/ContentDetails.htm\?ID=');
+                                    LAST;
+                                END;
+                            END;
+                        END;
+                    END;
+                END;
+
+                NEXT;
+            ELSE;
+                copies = volume.findnodes('./*[local-name()="copies"]/*[local-name()="copy"]');
+                FOR copy IN copies;
+                    parts = copy.findnodes('./*[local-name()="monograph_parts"]/*[local-name()="monograph_part"]');
+                    part_label = '';
+                    FOREACH part IN parts;
+                        part_label = part.getAttribute('label');
+                        LAST IF part_label != '';
+                    END;
+                    # Check copy visibility
+                    cp.deleted = copy.getAttribute('deleted');    
+                    cp.visible = copy.getAttribute('opac_visible');
+                    NEXT IF (cp.deleted == 'true' OR cp.visible == 'false');
+
+                    # Iterate through all of the children to determine visibility
+                    FOR node IN cp.childNodes;
+                        NEXT IF cp.visible == 'false';
+                        vis = node.getAttribute('opac_visible');
+                        del = node.getAttribute('deleted');
+                        IF vis == 'false' or del == 'true';
+                            cp.visible = 'false';
+                        END;
+                    END;
+
+                    NEXT IF cp.visible == 'false';
+                    
+                    loc = copy.findnodes('./*[local-name()="location"]');
+                    NEXT IF loc.getAttribute('opac_visible') == 'false';
+
+                    circlib = copy.findnodes('./*[local-name()="circlib"]');
+                    NEXT IF circlib.getAttribute('opac_visible') == 'false';
+
+                    status = copy.findnodes('./*[local-name()="status"]');
+                    NEXT IF status.getAttribute('opac_visible') == 'false';
+
+                    # 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('.*/', '');
+
+                    UNLESS ou_hiding_disabled;
+                        NEXT UNLESS ctx.org_within_hiding_scope(circ_lib_id);
+                    END;
+
+                    holding = {
+                        circ_lib => circ_lib_id,
+                        label => vol.label,
+                        part_label => part_label,
+                        location => loc.textContent,
+                        library => circlib.textContent,
+                        status => status.textContent,
+                        status_code => status.getAttribute('ident'),
+                        barcode => copy.getAttribute('barcode'),
+                        owner => volume.getAttribute('lib')
+                    };
+                    args.holdings.push(holding);
+                    part_label = '';
+                END;
+            END;
+        END;
+
+        # Extract the copy count summary
+        count_type = (ctx.is_staff) ? 'staff' : 'public';
+
+        # Consortial copy count summary first
+        xpath = '//*[local-name()="counts"]/*[local-name()="count"][@type="' _ count_type _ '"]';
+        args.copy_counts = {};
+        FOR node IN xml.findnodes(xpath);
+            FOR attr IN ['count', 'available', 'unshadow', 'transcendant', 'org_unit']; 
+                depth = node.getAttribute('depth');
+                count_org_unit = node.getAttribute('org_unit');
+                args.copy_counts.$depth.$attr = node.getAttribute(attr);
+                args.org_copy_counts.$count_org_unit.$attr = node.getAttribute(attr);
+            END;
+        END;
+
+        # Get preferred library copy count
+        args.plib_copy_counts = {};
+        count_type = 'pref_lib';
+        xpath = '//*[local-name()="counts"]/*[local-name()="count"][@type="' _ count_type _ '"]';
+        FOR node IN xml.findnodes(xpath);
+            FOR attr IN ['count', 'available', 'unshadow', 'transcendant', 'org_unit']; 
+                depth = node.getAttribute('depth');
+                args.plib_copy_counts.$depth.$attr = node.getAttribute(attr);
+            END;
+        END;
+
+        mmr_unique_bib = [];
+        # "mattype" == "custom marc format specifier"
+        icon_style = ctx.get_cgf('opac.icon_attr').value || 'item_type';
+        formats_xpath = '//*[local-name()="attributes"]/*[local-name()="field"][@name="' _ icon_style _ '"]';
+
+        args.all_formats = [];
+        FOR node IN xml.findnodes(formats_xpath);
+            IF node AND node.textContent;
+                ccvm = ctx.get_ccvm(node.getAttribute('cvmid'));
+                NEXT IF ccvm.opac_visible == 'f';
+
+                format = {};
+                this_icon_source = node.getAttribute('source_list');
+                including = 'F';
+                # Just display everything if we don't have the data
+                IF NOT args.mr_constituent_ids OR NOT this_icon_source;
+                    including = 'T';
+                # We have an array of search-included bib IDs and we have the bib ID that this icon belongs to
+                ELSE;
+                    FOR mr_constituent_id IN args.mr_constituent_ids;
+                        IF this_icon_source.split(',').grep('^' _ mr_constituent_id _ '$' ).size;
+                            # This bib appears to be in the array of filtered bibs
+                            including = 'T';
+                        END;
+                    END;
+                END;
+                IF including == 'T';
+                    type = ccvm.code.remove('-'); # blu-ray to bluray
+                    format.label = ccvm.search_label || ccvm.value;
+                    format.icon = PROCESS get_ccvm_icon ccvm=ccvm;
+                    format.itemtype = schema_typemap.$type || 'CreativeWork';
+                    format.search_format = ccvm.code;
+                    format.source_bibs = this_icon_source.split(',');
+                    FOR bib_source IN format.source_bibs;
+                        IF NOT mmr_unique_bib.grep(bib_source).size; mmr_unique_bib.push(bib_source); END;
+                    END;
+                    args.all_formats.push(format); # metarecords want all formats
+
+                    IF !args.format_label;
+                        # use the first format as the default
+                        args.format_label = format.label;
+                        args.schema.itemtype = format.itemtype;
+                        args.format_icon = format.icon;
+                    END;
+                END;
+            END;
+        END;
+
+        formats_xpath = '//*[local-name()="attributes"]/*[local-name()="field"][@name="item_lang"]';
+        args.all_lang = [];
+        FOR node IN xml.findnodes(formats_xpath);
+            IF node AND node.textContent;
+                ccvm = ctx.get_ccvm(node.getAttribute('cvmid'));
+                NEXT IF ccvm.opac_visible == 'f';
+
+                lang = {};
+                this_source = node.getAttribute('source_list');
+                including = 'F';
+                # Just display everything if we don't have the data
+                IF NOT args.mr_constituent_ids OR NOT this_source;
+                    including = 'T';
+                # We have an array of search-included bib IDs and we have the bib ID that this lang belongs to
+                ELSE;
+                    FOR mr_constituent_id IN args.mr_constituent_ids;
+                        IF this_source.split(',').grep('^' _ mr_constituent_id _ '$' ).size;
+                            # This bib appears to be in the array of filtered bibs
+                            including = 'T';
+                        END;
+                    END;
+                END;
+                IF including == 'T';
+                    lang.label = ccvm.search_label || ccvm.value;
+                    lang.itemtype = schema_typemap.$type || 'CreativeWork';
+                    lang.search_format = node.textContent;
+                    lang.source_bibs = this_source.split(',');
+                    FOR bib_source IN lang.source_bibs;
+                        IF NOT mmr_unique_bib.grep(bib_source).size; mmr_unique_bib.push(bib_source); END;
+                    END;
+
+                    args.all_lang.push(lang); # metarecords want all
+                END;
+            END;
+        END;
+        args.mmr_unique_bib = mmr_unique_bib;
+
+        IF args.ebook_test_id;
+            args.ebook.ebook_id = args.ebook_test_id;
+            args.ebook.vendor = 'ebook_test';
+        ELSIF args.oneclickdigital_id;
+            args.ebook.ebook_id = args.oneclickdigital_id;
+            args.ebook.vendor = 'oneclickdigital';
+        ELSIF args.overdrive_id;
+            args.ebook.ebook_id = args.overdrive_id;
+            args.ebook.vendor = 'overdrive';
+        END;
+
+    END;
+
+    # Get the library or location group
+    # get_library()
+    # magically upgrades any use of 'loc' to 'locg', 
+    # which is a superset of 'loc'.
+    BLOCK get_library;
+        loc_name = 'locg';
+
+        # Location groups don't have shortnames, so they'll take priority
+        # (_org doesn't contain the group and breaks the selector).
+        loc_value = ctx.copy_location_group ? CGI.param(loc_name)
+                  : ctx.copy_location_group_org # resolved locg
+                    || CGI.param(loc_name) || CGI.param('loc') || ctx.search_ou;
+    END;
+
+-%]
+
+[%- BLOCK carousels;
+    config = {
+        animated => 0,
+        animation_interval => 5,
+        width => 4,
+    };
+    config.import(args);
+    carousels = [];
+    IF config.carousel_id;
+        carousels.push(ctx.get_carousel(config.carousel_id));
+    ELSE;
+        visible_carousels = ctx.get_visible_carousels();
+        FOREACH car IN visible_carousels;
+            carousel = ctx.get_carousel(car.carousel);
+            IF car.override_name.defined;
+                carousel.name = car.override_name;
+            END;
+            carousels.push(carousel);
+        END; 
+    END;
+    IF carousels.size > 0;
+%]
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/common/build/js/glide/glide.min.js[% ctx.cache_key %]"></script>
+<div class="carousels">
+[% FOREACH carousel IN carousels;
+   IF carousel.bibs.size > 0;
+%]
+<div class="carousel">
+<div class="carousel-title">[% carousel.name %]</div>
+<div class="carousel-body glide" id="carousel-[% carousel.id %]">
+  <div class="glide__track" data-glide-el="track">
+    <ul class="glide__slides">
+[% FOREACH bib IN carousel.bibs; %]
+      <li class="glide__slide"><div class="carousel-entry">
+        <a href="[% mkurl(ctx.opac_root  _ '/record/' _ bib.id) %]">
+          <img alt="[% l('Book cover') %]" class="carousel-entry-image" src="[% ctx.media_prefix %]/opac/extras/ac/jacket/medium/r/[% bib.id | uri %]"></img>
+        </a><br>
+        <span class="carousel-entry-title"><a href="[% mkurl(ctx.opac_root  _ '/record/' _ bib.id) %]">[% bib.title | html %]</a></span>
+      </div></li>
+[% END; %]
+    </ul>
+  </div>
+  <div class="glide__arrows" data-glide-el="controls">
+    <button class="glide__arrow glide__arrow--left btn btn-confirm" data-glide-dir="<">prev</button>
+    <button class="glide__arrow glide__arrow--right btn btn-confirm" data-glide-dir=">">next</button>
+  </div>
+</div>
+</div>
+<script>
+    var glide = new Glide('#carousel-[% carousel.id %]', {
+        type: 'carousel',
+        rewind: false,
+        perView: [% config.width %],
+        startAt: 0,
+        [% IF config.animated %] autoplay: [% config.animation_interval * 1000 %],[% END %]
+    });
+    glide.mount();
+</script>
+[% END; %]
+[% END; %]
+</div>
+[% END -%]
+[% END -%]
diff --git a/Open-ILS/src/templates/opac-new/parts/multi_hold_select.tt2 b/Open-ILS/src/templates/opac-new/parts/multi_hold_select.tt2
new file mode 100644 (file)
index 0000000..020245b
--- /dev/null
@@ -0,0 +1,15 @@
+[%  # Check if we need to do anything.
+    hold_type = CGI.param('hold_type');
+    max_holds = ctx.get_org_setting(ctx.default_pickup_lib, 'circ.holds.max_duplicate_holds');
+    can_dup = ctx.has_perm('CREATE_DUPLICATE_HOLDS', ctx.default_pickup_lib);
+    IF ctx.hold_data.size == 1 && (hold_type == 'M' || hold_type == 'T') && max_holds && max_holds > 1 && can_dup;
+%]
+<p>
+<label for="num_copies">[% l('Number of copies') %]</label>
+<select id="num_copies" name="num_copies" title="[% l('Number of copies') %]">
+[% FOR num IN [1..max_holds] %]
+<option value="[% num %]">[% num %]</option>
+[% END %]
+</select>
+</p>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/myopac/base.tt2 b/Open-ILS/src/templates/opac-new/parts/myopac/base.tt2
new file mode 100644 (file)
index 0000000..d457ef3
--- /dev/null
@@ -0,0 +1,76 @@
+[%  WRAPPER "opac/parts/base.tt2" %]
+
+[% myopac_pages = [
+        {url => "main", name => l("Account Summary")},
+        {url => "messages", name => l("Messages")},
+        {url => "circs", name => l("Items Checked Out")},
+        {url => "holds", name => l("Holds")},
+        {url => "prefs", name => l("Account Preferences")},
+        {url => "lists", name => l("My Lists")}
+    ];
+     IF (ctx.show_reservations_tab == 'true');
+        myopac_pages.push({url => "reservations", name => l("Reservations")});
+    END;
+    skin_root = "../"
+%]
+    <h2 class="sr-only">[% l('My Account') %]</h2>
+    [% INCLUDE "opac/parts/topnav.tt2" %]
+    <div class="mobile_hide">
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    </div>
+
+
+    <div class="container my-3 min">
+        <div id="myopac_tabs">
+               <ul class="nav nav-tabs nav-justified d-none d-lg-flex">
+                [%- FOREACH page IN myopac_pages;
+                    IF page.url == myopac_page;
+                        cls_which = "active";
+                        ctx.page_title = l("My Account - [_1]", page.name);
+                    ELSE;
+                        cls_which = "";
+                    END -%]<li class="nav-item">
+                <a href="[% mkurl(ctx.opac_root _ '/myopac/' _ page.url, {}, ['bbid', 'offset', 'limit','sort','sort_type', 'hid']) %]"
+                    class="[% cls_which %] nav-link">[% page.name; %]</a></li>
+                [% END %]
+                       
+                       </ul>
+            <!-- Select box navigation for media view -->
+            <div class="mx-auto py-3 d-block d-lg-none">
+               <form name="acct_nav">
+                 <select name="acct_url" class="form-control"
+                    title="[% l('Select an account page') %]" 
+                    onchange="window.location.href=document.acct_nav.acct_url.options[document.acct_nav.acct_url.selectedIndex].value">
+                    <option value="" selected="selected">[% l('Go to...') %]</option>
+                    [% FOREACH page IN myopac_pages;
+                      IF page.url != myopac_page; %]
+                        <option value="[% mkurl(ctx.opac_root _ '/myopac/' _ page.url, {}, ['bbid', 'offset', 'limit', 'hid']) %]">[% page.name; %]</option>
+                    [% ELSE; %]
+                        <option value="" disabled>[% page.name; %]</option>
+                    [% END;
+                   END; %] 
+                 </select>
+                   </form>
+            </div>
+           </div><!-- myopac_tabs -->
+        <div id="main-content" class="p-3">
+            [% content %]
+            <div class="common-full-pad"></div>
+        </div>
+    </div> <!-- content-wrapper -->
+    
+    <!--Table functions ~ Controlling TH checkbox dynamically and allowing for row clicks to select items -->
+  <script>
+  jQuery('td input:checkbox').change(function(){
+   jQuery('th input:checkbox').prop('checked',jQuery('td input:checkbox:checked').length == jQuery('td input:checkbox').length);
+});
+
+jQuery( document ).ready(function( $ ) {
+    $('tr').click(function(event) {
+        if (event.target.type !== 'checkbox') {
+            $(':checkbox', this).trigger('click');
+        }
+    });  
+});
+</script>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/myopac/column_sort_support.tt2 b/Open-ILS/src/templates/opac-new/parts/myopac/column_sort_support.tt2
new file mode 100644 (file)
index 0000000..6d5ba47
--- /dev/null
@@ -0,0 +1,36 @@
+[%# Produce a URL for a given field that cycles for sorting from 
+    "nothing" to "ascending" to "descending" then back to "nothing".
+%]
+[% MACRO sort_url(field)
+        IF (CGI.param('sort') == field);
+                SWITCH CGI.param('sort_type');
+                CASE "asc";
+                   mkurl('',{sort=>field, sort_type=>'desc'},1);
+                CASE "desc";
+                   mkurl('',{sort=>field, sort_type=>'asc'}, 1);
+                END;
+       ELSE;
+           mkurl('',{sort=>field, sort_type=>'asc'}, 1);
+       END;
+%]
+[%# SET click_sort = l("click to sort") %]
+[%# SET click_sort = "title=\"$click_sort\"" %]
+
+[%# Produce arrows to indicate the sorting status of the column %]
+[% MACRO sort_indicator(field)
+        IF (CGI.param('sort') == field);
+                SWITCH CGI.param('sort_type');
+                CASE "asc";
+"&nbsp;<span class=\"column_sort_arrow\">&#9650;</span>";
+                CASE "desc";
+"&nbsp;<span class=\"column_sort_arrow\">&#9660;</span>";
+                END;
+        END;
+%]
+
+[%# Column headers for sortable columns %]
+[% MACRO sort_head(field, field_label) 
+   BLOCK %]
+<a href="[% sort_url(field) %]" [% click_sort %]>[% l(field_label) %]</a>[%- sort_indicator(field) %]
+[% END
+%]
diff --git a/Open-ILS/src/templates/opac-new/parts/myopac/main_base.tt2 b/Open-ILS/src/templates/opac-new/parts/myopac/main_base.tt2
new file mode 100644 (file)
index 0000000..97eb895
--- /dev/null
@@ -0,0 +1,91 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+
+     IF date.format(ctx.parse_datetime(ctx.user.expire_date), '%s') < date.format(date.now , '%s');
+        fmt_expire_date = date.format(ctx.parse_datetime(ctx.user.expire_date), DATE_FORMAT);
+        ctx.expired_card = "true";
+     END;
+%]
+
+<div id='myopac_summary_div'>
+    <div class="header_middle">[% l('Account Summary') %]</div>
+
+    <div>
+        <div id="acct_sum_block" class="container">
+            <div class="py-2 text-center">
+            </div>
+            <table class="acct_sum_table mx-auto" title="[% l('Account Summary') %]">
+                <tr>
+                    <td colspan="3" class="text-center">[% l("Account Expiration Date - ") %]
+                        <span [% IF ctx.expired_card %]style="color:red;"[% END %]>
+                            [% date.format(ctx.parse_datetime(ctx.user.expire_date), DATE_FORMAT) %]
+                        </span>
+                        [% IF ctx.expired_card %]
+                        <span>
+                            <em>
+                            [% l("<br>Your library card has expired.<br>Please contact a librarian to resolve this issue.", fmt_expire_date) %]
+                            </em>
+                        </span>
+                        [% END %]
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" class="text-center"> <a href="[% mkurl(ctx.opac_root _ '/myopac/circs') %]"
+                            title="[% l('View My Checked Out Items') %]">
+                            [% l("Items Currently Checked out ([_1])", ctx.user_stats.checkouts.total_out) %]
+                        </a>
+                    </td>
+                    <td class="td-right hidden" id="acct_sum_ebook_circs">
+                        <a href="[% mkurl(ctx.opac_root _ '/myopac/ebook_circs') %]"
+                            title="[% l('View My Checked Out E-Items') %]">
+                            [% l("E-Items Currently Checked out") %] (<span id="acct_sum_ebook_circ_total">-</span>)
+                        </a>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" class="text-center"><a href="[% mkurl(ctx.opac_root _ '/myopac/holds') %]"
+                            title="[% l('View My Holds') %]">
+                            [% l('Items Currently on Hold ([_1])', ctx.user_stats.holds.total) %]
+                        </a></td>
+                    <td class="td-right hidden" id="acct_sum_ebook_holds">
+                        <a href="[% mkurl(ctx.opac_root _ '/myopac/ebook_holds') %]"
+                            title="[% l('View My E-Items On Hold') %]">
+                            [% l("E-Items Currently on Hold") %] (<span id="acct_sum_ebook_hold_total">-</span>)
+                        </a>
+                    </td>
+                </tr>
+                <tr>
+                    <td colspan="2" class="text-center"> <a href="[% mkurl(ctx.opac_root _ '/myopac/holds', {available => 1}) %]"
+                            title="[% l('View My Holds Ready for Pickup') %]">
+                            [% l('Items ready for pickup ([_1])', ctx.user_stats.holds.ready) %]
+                        </a></td>
+                    
+                    <td class="td-right hidden" id="acct_sum_ebook_holds_ready">
+                        <a href="[% mkurl(ctx.opac_root _ '/myopac/ebook_holds_ready') %]"
+                            title="[% l('View My E-Items Ready for Pickup') %]">
+                            [% l("E-Items ready for pickup") %] (<span id="acct_sum_ebook_hold_ready_total">-</span>)
+                        </a>
+                    </td>
+                </tr>
+            </table>
+        </div>
+    </div>
+    <div class="clear-both"></div>
+
+    <div class="mt-4">
+        <div>
+                       <ul class="nav nav-tabs">
+            [% IF myopac_main_page == 'main' %]
+            <li class="nav-item"><a href='#' class="nav-link active">[% l("Charges") %]</a> </li>
+            <li class="nav-item"><a href='[% mkurl(ctx.opac_root _ '/myopac/main_payments') %]' class="nav-link">[% l("Payments") %]</a> </li>
+            [% ELSE %]
+            <li class="nav-item"><a href='[% mkurl(ctx.opac_root _ '/myopac/main') %]' class="nav-link">[% l("Charges") %]</a> </li>
+            <li class="nav-item"><a href='#' class="nav-link active">[% l("Payments") %]</a> </li>
+            [% END %]
+             </ul>
+        </div>
+    </div>
+       [% content %]
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/myopac/main_refund_policy.tt2 b/Open-ILS/src/templates/opac-new/parts/myopac/main_refund_policy.tt2
new file mode 100644 (file)
index 0000000..d020722
--- /dev/null
@@ -0,0 +1,18 @@
+<tr>
+    <td colspan="3">
+        <br />
+        [% l('Important! You must have a printed receipt ' _
+             'to be eligible for a refund on lost items ' _
+             '(regulations allow for no exceptions).')
+        %]
+        <br />
+        <strong>
+        [% l('To ensure your necessary receipt information ' _
+             'is not lost, enter your email address above ' _
+             'and a receipt will be emailed to you. Otherwise, ' _
+             'make certain you have a printed receipt in hand ' _
+             'before closing the payment receipt screen.')
+        %]
+        </strong>
+    </td>
+</tr>
diff --git a/Open-ILS/src/templates/opac-new/parts/myopac/prefs_base.tt2 b/Open-ILS/src/templates/opac-new/parts/myopac/prefs_base.tt2
new file mode 100644 (file)
index 0000000..0ca9b64
--- /dev/null
@@ -0,0 +1,56 @@
+[%  PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2"; %]
+
+        [% acct_prefs_pages = [
+           {url => "prefs", name => l("Personal Information")},
+           {url => "prefs_notify", name => l("Notification Preferences")},
+           {url => "prefs_settings", name => l("Search and History Preferences")},
+           {url => "prefs_my_lists", name => l("My Lists Preferences")}
+           ];
+           skin_root = "../"
+%]
+
+<div id='myopac_prefs_div'>
+    <div>
+        <ul class="nav nav-pills nav-justified">
+            [%- FOREACH page IN acct_prefs_pages;-%]
+            <li class="nav-item">
+                 <a class="m-2 nav-link [%-  IF page.url == prefs_page; -%]active [% ELSE %] not_active_pill [%- END -%]" href="[% mkurl(ctx.opac_root _ '/myopac/' _ page.url, {}, ['bbid', 'offset', 'limit']) %]">[% page.name; %]</a>
+            </li>
+            [% END %]
+         </ul>
+    </div>
+
+    <div class="clear-both"></div> <br/>
+
+    <div class="header_middle">
+        <span id="acct_prefs_header">[% l('Account Information and Preferences') %]</span>
+        <span class="float-right"> 
+            <a class="hide_me" href="#">[% l('Export List') %]</a><!-- what does this do? -->
+        </span>
+    </div>
+
+    [%- # Select box navigation for mobile view %]
+    <div id="acct_prefs_select" class="d-none">
+        <form name="acct_prefs_nav">
+            <select name="acct_prefs_url" 
+              title="[% l('Select a preference page') %]"
+              class="form-control"
+              onchange="window.location.href=document.acct_prefs_nav.acct_prefs_url.options[document.acct_prefs_nav.acct_prefs_url.selectedIndex].value">
+        [% FOREACH page IN acct_prefs_pages;
+            IF page.url != prefs_page; -%]
+            <option value="[% mkurl(ctx.opac_root _ '/myopac/' _ page.url, {}, ['bbid', 'offset', 'limit']) %]">[% page.name; %]</option>
+            [% ELSE; -%]
+            <option value="" disabled selected>[% page.name; %]</option>
+            [% END; -%]
+        [% END; -%]
+            </select>
+        </form>
+    </div>
+
+    <div class="clear-both normal-height"></div>
+
+[% content; %]
+
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/myopac/prefs_hints.tt2 b/Open-ILS/src/templates/opac-new/parts/myopac/prefs_hints.tt2
new file mode 100644 (file)
index 0000000..dc90c92
--- /dev/null
@@ -0,0 +1,5 @@
+<p>
+    [% l('Ensure your account has a valid email address so that we can notify ' _
+         'you about available holds, items that are about to be overdue, and ' _
+         'overdue items!') %]
+</p>
diff --git a/Open-ILS/src/templates/opac-new/parts/org_selector.tt2 b/Open-ILS/src/templates/opac-new/parts/org_selector.tt2
new file mode 100644 (file)
index 0000000..d37f99a
--- /dev/null
@@ -0,0 +1,134 @@
+[%
+# Org Unit Selector Widget :
+#   INCLUDE build_org_selector id='selector-id' name='selector-name' 
+#       value=org_id show_loc_groups=1/0 can_have_vols_only=1/0
+#       can_have_users_only=1/0 valid_org_list=[1,2,3]
+#
+# NOTE: DO NOT USE PROCESS
+# Use of PROCESS results in internal variables, such as value or org_unit, to "leak" out
+
+PROCESS "opac/parts/misc_util.tt2";
+PROCESS get_library;
+
+BLOCK build_org_selector;
+    node_stack = [{org => org_unit || ctx.aouct_tree || ctx.aou_tree}];
+    inherited_vis = ctx.get_cgf('opac.org_unit.non_inherited_visibility').enabled == 'f';
+
+    IF !name; 
+        name = loc_name;
+    END;
+    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 -%]
+            title='[% arialabel || l("Select Library") %]' 
+            name='[% name %]' class="form-control w-100">
+    [%- 
+        WHILE node_stack.size > 0; 
+            node = node_stack.pop();
+            org_unit = node.org;
+            loc_grp = node.loc_grp;
+            ou_id = org_unit.id;
+            css_class = '';
+            disabled = '';
+            selected = '';
+            visible = org_unit.opac_visible == 't';
+
+            # org and all children are invisible.
+            NEXT IF !visible AND inherited_vis AND !ctx.is_staff;
+
+            IF !loc_grp; # processing an org unit
+
+                top_loc_groups = [];
+                IF show_loc_groups;
+                    # add the location groups that sort below the child org units
+                    FOR grp IN ctx.copy_location_groups.$ou_id.reverse;
+                        IF grp.top == 't';
+                            top_loc_groups.push(grp);
+                        ELSE;
+                            node_stack.push({org => org_unit, loc_grp => grp});
+                        END;
+                    END;
+                END;
+
+                # add the child org units
+                FOR child IN org_unit.children.reverse;
+                    node_stack.push({org => child});
+                END;
+
+                # add the location groups that sort to the top
+                # above the child org units
+                FOR grp IN top_loc_groups;
+                    node_stack.push({org => org_unit, loc_grp => grp});
+                END;
+
+            END;
+
+            # 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;
+                node_value = node_value _ ':' _ loc_grp.id; 
+                css_class = 'class="loc_grp"';
+            ELSE;
+                css_class = 'class="org_unit"';
+            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; 
+
+            IF can_have_users_only AND org_unit.ou_type.can_have_users != 't';
+                disabled = 'disabled="disabled"';
+            ELSIF node_value == value;
+                selected = 'selected="selected"';
+            END; 
+
+            IF hold_pickup_lib AND ctx.get_org_setting(
+                    org_unit.id, 'opac.holds.org_unit_not_pickup_lib');
+                disabled = 'disabled="disabled"';
+            END;
+
+            # caller provided a list of valid org units.
+            # all orgs not in the list are marked invalid.
+            IF valid_org_list AND 
+                    !valid_org_list.grep('^' _ org_unit.id _ '$').size;
+                disabled = 'disabled="disabled"';
+                selected = '';
+            END;
+
+            pad_depth = 0;
+
+            # copy loc groups appear as children of the owning org unit
+            SET pad_depth = pad_depth + 1 IF loc_grp;
+
+            # determine the depth by calculating the distance from the root
+            porg = ctx.get_aou(org_unit.parent_ou);
+            WHILE porg;
+                SET pad_depth = pad_depth + 1 IF porg.opac_visible == 't' OR ctx.is_staff;
+                porg = ctx.get_aou(porg.parent_ou);
+            END;
+
+            pad_depth = pad_depth * 2;
+            display_name = loc_grp ? loc_grp.name : org_unit.name %]
+
+            <option value='[% node_value %]' [% selected %] [% disabled %] [% css_class %]> 
+                [% '&nbsp;' FOR [0..pad_depth]; display_name | html %]
+            </option> 
+
+        [%- END %]
+    </select>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/place_hold.tt2 b/Open-ILS/src/templates/opac-new/parts/place_hold.tt2
new file mode 100644 (file)
index 0000000..5db220e
--- /dev/null
@@ -0,0 +1,296 @@
+[%  PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_error_messages.tt2";
+    PROCESS "opac/parts/metarecord_hold_filters.tt2";
+%]
+<script>
+// Toggle the activation date input and check the suspend checkbox.
+// If JavaScript is disabled, the CSS will handle the former, but
+// the latter will not happen.
+function toggleActivationDate() {
+    var cb = document.getElementById("hold_suspend");
+    var block = document.getElementById("toggled-block-suspend");
+    var anchor = document.getElementById("actDateToggle");
+    var actText = "[%- l('Hide activation date') -%]";
+    var inActText = "[%- l('Set activation date') -%]";
+    // Check for not equal to block so it works on first click.
+    if (block.style.display != "block") {
+        block.style.display = "block";
+        anchor.innerHTML = actText;
+        if (cb.checked != true) cb.checked = true;
+    } else {
+        block.style.display = "none";
+        anchor.innerHTML = inActText;
+    }
+    // Prevent the href from being followed, thus overriding the CSS.
+    return false;
+}
+
+// Maybe enable or disable the num_copies select when the user selects
+// or deselects a part.
+function maybeToggleNumCopies(obj) {
+    var numCopies = document.getElementById("num_copies");
+    // Only if numCopies exists.
+    if (numCopies) {
+        var objValue;
+        if (obj.type == 'radio') {
+            if (obj.checked) objValue = obj.value;
+            else return;
+        } else {
+            objValue = obj.value;
+        }
+        if (objValue && objValue != '') {
+            if (numCopies.value != '1') numCopies.value = '1';
+            if (!numCopies.disabled) numCopies.disabled = true;
+        } else {
+            if (numCopies.disabled) numCopies.disabled = false;
+        }
+    }
+}
+</script>
+<div id='holds_box' class='canvas' style='margin-top: 6px;'>
+   <h1>[% l('Place Hold') %]</h1>
+
+    [% some_holds_allowed = -1 %]
+
+    <!-- loop through the holds and display status of request where appropriate -->
+        [% FOR hdata IN ctx.hold_data;
+            attrs = {marc_xml => hdata.marc_xml};
+            PROCESS get_marc_attrs args=attrs;
+            this_hold_disallowed = 0;
+
+            IF CGI.param('hold_type') == 'M';
+              IF hdata.metarecord_filters.formats.size == 0;
+                this_hold_disallowed = 1;
+                # if this is the first hold and it's disallowed,
+                # assume all holds are, until we proven otherwise
+                SET some_holds_allowed = 0 IF some_holds_allowed == -1;
+              ELSE; some_holds_allowed = 1; END;
+            END %]
+
+       [% IF loop.first %] 
+    <form method="post" name="PlaceHold" onsubmit="return validateHoldForm()" >
+        <input type="hidden" name="hold_type" value="[% CGI.param('hold_type') | html %]" />
+        [%  
+            redirect = CGI.param('hold_source_page') || CGI.param('redirect_to') || CGI.referer;
+            # since we have to be logged in to get this far, return to a secure page
+            redirect = redirect.replace('^http:', 'https:') 
+        %]
+        <input type="hidden" name="redirect_to" value="[% redirect | html %]" />
+        <input type="hidden" name="hold_source_page" value="[% CGI.param('hold_source_page') | html %]" />
+
+        <!-- Adding hidden fields so that parameters are maintained in
+        searchbar throughout the place hold process. -->
+        <input type="hidden" name="locg" value="[% CGI.param('locg') | html %]" />
+        <input type="hidden" name="qtype" value="[% CGI.param('qtype') | html %]" />
+        <input type="hidden" name="query" value="[% CGI.param('query') | html %]" />
+        [%
+            usr_barcode = CGI.param('usr_barcode') | html;
+            is_requestor = CGI.param('is_requestor');
+
+           IF is_requestor == '';
+               is_requestor = '0';
+           END;
+
+           IF is_requestor == '0' && usr_barcode == ctx.staff_recipient.card.barcode;
+               usr_barcode = '';
+           END;
+        %]
+
+        [% 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"
+                />
+            <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" 
+              aria-label="[% l('Barcode') %]"
+              value="[% usr_barcode | html %]" 
+              onpaste="return debounce_barcode_change(event)"
+              onkeydown="return debounce_barcode_change(event)" autofocus /> 
+            <span id="patron_name"></span>
+            <span id="patron_usr_barcode_not_found" style="display: none">
+              [% l('Patron barcode was not found') %]
+            </span>
+            [% IF ctx.is_browser_staff %]
+            <button id="hold_usr_search" type="button" class="opac-button" style="display: none;">[% l('Patron Search') %]</button>
+            [% END %]
+            <br />
+            <input type="hidden" id="staff_barcode" 
+              value="[% ctx.staff_recipient.card.barcode | html %]"/>
+            <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 %]
+        [% END %]
+
+        <table id='hold-items-list'>
+            <tr>
+                <td>
+                    [% IF !this_hold_disallowed %]
+                    <input type="hidden" name="hold_target" value="[% hdata.target.id | html %]" />
+                    [% END %]
+                    <div class='hold-items-list-title'>
+                                           <!-- If hold is for grouped formats/editions (metarecord), show short title - else, show complete title --> 
+                                           [% IF CGI.param('hold_type') == 'M' %]
+                                                   [% attrs.title | html %]
+                                               [% ELSE %]
+                                                   [% attrs.title_extended | html %]
+                                               [% END %]
+                    </div>
+                    [% IF hdata.parts AND !this_hold_disallowed %]
+                        [% IF hdata.parts.size > 0 %]
+                        <div class='hold-div'>
+                            [% IF enable.radio.parts == 'true' %]
+                                <span class='hold-span'><label for='select_hold_part'>[%
+                               l('Select a Part:')
+                               %]</label></span>
+                             <div class='radio-parts-selection'>
+                             [% IF !hdata.part_required %]
+                                <span class='parts-radio-option'>
+                                 <input type='radio' name='part' value='' onchange='maybeToggleNumCopies(this);' required>[% l('All Parts') %]</span>
+                              [% END %]
+                               [% FOR part IN hdata.parts %]
+                                 <span class='parts-radio-option'><input type='radio' name='part' id=[% part.id %] value=[% part.id %] onchange='maybeToggleNumCopies(this);' required>
+                                  <label for=[% part.id %]>[% part.label | html %]</label></span>
+                              [% END %]
+                              </div>
+                          [% ELSE %]
+                            <span style='font-weight: bold;'><label for='select_hold_part'>[%
+                                hdata.part_required ? l('Select a Part:') : l('Select a Part (optional):')
+                            %]</label></span>
+                            <select id='select_hold_part' name='part' onchange='maybeToggleNumCopies(this);'>
+                                [% IF !hdata.part_required %]
+                                <option selected='selected' value=''>[% l('- All Parts -') %]</option>
+                                [% END %]
+                                [% FOR part IN hdata.parts %]
+                                <option value='[% part.id %]'>[% part.label | html %]</option>
+                                [% END %]
+                            </select>
+                          [% END %]
+                        </div>
+                        [% ELSE %]
+                        <input type='hidden' name='part' value=''/>
+                        [% END %]
+                    [% END %]
+                   [% INCLUDE "opac/parts/multi_hold_select.tt2" IF NOT (this_hold_disallowed AND hdata.part_required); %]
+                   
+                </td>
+            </tr>
+
+            [% IF this_hold_disallowed %]
+              <tr><td>
+                <div class="mr_holds_no_formats">
+                [% l('This item does not have any formats available for holds placement') %]
+                </div>
+              </td></tr>
+            [% END %]
+
+            [%  IF !loop.last AND ctx.hold_data.size > 1 %]
+            <tr class="holds_item_row_separator"><td> </td></tr>
+            [% END %]
+
+        [% END %]
+        </table>
+
+        [% IF some_holds_allowed %]
+
+        <p class="w-50">
+            [%- org_select_id = 'pickup_lib'; -%]
+            <label for="[% org_select_id %]">[%l('Pickup location:') %]</label>
+            [% PROCESS "opac/parts/org_selector.tt2";
+                INCLUDE build_org_selector name='pickup_lib' 
+                    value=ctx.default_pickup_lib id=org_select_id 
+                    can_have_vols_only=1 hold_pickup_lib=1 %]
+        </p>
+        <p>
+            [% l('Notify when hold is ready for pickup?') %]
+            <blockquote>
+                <input class="hold-alert-method" type="checkbox" id="email_notify" name="email_notify" value="t"
+                    [% IF !ctx.user.email %]disabled="true"[% ELSIF ctx.default_email_notify %]checked="checked"[% END %]/>
+                    <label for="email_notify">[% l('Yes, by Email') %]</label><br/>
+                <blockquote>
+                    [% IF !ctx.user.email and !ctx.is_staff; l('No configured Email address. See "My Account" for setting your Email address.');
+                     ELSE; l('Email Address:') %] <span name="email_address">[% ctx.user.email %]</span>[% END %]
+                </blockquote>
+                [%- IF allow_phone_notifications == 'true' %]
+                <input class="hold-alert-method" type="checkbox" id="phone_notify_checkbox" name="phone_notify_checkbox"
+                    [% IF ctx.default_phone_notify %]checked="checked"[% END %]/>
+                    <label for="phone_notify_checkbox">[% l('Yes, by Phone') %]</label><br/>
+                <blockquote>
+                    <label>[% l('Phone Number:') %]<input type="text" class="form-control" name="phone_notify" [% setting = 'opac.default_phone';
+                    IF ctx.user_setting_map.$setting; %] value='[% ctx.user_setting_map.$setting | html %]'
+                    [%- ELSIF ctx.user.day_phone; %] value='[% ctx.user.day_phone | html %]' [% END %]/></label>
+                </blockquote>
+                [%- END -%]
+                [% IF ctx.get_org_setting(ctx.search_ou, 'sms.enable') == 1 %]
+                <input class="hold-alert-method" type="checkbox" id="sms_notify_checkbox" name="sms_notify_checkbox"
+                    [% IF ctx.default_sms_notify %]checked="checked"[% END %]/>
+                    <label for="sms_notify_checkbox">[% l('Yes, by Text Messaging') %]</label><br/>
+                <blockquote>
+                    [% INCLUDE "opac/parts/sms_carrier_selector.tt2" %]<br/>
+                    [% INCLUDE "opac/parts/sms_number_textbox.tt2" %]<br/>
+                </blockquote>
+                [% END %]
+            </blockquote>
+            <blockquote>
+            <label for="hold_suspend">
+                [% IF ctx.hold_data.size == 1;
+                      l('Suspend this hold?');
+                  ELSE;
+                      l('Suspend these holds?');
+                  END %]
+           </label>
+                <img src="[% ctx.media_prefix %]/images/question-mark.png[% ctx.cache_key %]"
+                     alt="[% l('Suspend Hold Help') %]"
+                     title="[% l('A suspended hold will retain its place in the queue, but will not be fulfilled until it has been activated.') %]" />
+                <br/>
+                <input type="checkbox" name="hold_suspend" id="hold_suspend" value="t"/> [% l('Yes') %]<br/>
+                <a class="btn btn-sm btn-action" id="actDateToggle" href="#toggled-block-suspend" onclick="return toggleActivationDate();">[% l('Set activation date') %]</a>
+            </blockquote>
+            <blockquote id="toggled-block-suspend">
+                <label for="thaw_date">[% l('Activate on') %]
+                <input class="form-control" type="text" id="thaw_date" name="thaw_date" />
+                <em>[% l('Enter date in MM/DD/YYYY format') %]</em></label>
+            </blockquote>
+        </p>
+         [% IF CGI.param('from_basket') %]
+          <blockquote><input type="checkbox" name="clear_cart">[% l('Clear basket after holds are requested?') %]</input></blockquote>
+        [% END %]
+                        [% IF NOT metarecords.disabled AND ctx.hold_data.size == 1 %]
+                        [% IF CGI.param('hold_type') == 'T' AND hdata.record.metarecord AND !hdata.part_required %]
+                        <!-- Grab the bre_id so that we can restore it if user accidentally clicks advanced options -->
+                           [% bre_id = hdata.target.id %]
+                            <a  id='advanced_hold_link'
+                                href="[% mkurl('', {hold_type => 'M', hold_target => hdata.record.metarecord.id, bre_id => bre_id}) %]">
+                                [% l('Advanced Hold Options') %]</a>
+                        [% END %]
+                        [% IF CGI.param('hold_type') == 'M' AND CGI.param('bre_id') %]
+                            <input type="hidden" name="bre_id" value="[% CGI.param('bre_id') %]" />
+                            <a id='basic_hold_link'
+                               href="[% mkurl('', {hold_target => CGI.param('bre_id'), hold_type => 'T'}) %]">
+                                [% l('Basic Hold Options') %]</a>
+                        [% END %]
+                        [% IF hdata.metarecord_filters.formats.size OR # should this be size > 1
+                            (hdata.metarecord_filters.langs.size && hdata.metarecord_filters.langs.size > 1);
+                            PROCESS metarecord_hold_filters_selector hold_data=hdata;
+                        END;
+                    END %]
+        <div class="py-3">
+        <input id="place_hold_submit" type="submit" name="submit" 
+            value="[% l('Submit') %]" title="[% l('Submit') %]"
+            alt="[% l('Submit') %]" class="btn btn-confirm" />
+        [% END # some_holds_allowed %]
+        <input type="reset" name="cancel" onclick="window.location='[% redirect | html %]'" value="[% l('Cancel') %]" id="holds_cancel" class="btn btn-deny" />
+        </div>
+    </form>
+</div>
+
diff --git a/Open-ILS/src/templates/opac-new/parts/place_hold_result.tt2 b/Open-ILS/src/templates/opac-new/parts/place_hold_result.tt2
new file mode 100644 (file)
index 0000000..f02b8e5
--- /dev/null
@@ -0,0 +1,171 @@
+[%  PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_error_messages.tt2";
+    override_possible = 0;
+    any_failures = 0;
+%]
+
+<!-- TODO: CSS for big/strong-->
+
+<script>
+function disable_submit() {
+   var submit_element = document.getElementById("place_hold_submit");
+   submit_element.disabled = true;
+   return true;
+};
+</script>
+
+<div id='holds_box' class='canvas' style='margin-top: 6px;'>
+    <h1>[% l('Place Hold') %]</h1>
+
+    <form method="post" onsubmit="return disable_submit();">
+        <input type="hidden" name="override" value="1" />
+        [% FOR k IN ctx.orig_params.keys %]
+        <input type="hidden" name="[% k %]" value="[% ctx.orig_params.$k | html %]" />
+        [% END %]
+
+        <table id='hold-items-list'>
+
+        [% FOR hdata IN ctx.hold_data;
+            attrs = {marc_xml => hdata.marc_xml};
+            PROCESS get_marc_attrs args=attrs %]
+            <tr>
+                <td>
+                    [% 
+                        override = 0;
+                        IF hdata.could_override || hdata.hold_local_alert;
+                            override_possible = 1;
+                            override = 1;
+                        END;
+                    %]
+                    <input 
+                        type="checkbox" name="hold_target" value="[% hdata.target.id | html %]"
+                        [% IF override %] checked='checked' [% ELSE %] disabled='disabled' [% END %]/>               
+                </td>
+                <td>
+                    
+                    [% IF hdata.parts %]
+                        <input type='hidden' name='part' value='[% hdata.selected_part || '' %]'/>
+                    [% END %]
+                    <div>
+                        [% IF hdata.hold_success %]
+
+                        <div class='success'><h5>[% l("Hold was successfully placed for: "); %]</h5></div>
+                                       [%
+                        title = attrs.title_extended;
+                        IF hdata.selected_part AND hdata.parts AND hdata.parts.size > 0;
+                            FOREACH part IN hdata.parts;
+                                IF hdata.selected_part == part.id;
+                                    title = l('[_1] ([_2])', title, part.label);
+                                END;
+                            END;
+                        END;
+                    %]
+                        
+                    <div class='hold-items-list-title'><h1>[% title | html %]</h1></div>
+                        [% IF hdata.frozen %]
+                        <div>
+                        [% l("Hold is suspended") %]
+                        [% IF hdata.thaw_date %]
+                        <br/>
+                        [% l("Reactivate on [_1]", date.format(ctx.parse_datetime(hdata.thaw_date), DATE_FORMAT));
+                           END %]
+                        </div>
+                        [% END %]
+
+                        [% IF ctx.is_staff %]
+                            <script>
+                                window.addEventListener(
+                                    'load',
+                                    function() {
+                                        setTimeout( // we want this to run _after_ other onload handlers (such as from eframe.js)
+                                            function() {
+                                                try {
+                                                    if (typeof xulG != 'undefined' && xulG.opac_hold_placed) {
+                                                        xulG.opac_hold_placed([% hdata.hold_success %]);
+                                                    }
+                                                } catch(E) {
+                                                    alert('Error updating Work Log with hold placement: ' + E);
+                                                }
+                                            }, 0
+                                        );
+                                    },
+                                    false
+                                );
+                            </script>
+                        [% END %]
+
+                        [% ELSIF hdata.hold_failed; any_failures = 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:
+                                                (hdata.age_protect ?
+                                                    l("All available copies are temporarily unavailable at your pickup library. Placing this hold could result in longer wait times.") :
+                                                    EVENT_MSG_MAP.$event_key ||
+                                                    FAIL_PART_MSG_MAP.$fail_part_key ||
+                                                    (hdata.hold_failed_event.desc ? 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 %]
+                                </div>
+                            [% END;
+                        END %]
+                    </div>
+                </td>
+            </tr>
+        [% END %]
+        </table>
+        [% IF override_possible %]
+            <br/>
+            <hr/>
+            <div class='big-strong'>
+                [% |l %]You have permission to override some of the failed holds. Click Submit to override and place your hold on the selected items.[% END %]
+            </div>
+            <span style='padding-right: 10px;'>
+                <input id="place_hold_submit" type="submit" name="submit" value="[% l('Submit') %]"
+                    title="[% l('Submit') %]" alt="[% l('Submit') %]"
+                    class="btn btn-confirm" />
+            </span>
+        [% END %]
+        <span>
+        [% IF any_failures OR ctx.general_hold_error %]
+        <a class="btn btn-deny" href="[% CGI.param('redirect_to') || CGI.referer | html %]">[% l('Cancel') %]</a>
+        [% ELSE %]
+        <div class='hold_success_links'>
+          <span><a class="btn btn-confirm" href="[% CGI.param('redirect_to') || CGI.referer | html %]">[% l('Continue') %]</a></span>
+           [% IF ctx.is_staff %]
+             [% IF CGI.param('hold_type') == 'C';
+                  hold_type_label = l('copy');
+                ELSIF CGI.param('hold_type') == 'V';
+                  hold_type_label = l('volume');
+                ELSE;
+                  hold_type_label = l('title');
+              END %]
+               <span><a href="[% mkurl(ctx.opac_root _ '/place_hold', {hold_source_page => CGI.param('hold_source_page'), bre_id => CGI.param('bre_id')}) %]">
+               [% l('Place another hold for this ') %] [% hold_type_label %]</a>
+               </span>
+          [% END %]
+        [% END %]
+        </span>
+    </form>
+</div>
+
diff --git a/Open-ILS/src/templates/opac-new/parts/pref_lib_display.tt2 b/Open-ILS/src/templates/opac-new/parts/pref_lib_display.tt2
new file mode 100644 (file)
index 0000000..9fe7d9c
--- /dev/null
@@ -0,0 +1,7 @@
+[%- IF ctx.pref_ou && ctx.pref_ou != ctx.search_ou; %]
+<span class="preflib">[%
+    l('Preferred library: [_1]', '<span class="bold">' _ ctx.get_aou(ctx.pref_ou).name _ '</span>');
+    IF NOT ctx.is_staff %]<a href="[% mkurl(ctx.opac_root _ '/myopac/prefs_settings')
+    %]" class="preflib_change" title="[% l("Change preferred library"); %]">[% l('?') %]</a>[% END; %]
+</span>
+[%- END; %]
diff --git a/Open-ILS/src/templates/opac-new/parts/pref_locale_picker.tt2 b/Open-ILS/src/templates/opac-new/parts/pref_locale_picker.tt2
new file mode 100644 (file)
index 0000000..61d2cc3
--- /dev/null
@@ -0,0 +1,31 @@
+[%- IF ctx.locales.keys.size > 1;
+
+    # Ensure the context locale is in xx_yy format
+    base_locale = ctx.locale FILTER lower;
+    base_locale = base_locale.replace('-','_');
+
+    set_locale = CGI.param('set_eg_locale') || CGI.cookie('eg_locale') || base_locale;
+%]
+<form class="row mx-2" action="[% mkurl() %]" method="post">
+    <td for="locale_picker" class="color_4 light_border">[% l("Language:") %]</td>
+    [%- FOREACH param IN CGI.params(); -%]
+        [%- NEXT IF param.key == 'set_eg_locale'; -%]
+        <input type="hidden" name="[% param.key | html %]" value="[% param.value | html %]" />
+    [%- END; -%]
+   <td>
+    <select id="locale_picker" name="set_eg_locale" class="form-control form-control-sm">
+    [%- FOREACH locale IN ctx.locales.keys %]
+        [%- IF set_locale == locale;
+                selected = 'selected="selected"';
+            ELSE;
+                selected = '';
+            END;
+        %]
+        <option value="[% locale | html %]" [% selected %]>[% ctx.locales.$locale | html %]</option>
+    [%- END %]
+    </select>
+    <br>
+    <input type="submit" class="btn btn-sm btn-confirm" value="[% l("Submit") %]" />
+    </td>
+</form>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/preserve_params.tt2 b/Open-ILS/src/templates/opac-new/parts/preserve_params.tt2
new file mode 100644 (file)
index 0000000..3517f58
--- /dev/null
@@ -0,0 +1,9 @@
+[%- 
+UNLESS params;
+    params = ['locg', 'loc', 'query', 'qtype', 'sort'];
+END;
+FOR param IN params;
+    IF CGI.param(param); %]
+    <input type="hidden" name="[% param %]" value="[% CGI.param(param) | html %]" />
+    [%- END;
+END; %]
diff --git a/Open-ILS/src/templates/opac-new/parts/qtype_selector.tt2 b/Open-ILS/src/templates/opac-new/parts/qtype_selector.tt2
new file mode 100644 (file)
index 0000000..ecb7e0e
--- /dev/null
@@ -0,0 +1,29 @@
+[%  query_types = [
+    {value => "keyword", label => l("Keyword")},
+    {value => "title", label => l("Title"), plural_label => l("Titles"), browse => 1},
+    {value => "jtitle", label => l("Journal Title")},
+    {value => "author", label => l("Author"), plural_label => l("Authors"), browse => 1},
+    {value => "subject", label => l("Subject"), plural_label => l("Subjects"), browse => 1},
+    {value => "series", label => l("Series"), plural_label => l("Series"), browse => 1}
+];
+    IF  ctx.get_org_setting(ctx.search_ou, 'opac.search.enable_bookplate_search');
+        query_types.push(
+            {value => "bookplate", label => l("Digital Bookplate"), plural_label => l("Digital Bookplates")}
+        );
+    END;
+-%]
+
+<select class="form-control w-100" name="[% name || 'qtype' %]"[% IF id; ' id="'; id ; '"' ; END -%]
+    title="[% l('Select query type:') %]">
+    [%  query_type = query_type || CGI.param('qtype') || search.default_qtypes.0;
+      FOR qt IN query_types;
+        NEXT IF browse_only AND NOT qt.browse -%]
+    <option value='[% qt.value | html %]'[%
+        query_type == qt.value ? ' selected="selected"' : ''
+    %]>[% IF plural AND qt.plural_label;
+        qt.plural_label | html;
+    ELSE;
+        qt.label | html;
+    END %]</option>
+    [% END -%]
+</select>
diff --git a/Open-ILS/src/templates/opac-new/parts/record/addedcontent.tt2 b/Open-ILS/src/templates/opac-new/parts/record/addedcontent.tt2
new file mode 100644 (file)
index 0000000..131e56c
--- /dev/null
@@ -0,0 +1,55 @@
+<div class='rdetail_extras_div'> 
+
+[% 
+    ac_types = {
+        reviews => l('Reviews'),
+        anotes  => l('Author Notes'),
+        toc     => l('Table of Contents'),
+        excerpt => l('Excerpt'),
+        summary => l('Summary')
+    };
+
+    selected_type = CGI.param('ac');
+    
+    # For each type of added content, render the link if it's known to have
+    # content, do not render the link if it's known to not have content.  If 
+    # the content status is unknown, render the link, but hide the link via CSS
+    # if dojo is enabled.  If dojo is not enabled, render and display the link.
+%]
+
+    <div id='ac_tab_wrapper'>
+    [% 
+        all_hidden = 1;
+        FOR type IN ac_types.keys;
+            tab_class = 'ac_tab';
+            SET tab_class = tab_class _ ' ac_tab_selected' IF type == selected_type; 
+            IF ctx.added_content.$type.status != '2'; # no content
+                all_hidden = 0;
+                IF ctx.added_content.$type.status == '3' AND want_dojo; # status unknown
+                    tab_class = tab_class _ ' hidden';
+                END %]
+            <div class="[% tab_class %]" id="ac:[% type %]">
+                <a href="[% mkurl('', {ac => type}) _ '#addedcontent' %]" rel="nofollow" vocab="">[% ac_types.$type %]</a>
+            </div>
+            [% END;
+        END;
+    %]
+    </div>
+
+    <div id='ac_content'>
+        [% 
+            IF selected_type; 
+                content = ctx.added_content.$selected_type.content;
+                IF content;
+                    content;
+                ELSE;
+                    l('No Content Available');
+                END;
+            ELSIF all_hidden;
+                l('No Content Available');
+            END;
+        %]
+    </div>
+</div>
+
+
diff --git a/Open-ILS/src/templates/opac-new/parts/record/authors.tt2 b/Open-ILS/src/templates/opac-new/parts/record/authors.tt2
new file mode 100644 (file)
index 0000000..25dac9a
--- /dev/null
@@ -0,0 +1,196 @@
+[%-  
+
+PROCESS "opac/parts/relators.tt2";
+
+author_cnt = 0;
+authors = [
+    {
+        type => 'author', 
+        label => l('Author'),
+        xpath => '//*[@tag="100"]|//*[@tag="110"]|//*[@tag="111"]'
+    }, {
+        type => 'added', 
+        label => l('Added Author'),
+        xpath => '//*[@tag="700"]|//*[@tag="710"]|//*[@tag="711"]'
+    }, {
+        type => 'cast', 
+        label => l('Cast'),
+        xpath => '//*[@tag="511"][@ind1="1"]'
+    }, {
+        type => 'notes', 
+        label => l('Author Notes: '),
+        xpath => '' # Comes from added content...
+    }
+];
+
+BLOCK find_hl_value;
+    outlist = [];
+    norm_needle = PROCESS normalize_string(unnorm_string=needle);
+    FOREACH hl IN attrs.display_field_list;
+        norm_value = PROCESS normalize_string(unnorm_string=hl.value);
+        outlist.push(hl.$f) IF norm_value == norm_needle;
+    END;
+
+    outlist.0;
+END;
+
+BLOCK normalize_string;
+    unnorm_string.replace('[#"^$\+\-,\.:;&|\[\]()]', ' ').replace('\s+',' ').replace('^\s+','').replace('\s+$','');
+END;
+
+BLOCK normalize_qterm;
+    PROCESS normalize_string(unnorm_string=subfield.textContent);
+END;
+
+BLOCK normalize_authors;
+    link_term = link_term _ ' ' _ sf;
+    sf_raw = PROCESS normalize_qterm;
+    qterm = qterm _ ' ' _ sf_raw;
+    indexed_term = 1;
+END;
+
+BLOCK build_author_links;
+    FOR node IN ctx.marc_xml.findnodes(xpath);
+        author_cnt = author_cnt + 1;
+        contrib_ref = '#schemacontrib' _ author_cnt;
+        iprop = ''; # schema.org item type / property
+        link_term = ''; # Linked term (e.g. Personal name + Fuller form of name)
+        supp_term = ''; # Supplementary terms
+        qterm = ''; # Search query
+        tlabels = [];
+        birthdate = '';
+        deathdate = '';
+        graphics = [];
+        tag = node.getAttribute('tag');
+        FOR subfield IN node.childNodes;
+            indexed_term = '';
+            NEXT UNLESS subfield.nodeName == "subfield";
+            code = subfield.getAttribute('code');
+            IF code == '4';
+                relcode = subfield.textContent.substr(0,3);
+                tlabels.push( relators.$relcode || label );
+            END;
+            IF code == 'e';
+                tlabels.push( subfield.textContent() );
+                indexed_term = 1;
+            END;
+            IF code == '6';
+               target_field = tag;
+               linked_fields = [subfield.textContent()];
+               get_linked_880s;
+            END;
+            NEXT UNLESS code.match('[a-z]');
+            sf = subfield.textContent | html;
+
+            # Only Persons have birth/death dates in schema.org
+            # Match personal/corporate/conference MODS subfields
+            IF tag.substr(1,2) == '00';
+                IF code.match('[abcqu]');
+                    PROCESS normalize_authors;
+                END;
+                IF code.match('d');
+                    IF subfield.textContent.match('^\s*\d{4}');
+                        birthdate = subfield.textContent.replace('^\s*(\d{4}).*$', '$1');
+                    END;
+                    IF subfield.textContent.match('-\d{4}.*$');
+                        deathdate = subfield.textContent.replace('^\s*.{4}\-(\d{4}).*$', '$1');
+                    END;
+                    indexed_term = 1;
+                    sf_raw = PROCESS normalize_qterm;
+                    qterm = qterm _ ' ' _ sf_raw;
+                END;
+            ELSIF tag.substr(1,2) == '10';
+                IF code.match('[abcdn]');
+                    PROCESS normalize_authors;
+                END;
+            ELSIF code.match('[acdeq]');
+                PROCESS normalize_authors;
+            END;
+            UNLESS indexed_term;
+                supp_term = supp_term _ ' ' _ sf;
+            END;
+        END;
+        url = mkurl(ctx.opac_root _ '/results', {query => qterm.replace('^\s*(.*?)\s*$', '$1'), qtype => 'author'}, stop_parms.merge(expert_search_parms, general_search_parms, browse_search_parms, facet_search_parms));
+       tlabel = tlabels.join(', ');
+       tlabels = [];
+        author_type = (tlabel || label) | html;
+        
+        # schema.org changes
+        IF type == 'author';
+            IF tag.substr(1,2) == '10' && args.schema.itemtype && args.schema.itemtype.match('MusicAlbum');
+                iprop = ' typeof="MusicGroup" property="byArtist"';
+            ELSIF tag.substr(1,2) == '00';
+                iprop = ' typeof="Person" property="author"';
+            ELSE;
+                iprop = ' typeof="Organization" property="author"';
+            END;
+        ELSIF type == 'added';
+            IF tag.substr(1,2) == '00';
+                iprop = ' typeof="Person" property="contributor';
+            ELSE;
+                iprop = ' typeof="Organization" property="contributor';
+            END;
+            IF relcode;
+                iprop = iprop _ ' http://id.loc.gov/vocabulary/relators/' _ relcode;
+            END;
+            iprop = iprop _ '"';
+        END;
+
+        link_term = link_term.replace('^\s+', '');
+        match_term = link_term _ ' ' _ birthdate _ ' ' _ deathdate;
+        matching_author_hl = PROCESS find_hl_value needle=match_term f=attrs.hl_field;
+
+        authtml = ' <span class="rdetail-author-div"' _ iprop _ ' resource="' _ contrib_ref _ '"><a href="' _ url _ '"><span resource="' _ contrib_ref _ '">';
+        IF iprop; authtml = authtml _ '<span property="name">'; END;
+        IF matching_author_hl;
+            authtml = authtml _ matching_author_hl;
+        ELSE;
+            authtml = authtml _ link_term;
+        END;
+        IF iprop; authtml = authtml _ '</span>'; END;
+        IF birthdate AND !matching_author_hl;
+            authtml = authtml _ ' <span property="birthDate">' _ birthdate _ '</span>-';
+        END;
+        IF deathdate AND !matching_author_hl;
+            authtml = authtml _ '<span property="deathDate">' _ deathdate _ '</span>';
+        END;
+        authtml = authtml _ '</span></a>'; # End search link
+
+        # Display supplemental terms (mostly about the author's work)
+        IF supp_term;
+            authtml = authtml _ ' ' _ supp_term;
+        END;
+
+        # Display linked 880 fields
+        FOREACH link880 IN graphics;
+            diratt = '';
+            IF link880.dir;
+                diratt = ' dir="' _ link880.dir _ '"';
+            END;
+            authtml = authtml _ ' <span class="graphic880"' _ diratt _ '>';
+            link880.value | html;
+            authtml = authtml _ '</span>';
+        END;
+        authtml = authtml _ ' (<span property="description">' _ author_type _ '</span>). ';
+        authtml = authtml _ '</span>'; # End author span
+        authlist.push(authtml);
+    END;
+END;
+%]
+
+<div class='rdetail_authors_div'>
+[%- FOREACH author IN authors;
+    NEXT UNLESS author.xpath; 
+    authlist = [];
+    PROCESS build_author_links(
+        xpath=author.xpath, label=author.label, type=author.type
+    );
+    IF authlist.size;
+        FOREACH authtml IN authlist;
+            authtml;
+        END;
+    END;
+END %]
+</div>
+
+
diff --git a/Open-ILS/src/templates/opac-new/parts/record/awards.tt2 b/Open-ILS/src/templates/opac-new/parts/record/awards.tt2
new file mode 100644 (file)
index 0000000..1e81833
--- /dev/null
@@ -0,0 +1,23 @@
+<div class='rdetail_extras_div'> 
+
+    <!-- Chilifresh patron reviews -->
+    [% IF ENV.OILS_CHILIFRESH_URL %] 
+    <strong>[% l('Patron Reviews:') %]</strong>
+    <div style="margin-bottom:20px;">
+        <span class="chili_review" id="isbn_[% attrs.isbn_clean | html %]"> </span>
+        <div id="chili_review_[% attrs.isbn_clean | html %]" style="display: none;" align="center" width="100%"></div>
+    </div>
+    [% END %]
+    <!-- Novelist suggestions-->
+    [% IF ENV.OILS_NOVELIST_URL %] 
+        <div id='novelist-loading'>
+        <h3 class="text-center">[% l('Loading Recommendations...') %]</h3>
+            <div class="progress">
+                <div class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
+            </div>
+        </div>
+        <div data-novelist-novelistselect="[% attrs.isbn_clean || attrs.upc %]"></div>
+    [% END %]
+    
+</div>
+
diff --git a/Open-ILS/src/templates/opac-new/parts/record/body.tt2 b/Open-ILS/src/templates/opac-new/parts/record/body.tt2
new file mode 100644 (file)
index 0000000..081e103
--- /dev/null
@@ -0,0 +1,23 @@
+[%-  attrs = {marc_xml => ctx.marc_xml};
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS get_marc_attrs args=attrs;
+    stop_parms = ['expand','cnoffset','copy_offset','copy_limit'];
+    ctx.record_attrs = attrs; # capture for JS
+    ctx.metalinks.push('<meta property="og:type" content="http://schema.org/' _ args.schema.itemtype _ '" />');
+%]
+<div id='canvas_main' class='container mx-auto p-3' vocab="http://schema.org/" typeof='[% args.schema.itemtype %] Product' resource="#schemarecord">
+[%- FOREACH link IN args.links.sameAs; %]
+    <link property="sameAs" href="[% link %]">
+[%- END; %]
+[%- FOREACH link IN args.links.exampleOfWork; %]
+    <link property="exampleOfWork" href="[% link %]">
+[%- END; %]
+    [%- INCLUDE "opac/parts/record/navigation.tt2" %]
+    [%- IF ctx.bib_is_dead %]
+    <div id='rdetail_deleted_exp'>
+        [% l("This record has been deleted from the database.  We recommend that you remove this title from any lists it may have been added to.") %]
+    </div>
+    [%- END %]
+    [% INCLUDE "opac/parts/record/summary.tt2" %]
+    [%- INCLUDE "opac/parts/record/navigation.tt2" %]
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/record/cnbrowse.tt2 b/Open-ILS/src/templates/opac-new/parts/record/cnbrowse.tt2
new file mode 100644 (file)
index 0000000..2423aaf
--- /dev/null
@@ -0,0 +1,64 @@
+[% 
+    cnoffset = CGI.param('cnoffset');
+    cnoffset = cnoffset.match('^-?\d+$') ? cnoffset : 0; # verify cnoffset is a sane value
+%]
+
+<div id='cnbrowse' class='cn_browser'>
+    <div id='cn_browse_div'> 
+        <p class='color_4 h5'>
+            [% l("You are now browsing [_1]", ctx.browsing_ou.name) | html %]
+        </p>
+        <br>
+        <table class='data_grid bookshelf' width='100%'>
+                <tr>
+                    <td>
+                        <a class='np_nav_link classic_link btn btn-outline-primary' 
+                            href="[% mkurl('', {cnoffset => cnoffset - 1}) %]#cnbrowse">[% l("&lt;&lt; Previous Page") %]</a>
+                    </td>
+                    <td colspan='1' align='center' class="h3">[% l("Shelf Browser") %]</td>
+                    <td>
+                        <a class='np_nav_link classic_link btn btn-outline-primary' 
+                            href="[% mkurl('', {cnoffset => cnoffset + 1}) %]#cnbrowse">[% l("Next Page &gt;&gt;") %]</a>
+                    </td>
+                </tr>
+            <tr class='browse_border'>
+                <td colspan='3'>&nbsp;</td>
+            </tr>
+            <tbody>
+            [%- tr_open = 0;
+                PROCESS get_library;
+                FOR cn IN ctx.browsed_call_numbers;
+                  IF loop.index % 3 == 0; tr_open = 1 %]
+                <tr class='cn_browse_row'>
+                [% END -%]
+                    <td class='cn_browse_item' width='25%' valign='top'>
+                       <div class="card">
+                        [%  rec_attrs = {marc_xml => cn.record.marc};
+                            PROCESS get_marc_attrs args=rec_attrs; %]
+                        <a href="[% mkurl(ctx.opac_root _ '/record/' _ cn.record.id, {$loc_name => loc_value}, 1) %]"><img height='60' width='50' 
+                            class='cn_browse_info' name='cn_browse_pic' border='0' 
+                            [% img_alt(l('Go to record [_1]', rec_attrs.title)) %]
+                            src="[% ctx.media_prefix %]/opac/extras/ac/jacket/small/r/[% cn.record.id | uri %]" /></a>
+                        <div class='cn_browse_info bold' name='cn_browse_cn'>[% cn.label | html %]</div>
+                        <div class='cn_browse_info'>
+                            <a name='cn_browse_title' class='classic_link record_title'
+                                href="[% mkurl(ctx.opac_root _ '/record/' _ cn.record.id, {$loc_name => loc_value}, 1) %]">[% rec_attrs.title | html %]</a>
+                        </div>
+                        [% IF rec_attrs.author %]<div class='cn_browse_info'>
+                            <a name='cn_browse_author' class='classic_link record_author'
+                                href="[%-
+                                    authorquery = rec_attrs.author | replace('[,\.:;]', '');
+                                    mkurl(ctx.opac_root _ '/results', {qtype => 'author', query => authorquery, $loc_name => loc_value}, 1)
+                                    -%]">[% rec_attrs.author | html %]</a>
+                        </div>[% END %]
+                        <div class='cn_browse_info' name='cn_browse_lib'>[% cn.owning_lib.name | html %]</div>
+                        </div>
+                    </td>
+                [% IF loop.index % 3 == 2; tr_open = 0 %]
+                </tr>
+                [% END %]
+            [% END; IF tr_open %]</tr>[% END %]
+            </tbody>
+        </table>
+    </div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/record/contents.tt2 b/Open-ILS/src/templates/opac-new/parts/record/contents.tt2
new file mode 100644 (file)
index 0000000..db80b47
--- /dev/null
@@ -0,0 +1,217 @@
+[%-
+contents =  [
+    {
+        display_field => 'general_note',
+        label => l('General Note: '),
+        xpath => '//*[@tag="500"]'
+    }, {
+        label => l('With Note: '),
+        xpath => '//*[@tag="501"]'
+    }, {
+        display_field => 'thesis',
+        label => l('Dissertation Note: '),
+        xpath => '//*[@tag="502"]'
+    }, {
+        display_field => 'bibliography',
+        label => l('Bibliography, etc. Note: '),
+        xpath => '//*[@tag="504"]'
+    }, {
+        display_field => 'toc',
+        label => l('Formatted Contents Note: '),
+        xpath => '//*[@tag="505"]'
+    }, {
+        label => l('Restrictions on Access Note: '),
+        xpath => '//*[@tag="506"]'
+    }, {
+        label => l('Scale Note for Graphic Material: '),
+        xpath => '//*[@tag="507"]'
+    }, {
+        display_field => 'production_credits',
+        label => l('Creation/Production Credits Note: '),
+        xpath => '//*[@tag="508"]'
+    }, {
+        label => l('Citation/References Note: '),
+        xpath => '//*[@tag="510"]'
+    }, {
+        display_field => 'performers',
+        label => l('Participant or Performer Note: '),
+        xpath => '//*[@tag="511"]'
+    }, {
+        label => l('Type of Report and Period Covered Note: '),
+        xpath => '//*[@tag="513"]'
+    }, {
+        label => l('Data Quality Note: '),
+        xpath => '//*[@tag="514"]'
+    }, {
+        label => l('Numbering Peculiarities Note: '),
+        xpath => '//*[@tag="515"]'
+    }, {
+        label => l('Type of Computer File or Data Note: '),
+        xpath => '//*[@tag="516"]'
+    }, {
+        label => l('Date/Time and Place of an Event Note: '),
+        xpath => '//*[@tag="518"]'
+    }, {
+        display_field => 'abstract',
+        label => l('Summary, etc.: '),
+        xpath => '//*[@tag="520"]'
+    }, {
+        label => l('Target Audience Note: '),
+        xpath => '//*[@tag="521"]'
+    }, {
+        label => l('Geographic Coverage Note: '),
+        xpath => '//*[@tag="522"]'
+    }, {
+        label => l('Preferred Citation of Described Materials Note: '),
+        xpath => '//*[@tag="524"]'
+    }, {
+        label => l('Supplement Note: '),
+        xpath => '//*[@tag="525"]'
+    }, {
+        label => l('Study Program Information Note: '),
+        xpath => '//*[@tag="526"]'
+    }, {
+        label => l('Additional Physical Form available Note: '),
+        xpath => '//*[@tag="530"]'
+    }, {
+        label => l('Reproduction Note: '),
+        xpath => '//*[@tag="533"]'
+    }, {
+        label => l('Original Version Note: '),
+        xpath => '//*[@tag="534"]'
+    }, {
+        label => l('Location of Originals/Duplicates Note: '),
+        xpath => '//*[@tag="535"]'
+    }, {
+        label => l('Funding Information Note: '),
+        xpath => '//*[@tag="536"]'
+    }, {
+        label => l('System Details Note: '),
+        xpath => '//*[@tag="538"]'
+    }, {
+        label => l('Terms Governing Use and Reproduction Note: '),
+        xpath => '//*[@tag="540"]'
+    }, {
+        label => l('Immediate Source of Acquisition Note: '),
+        xpath => '//*[@tag="541"]'
+    }, {
+        label => l('Information Relating to Copyright Status: '),
+        xpath => '//*[@tag="542"]'
+    }, {
+        label => l('Location of Other Archival Materials Note: '),
+        xpath => '//*[@tag="544"]'
+    }, {
+        label => l('Biographical or Historical Data: '),
+        xpath => '//*[@tag="545"]'
+    }, {
+        label => l('Language Note: '),
+        xpath => '//*[@tag="546"]'
+    }, {
+        label => l('Former Title Complexity Note: '),
+        xpath => '//*[@tag="547"]'
+    }, {
+        label => l('Issuing Body Note: '),
+        xpath => '//*[@tag="550"]'
+    }, {
+        label => l('Entity and Attribute Information Note: '),
+        xpath => '//*[@tag="552"]'
+    }, {
+        label => l('Cumulative Index/Finding Aids Note: '),
+        xpath => '//*[@tag="555"]'
+    }, {
+        label => l('Information About Documentation Note: '),
+        xpath => '//*[@tag="556"]'
+    }, {
+        label => l('Ownership and Custodial History: '),
+        xpath => '//*[@tag="561"]'
+    }, {
+        label => l('Copy and Version Identification Note: '),
+        xpath => '//*[@tag="562"]'
+    }, {
+        label => l('Binding Information: '),
+        xpath => '//*[@tag="563"]'
+    }, {
+        label => l('Case File Characteristics Note: '),
+        xpath => '//*[@tag="565"]'
+    }, {
+        label => l('Methodology Note: '),
+        xpath => '//*[@tag="567"]'
+    }, {
+        label => l('Linking Entry Complexity Note: '),
+        xpath => '//*[@tag="580"]'
+    }, {
+        label => l('Publications About Described Materials Note: '),
+        xpath => '//*[@tag="581"]'
+    }, {
+        label => l('Action Note: '),
+        xpath => '//*[@tag="583"]'
+    }, {
+        label => l('Accumulation and Frequency of Use Note: '),
+        xpath => '//*[@tag="584"]'
+    }, {
+        label => l('Exhibitions Note: '),
+        xpath => '//*[@tag="585"]'
+    }, {
+        label => l('Awards Note: '),
+        xpath => '//*[@tag="586"]'
+    }, {
+        label => l('Source of Description Note: '),
+        xpath => '//*[@tag="588"]'
+    } 
+];
+
+BLOCK render_contents;
+    xpath = xpath || '//*[starts-with(@tag,"5")]';
+    FOR node IN ctx.marc_xml.findnodes(xpath);
+        all_content = [];
+        graphics = [];
+        FOR subfield IN node.childNodes;
+            NEXT UNLESS subfield.nodeName == "subfield";
+            code = subfield.getAttribute('code');
+            IF code == '6';
+               linked_fields = [subfield.textContent()];
+               target_field = node.getAttribute('tag');
+               get_linked_880s;
+            END;
+            NEXT UNLESS code.match('[a-z]');
+            all_content.push(subfield.textContent);
+        END;
+        total_contents = all_content.join(" ").replace('\s+$', '');
+        %] [% "<div class='content_field'>"; total_contents | html ; "</div>";
+        FOREACH link880 IN graphics;
+            '<div class="graphic880"' _ link880.dir _ '>';
+            link880.value | html;
+            '</div>';
+        END;
+    END;
+END;
+
+BLOCK render_all_contents;
+    FOREACH cont IN contents;
+        content = '';
+        df = cont.display_field;
+        IF df AND attrs.hl.$df.size;
+            content = '<!-- highlighted -->' _ attrs.hl.$df.join('<br/>');
+        ELSE;
+            content = PROCESS render_contents(xpath=cont.xpath);
+        END;
+        IF content.match('\S');
+-%]
+<tr>
+    <td class='rdetail_content_type'>[% cont.label %]</td>
+    <td class='rdetail_content_value' property='keywords'>[% content %]</td>
+</tr>
+        [%- END; %]
+    [%- END; %]
+[%- END %]
+
+[%-  content_html = PROCESS render_all_contents;
+    IF content_html.length > 0;
+%]
+<h2 class='rdetail_contents'>[% l('Content descriptions') %]</h2>
+<table class='rdetail_content'>
+    <tbody>
+[%- content_html %]
+    </tbody>
+</table>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/record/copy_counts.tt2 b/Open-ILS/src/templates/opac-new/parts/record/copy_counts.tt2
new file mode 100644 (file)
index 0000000..32360a7
--- /dev/null
@@ -0,0 +1,52 @@
+<span id="rdetail_copy_counts">
+    <h2>[% l('Available copies') %]</h2>
+    <ul>
+    [%- depths = ctx.copy_summary.size;
+        depth = 0;
+        displayed_ous = {};
+        ou_hiding_disabled = ctx.org_hiding_disabled();
+        WHILE depth < depths;
+            ou_avail = ctx.copy_summary.$depth.available;
+            ou_count = ctx.copy_summary.$depth.count;
+            ou_id = ctx.copy_summary.$depth.org_unit;
+            cp_org_unit = ctx.get_aou(ou_id);
+            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;
+            ou_name = cp_org_unit.name;
+            displayed_ous.$ou_name = 1;
+    %]
+    <li>
+    [% l('[_1] of [quant,_2,copy,copies] available at [_3].', ou_avail, ou_count, ou_name) | html %]
+    [%- this_depth = ctx.get_aou(ou_id).ou_type.depth;
+        IF ou_count > 0 && this_depth != ctx.copy_depth %]
+    <a href="[% mkurl('', {copy_depth => this_depth}, ['copy_offset']); %]"
+       title="[% l('Show copies at [_1]', ou_name) | html; %]">
+       [%- l('(Show)'); %]</a>
+    [%- END; %]
+    </li> 
+    [%- depth = depth + 1;
+        END;
+
+        depth = attrs.plib_copy_counts.size - 1;
+        ou_name = ctx.get_aou(attrs.plib_copy_counts.$depth.org_unit).name;
+        ou_id = attrs.plib_copy_counts.$depth.org_unit;
+        UNLESS depth < 0 || displayed_ous.exists(ou_name);
+    %]
+    [%- IF attrs.plib_copy_counts.$depth.count > 0; %]
+    <li class="preferred">
+    [%-
+        l('[_1] of [quant,_2,copy,copies] available at [_3].',
+            attrs.plib_copy_counts.$depth.available,
+            attrs.plib_copy_counts.$depth.count,
+            ou_name) | html
+    %] <a href="[% mkurl('', {locg => ou_id}, ['copy_offset']); %]"
+       title="[% l('Show copies at [_1]', ou_name) | html; %]">[%
+       l('(Show preferred library)');
+    %]</a></li>
+    [%- END %]
+    [%- END %]
+    </ul>
+</span>
diff --git a/Open-ILS/src/templates/opac-new/parts/record/copy_table.tt2 b/Open-ILS/src/templates/opac-new/parts/record/copy_table.tt2
new file mode 100644 (file)
index 0000000..0dd3734
--- /dev/null
@@ -0,0 +1,319 @@
+[%-
+
+# If being used in serial mode, flatten list of units so that they can be
+# used like one long list of copies without changing so much code below.
+IF serial_holdings;
+    copies = [];
+    FOREACH h IN serial_holdings;
+        units = h.units.slice(0); # copy
+        FOREACH unit IN units;
+            unit.holding_label = h.label;
+        END;
+        copies = copies.merge(units);
+    END;
+END;
+
+FOREACH copy_info IN copies;
+    IF copy_info.call_number_label != '##URI##';
+        has_copies = 'true';
+    END;
+    IF copy_info.part_label != '';
+        has_parts = 'true';
+    END;
+    IF has_parts && has_copies;
+        LAST;
+    END;
+END;
+-%]
+[%-
+IF has_copies or ctx.foreign_copies;
+  depth = CGI.param('copy_depth').defined ? CGI.param('copy_depth') : CGI.param('depth').defined ? CGI.param('depth') : ctx.copy_summary.last.depth;
+  total_copies = ctx.copy_summary.$depth.count;
+%]
+<table class="container-fluid table table-hover mt-4 miniTable copyTable" width="100%" >
+    <thead>
+        <tr>
+            [% IF serial_holdings -%]
+            <th scope='col'>[% l("Issue Label") %]</th>
+            [%- ELSE -%]
+            <th scope='col'>[% l("Location") %]</th>
+            [%- END %]
+            <th scope='col'>[% l("Call Number / Copy Notes") %]</th>
+            [%- IF has_parts == 'true' %]
+            <th scope='col'>[% l("Part") %]</th>
+            [%- END %]
+            <th scope='col'>[% l("Barcode") %]</th>
+            <th scope='col'>[% l("Shelving Location") %]</th>
+            [%- IF ctx.is_staff %]
+            <th scope='col'>[% l("Circulation Modifier") %]</th>
+            <th scope='col'>[% l("Age Hold Protection") %]</th>
+            <th scope='col'>[% l("Active/Create Date") %]</th>
+            [%- END %]
+            [%- IF ctx.is_staff OR serial_holdings %]
+            <th scope='col'>[% l("Holdable?") %]</th>
+            [%- END %]
+            <th scope='col'>[% l("Status") %]</th>
+            <th scope='col'>[% l("Due Date") %]</th>
+        </tr>
+    </thead>
+    <tbody>
+[%- FOREACH peer IN ctx.foreign_copies;
+        FOREACH bib IN peer.foreign_copy_maps; %]
+    <tr><td>
+    [%- bib_lib_name = ctx.get_aou(bib.target_copy.circ_lib).name | html;
+        l("[_1] (foreign item)", bib_lib_name); -%]
+        <ul><li>
+            <span class="bib_peer_type">[% bib.peer_type.name | html %]</span>:
+            <a href="[% mkurl(ctx.opac_root _ '/record/' _ bib.target_copy.call_number.record) %]">
+                <span class="bib_peer_title">[% peer.title | html %]</span> / 
+                <span class="bib_peer_author">[% peer.author | html %]</span>
+            </a>
+        </li></ul>
+    </td>
+    <td>[% bib.target_copy.call_number.label | html %]</td>
+    <td>[% bib.target_copy.barcode | html %]</td>
+    <td>[% bib.target_copy.location.name | html %]</td>
+    <td>[% bib.target_copy.status.name | html %]</td>
+    <td>[% date.format(ctx.parse_datetime(copy_info.due_date, copy_info.circ_circ_lib),DATE_FORMAT) %]</td>
+</tr>
+   [%- END; # FOREACH peer
+END; # FOREACH bib
+-%]
+        [%- last_cn = 0;
+        FOR copy_info IN copies;
+            callnum = copy_info.call_number_label;
+            NEXT IF callnum == '##URI##';
+
+            callnum_prefix = copy_info.call_number_prefix_label;
+            IF callnum_prefix != "";
+                callnum = callnum_prefix _ " " _ callnum;
+            END;
+
+            callnum_suffix = copy_info.call_number_suffix_label;
+            IF callnum_suffix != "";
+                callnum = callnum  _ " " _ callnum_suffix;
+            END;
+        -%]
+        <tr property="offers" typeof="Offer">
+            [%- IF serial_holdings %]
+            <td class='rdetail-issue-issue'>
+                [%- copy_info.holding_label | html; -%]
+            </td>
+            [%- ELSE %]<td>
+            [%- INCLUDE "opac/parts/library_name_link.tt2"; -%]
+                <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
+                <meta property="price" content="0.00">
+            </td>[% END %]
+            <td><span property="sku">[% callnum | html %]</span> [% 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}) %];rec=[%- ctx.bre_id -%]" rel="nofollow" vocab="">Text</a>)[% END %]</td>
+            [%- IF has_parts == 'true' %]
+            <td>[% copy_info.part_label | html %]</td>
+            [%- END %]
+            <td property="serialNumber">
+                [% copy_info.barcode | html -%]
+                [% IF ctx.is_staff %]
+                  [%- IF ctx.is_browser_staff %]
+                    <a target="_blank" href="[% ctx.base_path %]/staff/cat/item/[% copy_info.id %]">[% l('view') %]</a>
+                    [% IF ctx.has_perm('UPDATE_COPY', copy_info.circ_lib) 
+                        OR ctx.has_perm('UPDATE_COPY', copy_info.call_number_owning_lib) %]
+                        <span> | </span>
+                        <a target="_blank" href="[% ctx.base_path %]/staff/cat/item/[% copy_info.id %]/edit">[% l('edit') %]</a>
+                    [% END %]
+                  [% ELSE %]
+                    <a onclick="xulG.new_tab(xulG.urls.XUL_COPY_STATUS, {}, {'from_item_details_new': true, 'barcodes': ['[%- copy_info.barcode | html | replace('\'', '\\\'') -%]']})"
+                        href="javascript:;">[% l('view') %]</a>
+                    [%# if the user can edit copies, show the copy edit link %]
+                    [% IF ctx.has_perm('UPDATE_COPY', copy_info.circ_lib) 
+                        OR ctx.has_perm('UPDATE_COPY', copy_info.call_number_owning_lib) %]
+                        <span> | </span>
+                        <a href="javascript:;" 
+                            onclick="xulG.volume_item_creator({copy_id : [% copy_info.id %]})">
+                            [% l(' edit') %]
+                        </a>
+                    [% END %]
+                  [% END %]
+                [% END %]
+                [%- IF attrs.gtin13;
+                    '<meta property="gtin13" content="' _ attrs.gtin13 _ '" />';
+                END; -%]
+            </td>
+            <td property="availableAtOrFrom">[%- INCLUDE "opac/parts/location_name_link.tt2"; -%]</td>
+            [%- IF ctx.is_staff %]
+            <td>[% copy_info.circ_modifier | html %]</td>
+            <td>
+                [% copy_info.age_protect ?
+                    ctx.get_crahp(copy_info.age_protect).name : l('None') | html %]
+            </td>
+            <td>[% 
+                IF ctx.get_org_setting(copy_info.circ_lib, 'circ.holds.age_protect.active_date') == 1;
+                    disp_date = copy_info.active_date ? copy_info.active_date : copy_info.create_date;
+                ELSE;
+                    disp_date = copy_info.create_date;
+                END;
+           
+                IF disp_date;
+                    date.format(
+                        ctx.parse_datetime(disp_date),
+                        DATE_FORMAT
+                    );
+                ELSE;
+                   '-';
+                END;
+            %]</td>
+            [% END # is_staff %]
+            [% IF ctx.is_staff OR serial_holdings %]
+            <td>[%  # Show copy/volume hold links to staff (without
+                # checking whether they have permissions to do those).
+                overall_holdable = (
+                    copy_info.holdable == 't' AND
+                    copy_info.location_holdable == 't' AND
+                    copy_info.status_holdable == 't'
+                );
+                IF overall_holdable;
+                    hold_link = '';
+                    param_sep = l(" / ");
+
+                    # Only staff get to place copy or volume holds
+                    IF ctx.is_staff; 
+                        hold_link = '<a href="' _
+                            mkurl(ctx.opac_root _ '/place_hold', {
+                                hold_target => copy_info.id,
+                                hold_type => 'C',
+                                hold_source_page => mkurl()
+                            }) _ '">' _ l('Copy hold') _ '</a>';
+                        IF copy_info.call_number != last_cn;
+                            last_cn = copy_info.call_number; 
+                            hold_link = hold_link _ param_sep;
+                            hold_link = hold_link _ '<a href="' _
+                                mkurl(ctx.opac_root _ '/place_hold', {
+                                    hold_target => copy_info.call_number,
+                                    hold_type => 'V',
+                                    hold_source_page => mkurl()
+                                }) _ '">' _ l('Volume hold') _ '</a>';
+                        END;
+                        IF serial_holdings;
+                            hold_link = hold_link _ param_sep;
+                        END;
+                    END; 
+                    IF serial_holdings; 
+                        hold_link = hold_link _
+                            '<a class="rdetail-issue-place-hold" href="' _
+                            mkurl(ctx.opac_root _ '/place_hold', {
+                                hold_target => copy_info.issuance,
+                                hold_type => 'I',
+                                hold_source_page => mkurl()
+                            }) _ '" rel="nofollow" vocab="">' _ l('Issue hold') _ '</a>';
+                    END; 
+
+                    hold_link;
+                ELSE; 
+                    l("Not holdable");
+                END %]</td>
+            [%- END %]
+            <td>[%-
+                schema_copy_status.${copy_info.status_code};
+                copy_info.copy_status | html;
+            -%]</td>
+            <td>[%
+                IF copy_info.due_date;
+                    date.format(
+                        ctx.parse_datetime(copy_info.due_date, copy_info.circ_circ_lib),
+                        DATE_FORMAT
+                    );
+                ELSE;
+                    '-';
+                END %]</td>
+        </tr>
+
+        [% IF copy_info.notes; %]
+            [% FOREACH note IN copy_info.notes; %]
+                <tr><td>&nbsp;</td><td class="copy_note" colspan="4" property="description"><strong>[% note.title | html %]:</strong> [% note.value | html %]</td></tr>
+            [% END %]
+        [% END %]
+
+        [% IF copy_info.tags; %]
+            [% FOREACH tag IN copy_info.tags; %]
+                <tr class="copy_tag_row">
+                    <td>&nbsp;</td>
+                    <td class="copy_tag" colspan="4">
+                    <span class="copy_tag_type_label">[% tag.tag_type.label _ ": " | html %]</span>
+                        <span class="copy_tag_value">
+                        [% IF tag.url %]
+                            <a href="[% tag.url | html %]">
+                            [% END %]
+                                [% tag.value | html %]
+                            [% IF tag.url %]
+                            </a>
+                            [% END %]
+                        </span>
+                    </td>
+                </tr>
+            [% END %]
+        [% END %]
+
+
+[%- IF copy_info.peer_bib_marc.size > 0;
+'<tr><td><ul>';
+    FOREACH bib IN copy_info.peer_bib_marc;
+        attrs = {marc_xml => bib};
+        PROCESS get_marc_attrs args=attrs %]
+[%- IF attrs.bibid != ctx.bre_id; -%]
+    <li property="isRelatedTo" typeof="Product">
+        [%- # Map the bound-with relationship -%]
+        <a href="[% mkurl(ctx.opac_root _ '/record/' _ attrs.bibid) %]" property="url">
+            <span class="bib_peer_title" property="name">[% attrs.title | html %]</span>
+            / <span class="bib_peer_author">[% attrs.author | html %]</span>
+        </a>
+    </li>
+[%- END; # IF attrs.bibid %]
+[%- END; # FOREACH bib in copy_info.peer_bib_marc
+'</ul></td></tr>';
+    END # IF copy_info.peer_bib_marc.size
+%]
+
+[%- END; # FOR copy_info
+%]
+               [%- IF ctx.copy_offset > 0 AND NOT serial_holdings;
+            new_offset = ctx.copy_offset - ctx.copy_limit; 
+            IF new_offset < 0; new_offset = 0; END %]
+        [%- IF copies.size >= ctx.copy_limit AND NOT serial_holdings AND (ctx.copy_offset + ctx.copy_limit < total_copies) %]
+               <tr class="offset">
+               [%- IF ctx.copy_offset > 0 AND NOT serial_holdings;
+               new_offset = ctx.copy_offset - ctx.copy_limit;
+               IF new_offset < 0; new_offset = 0; END %]
+               <td>
+                <a href="[% mkurl('', {copy_offset => new_offset, copy_limit => ctx.copy_limit}) %]">&laquo; [%
+                    l('Previous [_1]', ctx.copy_offset - new_offset) %]</a>
+               </td>
+               [%- END %]
+               [%- IF copies.size >= ctx.copy_limit AND NOT serial_holdings AND (ctx.copy_offset + ctx.copy_limit < total_copies) %]
+               <td>
+                       <a href="[% mkurl('', {copy_offset => ctx.copy_offset + ctx.copy_limit, copy_limit => ctx.copy_limit}) %]">[%
+                    l('Next [_1]', ctx.copy_limit) %] &raquo;</a>
+               </td>
+               [%- END %]
+            </tr>
+        [%- END %]
+        [%- END %]
+        [% IF NOT serial_holdings && ctx.copy_limit < total_copies -%]
+               <tr class="not_serial">
+               <td>
+                       [%- more_copies_limit = 50 %] [%# TODO: config %]
+                       [%- IF  ctx.copy_limit != more_copies_limit AND copies.size >= ctx.copy_limit AND ctx.copy_limit < total_copies %]
+                       <div class="rdetail_show_copies">
+                               <img src="[% ctx.media_prefix %]/images/plus_sign.png[% ctx.cache_key %]" alt="[% l('Show more copies icon') %]"/>
+                               <a href="[% mkurl('', {copy_limit => more_copies_limit, copy_offset => 0}) %]">[% l('Show more copies') %]</a>
+                       </div>
+                       [%- ELSIF ctx.copy_limit == more_copies_limit %]
+                       <div  class="rdetail_show_copies">
+                               <img src="[% ctx.media_prefix %]/images/minus_sign.png[% ctx.cache_key %]" alt="[% l('Show fewer copies icon') %]"/>
+                               <a href="[% mkurl('', {copy_limit => 0, copy_offset => 0}) %]">[% l('Show fewer copies') %]</a>
+                       </div>
+                       [%- END %]
+               </td>
+               </tr>
+               [%- END %]
+    </tbody>
+</table>
+[% END; %]
diff --git a/Open-ILS/src/templates/opac-new/parts/record/extras.tt2 b/Open-ILS/src/templates/opac-new/parts/record/extras.tt2
new file mode 100644 (file)
index 0000000..a56864d
--- /dev/null
@@ -0,0 +1,105 @@
+[%
+    arrow_right = '&#9658;';
+    arrow_down = '&#9660;';
+%]
+<div>
+    <div id='rdetail_extras_div' class="card-columns row" style='width: 100%;'> 
+        [%
+            IF ctx.google_books_preview;
+                label = l('Google Preview');
+                name = 'google_preview';
+        %]
+        <div id="gbp_extra" class="card">
+            <div id="gbp_extra_links" class="card-header">
+                <a id='gbp_arrow_link' name='[% name %]' class='rdetail_extras_lbl'>[% arrow_right %]</a>
+                <a id='gbp_arrow_down_link' name='[% name %]' class='rdetail_extras_lbl'>[% arrow_down %]</a>
+                <a name='[% name %]_lbl' class="rdetail_extras_lbl">[% label %]</a></div>
+        </div>
+        <div id="gbp_extra_container" class='card-body'></div>
+        [%  END %]
+
+        [%  # Hidden extras are not yet implemented.  Some may require JS
+
+        MACRO tab_is_active(tab) BLOCK;
+            exp_name = 'expand_' _ tab;
+            IF ctx.$exp_name OR ctx.expand_all; 1; END;
+        END;
+
+        # Let's see if we should hide the content cafe / simple summary content
+        hide_summary = 1;
+        IF attrs.summaries.0; hide_summary = 0; ELSE;
+            # Expose content cafe if it's reasonable to do so.
+            # This approach only works when using embedded content cafe.
+            IF ENV.OILS_CONTENT_CAFE_USER; 
+                ident = attrs.isbn_clean || attrs.upc; 
+                IF ident; hide_summary = 0; END;
+            END;
+        END;
+
+        # if no added content is available, hide the main tab.
+        # if any content is available, use the first tab as the default display tab.
+        default_ac = '';
+        IF !tab_is_active('addedcontent');
+            hide_ac = 1;
+            FOR type IN ctx.added_content.keys;
+                IF ctx.added_content.$type.status != '2'; # not available
+                    hide_ac = 0;
+                END;
+                IF ctx.added_content.$type.status == '1';
+                    SET default_ac = type UNLESS default_ac;
+                END;
+            END;
+        END;
+
+        extras = [
+               {name => 'summaryplus',  label => l('Summaries & More'), hide => hide_summary},
+            {name => 'annotation', label => l('Annotation'), hide => 1}, 
+            {name => 'awards',  label => l('Awards, Reviews, & Suggested Reads')}, 
+            {name => 'excerpt',  label => l('Excerpt'), hide => 1},
+            {name => 'issues',   label => l('Issues Held'), hide => !(ctx.have_holdings_to_show || ctx.have_mfhd_to_show)},
+            {name => 'preview',  label => l('Preview'), hide => 1}, 
+            {name => 'addedcontent',  label => l('Additional Content'), hide => hide_ac},  # hide if all content is known to not exist
+            {name => 'cnbrowse', label => l('Shelf Browser')},
+            {name => 'marchtml', label => l('MARC Record')}
+        ];
+
+        FOREACH extra IN extras;
+            IF extra.hide; NEXT; END; 
+            name = extra.name;
+        %]
+        <div class="card border-secondary col-12 p-0">
+         [%  
+                    IF tab_is_active(name);
+                        href = mkurl('', {}, ['expand', 'ac']);
+                        arrow = arrow_down;
+                    ELSE;
+                        IF name == 'addedcontent' AND default_ac;
+                            href = mkurl('', {expand => name, ac => default_ac}) _ '#' _ name; 
+                        ELSE;
+                            href = mkurl('', {expand => name}) _ '#' _ name; 
+                        END;
+                        arrow = arrow_right;
+                    END;
+                %]
+            <a name='[% name %]' href='[% href %]' class="text-white" rel="nofollow" vocab="" style="text-decoration:none;">
+           <div class="card-header bg-success">
+               
+                <span>[% arrow %] [% extra.label | html %]</span>
+            </div>
+            </a> 
+            [%  IF tab_is_active(name); %]
+            <div class='card-body'>
+                
+                    [% IF name == 'marchtml';
+                        ctx.marchtml;
+                    ELSE;
+                        # Load the template for the selected extra
+                        INCLUDE "opac/parts/record/${name}.tt2";
+                    END;
+                    -%]
+                </div>
+                [% END; %]
+            </div>
+        [% END %]
+    </div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/record/issues-db.tt2 b/Open-ILS/src/templates/opac-new/parts/record/issues-db.tt2
new file mode 100644 (file)
index 0000000..2864161
--- /dev/null
@@ -0,0 +1,199 @@
+[%-
+expand_path = CGI.param('sepath') || [];
+expand_path = expand_path.list; # sic
+
+seoffset_list = CGI.param('seoffset') || [];
+seoffset_list = seoffset_list.list; # sic
+
+IF expand_path.size == 0 AND seoffset_list.size == 0;
+    seoffset_list = [0,0]; # compensate for $auto_expand_first; see ML
+END;
+
+selimit = CGI.param('selimit') || 10;
+ght_sepath = [];
+ght_depth = 0;
+
+VIEW grouped_holding_tree;
+    BLOCK list;
+        level_description = item.shift;
+        level_description.caption =
+            level_description.caption.replace('[\(\)]', '');
+
+        '<div class="rdetail-holding-group">';
+        prev_seoffset_list = seoffset_list.slice(0, ght_depth);
+        next_seoffset_list = seoffset_list.slice(0, ght_depth);
+
+        prev_seoffset_list.$ght_depth = prev_seoffset_list.$ght_depth - selimit;
+        IF prev_seoffset_list.$ght_depth < 0;
+            prev_seoffset_list.$ght_depth = 0;
+        END;
+
+        has_more = 0;
+        at_deepest_level = 0;
+
+        next_seoffset_list.$ght_depth = next_seoffset_list.$ght_depth + selimit;
+        IF item.0.units;
+            INCLUDE "opac/parts/record/copy_table.tt2" serial_holdings=item;
+            "<hr />";
+
+            at_deepest_level = 1;
+            IF NOT item.last.label;
+                has_more = 1;
+            END;
+        ELSE;
+            FOREACH node IN item;
+                IF NOT node.label;
+                    has_more = 1;
+                    LAST;
+                END;
+
+                IF node.value;
+                    ght_sepath.push(node.value);
+                    new_seoffsets = seoffset_list.slice(0, ght_depth);
+                    new_seoffsets.push(0);
+
+                    expand_link = mkurl(
+                        '', {'sepath' => ght_sepath, 'seoffset' => new_seoffsets},
+                        0, 'issues'
+                    );
+
+                    collapse_sepath = ght_sepath.slice(0, -2);
+                    IF collapse_sepath.size == 0;
+                        collapse_clear_params = ['sepath'];
+                    ELSE;
+                        collapse_clear_params = 0;
+                    END;
+
+                    collapse_link = mkurl(
+                        '', {
+                            'sepath' => collapse_sepath,
+                            'seoffset' => new_seoffsets.slice(0, -2)
+                        }, collapse_clear_params, 'issues'
+                    );
+
+                    "<div class='rdetail-holding-group'>";
+                    IF node.children.size;
+                        # TODO: make images or figure out a CSS trick or
+                        # something. I doubt we can count on all OPAC clients
+                        # having adequate fonts to cover these Unicode glyphs.
+                        "&#x25bc; <a href='"; collapse_link;
+                    ELSE;
+                        "&#x25ba; <a href='"; expand_link;
+                    END;
+                    "'>"; node.label; "</a></div>";
+
+                    IF node.children.size;
+                        ght_depth = ght_depth + 1;
+                        view.print(node.children);
+                        ght_depth = ght_depth - 1;
+                    END;
+
+                    waste = ght_sepath.pop;
+                ELSE;
+                    "<div class='rdetail-holding-group'>"; node.label; "</div>";
+                    at_deepest_level = 1;
+                END;
+            END;
+        END;
+
+        to_clear = 0;
+        new_sepath_end = ght_depth - 1;
+        IF new_sepath_end < 0;
+            to_clear = ['sepath'];
+            new_sepath = [];
+        ELSE;
+            new_sepath = expand_path.slice(0, ght_depth - 1);
+        END;
+
+        # So the "holdings" level of the tree is sorted ascending, while all
+        # the higher levels are sorted descending.  This seems weird until you
+        # look at it.  I dunno. I think it feels right.  It could be changed I
+        # guess.  Anyway, this means we have to be careful about which
+        # paging link we label "earlier" and which one we label "later."
+
+        next_link = ''; prev_link = '';
+        IF has_more;
+            next_link = '<a class="paging" href="' _
+                mkurl('',{seoffset => next_seoffset_list, sepath => new_sepath},to_clear,'issues') _ '">LABEL_HERE</a>&nbsp; ';
+        END;
+        IF seoffset_list.$ght_depth > 0;
+            prev_link = '<a class="paging" href="' _
+                mkurl('',{seoffset => prev_seoffset_list, sepath => new_sepath},to_clear,'issues') _ '">LABEL_HERE</a>&nbsp; ';
+        END;
+
+        IF at_deepest_level;
+            prev_link.replace('LABEL_HERE', '&laquo; ' _ l('Earlier issues'));
+            next_link.replace('LABEL_HERE', l('Later issues') _ ' &raquo;');
+        ELSE;
+            # XXX this is really bad for i18n (notice the sloppy pluralization),
+            # but then the middle layer for serials only knows English names
+            # for things like "month". There's a bigger problem to solve
+            # here...
+            caption = level_description.caption;
+            IF level_description.display_grouping == 'chron';
+                caption = caption _ 's';
+            END;
+            next_link.replace(
+                'LABEL_HERE',
+                '&laquo; ' _ l('Earlier') _ ' ' _ caption
+            );
+            prev_link.replace(
+                'LABEL_HERE',
+                l('Later') _ ' ' _ caption _ ' &raquo;'
+            );
+        END;
+        '</div>';
+    END;
+END;
+
+VIEW holding_summary_tree;
+    BLOCK hash;
+        '<div class="rdetail-holding-group">';
+        ctx.get_aou(item.org_unit).name; "<br />";
+        FOREACH summary IN item.holding_summaries;
+            IF summary.holdings;
+                twisty = '&#x25bc; ';
+                link = mkurl(
+                    '', {},
+                    ['sid','stype','selimit','sepath','seoffset'], 'issues'
+                );
+                link_title = l('Collapse');
+            ELSE;
+                twisty = '&#x25ba; ';
+                link = mkurl(
+                    '', {sid => summary.id, stype => summary.summary_type},
+                    ['selimit','sepath','seoffset'], 'issues'
+                );
+                link_title = l('Expand');
+            END;
+            '<span>'; twisty;
+            '<a href="' _ link _ '" title="' _ link_title _ '">';
+            summary.generated_coverage.join(", ");
+            '</a></span><br />';
+            IF summary.holdings;
+                grouped_holding_tree.print(summary.holdings);
+            END;
+        END;
+        FOREACH child IN item.children;
+            view.print(child);
+        END;
+        '</div>';
+    END;
+END %]
+    <div class="holding-summary-tree">
+    [% holding_summary_tree.print(ctx.holding_summary_tree) %]
+    </div>
+    <div class="holding-summary-tree-pager">
+        [%  slimit = CGI.param('slimit') || 10;
+            soffset = CGI.param('soffset') || 0;
+            soffset_prev = soffset - slimit;
+            IF soffset_prev < 0; soffset_prev = 0; END;
+            soffset_next = soffset + slimit;
+        %]
+        [% IF soffset > 0 %]
+        <a href="[% mkurl('', {soffset => soffset_prev}, ['sid','stype','sepath','selimit','seoffset'], 'issues') %]>[% l('Previous') %]</a>
+        [% END %]
+        [% IF ctx.holding_summary_tree.more %]
+        <a href="[% mkurl('', {soffset => soffset_next}, ['sid','stype','sepath','selimit','seoffset'], 'issues') %]">[% l('Next') %]</a>
+        [% END %]
+    </div>
diff --git a/Open-ILS/src/templates/opac-new/parts/record/issues-mfhd.tt2 b/Open-ILS/src/templates/opac-new/parts/record/issues-mfhd.tt2
new file mode 100644 (file)
index 0000000..bd6bb97
--- /dev/null
@@ -0,0 +1,43 @@
+[% IF ctx.mfhd_summaries.size; %]
+    <div class="rdetail-mfhd-holdings">
+        <table><tbody>
+[%
+        mfhd = {
+            basic_holdings = l('Volumes'),
+            basic_holdings_add = l('Additional Volume Information'),
+            supplement_holdings = l('Supplements'),
+            supplement_holdings_add = l('Additional Supplement Information'),
+            index_holdings = l('Indexes'),
+            index_holdings_add = l('Additional Index Information'),
+            online = l('Online'),
+            missing = l('Missing'),
+            incomplete = l('Incomplete'),
+        };
+
+        FOREACH serial IN ctx.mfhd_summaries;
+            printed_mfhd_header = 0;
+            FOREACH type IN mfhd.keys;
+                NEXT UNLESS serial.$type.size;
+                IF !printed_mfhd_header; %]
+                <tr>
+                    <td class="rdetail-mfhd-head" colspan="2">[% l('Holdings summary ([_1])', serial.location) | html %]</td>
+                </tr>
+                [% printed_mfhd_header = 1;
+                END; %]
+                <tr>
+                    <td class="rdetail-mfhd-type">[% mfhd.$type %]</td>
+                    <td class="rdetail-mfhd-contents">[%
+                        FOR thing IN serial.$type;
+                            thing.join(", ") | html;
+                        END %]</td>
+                </tr>
+            [% END;
+            IF printed_mfhd_header; %]
+                <tr>
+                    <td class="rdetail-mfhd-foot" colspan="2"> </td>
+                </tr>
+            [% END; %]
+        [% END %]
+        </tbody></table>
+    </div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/record/issues.tt2 b/Open-ILS/src/templates/opac-new/parts/record/issues.tt2
new file mode 100644 (file)
index 0000000..998e2d6
--- /dev/null
@@ -0,0 +1,8 @@
+<div class='rdetail_extras_div'>
+    [%
+        IF ctx.holding_summary_tree;
+            INCLUDE 'opac/parts/record/issues-db.tt2'; # "new" serials holdings
+        END;
+    %]
+    [% INCLUDE 'opac/parts/record/issues-mfhd.tt2' # mfhd-based "classic" serials %]
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/record/navigation.tt2 b/Open-ILS/src/templates/opac-new/parts/record/navigation.tt2
new file mode 100644 (file)
index 0000000..94a6ff5
--- /dev/null
@@ -0,0 +1,56 @@
+[% IF ctx.search_result_index.defined && ctx.search_result_index >= 0 %]
+<div class="row pt-2">
+    
+    
+    <div class="col-3">
+       <div>
+        [%
+            IF ctx.prev_search_record;
+                prev_args = {};
+                IF ctx.search_page > 0 AND 
+                        ctx.search_result_index % ((ctx.page_size * ctx.search_page)) == 0; # first record in the page
+                    prev_args.page = ctx.search_page - 1;
+                END;
+                ctx.prev_rec_url = mkurl(ctx.prev_search_record, prev_args, stop_parms);
+        %]
+        <a class='np_nav_link classic_link btn btn-outline-primary' title='[% l("Previous Record") %]'
+            href='[% ctx.prev_rec_url %]'><span class="nav_arrow_fix">&#9668;</span> <span class="d-none d-md-inline-block">[% l('Previous') %]</span></a>
+        [% END %]
+        </div>
+       </div>
+    <div class="col-6 text-center">
+       <span>
+               <a class="btn btn-confirm" href='[% mkurl(ctx.opac_root _ '/results', {}, stop_parms); %]#result[% ctx.bre_id %]'>[% l('Back To Results') %]</a>
+               <!-- stash these in JS for the web staff client to find -->
+               <script>
+               window.search_result_hit_count = [% ctx.hit_count %];
+               window.search_result_index = [% ctx.search_result_index %];
+               </script>
+       </span>
+        <br>
+        <span>
+               [% l('Showing Item [_1] of [_2]', ctx.search_result_index + 1, ctx.hit_count) %]
+        </span>
+        <br>
+        <span>
+               [%- INCLUDE "opac/parts/pref_lib_display.tt2" %]
+        </span>
+        
+    </div>
+    <div class="col-3 text-right">
+       <div>
+        [% 
+            IF ctx.next_search_record;
+                next_args = {};
+                IF (ctx.page_size * (ctx.search_page + 1)) == ctx.search_result_index + 1;
+                    next_args.page = ctx.search_page + 1;
+                END;
+                ctx.next_rec_url = mkurl(ctx.next_search_record, next_args, stop_parms);
+        %]
+        <a class='np_nav_link classic_link btn btn-outline-primary' title='[% l("Next Record") %]'
+            href='[% ctx.next_rec_url %]'><span class="d-none d-md-inline-block">[% l('Next') %]</span> <span class="nav_arrow_fix">&#9658;</span></a>
+        [% END %]
+               </div>
+    </div>
+</div>
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/record/refworks.tt2 b/Open-ILS/src/templates/opac-new/parts/record/refworks.tt2
new file mode 100644 (file)
index 0000000..b8775c7
--- /dev/null
@@ -0,0 +1,20 @@
+[%-
+    # Default to the root of the org unit tree in the absence of a specific library
+    loc = ctx.search_ou;
+
+    # Get the full name of the library
+    ou_name = ctx.get_aou(loc).name | uri;
+
+    # Generate the gross RefWorks URL, based on parameters from config.tt2
+    rw_uri = ctx.refworks.url _ 
+        '/express/expressimport.asp?vendor=' _
+        ou_name _
+        '&filter=MARC+Format&database=All+MARC+Formats&' _
+        'encoding=65001&url=http%3A%2F%2F' _
+        CGI.server_name _ 
+        '/opac/extras/supercat/marctxt/record/' _ 
+        ctx.bre_id;
+%]
+<div class="rdetail_aux_utils rdetail_refworks">
+    <a href="[% rw_uri %]" rel="nofollow" vocab=""><img src="/images/starz.png[% ctx.cache_key %]" alt="" />[% l('Export to RefWorks') %]</a>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/record/series.tt2 b/Open-ILS/src/templates/opac-new/parts/record/series.tt2
new file mode 100644 (file)
index 0000000..b9b8f4e
--- /dev/null
@@ -0,0 +1,64 @@
+[%- 
+    series_tags = ['440', '490', '800', '810', '811', '830', '694']; 
+    loc = ctx.search_ou;
+
+BLOCK render_series;
+    results = [];
+    IF attrs.hl_display_fields.series_title.size;
+    hl_field = attrs.hl_field;
+        FOREACH s IN attrs.hl_display_fields.series_title;
+            search_term = s.value.replace('[#"^$\+\-,\.:;&|\[\]()]', ' ').replace('\s+$', '') | html;
+    
+            url = mkurl(ctx.opac_root _ '/results',
+                { qtype=>'series', query=>search_term }, stop_parms.merge(expert_search_parms, general_search_parms, browse_search_parms, facet_search_parms)
+            );
+            series = '<a href="' _ url _ '">' _ s.$hl_field _ '</a> ';
+            results.push(series);
+        END;
+    ELSE;
+        FOR tag IN series_tags;
+            FOR node IN ctx.marc_xml.findnodes('//*[@tag="' _ tag _ '"]');
+                all_terms = [];
+                graphics = [];
+                series = '';
+                FOR subfield IN node.childNodes;
+                    NEXT UNLESS subfield.nodeName == "subfield";
+                    code = subfield.getAttribute('code');
+                    IF code == '6';
+                       linked_fields = [subfield.textContent()];
+                       target_field = node.getAttribute('tag');
+                       get_linked_880s;
+                    END;
+                    NEXT UNLESS code.match('[a-z]');
+                    # at this point, we actually have a partial term to use.
+                    single_term = subfield.textContent | html;
+                    all_terms.push(subfield.textContent.replace('[#"^$\+\-,\.:;&|\[\]()]', ' '));
+                    total_term = all_terms.join(" ").replace('\s+$', '');
+    
+                    url = mkurl(ctx.opac_root _ '/results',
+                        { qtype=>'series', query=>total_term }, stop_parms.merge(expert_search_parms, general_search_parms, browse_search_parms, facet_search_parms)
+                    );
+                    series = series _ '<a href="' _ url _ '">' _ single_term _ '</a> ';
+                END;
+                FOREACH link880 IN graphics;
+                    link = link880.value | html;
+                    series = series _ '<div class="graphic880"' _ link880.dir _ '>' _ link _ '</div>';
+                END;
+                results.push(series);
+            END;
+        END; 
+    END;
+    FOR entry IN results;
+    -%]
+    <li class='rdetail_series_value'>[% entry %]</li>
+    [%- END;
+END;
+%]
+
+[%- series_anchors = PROCESS render_series;
+    IF series_anchors.length > 0; %]
+<h2 class='rdetail_related_series'>[% l('Search for related items by series') %]</h2>
+<ul>
+    [% series_anchors %]
+</ul>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/record/subjects.tt2 b/Open-ILS/src/templates/opac-new/parts/record/subjects.tt2
new file mode 100644 (file)
index 0000000..f987125
--- /dev/null
@@ -0,0 +1,144 @@
+[% 
+    subjects = [
+        {
+            display_field => 'subject',
+            label => l('Subject: '),
+            xpath => '//*[@tag="600" or @tag="610" or @tag="611" or @tag="630" or @tag="650" or @tag="651"]'
+        }, {
+            display_field => 'genre',
+            label => l('Genre: '),
+            facet => 'identifier|genre',
+            joiner => ' -- ',
+            xpath => '//*[@tag="655"]',
+            subfields => '[abvxyz]'
+        }, {
+            label => l('Topic Heading: '),
+            xpath => '//*[@tag="690"]'
+        }, {
+            label => l('Geographic Setting: '),
+            xpath => '//*[@tag="691"]'
+        }, {
+            label => l('Biographical Subject: '),
+            xpath => '//*[@tag="692"]'
+        }, {
+            label => l('Character Attributes: '),
+            xpath => '//*[@tag="693"]'
+        }, {
+            label => l('Setting: '),
+            xpath => '//*[@tag="698"]'
+        }, {
+            label => l('Time Period: '),
+            xpath => '//*[@tag="699"]'
+        }
+    ];
+
+    BLOCK render_subject;
+        xpath = s.xpath || '//*[starts-with(@tag,"6")]';
+        FOR node IN ctx.marc_xml.findnodes(xpath);
+            '<span property="about">';
+            all_terms = [];
+            graphics = [];
+            FOR subfield IN node.childNodes;
+                NEXT UNLESS subfield.nodeName == "subfield";
+                code = subfield.getAttribute('code');
+                IF code == '6';
+                   linked_fields = [subfield.textContent()];
+                   target_field = node.getAttribute('tag');
+                   get_linked_880s;
+                END;
+
+                NEXT UNLESS code.match('[a-z]');
+                IF s.subfields; NEXT UNLESS code.match(s.subfields); END;
+
+                IF code.match('[vxyz]'); " &gt; "; END;
+
+                # at this point, we actually have a partial term to use.
+                raw_term = subfield.textContent;
+                single_term = raw_term | html;
+
+                # facets should be used as-is
+                IF !s.facet; raw_term = raw_term.replace('\-', ' ').replace('[#"^$\+,:;&|\[\]()]', ''); END;
+
+                all_terms.push(raw_term);
+
+                IF s.facet;
+                    total_term = s.facet _ '[' _ all_terms.join(s.joiner).replace('\s+$', '') _ ']';
+                ELSE;
+                    total_term = all_terms.join(" ").replace('\s+$', '');
+                END;
+
+                %]<a href="[%-
+                       mkurl(ctx.opac_root _ '/results', {qtype=>'subject', query=>total_term}, stop_parms.merge(expert_search_parms, general_search_parms, browse_search_parms, facet_search_parms))
+                -%]">[% single_term %]</a> [%-
+
+            END;
+            IF all_terms.size; "<br/>"; END;
+            FOREACH link880 IN graphics;
+                '<div class="graphic880"' _ link880.dir _ '>';
+                link880.value | html;
+                '</div>';
+            END;
+            '</span>';
+        END;
+    END;
+
+    BLOCK render_hl_subject;
+        total_term = s.value;
+        IF s.facet;
+            total_term = s.facet _ '[' _ s.value _ ']';
+        END;
+
+        '<span property="about"><!-- highlighted -->';
+        %]<a href="[%-
+               mkurl(ctx.opac_root _ '/results', {qtype=>'subject', query=>total_term}, stop_parms.merge(expert_search_parms, general_search_parms, browse_search_parms, facet_search_parms))
+        -%]">[% s.$f %]</a> [%-
+        '</span>';
+    END;
+%]
+
+[%  BLOCK render_all_subjects;
+    FOREACH subj IN subjects;
+        content = '';
+        df = subj.display_field;
+        IF df AND attrs.hl_display_fields.$df.size;
+            content = [];
+            FOREACH hl_s IN attrs.hl_display_fields.$df;
+                hl_s.facet = subj.facet;
+                next_s = PROCESS render_hl_subject(s=hl_s,f=attrs.hl_field);
+                content.push(next_s);
+            END;
+
+            content = content.join('<br/>');
+%]
+            <table class='rdetail_subject'>
+                <tbody>
+                    <tr>
+                        <td class='rdetail_subject_type'>[% subj.label %]</td>
+                        <td class='rdetail_subject_value'>[% content %]</td>
+                    </tr>
+                </tbody>
+            </table>
+[%          
+        ELSE;
+            content = PROCESS render_subject(s=subj);
+            IF content.match('\S');
+%]
+        <table class='rdetail_subject'>
+            <tbody>
+                <tr>
+                    <td class='rdetail_subject_type'>[% subj.label %]</td>
+                    <td class='rdetail_subject_value'>[% content %]</td>
+                </tr>
+            </tbody>
+        </table>
+            [%- END; %]
+        [%- END; %]
+    [%- END; %]
+[%- END %]
+
+[%-  subject_html = PROCESS render_all_subjects;
+    IF subject_html.length > 0;
+%]
+<h2 class='rdetail_related_subjects'>[% l('Search for related items by subject') %]</h2>
+[%- subject_html %]
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/record/summary.tt2 b/Open-ILS/src/templates/opac-new/parts/record/summary.tt2
new file mode 100644 (file)
index 0000000..44ec36a
--- /dev/null
@@ -0,0 +1,581 @@
+[% PROCESS "opac/parts/misc_util.tt2";
+USE ResolverResolver;
+ctx.page_title = attrs.title | html
+ctx.metalinks.push('
+<meta property="og:image" content="' _ ctx.media_prefix _ '/opac/extras/ac/jacket/large/r/' _ ctx.bre_id _ '" />');
+%]
+<!-- ****************** rdetail_summary.xml ***************************** -->
+<abbr class="unapi-id" title='tag:[% ctx.hostname %],[% date.format(date.now, ' %Y') %]:biblio-record_entry/[%
+    ctx.bre_id %]'></abbr>
+
+<hr />
+
+[%-# This holds the record summary information %]
+<div class="row">
+    <div class="col-lg-3">
+        <div id="rdetail_image_div" class="w-100 text-center">
+            [%- IF obalkyknih_cz.enabled == 'true' %]
+            [%-
+            isbnissn = '';
+            IF attrs.isbns.0;
+            isbnissn = attrs.isbns.0;
+            IF (matches = isbnissn.match('^(.+?)(\s.+)$'));
+            isbnissn = matches.0;
+            END;
+            END;
+            IF attrs.issns.0;
+            isbnissn=attrs.issns.0;
+            END;
+            %]
+            <a href='http://obalkyknih.cz/view?isbn=[% isbnissn %]'>
+                <img alt="[% l('Image of item') %]" class='img-thumbnail w-auto'
+                    src='[% ctx.media_prefix %]/opac/extras/ac/jacket/[% record.summary.jacket_size %]/r/[% ctx.bre_id | uri %]' />
+            </a>
+            [% ELSE %]
+            <a href='[% ctx.media_prefix %]/opac/extras/ac/jacket/large/r/[% ctx.bre_id | uri %]'><img
+                    alt="[% l('Image of item') %]" class='img-thumbnail w-auto'
+                    src='[% ctx.media_prefix %]/opac/extras/ac/jacket/[% record.summary.jacket_size %]/r/[% ctx.bre_id | uri %]' />
+            </a>
+            [%- END %]
+
+            [%- IF attrs.format_label %]
+            [% FOR format IN attrs.all_formats %]
+            <span style="font-size:14px;" class="mt-3 d-block">
+                <img title="[% format.label | html %]" alt="[% format.label | html %]" src="[% format.icon %]" />
+                [% format.label | html %]
+            </span>
+            [% END %]
+            [%- END %]
+
+
+
+           
+        </div>
+    </div>
+
+    <div class="col-lg-9">
+        <div class="row h-100">
+            <div class="col-lg-8">
+                <h1 class='h1' property="name">[% IF attrs.hl.title; attrs.hl.title; ELSE; attrs.title_extended | html; END %]</h1>
+                [%-
+                FOR link880 IN attrs.graphic_titles;
+                FOR alt IN link880.graphic;
+                '<h2 class="graphic880"';
+                    IF alt.dir;
+                        ' dir="' _ alt.dir _ '"';
+                    END;
+                    '>'; alt.value | html; '</h2>';
+                END;
+                END;
+                -%]
+                [%- INCLUDE "opac/parts/record/authors.tt2" %]
+                <div>
+                    <button id="btnMore" data-toggle="collapse" data-target="#demo"
+                        class="btn btn-action btn-sm my-3">More Details</button>
+                    <div id="demo" class="collapse">
+                        <h2 id='rdetail_record_details'>[% l("Record details") %]</h2>
+                        <ul class="list-unstyled">
+                            [%- IF attrs.hl.isbn.size; FOR isbn IN attrs.hl.isbn %]
+                            <li class='rdetail_isbns'>
+                                <strong class='rdetail_label'>[% l('ISBN:'); %]</strong> [% isbn %]
+                            </li>
+                            [%- END; ELSIF attrs.isbns.0;
+                            FOR isbn IN attrs.isbns;
+                            isbn_extra = '';
+                            IF (matches = isbn.match('^(.+?)(\s.+)$'));
+                            isbn = matches.0;
+                            isbn_extra = matches.1;
+                            END;
+                            %]
+                            <li class='rdetail_isbns'>
+                                <strong class='rdetail_label'>[% l('ISBN:'); %]</strong>
+                                <span class='rdetail_value' property='isbn'>[% isbn | html %]</span>[% isbn_extra | html
+                                %]
+                            </li>
+                            [%- END %]
+                            [%- END %]
+                            [%- IF attrs.hl.issn.size; FOR issn IN attrs.hl.issn %]
+                            <li class='rdetail_issns'>
+                                <strong class='rdetail_label'>[% l('ISSN:'); %]</strong> [% issn %]
+                            </li>
+                            [%- END; ELSIF attrs.issns.0; FOR issn IN attrs.issns %]
+                            <li class='rdetail_issns'>
+                                <strong class='rdetail_label'>[% l('ISSN:'); %]</strong>
+                                <span class='rdetail_value'>[% issn | html %]</span>
+                            </li>
+                            [%- END %]
+                            [%- END %]
+                            [%- IF attrs.hl.physical_description.size %]
+                            <li id='rdetail_phys_desc'>
+                                <strong class='rdetail_label'>[% l("Physical Description:") %]</strong>
+                                <span class='rdetail_value' highlighted='true'>[%
+                                    attrs.hl.physical_description.join('<br />') %]</span>
+                            </li>
+                            [%- ELSIF attrs.phys_desc %]
+                            <li id='rdetail_phys_desc'>
+                                <strong class='rdetail_label'>[% l("Physical Description:") %]</strong>
+                                <span class='rdetail_value'>[% attrs.phys_desc | html %]</span>
+                            </li>
+                            [%- END %]
+                            [%- IF attrs.hl.edition %]
+                            <li id='rdetail_edition'>
+                                <strong class='rdetail_label'>[% l("Edition:") %]</strong>
+                                <span class='rdetail_value' highlighted='true'>[% attrs.hl.edition %]</span>
+                                [%- ELSIF attrs.edition %]
+                            <li id='rdetail_edition'>
+                                <strong class='rdetail_label'>[% l("Edition:") %]</strong>
+                                <span class='rdetail_value'>[% attrs.edition | html %]</span>
+                                [%-
+                                FOR entry IN attrs.graphic_editions;
+                                FOR alt IN entry.graphic;
+                                diratt = "";
+                                IF alt.dir;
+                                diratt = ' dir="' _ alt.dir _ '"';
+                                END;
+                                -%]
+                                <div class="graphic880 rdetail_value" [% diratt %]>
+                                    [% alt.value | html %]
+                                </div>
+                                [%-
+                                END;
+                                END;
+                                -%]
+                            </li>
+                            [%- END %]
+                            [%- IF attrs.hl.publisher %]
+                            <li id='rdetail_publisher'>
+                                <strong class='rdetail_label'>[% l("Publisher:") %]</strong>
+                                <span class='rdetail_value' highlighted='true'>[% attrs.hl.publisher %]</span>
+                            </li>
+                            [%- ELSIF attrs.publisher %]
+                            <li id='rdetail_publisher'>
+                                <strong class='rdetail_label'>[% l("Publisher:") %]</strong>
+                                <span class='rdetail_value' property="publisher" typeof="Organization">
+                                    [%- IF attrs.pubplace; %]
+                                    <span property="location">[% attrs.pubplace | html; %]</span>
+                                    [%- END; %]
+                                    <span property="name">[% attrs.publisher | html; %]</span>
+                                </span>
+                                [%- IF attrs.pubdate; %]
+                                <span property="datePublished">[% attrs.pubdate | html; %]</span>
+                                [%- END; %]
+                                [%-
+                                IF attrs.graphic_pubinfos.size > 0;
+                                FOR entry IN attrs.graphic_pubinfos;
+                                FOR alt IN entry.graphic;
+                                diratt = "";
+                                IF alt.dir;
+                                diratt = ' dir="' _ alt.dir _ '"';
+                                END;
+                                -%]
+                                <div class="graphic880" [% diratt %]>
+                                    [% alt.value | html %]
+                                </div>
+                                [%-
+                                END;
+                                END;
+                                END
+                                -%]
+                            </li>
+                            [%- END %]
+                            [%- IF attrs.producer %]
+                            <li id='rdetail_producer'>
+                                <strong class='rdetail_label'>[% l("Producer:") %]</strong>
+                                <span class='rdetail_value'>
+                                    [%- IF attrs.prodplace; %]
+                                    <span>[% attrs.prodplace | html; %]</span>
+                                    [%- END; %]
+                                    <span>[% attrs.producer | html; %]</span>
+                                    [%- IF attrs.proddate; %]
+                                    <span>[% attrs.proddate | html; %]</span>
+                                    [%- END; %]
+                                </span>
+                            </li>
+                            [%- END %]
+                            [%- IF attrs.distributor %]
+                            <li id='rdetail_distributor'>
+                                <strong class='rdetail_label'>[% l("Distributor:") %]</strong>
+                                <span class='rdetail_value'>
+                                    [%- IF attrs.distplace; %]
+                                    <span>[% attrs.distplace | html; %]</span>
+                                    [%- END; %]
+                                    <span>[% attrs.distributor | html; %]</span>
+                                    [%- IF attrs.distdate; %]
+                                    <span>[% attrs.distdate | html; %]</span>
+                                    [%- END; %]
+                                </span>
+                            </li>
+                            [%- END %]
+                            [%- IF attrs.manufacturer %]
+                            <li id='rdetail_manufacturer'>
+                                <strong class='rdetail_label'>[% l("Manufacturer:") %]</strong>
+                                <span class='rdetail_value' property="manufacturer" typeof="Organization">
+                                    [%- IF attrs.manplace; %]
+                                    <span property="location">[% attrs.manplace | html; %]</span>
+                                    [%- END; %]
+                                    <span property="name">[% attrs.manufacturer | html; %]</span>
+                                    [%- IF attrs.mandate; %]
+                                    <span>[% attrs.mandate | html; %]</span>
+                                    [%- END; %]
+                                </span>
+                            </li>
+                            [%- END %]
+                            [%- IF attrs.copyright %]
+                            <li id='rdetail_copyright'>
+                                <strong class='rdetail_label'>[% l("Copyright:") %]</strong>
+                                <span class='rdetail_value'>[% attrs.copyright | html_entity; %]
+                                    [%-# Provide the 4-digit year, cleansed of '@' and other junk %]
+                                    [%- IF attrs.copyrightYear -%]
+                                    <meta property='copyrightYear' content='[% attrs.copyrightYear | html; %]'>
+                                    [%- END -%]
+                                </span>
+                            </li>
+                            [%- END %]
+                            [%- IF (ctx.badge_scores.size > 0 && ctx.hide_badge_scores != 'true') %]
+                            <li id='rdetail_badges'>
+                                <strong class='rdetail_label'>[% l("Badges:") %]</strong>
+                                <ul>
+                                    [% FOR bscore IN ctx.badge_scores; %]
+                                    <li><strong>[% bscore.badge.name | html %]</strong>: [% bscore.score %] / 5.0</li>
+                                    [%- END -%]
+                                </ul>
+                            </li>
+                            [%- END %]
+                        </ul>
+
+
+                        [%- INCLUDE "opac/parts/record/contents.tt2" %]
+                        [%- INCLUDE "opac/parts/record/subjects.tt2" %]
+                        [%- INCLUDE "opac/parts/record/series.tt2" %]
+                        <div>
+                            [%-
+                            # l( 'mmr id = ' _ ctx.mmr_id );
+                            # l( 'mmr data = ' _ ctx.mmr_data );
+                            mmr_attrs = {marc_xml => ctx.mmr_data.marc_xml};
+                            PROCESS get_marc_attrs args=mmr_attrs;
+
+                            IF args.mmr_unique_bib.size > 1;
+                            %]
+                            <h3>[% l("More Options") %]</h3>
+                            <span class="metarecord_population_format">
+                                [%- IF mmr_attrs.format_label;
+
+                                FOR format IN mmr_attrs.all_formats;
+
+                                link = mkurl(ctx.opac_root _ '/record/' _ format.source_bibs.0);
+                                IF format.source_bibs.size > 1;
+                                link = mkurl( ctx.opac_root _ '/results', { modifier => 'metabib', metarecord =>
+                                ctx.mmr_id, 'fi:icon_format' => format.search_format, 'fi:from_metarecord' => ctx.mmr_id
+                                }, stop_parms.merge(expert_search_parms, general_search_parms, browse_search_parms,
+                                facet_search_parms,
+                                ['qtype','fi:search_format','fi:icon_format','fi:item_lang','fi:from_metarecord']) );
+                                END; -%]
+                                <span class="metarecord_population_span_link">
+                                    <a href="[%- l( link ) %]">
+                                        [% format.label | html %]
+                                        ([%- l( format.source_bibs.size ) %])
+                                    </a></span><br />
+
+                                [%- END %]
+                            </span>
+                            [%- IF mmr_attrs.all_lang.size > 0 %]
+                            <span class="metarecord_population_item_lang">
+                                [% FOR lang IN mmr_attrs.all_lang;
+                                link = mkurl(ctx.opac_root _ '/record/' _ lang.source_bibs.0);
+                                IF lang.source_bibs.size > 1;
+                                USE url(ctx.opac_root _ '/results');
+                                link = mkurl( ctx.opac_root _ '/results', { modifier => 'metabib', metarecord =>
+                                ctx.mmr_id, 'fi:item_lang' => lang.search_format, 'fi:from_metarecord' => ctx.mmr_id },
+                                stop_parms.merge(expert_search_parms, general_search_parms, browse_search_parms,
+                                facet_search_parms,
+                                ['qtype','fi:search_format','fi:icon_format','fi:item_lang','fi:from_metarecord']) );
+                                END; -%]
+                                <span class="metarecord_population_span_link">
+                                    <a href="[%- l( link ) %]">
+                                        [% lang.label | html %]
+                                        ([%- l( lang.source_bibs.size ) %])
+                                    </a></span><br />
+                                [%- END %]
+                            </span> <!-- metarecord_population_item_lang -->
+                            [%- END %]
+                            <div class="metarecord_population_all">
+                                [%
+                                link = mkurl( ctx.opac_root _ '/results', { modifier => 'metabib', metarecord =>
+                                ctx.mmr_id, 'fi:from_metarecord' => ctx.mmr_id }, stop_parms.merge(expert_search_parms,
+                                general_search_parms, browse_search_parms, facet_search_parms,
+                                ['qtype','fi:search_format','fi:icon_format','fi:item_lang','fi:from_metarecord'] ) );
+                                %]
+                                <span class="metarecord_population_span_link">
+                                    <a href="[%- l( link ) %]">[% l("View all formats and editions ") %] ([%-
+                                        args.mmr_unique_bib.size %])
+                                    </a></span><br />
+                            </div>
+                            [%- END;
+                            END # ending tag for IF args.mmr_unique_bib.size > 1; %]
+                        </div>
+
+                    </div>
+                    
+                </div>
+                <script>jQuery('#demo').on('hidden.bs.collapse', function () {
+                        jQuery('#btnMore').html("More Details");
+                    })
+                    jQuery('#demo').on('shown.bs.collapse', function () {
+                        jQuery('#btnMore').html("Less Details");
+                    })
+                    </script>
+
+               <div id="copy_hold_counts">
+
+                [%-
+                # Hold/copy summary
+                IF ctx.copy_summary.0.count;
+                INCLUDE "opac/parts/record/copy_counts.tt2";
+                %]
+
+
+                [%- END %]
+
+                </div> <!-- metarecord_population -->
+            </div>
+            <!--rdetail_title_div-->
+
+            <div class="col-lg-4 text-center">
+             <div class="mx-auto btn-group btn-group-vertical dropright mt-3">
+                [%- search_ou = ctx.search_ou;
+                IF ctx.place_unfillable ||
+                ( attrs.marc_xml.findnodes('//*[local-name()="holdings" and @has_holdable="true"]').size
+                && (ctx.holds_block.enabled != 'true' || !attrs.org_copy_counts.$search_ou.available)
+                )
+                %]
+                <button class="btn btn-action">
+                    <a href="[% mkurl(ctx.opac_root _ '/place_hold', 
+                    {hold_target => ctx.bre_id, hold_type => 'T', hold_source_page => mkurl()}, stop_parms) %]"
+                        class="no-dec" rel="nofollow" vocab=""><img
+                            src="[% ctx.media_prefix %]/images/green_check.png[% ctx.cache_key %]" [% img_alt(l('Place
+                            Hold on [_1]', attrs.title)) %] />
+                        <span class="place_hold">[% l('Place Hold') %]</span></a>
+                </button>
+                [%- END -%]
+
+                [%- IF ebook_api.enabled == 'true' && args.ebook %]
+                <button class="btn btn-action">
+                    <div id="[%- ctx.bre_id -%]_ebook_checkout" class="rdetail_aux_utils ebook_action hidden">
+                        <a href="[% mkurl(ctx.opac_root _ '/myopac/ebook_checkout',
+                    {title => args.ebook.ebook_id, vendor => args.ebook.vendor, action => 'checkout'}, stop_parms) %]"
+                            class="no-dec" rel="nofollow" vocab=""><img
+                                src="[% ctx.media_prefix %]/images/green_check.png[% ctx.cache_key %]" [%
+                                img_alt(l('Check Out [_1]', attrs.title)) %] />
+                            <span class="place_hold">[% l('Check Out E-Item') %]</span></a>
+                    </div>
+                </button>
+                <button class="btn btn-action">
+                    <div id="[%- ctx.bre_id -%]_ebook_place_hold" class="rdetail_aux_utils ebook_action hidden">
+                        <a href="[% mkurl(ctx.opac_root _ '/myopac/ebook_place_hold',
+                    {title => args.ebook.ebook_id, vendor => args.ebook.vendor, action => 'place_hold'}, stop_parms) %]"
+                            class="no-dec" rel="nofollow" vocab=""><img
+                                src="[% ctx.media_prefix %]/images/green_check.png[% ctx.cache_key %]" [%
+                                img_alt(l('Place Hold on [_1]', attrs.title)) %] />
+                            <span class="place_hold">[% l('Place Hold on E-Item') %]</span></a>
+                    </div>
+                </button>
+                [%- END -%]
+
+
+                <div class="btn btn-action">
+                    [% operation = ctx.mylist.grep('^' _ ctx.bre_id _ '$').size ? "delete" : "add";
+                    addhref = mkurl(ctx.opac_root _ '/mylist/add', {record => ctx.bre_id}, stop_parms);
+                    delhref = mkurl(ctx.opac_root _ '/mylist/delete', {record => ctx.bre_id}, stop_parms);
+                    label = (operation == "add") ? l("Add to Basket") : l("Remove from Basket");
+                    %]
+                    <a href="[% addhref %]" id="mylist_add_[% ctx.bre_id %]" rel="nofollow" vocab=""
+                        data-recid="[% ctx.bre_id %]" data-action="add"
+                        class="no-dec mylist_action [% IF ctx.mylist.grep('^' _ ctx.bre_id _ '$').size %]hidden[% END %]"
+                        title="[% l(" Add [_1] to basket", attrs.title) %]" rel="nofollow" vocab="">
+                        <img src="[% ctx.media_prefix %]/images/add-to-cart.png[% ctx.cache_key %]" alt="" />
+                        [% l("Add to basket") %]
+                    </a>
+                    <a href="[% delhref %]" id="mylist_delete_[% ctx.bre_id %]" rel="nofollow" vocab=""
+                        data-recid="[% ctx.bre_id %]" data-action="delete"
+                        class="mylist_action [% IF !ctx.mylist.grep('^' _ ctx.bre_id _ '$').size %]hidden[% END %]"
+                        title="[% l(" Remove [_1] from basket", attrs.title) %]" rel="nofollow" vocab="">
+                        <img src="[% ctx.media_prefix %]/images/add-to-cart.png[% ctx.cache_key %]" alt="" />
+                        [% l("Remove from basket") %]
+                    </a>
+                </div>
+                [% IF ctx.mylist.size %]
+                <button class="btn btn-action">
+                    [%- IF ctx.user; %]
+                    <a href="[% mkurl(ctx.opac_root _ '/myopac/lists') %]" class="no-dec" rel="nofollow" vocab=""><img
+                            src="[% ctx.media_prefix %]/images/clipboard.png[% ctx.cache_key %]"
+                            alt="[% l('View Basket') %]" />[% l(' View Basket') %]</a>
+                    [%- ELSE %]
+                    <a href="[% mkurl(ctx.opac_root _ '/mylist') %]" class="no-dec" rel="nofollow" vocab=""><img
+                            src="[% ctx.media_prefix %]/images/add-to-cart.png[% ctx.cache_key %]"
+                            alt="[% l('View My Basket') %]" />[% l(' View My Basket') %]</a>
+                    [%- END %]
+                </button>
+                [% END %]
+                <div class="btn btn-action">
+                    [% IF !ctx.is_staff %]
+                    [% IF ctx.user;
+                    INCLUDE "opac/parts/bookbag_actions.tt2";
+                    END;
+                    %]
+                    [% END %]
+                </div>
+                <div class="btn btn-action">
+                    <img src="[% ctx.media_prefix %]/images/clipboard.png[% ctx.cache_key %]"
+                        alt="[% l('Print / Email Actions Image') %]" />
+                    <a href="[% mkurl(ctx.opac_root _ '/record/print/' _ ctx.bre_id) %]" class="no-dec" rel="nofollow"
+                        vocab="">[% l('Print') %]</a> /
+                    <a href="[% mkurl(ctx.opac_root _ '/record/email/' _ ctx.bre_id) %]" class="no-dec" rel="nofollow"
+                        vocab="">[% l('Email') %]</a>
+                </div>
+                [%- IF ctx.refworks.enabled == 'true' %]
+                [%- INCLUDE 'opac/parts/record/refworks.tt2' %]
+                [%- END %]
+                [% IF !ctx.is_staff %]
+                <button class="btn btn-action">
+                    <a href="[% mkurl('', {locg =>CGI.param('locg'), copy_depth =>CGI.param('copy_depth')}, 1) %]"
+                        class="no-dec">
+                        <img src="[% ctx.media_prefix %]/images/link.png[% ctx.cache_key %]"
+                            alt="[% l('Permalink') %]" />
+                        [% l('Permalink') %]
+                    </a>
+                </button>
+                [% END %]
+                [% IF !search.no_highlight %]
+                <button class="btn btn-action">
+                    [% IF CGI.param('no_highlight') %]
+                    <a href="[% mkurl('', {}, ['no_highlight']) %]" class="no-dec">
+                        [% ELSE %]
+                        <a href="[% mkurl('', {no_highlight => '1'}) %]" class="no-dec">
+                            [% END %]
+                            <img src="[% ctx.media_prefix %]/images/highlight.png[% ctx.cache_key %]"
+                                alt="[% l('Toggle highlighting') %]" />
+                            [% CGI.param('no_highlight') ? l('Enable Highlighting') : l('Disable Highlighting') %]
+                        </a>
+                </button>
+                [% END %]
+                [%- IF ctx.is_staff %]
+                <button class="btn btn-action">
+                    <a href="[% ctx.media_prefix %]/opac/extras/ac/clearcache/all/r/[% ctx.bre_id | uri %]"
+                        class="no-dec" target="_blank">
+                        [% l('Clear AddedContent Cache') %]
+                    </a>
+                </button>
+                [%- END %]
+                
+            </div>
+            
+        </div>
+    </div>
+</div>
+</div>
+<div class="row">
+<div class="col-12">
+        
+<div class="float-right">
+                        <span>
+                            <h2 class="text-right">[% l('Holds') %]</h2>
+                            <p class="text-right">
+                                [%
+                                # 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>
+                    </div>
+
+        [%-
+        IF ctx.copy_summary.0.count;
+        INCLUDE "opac/parts/record/copy_table.tt2" copies=ctx.copies;
+        END;
+        %]
+
+
+        [%- IF openurl.enabled == 'true';
+        openurls = [];
+        FOREACH issn IN args.issns;
+        NEXT IF issn == '';
+        openurls = openurls.import(ResolverResolver.resolve_issn(issn, openurl.baseurl));
+        END;
+        IF openurls.size && openurls.0 != '';
+        %]
+        <div id='rdetail_openurl'>
+            <strong class='rdetail_openurl_title'>[% l("Electronic resources") %]</strong>
+            <table>
+                <tbody>
+                    [%-
+                    FOREACH res IN openurls;
+                    %]
+                    <tr>
+                        <td class='rdetail_openurl_entry'><a href="[% res.target_url %]">[% res.public_name | html
+                                %]</a></td>
+                        <td>[% res.target_coverage | html %]
+                            [%- IF res.target_embargo != '';
+                            ' - ';
+                            res.target_embargo | html;
+                            END;
+                            -%]
+                        </td>
+                    </tr>
+                    [%- END %]
+                </tbody>
+            </table>
+        </div>
+        [%- END %]
+        [%- END %]
+        [%- merged_uris = args.uris.merge(args.online_res);
+        num_uris = merged_uris.size;
+        IF num_uris > 0;
+        -%]
+
+        <h2 class="rdetail_uris">[% l("Electronic resources") %]</h2>
+        <div class="rdetail_uris">
+            [%- IF num_uris > 1 %]<ul>[% END %]
+                [%- FOR uri IN merged_uris %]
+                [%- IF num_uris == 1 -%]
+                <p class="rdetail_uri" property="offers" vocab="http://schema.org/" typeof="Offer">
+                    [%- ELSE -%]
+                    <li class="rdetail_uri" property="offers" vocab="http://schema.org/" typeof="Offer">
+                        [%- END -%]
+                        <a href="[% uri.href %]" class="uri_link" property="url">
+                            [%- IF uri.href != uri.link;
+                            '<span property="description">' _ uri.link _ '</span>';
+                            ELSE;
+                            uri.link;
+                            END;
+                            END;
+                            -%]
+                        </a>
+                        [%-# ' - <span property="description">' _ uri.note _ '</span>' IF uri.note %]
+                        <link property="availability" href="http://schema.org/OnlineOnly" />
+                        [%- IF attrs.gtin13; '
+                        <meta property="gtin13" content="' _ attrs.gtin13 _ '" />'; END; %]
+                        [%- IF num_uris == 1 %]</p>[% ELSE %]</li>[% END %]
+                [%- END %]
+                [%- IF num_uris > 1 %]</ul>[% END %]
+        </div>
+        [%
+        IF ebook_api.enabled == 'true';
+        INCLUDE "opac/parts/ebook_api/avail.tt2";
+        END;
+        %]
+    </div>
+<hr>
+<div class="col-12">
+    [%- INCLUDE "opac/parts/record/extras.tt2" %]
+</div>
+</div>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/parts/record/summaryplus.tt2 b/Open-ILS/src/templates/opac-new/parts/record/summaryplus.tt2
new file mode 100644 (file)
index 0000000..23e21ea
--- /dev/null
@@ -0,0 +1,20 @@
+<div class='rdetail_extras_div'> 
+    [%- IF attrs.summary %]
+    <div class='rdetail-extras-summary'>
+        <strong>[% l('Summary: ') %]</strong>
+        [% FOR sum IN attrs.summaries %]
+            [% IF !loop.first; '<br/>'; END %]
+            <span>[% sum | html %] </span>
+        [% END %]
+    </div>
+    [%- END %]
+    [%- IF ENV.OILS_CONTENT_CAFE_USER %]
+    <!-- Embedded content cafe iframe -->
+    [% ident = attrs.isbn_clean || attrs.upc %]
+    <iframe width="100%" height="400" frameborder="0" 
+        src="[% ctx.ext_proto %]://contentcafe2.btol.com/ContentCafeClient/ContentCafe.aspx?UserID=[%- 
+            ENV.OILS_CONTENT_CAFE_USER %]&amp;Password=[% ENV.OILS_CONTENT_CAFE_PASS %]&amp;ItemKey=[% ident | uri %]&amp;Options=Y" >
+    </iframe>
+    [%- END %]
+</div>
+
diff --git a/Open-ILS/src/templates/opac-new/parts/relators.tt2 b/Open-ILS/src/templates/opac-new/parts/relators.tt2
new file mode 100644 (file)
index 0000000..ff9550d
--- /dev/null
@@ -0,0 +1,276 @@
+[%-  
+# Generated from http://www.loc.gov/marc/relators/relacode.html
+# using the build/tools/relator_map script
+relators = {
+    'abr' => l('Abridger'),
+    'acp' => l('Art copyist'),
+    'act' => l('Actor'),
+    'adi' => l('Art director'),
+    'adp' => l('Adapter'),
+    'aft' => l('Author of afterword, colophon, etc.'),
+    'anl' => l('Analyst'),
+    'anm' => l('Animator'),
+    'ann' => l('Annotator'),
+    'ant' => l('Bibliographic antecedent'),
+    'ape' => l('Appellee'),
+    'apl' => l('Appellant'),
+    'app' => l('Applicant'),
+    'aqt' => l('Author in quotations or text abstracts'),
+    'arc' => l('Architect'),
+    'ard' => l('Artistic director'),
+    'arr' => l('Arranger'),
+    'art' => l('Artist'),
+    'asg' => l('Assignee'),
+    'asn' => l('Associated name'),
+    'ato' => l('Autographer'),
+    'att' => l('Attributed name'),
+    'auc' => l('Auctioneer'),
+    'aud' => l('Author of dialog'),
+    'aui' => l('Author of introduction, etc.'),
+    'aus' => l('Screenwriter'),
+    'aut' => l('Author'),
+    'bdd' => l('Binding designer'),
+    'bjd' => l('Bookjacket designer'),
+    'bkd' => l('Book designer'),
+    'bkp' => l('Book producer'),
+    'blw' => l('Blurb writer'),
+    'bnd' => l('Binder'),
+    'bpd' => l('Bookplate designer'),
+    'brd' => l('Broadcaster'),
+    'brl' => l('Braille embosser'),
+    'bsl' => l('Bookseller'),
+    'cas' => l('Caster'),
+    'ccp' => l('Conceptor'),
+    'chr' => l('Choreographer'),
+    'clb' => l('Collaborator'),
+    'cli' => l('Client'),
+    'cll' => l('Calligrapher'),
+    'clr' => l('Colorist'),
+    'clt' => l('Collotyper'),
+    'cmm' => l('Commentator'),
+    'cmp' => l('Composer'),
+    'cmt' => l('Compositor'),
+    'cnd' => l('Conductor'),
+    'cng' => l('Cinematographer'),
+    'cns' => l('Censor'),
+    'coe' => l('Contestant-appellee'),
+    'col' => l('Collector'),
+    'com' => l('Compiler'),
+    'con' => l('Conservator'),
+    'cor' => l('Collection registrar'),
+    'cos' => l('Contestant'),
+    'cot' => l('Contestant-appellant'),
+    'cou' => l('Court governed'),
+    'cov' => l('Cover designer'),
+    'cpc' => l('Copyright claimant'),
+    'cpe' => l('Complainant-appellee'),
+    'cph' => l('Copyright holder'),
+    'cpl' => l('Complainant'),
+    'cpt' => l('Complainant-appellant'),
+    'cre' => l('Creator'),
+    'crp' => l('Correspondent'),
+    'crr' => l('Corrector'),
+    'crt' => l('Court reporter'),
+    'csl' => l('Consultant'),
+    'csp' => l('Consultant to a project'),
+    'cst' => l('Costume designer'),
+    'ctb' => l('Contributor'),
+    'cte' => l('Contestee-appellee'),
+    'ctg' => l('Cartographer'),
+    'ctr' => l('Contractor'),
+    'cts' => l('Contestee'),
+    'ctt' => l('Contestee-appellant'),
+    'cur' => l('Curator'),
+    'cwt' => l('Commentator for written text'),
+    'dbp' => l('Distribution place'),
+    'dfd' => l('Defendant'),
+    'dfe' => l('Defendant-appellee'),
+    'dft' => l('Defendant-appellant'),
+    'dgg' => l('Degree granting institution'),
+    'dgs' => l('Degree supervisor'),
+    'dis' => l('Dissertant'),
+    'dln' => l('Delineator'),
+    'dnc' => l('Dancer'),
+    'dnr' => l('Donor'),
+    'dpc' => l('Depicted'),
+    'dpt' => l('Depositor'),
+    'drm' => l('Draftsman'),
+    'drt' => l('Director'),
+    'dsr' => l('Designer'),
+    'dst' => l('Distributor'),
+    'dtc' => l('Data contributor'),
+    'dte' => l('Dedicatee'),
+    'dtm' => l('Data manager'),
+    'dto' => l('Dedicator'),
+    'dub' => l('Dubious author'),
+    'edc' => l('Editor of compilation'),
+    'edm' => l('Editor of moving image work'),
+    'edt' => l('Editor'),
+    'egr' => l('Engraver'),
+    'elg' => l('Electrician'),
+    'elt' => l('Electrotyper'),
+    'eng' => l('Engineer'),
+    'enj' => l('Enacting jurisdiction'),
+    'etr' => l('Etcher'),
+    'evp' => l('Event place'),
+    'exp' => l('Expert'),
+    'fac' => l('Facsimilist'),
+    'fds' => l('Film distributor'),
+    'fld' => l('Field director'),
+    'flm' => l('Film editor'),
+    'fmd' => l('Film director'),
+    'fmk' => l('Filmmaker'),
+    'fmo' => l('Former owner'),
+    'fmp' => l('Film producer'),
+    'fnd' => l('Funder'),
+    'fpy' => l('First party'),
+    'frg' => l('Forger'),
+    'gis' => l('Geographic information specialist'),
+    'grt' => l('Graphic technician'),
+    'his' => l('Host institution'),
+    'hnr' => l('Honoree'),
+    'hst' => l('Host'),
+    'ill' => l('Illustrator'),
+    'ilu' => l('Illuminator'),
+    'ins' => l('Inscriber'),
+    'inv' => l('Inventor'),
+    'isb' => l('Issuing body'),
+    'itr' => l('Instrumentalist'),
+    'ive' => l('Interviewee'),
+    'ivr' => l('Interviewer'),
+    'jud' => l('Judge'),
+    'jug' => l('Jurisdiction governed'),
+    'lbr' => l('Laboratory'),
+    'lbt' => l('Librettist'),
+    'ldr' => l('Laboratory director'),
+    'led' => l('Lead'),
+    'lee' => l('Libelee-appellee'),
+    'lel' => l('Libelee'),
+    'len' => l('Lender'),
+    'let' => l('Libelee-appellant'),
+    'lgd' => l('Lighting designer'),
+    'lie' => l('Libelant-appellee'),
+    'lil' => l('Libelant'),
+    'lit' => l('Libelant-appellant'),
+    'lsa' => l('Landscape architect'),
+    'lse' => l('Licensee'),
+    'lso' => l('Licensor'),
+    'ltg' => l('Lithographer'),
+    'lyr' => l('Lyricist'),
+    'mcp' => l('Music copyist'),
+    'mdc' => l('Metadata contact'),
+    'med' => l('Medium'),
+    'mfp' => l('Manufacture place'),
+    'mfr' => l('Manufacturer'),
+    'mod' => l('Moderator'),
+    'mon' => l('Monitor'),
+    'mrb' => l('Marbler'),
+    'mrk' => l('Markup editor'),
+    'msd' => l('Musical director'),
+    'mte' => l('Metal-engraver'),
+    'mtk' => l('Minute taker'),
+    'mus' => l('Musician'),
+    'nrt' => l('Narrator'),
+    'opn' => l('Opponent'),
+    'org' => l('Originator'),
+    'orm' => l('Organizer'),
+    'osp' => l('Onscreen presenter'),
+    'oth' => l('Other'),
+    'own' => l('Owner'),
+    'pan' => l('Panelist'),
+    'pat' => l('Patron'),
+    'pbd' => l('Publishing director'),
+    'pbl' => l('Publisher'),
+    'pdr' => l('Project director'),
+    'pfr' => l('Proofreader'),
+    'pht' => l('Photographer'),
+    'plt' => l('Platemaker'),
+    'pma' => l('Permitting agency'),
+    'pmn' => l('Production manager'),
+    'pop' => l('Printer of plates'),
+    'ppm' => l('Papermaker'),
+    'ppt' => l('Puppeteer'),
+    'pra' => l('Praeses'),
+    'prc' => l('Process contact'),
+    'prd' => l('Production personnel'),
+    'pre' => l('Presenter'),
+    'prf' => l('Performer'),
+    'prg' => l('Programmer'),
+    'prm' => l('Printmaker'),
+    'prn' => l('Production company'),
+    'pro' => l('Producer'),
+    'prp' => l('Production place'),
+    'prs' => l('Production designer'),
+    'prt' => l('Printer'),
+    'prv' => l('Provider'),
+    'pta' => l('Patent applicant'),
+    'pte' => l('Plaintiff-appellee'),
+    'ptf' => l('Plaintiff'),
+    'pth' => l('Patent holder'),
+    'ptt' => l('Plaintiff-appellant'),
+    'pup' => l('Publication place'),
+    'rbr' => l('Rubricator'),
+    'rcd' => l('Recordist'),
+    'rce' => l('Recording engineer'),
+    'rcp' => l('Addressee'),
+    'rdd' => l('Radio director'),
+    'red' => l('Redaktor'),
+    'ren' => l('Renderer'),
+    'res' => l('Researcher'),
+    'rev' => l('Reviewer'),
+    'rpc' => l('Radio producer'),
+    'rps' => l('Repository'),
+    'rpt' => l('Reporter'),
+    'rpy' => l('Responsible party'),
+    'rse' => l('Respondent-appellee'),
+    'rsg' => l('Restager'),
+    'rsp' => l('Respondent'),
+    'rsr' => l('Restorationist'),
+    'rst' => l('Respondent-appellant'),
+    'rth' => l('Research team head'),
+    'rtm' => l('Research team member'),
+    'sad' => l('Scientific advisor'),
+    'sce' => l('Scenarist'),
+    'scl' => l('Sculptor'),
+    'scr' => l('Scribe'),
+    'sds' => l('Sound designer'),
+    'sec' => l('Secretary'),
+    'sgd' => l('Stage director'),
+    'sgn' => l('Signer'),
+    'sht' => l('Supporting host'),
+    'sll' => l('Seller'),
+    'sng' => l('Singer'),
+    'spk' => l('Speaker'),
+    'spn' => l('Sponsor'),
+    'spy' => l('Second party'),
+    'srv' => l('Surveyor'),
+    'std' => l('Set designer'),
+    'stg' => l('Setting'),
+    'stl' => l('Storyteller'),
+    'stm' => l('Stage manager'),
+    'stn' => l('Standards body'),
+    'str' => l('Stereotyper'),
+    'tcd' => l('Technical director'),
+    'tch' => l('Teacher'),
+    'ths' => l('Thesis advisor'),
+    'tld' => l('Television director'),
+    'tlp' => l('Television producer'),
+    'trc' => l('Transcriber'),
+    'trl' => l('Translator'),
+    'tyd' => l('Type designer'),
+    'tyg' => l('Typographer'),
+    'uvp' => l('University place'),
+    'vac' => l('Voice actor'),
+    'vdg' => l('Videographer'),
+    'voc' => l('Vocalist'),
+    'wac' => l('Writer of added commentary'),
+    'wal' => l('Writer of added lyrics'),
+    'wam' => l('Writer of accompanying material'),
+    'wat' => l('Writer of added text'),
+    'wdc' => l('Woodcutter'),
+    'wde' => l('Wood engraver'),
+    'win' => l('Writer of introduction'),
+    'wit' => l('Witness'),
+    'wpr' => l('Writer of preface'),
+    'wst' => l('Writer of supplementary textual content'),
+} -%]
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/parts/result/adv_filter.tt2 b/Open-ILS/src/templates/opac-new/parts/result/adv_filter.tt2
new file mode 100644 (file)
index 0000000..7f21117
--- /dev/null
@@ -0,0 +1,85 @@
+[%-
+
+pubdate_filters = ['date1', 'before', 'after', 'between'];
+
+FOR filter IN ctx.query_struct.filters;
+    fname = filter.name;
+    fvalues = filter.args;
+    crad = ctx.get_crad(fname);
+
+    # will be some special ones, like locations
+    IF crad AND NOT pubdate_filters.grep('^' _ filter.name _ '$').size;
+        remove_filter = 'fi:' _ fname;
+-%]
+         <div class="adv_filter_results_group_wrapper" id="1">
+           <div class="adv_filter_results_group">
+           <div class="adv_filter_results_group_header"> <h5 class="title">[% IF filter.negate; l('Not'); END %] [% (crad.description || crad.label) | html %]</h5></div>
+           <div class="adv_filter_results_group_values d-inline-block align-middle"> [% temp = [];
+               FOR fval IN fvalues;
+                thing = ctx.search_ccvm('ctype',fname,'code',fval).0;
+                display_value = thing.search_label || thing.value;
+                IF display_value.defined;
+                 temp.push(display_value);
+                END;
+               END;
+               FOR display_value IN temp.sort;
+            %]
+                 <span class="adv_search_result_filter">
+                    [% display_value | html %]
+                 </span>
+                 [% UNLESS loop.last %]
+                  <span class="adv_search_result_filter"> [% l('OR') %] </span>
+                 [% END %]
+            [% END; # FOR %]
+             </div>
+              <a class="button remove_filter btn btn-deny btn-sm"
+              title="[% l('Remove [_1] filter', (crad.description || crad.label)) %]"
+              aria-label="[% l('Remove [_1] filter', (crad.description || crad.label)) %]" 
+              href="[% mkurl('', {}, [remove_filter]) %]" rel="nofollow" vocab=""><span class="text-white">&times;</span> </a>
+            </div>
+           </div>
+           </div>
+    [%- END; # IF crad -%]
+
+[%-  IF filter.name == 'locations'; locs = ctx.search_acpl('id',filter.args) -%]
+    <div class="adv_filter_results_group_wrapper" id="2">
+      <div class="adv_filter_results_group">
+            <h4 class="title">[% IF filter.negate; l('Not'); END %] </h4>
+            [% temp = [];
+               FOR loc IN locs;
+                temp.push(loc.name);
+               END;
+               FOR display_name IN temp.sort; %]
+                  <span class="adv_search_result_filter">
+                    [% display_name | html%]
+                  </span>
+            [% END; # FOR %]
+            <a class="button remove_filter btn btn-deny btn-sm"
+              title="[% l('Remove location filter') %]"
+              href="[% mkurl('', {}, ['fi:locations']) %]" rel="nofollow" vocab=""> <span class="text-white">×</span></a>
+      </div>
+    </div> 
+[%- END; # IF locations -%]
+
+[%- IF pubdate_filters.grep('^' _ filter.name _ '$').size;
+    date1 = CGI.param('date1') | html;
+    date2 = CGI.param('date2') | html;
+-%]
+    <div class="adv_filter_results_group_wrapper" id="3">
+      <div class="adv_filter_results_group">
+            <h5 class="title">[% IF filter.negate; l('Not'); END %] [% l('Publication Year') %]</h5>
+              <span class="adv_search_result_filter">
+              [% IF    filter.name == 'date1'      %][% l('[_1]', date1) %]
+              [% ELSIF filter.name == 'before'  %][% l('Before [_1]', date1) %]
+              [% ELSIF filter.name == 'after'   %][% l('After [_1]', date1) %]
+              [% ELSIF filter.name == 'between' %][% l('Between [_1] and [_2]', date1, date2) %]
+              [% END %]
+              </span>
+              <a class="button remove_filter btn btn-deny btn-sm"
+              title="[% l('Remove publication date filter') %]"
+              href="[% mkurl('', {}, ['pubdate', 'date1', 'date2']) %]" rel="nofollow" vocab=""><span class="text-white">×</span> </a>
+      </div>
+    </div>
+
+[%- END; # IF pubdate_filters -%]
+[%- END; # FOR -%]
diff --git a/Open-ILS/src/templates/opac-new/parts/result/copy_counts.tt2 b/Open-ILS/src/templates/opac-new/parts/result/copy_counts.tt2
new file mode 100644 (file)
index 0000000..dc8aab7
--- /dev/null
@@ -0,0 +1,41 @@
+[%- depths = attrs.copy_counts.size;
+    depth = 0;
+    displayed_ous = {};
+    hiding_disabled = ctx.org_hiding_disabled();
+    WHILE depth < depths;
+        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 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' %]
+    [% l('[_1] of [quant,_2,copy,copies] available at [_3].',
+        attrs.copy_counts.$depth.available,
+        attrs.copy_counts.$depth.count,
+        ou_name) | html
+    %]
+[% END %]
+</div>
+[%-     END;
+    depth = depth + 1;
+    END;
+
+    depth = attrs.plib_copy_counts.size - 1;
+    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 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,
+        attrs.plib_copy_counts.$depth.count,
+        ou_name) | html
+    %]
+</div>
+[%- END %]
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/result/facets-hz.tt2 b/Open-ILS/src/templates/opac-new/parts/result/facets-hz.tt2
new file mode 100644 (file)
index 0000000..30646dc
--- /dev/null
@@ -0,0 +1,153 @@
+<div class="container-fluid my-3">
+<div class="facet_box_wrapper row">
+[% 
+
+long_facets = CGI.param('long_facet') || [];
+selected_facets = CGI.param('facet') || [];
+
+# sorted list of search facets
+sorted_facets = [];
+
+# we'll clobber the facet. "namespace" later
+# Provide a default value if unset in config.tt2
+DEFAULT_DISPLAY_COUNT = facet.default_display_count || 5;
+
+IF facet.display;
+
+    # facet display configuration present.  Traverse the 
+    # configuration lists, find the matching facets from 
+    # search facet data, and append them to the sorted 
+    # list of facets.
+
+    FOR display_chunk IN facet.display;
+        FOR display_field IN display_chunk.facet_order;
+            # find the matching facet in the facet data
+            FOR facet IN ctx.search_facets.values;
+                IF facet.cmf.field_class == display_chunk.facet_class 
+                        AND facet.cmf.name == display_field;
+                    sorted_facets.push(facet);
+                END;
+            END;
+        END;
+    END;
+
+ELSE; 
+
+    # No facet display configuration is present.
+    # show all facets, sorted by name.
+
+    # collect facet type labels for easier sorting
+    labels = []; 
+    FOR facet IN ctx.search_facets.values;
+        labels.push(facet.cmf.label);
+    END;
+    
+    # We used to do a sort here, but now that's handled in EGCatLoader.
+    sorted_facets = ctx.search_facets.values;
+END;
+
+display_count_by_cmf = {};
+
+FOR facet IN sorted_facets;
+    fclass = facet.cmf.field_class;
+    fname = facet.cmf.name;
+    fid = facet.cmf.id;
+    long_key = fclass _ fname %]
+
+    <div id="facets" class="col-xs-12 col-sm-12 col-md-6 col-lg-3">
+    <div class="card">
+        <div class="card-header">
+               <h4 class="title text-center">[% facet.cmf.label %]</h4>
+            <span class="float-right">
+                [% IF long_facets.grep(long_key).0;
+                    new_long = [];
+                    FOR fct IN long_facets;
+                        IF fct != long_key;
+                            new_long.push(fct);
+                        END;
+                    END;
+                    expand_url = mkurl('', {long_facet => new_long});
+                    IF new_long.size == 0;
+                        expand_url  = mkurl('', {}, ['long_facet']);
+                    END;
+                %]
+                    <a class="button" href="[% expand_url %]#facets"
+                      title="[% l('Show Fewer [_1] Entries', facet.cmf.label) %]" rel="nofollow" vocab="">
+                      [% l("Fewer") %]
+                    </a>
+                [% ELSIF facet.data.size > DEFAULT_DISPLAY_COUNT %]
+                    <a class="button" 
+                      title="[% l('Show More [_1] Entries', facet.cmf.label) %]"
+                      href="[% mkurl('', {long_facet => long_facets.merge([long_key])}) %]#facets" rel="nofollow" vocab="">
+                      [% l("More") %]
+                    </a>
+                [% END %]
+            </span>
+        </div>
+        <div class="card-body" style="max-height:250px;overflow-x:overlay;">
+            <div>
+            [% FOR facet_data IN facet.data;
+                display_value = facet_data.value | html;
+                param_string = fclass _ '|' _ fname _ '[' _ facet_data.value _ ']';
+                new_facets = [];
+                this_selected = 0;
+                FOR selected IN selected_facets;
+                    IF selected == param_string; 
+                        this_selected = 1; 
+                    ELSE;
+                        new_facets.push(selected);
+                    END;
+                END;
+
+                display_count_by_cmf.$fid = display_count_by_cmf.$fid || 0;
+
+                NEXT UNLESS long_facets.grep(long_key).0 OR
+                    display_count_by_cmf.$fid < DEFAULT_DISPLAY_COUNT;
+
+                # fix syntax highlighting: >
+
+                display_count_by_cmf.$fid = display_count_by_cmf.$fid + 1;
+
+                IF this_selected;
+                    # This facet is already selected by the user. 
+                    # Link removes the facet from the set of selected facets.
+                %] 
+                    <div class="facet_template facet_template_selected">
+                        <div class="facet">
+                            [% IF new_facets.size == 0 %]
+                            <a
+                              title="[% l('Show all results, not just those matching this term') %]"
+                              href="[% mkurl('', {}, ['facet']) %]" rel="nofollow" vocab="">[% display_value %]</a>
+                            [% ELSE %]
+                            <a
+                              title="[% l('Expand to also show results not matching this term') %]"
+                              href="[% mkurl('', {facet => new_facets}) %]" rel="nofollow" vocab="">[% display_value %]</a>
+                            [% END %]
+                        </div>
+                        <div class="count">([% facet_data.count; IF facet_data.count == (ctx.superpage + 1) * ctx.superpage_size; '+'; END %])</div>
+                    </div>
+                [% 
+                    ELSE;
+                    # This facet is not currently selected.  If selected, 
+                    # append this facet to the list of currently active facets.
+                %]
+                    <div class="facet_template">
+                        <div class="facet">
+                            <a
+                              title="[% l('Limit to results matching this term') %]"
+                              href='[% mkurl('', {facet => selected_facets.merge([param_string])}, ['page']) %]' rel="nofollow" vocab="">
+                              [% display_value %]
+                            </a>
+                        </div>
+                        <div class="count">([% facet_data.count %])</div>
+                    </div>
+                    
+                [% END %]
+            [% END %]
+            </div>
+            </div>
+        </div> <!-- box_wrapper -->
+    </div> <!-- facet_box_temp -->
+[% END %]
+</div> <!-- facet_box_wrapper -->
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/result/facets.tt2 b/Open-ILS/src/templates/opac-new/parts/result/facets.tt2
new file mode 100644 (file)
index 0000000..d57426a
--- /dev/null
@@ -0,0 +1,156 @@
+<div class="container-fluid my-3">
+<div class="facet_box_wrapper row">
+[% 
+
+long_facets = CGI.param('long_facet') || [];
+selected_facets = CGI.param('facet') || [];
+
+# sorted list of search facets
+sorted_facets = [];
+
+# we'll clobber the facet. "namespace" later
+# Provide a default value if unset in config.tt2
+DEFAULT_DISPLAY_COUNT = facet.default_display_count || 5;
+
+IF facet.display;
+
+    # facet display configuration present.  Traverse the 
+    # configuration lists, find the matching facets from 
+    # search facet data, and append them to the sorted 
+    # list of facets.
+
+    FOR display_chunk IN facet.display;
+        FOR display_field IN display_chunk.facet_order;
+            # find the matching facet in the facet data
+            FOR facet IN ctx.search_facets.values;
+                IF facet.cmf.field_class == display_chunk.facet_class 
+                        AND facet.cmf.name == display_field;
+                    sorted_facets.push(facet);
+                END;
+            END;
+        END;
+    END;
+
+ELSE; 
+
+    # No facet display configuration is present.
+    # show all facets, sorted by name.
+
+    # collect facet type labels for easier sorting
+    labels = []; 
+    FOR facet IN ctx.search_facets.values;
+        labels.push(facet.cmf.label);
+    END;
+    
+    # We used to do a sort here, but now that's handled in EGCatLoader.
+    sorted_facets = ctx.search_facets.values;
+END;
+
+display_count_by_cmf = {};
+
+FOR facet IN sorted_facets;
+    fclass = facet.cmf.field_class;
+    fname = facet.cmf.name;
+    fid = facet.cmf.id;
+    long_key = fclass _ fname %]
+
+    <div id="facets" class="w-100">
+    <div class="card">
+        <div class="card-header">
+               <span class="title text-center">[% facet.cmf.label %]</span>
+            
+        </div>
+        <div class="card-body">
+            <div>
+            [% FOR facet_data IN facet.data;
+                display_value = facet_data.value | html;
+                param_string = fclass _ '|' _ fname _ '[' _ facet_data.value _ ']';
+                new_facets = [];
+                this_selected = 0;
+                FOR selected IN selected_facets;
+                    IF selected == param_string; 
+                        this_selected = 1; 
+                    ELSE;
+                        new_facets.push(selected);
+                    END;
+                END;
+
+                display_count_by_cmf.$fid = display_count_by_cmf.$fid || 0;
+
+                NEXT UNLESS long_facets.grep(long_key).0 OR
+                    display_count_by_cmf.$fid < DEFAULT_DISPLAY_COUNT;
+
+                # fix syntax highlighting: >
+
+                display_count_by_cmf.$fid = display_count_by_cmf.$fid + 1;
+
+                IF this_selected;
+                    # This facet is already selected by the user. 
+                    # Link removes the facet from the set of selected facets.
+                %] 
+                    <div class="facet_template facet_template_selected">
+                        <div class="facet">
+                            [% IF new_facets.size == 0 %]
+                            <a
+                              title="[% l('Show all results, not just those matching this term') %]"
+                              href="[% mkurl('', {}, ['facet']) %]" rel="nofollow" vocab="">[% display_value %]</a>
+                            [% ELSE %]
+                            <a
+                              title="[% l('Expand to also show results not matching this term') %]"
+                              href="[% mkurl('', {facet => new_facets}) %]" rel="nofollow" vocab="">[% display_value %]</a>
+                            [% END %]
+                        </div>
+                        <div class="count">([% facet_data.count; IF facet_data.count == (ctx.superpage + 1) * ctx.superpage_size; '+'; END %])</div>
+                    </div>
+                [% 
+                    ELSE;
+                    # This facet is not currently selected.  If selected, 
+                    # append this facet to the list of currently active facets.
+                %]
+                    <div class="facet_template">
+                        <div class="facet">
+                            <a
+                              title="[% l('Limit to results matching this term') %]"
+                              href='[% mkurl('', {facet => selected_facets.merge([param_string])}, ['page']) %]' rel="nofollow" vocab="">
+                              [% display_value %]
+                            </a>
+                        </div>
+                        <div class="count">([% facet_data.count %])</div>
+                    </div>
+                    
+                [% END %]
+                
+            [% END %]</div>
+            <span class="float-right">
+                [% IF long_facets.grep(long_key).0;
+                    new_long = [];
+                    FOR fct IN long_facets;
+                        IF fct != long_key;
+                            new_long.push(fct);
+                        END;
+                    END;
+                    expand_url = mkurl('', {long_facet => new_long});
+                    IF new_long.size == 0;
+                        expand_url  = mkurl('', {}, ['long_facet']);
+                    END;
+                %]
+                    <a class="button" href="[% expand_url %]#facets"
+                      title="[% l('Show Fewer [_1] Entries', facet.cmf.label) %]" rel="nofollow" vocab="">
+                      [% l("Fewer") %]
+                    </a>
+                [% ELSIF facet.data.size > DEFAULT_DISPLAY_COUNT %]
+                    <a class="button" 
+                      title="[% l('Show More [_1] Entries', facet.cmf.label) %]"
+                      href="[% mkurl('', {long_facet => long_facets.merge([long_key])}) %]#facets" rel="nofollow" vocab="">
+                      [% l("More") %]
+                    </a>
+                [% END %]
+            </span>
+            </div>
+            
+        </div> <!-- box_wrapper -->
+    </div> <!-- facet_box_temp -->
+[% END %]
+
+</div> <!-- facet_box_wrapper -->
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/result/lowhits.tt2 b/Open-ILS/src/templates/opac-new/parts/result/lowhits.tt2
new file mode 100644 (file)
index 0000000..e9155ce
--- /dev/null
@@ -0,0 +1,69 @@
+<div>
+    <div id="zero_search_hits">
+        <div class="facet_sidebar_hidden" id="facet_sidebar">
+          <h3 class="sr-only">[% l('Search Results filters') %]</h3>
+        </div>
+        <div class="zero_search_hits_saved">
+            [% INCLUDE "opac/parts/staff_saved_searches.tt2" %]
+        </div>
+        <div class="zero_search_hits_main">
+            <p>[% qhtml = CGI.param('query') | html;
+                  IF ctx.bookbag;
+                      wbbag = ctx.bookbag.name | html;
+                      fmt_bookbag = '<span class="lowhits-bookbag-name">' _ wbbag _ '</span>';
+                       IF is_advanced OR is_special;
+                         l('Sorry, no entries were found for your search within [_1].', fmt_bookbag);
+                       ELSE;
+                          l('Sorry, no entries were found for [_1] within [_2].', '<q>' _ qhtml _ '</q>', fmt_bookbag);
+                       END;
+                   ELSE;
+                     IF is_advanced OR is_special;
+                          l('Sorry, no entries were found for your search.');
+                       ELSE;
+                          IF !qhtml;
+                             l('Please enter a search term in the Search box.');
+                          ELSE;
+                             l('Sorry, no entries were found for [_1].', '<q>' _ qhtml _ '</q>');
+                          END;
+                       END;
+                   END %]
+            </p>
+        </div>
+        <div id="lowhits_help">
+        [% INCLUDE "opac/parts/result/lowhits_purchase.tt2" %]
+            <p>
+                <strong>[% l('Keyword Search Tips') %]</strong><br />
+               [% i18n_advsearch = l('Advanced Search');
+                   l('Try changing to [_1].', '<strong>' _ i18n_advsearch _ '</strong>') %]
+            </p>
+            <p>
+                <strong>[% l('Adjacency') %]</strong><br />
+                [% l('Multiple words are not searched together as a phrase. They will ' _
+                    'be found in various parts of the record. To search for a phrase, enclose your ' _
+                    'search terms in quotation marks.') %]<br />
+               [% i18n_searchphrase = l('garcia marquez');
+                   l('(example: [_1])', '<strong>&quot;' _ i18n_searchphrase _ '&quot;</strong>') %]
+            </p>
+            <p>
+                <strong>[% l('Truncation') %]</strong><br />
+                [% l('Words may be right-hand truncated using an asterisk. Use a single asterisk * ' _
+                    'to truncate any number of characters.') %]<br />
+               [% i18n_searchtrunc = l('environment* agency');
+                   l('(example: [_1])', '<strong>' _ i18n_searchtrunc _ '</strong>') %]
+            </p>
+            <p>
+                <strong>[% l('Anchored Searching') %]</strong><br />
+                [% l('You may use ^ and $ to indicate "phrase begins with" and ' _
+                    '"phrase ends with," respectively, within a search phrase ' _
+                    'enclosed in quotation marks.') %]<br />
+               [% i18n_searchbegins = l('harry');
+                  i18n_searchends = l('stone');
+                   l('(examples: [_1] for phrases that begin with the term [_2]. ' _
+                     '[_3] for phrases that end in [_4].)',
+                    '<strong>&quot;^' _ i18n_searchbegins _ '&quot;</strong>', '<em>' _ i18n_searchbegins _ '</em>',
+                    '<strong>&quot;' _ i18n_searchends _ '$&quot;</strong>', '<em>' _ i18n_searchends _ '</em>') %]
+            </p>
+            [% INCLUDE "opac/parts/result/lowhits_purchase.tt2" %]
+        </div>
+    </div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/result/lowhits_purchase.tt2 b/Open-ILS/src/templates/opac-new/parts/result/lowhits_purchase.tt2
new file mode 100644 (file)
index 0000000..ff34338
--- /dev/null
@@ -0,0 +1,6 @@
+[%# XXX Is this block generally desired? <p>
+    <strong>Still not finding what you are looking for?</strong><br />
+    Request that your library purchase the material you are looking for by making a
+    <a href="javascript:;">Purchase Request</a><br />
+    <strong>Note:</strong> You must be logged in to make a Purchase Request<br />
+</p> %]
diff --git a/Open-ILS/src/templates/opac-new/parts/result/paginate.tt2 b/Open-ILS/src/templates/opac-new/parts/result/paginate.tt2
new file mode 100644 (file)
index 0000000..6c0c9d3
--- /dev/null
@@ -0,0 +1,67 @@
+[% BLOCK results_count_header %]
+<div class="results_header_nav1 text-center py-4">
+    [%- INCLUDE "opac/parts/pref_lib_display.tt2" %]
+    
+    <nav aria-label="Page navigation">
+                <ul class='start_end_links_span pagination d-inline-flex'>
+
+                    [%- class = 'search_page_nav_link';
+                        href = '#';
+                        IF page > 0;
+                            href = mkurl('', {page => page - 1});
+                    -%]
+                    <li class="page-item"><a class='page-link [% class %]' href='[% href %]' 
+                        title='[% l("Previous page") %]'><span class="nav_arrow_fix">&#9668;</span> <span class="paginateText"> [% l('Previous') %]</span> </a></li>
+                    [%- END;
+
+                        # show links to 4 previous pages, the current page, and 3 more pages.
+                        added = 0;
+                        pageitr = page - 5; 
+                        IF page_count > 1; # no need to paginate 1 page
+                    -%]
+                    
+                    [%-
+                                WHILE (pageitr = pageitr + 1) < page_count;
+                                    IF pageitr < 0; NEXT; END;
+                                    IF pageitr == page; %]
+                                       <li class="page-item active"> <span class='results-paginator-selected page-link disabled'>[% pageitr + 1%]</span></li>
+                                    [% ELSE %]
+                                        <li class="page-item"><a href="[% mkurl('', {page => pageitr}) %]" class="page-link">[% pageitr + 1%]</a></li>
+                                [%  END;
+                                    added = added + 1;
+                                    #Number of pages to paginate
+                                    IF added == 5; LAST; END;
+                                END;
+                    -%]
+                    
+                    [%- END;
+
+                        class = 'search_page_nav_link';
+                        href = '#';
+                        IF (page + 1) < page_count;
+                            href = mkurl('', {page => page + 1});
+                    -%]
+                    <li class="page-item"><a class='page-link [% class %]' href='[% href %]' 
+                        title='[% l("Next page") %]'>
+                        <span class="paginateText">[% l('Next') %] </span><span class="nav_arrow_fix">&#9658;</span></a></li>
+                    [%- END; -%]
+                </ul>
+                </nav>
+    
+    <span class="result_number">
+                [%~ IF ctx.hit_count == ctx.pagable_limit; ctx.hit_count = ctx.hit_count _ '+'; END ~%]
+                [%~ |l('<span class="result_count_number">' _ ctx.result_start _'</span>',
+                '<span class="result_count_number">' _ ctx.result_stop _ '</span>',
+                '<span class="result_count_number">' _ ctx.hit_count _ '</span>')  ~%]
+                Results [_1] - [_2] of [_3]
+                [%~ END %]
+                <span class='padding-left-6'>
+                    [%~ |l('<span class="result_count_number">' _ (page + 1) _ '</span>',
+                    '<span class="result_count_number">' _ page_count _ '</span>') ~%]
+                    (page [_1] of [_2])
+                    [%~ END %]
+                </span>
+    </span>
+</div>
+[% END %]
+
diff --git a/Open-ILS/src/templates/opac-new/parts/result/table.tt2 b/Open-ILS/src/templates/opac-new/parts/result/table.tt2
new file mode 100644 (file)
index 0000000..2f33490
--- /dev/null
@@ -0,0 +1,525 @@
+[%  PROCESS "opac/parts/misc_util.tt2";
+
+    USE ResolverResolver;
+
+    ctx.result_start = 1 + ctx.page_size * page;
+    ctx.result_stop = ctx.page_size * (page + 1);
+    IF ctx.result_stop > ctx.hit_count; ctx.result_stop = ctx.hit_count; END;
+
+    result_count = ctx.result_start;
+
+%]
+<div class="row">
+    <div class="col-12">
+    [% PROCESS "opac/parts/result/paginate.tt2" %] 
+    [% ctx.results_count_header = PROCESS results_count_header;
+        ctx.results_count_header %]
+    </div>
+</div>
+<div class="row">
+<div class="col-xl-2 d-none d-xl-block">
+[% INCLUDE 'opac/parts/result/facets.tt2' %]
+</div>
+<div id="result_table_div" class="border-bottom col-12 col-xl-10 px-3">
+    <div id="result_block" class="result_block_visible">
+        [% IF !ctx.is_meta %]
+        <div id="record_selector_block" class="mx-2 hidden">
+            <input type="checkbox" id="select_all_records"></input>
+            <label for="select_all_records">[% l('Select [_1] - [_2]', ctx.result_start, ctx.result_stop) %]</label>
+            <span id="selected_records_summary">
+            <a href="[% mkurl(ctx.opac_root _ '/mylist') %]" class="no-dec" rel="nofollow" vocab="">
+                <span id="selected_records_count">[% ctx.mylist.size %]
+                   
+                </span>
+                        [% l('selected') %]
+            </a>
+            <span id="hit_selected_record_limit" class="hidden">Reached limit!</span>
+            <a id="clear_basket" href="#">[% l('Clear basket') %]</a>
+        </div>
+        [% END %]
+
+        <table id="result_table_table" title="[% l('Search Results') %]" class="table_no_border_space container-fluid">
+            <thead class="sr-only">
+                <tr>
+                <th>[% l('Search result number') %]</th>
+                <th>[% l('Book jacket cover art') %]</th>
+                <th>[% l('Item details and Actions') %]</th>
+                </tr>
+            </thead>
+
+            <tbody id="result_table">
+                [%  FOR rec IN ctx.records;
+                    attrs = {};
+                    attrs.marc_xml = rec.marc_xml;
+                    attrs.mr_constituent_ids = [];
+                        FOREACH mr_constituent_id IN rec.mr_constituent_ids;
+                            attrs.mr_constituent_ids.push(mr_constituent_id);
+                        END;
+                        PROCESS get_marc_attrs args=attrs;
+                        IF show_detail_view;
+                            attrs.title = attrs.title_extended;
+                        END;
+                        # note: rec.id refers to the record identifier, regardless
+                        # of the type of record. i.e. rec.id = mmr_id ? mmr_id : bre_id
+                        IF rec.mmr_id;
+                            IF rec.mr_constituent_count > 1;
+                                # metarecords link to record list page
+                                record_url_path = ctx.opac_root _ '/results'; 
+                                add_parms = {metarecord => rec.mmr_id};
+                                del_parms = ['page'];
+                            ELSE;
+                                # for MR, bre_id refers to the master and in
+                                # this case, only, record
+                                    record_url_path = ctx.opac_root _ '/record/' _ attrs.mr_constituent_ids.0;
+                                add_parms = { badges => rec.badges.join(',') };
+                                del_parms = ['quux'];
+                            END;
+                            hold_type = 'M';
+                        ELSE;
+                        record_url_path = ctx.opac_root _ '/record/' _ rec.bre_id; 
+                        add_parms = { badges => rec.badges.join(',') };
+                        del_parms = ['quux'];
+                        hold_type = 'T';
+                        END; -%]
+                        
+                <tr class="result_table_row">
+                    <td>
+                        <!--Count&Checkboxes-->
+                        <div class="row">
+                            <div class="col-lg-1">
+                                <div class="results_row_count" name="results_row_count"> [% IF !ctx.is_meta; %]
+                                    <label for="selected_record">[% result_count; result_count = result_count + 1 %]</label>
+                                    <input type="checkbox" id="select-[% rec.bre_id %]" name="selected_record"
+                                        [% IF is_selected %] checked="checked" [% END %]
+                                        title="[% l('Add to Basket') %]"
+                                        aria-label="[% l('Add to Basket') %]"
+                                        class="result_record_selector" value="[% rec.bre_id %]">
+                                    </input>
+                                    [% END %]
+                                    
+                                </div>
+                            </div>   
+
+                       
+                            <!--Cover Image-->
+                            <div class='my-auto col-lg-2 text-center' id="result[% rec.bre_id %]">
+                                <a href="[% record_url %]"><img alt="[% l('Book cover') %]"
+                                        name='item_jacket' class='my-2' 
+                                        src='[% ctx.media_prefix %]/opac/extras/ac/jacket/large/r/[% rec.bre_id | uri %]' style="max-height:150px;" /></a><br />
+                            </div>       
+                            <!--Info-->
+                            <div class='result_table_title_cell col-lg-9' name='result_table_title_cell'>
+                                <div class="row">
+                                    <div class="result_metadata col-md-9 col-12">
+                                        [% IF rec.mmr_id %]
+                                        <abbr class="unapi-id" title='tag:[% ctx.hostname %],[% date.format(date.now, '%Y') %]:metabib-metarecord/[% rec.mmr_id %]'></abbr>
+                                        [% ELSE %]
+                                        <abbr class="unapi-id" title='tag:[% ctx.hostname %],[% date.format(date.now, '%Y') %]:biblio-record_entry/[% rec.bre_id %]'></abbr>
+                                        [% END %]
+                                        <a class='record_title search_link' name='record_[% rec.id %]'
+                                            href="[% mkurl(record_url_path, add_parms, del_parms); %]"
+                                            [% html_text_attr('title', l('Display record details for "[_1]"', attrs.title)) %]>
+                                            [% IF attrs.hl.title; attrs.hl.title; ELSE; attrs.title | html; END %]
+                                        </a>
+                                        
+                                       
+                                        [%- UNLESS show_detail_view;
+                                            IF attrs.pubdate;
+                                                pubdate_clean = attrs.pubdate | html;
+                                                l(" ([_1])", pubdate_clean);
+                                            ELSIF attrs.copyright;
+                                                copyright_clean = attrs.copyright | html;
+                                                l(" ([_1])", copyright_clean);                                                                
+                                            END;
+                                        END -%]
+                                        
+                                        [% IF rec.mr_constituent_count.defined && rec.mr_constituent_count > 1 %]
+                                        <span title="[% l('This group contains [_1] records', rec.mr_constituent_count) %]">([% rec.mr_constituent_count %])</span>
+                                        [% END %]
+
+                                        [%- FOR entry IN attrs.graphic_titles;
+                                                FOR alt IN entry.graphic;
+                                                diratt = "";
+                                                IF alt.dir;
+                                                    diratt = ' dir="' _ alt.dir _ '"';
+                                                END; -%]
+                                        <div class="graphic880"[% diratt %]>
+                                            [% alt.value | html %]
+                                        </div>
+                                        [%- END; END; -%]
+                                        <div>
+                                            <a  title="[% l("Perform an Author Search") %]"
+                                                class="record_author"
+                                                href="[%- 
+                                                authorquery = attrs.author | replace('[#"^$\+\-,\.:;&|\[\]()]', ' ');
+                                                mkurl(ctx.opac_root _ '/results', {qtype => 'author', query => authorquery}, general_search_parms.merge(expert_search_parms, browse_search_parms, facet_search_parms))
+                                                -%]" rel="nofollow" vocab="">[% IF attrs.hl.author; attrs.hl.author; ELSE; attrs.author | html; END %]</a>
+                                                [%-
+                                                FOR entry IN attrs.graphic_authors;
+                                                    FOR alt IN entry.graphic;
+                                                        diratt = "";
+                                                        IF alt.dir;
+                                                            diratt = ' dir="' _ alt.dir _ '"';
+                                                        END;
+                                                -%]
+                                            <div class="graphic880"[% diratt %]>
+                                                [% alt.value | html %]
+                                            </div>
+                                            [%- END; END; -%]
+                                        </div>
+                                                    
+                                        <div class='result_table_title_cell'>
+                                            [%- IF attrs.format_label; %]
+                                                [% FOR format IN attrs.all_formats %]
+                                                    <img title="[% format.label | html %]" 
+                                                        alt="[% format.label | html %]" 
+                                                        src="[% format.icon %]" /> 
+                                                    [% format.label | html %]
+                                                [% END %]
+                                            [%- END %]
+                                          
+                                        </div>
+                                                    
+                                        [% IF rec.popularity > 0.0 AND ctx.hide_badge_scores != 'true' %]
+                                            <div><span><strong>[% l('Popularity:') %]</strong> [% rec.popularity %] / 5.0</span></div>
+                                        [% END %]
+                                        <table 
+                                            role="presentation"
+                                            title="[% l('Record Holdings Summary') %]"
+                                            class="table_no_border_space table_no_border results_info_table container-fluid">
+                                       
+
+                                            [%- IF show_detail_view -%]
+                                            [% IF args.holdings.size > 0 %]
+                                            <tr name='bib_cn_list' class='result_table_title_cell'>
+                                                <td valign='top'><strong>[% l('Call number:') %]</strong></td> 
+                                                <td>[% args.holdings.0.label | html %]</td>
+                                            </tr>
+                                            [% END %]
+                                            <!-- These fields are visible when viewing the results page in 'detailed' mode -->
+                                            [% UNLESS rec.mmr_id %]
+                                            <!-- Do not show publication-specific information on a metarecord search results page -->
+                                            [% IF attrs.publisher %]
+                                            <tr name="results_pub_tr">
+                                                <td valign="top"><strong>[% l('Publisher:') %]</strong></td>
+                                                <td>[% attrs.pubplace | html; %] [% attrs.publisher | html; %] [% attrs.pubdate | html; %]
+                                                [%- FOR entry IN attrs.graphic_pubinfos;
+                                                        FOR alt IN entry.graphic;
+                                                        diratt = "";
+                                                        IF alt.dir;
+                                                            diratt = ' dir="' _ alt.dir _ '"';
+                                                        END;
+                                                -%]
+                                                <div class="graphic880"[% diratt %]>[% alt.value | html %]</div>
+                                                [%- END; END; -%]
+                                                </td>
+                                            </tr>
+                                            [% ELSIF attrs.producer %]
+                                            <tr name="results_pub_tr">
+                                                <td valign="top"><strong>[% l('Producer:') %]</strong></td>
+                                                <td>[% attrs.prodplace | html; %] [% attrs.producer | html; %] [% attrs.proddate | html; %]</td>
+                                            </tr>
+                                            [% ELSIF attrs.distributor %]
+                                            <tr name="results_pub_tr">
+                                                <td valign="top"><strong>[% l('Distributor:') %]</strong></td>
+                                                <td>[% attrs.distplace | html; %] [% attrs.distributor | html; %] [% attrs.distdate | html; %]</td>
+                                            </tr>
+                                            [% ELSIF attrs.manufacturer %]
+                                            <tr name="results_pub_tr">
+                                                <td valign="top"><strong>[% l('Manufacturer:') %]</strong></td>
+                                                <td>[% attrs.manplace | html; %] [% attrs.manufacturer | html; %] [% attrs.mandate | html; %]</td>
+                                            </tr>
+                                            [% END %]
+                                            [% IF attrs.isbns.size > 0 %]
+                                            <tr name="results_isbn_tr">
+                                                <td valign="top"><strong>[% l('ISBN:') %]</strong></td>
+                                                <td>[% attrs.isbns.0 | html %]</td>
+                                            </tr>
+                                            [% END %]
+                                            [%- IF attrs.issns.size > 0 %]
+                                            <tr name="results_issn_tr">
+                                                <td valign="top">
+                                                    <strong>[% l('ISSN:') %]</strong>
+                                                </td>
+                                                <td>[% attrs.issns.0 | html %]</td>
+                                            </tr>
+                                            [%- END %]
+                                            [%- IF openurl.enabled == 'true';
+                                                FOREACH issn IN args.issns;
+                                                    NEXT IF issn == '';
+                                                    res_urls = ResolverResolver.resolve_issn(issn, openurl.baseurl);
+                                                    FOREACH res IN res_urls; %]
+                                            <tr name="results_issn_tr">
+                                                <td valign="top"><strong><a href="[% res.target_url %]">[% res.public_name | html %]</a></strong>
+                                                </td>
+                                                    <td>[% res.target_coverage | html %]
+                                                    [%- IF res.target_embargo != '';' - ';res.target_embargo | html; END;-%]
+                                                    </td>
+                                            </tr>
+                                                    [% END %]
+                                                [% END %]
+                                            [% END %]
+
+                                            [% IF attrs.edition %]
+                                            <tr name="results_edition_tr">
+                                                <td valign="top"><strong>[% l('Edition:') %]</strong></td>
+                                                <td>[% attrs.edition | html %]
+                                                    [%- FOR entry IN attrs.graphic_editions;
+                                                        FOR alt IN entry.graphic;
+                                                            diratt = "";
+                                                            IF alt.dir; diratt = ' dir="' _ alt.dir _ '"'; END;
+                                                    -%]
+                                                    <div class="graphic880"[% diratt %]>
+                                                        [% alt.value | html %]
+                                                    </div>
+                                                    [%- END; END; -%]
+                                                </td>
+                                            </tr>
+                                            [% END %]
+                                            [% IF attrs.phys_desc %]
+                                                <tr name="results_phys_desc_tr">
+                                                    <td nowrap="nowrap" valign="top"><strong>[% l('Phys. Desc.:') %]</strong></td>
+                                                    <td>[% args.phys_desc | html %]</td>
+                                                </tr>
+                                            [% END %]
+                                            [% FOR uri IN args.uris %]
+                                                <tr name='bib_uri_list' class='result_table_title_cell row'>
+                                                    <td valign='top'><strong>[% l('Electronic resource') %]</strong></td>
+                                                    <td><a href="[% uri.href %]" class="uri_link" target="_blank">[% uri.link | html %]</a>[% ' - ' _ uri.note | html IF uri.note %]</td>
+                                                </tr>
+                                            [% END %]
+                                            [% END %]
+                                            [%- IF args.holdings.size > 0;
+                                                FOREACH copy IN args.holdings;
+                                                    IF copy.part_label != '';
+                                                        has_parts = 'true';
+                                                        LAST;
+                                                    END;
+                                                END;
+                                            %]
+                                            <tr name='bib_cn_list' class='result_table_title_cell container-fluid'>
+                                                <td colspan='2'>
+                                                    <table role="presentation" title="[% l('Record Holdings Details') %]" class='container-fluid table table-hover mt-4 miniTable holdingsTable'>
+                                                        <thead>
+                                                            <tr>
+                                                                <th>[% l('Library') %]</th>
+                                                                <th>[% l('Shelving location') %]</th>
+                                                                <th>[% l('Call number') %]</th>
+                                                                [%- IF has_parts == 'true'; %]
+                                                                <th>[% l('Part') %]</th>
+                                                                [%- END %]
+                                                                <th>[% l('Status') %]</th>
+                                                            </tr>
+                                                        </thead>
+                                                    <tbody>
+                                                    [% FOR copy IN args.holdings %]
+                                                        <tr>
+                                                            <td><span class="sr-only">Library </span>[%- copy_info = copy; INCLUDE "opac/parts/library_name_link.tt2"; %]</td>
+                                                            <td><span class="sr-only">Shelving Location </span>[% copy.location | html %]</td>
+                                                            <td><span class="sr-only">Call Number </span>[% copy.label | html %]</td>
+                                                            [%- IF has_parts == 'true'; %]
+                                                            <td><span class="sr-only">Part </span>[% copy.part_label %]</td>
+                                                            [%- END %]
+                                                            <td><span class="sr-only">Status </span>[% copy.status | html %]</td>
+                                                        </tr>
+                                                    [% END %]
+                                                    </tbody>
+                                                    </table>
+                                                </td>
+                                            </tr>
+                                            [%- has_parts = 'false'; END; %]
+                                        [% END %] <!-- END detail_record_view -->
+                                    </table>
+                                    [% 
+                                        IF ebook_api.enabled == 'true';
+                                            INCLUDE "opac/parts/ebook_api/avail.tt2";
+                                        END;
+                                    %]
+                                    [% UNLESS rec.mmr_id;
+                                            PROCESS "opac/parts/result/copy_counts.tt2";
+                                    END; %]
+                                    [% IF rec.user_circulated %]
+                                    <div class="result_item_circulated">
+                                        <img src="[% ctx.media_prefix %]/images/green_check.png[% ctx.cache_key %]" alt="[% l('Checked Out Before') %]"/>
+                                        <span>[% l('I have checked this item out before') %]</span>
+                                    </div>
+                                    [% END %]
+                                    [% IF ctx.bookbag;
+                                        rec_id = rec.id;
+                                        FOR note IN ctx.bookbag_items_by_bre_id.$rec_id.notes %]
+                                            <div class="result-bookbag-item-note">[% note.note | html %]</div>
+                                        [% END %]
+                                    [% END %]
+                                </div>
+
+
+
+                                <div class="result_table_utils_cont col-md-3 d-flex">
+                                    <div class="result_table_utils row mx-auto d-inline-block d-md-inline-flex">
+                                        <div class="btn-group-vertical">
+                                        [%- search_ou = ctx.search_ou;
+                                            num_holdable_copies = attrs.marc_xml.findnodes('//*[local-name()="holdings" and @has_holdable="true"]').size || 0;
+                                            IF ctx.place_unfillable || (num_holdable_copies > 0 && (ctx.holds_block.enabled != 'true' || attrs.org_copy_counts.$search_ou.available == 0))
+                                        %]
+                                        [% del_parms = ['tag','subfield','term','_special','sort','page'];
+                                        add_parms = {hold_target => rec.id, hold_type => hold_type, hold_source_page => mkurl()};
+
+                                        IF is_advanced;
+                                            # Do not pass "advanced params" to hold code
+                                            # Instead, pass the scrubed query in one-line form
+                                            del_parms = del_parms.merge(['query', 'bool', 'qtype', 'contains', '_adv']);
+                                            add_parms.import({query => ctx.naive_query_scrub(ctx.user_query)});
+                                        END; %]
+                                        
+
+
+                                        <!--Place Hold Button -->
+                                        <button type="button" class="btn btn-light">
+                                                <a href="[% mkurl(ctx.opac_root _ '/place_hold', add_parms, del_parms) %]" 
+                                                    [% html_text_attr('title', l('Place Hold on [_1]', attrs.title)) %]
+                                                    class="no-dec" rel="nofollow" vocab="">
+                                                    <img src="[% ctx.media_prefix %]/images/green_check.png[% ctx.cache_key %]" alt=""/>
+                                                    <span class="result_place_hold">[% l('Place Hold') %]</span>
+                                                </a>
+                                        </button>
+                                        [%- END -%]
+
+                                        <!-- eBook Buttons -->
+                                        [%- IF ebook_api.enabled == 'true' && args.ebook %]
+                                        <button type="button" class="btn btn-light">
+                                            <a href="[% mkurl(ctx.opac_root _ '/myopac/ebook_checkout',
+                                                {title => args.ebook.ebook_id, vendor => args.ebook.vendor, action => 'checkout'},
+                                                ['query','tag','subfield','term','_special','sort','page']) %]"
+                                                [% html_text_attr('title', l('Check Out [_1]', attrs.title)) %]
+                                                    class="no-dec" rel="nofollow" vocab=""><img
+                                                src="[% ctx.media_prefix %]/images/green_check.png[% ctx.cache_key %]"
+                                                alt=""/><span class="result_place_hold">[% l('Check Out E-Item') %]</span></a>
+                                        </button>
+                                        <button type="button" class="btn btn-light">
+                                            <a href="[% mkurl(ctx.opac_root _ '/myopac/ebook_place_hold',
+                                                {title => args.ebook.ebook_id, vendor => args.ebook.vendor, action => 'place_hold'},
+                                                ['query','tag','subfield','term','_special','sort','page']) %]"
+                                                [% html_text_attr('title', l('Place Hold on [_1]', attrs.title)) %]
+                                                    class="no-dec btn btn-primary btn-sm" rel="nofollow" vocab=""><img
+                                                src="[% ctx.media_prefix %]/images/green_check.png[% ctx.cache_key %]"
+                                                alt=""/><span class="result_place_hold">[% l('Place Hold on E-Item') %]</span></a>
+                                        </button>
+                                        [%- END -%]
+
+                                        <!--Basket and My Lists Buttons -->
+                                        [% IF !ctx.is_meta %]
+                                                [% IF !ctx.is_staff %]
+                                                [%
+                                                    addhref = mkurl(ctx.opac_root _ '/mylist/add',
+                                                            {record => rec.id, anchor => 'record_' _ rec.id}, 1);
+                                                    delhref = mkurl(ctx.opac_root _ '/mylist/delete',
+                                                            {record => rec.id, anchor => 'record_' _ rec.id}, 1);
+                                                %]
+                                                <button type="button" class="btn btn-light">
+                                                <a href="[% addhref %]" id="mylist_add_[% rec.id %]"
+                                                    data-recid="[% rec.id %]" data-action="add"
+                                                    class="mylist_action [% IF ctx.mylist.grep('^' _ rec.id _ '$').size %]hidden[% END %]"
+                                                    title="[% l("Add [_1] to basket", attrs.title) %]" rel="nofollow" vocab="">
+                                                    <img src="[% ctx.media_prefix %]/images/add-to-cart.png[% ctx.cache_key %]" alt="" />
+                                                    [% l("Add to basket") %]
+                                                </a>
+                                                
+                                                <a href="[% delhref %]" id="mylist_delete_[% rec.id %]"
+                                                    data-recid="[% rec.id %]" data-action="delete"
+                                                    class="mylist_action [% IF !ctx.mylist.grep('^' _ rec.id _ '$').size %]hidden[% END %]"
+                                                    title="[% l("Remove [_1] from basket", attrs.title) %]" rel="nofollow" vocab="">
+                                                    <img src="[% ctx.media_prefix %]/images/add-to-cart.png[% ctx.cache_key %]" alt="" />
+                                                    [% l("Remove from basket") %]
+                                                </a>
+                                                </button>
+                                                [%  IF ctx.user;
+                                                    INCLUDE "opac/parts/bookbag_actions.tt2";
+                                                    END;
+                                                %]
+                                                [% END %]
+                                        [% END %]
+
+                                        <!--Added Content - Content Cafe -->
+                                        [% IF ENV.OILS_CONTENT_CAFE_USER %]
+                                        [% ident = attrs.isbn_clean || attrs.upc %]
+                                            <button type="button" class="btn btn-light">
+                                                <a target='_blank' 
+                                                    [% html_text_attr('title', l('Reviews and More for [_1]', attrs.title)) %]
+                                                    href="[% ctx.ext_proto %]://contentcafe2.btol.com/ContentCafeClient/ContentCafe.aspx?UserID=[%- 
+                                                        ENV.OILS_CONTENT_CAFE_USER %]&amp;Password=[%-
+                                                        ENV.OILS_CONTENT_CAFE_PASS %]&amp;ItemKey=[% ident | uri %]&amp;Options=Y" rel="nofollow" vocab="">
+                                                    <img src='[% ctx.media_prefix %]/images/starz.png[% ctx.cache_key %]' alt="[% l('Ratings Icon') %]"/>
+                                                    <span class="results_reviews">[% l('Reviews &amp; More') %]</span>
+                                                </a>
+                                            </button>
+                                        [% END %]
+                                        </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </td>
+                    </tr>
+                </div>
+                [%- IF ENV.OILS_CHILIFRESH_ACCOUNT %]
+                <tr>
+                    <td/>
+                    <td align='center'> <!-- Chilifresh reviews link --> 
+                        <span class="chili_review" id="isbn_[% attrs.isbn_clean | html %]"> </span>
+                    </td>
+                </tr>
+                <tr>
+                    <td/>
+                    <td colspan='5'> <!-- Chilifresh reviews panel -->
+                        <div id="chili_review_[% attrs.isbn_clean | html %]" style="display: none;" align="center"></div>
+                    </td>
+                </tr>
+                [%- END %]
+                    [% END %]
+            </tbody>
+        </table>
+    </div>
+</div>
+
+
+
+<div class="facet_sidebar_hidden" id="facet_sidebar">
+    [%- IF ctx.is_staff %]
+    <h3 class="sr-only">[% l('Saved Searches') %]</h3>
+        [% INCLUDE "opac/parts/staff_saved_searches.tt2" %]
+    [%-  END %]
+    <h3 class="sr-only">[% l('Search Results facets') %]</h3>
+    <h3 class="sr-only">[% l('Search Results List') %]</h3>
+</div>
+</div>
+
+</div>
+<div class="col-12">
+<div class="result_footer_nav1">
+    [% ctx.results_count_header %]
+</div>
+</div>
+<div class="d-xl-none d-block">
+[% INCLUDE 'opac/parts/result/facets.tt2' %]
+</div>
+<script>
+resultBlock = document.getElementById('result_block');
+resultButton = document.getElementById('return_to_hits');
+facetSidebar = document.getElementById('facet_sidebar');
+facetButton =  document.getElementById('refine_hits');
+function getFacety() {
+    resultBlock.setAttribute('class', 'result_block_hidden');
+    resultButton.setAttribute('class', 'results_header_btns result_block_visible');
+    facetSidebar.setAttribute('class', 'facet_sidebar_visible');
+    facetButton.setAttribute('class', 'result_block_hidden');
+    window.location.hash = 'return_to_hits';
+}
+function getResulty() {
+    resultBlock.setAttribute('class', 'result_block_visible');
+    resultButton.setAttribute('class', 'result_block_hidden');
+    facetSidebar.setAttribute('class', 'facet_sidebar_hidden');
+    facetButton.setAttribute('class', 'results_header_btns result_block_visible');
+    window.location.hash = 'refine_hits';
+}
+</script>
diff --git a/Open-ILS/src/templates/opac-new/parts/searchbar.tt2 b/Open-ILS/src/templates/opac-new/parts/searchbar.tt2
new file mode 100644 (file)
index 0000000..144e3f4
--- /dev/null
@@ -0,0 +1,215 @@
+<h3 class="sr-only">[% l('Catalog Search') %]</h3>
+[% PROCESS "opac/parts/org_selector.tt2";
+
+# We need to ignore some filters in our count
+
+fignore = ['location_groups','site','core_limit','limit','badge_orgs','badges','estimation_strategy','depth'];
+fcount = 0;
+FOR f IN ctx.query_struct.filters;
+    IF fignore.grep('^' _ f.name _ '$').size;
+        NEXT;
+    END;
+    fcount = fcount + 1;
+END;
+
+    # don't display a box for the search_format filter,
+    # as that's got its own widget
+    ignore_filters = ['search_format'];
+
+    trimmed_filters = [];
+    FOR filter IN ctx.query_struct.filters;
+        fname = filter.name;
+        IF ignore_filters.grep('^' _ fname _ '$').size;
+            NEXT;
+        END;
+        trimmed_filters.push(filter);
+    END;
+
+    ctx.query_struct.filters = trimmed_filters;
+
+ %]
+
+<div id="search-wrapper">
+    [% UNLESS took_care_of_form -%]
+    <form action="[% ctx.opac_root %]/results" method="get">
+    [%- END %]
+    [% IF ctx.page == 'rresult' && ctx.metarecord && search.metarecord_default %]
+    <input type="hidden" name="modifier" value="metabib"/>
+    [% END %]
+    [% IF (ctx.page == 'place_hold' || ctx.page == 'myopac' || ctx.page == 'home' || ctx.page == 'record') && search.metarecord_default %]
+    <input type="hidden" name="modifier" value="metabib"/>
+    [% END %]
+    <div class="container py-3">
+    <div class="row">
+     <!--Keyword-->
+       <div class="col-xs-12 col-sm-12 col-md-6 col-lg-3">
+        <span class='search_box_wrapper'>
+            [%- # autosuggest breaks accessibility, as the aria-label
+                # attribute is removed when the Dijit is created. :(  %]
+            <label id="search_box_label" for="search_box" class="w-100">[% l('Search: ') %]
+            <input class="form-control form-control-lg" type="text" id="search_box" name="query" aria-label="[%
+                    l('Enter search query:');
+                %]" value="[% is_advanced ? ctx.naive_query_scrub(ctx.user_query) : CGI.param('query') | html %]"
+                [%- IF use_autosuggest.enabled == "t" %]
+                dojoType="openils.widget.AutoSuggest" type_selector="'qtype'"
+                submitter="this.textbox.form.submit();"
+                [%-     IF use_autosuggest.value.search('opac_visible') %]
+                store_args='{"org_unit_getter": function() { return [% ctx.search_ou %]; }}'
+                [%-     END # opac_visible -%]
+                [%- ELSE -%]
+                    [% IF basic_search != "f" AND is_home_page%] autofocus [% END %] x-webkit-speech
+                [%- END # autosuggest enabled %] />
+            </label>
+        </span>
+        </div>
+        
+        <!--Type-->
+        <div class="col-xs-12 col-sm-12 col-md-6 col-lg-3">
+        <label id="search_qtype_label" for="qtype" class="w-100">
+        [%- 
+            l('Type: ');
+            INCLUDE "opac/parts/qtype_selector.tt2" id="qtype";
+        -%]
+        </label>
+        </div>
+        
+         <!--Format-->
+        <div class="col-xs-12 col-sm-12 col-md-6 col-lg-3">
+        <label id="search_itype_label" for="search_itype_selector" class="w-100">
+        [%-
+            l('Format: ');
+            IF search.basic_config.type == 'attr';
+                INCLUDE "opac/parts/coded_value_selector.tt2"
+                    attr=search.basic_config.group none_ok=1 
+                    id='search_itype_selector'
+                    none_label=search.basic_config.none_label;
+            ELSIF search.basic_config.type == 'filter';
+                INCLUDE "opac/parts/filter_group_selector.tt2"
+                    filter_group=search.basic_config.group none_ok=1 
+                    id='search_itype_selector'
+                    none_label=search.basic_config.none_label;
+            END;
+        -%]
+        </label>
+        </div>
+        
+         <!--Library-->
+        <div class="col-xs-12 col-sm-12 col-md-6 col-lg-3">
+        <label id="search_locg_label" for="search_org_selector" class="w-100">
+        [%- 
+            l('Library: ');
+            select_lib_label = l("Select search library");
+            INCLUDE build_org_selector arialabel=select_lib_label 
+              id='search_org_selector' show_loc_groups=1
+        -%]
+        </label>
+        </div>
+      </div>
+      <div class="row">
+      <span class="mx-auto mt-3">
+        <input id="detail" type="hidden" name="detail_record_view" value="[% show_detail_view %]"/>
+        <input id='search-submit-go' type="submit" value="[% l('Search') %]" class="btn btn-opac"
+            onclick='setTimeout(function(){$("search-submit-spinner").className=""; $("search-submit-go").className="hidden";[% IF ctx.depth_sel_button AND NOT took_care_of_form %] $("search-submit-go-depth").className="hidden";[% END %]}, 2000)'/>
+        [%- IF ctx.depth_sel_button AND NOT took_care_of_form %]
+        <button id='search-submit-go-depth' type="submit" value="[% ctx.depth_sel_depth %]" name="depth" class="[% #ctx.depth_sel_button_class %] btn btn-opac"
+            onclick='setTimeout(function(){$("search-submit-spinner").className=""; $("search-submit-go").className="hidden"; $("search-submit-go-depth").className="hidden";}, 2000)' title="[% ctx.depth_sel_tooltip | html %]">[% ctx.depth_sel_button_label | html %]</button>
+        [%- END %]
+        <img id='search-submit-spinner' src='[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]' style='height:16px;width:16px;' class='hidden' alt='[% l("Search In Progress") %]'/>
+        
+          
+            </div>
+           
+          
+      </span>
+     
+    </div>
+    
+  
+            
+    [% IF ctx.bookbag %]
+    <div id="search-only-bookbag-container" class="text-center">
+        <input type="checkbox" id="search-only-bookbag" name="bookbag"
+            value="[% ctx.bookbag.id | html %]" checked="checked" />
+        <label for="search-only-bookbag">
+            [% l('Search only within the chosen list') %]
+        </label>
+    </div>
+    [% END %]
+    [% IF is_advanced || is_special %]
+    <div>
+        <input type="hidden" name="_adv" value="1" />
+        [% IF ctx.processed_search_query OR (NOT is_advanced AND NOT is_special) %]
+        <input name='page' type='hidden' value="0" />
+        [% END %]
+        [% IF is_advanced;
+            FOR p IN CGI.params.keys;
+                NEXT UNLESS p.match('^fi:');
+                NEXT IF p.match('^fi:search_format');
+                FOR pv IN CGI.params.$p;
+                    %]<input type="hidden" name="[% p | html %]" value="[% pv | html %]" />[%
+                END;
+            END;
+        END %]
+        [% IF is_special %]
+            <input type="hidden" name="_special" value="1" /> [%
+            number_of_expert_rows = CGI.param('tag').list.size;
+            index = 0;
+            WHILE index < number_of_expert_rows %]
+                <input type="hidden" name="tag" value="[% CGI.param('tag').list.$index | html %]" />
+                <input type="hidden" name="subfield" value="[% CGI.param('subfield').list.$index | html %]" />
+                <input type="hidden" name="term" value="[% CGI.param('term').list.$index | html %]" />
+                [% index = index + 1; %]
+            [% END %]
+        [% END %]
+    </div>
+    [%- END %]
+    [% UNLESS took_care_of_form %]
+        [% IF ctx.default_sort %]
+            <input type="hidden" name="sort" value="[% ctx.default_sort %]"/>
+        [% END %]
+        </form>
+    [% END %]
+    [% IF fcount > 0 %]
+      <div class="refine_search result_block_visible">
+        <span id="filter_hits">[ <a href="#" onclick="getAdvLimits();return false;">[% l('[quant,_1,filter,filters] applied', fcount) %]</a> ]</span>
+      </div>
+    [% END %]
+    [% IF ctx.query_struct.filters.size > 0 %]
+        [% stuff = INCLUDE 'opac/parts/result/adv_filter.tt2' %]
+        [% IF stuff %]
+        <h3 class="sr-only">[% l('Search Results filters') %]</h3>
+        <div id="adv_filter_results_block" class="adv_filter_results_hide">
+        <span class="adv_filter_results_block_label">[% l('Filtered by:') %]</span>
+            [% stuff %]
+        </div>
+        [% END %]
+    [% END %]
+   
+
+    <script>
+    function getAdvLimits() {
+        var AdvLimitsClass = document.getElementById('adv_filter_results_block').classList;
+        if (AdvLimitsClass.contains("adv_filter_results_hide")) {
+           AdvLimitsClass.remove("adv_filter_results_hide");
+        } else {
+           AdvLimitsClass.add("adv_filter_results_hide");
+        }
+        if (AdvLimitsClass.contains("adv_filter_results_show")) {
+           AdvLimitsClass.remove("adv_filter_results_show");
+        } else {
+           AdvLimitsClass.add("adv_filter_results_show");
+        }
+     }
+     </script>
+    <!-- Canonicalized query:
+
+    [% ctx.canonicalized_query | html %]
+
+    -->
+    <!--
+    <div id="breadcrumb">
+        <a href="[% ctx.opac_root %]/home">[% l('Catalog Home') %]</a> &gt;
+    </div>
+    -->
+    <div class="clear-both"></div>
+</div>
diff --git a/Open-ILS/src/templates/opac-new/parts/sms_carrier_selector.tt2 b/Open-ILS/src/templates/opac-new/parts/sms_carrier_selector.tt2
new file mode 100644 (file)
index 0000000..95ba8ff
--- /dev/null
@@ -0,0 +1,29 @@
+[%
+    setting = 'opac.default_sms_carrier';
+    IF ctx.user_setting_map.$setting;
+        default_carrier = ctx.user_setting_map.$setting;
+    END;
+
+    temp = ctx.search_csc('active','t');
+
+    # turn the list of objects into a list of hashes to
+    # leverage TT's array.sort('<hashkey>') behavior
+    carriers = [];
+    FOR o IN temp;
+        carriers.push({
+            id => o.id,
+            region => o.region,
+            name => o.name
+        });
+    END;
+%]
+[% IF NOT sms_carrier_hide_label; '<label for="sms_carrier">' _ l('Mobile carrier:') _ '</label>'; END; %]
+<select name="sms_carrier" id="sms_carrier" [% IF sms_carrier_hide_label; 'aria-label="' _ l('Mobile carrier') _ '"'; END; %]>
+    <option value="">[% l('Please select your mobile carrier') %]</option>
+    [% FOR carrier IN carriers.sort('name','region') -%]
+    <option value='[% carrier.id | html %]'[%
+        default_carrier == carrier.id ? ' selected="selected"' : ''
+    %]>[% carrier.name | html %] ([% carrier.region | html %])</option>
+    [% END -%]
+</select>
+[% IF NOT sms_carrier_hide_warning; l('Note: carrier charges may apply'); END; %]
diff --git a/Open-ILS/src/templates/opac-new/parts/sms_number_textbox.tt2 b/Open-ILS/src/templates/opac-new/parts/sms_number_textbox.tt2
new file mode 100644 (file)
index 0000000..395b5a3
--- /dev/null
@@ -0,0 +1,5 @@
+[% IF NOT sms_number_hide_label; '<label for="sms_notify">' _ l('Mobile number:') _ '</label>'; END; %]
+<input type="text" name="sms_notify" [% setting = 'opac.default_sms_notify';
+IF ctx.user_setting_map.$setting; %] value='[% ctx.user_setting_map.$setting | html %]' [% END %]
+[% IF sms_number_hide_label; 'aria-label="' _ l('Mobile number') _ '"'; END; %] id="sms_notify" />
+[% IF NOT sms_number_hide_hint; l('Hint: use the full 10 digits of your phone #, no spaces, no dashes'); END; %]
diff --git a/Open-ILS/src/templates/opac-new/parts/staff_saved_searches.tt2 b/Open-ILS/src/templates/opac-new/parts/staff_saved_searches.tt2
new file mode 100644 (file)
index 0000000..2787443
--- /dev/null
@@ -0,0 +1,25 @@
+[% IF ctx.saved_searches.size %]
+<div id="accordion">
+  <div class="card">
+    <div class="card-header" id="headingOne">
+      <h5 class="mb-0">
+        <button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
+          [% l("Recent searches") %]
+        </button>
+      </h5>
+    </div>
+
+    <div id="collapseOne" class="collapse" aria-labelledby="headingOne" data-parent="#accordion">
+      <div class="card-body">
+               [% FOR s IN ctx.saved_searches %]
+               <ul>
+               <li><a href="[% ctx.opac_root %]/results?query=[% s | uri %]&amp;_adv=1">[% s | html %]</a></li>
+               </ul>
+            [% END %]
+      </div>
+    </div>
+  </div>
+</div>
+  
+   
+[% END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/stripe.tt2 b/Open-ILS/src/templates/opac-new/parts/stripe.tt2
new file mode 100644 (file)
index 0000000..ea4ca04
--- /dev/null
@@ -0,0 +1,35 @@
+[%- PROCESS "opac/parts/header.tt2" %]
+[% IF ctx.use_stripe %]
+        <script type="text/javascript" src="https://js.stripe.com/v2/"></script> <!-- use an ou setting for this url? -->
+        <script type="text/javascript">
+        // This script is only displayed when logged in, so ctx.user.home_ou is always available
+        Stripe.setPublishableKey('[% ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.stripe.pubkey') %]');
+
+        function stripe_onsubmit() {
+            var form = document.getElementById("payment_form");
+            var button = document.getElementById("payment_submit");
+
+            button.disabled = true;
+
+            Stripe.card.createToken(form, stripe_callback);
+
+            return false;
+        }
+
+        function stripe_callback(status, response) {
+            var form = document.getElementById("payment_form");
+            var button = document.getElementById("payment_submit");
+            var stripe_token = document.getElementById("stripe_token");
+
+            if(response.error) {
+                alert(response.error.message);
+                button.disabled = false;
+                return;
+            }
+
+            stripe_token.value = response.id; // response.id is the token id, though there are more fields available if needed.
+            form.setAttribute("onsubmit","");
+            form.submit();
+        }
+        </script>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/parts/tips.tt2 b/Open-ILS/src/templates/opac-new/parts/tips.tt2
new file mode 100644 (file)
index 0000000..f4e73ed
--- /dev/null
@@ -0,0 +1,11 @@
+<!-- ****************** tips.xml ***************************** -->
+<div class="hide_me">
+    <div id='tips' class='tips hide_me'>
+        <div class='hide_me'>
+            <span>[% l("Click on a folder icon in the sidebar to access related quick searches") %]</span>
+            <span>[% l("If you don't find what you want try expanding your search using the range selector at the right of the search bar") %]</span>
+        </div>
+        <strong>[% l("Tip:") %]</strong>
+    </div>
+</div>
+<!-- ****************** end: tips.xml ***************************** -->
diff --git a/Open-ILS/src/templates/opac-new/parts/topnav.tt2 b/Open-ILS/src/templates/opac-new/parts/topnav.tt2
new file mode 100644 (file)
index 0000000..9edbf3f
--- /dev/null
@@ -0,0 +1,18 @@
+[%- IF ctx.maintenance_message -%]
+<div class="alert alert-[% ctx.maintenance_type %] alert-dismissible mb-0 text-center" role="alert">
+       <button type="button" class="close" data-dismiss="alert" aria-label="Close">
+       <span aria-hidden="true">&times;</span>
+       </button>
+[% ctx.maintenance_message %]
+</div>
+[%- END -%]
+[% IF !ctx.is_staff %]
+
+<div id="header-wrap">
+[% INCLUDE "opac/parts/topnav_links.tt2" %]
+
+[% END %]
+
+[% IF ctx.is_staff %]
+[% INCLUDE "opac/parts/topnav_subnav.tt2" %]
+[% END %]
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/parts/topnav_cannedsearch.tt2 b/Open-ILS/src/templates/opac-new/parts/topnav_cannedsearch.tt2
new file mode 100644 (file)
index 0000000..9ca34c0
--- /dev/null
@@ -0,0 +1,56 @@
+[%
+    ctx.cloud = "https://live.nflibrary.ca/eg/opac/results?query=cloudlibrary&qtype=keyword&fi%3Asearch_format=&locg=101&detail_record_view=1&sort=create_date.descending&depth=0"
+    ctx.afic = "https://live.nflibrary.ca/eg/opac/results?bool=and&qtype=keyword&contains=contains&query=&bool=and&qtype=title&contains=contains&query=&bool=and&qtype=author&contains=contains&query=&_adv=1&detail_record_view=1&fi%3Aaudience=e&fi%3Aaudience=g&fi%3Alit_form=1&locg=101&pubdate=is&date1=&date2=&sort=create_date.descending";
+    ctx.anonfic = "https://live.nflibrary.ca/eg/opac/results?bool=and&qtype=keyword&contains=contains&query=&bool=and&qtype=title&contains=contains&query=&bool=and&qtype=author&contains=contains&query=&_adv=1&detail_record_view=1&fi%3Aaudience=e&fi%3Aaudience=g&fi%3Alit_form=0&locg=101&pubdate=is&date1=&date2=&sort=create_date.descending";
+    ctx.child = "https://live.nflibrary.ca/eg/opac/results?bool=and&qtype=keyword&contains=contains&query=&bool=and&qtype=title&contains=contains&query=&bool=and&qtype=author&contains=contains&query=&_adv=1&detail_record_view=1&fi%3Aaudience=j&fi%3Aaudience=c&fi%3Aaudience=a&fi%3Aaudience=b&locg=101&pubdate=is&date1=&date2=&sort=create_date.descending";
+    ctx.teen = "https://live.nflibrary.ca/eg/opac/results?query=&qtype=keyword&fi%3Asearch_format=&locg=101&detail_record_view=1&_adv=1&page=0&fi%3Aaudience=d&sort=create_date.descending";
+    ctx.movies = "https://live.nflibrary.ca/eg/opac/results?bool=and&qtype=keyword&contains=contains&query=&bool=and&qtype=title&contains=contains&query=&bool=and&qtype=author&contains=contains&query=&_adv=1&detail_record_view=1&fi%3Avr_format=s&fi%3Avr_format=v&locg=101&pubdate=is&date1=&date2=&sort=create_date.descending";
+    ctx.games = "https://live.nflibrary.ca/eg/opac/results?query=&qtype=keyword&fi%3Asearch_format=&locg=101&detail_record_view=1&_adv=1&page=0&fi%3Alocations=114&sort=create_date.descending";
+%]
+<nav class="navbar navbar-expand-lg navbar-light secondary-nav">
+<div class="row mx-auto">
+<div class="col-12">
+    <ul class="navbar-nav nav-fl px-3 d-lg-flex d-none">
+       <!--Link 1-->
+      <li class="nav-item">
+       <span class="navbar-text nav-label">
+                               Browse New to the Library: 
+                </span>
+      </li>
+       <li class="nav-item">
+        <a style="color:red;" class="nav-link" href="[% ctx.cloud %]">cloudLibrary</a>
+      </li>
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.afic %]">Adult Fiction</a>
+      </li>
+       <!--Link 2-->
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.anonfic %]">Adult Non-Fiction</a>
+      </li>
+       <!--Link 3-->
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.child %]">Children's</a>
+      </li>
+       <!--Link 4-->
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.teen %]">Teen</a>
+      </li>
+       <!--Link 5-->
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.movies %]">Movies</a>
+      </li>
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.games %]">Video Games</a>
+      </li>
+    </ul>
+    
+    </div>
+    
+</div></nav>
+
+<br>
+
+
+
+<br>
+
diff --git a/Open-ILS/src/templates/opac-new/parts/topnav_links.tt2 b/Open-ILS/src/templates/opac-new/parts/topnav_links.tt2
new file mode 100644 (file)
index 0000000..b282005
--- /dev/null
@@ -0,0 +1,141 @@
+<nav class="navbar navbar-expand-lg navbar-dark ">
+<!--Home-->
+  <a class="navbar-brand" href="/eg/opac/">[% INCLUDE "opac/parts/topnav_logo.tt2" %]</a>
+  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
+    <span class="navbar-toggler-icon"></span>
+  </button>
+  <div class="collapse navbar-collapse" id="navbarNav" style="height:auto !important;">
+  <!--Left Links-->
+  <div class="row w-100">
+  <div class="col-12">
+  <div id="nav-divide" class="dropdown-divider"></div>
+   <ul class="navbar-nav nav-fl px-3">
+       <!--Link 1-->
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.link_one %]">Link 1<span class="sr-only">(current)</span></a>
+      </li>
+       <!--Link 2-->
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.link_two %]">Link 2</a>
+      </li>
+       <!--Link 3-->
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.link_three %]">Link 3</a>
+      </li>
+       <!--Link 4-->
+      <li class="nav-item">
+        <a class="nav-link" href="[% ctx.link_four %]">Link 4</a>
+      </li>
+    </ul>
+    <div id="nav-divide" class="dropdown-divider"></div>
+    
+    <!--Right Links-->
+    <ul class="navbar-nav ml-auto nav-fr px-3">
+       <!--If not signed in, show sign in button-->
+    [% IF !ctx.user %]
+       <li class="nav-item">
+               <a href="#exampleModal" class="nav-link login" data-toggle="modal" data-target="#exampleModal" >
+                [% l('My Account') %]
+            </a>
+       </li>
+      <!--User is signed in-->
+    [% ELSE %]
+       <!--User-->
+       <li class="nav-item">
+               <a class="nav-link disabled active mx-2" href="#">Hello, [%  l('[_1] [_2]', ctx.user.first_given_name, ctx.user.family_name) | html %]</a>
+       </li>
+       <!--Messages with sup tag for # of messages-->
+       <li class="nav-item">
+        <a class="nav-link  [% IF ctx.user_stats.messages.unread %] active [% END %]" href="[% mkurl(ctx.opac_root _ '/myopac/messages', {}, ['single', 'message_id']) %]">[% l('Messages') %]
+                    [% IF ctx.user_stats.messages.unread %]
+                        <sup class="badge badge-danger">
+                            [%- ctx.user_stats.messages.unread -%]
+                            <span class="sr-only">[% l('unread') %]</a>
+                        </sup>
+                    [% END %]</a>
+       </li>
+       <!--My Account-->
+       <li class="nav-item">
+         <a class="nav-link" href="[% mkurl(ctx.opac_root _ '/myopac/main', {}, ['single', 'message_id', 'sort','sort_type']) %]">[% l('My Account') %]</a>
+       </li>
+       <!--Lists Link-->
+       <li class="nav-item">
+        <a class="nav-link" href="[% mkurl(ctx.opac_root _ '/myopac/lists', {}, ['single', 'message_id']) %]">[% l('My Lists') %]</a>
+       </li>
+       <!--Logout-->
+       <li class="nav-item">
+        <a class="nav-link" href="[% mkurl(ctx.opac_root _ '/logout', {}, 1) %]" id="logout_link">[% l('Logout') %]</a>
+       </li>
+      [% END %]
+      <!--must end ul and div no matter the user state-->
+    </ul>
+       </div>
+       
+       
+               [% IF ctx.user %]
+       <div class="col-12">
+                       <div id="dashboard" class="float-right pr-4 row">
+                <span class="dash-align">
+                    <a class="dash-link" href="[% mkurl(ctx.opac_root _ '/myopac/circs', {}, 
+                        ['limit','offset', 'single', 'message_id', 'sort','sort_type'])
+                        %]"><span id="dash_checked" class="[% IF ctx.user_stats.checkouts.total_out == 0 %]dash-clear[% ELSE %]dash-unclear[% END %]">[% ctx.user_stats.checkouts.total_out
+                        %]</span> [% l("Checked Out") %]</a>
+                </span>
+                <span class="dash_divider">|</span>
+                <span class="dash-align">
+                    <a class="dash-link" href="[% mkurl(ctx.opac_root _ '/myopac/holds', {}, 
+                        ['available', 'single', 'message_id', 'sort','sort_type'])
+                        %]"><span id="dash_holds" class="[% IF ctx.user_stats.holds.total == 0 %]dash-clear[% ELSE %]dash-unclear[% END %]">[% ctx.user_stats.holds.total %]</span> [% l("On Hold") %]</a>
+                </span>
+                <span class="dash_divider">|</span>
+                <span class="dash-align">
+                    <a class="dash-link" href="[% mkurl(ctx.opac_root _ '/myopac/holds',
+                        {available => 1}, ['single', 'message_id', 'sort','sort_type']) %]"><span id="dash_pickup" class="[% IF ctx.user_stats.holds.ready == 0 %]dash-clear[% ELSE %]dash-unclear[% END %]">[%
+                        ctx.user_stats.holds.ready %]</span> [% l("Ready for Pickup") %]</a>
+                </span>
+                <span class="dash_divider">|</span>
+                <span class="dash-align">
+                    <a class="dash-link" href="[% mkurl(ctx.opac_root _ '/myopac/main', {}, ['single', 'message_id', 'sort','sort_type'])
+                        %]"><span class="[% IF ctx.user_stats.fines.balance_owed == "0.0" %]dash-clear[% ELSE %]dash-unclear[% END %]" id="dash_fines">[% money(ctx.user_stats.fines.balance_owed)
+                        %]</span> [% l("Charges") %]</a>
+                </span>
+            </div>
+            </div>
+            <div class="col-12">
+            <div id="dashboard_e" class="float-right pr-4 row hidden">
+                <span class="dash-align">
+                    <a class="dash-link" href="[% mkurl(ctx.opac_root _ '/myopac/ebook_circs')
+                        %]"><span id="dash_e_checked" class="count">-</span> [% l("E-Items Checked Out") %]</a>
+                </span>
+                <span class="dash_divider">|</span>
+                <span class="dash-align">
+                    <a class="dash-link" href="[% mkurl(ctx.opac_root _ '/myopac/ebook_holds')
+                        %]"><span id="dash_e_holds" class="count">-</span> [% l("E-Items on Hold") %]</a>
+                </span>
+                <span class="dash_divider">|</span>
+                <span class="dash-align">
+                    <a class="dash-link" href="[% mkurl(ctx.opac_root _ '/myopac/ebook_holds_ready')
+                        %]"><span id="dash_e_pickup" class="count">-</span> [% l("E-Items Ready for Checkout") %]</a>
+                </span>
+            </div>
+           </div>  
+            [% END %]
+           </div>
+       </div>
+   </div>
+   </nav>
+   [% INCLUDE "opac/parts/topnav_subnav.tt2" %]
+
+<script>
+jQuery(document).ready(function(){
+       var item = jQuery('#dashboard_e .dash-link');
+       item.children('.count').each(function() {
+       if(jQuery(this).text() == "0")
+               jQuery(this).addClass("dash-unclear");
+        else
+               jQuery(this).addClass("dash-clear");
+    });
+});
+</script>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/parts/topnav_logo.tt2 b/Open-ILS/src/templates/opac-new/parts/topnav_logo.tt2
new file mode 100644 (file)
index 0000000..0c79fc6
--- /dev/null
@@ -0,0 +1 @@
+<img alt="[% l('Evergreen Logo') %]" src="[% ctx.media_prefix %]/opac/images/small_logo.png[% ctx.cache_key %]" class="mr-2" />
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/parts/topnav_subnav.tt2 b/Open-ILS/src/templates/opac-new/parts/topnav_subnav.tt2
new file mode 100644 (file)
index 0000000..e53f2c5
--- /dev/null
@@ -0,0 +1,34 @@
+<nav class="navbar navbar-expand-lg navbar-light secondary-nav">
+<div class="row mx-auto">
+<div class="col-12">
+    <ul class="navbar-nav nav-fl px-3 d-lg-flex d-none">
+        <li class=" nav-item mx-2">
+            [% IF ctx.page != 'home'; %]
+            <a href="[% mkurl(ctx.opac_root _ '/home') %]" class="search_catalog_lbl">[%l('Basic Search')%]</a>
+            [% ELSE %]
+            <span class="search_catalog_lbl">[%l('Basic Search')%]</span>
+            [% END %]
+        </li>
+        <li class=" nav-item mx-2">
+            [% IF ctx.page != 'browse'; %]
+            <a href="[% mkurl(ctx.opac_root _ '/browse', {}, expert_search_parms.merge(general_search_parms, facet_search_parms, ['fi:has_browse_entry'])) %]" class="browse_the_catalog_lbl">[% l('Browse the Catalog') %]</a>
+            [% ELSE %]
+            <span class="browse_the_catalog_lbl">[%l('Browse the Catalog')%]</span>
+            [% END %]
+        </li>
+        <li class="nav-item mx-2">
+            [% IF ctx.page != 'advanced'; %]
+            <a href="[% mkurl(ctx.opac_root _ '/advanced', {},  expert_search_parms.merge(browse_search_parms, facet_search_parms)) %]" id="home_adv_search_link" class="adv_search_catalog_lbl">[% l('Advanced Search') %]</a></span>
+            [% ELSE %]
+            <span class="adv_search_catalog_lbl">[%l('Advanced Search')%]</span>
+            [% END %]
+        </li>
+    </ul>
+<div>
+
+</div>
+    </div>
+    
+</div>
+[% INCLUDE 'opac/parts/cart_nav.tt2' %]
+</nav>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/password_reset.tt2 b/Open-ILS/src/templates/opac-new/password_reset.tt2
new file mode 100644 (file)
index 0000000..f0855a3
--- /dev/null
@@ -0,0 +1,74 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l('Library system password reset request form');
+-%]
+<h2 class="sr-only">[% l('Reset Password') %]</h2>
+<div class="mobile_hide">
+[% INCLUDE "opac/parts/searchbar.tt2" %]
+</div>
+
+[%  
+    uuid = ctx.page_args.0;
+    msg_map = {
+        SUCCESS => l('Password has been reset'),
+        NO_MATCH => l('Passwords did not match. Please try again'),
+        NOT_ACTIVE => l('This was not an active password reset request. Your password has not been reset.'),
+        NOT_STRONG => l('The password you chose was not considered complex enough to protect your account. Your password has not been reset.'),
+        TWO_PASSWORDS => l('Please enter and repeat your new password.'),
+        REQUEST_SUCCESS => l('Your user name or barcode has been submitted for a password reset. ' _ 
+            'If a matching account with an email address is found, you will soon receive an email at that address with further instructions for resetting your password.')
+    }
+%]
+<div class="container">
+    <div id="main-content" class="text-center">
+        <br/>
+        <p class='[% ctx.pwreset.style %]'>[% stat = ctx.pwreset.status; msg_map.$stat %]</p>
+        [% IF uuid %]
+            [% IF stat == 'SUCCESS' %]
+                <p><a href='[% mkurl(ctx.opac_root _ '/myopac/main', {}, 1) %]'>[% l('Log in to My Account') %]</a>
+            [% ELSE %]
+            <form method="post">
+                <input type='hidden' name='uuid' value='[% uuid | html %]'/>
+                <table>
+                    <tr>
+                        <td><label for="pwd1">[% l('New password:') %]</label></td>
+                        <td><input type="password" name="pwd1"/></td>
+                    </tr>
+                    <tr>
+                        <td><label for="pwd2">[% l('Re-enter new password:') %]</label></td>
+                        <td><input type="password" name="pwd2"/></td>
+                    </tr>
+                    <tr>
+                        <td>
+                            <button name="submit" class="btn btn-confirm" id="submitButton" type="submit">[% l('Submit') %]</button>
+                        </td>
+                    </tr>
+                </table>
+            </form>
+            [% END %]
+        [% ELSIF !ctx.pwreset.status %]
+        <h2>[% l('Please enter your user name or barcode to identify your library account and request a password reset') %]</h2>
+        [%- INCLUDE "opac/password_reset_msg.tt2" %]
+        <form method="post">
+            <table class="mx-auto table">
+                <tr>
+                    <td><label for="barcode">[% l('Barcode:') %] </label></td>
+                    <td><input type="text" id="barcode" name="barcode"/></td>
+                </tr>
+                <tr>
+                    <td><label for="username">[% l('User name:') %] </label></td>
+                    <td><input type="text" id="username" name="username"/></td>
+                </tr>
+                [% IF ctx.get_org_setting(ctx.physical_loc || ctx.aou_tree.id, 'circ.password_reset_request_requires_matching_email') %]
+                <tr>
+                       <td><label for="email">[% l('Email address associated with the account:') %] </label></td><td><input type="text" name="email"/><br/></td>
+                </tr>
+                [% END %]
+            </table>
+            <button name="submit" id="submitButton" class="btn btn-confirm" type="submit">[% l('Submit') %]</button>
+        </form>
+        [% END %]
+    </div>
+</div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/password_reset_msg.tt2 b/Open-ILS/src/templates/opac-new/password_reset_msg.tt2
new file mode 100644 (file)
index 0000000..c453768
--- /dev/null
@@ -0,0 +1,3 @@
+<div class="password_message">
+[% l('Note: You must have a valid email address associated with your library account. If not, please contact your local library for further assistance.'); %]
+</div>
diff --git a/Open-ILS/src/templates/opac-new/place_hold.tt2 b/Open-ILS/src/templates/opac-new/place_hold.tt2
new file mode 100644 (file)
index 0000000..2262b8d
--- /dev/null
@@ -0,0 +1,21 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    IF ctx.is_staff;
+        basic_search = "f";
+    END;
+    ctx.page_title = l("Place Hold") %]
+    <h2 class="sr-only">[% l('Hold Placement') %]</h2>
+    [% INCLUDE "opac/parts/searchbar.tt2" %]    
+    <div id="content-wrapper">
+        <div id="main-content" class="container">
+            <div class="common-full-pad"></div>        
+            [% IF ctx.hold_attempt_made %]
+                [% INCLUDE "opac/parts/place_hold_result.tt2" %]
+            [% ELSE %]
+                [% INCLUDE "opac/parts/place_hold.tt2" %]
+            [% END %]
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/record.tt2 b/Open-ILS/src/templates/opac-new/record.tt2
new file mode 100644 (file)
index 0000000..6fc13fa
--- /dev/null
@@ -0,0 +1,27 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Record Detail");
+    canon = ctx.proto _ '://' _ ctx.hostname _ mkurl('', {}, 1);
+    ctx.metalinks.push('<link rel="canonical" href="' _ canon  _ '" />');
+    ctx.metalinks.push('<meta property="og:url" content="' _ canon  _ '" />');
+    IF CGI.param("expand"); basic_search = "f"; END;    
+-%]
+    <span class="sr-only">[% l('Record Details') %]</span>
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    <br class="clear-both" />
+    <div id="content-wrapper" class="content-wrapper-record-page">
+        [% IF ctx.staff_saved_search_size %]
+        <div id="results-side-bar">
+            <div id="staff-saved-search">
+                [% INCLUDE "opac/parts/staff_saved_searches.tt2" %]
+            </div>
+        </div>
+        [% END %]
+        <div id="[% ctx.staff_saved_search_size ? 'main-content-after-bar' : 'main-content' %]">
+            [% INCLUDE "opac/parts/record/body.tt2" %]
+            <div class="common-full-pad"></div>
+        </div>
+        <br class="clear-both" />
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/record/email.tt2 b/Open-ILS/src/templates/opac-new/record/email.tt2
new file mode 100644 (file)
index 0000000..88cf88c
--- /dev/null
@@ -0,0 +1,32 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/config.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Record Detail") %]
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    <br class="clear-both" />
+    <div id="content-wrapper" class="content-wrapper-record-page">
+        <div id='main-content'>
+            <br/>
+            [% IF ctx.user.email %]
+            <h2 class='success'>[% l('Your email has been queued for delivery to [_1]', ctx.user.email ) %]</h2>
+            [% ELSE %]
+            <h2 class='error'>
+                [% here_link_text = "<a href=\"${ctx.opac_root}/myopac/update_email?return_to_referer=1\">";
+                   here_link_text = here_link_text _ l("here");
+                   here_link_text = here_link_text _ "</a>";
+                   l('Your account does not currently have an email address set. Set your email address [_1]', here_link_text) %]
+            </h2>
+            [% END %]
+            <br/>
+            [% IF ctx.redirect_to %]
+            <p>[ <a href="[% ctx.redirect_to | html %]">[% l("Return") %]</a> ] </p>
+            [% ELSE %]
+            <p>[ <a href="[% mkurl(ctx.opac_root  _ '/record/' _ ctx.bre_id) %]">[% l("Back to Record") %]</a> ]</p>
+            [% END %]
+            <div class="common-full-pad"></div>
+        </div>
+        <br class="clear-both" />
+    </div>
+[% END %]
+
diff --git a/Open-ILS/src/templates/opac-new/record/print.tt2 b/Open-ILS/src/templates/opac-new/record/print.tt2
new file mode 100644 (file)
index 0000000..ee2577c
--- /dev/null
@@ -0,0 +1,32 @@
+[% PROCESS 'opac/parts/header.tt2' %]
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>[% l('Print Record') %]</title>
+        <style type="text/css" media="print">.noprint {display: none}</style>
+    </head>
+    <body onload="if (document.getElementById('printable-record')) window.print();">
+        [% IF ctx.printable_record.template_output %]
+        <tt id="printable-record">
+            [% ctx.printable_record.template_output.data %]
+        </tt>
+        [% ELSE %]
+        <div class="noprint print-error">
+            [% l(
+                'Error printing record: [_1]',
+                    (ctx.printable_record.textcode ? ctx.printable_record.textcode _ ' / ' _ ctx.printable_record.desc : 0) ||
+                    ctx.printable_record.error_output.data ||
+                    l('No record data returned from server')
+                ) | html %]
+        </div>
+        [% END %]
+        <div class='noprint'>
+            <hr />
+            [% IF ctx.redirect_to %]
+            <p>[ <a href="[% ctx.redirect_to | html %]">[% l("Return") %]</a> ] </p>
+            [% ELSE %]
+            <p>[ <a href="[% mkurl(ctx.opac_root  _ '/record/' _ ctx.bre_id) %]">[% l("Back to Record") %]</a> ]</p>
+            [% END %]        
+        </div>
+    </body>
+</html>
diff --git a/Open-ILS/src/templates/opac-new/register.tt2 b/Open-ILS/src/templates/opac-new/register.tt2
new file mode 100644 (file)
index 0000000..4f199af
--- /dev/null
@@ -0,0 +1,250 @@
+[%- PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/org_selector.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Request Library Card");
+
+# for privacy, reload the page after (default) 5 minutes
+refresh_time = ctx.register.settings.refresh_timeout || 300; 
+ctx.refresh = refresh_time _ '; ' _ ctx.opac_root _ '/home';
+
+# some useful variables and MACROs for display, 
+# field validation, and added info display
+
+ctx_org = ctx.physical_loc || ctx.search_ou || ctx.aou_tree.id;
+
+# list of the registration fields to (potentially) 
+# display in the order they should be shown
+
+# post_code is the only field below that is required in the database and
+# post_code is only required if an address is created.
+# To prevent any of these fields from showing locally, regardless org unit
+# settings, simply remove the fields from this list.  In the case of 
+# addresses, if all address fields are removed, no attempt at creating
+# an address will be made (and post_code will no longer be required).
+
+register_fields = [
+    {class => 'stgu',  name = 'first_given_name', label => l('First Name')},
+    {class => 'stgu',  name = 'second_given_name', label => l('Middle Name')},
+    {class => 'stgu',  name = 'family_name', label => l('Last Name')},
+    {class => 'stgu',  name = 'pref_first_given_name', label => l('Preferred First Name')},
+    {class => 'stgu',  name = 'pref_second_given_name', label => l('Preferred Middle Name')},
+    {class => 'stgu',  name = 'pref_family_name', label => l('Preferred Last Name')},
+    {class => 'stgma', name = 'street1', label => l('Street Address')},
+    {class => 'stgma', name = 'street2', label => l('Street Address (2)')},
+    {class => 'stgma', name = 'city', label => l('City')},
+    {class => 'stgma', name = 'county', label => l('County')},
+    {class => 'stgma', name = 'state', label => l('State')},
+    {class => 'stgma', name = 'post_code', label => l('Zip Code')},
+    {class => 'stgu',  name = 'dob', label => l('Date of Birth')},
+    {class => 'stgu',  name = 'day_phone', label => l('Phone Number')},
+    {class => 'stgu',  name = 'email', label => l('Email Address')}
+    {class => 'stgu',  name = 'usrname', label => l('Requested Username')}
+];
+
+# The dojo date widget in the patron edit UI only accepts default 
+# values in ISO8601 format.  It will not accept locale-shaped dates.
+IF !ctx.register.settings.stgu.dob.example;
+    ctx.register.settings.stgu.dob.example = l('YYYY-MM-DD or YYYY/MM/DD');
+END;
+
+%]
+
+<h2 class="sr-only">[% l('Account Registration') %]</h2>
+<div id="content-wrapper">
+    <div id="main-content-register" class="container">
+        <div class="common-full-pad"></div>
+        <h1>[% l('Request a Library Card')%]</h1>
+        <span style="color:red;">&bigstar; = Required Field</span> 
+        <hr/>
+
+        [% IF ctx.register.success %]
+            <h3>[% l('Registration successful!') %]<h3>
+            <h4>[% l('Please see library staff to complete your registration.') %]</h4>
+
+            [% IF ctx.register.username_taken %]
+            <p>
+                [% |l %]
+                Note: The selected username may be in use by another patron.  
+                You may select another username when finalizing your 
+                registration or in the online catalog.
+                [% END %]
+            </p>
+            [% END %]
+
+            <br/>
+            <p>
+                <a href="[% ctx.opac_root %]/home" 
+                    class="btn btn-confirm">[% l('Return to the Catalog') %]</a>
+            </p>
+
+        [% ELSIF ctx.register.error %]
+            <h3>[% l('A registration error has occurred') %]</h3>
+            <h4>[% l('Please see library staff to complete your registration.') %]</h4>
+
+            <br/>
+            <p>
+                <a href="[% ctx.opac_root %]/home" 
+                    class="btn btn-confirm">[% l('Return to the Catalog') %]</a>
+            </p>
+
+        [% ELSE %]
+
+        [% IF ctx.user %]
+            <!-- if the user is logged in, make it 
+                clear we are tracking the requestor -->
+            <h4>[% l('New account requested by [_1] [_2] [_3] [_4] [_5]',
+                    ctx.user.prefix, ctx.user.first_given_name,
+                    ctx.user.second_given_name, ctx.user.family_name,
+                    ctx.user.suffix
+                ) | html %]</h4>
+        [% END %]
+
+        <form method='POST' class="needs-validation" onSubmit="return dobValidate(document.getElementById('stgu.dob'))" novalidate>
+            <table class="w-100">
+                <tr>
+                    <td width="30">
+                        <label for='stgu.home_ou'>[% l('Home Library') %]</label>
+                    </td>
+                    <td width="50">[% INCLUDE build_org_selector 
+                            name='stgu.home_ou' 
+                            value=value || ctx_org
+                            can_have_users_only=1
+                            valid_org_list=ctx.register.valid_orgs
+                        %]
+                    </td>
+                    <td width="20" >
+                        [% IF ctx.register.invalid.bad_home_ou %]
+                        <span class='patron-reg-invalid'>
+                            [% l('Please select a valid library') %]
+                        </span>
+                        [% END %]
+                </tr>
+                
+[%
+# <=== shifting code left for readability
+
+# render the table row for each of the register fields
+FOR field_def IN register_fields;
+    fclass = field_def.class;
+    fname = field_def.name;
+    orig_name = fname;
+
+    field_path = fclass _ "." _ fname;
+    
+    IF fname.match('^pref_');
+        # Preferred name fields adopt most visibility, etc.
+        # settings from the primary name counterparts.
+        fname = fname.remove('^pref_');
+    END;
+
+    show = ctx.register.settings.$fclass.$fname.show;
+    require = ctx.register.settings.$fclass.$fname.require;
+    example = ctx.register.settings.$fclass.$fname.example;
+    value = ctx.register.values.$fclass.$fname;
+
+    invalid_require = ctx.register.invalid.$fclass.$fname.require;
+    invalid_regex = ctx.register.invalid.$fclass.$fname.regex;
+
+    IF orig_name.match('^pref_');
+        show = show || require;
+        require = 0; # pref name values never required
+    END;
+     
+    NEXT UNLESS require OR show;
+%]
+<tr>
+    <td>
+        <label for='[% field_path %]'>[% field_def.label | html %]</label>
+    </td>
+    <td class="py-3">
+        <input 
+               class='form-control'
+            type='text'
+            id='[% field_path %]'
+            name='[% field_path %]'
+            value='[% value || CGI.param(field_path) | html %]' [% IF require %]required[% END %]/>
+        <div class="invalid-feedback">
+          Please enter a [% field_def.label | html %]
+        </div>
+         [% IF example %]
+        <span class='patron-reg-extra'>
+            [% l('(Example: [_1])', example) %]
+        </span>
+    [% END %]
+    </td>
+    <td>
+        [% IF require %]
+        <span style="color:red;">&bigstar;</span>
+        [% END %]
+    <!-- display errors and example text -->
+
+    [% IF invalid_require %]
+        <span class='patron-reg-invalid'>
+            [% l('This field is required') %]
+        </span>
+    [% ELSIF invalid_regex %]
+        <span class='patron-reg-invalid'>
+            [% l('The value entered does not have the correct format') %]
+        </span>
+    [% END %]
+    [% IF example %]
+        <span class='patron-reg-extra'>
+            [% l('(Example: [_1])', example) %]
+        </span>
+    [% END %]
+
+    </td>
+</tr>
+[% END %]
+<!-- ====> shifting the code back to the right for context -->
+                    [% IF ctx.register.opt_in_settings.size > 0 %]
+                        [% FOR optin IN ctx.register.opt_in_settings %]
+                        <tr>
+                            <td><label for="stgs.[% optin.name | uri %]'">[% optin.label | html %]</label></td>
+                            <td>
+                                <input type='checkbox'
+                                    name='stgs.[% optin.name | uri %]'
+                                    id='stgs.[% optin.name | uri %]'
+                                    title="[% optin.label | html %]"
+                                ></input>
+                            </td>
+                            <td><!-- display errors and example text --></td>
+                        </tr>
+                        [% END %]
+                    [% END %]
+                    <tr>
+                        <td colspan='3' class="text-center py-4">
+                            <a href="[% ctx.opac_root %]/home" 
+                                class="btn btn-confirm">[% l('Go Back') %]</a>
+                            <input type="submit" 
+                                value="[% l('Submit Registration') %]" 
+                                class="btn btn-confirm" />
+                        </td>
+                    </tr>
+                </table>
+            </form>
+            [% END %]
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[%- END %]
+<script>
+(function() {
+  'use strict';
+  window.addEventListener('load', function() {
+    // Fetch all the forms we want to apply custom Bootstrap validation styles to
+    var forms = document.getElementsByClassName('needs-validation');
+    // Loop over them and prevent submission
+    var validation = Array.prototype.filter.call(forms, function(form) {
+      form.addEventListener('submit', function(event) {
+        if (form.checkValidity() === false) {
+          event.preventDefault();
+          event.stopPropagation();
+        }
+        form.classList.add('was-validated');
+      }, false);
+    });
+  }, false);
+})();
+</script>
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac-new/results.tt2 b/Open-ILS/src/templates/opac-new/results.tt2
new file mode 100644 (file)
index 0000000..9ab664f
--- /dev/null
@@ -0,0 +1,155 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+
+    IF is_advanced || is_special;
+        ctx.page_title = l("Search Results");
+    ELSE;
+        ctx.page_title = l("Search Results: ") _ CGI.param('query') | html;
+    END;
+
+    page = CGI.param('page');
+    page = page.match('^\d+$') ? page : 0; # verify page is a sane value
+
+    page_count = (!ctx.page_size.defined || !ctx.hit_count.defined || ctx.page_size == 0) ? 1 : POSIX.ceil(ctx.hit_count / ctx.page_size);
+
+    # We don't want search engines indexing search results
+    ctx.metalinks.push('<meta name="robots" content="noindex,follow">');
+
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS get_library;
+-%]
+    <h2 class="sr-only">[% l('Search Results') %]</h2>
+    <form action="[% ctx.opac_root %]/results" method="get">
+    [% INCLUDE "opac/parts/searchbar.tt2" took_care_of_form=1 %]
+    <h3 class="sr-only">[% l('Additional search filters and navigation') %]</h3>
+    <div class="almost-content-wrapper border-bottom">
+
+        [%# hide the header bar when displaying metarecord constituents
+          instead of skipping it altogether to allow the search form
+          variables to propagate %]
+        [% IF ctx.metarecord;
+          mr_attrs = {marc_xml => ctx.metarecord_master.marc_xml};
+          PROCESS get_marc_attrs args=mr_attrs %]
+          <div class="results_header_lbl">
+            [% l('Viewing Results for Grouped Record: [_1]',
+                mr_attrs.title) | html %]
+          </div>
+          [% IF CGI.param('query') %]
+          <div>
+            <a href="[% mkurl(ctx.opac_root _ '/results', {}, ['metarecord','page']); %]">
+              [% l('&#9668; Return to Grouped Search Results') %]
+            </a>
+          </div>
+          <br/>
+          [% END %]
+        [% END %]
+        <div [%- IF ctx.metarecord %]class="hidden"[%- ELSE %]class="container"[%- END %]>
+            <div id="results_header_inner">
+               
+                <div id="refine_hits" class="btn btn-secondary"><a onclick="getFacety();">[% l('Refine these results') %]</a></div>
+                <div id="return_to_hits" class="results_header_btns"><a onclick="getResulty();">[% l('Back to results') %]</a></div>
+               
+
+
+                <div class="search-options text-center">
+                    <div class="mx-auto">
+                        <!-- ===== Drop Down ===== -->
+                        <div class="search-option my-2">
+                               <label for="opac.result.sort">[% l('') %]</label>
+                               [% INCLUDE "opac/parts/filtersort.tt2" value=CGI.param('sort') submit_on_change=1 class="form-control" %]
+                        </div>
+                        [%- IF show_more_details.default != 'hide' -%]
+
+                        <!-- ===== Buttons ===== -->
+                       <div class="search-option my-2">
+                               [% IF show_detail_view %]
+                               <a href="[% mkurl('', {detail_record_view => 0}) %]" class="btn btn-sm btn-secondary">[% l('Show Fewer Details') %]</a> [% ELSE %]
+                               <a href="[% mkurl('', {detail_record_view => 1}) %]" class="btn btn-sm btn-secondary">[% l('Show More Details') %]</a>
+                               [% END %]
+                       </div>
+                       [%- END -%]
+                         [% IF (is_advanced AND NOT is_special) AND CGI.param('qtype') %]
+                        <div class="search-option my-2">
+                            <span id="refine_search_link"> <a class="btn btn-secondary btn-sm" href="[% mkurl(ctx.opac_root _ '/advanced') %]">[%
+                                l('Refine My Original Search')
+                            %]</a> </span>
+                        </div>
+                        [% END %]
+                    </div>
+
+
+                    <!-- ===== Checkboxes ===== -->
+                    <div class="search-check text-center mb-3">
+                     [%- IF !search.no_highlight %]
+                        <div class="search-filter form-check m-2">        
+                            <label for="no_highlight" class="form-check-label">
+                            <input class="form-check-input" type="checkbox" id="no_highlight" name="no_highlight" value="1"
+                                onchange="search_modifier_onchange('no_highlight', this, true)"
+                                [% CGI.param('no_highlight').size ? ' checked="checked"' : '' %] />
+                            [% l('Disable Highlighting') %]
+                            </label>
+                        </div>
+                        [% END; %]
+                        <div class="search-filter form-check m-2">
+                           <label for="limit_to_available" class="form-check-label">
+                               <input class="form-check-input" type="checkbox" id="limit_to_available" name="modifier" value="available"
+                            onchange="search_modifier_onchange('available', this, true)"
+                            [% CGI.param('modifier').grep('available').size ? ' checked="checked"' : '' %] />
+                               [% l('Limit to Available Items') %]
+                           </label>
+                        </div>
+                        [% IF NOT metarecords.disabled %]
+                       <div class="search-filter form-check m-2">
+                        <label for="metabib_search_modifier" class="form-check-label">
+                            <input class="form-check-input" type="checkbox" id="metabib_search_modifier"
+                                name="modifier" value="metabib"
+                                onchange="search_modifier_onchange('metabib', this, true)"
+                                [% CGI.param('modifier').grep('metabib').size ? ' checked="checked"' : '' %] />
+                            [% l('Group By Formats and Editions') %]
+                        </label>
+                       </div>
+                    [% END %]
+                    [% IF ctx.depth_sel_checkbox %]
+                    <div class="search-filter form-check m-2">
+                       <label class="form-check-label" for="depth_sel" title="[% ctx.depth_sel_tooltip | html %]">
+                               <input class="form-check-input" type="checkbox" id="depth_sel" name="depth" value="[% ctx.depth_sel_depth %]"
+                            onchange="this.form.submit()"
+                            [% CGI.param('depth') == ctx.depth_sel_depth ? ' checked="checked"' : '' %] />
+                               [% ctx.depth_sel_checkbox_label | html %]
+                       </label>
+                    </div>
+                    [% END %]
+                    [% IF ctx.exclude_electronic_checkbox %]
+                                <div class="search-filter form-check m-2">
+                       <label class="form-check-label" for='opac.result.excludeelec'>
+                        <input class="form-check-input" type='checkbox' id='opac.result.excludeelec' name="fi:-search_format" value="electronic"
+                            onchange="exclude_onchange(this)"
+                            [%- CGI.param('fi:-search_format').grep('electronic').size ? ' checked="checked"' : '' %] />
+                        [% l("Exclude Electronic Resources") %]
+                       </label>
+                    </div>
+                    [% END %]
+                   </div>
+                </div>
+
+
+            </div>
+        </div>
+    </div>
+    </form>
+    <div id="content-wrapper">
+        <div id="main-content">
+            <div id="results-page">
+                [%- IF (ctx.depth_sel_checkbox OR ctx.depth_sel_button) AND CGI.param('depth') == ctx.depth_sel_depth AND ctx.depth_sel_resultshint %]
+                <div id="results-page-depth-hint">[% ctx.depth_sel_resultshint | html %]</div>
+                [%- END %]
+                [% path = "opac/parts/result/" _
+                    (ctx.records.size ? "table.tt2" : "lowhits.tt2");
+                INCLUDE $path %]
+            </div>
+            <div class="common-full-pad"></div>
+        </div>
+        <br class="clear-both" />
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/sms_cn.tt2 b/Open-ILS/src/templates/opac-new/sms_cn.tt2
new file mode 100644 (file)
index 0000000..7c958d1
--- /dev/null
@@ -0,0 +1,52 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Send Call Number via Text/SMS") %]
+    <h2 class="sr-only">[% l('Send Call Number via Text/SMS') %]</h2>
+    <div class="mobile_hide">
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    </div>
+    <div id="content-wrapper">
+        <div id="main-content">
+            <div class="common-full-pad"></div>
+            <div>
+                <p>
+                    [% IF ctx.event != -1 %]
+                    <br/>
+
+                    [% IF ctx.origin.search('redirect');
+                        SET ctx.origin = ctx.opac_root _ '/record/' _ CGI.url_param('rec') _ '?query=' _
+                        CGI.url_param('query') _ ';qtype=' _ CGI.url_param('qtype') _ ';locg=' _ CGI.url_param('locg');
+                    END %]
+
+                    [% IF ctx.sms_notify %]
+                    <h1>[% l('Your message has been sent!') %]</h1>
+                    <a href="[% ctx.origin %]">[% l('Return to record') %]</a>
+                    <div class="sms_text"><pre>[% ctx.event.template_output.data %]</pre></div>
+                    [% ELSE %]
+                    <h1>[% l('Text call number') %]</h1>
+                    <a href="[% ctx.origin %]">[% l('Return to record') %]</a>
+                    <div class="sms_text"><pre>[% ctx.event.template_output.data %]</pre></div>
+                        <form method="post">
+                                <input type="hidden" name="copy_id" value="[% ctx.copy_id %]"/>
+                                <input type="hidden" name="origin" value="[% ctx.origin %]"/>
+                                [% INCLUDE "opac/parts/sms_carrier_selector.tt2" sms_carrier_hide_warning="true" %]<br/>
+                                [% INCLUDE "opac/parts/sms_number_textbox.tt2" %]<br/>
+                                <input type="submit"
+                                    name="submit"
+                                    value="[% l('Submit') %]"
+                                    title="[% l('Submit') %]"
+                                    alt="[% l('Submit') %]"
+                                    class="opac-button" />
+                                <br/>[% l('Note: carrier charges may apply'); %]
+                        </form>
+                    [% END %]
+                    [% ELSE %]
+                    <span>[% l('SMS not enabled for this site.') %]</span>
+                    [% END %]
+                </p>
+            </div>
+            <div class="common-full-pad"></div>
+        </div>
+    </div>
+[%- END %]
diff --git a/Open-ILS/src/templates/opac-new/temp_warn.tt2 b/Open-ILS/src/templates/opac-new/temp_warn.tt2
new file mode 100644 (file)
index 0000000..8fdea97
--- /dev/null
@@ -0,0 +1,27 @@
+[%- PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Basket Warning") %]
+    <h2 class="sr-only">[% l('Basket Warning') %]</h2>
+    [% INCLUDE "opac/parts/searchbar.tt2" %]
+    <div id="content-wrapper">
+        <div id="main-content">
+             <p class="big-strong">[% l('You are adding to a basket.') %]
+                [% IF ctx.user ;
+                      l('This information will disappear when you logout, unless you save it to a permanent list.');
+                   ELSE;
+                      l('This information will disappear when you end your session, unless you login and save it to a permanent list.');
+                   END
+                %]</p>
+             <form method="post" action="[% mkurl(ctx.opac_root _ '/temp_warn/post', {}, 1) %]">
+             <input type="hidden" name="redirect_to" value="[% ctx.redirect_to %]" />
+             <input type="checkbox" name="no_temp_list_warn" value="on" />
+             <label for="no_temp_list_warn">[% l('Do not show this warning again.') %]</label>
+             <br />
+             <input type="submit" class="fixed" value="[% l('OK') %]" />
+             </form>
+            <div class="common-full-pad"></div>        
+        </div>
+    </div>
+[%- END %]