From: Fredrick Parks Date: Wed, 1 May 2013 17:00:19 +0000 (-0700) Subject: Added boolean search tab to advanced search, translates boolean key words into the... X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=f8c2c778ffb5691a2fbbb7d06012c503a4a7907a;p=evergreen%2Fmasslnc.git Added boolean search tab to advanced search, translates boolean key words into the appropriate binary operators Signed-off-by: Fredrick Parks Signed-off-by: Kathy Lussier --- diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm index 7ee9cfae20..1c718769f5 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm @@ -6,6 +6,7 @@ use OpenILS::Utils::CStoreEditor qw/:funcs/; use OpenILS::Utils::Fieldmapper; use OpenILS::Application::AppUtils; use OpenSRF::Utils::JSON; +use OpenSRF::Utils::Cache; use Data::Dumper; $Data::Dumper::Indent = 0; my $U = 'OpenILS::Application::AppUtils'; @@ -14,7 +15,7 @@ sub _prepare_biblio_search_basics { my ($cgi) = @_; return $cgi->param('query') unless $cgi->param('qtype'); - + $logger->info(Dumper($cgi->param('query'))); my %parts; my @part_names = qw/qtype contains query bool/; $parts{$_} = [ $cgi->param($_) ] for (@part_names); @@ -31,7 +32,6 @@ sub _prepare_biblio_search_basics { $qtype = 'title'; $jtitle = 1; } - # This stuff probably will need refined or rethought to better handle # the weird things Real Users will surely type in. $contains = "" unless defined $contains; # silence warning @@ -66,13 +66,89 @@ sub _prepare_biblio_search_basics { return $full_query; } -sub _prepare_biblio_search { +######################################################################## +# Replaces boolean search keywords [and, or, not] with the appropriate +# tokens [&&, ||, -], and returns the modified query. +# Keywords can be localised, localization files are located in +# templates/opac/location +######################################################################## +sub _prepare_biblio_search_boolean { my ($cgi, $ctx) = @_; + my $cache = OpenSRF::Utils::Cache->new; + my $i18n; + my $location = $ctx->{locale}; + #Reformat location abreveation [en_us -> en-US] + $location =~ s/(\w\w)_(\w\w)/$1\-\U$2\E/g; + + #Check cache for localization valuse for the current locale + $i18n = $cache->get_cache("boolsearchlocale.${location}"); + #unless there is a cached set of values load from disk and add to cache + unless (defined($i18n)){ + #Else if the isn't: load values from disk and cache values + $i18n = {}; + my $conf = OpenSRF::Utils::SettingsClient->new(); + my $dir = $conf->config_value("dirs", "script"); + my @foundFiles = glob("$dir/web/opac/locale/${location}/opac.dtd"); + my $string_bundle = shift @foundFiles; + { #if file can not be opened we will just break out of this block + #open locale file or break out of the block + open(I18NFH, '<', $string_bundle) or last; + local $/ = undef; + my $file = ; + close(I18NFH); + + #Regex for the keywords and, or, not + ( $i18n->{"AND"} ) = $file =~ /opac\.boolean\.keyword\.and "(.+)"/; + ( $i18n->{"OR"} ) = $file =~ /opac\.boolean\.keyword\.or "(.+)"/; + ( $i18n->{"NOT"} ) = $file =~ /opac\.boolean\.keyword\.not "(.+)"/; + } + #if AND keyword was not in opac.dtd or file couldn't be opened default to english + if(!defined($i18n->{"AND"})) + { + $logger->info("[ERROR] Boolean Search: AND keyword not found in opac.dtd, defaulting to english"); + $i18n->{"AND"} = "and"; + } + #if OR keyword was not in opac.dtd or file couldn't be opened default to english + if(!defined($i18n->{"OR"})) + { + $logger->info("[ERROR] Boolean Search: OR keyword not found in opac.dtd, defaulting to english"); + $i18n->{"OR"} = "or"; + } + #if NOT keyword was not in opac.dtd or file couldn't be opened default to english + if(!defined($i18n->{"NOT"})) + { + $logger->info("[ERROR] Boolean Search: NOT keyword not found in opac.dtd, defaulting to english"); + $i18n->{"NOT"} = "not"; + } + #add localization keywords to cache + $cache->put_cache("boolsearchlocale.${location}", $i18n); + } + + my $and = $i18n->{"AND"}; + my $or = $i18n->{"OR"}; + my $not = $i18n->{"NOT"}; + #replace keywords in the submited query with the boolean tokens + my $queryStatement = $cgi->param('boolean_query'); + #RegEx selects the l18n keyword in the query and substatutes in '&&' + $queryStatement =~ s/((?<=" )(\b$and\b)|(\b$and\b)((?= ")|(?=(\w|\s)+((\b($and|$or|$not)\b)(?![^"]+(\w"))))|(?=[^"]+$)))/\&\&/gi; + #RegEx selects the l18n keyword in the query and substatutes in '||' + $queryStatement =~ s/((?<=" )(\b$or\b)|(\b$or\b)((?= ")|(?=(\w|\s)+((\b($and|$or|$not})\b)(?![^"]+(\w"))))|(?=[^"]+$)))/\|\|/gi; + #RegEx selects the l18n keyword and all trailing white space in the query and substatutes in '-' + $queryStatement =~ s/((?<=" )(\b$not\s+)|(\b$not\s+)((?= ")|(?=(\w|\s)+((\b($and|$or|$not)\b)(?![^"]+(\w"))))|(?=[^"]+$)))/\-/gi; + $ctx->{query} = $queryStatement; + return $queryStatement; +} - my $query = _prepare_biblio_search_basics($cgi) || ''; +sub _prepare_biblio_search { + my ($cgi, $ctx) = @_; + my $query; + if ($cgi->param('boolean')) { + $query = _prepare_biblio_search_boolean($cgi,$ctx); + }else { + $query = _prepare_biblio_search_basics($cgi) || ''; + } $query .= ' ' . $ctx->{global_search_filter} if $ctx->{global_search_filter}; - foreach ($cgi->param('modifier')) { # The unless bit is to avoid stacking modifiers. $query = ('#' . $_ . ' ' . $query) unless @@ -390,7 +466,6 @@ sub load_rresults { } my ($query, $site, $depth) = _prepare_biblio_search($cgi, $ctx); - $self->get_staff_search_settings; if (!$find_last and $ctx->{staff_saved_search_size}) { diff --git a/Open-ILS/src/templates/opac/advanced.tt2 b/Open-ILS/src/templates/opac/advanced.tt2 index de86006f49..c90635d32c 100644 --- a/Open-ILS/src/templates/opac/advanced.tt2 +++ b/Open-ILS/src/templates/opac/advanced.tt2 @@ -28,6 +28,11 @@ [% l('Expert Search') %] + [% IF search.boolean_enabled == "true" %] + [% l('Boolean Search') %] + [% END %] @@ -40,6 +45,8 @@ [% INCLUDE "opac/parts/advanced/numeric.tt2" %] [% ELSIF pane == 'expert' %] [% INCLUDE "opac/parts/advanced/expert.tt2" %] + [% ELSIF pane == 'boolean' %] + [% INCLUDE "opac/parts/advanced/boolean.tt2" %] [% END %]
diff --git a/Open-ILS/src/templates/opac/css/style.css.tt2 b/Open-ILS/src/templates/opac/css/style.css.tt2 index 8a3c4f72d0..59a92b52ca 100644 --- a/Open-ILS/src/templates/opac/css/style.css.tt2 +++ b/Open-ILS/src/templates/opac/css/style.css.tt2 @@ -338,7 +338,7 @@ for now until a better color is picked - if needed. padding-left: 10px !important; } -#adv_search.on, #num_search.on, #expert_search.on { +#adv_search.on, #num_search.on, #expert_search.on, #boolean_search.on { color: [% css_colors.accent_darker %]; background: [% css_colors.background %]; text-decoration: none; diff --git a/Open-ILS/src/templates/opac/parts/advanced/boolean.tt2 b/Open-ILS/src/templates/opac/parts/advanced/boolean.tt2 new file mode 100644 index 0000000000..3b4e36c2ed --- /dev/null +++ b/Open-ILS/src/templates/opac/parts/advanced/boolean.tt2 @@ -0,0 +1,79 @@ +[% query = CGI.param('boolean_query'); %] +
+
[% l("Free-Form Boolean Search") %]
+ + +
[% l('Search Filters') %]
+ + [% + in_row = 0; + FOR adv_chunk IN search.bool_config; + NEXT IF adv_chunk.adv_hide; + IF in_row == 0; + in_row = 1; %] + + [% + END; %] + + [% + IF adv_chunk.adv_break; + in_row = 0; %] + + [% + END; + END; %] +
+ [% adv_chunk.adv_label %]
+ [% + IF adv_chunk.adv_special; + SWITCH adv_chunk.adv_special; + CASE "lib_selector"; + PROCESS "opac/parts/org_selector.tt2"; + INCLUDE build_org_selector show_loc_groups=1; %] +
+ + +
+ [% + CASE "pub_year"; %] + +
+ + + [% l("and") %] + +
+ [% + CASE "sort_selector"; + INCLUDE "opac/parts/filtersort.tt2" + value=CGI.param('sort') class='results_header_sel'; + END; + ELSIF adv_chunk.adv_attr; + INCLUDE "opac/parts/coded_value_selector.tt2" + attr=adv_chunk.adv_attr multiple="multiple" size="4"; + END; %] +
+ + + + + [% l('Clear Form') %] +
\ No newline at end of file diff --git a/Open-ILS/src/templates/opac/parts/config.tt2 b/Open-ILS/src/templates/opac/parts/config.tt2 index 58d2c09e41..5d6e5ff9c2 100644 --- a/Open-ILS/src/templates/opac/parts/config.tt2 +++ b/Open-ILS/src/templates/opac/parts/config.tt2 @@ -145,6 +145,32 @@ search.adv_config = [ #search.default_adv_select_height = 4; ############################################################################## +# Define the boolean search limiters and labels. +# 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_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: +# sort_selector will put the sort results selector +# lib_selector will put the search library box (with limit to available) +# pub_year will put the publication year box + +search.bool_config = [ + {adv_label => l("Item Type"), adv_attr => ["mattype", "item_type"]}, + {adv_label => l("Language"), adv_attr => "item_lang"}, + {adv_label => l("Sort Results"), adv_special => "sort_selector", adv_break => 1}, + {adv_label => l("Search Library"), adv_special => "lib_selector"}, + {adv_label => l("Publication Year"), adv_special => "pub_year"}, +]; + +############################################################################## +# Define if the boolean search tab is enabled on the +# advanced search page + +search.boolean_enabled = "true"; + +############################################################################## # 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 diff --git a/Open-ILS/src/templates/opac/parts/header.tt2 b/Open-ILS/src/templates/opac/parts/header.tt2 index 8b60ba9c7b..00dc983ef5 100644 --- a/Open-ILS/src/templates/opac/parts/header.tt2 +++ b/Open-ILS/src/templates/opac/parts/header.tt2 @@ -11,6 +11,7 @@ is_advanced = CGI.param("_adv").size; is_special = CGI.param("_special").size; + is_boolean = CGI.param("boolean").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 diff --git a/Open-ILS/src/templates/opac/parts/searchbar.tt2 b/Open-ILS/src/templates/opac/parts/searchbar.tt2 index a48c4a0e6f..b48ccd4920 100644 --- a/Open-ILS/src/templates/opac/parts/searchbar.tt2 +++ b/Open-ILS/src/templates/opac/parts/searchbar.tt2 @@ -17,7 +17,10 @@