From: Bill Erickson Date: Tue, 20 Dec 2011 20:46:24 +0000 (-0500) Subject: TPac: patron saved searches X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=refs%2Fheads%2Fuser%2Fberick%2Ftpac-user-saved-searches;p=working%2FEvergreen.git TPac: patron saved searches Adds a "Save Search" option on search results pages. When clicked, users are prompted to log in (if not already), then given a chance to save the search. Saving is done from within a new "Saved Searches" tab in My Account, where users can also see/delete/execute existing saved searches. The saved search UI also provides an RSS link. Currently, only searches that build a query-parser query are save-able. This excludes MARC expert search, at least until it can be expressed as a query-parser query. For more easier additions of query_type in the future, this commit also changes the query_type column from TEXT w/ constraint to ENUM, which is simpler to add types to. Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm index a0413c6eab..0511d45fdd 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm @@ -160,6 +160,7 @@ sub load { return $self->load_myopac_prefs_settings if $path =~ m|opac/myopac/prefs_settings|; return $self->load_myopac_prefs if $path =~ m|opac/myopac/prefs|; return $self->load_sms_cn if $path =~ m|opac/sms_cn|; + return $self->load_myopac_save_search if $path =~ m|opac/myopac/save_search|; return Apache2::Const::OK; } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm index 489594ce2f..f7d3e25a41 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm @@ -1957,4 +1957,108 @@ sub load_password_reset { return Apache2::Const::OK; } +sub load_myopac_save_search { + my $self = shift; + my $e = $self->editor; + my $ctx = $self->ctx; + my $name = $self->cgi->param('name'); + my $action = $self->cgi->param('action') || ''; + my @selected = $self->cgi->param('selected'); + + my ($query) = + OpenILS::WWW::EGCatLoader::_prepare_biblio_search($self->cgi, $ctx); + + $ctx->{full_query} = $query; + + $e->xact_begin; # for saving and master-db reading + + my $saved = $e->search_actor_usr_saved_search([ + { owner => $e->requestor->id, + query_type => 'query_parser', + target => 'record' + }, {order_by => {auss => 'create_date DESC'}} + ]); + + if ($name) { # save the current query + + # see if that name is already taken + if (grep { $_->name eq $name } @$saved) { + $ctx->{name_taken} = $name; + $e->rollback; + + } else { + + # all clear, create the list + my $ss = Fieldmapper::actor::usr_saved_search->new; + $ss->owner($e->requestor->id); + $ss->query_type('query_parser'); + $ss->query_text($query); + $ss->name($name); + $ss->target('record'); + + unless ($ss = $e->create_actor_usr_saved_search($ss)) { + $logger->error("tpac: error creating saved search ".$e->die_event); + return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; + } + + $e->commit; + unshift(@$saved, $ss); + } + + } elsif ($action eq 'delete' and @selected) { + + # verify the deleted searches all belong to this user + for my $ss_id (@selected) { + my ($ss) = grep { $_->id eq $ss_id } @$saved; + + unless ($ss) { + $logger->error("tpac: attempt made to delete another user's list"); + $e->rollback; + return Apache2::Const::HTTP_BAD_REQUEST; + } + + unless ($e->delete_actor_usr_saved_search($ss)) { + $logger->error("tpac: error deleting saved search ".$e->die_event); + return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; + } + + # remove the deteled list from our collection + $saved = [ grep { $_->id ne $ss_id } @$saved ]; + } + + $ctx->{searches_deleted} = scalar(@selected); + $e->commit; + + } else { + $e->rollback; # read-only + } + + + # extract the site() and depth() information so links can be built + # (if desired) using the CGI params for those instead. + my @saved_scrubbed; + for my $search (@$saved) { + my %blob = (search => $search); + my $query = $search->query_text; + + if (my ($site) = ($query =~ /site\(([^\)]+)\)/)) { + $self->apache->log->warn("site = $site"); + my ($org) = grep { $_->shortname eq $site } @{$ctx->{aou_list}->()}; + $blob{loc} = $org->id; + $query =~ s/site\(([^\)]+)\)//g; + } + + if (my ($depth) = ($query =~ /depth\((\d+)\)/)) { + $blob{depth} = $depth; + $query =~ s/depth\((\d+)\)//g; + } + + $blob{scrubbed_query} = $query; + push(@saved_scrubbed, \%blob); + } + + $ctx->{saved_searches} = \@saved_scrubbed; + return Apache2::Const::OK; +} + 1; diff --git a/Open-ILS/src/sql/Pg/005.schema.actors.sql b/Open-ILS/src/sql/Pg/005.schema.actors.sql index fbe05048c5..eddf073a00 100644 --- a/Open-ILS/src/sql/Pg/005.schema.actors.sql +++ b/Open-ILS/src/sql/Pg/005.schema.actors.sql @@ -587,6 +587,7 @@ $$; CREATE INDEX actor_usr_standing_penalty_usr_idx ON actor.usr_standing_penalty (usr); CREATE INDEX actor_usr_standing_penalty_staff_idx ON actor.usr_standing_penalty ( staff ); +CREATE TYPE actor.usr_saved_search_query_type AS ENUM ('URL', 'query_parser'); CREATE TABLE actor.usr_saved_search ( id SERIAL PRIMARY KEY, @@ -596,10 +597,7 @@ CREATE TABLE actor.usr_saved_search ( name TEXT NOT NULL, create_date TIMESTAMPTZ NOT NULL DEFAULT now(), query_text TEXT NOT NULL, - query_type TEXT NOT NULL - CONSTRAINT valid_query_text CHECK ( - query_type IN ( 'URL' )) DEFAULT 'URL', - -- we may add other types someday + query_type actor.usr_saved_search_query_type NOT NULL DEFAULT 'URL', target TEXT NOT NULL CONSTRAINT valid_target CHECK ( target IN ( 'record', 'metarecord', 'callnumber' )), diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.saved_search_qtype_constraint.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.saved_search_qtype_constraint.sql new file mode 100644 index 0000000000..13da58c4b7 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.saved_search_qtype_constraint.sql @@ -0,0 +1,17 @@ +-- Evergreen DB patch XXXX.schema.saved_search_qtype_constraint.sql +-- +BEGIN; + +-- check whether patch can be applied +SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version); + +CREATE TYPE actor.usr_saved_search_query_type AS ENUM ('URL', 'query_parser'); + +ALTER TABLE actor.usr_saved_search + DROP CONSTRAINT valid_query_text, + ALTER COLUMN query_type DROP DEFAULT, + ALTER COLUMN query_type TYPE actor.usr_saved_search_query_type + USING (query_type::actor.usr_saved_search_query_type), + ALTER COLUMN query_type SET DEFAULT 'URL'; + +COMMIT; diff --git a/Open-ILS/src/templates/opac/myopac/save_search.tt2 b/Open-ILS/src/templates/opac/myopac/save_search.tt2 new file mode 100644 index 0000000000..ae95c3b319 --- /dev/null +++ b/Open-ILS/src/templates/opac/myopac/save_search.tt2 @@ -0,0 +1,81 @@ +[% PROCESS "opac/parts/header.tt2"; + PROCESS "opac/parts/misc_util.tt2"; + WRAPPER "opac/parts/myopac/base.tt2"; + myopac_page = "save_search" %] +
+ + [% IF CGI.param('save') OR ctx.name_taken %] +
+ [% FOR k IN CGI.Vars; # copy the search params into the form + NEXT IF !k OR k == 'save' OR k == 'name'; + FOR val IN CGI.param(k) %] + + [% END; END %] + [% l('Save search as: ') %] + + + [% IF ctx.name_taken %] + + [% l('You already have a list named "[_1]". Please choose another name.', ctx.name_taken) %] + + [% END %] + + [ [% l('Full Query : [_1]', ctx.full_query) | html %] ] + +
+
+ [% END %] + + [% IF ctx.searches_deleted %] +
+ [% l('Successfully deleted [quant,_1,search,searches]', ctx.searches_deleted) %] +
+ [% END %] + + [% IF ctx.saved_searches.0 %] +
+
[% l('Saved Searches') %]
+ + + + + + + + + + + + + + [% FOR search_data IN ctx.saved_searches %] + + + + + + + + [% END %] + +
+ + [% l('Created On') %][% l('Name') %][% l('RSS') %][% l('Full Query') %]
+ + [% date.format(ctx.parse_datetime(search_data.search.create_date), DATE_FORMAT) %] + [% search_data.search.name | html %] + + [% l('RSS Feed') %] + [% search_data.search.query_text | html %]
+
+ [% END %] + +
+[% END %] diff --git a/Open-ILS/src/templates/opac/parts/myopac/base.tt2 b/Open-ILS/src/templates/opac/parts/myopac/base.tt2 index 9c11f132e5..800f239d4e 100644 --- a/Open-ILS/src/templates/opac/parts/myopac/base.tt2 +++ b/Open-ILS/src/templates/opac/parts/myopac/base.tt2 @@ -6,6 +6,7 @@ {url => "holds", name => l("Holds")}, {url => "prefs", name => l("Account Preferences")}, {url => "lists", name => l("My Lists")} + {url => "save_search", name => l("Saved Searches")} ]; skin_root = "../" %] diff --git a/Open-ILS/src/templates/opac/results.tt2 b/Open-ILS/src/templates/opac/results.tt2 index 566936a6f8..9cc2e02275 100644 --- a/Open-ILS/src/templates/opac/results.tt2 +++ b/Open-ILS/src/templates/opac/results.tt2 @@ -59,6 +59,14 @@ [% CGI.param('modifier').grep('available').size ? ' checked="checked"' : '' %] /> [% l('Limit to available items') %] + + [% IF CGI.param('query') %] +
+ + [% END %] +
diff --git a/Open-ILS/web/css/skin/default/opac/style.css b/Open-ILS/web/css/skin/default/opac/style.css index d9f2120262..1b8c3e4709 100644 --- a/Open-ILS/web/css/skin/default/opac/style.css +++ b/Open-ILS/web/css/skin/default/opac/style.css @@ -904,6 +904,30 @@ div.result_place_hold { padding: 8px 0px 7px 0px; } +.generic_table { + border-collapse: collapse; +} + +.generic_table_header th { + text-align: left; + font-weight:bold; + text-transform:uppercase; + background: #d8d8d8; + font-size: 10px; + padding: 8px; +} +.generic_table_checkbox { + width: 1%; + padding-left:10px; +} + +#acct_saved_search_table { + width: 75%; +} +#acct_saved_search_table td { + padding : 8px; +} + #acct_list_header select, #acct_list_header_anon select { font-weight:normal; text-transform:none;