Bug 14695 - Tidy C4::Reserves::CanItemBeReserved
[koha.git] / C4 / Log.pm
1 package C4::Log;
2
3 #package to deal with Logging Actions in DB
4
5
6 # Copyright 2000-2002 Katipo Communications
7 # Copyright 2011 MJ Ray and software.coop
8 #
9 # This file is part of Koha.
10 #
11 # Koha is free software; you can redistribute it and/or modify it
12 # under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
15 #
16 # Koha is distributed in the hope that it will be useful, but
17 # WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with Koha; if not, see <http://www.gnu.org/licenses>.
23
24 use strict;
25 use warnings;
26
27 use JSON qw( to_json );
28
29 use C4::Context;
30 use Koha::DateUtils;
31 use Koha::Logger;
32
33 use vars qw(@ISA @EXPORT);
34
35 BEGIN {
36         require Exporter;
37         @ISA = qw(Exporter);
38         @EXPORT = qw(&logaction &cronlogaction &GetLogStatus &displaylog &GetLogs);
39 }
40
41 =head1 NAME
42
43 C4::Log - Koha Log Facility functions
44
45 =head1 SYNOPSIS
46
47   use C4::Log;
48
49 =head1 DESCRIPTION
50
51 The functions in this module perform various functions in order to log all the operations done on the Database, including deleting and undeleting books, adding/editing members, etc.
52
53 =head1 FUNCTIONS
54
55 =over 2
56
57 =item logaction
58
59   &logaction($modulename, $actionname, $objectnumber, $infos);
60
61 Adds a record into action_logs table to report the different changes upon the database.
62 Each log entry includes the number of the user currently logged in.  For batch
63 jobs, which operate without authenticating a user and setting up a session, the user
64 number is set to 0, which is the same as the superlibrarian's number.
65
66 =cut
67
68 #'
69 sub logaction {
70     my ($modulename, $actionname, $objectnumber, $infos, $interface)=@_;
71
72     # Get ID of logged in user.  if called from a batch job,
73     # no user session exists and C4::Context->userenv() returns
74     # the scalar '0'.
75     my $userenv = C4::Context->userenv();
76     my $usernumber = (ref($userenv) eq 'HASH') ? $userenv->{'number'} : 0;
77     $usernumber ||= 0;
78     $interface //= C4::Context->interface;
79
80     my $dbh = C4::Context->dbh;
81     my $sth=$dbh->prepare("Insert into action_logs (timestamp,user,module,action,object,info,interface) values (now(),?,?,?,?,?,?)");
82     $sth->execute($usernumber,$modulename,$actionname,$objectnumber,$infos,$interface);
83     $sth->finish;
84
85     my $logger = Koha::Logger->get(
86         {
87             interface => 'intranet',
88             category  => "ActionLogs.$modulename.$actionname"
89         }
90     );
91     $logger->debug(
92         sub {
93             "ACTION LOG: " . to_json(
94                 {
95                     user   => $usernumber,
96                     module => $modulename,
97                     action => $actionname,
98                     object => $objectnumber,
99                     info   => $infos
100                 }
101             );
102         }
103     );
104 }
105
106 =item cronlogaction
107
108   &cronlogaction($infos);
109
110 Convenience routine to add a record into action_logs table from a cron job.
111 Logs the path and name of the calling script plus the information privided by param $infos.
112
113 =cut
114
115 #'
116 sub cronlogaction {
117     my ($infos)=@_;
118     my $loginfo = (caller(0))[1];
119     $loginfo .= ' ' . $infos if $infos;
120     logaction( 'CRONJOBS', 'Run', undef, $loginfo ) if C4::Context->preference('CronjobLog');
121 }
122
123
124 =item GetLogStatus
125
126   $status = GetLogStatus;
127
128 C<$status> is a hasref like this example:
129     $hash = {
130         BorrowersLog   => 1,
131         CataloguingLog => 0,
132         IssueLog       => 0,
133         ...
134     }
135
136 =cut
137
138 #'
139 sub GetLogStatus {
140     my %hash;
141     $hash{BorrowersLog}    = C4::Context->preference("BorrowersLog");
142     $hash{CataloguingLog}  = C4::Context->preference("CataloguingLog");
143     $hash{HoldsLog}        = C4::Context->preference("HoldsLog");
144     $hash{IssueLog}        = C4::Context->preference("IssueLog");
145     $hash{ReturnLog}       = C4::Context->preference("ReturnLog");
146     $hash{SubscriptionLog} = C4::Context->preference("SubscriptionLog");
147     $hash{LetterLog}       = C4::Context->preference("LetterLog");
148     $hash{FinesLog}        = C4::Context->preference("FinesLog");
149     return \%hash;
150 }
151
152 =item displaylog
153
154   &displaylog($modulename, @filters);
155   $modulename is the name of the module on which the user wants to display logs
156   @filters is an optional table of hash containing :
157       - name : the name of the variable to filter
158     - value : the value of the filter.... May be with * joker
159
160 returns a table of hash containing who did what on which object at what time
161
162 =cut
163
164 #'
165 sub displaylog {
166   my ($modulename, @filters) = @_;
167     my $dbh = C4::Context->dbh;
168     my $strsth=qq|
169                 SELECT action_logs.timestamp, action_logs.action, action_logs.info,
170                                 borrowers.cardnumber, borrowers.surname, borrowers.firstname, borrowers.userid,
171                         biblio.biblionumber, biblio.title, biblio.author
172         FROM action_logs
173                 LEFT JOIN borrowers ON borrowers.borrowernumber=action_logs.user
174         LEFT JOIN  biblio   ON action_logs.object=biblio.biblionumber
175         WHERE action_logs.module = 'cataloguing'
176         |;
177         my %filtermap = ();
178     if ($modulename eq "catalogue" or $modulename eq "acqui") {
179                 %filtermap = (
180                           user => 'borrowers.surname',
181                          title => 'biblio.title',
182                         author => 'biblio.author',
183                 );
184     } elsif ($modulename eq "members") {
185         $strsth=qq|
186                 SELECT action_logs.timestamp, action_logs.action, action_logs.info,
187                         borrowers.cardnumber, borrowers.surname, borrowers.firstname, borrowers.userid,
188                         bor2.cardnumber, bor2.surname, bor2.firstname, bor2.userid
189         FROM action_logs
190                 LEFT JOIN borrowers ON borrowers.borrowernumber=action_logs.user
191                 LEFT JOIN borrowers as bor2 ON action_logs.object=bor2.borrowernumber
192         WHERE action_logs.module = 'members'
193                 |;
194                 %filtermap = (
195                        user => 'borrowers.surname',
196                     surname => 'bor2.surname',
197                   firstname => 'bor2.firstname',
198                  cardnumber => 'bor2.cardnumber',
199                 );
200     } else {
201                 return 0;
202         }
203
204     if (@filters) {
205                 foreach my $filter (@filters) {
206                         my $tempname = $filter->{name}         or next;
207                         (grep {/^$tempname$/} keys %filtermap) or next;
208                         $filter->{value} =~ s/\*/%/g;
209                         $strsth .= " AND " . $filtermap{$tempname} . " LIKE " . $filter->{value};
210                 }
211         }
212     my $sth=$dbh->prepare($strsth);
213     $sth->execute;
214     my @results;
215     my $count;
216     my $hilighted=1;
217     while (my $data = $sth->fetchrow_hashref){
218         $data->{hilighted} = ($hilighted>0);
219         $data->{info} =~ s/\n/<br\/>/g;
220         $data->{day} = output_pref({ str => $data->{timestamp} });
221         push @results, $data;
222         $count++;
223         $hilighted = -$hilighted;
224     }
225     return ($count, \@results);
226 }
227
228 =item GetLogs
229
230 $logs = GetLogs($datefrom,$dateto,$user,\@modules,$action,$object,$info);
231
232 Return:
233 C<$logs> is a ref to a hash which containts all columns from action_logs
234
235 =cut
236
237 sub GetLogs {
238     my $datefrom = shift;
239     my $dateto   = shift;
240     my $user     = shift;
241     my $modules  = shift;
242     my $action   = shift;
243     my $object   = shift;
244     my $info     = shift;
245     my $interfaces = shift;
246
247     my $iso_datefrom = $datefrom ? output_pref({ dt => dt_from_string( $datefrom ), dateformat => 'iso', dateonly => 1 }) : undef;
248     my $iso_dateto = $dateto ? output_pref({ dt => dt_from_string( $dateto ), dateformat => 'iso', dateonly => 1 }) : undef;
249
250     $user ||= q{};
251
252     my $dbh   = C4::Context->dbh;
253     my $query = "
254         SELECT *
255         FROM   action_logs
256         WHERE 1
257     ";
258
259     my @parameters;
260     $query .=
261       " AND DATE_FORMAT(timestamp, '%Y-%m-%d') >= \"" . $iso_datefrom . "\" "
262       if $iso_datefrom;    #fix me - mysql specific
263     $query .=
264       " AND DATE_FORMAT(timestamp, '%Y-%m-%d') <= \"" . $iso_dateto . "\" "
265       if $iso_dateto;
266     if ( $user ne q{} ) {
267         $query .= " AND user = ? ";
268         push( @parameters, $user );
269     }
270     if ( $modules && scalar(@$modules) ) {
271         $query .=
272           " AND module IN (" . join( ",", map { "?" } @$modules ) . ") ";
273         push( @parameters, @$modules );
274     }
275     if ( $action && scalar(@$action) ) {
276         $query .= " AND action IN (" . join( ",", map { "?" } @$action ) . ") ";
277         push( @parameters, @$action );
278     }
279     if ($object) {
280         $query .= " AND object = ? ";
281         push( @parameters, $object );
282     }
283     if ($info) {
284         $query .= " AND info LIKE ? ";
285         push( @parameters, "%" . $info . "%" );
286     }
287     if ( $interfaces && scalar(@$interfaces) ) {
288         $query .=
289           " AND interface IN (" . join( ",", map { "?" } @$interfaces ) . ") ";
290         push( @parameters, @$interfaces );
291     }
292
293     my $sth = $dbh->prepare($query);
294     $sth->execute(@parameters);
295
296     my @logs;
297     while ( my $row = $sth->fetchrow_hashref ) {
298         push @logs, $row;
299     }
300     return \@logs;
301 }
302
303 1;
304 __END__
305
306 =back
307
308 =head1 AUTHOR
309
310 Koha Development Team <http://koha-community.org/>
311
312 =cut