Bug 17600: Standardize our EXPORT_OK
[srvgit] / 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 }
222
223 1;
224
225 __END__
226
227 =pod
228
229 =head1 NAME
230
231 C4::Search::History - Manage search history
232
233 =head1 DESCRIPTION
234
235 This module provides some routines for the search history management.
236 It deals with session or database.
237
238 =head1 ROUTINES
239
240 =head2 add
241
242     C4::Search::History::add({
243         userid => $userid,
244         sessionid => $cgi->cookie("CGIESSID"),
245         query_desc => $query_desc,
246         query_cgi => $query_cgi,
247         total => $total,
248         type => $type,
249     });
250
251 type is "biblio" or "authority".
252
253 Add a new search to the user's history.
254
255 =head2 add_to_session
256
257     my $value = C4::Search::History::add_to_session({
258         cgi => $cgi,
259         query_desc => $query_desc,
260         query_cgi => $query_cgi,
261         total => $total,
262         type => $type,
263     });
264
265 Add a search to the session. The number of searches to keep is hardcoded to 15.
266
267 =head2 delete
268
269     C4::Search::History::delete({
270         userid => $loggedinuser,
271         sessionid => $sessionid,
272         type => $type,
273         previous => $previous
274     });
275
276 Delete searches in the database.
277 If the sessionid is missing all searches for all sessions will be deleted.
278 It is possible to delete searches for current session or all previous sessions using the previous flag.
279 If the type ("biblio" or "authority") is missing, all type will be deleted.
280 To delete *all* searches for a given userid, just pass a userid.
281
282 =head2 get
283
284     my $searches C4::Search::History::get({
285         userid => $userid,
286         sessionsid => $sessionid,
287         type => $type,
288         previous => $previous
289     });
290
291 Return a list of searches for a given userid.
292 If a sessionid is given, searches are limited to the matching session.
293 type and previous follow the same behavior as the delete routine.
294
295 =head2 get_from_session
296
297     my $searches = C4::Search::History::get_from_session({
298         cgi => $cgi
299     });
300
301 Return all searches present for the given session.
302
303 =head2 set_to_session
304
305     C4::Search::History::set_to_session({
306         cgi => $cgi,
307         search_history => $search_history
308     });
309
310 Store searches into the session.
311
312 =head1 AUTHORS
313
314 Jonathan Druart <jonathan.druart@biblibre.com>
315
316 =head1 LICENSE
317
318 This file is part of Koha.
319
320 Copyright 2013 BibLibre SARL
321
322 Koha is free software; you can redistribute it and/or modify it
323 under the terms of the GNU General Public License as published by
324 the Free Software Foundation; either version 3 of the License, or
325 (at your option) any later version.
326
327 Koha is distributed in the hope that it will be useful, but
328 WITHOUT ANY WARRANTY; without even the implied warranty of
329 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
330 GNU General Public License for more details.
331
332 You should have received a copy of the GNU General Public License
333 along with Koha; if not, see <http://www.gnu.org/licenses>.