Bug 14100: (follow-up) Language overlay for item types
[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;
8
9 use JSON qw( encode_json decode_json );
10 use URI::Escape;
11 use Encode;
12
13 sub add {
14     my ($params)   = @_;
15     my $userid     = $params->{userid};
16     my $sessionid  = $params->{sessionid};
17     my $query_desc = $params->{query_desc};
18     my $query_cgi  = $params->{query_cgi};
19     my $total      = $params->{total} // 0;
20     my $type       = $params->{type} || 'biblio';
21     my $time       = $params->{time};
22
23     my $dbh = C4::Context->dbh;
24
25     # Add the request the user just made
26     my $query = q{
27         INSERT INTO search_history(
28             userid, sessionid, query_desc, query_cgi, type, total, time
29         ) VALUES(
30             ?, ?, ?, ?, ?, ?, ?
31         )
32     };
33     my $sth = $dbh->prepare($query);
34     $sth->execute( $userid, $sessionid, $query_desc, $query_cgi, $type,
35         $total, $time );
36 }
37
38 sub add_to_session {
39     my ($params) = @_;
40     my $cgi = $params->{cgi};
41     my $query_desc = $params->{query_desc} || "unknown";
42     my $query_cgi  = $params->{query_cgi} || "unknown";
43     my $total      = $params->{total};
44     my $type       = $params->{type}                              || 'biblio';
45
46     # To a cookie (the user is not logged in)
47     my $now = dt_from_string;
48     my $id = $now->year . $now->month . $now->day . $now->hour . $now->minute . $now->second . int(rand(100));
49     my @recent_searches = get_from_session( { cgi => $cgi } );
50     push @recent_searches, {
51         query_desc => $query_desc,
52         query_cgi  => $query_cgi,
53         total      => "$total",
54         type       => $type,
55         time       => output_pref( { dt => $now, dateformat => 'iso', timeformat => '24hr' } ),
56         id         => $id,
57     };
58
59     shift @recent_searches if ( @recent_searches > 15 );
60     set_to_session( { cgi => $cgi, search_history => \@recent_searches } );
61 }
62
63 sub delete {
64     my ($params)  = @_;
65     my $id        = $params->{id};
66     my $userid    = $params->{userid};
67     my $sessionid = $params->{sessionid};
68     my $type      = $params->{type}     || q{};
69     my $previous  = $params->{previous} || 0;
70     my $interval  = $params->{interval} || 0;
71
72     unless ( ref( $id ) ) {
73         $id = $id ? [ $id ] : [];
74     }
75
76     unless ( $userid or @$id or $interval ) {
77         warn "ERROR: userid, id or interval is required for history deletion";
78         return;
79     }
80
81     my $dbh   = C4::Context->dbh;
82     my $query = q{
83         DELETE FROM search_history
84         WHERE 1
85     };
86
87     $query .= q{ AND id IN ( } . join( q{,}, (q{?}) x @$id ) . q{ )}
88         if @$id;
89
90     $query .= q{
91         AND userid = ?
92     } if $userid;
93
94     if ($sessionid) {
95         $query .=
96           $previous
97           ? q{ AND sessionid != ?}
98           : q{ AND sessionid = ?};
99     }
100
101     $query .= q{ AND type = ?}
102       if $type;
103
104     # FIXME DATE_SUB is a Mysql-ism. Postgres uses: datefield - INTERVAL '6 months'
105     $query .= q{ AND time < DATE_SUB( NOW(), INTERVAL ? DAY )}
106         if $interval;
107
108     $dbh->do(
109         $query, {},
110         ( @$id ? ( @$id ) : () ),
111         ( $userid ? $userid : () ),
112         ( $sessionid ? $sessionid : () ),
113         ( $type      ? $type      : () ),
114         ( $interval  ? $interval  : () ),
115     );
116 }
117
118 sub delete_from_cookie {
119     my ($params) = @_;
120     my $cookie   = $params->{cookie};
121     my $id       = $params->{id};
122
123     return unless $cookie;
124
125     unless ( ref( $id ) ) {
126         $id = $id ? [ $id ] : [];
127     }
128     return unless @$id;
129
130     my @searches;
131     if ( $cookie ){
132         $cookie = uri_unescape( $cookie );
133         if (decode_json( $cookie )) {
134             @searches = @{decode_json( $cookie )}
135         }
136     }
137
138     @searches = map {
139         my $search = $_;
140         ( grep { $_ != $search->{id} } @$id ) ? $search : ()
141     } @searches;
142
143     return uri_escape( encode_json( \@searches ) );
144
145 }
146
147 sub get {
148     my ($params)  = @_;
149     my $id        = $params->{id};
150     my $userid    = $params->{userid};
151     my $sessionid = $params->{sessionid};
152     my $type      = $params->{type};
153     my $previous  = $params->{previous};
154
155     unless ( ref( $id ) ) {
156         $id = $id ? [ $id ] : [];
157     }
158
159     unless ( $userid or @$id ) {
160         warn "ERROR: userid is required for history search";
161         return;
162     }
163
164     my $query = q{
165         SELECT *
166         FROM search_history
167         WHERE 1
168     };
169
170     $query .= q{ AND id IN ( } . join( q{,}, (q{?}) x @$id ) . q{ )}
171         if @$id;
172
173     $query .= q{
174         AND userid = ?
175     } if $userid;
176
177     if ($sessionid) {
178         $query .=
179           $previous
180           ? q{ AND sessionid != ?}
181           : q{ AND sessionid = ?};
182     }
183
184     $query .= q{ AND type = ?}
185       if $type;
186
187     my $dbh = C4::Context->dbh;
188     my $sth = $dbh->prepare($query);
189     $sth->execute(
190         ( @$id ? ( @$id ) : () ),
191         ( $userid ? $userid : () ),
192         ( $sessionid ? $sessionid : () ),
193         ( $type      ? $type      : () )
194     );
195     return $sth->fetchall_arrayref( {} );
196 }
197
198 sub get_from_session {
199     my ($params)  = @_;
200     my $cgi       = $params->{cgi};
201     my $sessionID = $cgi->cookie('CGISESSID');
202     return () unless $sessionID;
203     my $session = C4::Auth::get_session($sessionID);
204     return () unless $session and $session->param('search_history');
205     my $obj =
206       eval { decode_json( uri_unescape( $session->param('search_history') ) ) };
207     return () unless defined $obj;
208     return () unless ref $obj eq 'ARRAY';
209     return @{$obj};
210 }
211
212 sub set_to_session {
213     my ($params)       = @_;
214     my $cgi            = $params->{cgi};
215     my $search_history = $params->{search_history};
216     my $sessionID      = $cgi->cookie('CGISESSID');
217     return () unless $sessionID;
218     my $session = C4::Auth::get_session($sessionID);
219     return () unless $session;
220     $session->param( 'search_history',
221         uri_escape( encode_json($search_history) ) );
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>.