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;
}
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;
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,
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' )),
--- /dev/null
+-- 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;
--- /dev/null
+[% PROCESS "opac/parts/header.tt2";
+ PROCESS "opac/parts/misc_util.tt2";
+ WRAPPER "opac/parts/myopac/base.tt2";
+ myopac_page = "save_search" %]
+<div id='myopac_save_search_div' style="padding:5px;">
+
+ [% IF CGI.param('save') OR ctx.name_taken %]
+ <form action="[% ctx.opac_root %]/myopac/save_search" method="POST">
+ [% 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) %]
+ <input type="hidden" name="[% k | html %]" value="[% val | html %]" />
+ [% END; END %]
+ <span>[% l('Save search as: ') %]</span>
+ <span class='pad-level-1'><input type='text' name='name'/></span>
+ <input type="submit" value="[% l('Save') %]" alt="[% l('Save') %]" class="opac-button"/>
+ [% IF ctx.name_taken %]
+ <span class='error'>
+ [% l('You already have a list named "[_1]". Please choose another name.', ctx.name_taken) %]
+ </span>
+ [% END %]
+ <span class='pad-level-1'>
+ <b>[ <a href="[% mkurl(ctx.opac_root _ '/results') %]">[% l('Full Query : [_1]', ctx.full_query) | html %]</a> ]</b>
+ </span>
+ <hr/>
+ </form>
+ [% END %]
+
+ [% IF ctx.searches_deleted %]
+ <div class='pad-level-1 success'>
+ [% l('Successfully deleted [quant,_1,search,searches]', ctx.searches_deleted) %]
+ </div>
+ [% END %]
+
+ [% IF ctx.saved_searches.0 %]
+ <form method='POST'>
+ <div class="header_middle">[% l('Saved Searches') %]</div>
+ <input type="submit" value="[% l('Delete Selected') %]" alt="[% l('Delete Selected') %]" class="opac-button"/>
+ <input type='hidden' name='action' value='delete'/>
+ <table id='acct_saved_search_table' class='generic_table generic_table_header'>
+ <thead>
+ <tr>
+ <th class='generic_table_checkbox'>
+ <input type="checkbox" onclick="select_all_checkboxes('selected', this.checked)"/>
+ </th>
+ <th>[% l('Created On') %]</th>
+ <th>[% l('Name') %]</th>
+ <th>[% l('RSS') %]</th>
+ <th>[% l('Full Query') %]</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOR search_data IN ctx.saved_searches %]
+ <tr>
+ <td class='generic_table_checkbox'>
+ <input type="checkbox" name="selected" value="[% search_data.search.id %]"/>
+ </td>
+ <td>[% date.format(ctx.parse_datetime(search_data.search.create_date), DATE_FORMAT) %]</td>
+ <td>
+ <a href="[% mkurl(ctx.opac_root _ '/results', {
+ query => search_data.scrubbed_query,
+ loc => search_data.loc,
+ depth => search_data.depth}, 1)
+ %]">[% search_data.search.name | html %]</a>
+ </td>
+ <td>
+ <a target='_blank'
+ href='/opac/extras/opensearch/1.1/-/rss2-full?searchTerms=[% search_data.search.query_text | uri %]'><img
+ alt="[% l('RSS Feed') %]" border="0"
+ src="[% ctx.media_prefix %]/images/small-rss.png"/></a>
+ </td>
+ <td>[% search_data.search.query_text | html %]</td>
+ </tr>
+ [% END %]
+ </tbody>
+ </table>
+ </form>
+ [% END %]
+
+</div>
+[% END %]
{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 = "../"
%]
[% CGI.param('modifier').grep('available').size ? ' checked="checked"' : '' %] />
[% l('Limit to available items') %]
</label>
+
+ [% IF CGI.param('query') %]
+ <div class="results_header_div"></div>
+ <div class='results_header_btns'>
+ <a href="[% mkurl(ctx.opac_root _ '/myopac/save_search', {save => 1}) %]">[% l('Save Search') %]</a>
+ </div>
+ [% END %]
+
<div class="clear-both"></div>
</div>
</div>
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;