Bug 29915: Tiny session adjustments
[koha.git] / C4 / Search / History.pm
1 package C4::Search::History;
2
3 use Modern::Perl;
4
5 use C4::Auth qw( get_session );
6 use C4::Context;
7 use Koha::DateUtils qw( dt_from_string output_pref );
8
9 use JSON qw( decode_json encode_json );
10 use URI::Escape qw( uri_escape uri_unescape );
11
12 sub add {
13     my ($params)   = @_;
14     my $userid     = $params->{userid};
15     my $sessionid  = $params->{sessionid};
16     my $query_desc = $params->{query_desc};
17     my $query_cgi  = $params->{query_cgi};
18     my $total      = $params->{total} // 0;
19     my $type       = $params->{type} || 'biblio';
20     my $time       = $params->{time};
21
22     my $dbh = C4::Context->dbh;
23
24     # Add the request the user just made
25     my $query = q{
26         INSERT INTO search_history(
27             userid, sessionid, query_desc, query_cgi, type, total} . ( $time ? ', time' : '' ) . q{
28         ) VALUES(
29             ?, ?, ?, ?, ?, ?} . ( $time ? ', ?' : '' ) . q{
30         )
31     };
32     my $sth = $dbh->prepare($query);
33     $sth->execute( $userid, $sessionid, $query_desc, $query_cgi, $type,
34         $total, ( $time ? $time : () ) );
35 }
36
37 sub add_to_session {
38     my ($params) = @_;
39     my $cgi = $params->{cgi};
40     my $query_desc = $params->{query_desc} || "unknown";
41     my $query_cgi  = $params->{query_cgi} || "unknown";
42     my $total      = $params->{total};
43     my $type       = $params->{type}                              || 'biblio';
44
45     # To a cookie (the user is not logged in)
46     my $now = dt_from_string;
47     my $id = $now->year . $now->month . $now->day . $now->hour . $now->minute . $now->second . int(rand(100));
48     my @recent_searches = get_from_session( { cgi => $cgi } );
49     push @recent_searches, {
50         query_desc => $query_desc,
51         query_cgi  => $query_cgi,
52         total      => "$total",
53         type       => $type,
54         time       => output_pref( { dt => $now, dateformat => 'iso', timeformat => '24hr' } ),
55         id         => $id,
56     };
57
58     shift @recent_searches if ( @recent_searches > 15 );
59     set_to_session( { cgi => $cgi, search_history => \@recent_searches } );
60 }
61
62 sub delete {
63     my ($params)  = @_;
64     my $id        = $params->{id};
65     my $userid    = $params->{userid};
66     my $sessionid = $params->{sessionid};
67     my $type      = $params->{type}     || q{};
68     my $previous  = $params->{previous} || 0;
69     my $interval  = $params->{interval} || 0;
70
71     unless ( ref( $id ) ) {
72         $id = $id ? [ $id ] : [];
73     }
74
75     unless ( $userid or @$id or $interval ) {
76         warn "ERROR: userid, id or interval is required for history deletion";
77         return;
78     }
79
80     my $dbh   = C4::Context->dbh;
81     my $query = q{
82         DELETE FROM search_history
83         WHERE 1
84     };
85
86     $query .= q{ AND id IN ( } . join( q{,}, (q{?}) x @$id ) . q{ )}
87         if @$id;
88
89     $query .= q{
90         AND userid = ?
91     } if $userid;
92
93     if ($sessionid) {
94         $query .=
95           $previous
96           ? q{ AND sessionid != ?}
97           : q{ AND sessionid = ?};
98     }
99
100     $query .= q{ AND type = ?}
101       if $type;
102
103     # FIXME DATE_SUB is a Mysql-ism. Postgres uses: datefield - INTERVAL '6 months'
104     $query .= q{ AND time < DATE_SUB( NOW(), INTERVAL ? DAY )}
105         if $interval;
106
107     $dbh->do(
108         $query, {},
109         ( @$id ? ( @$id ) : () ),
110         ( $userid ? $userid : () ),
111         ( $sessionid ? $sessionid : () ),
112         ( $type      ? $type      : () ),
113         ( $interval  ? $interval  : () ),
114     );
115 }
116
117 sub delete_from_cookie {
118     my ($params) = @_;
119     my $cookie   = $params->{cookie};
120     my $id       = $params->{id};
121
122     return unless $cookie;
123
124     unless ( ref( $id ) ) {
125         $id = $id ? [ $id ] : [];
126     }
127     return unless @$id;
128
129     my @searches;
130     if ( $cookie ){
131         $cookie = uri_unescape( $cookie );
132         if (decode_json( $cookie )) {
133             @searches = @{decode_json( $cookie )}
134         }
135     }
136
137     @searches = map {
138         my $search = $_;
139         ( grep { $_ != $search->{id} } @$id ) ? $search : ()
140     } @searches;
141
142     return uri_escape( encode_json( \@searches ) );
143
144 }
145
146 sub get {
147     my ($params)  = @_;
148     my $id        = $params->{id};
149     my $userid    = $params->{userid};
150     my $sessionid = $params->{sessionid};
151     my $type      = $params->{type};
152     my $previous  = $params->{previous};
153
154     unless ( ref( $id ) ) {
155         $id = $id ? [ $id ] : [];
156     }
157
158     unless ( $userid or @$id ) {
159         warn "ERROR: userid is required for history search";
160         return;
161     }
162
163     my $query = q{
164         SELECT *
165         FROM search_history
166         WHERE 1
167     };
168
169     $query .= q{ AND id IN ( } . join( q{,}, (q{?}) x @$id ) . q{ )}
170         if @$id;
171
172     $query .= q{
173         AND userid = ?
174     } if $userid;
175
176     if ($sessionid) {
177         $query .=
178           $previous
179           ? q{ AND sessionid != ?}
180           : q{ AND sessionid = ?};
181     }
182
183     $query .= q{ AND type = ?}
184       if $type;
185
186     my $dbh = C4::Context->dbh;
187     my $sth = $dbh->prepare($query);
188     $sth->execute(
189         ( @$id ? ( @$id ) : () ),
190         ( $userid ? $userid : () ),
191         ( $sessionid ? $sessionid : () ),
192         ( $type      ? $type      : () )
193     );
194     return $sth->fetchall_arrayref( {} );
195 }
196
197 sub get_from_session {
198     my ($params)  = @_;
199     my $cgi       = $params->{cgi};
200     my $sessionID = $cgi->cookie('CGISESSID');
201     return () unless $sessionID;
202     my $session = C4::Auth::get_session($sessionID);
203     return () unless $session and $session->param('search_history');
204     my $obj =
205       eval { decode_json( uri_unescape( $session->param('search_history') ) ) };
206     return () unless defined $obj;
207     return () unless ref $obj eq 'ARRAY';
208     return @{$obj};
209 }
210
211 sub set_to_session {
212     my ($params)       = @_;
213     my $cgi            = $params->{cgi};
214     my $search_history = $params->{search_history};
215     my $sessionID      = $cgi->cookie('CGISESSID');
216     return () unless $sessionID;
217     my $session = C4::Auth::get_session($sessionID);
218     return () unless $session;
219     $session->param( 'search_history',
220         uri_escape( encode_json($search_history) ) );
221     $session->flush;
222 }
223
224 1;
225
226 __END__
227
228 =pod
229
230 =head1 NAME
231
232 C4::Search::History - Manage search history
233
234 =head1 DESCRIPTION
235
236 This module provides some routines for the search history management.
237 It deals with session or database.
238
239 =head1 ROUTINES
240
241 =head2 add
242
243     C4::Search::History::add({
244         userid => $userid,
245         sessionid => $cgi->cookie("CGIESSID"),
246         query_desc => $query_desc,
247         query_cgi => $query_cgi,
248         total => $total,
249         type => $type,
250     });
251
252 type is "biblio" or "authority".
253
254 Add a new search to the user's history.
255
256 =head2 add_to_session
257
258     my $value = C4::Search::History::add_to_session({
259         cgi => $cgi,
260         query_desc => $query_desc,
261         query_cgi => $query_cgi,
262         total => $total,
263         type => $type,
264     });
265
266 Add a search to the session. The number of searches to keep is hardcoded to 15.
267
268 =head2 delete
269
270     C4::Search::History::delete({
271         userid => $loggedinuser,
272         sessionid => $sessionid,
273         type => $type,
274         previous => $previous
275     });
276
277 Delete searches in the database.
278 If the sessionid is missing all searches for all sessions will be deleted.
279 It is possible to delete searches for current session or all previous sessions using the previous flag.
280 If the type ("biblio" or "authority") is missing, all type will be deleted.
281 To delete *all* searches for a given userid, just pass a userid.
282
283 =head2 get
284
285     my $searches C4::Search::History::get({
286         userid => $userid,
287         sessionsid => $sessionid,
288         type => $type,
289         previous => $previous
290     });
291
292 Return a list of searches for a given userid.
293 If a sessionid is given, searches are limited to the matching session.
294 type and previous follow the same behavior as the delete routine.
295
296 =head2 get_from_session
297
298     my $searches = C4::Search::History::get_from_session({
299         cgi => $cgi
300     });
301
302 Return all searches present for the given session.
303
304 =head2 set_to_session
305
306     C4::Search::History::set_to_session({
307         cgi => $cgi,
308         search_history => $search_history
309     });
310
311 Store searches into the session.
312
313 =head1 AUTHORS
314
315 Jonathan Druart <jonathan.druart@biblibre.com>
316
317 =head1 LICENSE
318
319 This file is part of Koha.
320
321 Copyright 2013 BibLibre SARL
322
323 Koha is free software; you can redistribute it and/or modify it
324 under the terms of the GNU General Public License as published by
325 the Free Software Foundation; either version 3 of the License, or
326 (at your option) any later version.
327
328 Koha is distributed in the hope that it will be useful, but
329 WITHOUT ANY WARRANTY; without even the implied warranty of
330 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
331 GNU General Public License for more details.
332
333 You should have received a copy of the GNU General Public License
334 along with Koha; if not, see <http://www.gnu.org/licenses>.