Bug 36207: (RM follow-up) CSRF correction
[koha.git] / C4 / Log.pm
1 package C4::Log;
2
3 #package to deal with Logging Actions in DB
4
5 # Copyright 2000-2002 Katipo Communications
6 # Copyright 2011 MJ Ray and software.coop
7 #
8 # This file is part of Koha.
9 #
10 # Koha is free software; you can redistribute it and/or modify it
11 # under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # Koha is distributed in the hope that it will be useful, but
16 # WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License
21 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22
23 use strict;
24 use warnings;
25
26 use Data::Dumper   qw( Dumper );
27 use File::Basename qw( basename );
28 use JSON           qw( to_json encode_json );
29 use Scalar::Util   qw( blessed );
30 use Struct::Diff   qw( diff );
31
32 use C4::Context;
33 use Koha::Logger;
34 use Koha::ActionLogs;
35
36 use vars qw(@ISA @EXPORT);
37
38 BEGIN {
39     require Exporter;
40     @ISA    = qw(Exporter);
41     @EXPORT = qw(logaction cronlogaction);
42 }
43
44 =head1 NAME
45
46 C4::Log - Koha Log Facility functions
47
48 =head1 SYNOPSIS
49
50   use C4::Log;
51
52 =head1 DESCRIPTION
53
54 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.
55
56 =head1 FUNCTIONS
57
58 =over 2
59
60 =item logaction
61
62   &logaction($modulename, $actionname, $objectnumber, $infos, $interface, $original_as_hashref_or_object);
63
64 Adds a record into action_logs table to report the different changes upon the database.
65 Each log entry includes the number of the user currently logged in.  For batch
66 jobs, which operate without authenticating a user and setting up a session, the user
67 number is set to 0, which is the same as the superlibrarian's number.
68
69 =cut
70
71 #'
72 sub logaction {
73     my ( $modulename, $actionname, $objectnumber, $infos, $interface, $original ) = @_;
74
75     my $updated;
76
77     # Get ID of logged in user.  if called from a batch job,
78     # no user session exists and C4::Context->userenv() returns
79     # the scalar '0'.
80     my $userenv    = C4::Context->userenv();
81     my $usernumber = ( ref($userenv) eq 'HASH' ) ? $userenv->{'number'} : 0;
82     $usernumber ||= 0;
83     $interface //= C4::Context->interface;
84
85     if ( blessed($infos) && $infos->isa('Koha::Object') ) {
86         $infos   = $infos->get_from_storage if $infos->in_storage;
87         $updated = $infos->unblessed;
88         local $Data::Dumper::Sortkeys = 1;
89
90         if ( $infos->isa('Koha::Item') && $modulename eq 'CATALOGUING' && $actionname eq 'MODIFY' ) {
91             $infos = "item " . Dumper($updated);
92         } else {
93             $infos = Dumper($updated);
94         }
95     } else {
96         $updated = $infos;
97     }
98
99     my $script =
100         ( $interface eq 'cron' or $interface eq 'commandline' )
101         ? basename($0)
102         : undef;
103
104     my @trace;
105     my $depth = C4::Context->preference('ActionLogsTraceDepth') || 0;
106     for ( my $i = 0 ; $i < $depth ; $i++ ) {
107         my ( $package, $filename, $line, $subroutine ) = caller($i);
108         last unless defined $line;
109         push(
110             @trace,
111             {
112                 package    => $package,
113                 filename   => $filename,
114                 line       => $line,
115                 subroutine => $subroutine,
116             }
117         );
118     }
119     my $trace = @trace ? to_json( \@trace, { utf8 => 1, pretty => 0 } ) : undef;
120
121     my $diff = undef;
122     $diff = encode_json( diff( $original->unblessed, $updated, noU => 1 ) )
123         if blessed($original) && $original->isa('Koha::Object');
124     $diff //= encode_json( diff( $original, $updated, noU => 1 ) )
125         if $original && ref $updated eq 'HASH';
126
127     Koha::ActionLog->new(
128         {
129             timestamp => \'NOW()',
130             user      => $usernumber,
131             module    => $modulename,
132             action    => $actionname,
133             object    => $objectnumber,
134             info      => $infos,
135             interface => $interface,
136             script    => $script,
137             trace     => $trace,
138             diff      => $diff,
139         }
140     )->store();
141
142     my $logger = Koha::Logger->get(
143         {
144             interface => $interface,
145             category  => "ActionLogs.$modulename.$actionname"
146         }
147     );
148     $logger->debug(
149         sub {
150             "ACTION LOG: " . encode_json(
151                 {
152                     user   => $usernumber,
153                     module => $modulename,
154                     action => $actionname,
155                     object => $objectnumber,
156                     info   => $infos
157                 }
158             );
159         }
160     );
161 }
162
163 =item cronlogaction
164
165   &cronlogaction($infos);
166
167 Convenience routine to add a record into action_logs table from a cron job.
168 Logs the path and name of the calling script plus the information privided by param $infos.
169
170 =cut
171
172 #'
173 sub cronlogaction {
174     my $params = shift;
175     my $info   = $params->{info};
176     my $action = $params->{action};
177     $action ||= "Run";
178     my $loginfo = ( caller(0) )[1];
179     $loginfo .= ' ' . $info                        if $info;
180     logaction( 'CRONJOBS', $action, $$, $loginfo ) if C4::Context->preference('CronjobLog');
181 }
182
183 1;
184 __END__
185
186 =back
187
188 =head1 AUTHOR
189
190 Koha Development Team <http://koha-community.org/>
191
192 =cut