Bugzilla – Attachment 41805 Details for
Bug 14567
Add an elasticsearch driven browse interface to the OPAC
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 14567: frontend template and JS for browse interface
Bug-14567-frontend-template-and-JS-for-browse-inte.patch (text/plain), 15.53 KB, created by
Robin Sheat
on 2015-08-24 05:28:42 UTC
(
hide
)
Description:
Bug 14567: frontend template and JS for browse interface
Filename:
MIME Type:
Creator:
Robin Sheat
Created:
2015-08-24 05:28:42 UTC
Size:
15.53 KB
patch
obsolete
>From f11d6968e08455832bb27310a845d3dd2020489a Mon Sep 17 00:00:00 2001 >From: Jen Zajac <jen@catalyst.net.nz> >Date: Fri, 14 Aug 2015 19:36:15 +1200 >Subject: [PATCH] Bug 14567: frontend template and JS for browse interface > >--- > .../opac-tmpl/bootstrap/en/modules/opac-browse.tt | 83 ++++++++++ > koha-tmpl/opac-tmpl/bootstrap/js/browse.js | 176 +++++++++++++++++++++ > koha-tmpl/opac-tmpl/bootstrap/less/opac.less | 101 ++++++++++++ > opac/opac-browse.pl | 13 +- > 4 files changed, 372 insertions(+), 1 deletion(-) > create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt > create mode 100644 koha-tmpl/opac-tmpl/bootstrap/js/browse.js > >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt >new file mode 100644 >index 0000000..5bda6a1 >--- /dev/null >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt >@@ -0,0 +1,83 @@ >+[% USE Koha %] >+[% INCLUDE 'doc-head-open.inc' %][% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog › Browse our catalog >+[% INCLUDE 'doc-head-close.inc' %] >+[% BLOCK cssinclude %][% END %] >+</head> >+[% INCLUDE 'bodytag.inc' bodyid='opac-browser' %] >+[% INCLUDE 'masthead.inc' %] >+ >+ <div class="main"> >+ <ul class="breadcrumb"> >+ <li><a href="/cgi-bin/koha/opac-main.pl">Home</a> <span class="divider">›</span></li> >+ <li><a href="#">Browse search</a></li> >+ </ul> >+ >+ <div class="container-fluid"> >+ <div class="row-fluid"> >+ [% IF ( OpacNav || OpacNavBottom ) %] >+ <div class="span2"> >+ <div id="navigation"> >+ [% INCLUDE 'navigation.inc' %] >+ </div> >+ </div> >+ [% END %] >+ >+ [% IF ( OpacNav ) %]<div class="span10">[% ELSE %]<div class="span12">[% END %] >+ >+ <div id="browse-search"> >+ <h1>Browse search</h1> >+ >+ <form> >+ <label for="browse-searchterm">Search for: </label><input type="search" id="browse-searchterm" name="searchterm" value="" /> >+ >+ <label for="browse-searchfield" class="hide-text">Search type: </label> >+ <select id="browse-searchfield" name="searchfield"> >+ <option value="author">Author</option> >+ <option value="subject">Subject</option> >+ <option value="title">Title</option> >+ </select> >+ >+ <div id="browse-searchfuzziness"> >+ <label for="exact" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="exact" value="0" /> Exact</label> >+ >+ <label for="fuzzy" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="fuzzy" value="1" checked="checked" /> Fuzzy</label> >+ >+ <label for="reallyfuzzy" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="reallyfuzzy" value="2" /> Really Fuzzy</label> >+ </div> >+ >+ <button class="btn btn-success" type="submit" accesskey="s">Search</button> >+ </form> >+ >+ <p id="browse-suggestionserror" class="error hidden">An error occured, please try again.</p> >+ >+ <div id="browse-resultswrapper" class="hidden"> >+ >+ <ol id="browse-searchresults" class="span3" start="-1" aria-live="polite"> >+ <li class="loading hidden"><img src="[% interface %]/[% theme %]/images/loading.gif" alt="" /> Loading</li> >+ <li class="no-results hidden">Sorry, there are no results, try a different search term.</li> >+ </ol> >+ >+ <h3 id="browse-selection" class="span9"></h3> >+ <div id="browse-selectionsearch" class="span9 hidden"> >+ >+ <div class="loading hidden"> >+ <img src="[% interface %]/[% theme %]/images/loading.gif" alt="" /> Loading >+ </div> >+ >+ <div class="no-results hidden"> >+ No results >+ </div> >+ >+ <ol aria-live="polite"> >+ </ol> >+ </div> >+ </div><!-- / .results-wrapper --> >+ >+ </div> <!-- / .userbrowser --> >+ </div> <!-- / .span10 --> >+ </div> <!-- / .row-fluid --> >+ </div> <!-- / .container-fluid --> >+ </div> <!-- / .main --> >+ >+[% INCLUDE 'opac-bottom.inc' %] >+[% BLOCK jsinclude %]<script type="text/javascript" src="[% interface %]/[% theme %]/js/browse.js"></script>[% END %] >diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/browse.js b/koha-tmpl/opac-tmpl/bootstrap/js/browse.js >new file mode 100644 >index 0000000..d8828af >--- /dev/null >+++ b/koha-tmpl/opac-tmpl/bootstrap/js/browse.js >@@ -0,0 +1,176 @@ >+jQuery.fn.overflowScrollReset = function() { >+ $(this).scrollTop($(this).scrollTop() - $(this).offset().top); >+ return this; >+}; >+ >+$(document).ready(function(){ >+ var xhrGetSuggestions, xhrGetResults; >+ >+ $('#browse-search form').submit(function(event) { >+ // if there is an in progress request, abort it so we >+ // don't end up with a race condition >+ if(xhrGetSuggestions && xhrGetSuggestions.readyState != 4){ >+ xhrGetSuggestions.abort(); >+ } >+ >+ var userInput = $('#browse-searchterm').val().trim(); >+ var userField = $('#browse-searchfield').val(); >+ var userFuzziness = $('input[name=browse-searchfuzziness]:checked', '#browse-searchfuzziness').val(); >+ var leftPaneResults = $('#browse-searchresults li').not('.loading, .no-results'); >+ var rightPaneResults = $('#browse-selectionsearch ol li'); >+ >+ event.preventDefault(); >+ >+ if(!userInput) { >+ return; >+ } >+ >+ // remove any error states and show the results area (except right pane) >+ $('#browse-suggestionserror').addClass('hidden'); >+ $('#browse-searchresults .no-results').addClass('hidden'); >+ $('#browse-resultswrapper').removeClass('hidden'); >+ $('#browse-selection').addClass('hidden').text(""); >+ $('#browse-selectionsearch').addClass('hidden'); >+ >+ // clear any results from left and right panes >+ leftPaneResults.remove(); >+ rightPaneResults.remove(); >+ >+ // show the spinner in the left pane >+ $('#browse-searchresults .loading').removeClass('hidden'); >+ >+ xhrGetSuggestions = $.get(window.location.pathname, {api: "GetSuggestions", field: userField, prefix: userInput, fuzziness: userFuzziness}) >+ .always(function() { >+ // hide spinner >+ $('#browse-searchresults .loading').addClass('hidden'); >+ }) >+ .done(function(data) { >+ var fragment = document.createDocumentFragment(); >+ >+ if (data.length === 0) { >+ $('#browse-searchresults .no-results').removeClass('hidden'); >+ >+ return; >+ } >+ >+ // scroll to top of container again >+ $("#browse-searchresults").overflowScrollReset(); >+ >+ // store the type of search that was performed as an attrib >+ $('#browse-searchresults').data('field', userField); >+ >+ $.each(data, function(index, object) { >+ // use a document fragment so we don't need to nest the elems >+ // or append during each iteration (which would be slow) >+ var elem = document.createElement("li"); >+ var link = document.createElement("a"); >+ link.textContent = object.text; >+ link.setAttribute("href", "#"); >+ elem.appendChild(link); >+ fragment.appendChild(elem); >+ }); >+ >+ $('#browse-searchresults').append(fragment.cloneNode(true)); >+ }) >+ .fail(function(jqXHR) { >+ //if 500 or 404 (abort is okay though) >+ if (jqXHR.statusText !== "abort") { >+ $('#browse-resultswrapper').addClass('hidden'); >+ $('#browse-suggestionserror').removeClass('hidden'); >+ } >+ }); >+ }); >+ >+ $('#browse-searchresults').on("click", 'a', function(event) { >+ // if there is an in progress request, abort it so we >+ // don't end up with a race condition >+ if(xhrGetResults && xhrGetResults.readyState != 4){ >+ xhrGetResults.abort(); >+ } >+ >+ var term = $(this).text(); >+ var field = $('#browse-searchresults').data('field'); >+ var rightPaneResults = $('#browse-selectionsearch ol li'); >+ >+ event.preventDefault(); >+ >+ // clear any current selected classes and add a new one >+ $(this).parent().siblings().children().removeClass('selected'); >+ $(this).addClass('selected'); >+ >+ // copy in the clicked text >+ $('#browse-selection').removeClass('hidden').text(term); >+ >+ // show the right hand pane if it is not shown already >+ $('#browse-selectionsearch').removeClass('hidden'); >+ >+ // hide the no results element >+ $('#browse-selectionsearch .no-results').addClass('hidden'); >+ >+ // clear results >+ rightPaneResults.remove(); >+ >+ // turn the spinner on >+ $('#browse-selectionsearch .loading').removeClass('hidden'); >+ >+ // do the query for the term >+ xhrGetResults = $.get(window.location.pathname, {api: "GetResults", field: field, term: term}) >+ .always(function() { >+ // hide spinner >+ $('#browse-selectionsearch .loading').addClass('hidden'); >+ }) >+ .done(function(data) { >+ var fragment = document.createDocumentFragment(); >+ >+ if (data.length === 0) { >+ $('#browse-selectionsearch .no-results').removeClass('hidden'); >+ >+ return; >+ } >+ >+ // scroll to top of container again >+ $("#browse-selectionsearch").overflowScrollReset(); >+ >+ $.each(data, function(index, object) { >+ // use a document fragment so we don't need to nest the elems >+ // or append during each iteration (which would be slow) >+ var elem = document.createElement("li"); >+ var title = document.createElement("h4"); >+ var link = document.createElement("a"); >+ var author = document.createElement("p"); >+ var subjects = document.createElement("p"); >+ var destination = window.location.pathname; >+ >+ destination = destination.replace("browse", "detail"); >+ destination = destination + "?biblionumber=" + object.id; >+ >+ author.className = "author"; >+ subjects.className = "subjects"; >+ >+ link.setAttribute("href", destination); >+ link.setAttribute("target", "_blank"); >+ link.textContent = object.title; >+ title.appendChild(link); >+ >+ author.textContent = object.authors.join(', '); >+ subjects.textContent = object.subjects.join(' | '); >+ >+ elem.appendChild(title); >+ elem.appendChild(author); >+ elem.appendChild(subjects); >+ fragment.appendChild(elem); >+ }); >+ >+ $('#browse-selectionsearch ol').append(fragment.cloneNode(true)); >+ }) >+ .fail(function(jqXHR) { >+ //if 500 or 404 (abort is okay though) >+ if (jqXHR.statusText !== "abort") { >+ $('#browse-resultswrapper').addClass('hidden'); >+ $('#browse-suggestionserror').removeClass('hidden'); >+ } >+ }); >+ >+ }); >+ >+}); >diff --git a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less >index 7c79c7d..14635da 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less >+++ b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less >@@ -2473,4 +2473,105 @@ a.reviewlink:visited { > padding-left:20px; > } > >+/*opac browse search*/ >+#browse-search { >+ >+ form { >+ label { >+ display: inline-block; >+ margin-right:5px; >+ } >+ >+ [type=submit] { >+ margin-top: 10px; >+ } >+ } >+ >+ #browse-resultswrapper { >+ margin-top: 4em; >+ >+ @media (min-width: 768px) and (max-width: 984px) { >+ margin-top: 2em; >+ } >+ >+ @media (max-width: 767px) { >+ margin-top: 1em; >+ } >+ } >+ #browse-searchresults, #browse-selectionsearch { >+ border: 1px solid #E3E3E3; >+ .border-radius-all(4px); >+ padding: 0; >+ overflow-y: auto; >+ max-height: 31em; >+ margin-bottom: 2em; >+ } >+ #browse-searchresults { >+ max-height: 31em; >+ list-style: none; >+ padding: 10px; >+ >+ a { >+ display: block; >+ margin-bottom: 5px; >+ >+ &.selected { >+ background-color:#EEE; >+ } >+ } >+ >+ li:last-child a { >+ margin-bottom: 0; >+ } >+ >+ @media (max-width: 767px) { >+ max-height: 13em; >+ } >+ } >+ #browse-selection { >+ margin-top: -40px; >+ padding-top: 0; >+ >+ @media (max-width: 767px) { >+ margin-top: 0; >+ } >+ } >+ #browse-selectionsearch ol { >+ list-style: none; >+ margin: 0; >+ >+ li { >+ padding: 1em; >+ >+ &:nth-child(odd) { >+ background-color: #F4F4F4; >+ } >+ } >+ } >+ #browse-selectionsearch p.subjects { >+ font-size: 0.9em; >+ margin-bottom: 0; >+ } >+ #browse-selectionsearch h4 { >+ margin: 0; >+ } >+ .error, .no-results { >+ background-color: #EEE; >+ border: 1px solid #E8E8E8; >+ text-align: left; >+ padding: 0.5em; >+ .border-radius-all(3px); >+ } >+ .loading { >+ text-align: center; >+ >+ img { >+ margin:0.5em 0; >+ position: relative; >+ left: -5px; >+ } >+ } >+} >+/*end browse search*/ >+ > @import "responsive.less"; >diff --git a/opac/opac-browse.pl b/opac/opac-browse.pl >index 4fc07c7..a6577eb 100755 >--- a/opac/opac-browse.pl >+++ b/opac/opac-browse.pl >@@ -24,6 +24,7 @@ use CGI qw( -utf8 :standard ); > > use C4::Auth; > use C4::Context; >+use C4::Output; > > use Koha::ElasticSearch; > use Koha::SearchEngine::Elasticsearch::Browse; >@@ -43,7 +44,17 @@ my $api = $query->param('api'); > if ( !$api ) { > > # No parameters, so we just render the template. >- >+ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( >+ { >+ template_name => "opac-browse.tt", >+ query => $query, >+ type => "opac", >+ authnotrequired => ( C4::Context->preference("OpacPublic") ? 1 : 0 ), >+ debug => 1, # TODO remove >+ } >+ ); >+ $template->param(); >+ output_html_with_http_headers $query, $cookie, $template->output; > } > elsif ( $api eq 'GetSuggestions' ) { > my $fuzzie = $query->param('fuzziness'); >-- >2.1.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 14567
:
41096
|
41804
|
41805
|
41806
|
56253
|
56254
|
56255
|
56256
|
56262
|
56263
|
56264
|
56270
|
56271
|
56272
|
56273
|
57141
|
61660
|
64477
|
64478
|
64779
|
69614
|
83127
|
83128
|
85401
|
85402
|
85403
|
85887
|
85888
|
86453
|
98509
|
99060
|
99292
|
99298