| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# ************************************************************************* |
|
2
|
|
|
|
|
|
|
# Copyright (c) 2014-2015, SUSE LLC |
|
3
|
|
|
|
|
|
|
# |
|
4
|
|
|
|
|
|
|
# All rights reserved. |
|
5
|
|
|
|
|
|
|
# |
|
6
|
|
|
|
|
|
|
# Redistribution and use in source and binary forms, with or without |
|
7
|
|
|
|
|
|
|
# modification, are permitted provided that the following conditions are met: |
|
8
|
|
|
|
|
|
|
# |
|
9
|
|
|
|
|
|
|
# 1. Redistributions of source code must retain the above copyright notice, |
|
10
|
|
|
|
|
|
|
# this list of conditions and the following disclaimer. |
|
11
|
|
|
|
|
|
|
# |
|
12
|
|
|
|
|
|
|
# 2. Redistributions in binary form must reproduce the above copyright |
|
13
|
|
|
|
|
|
|
# notice, this list of conditions and the following disclaimer in the |
|
14
|
|
|
|
|
|
|
# documentation and/or other materials provided with the distribution. |
|
15
|
|
|
|
|
|
|
# |
|
16
|
|
|
|
|
|
|
# 3. Neither the name of SUSE LLC nor the names of its contributors may be |
|
17
|
|
|
|
|
|
|
# used to endorse or promote products derived from this software without |
|
18
|
|
|
|
|
|
|
# specific prior written permission. |
|
19
|
|
|
|
|
|
|
# |
|
20
|
|
|
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
21
|
|
|
|
|
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
22
|
|
|
|
|
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
23
|
|
|
|
|
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
24
|
|
|
|
|
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
25
|
|
|
|
|
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
26
|
|
|
|
|
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
27
|
|
|
|
|
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
28
|
|
|
|
|
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
29
|
|
|
|
|
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
30
|
|
|
|
|
|
|
# POSSIBILITY OF SUCH DAMAGE. |
|
31
|
|
|
|
|
|
|
# ************************************************************************* |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
use 5.012; |
|
35
|
42
|
|
|
42
|
|
171948
|
use strict; |
|
|
42
|
|
|
|
|
130
|
|
|
36
|
42
|
|
|
42
|
|
203
|
use warnings; |
|
|
42
|
|
|
|
|
69
|
|
|
|
42
|
|
|
|
|
808
|
|
|
37
|
42
|
|
|
42
|
|
184
|
|
|
|
42
|
|
|
|
|
117
|
|
|
|
42
|
|
|
|
|
1223
|
|
|
38
|
|
|
|
|
|
|
use App::CELL qw( $CELL $log $meta $core $site ); |
|
39
|
42
|
|
|
42
|
|
200
|
use App::Dochazka::REST::ConnBank qw( $dbix_conn conn_status ); |
|
|
42
|
|
|
|
|
72
|
|
|
|
42
|
|
|
|
|
4204
|
|
|
40
|
42
|
|
|
42
|
|
4906
|
use Data::Dumper; |
|
|
42
|
|
|
|
|
90
|
|
|
|
42
|
|
|
|
|
3417
|
|
|
41
|
42
|
|
|
42
|
|
255
|
use File::Path; |
|
|
42
|
|
|
|
|
91
|
|
|
|
42
|
|
|
|
|
1618
|
|
|
42
|
42
|
|
|
42
|
|
261
|
use File::ShareDir; |
|
|
42
|
|
|
|
|
76
|
|
|
|
42
|
|
|
|
|
1837
|
|
|
43
|
42
|
|
|
42
|
|
232
|
use File::Spec; |
|
|
42
|
|
|
|
|
86
|
|
|
|
42
|
|
|
|
|
1204
|
|
|
44
|
42
|
|
|
42
|
|
215
|
use Log::Any::Adapter; |
|
|
42
|
|
|
|
|
83
|
|
|
|
42
|
|
|
|
|
915
|
|
|
45
|
42
|
|
|
42
|
|
16352
|
use Params::Validate qw( :all ); |
|
|
42
|
|
|
|
|
12981
|
|
|
|
42
|
|
|
|
|
152
|
|
|
46
|
42
|
|
|
42
|
|
1213
|
use Try::Tiny; |
|
|
42
|
|
|
|
|
79
|
|
|
|
42
|
|
|
|
|
5046
|
|
|
47
|
42
|
|
|
42
|
|
269
|
use Web::Machine; |
|
|
42
|
|
|
|
|
80
|
|
|
|
42
|
|
|
|
|
1651
|
|
|
48
|
42
|
|
|
42
|
|
15956
|
use Web::MREST; |
|
|
42
|
|
|
|
|
3862074
|
|
|
|
42
|
|
|
|
|
1293
|
|
|
49
|
42
|
|
|
42
|
|
21256
|
use Web::MREST::CLI qw( normalize_filespec ); |
|
|
42
|
|
|
|
|
44862
|
|
|
|
42
|
|
|
|
|
1261
|
|
|
50
|
42
|
|
|
42
|
|
16386
|
|
|
|
42
|
|
|
|
|
4853363
|
|
|
|
42
|
|
|
|
|
4163
|
|
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=head1 NAME |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
App::Dochazka::REST - Dochazka REST server |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=head1 VERSION |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
Version 0.559 |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=cut |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
our $VERSION = '0.559'; |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head2 Development status |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Alpha. |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
Start the server with default settings: |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
$ dochazka-rest |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Point browser to: |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
http://localhost:5000/ |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
Use L<App::Dochazka::CLI> command-line interface to access full functionality: |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
$ dochazka-cli |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
This distribution, L<App::Dochazka::REST>, including all the modules in C<lib/>, |
|
93
|
|
|
|
|
|
|
the scripts in C<bin/>, and the configuration files in C<config/>, |
|
94
|
|
|
|
|
|
|
constitutes the REST server (API) component of Dochazka, the open-source |
|
95
|
|
|
|
|
|
|
Attendance/Time Tracking (ATT) system. |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
Dochazka as a whole aims to be a convenient, open-source ATT solution. |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=head1 ARCHITECTURE |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Dochazka consists of four main components: |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=over |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=item * Dochazka clients |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=item * REST server (this module) |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=item * PostgreSQL database |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=item * Data model |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=back |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
In a nutshell, clients attempt to translate user intent into REST API |
|
118
|
|
|
|
|
|
|
calls, which are transmitted over a network (using the HTTP protocol) to |
|
119
|
|
|
|
|
|
|
the server. The server processes incoming HTTP requests. Requests for |
|
120
|
|
|
|
|
|
|
valid REST resources are passed to the API for processing and errors are |
|
121
|
|
|
|
|
|
|
generated for invalid requests. The result is returned to the client in |
|
122
|
|
|
|
|
|
|
an HTTP response. The REST API uses the PostgreSQL server to save state. |
|
123
|
|
|
|
|
|
|
The clients and the REST API use the data model to represent and manipulate |
|
124
|
|
|
|
|
|
|
objects. |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head1 DOCUMENTATION |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=over |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item * L<App::Dochazka::REST::Guide> |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
A detailed guide to the REST server. |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item * L<App::Dochazka::REST::Docs::Resources> |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Dochazka REST API documentation. |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=item * L<App::Dochazka::Common> |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
Dochazka data model and other bits used by all Dochazka components. |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=item * L<App::Dochazka::CLI> and L<App::Dochazka::CLI::Guide> |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
Reference Dochazka command-line client. |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=item * L<App::Dochazka::WWW> |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Reference Dochazka WWW client. |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=over |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head1 EXPORTS |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=cut |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
use Exporter qw( import ); |
|
162
|
42
|
|
|
42
|
|
435
|
our @EXPORT_OK = qw( init_arbitrary_script $faux_context ); |
|
|
42
|
|
|
|
|
162
|
|
|
|
42
|
|
|
|
|
72837
|
|
|
163
|
|
|
|
|
|
|
our $faux_context; |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head2 run_sql |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
Takes a L<DBIx::Connector> object and an array of SQL statements. Runs them |
|
173
|
|
|
|
|
|
|
one by one until an exception is thrown or the last statement completes |
|
174
|
|
|
|
|
|
|
successfully. Returns a status object which will be either OK or ERR. |
|
175
|
|
|
|
|
|
|
If NOT_OK, the error text will be in C<< $status->text >>. |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=cut |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
my ( $conn, @stmts ) = @_; |
|
180
|
|
|
|
|
|
|
my $status; |
|
181
|
0
|
|
|
0
|
1
|
0
|
try { |
|
182
|
0
|
|
|
|
|
0
|
foreach my $stmt ( @stmts ) { |
|
183
|
|
|
|
|
|
|
$log->debug( "Running SQL statement $stmt" ); |
|
184
|
0
|
|
|
0
|
|
0
|
$conn->run( fixup => sub { $_->do( $stmt ); } ); |
|
185
|
0
|
|
|
|
|
0
|
} |
|
186
|
0
|
|
|
|
|
0
|
} catch { |
|
|
0
|
|
|
|
|
0
|
|
|
187
|
|
|
|
|
|
|
$status = $CELL->status_err( 'DOCHAZKA_DBI_ERR', args => [ $_ ] ); |
|
188
|
|
|
|
|
|
|
}; |
|
189
|
0
|
|
|
0
|
|
0
|
return $status if $status; |
|
190
|
0
|
|
|
|
|
0
|
return $CELL->status_ok; |
|
191
|
0
|
0
|
|
|
|
0
|
} |
|
192
|
0
|
|
|
|
|
0
|
|
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
my ( $mode, $conn ) = @_; |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
my $sql; |
|
197
|
0
|
|
|
0
|
|
0
|
if ( $mode eq 'create' ) { |
|
198
|
|
|
|
|
|
|
$sql = $site->DBINIT_CREATE_AUDIT_TRIGGERS; |
|
199
|
0
|
|
|
|
|
0
|
} elsif ( $mode eq 'delete' ) { |
|
200
|
0
|
0
|
|
|
|
0
|
$sql = $site->DBINIT_DELETE_AUDIT_TRIGGERS; |
|
|
|
0
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
0
|
} else { |
|
202
|
|
|
|
|
|
|
die "AAADFDGGGGGGAAAAAAAHHH! " . __PACKAGE__ . "::_do_audit_triggers"; |
|
203
|
0
|
|
|
|
|
0
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
0
|
|
|
|
|
0
|
my @prepped_sql; |
|
206
|
|
|
|
|
|
|
foreach my $table ( @{ $site->DOCHAZKA_AUDIT_TABLES } ) { |
|
207
|
|
|
|
|
|
|
my $sql_copy = $sql; |
|
208
|
0
|
|
|
|
|
0
|
my $question_mark = quotemeta('?'); |
|
209
|
0
|
|
|
|
|
0
|
$log->debug( "Replacing question mark with $table" ); |
|
|
0
|
|
|
|
|
0
|
|
|
210
|
0
|
|
|
|
|
0
|
$sql_copy =~ s{$question_mark}{$table}; |
|
211
|
0
|
|
|
|
|
0
|
push( @prepped_sql, $sql_copy ); |
|
212
|
0
|
|
|
|
|
0
|
} |
|
213
|
0
|
|
|
|
|
0
|
my $status = run_sql( |
|
214
|
0
|
|
|
|
|
0
|
$conn, |
|
215
|
|
|
|
|
|
|
@prepped_sql, |
|
216
|
0
|
|
|
|
|
0
|
); |
|
217
|
|
|
|
|
|
|
return $status; |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
0
|
|
|
221
|
|
|
|
|
|
|
=head2 create_audit_triggers |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
Create the audit triggers. Wrapper for _do_audit_triggers |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=cut |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
my $conn = shift; |
|
228
|
|
|
|
|
|
|
return _do_audit_triggers( 'create', $conn ); |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
0
|
|
|
0
|
1
|
0
|
|
|
232
|
0
|
|
|
|
|
0
|
=head2 delete_audit_triggers |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
Delete the audit triggers. Wrapper for _do_audit_triggers |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=cut |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
my $conn = shift; |
|
239
|
|
|
|
|
|
|
return _do_audit_triggers( 'delete', $conn ); |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
|
|
243
|
0
|
|
|
0
|
1
|
0
|
=head2 reset_mason_dir |
|
244
|
0
|
|
|
|
|
0
|
|
|
245
|
|
|
|
|
|
|
Wipe out and re-create the Mason state directory. Returns status object. |
|
246
|
|
|
|
|
|
|
Upon success, level will be 'OK' and payload will contain the full path |
|
247
|
|
|
|
|
|
|
to the Mason component root. |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=cut |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
my $status; |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
$log->info( "Checking permissions of Mason directory (DOCHAZKA_STATE_DIR)" ); |
|
254
|
|
|
|
|
|
|
my $statedir = $site->DOCHAZKA_STATE_DIR; |
|
255
|
|
|
|
|
|
|
die "OUCH!!! DOCHAZKA_STATE_DIR site parameter not defined!" unless $statedir; |
|
256
|
|
|
|
|
|
|
die "OUCH!!! DOCHAZKA_STATE_DIR $statedir is not readable by me!" unless -r $statedir; |
|
257
|
39
|
|
|
39
|
1
|
85
|
die "OUCH!!! DOCHAZKA_STATE_DIR $statedir is not writable by me!" unless -w $statedir; |
|
258
|
|
|
|
|
|
|
die "OUCH!!! DOCHAZKA_STATE_DIR $statedir is not executable by me!" unless -x $statedir; |
|
259
|
39
|
|
|
|
|
231
|
my $masondir = File::Spec->catfile( $statedir, 'Mason' ); |
|
260
|
39
|
|
|
|
|
3026
|
$log->debug( "Mason directory is $masondir" ); |
|
261
|
39
|
50
|
|
|
|
984
|
rmtree( $masondir ); |
|
262
|
39
|
50
|
|
|
|
4632
|
mkpath( $masondir, 0, 0750 ); |
|
263
|
0
|
0
|
|
|
|
|
|
|
264
|
0
|
0
|
|
|
|
|
# re-create |
|
265
|
0
|
|
|
|
|
|
my $comp_root = File::Spec->catfile( $masondir, 'comp_root' ); |
|
266
|
0
|
|
|
|
|
|
mkpath( $comp_root, 0, 0750 ); |
|
267
|
0
|
|
|
|
|
|
my $data_dir = File::Spec->catfile( $masondir, 'data_dir' ); |
|
268
|
0
|
|
|
|
|
|
mkpath( $data_dir, 0, 0750 ); |
|
269
|
|
|
|
|
|
|
$status = App::Dochazka::REST::Mason::init_singleton( |
|
270
|
|
|
|
|
|
|
comp_root => $comp_root, |
|
271
|
0
|
|
|
|
|
|
data_dir => $data_dir |
|
272
|
0
|
|
|
|
|
|
); |
|
273
|
0
|
|
|
|
|
|
return $status unless $status->ok; |
|
274
|
0
|
|
|
|
|
|
$status->payload( $comp_root ); |
|
275
|
0
|
|
|
|
|
|
return $status; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
|
|
279
|
0
|
0
|
|
|
|
|
=head2 initialize_activities_table |
|
280
|
0
|
|
|
|
|
|
|
|
281
|
0
|
|
|
|
|
|
Create the activities defined in the site parameter |
|
282
|
|
|
|
|
|
|
DOCHAZKA_ACTIVITY_DEFINITIONS |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=cut |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
my $conn = shift; |
|
287
|
|
|
|
|
|
|
my $status = $CELL->status_ok; |
|
288
|
|
|
|
|
|
|
try { |
|
289
|
|
|
|
|
|
|
$conn->txn( fixup => sub { |
|
290
|
|
|
|
|
|
|
my $sth = $_->prepare( $site->SQL_ACTIVITY_INSERT ); |
|
291
|
|
|
|
|
|
|
foreach my $actdef ( @{ $site->DOCHAZKA_ACTIVITY_DEFINITIONS } ) { |
|
292
|
|
|
|
|
|
|
$sth->bind_param( 1, $actdef->{code} ); |
|
293
|
0
|
|
|
0
|
1
|
|
$sth->bind_param( 2, $actdef->{long_desc} ); |
|
294
|
0
|
|
|
|
|
|
$sth->bind_param( 3, 'dbinit' ); |
|
295
|
|
|
|
|
|
|
$sth->execute; |
|
296
|
|
|
|
|
|
|
} |
|
297
|
0
|
|
|
|
|
|
} ); |
|
298
|
0
|
|
|
|
|
|
} catch { |
|
|
0
|
|
|
|
|
|
|
|
299
|
0
|
|
|
|
|
|
$status = $CELL->status_err( 'DOCHAZKA_DBI_ERR', args => [ $_ ] ); |
|
300
|
0
|
|
|
|
|
|
}; |
|
301
|
0
|
|
|
|
|
|
return $status; |
|
302
|
0
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
0
|
|
|
0
|
|
|
|
|
305
|
|
|
|
|
|
|
=head2 reset_db |
|
306
|
0
|
|
|
0
|
|
|
|
|
307
|
0
|
|
|
|
|
|
Drop and re-create a Dochazka database. Takes superuser credentials as |
|
308
|
0
|
|
|
|
|
|
arguments. |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
Be very, _very_, _VERY_ careful with this function. |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=cut |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
my $status; |
|
316
|
|
|
|
|
|
|
my $dbname = $site->DOCHAZKA_DBNAME; |
|
317
|
|
|
|
|
|
|
my $dbuser = $site->DOCHAZKA_DBUSER; |
|
318
|
|
|
|
|
|
|
my $dbpass = $site->DOCHAZKA_DBPASS; |
|
319
|
|
|
|
|
|
|
$log->debug( "Entering " . __PACKAGE__ . "::reset_db to initialize database $dbname with credentials $dbuser / $dbpass" ); |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# PGTZ *must* be set |
|
322
|
|
|
|
|
|
|
$ENV{'PGTZ'} = $site->DOCHAZKA_TIMEZONE; |
|
323
|
0
|
|
|
0
|
1
|
|
|
|
324
|
0
|
|
|
|
|
|
# create: |
|
325
|
0
|
|
|
|
|
|
# - audit schema (see config/sql/audit_Config.pm) |
|
326
|
0
|
|
|
|
|
|
# - public schema (all application-specific tables, functions, triggers, etc.) |
|
327
|
0
|
|
|
|
|
|
# - the 'root' and 'demo' employees |
|
328
|
|
|
|
|
|
|
# - privhistory record for root |
|
329
|
|
|
|
|
|
|
print "Getting database connection..."; |
|
330
|
0
|
|
|
|
|
|
my $conn = App::Dochazka::REST::ConnBank::get_arbitrary_dbix_conn( |
|
331
|
|
|
|
|
|
|
$dbname, $dbuser, $dbpass |
|
332
|
|
|
|
|
|
|
); |
|
333
|
|
|
|
|
|
|
print "done\n"; |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
print "Initializing audit schema..."; |
|
336
|
|
|
|
|
|
|
$status = run_sql( |
|
337
|
0
|
|
|
|
|
|
$conn, |
|
338
|
0
|
|
|
|
|
|
@{ $site->DBINIT_AUDIT }, |
|
339
|
|
|
|
|
|
|
); |
|
340
|
|
|
|
|
|
|
if ( $status->not_ok ) { |
|
341
|
0
|
|
|
|
|
|
print Dumper( $status ), "\n"; |
|
342
|
|
|
|
|
|
|
return $status; |
|
343
|
0
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
print "done\n"; |
|
345
|
|
|
|
|
|
|
|
|
346
|
0
|
|
|
|
|
|
print "Initializing public schema..."; |
|
|
0
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
$status = run_sql( |
|
348
|
0
|
0
|
|
|
|
|
$conn, |
|
349
|
0
|
|
|
|
|
|
@{ $site->DBINIT_CREATE }, |
|
350
|
0
|
|
|
|
|
|
); |
|
351
|
|
|
|
|
|
|
if ( $status->not_ok ) { |
|
352
|
0
|
|
|
|
|
|
print Dumper( $status ), "\n"; |
|
353
|
|
|
|
|
|
|
return $status; |
|
354
|
0
|
|
|
|
|
|
} |
|
355
|
|
|
|
|
|
|
print "done\n"; |
|
356
|
|
|
|
|
|
|
|
|
357
|
0
|
|
|
|
|
|
# get EID of root employee that was just created, since |
|
|
0
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# we will need it in the second round of SQL statements |
|
359
|
0
|
0
|
|
|
|
|
my $eids = get_eid_of( $conn, "root", "demo" ); |
|
360
|
0
|
|
|
|
|
|
$site->set( 'DOCHAZKA_EID_OF_ROOT', $eids->{'root'} ); |
|
361
|
0
|
|
|
|
|
|
$site->set( 'DOCHAZKA_EID_OF_DEMO', $eids->{'demo'} ); |
|
362
|
|
|
|
|
|
|
|
|
363
|
0
|
|
|
|
|
|
# the second round of SQL statements to make root employee immutable |
|
364
|
|
|
|
|
|
|
# is taken from DBINIT_MAKE_ROOT_IMMUTABLE site param |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# prep DBINIT_MAKE_ROOT_IMMUTABLE |
|
367
|
0
|
|
|
|
|
|
# (replace ? with EID of root employee in all the statements |
|
368
|
0
|
|
|
|
|
|
# N.B.: we avoid the /r modifier here because we might be using Perl # 5.012) |
|
369
|
0
|
|
|
|
|
|
my @root_immutable_statements = map { |
|
370
|
|
|
|
|
|
|
local $_ = $_; s/\?/$eids->{'root'}/g; $_; |
|
371
|
|
|
|
|
|
|
} @{ $site->DBINIT_MAKE_ROOT_IMMUTABLE }; |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# run the modified statements |
|
374
|
|
|
|
|
|
|
$status = run_sql( |
|
375
|
|
|
|
|
|
|
$conn, |
|
376
|
|
|
|
|
|
|
@root_immutable_statements, |
|
377
|
|
|
|
|
|
|
); |
|
378
|
0
|
|
|
|
|
|
return $status unless $status->ok; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# insert initial set of activities |
|
381
|
|
|
|
|
|
|
$status = initialize_activities_table( $conn ); |
|
382
|
0
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# insert initial set of components |
|
384
|
|
|
|
|
|
|
try { |
|
385
|
|
|
|
|
|
|
$conn->txn( fixup => sub { |
|
386
|
0
|
0
|
|
|
|
|
my $sth = $_->prepare( $site->SQL_COMPONENT_INSERT ); |
|
387
|
|
|
|
|
|
|
foreach my $actdef ( @{ $site->DOCHAZKA_COMPONENT_DEFINITIONS } ) { |
|
388
|
|
|
|
|
|
|
$actdef->{'validations'} = undef unless exists( $actdef->{'validations'} ); |
|
389
|
0
|
|
|
|
|
|
$sth->bind_param( 1, $actdef->{path} ); |
|
390
|
|
|
|
|
|
|
$sth->bind_param( 2, $actdef->{source} ); |
|
391
|
|
|
|
|
|
|
$sth->bind_param( 3, $actdef->{acl} ); |
|
392
|
|
|
|
|
|
|
$sth->bind_param( 4, $actdef->{validations} ); |
|
393
|
|
|
|
|
|
|
$sth->execute; |
|
394
|
0
|
|
|
|
|
|
} |
|
395
|
0
|
|
|
|
|
|
} ); |
|
|
0
|
|
|
|
|
|
|
|
396
|
0
|
0
|
|
|
|
|
} catch { |
|
397
|
0
|
|
|
|
|
|
$status = $CELL->status_err( 'DOCHAZKA_DBI_ERR', args => [ $_ ] ); |
|
398
|
0
|
|
|
|
|
|
}; |
|
399
|
0
|
|
|
|
|
|
return $status unless $status->ok; |
|
400
|
0
|
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
|
# if auditing is enabled, create the audit triggers |
|
402
|
|
|
|
|
|
|
if ( $site->DOCHAZKA_AUDITING ) { |
|
403
|
0
|
|
|
0
|
|
|
$status = create_audit_triggers( $conn ); |
|
404
|
|
|
|
|
|
|
return $status unless $status->ok; |
|
405
|
0
|
|
|
0
|
|
|
} |
|
406
|
0
|
|
|
|
|
|
|
|
407
|
0
|
0
|
|
|
|
|
$log->notice( "Database $dbname successfully (re-)initialized" ); |
|
408
|
|
|
|
|
|
|
return $status; |
|
409
|
|
|
|
|
|
|
} |
|
410
|
0
|
0
|
|
|
|
|
|
|
411
|
0
|
|
|
|
|
|
|
|
412
|
0
|
0
|
|
|
|
|
=head2 get_eid_of |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
Obtain the EIDs of a list of employee nicks. Returns a reference to a hash |
|
415
|
0
|
|
|
|
|
|
where the keys are the nicks and the values are the corresponding EIDs. |
|
416
|
0
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
NOTE 1: This routine expects to receive a L<DBIx::Connector> object as its |
|
418
|
|
|
|
|
|
|
first argument. It does not use the C<$dbix_conn> singleton. |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
NOTE 2: The nicks are expected to exist and no provision (other than logging a |
|
421
|
|
|
|
|
|
|
DOCHAZKA_DBI_ERR) is made for their non-existence. |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=cut |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
my ( $conn, @nicks ) = @_; |
|
426
|
|
|
|
|
|
|
$log->debug( "Entering " . __PACKAGE__ . "::get_eid_of" ); |
|
427
|
|
|
|
|
|
|
my ( %eids, $status ); |
|
428
|
|
|
|
|
|
|
$status = $CELL->status_ok; |
|
429
|
|
|
|
|
|
|
try { |
|
430
|
|
|
|
|
|
|
$conn->run( fixup => sub { |
|
431
|
|
|
|
|
|
|
my $sth = $_->prepare( $site->DBINIT_SELECT_EID_OF ); |
|
432
|
|
|
|
|
|
|
foreach my $nick ( @nicks ) { |
|
433
|
|
|
|
|
|
|
$sth->bind_param( 1, $nick ); |
|
434
|
0
|
|
|
0
|
1
|
|
$sth->execute; |
|
435
|
0
|
|
|
|
|
|
( $eids{$nick} ) = $sth->fetchrow_array(); |
|
436
|
0
|
|
|
|
|
|
$log->debug( "EID of $nick is $eids{$nick}" ); |
|
437
|
0
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
} ); |
|
439
|
|
|
|
|
|
|
} catch { |
|
440
|
0
|
|
|
|
|
|
$status = $CELL->status_err( 'DOCHAZKA_DBI_ERR', args => [ $_ ] ); |
|
441
|
0
|
|
|
|
|
|
}; |
|
442
|
0
|
|
|
|
|
|
die $status->text unless $status->ok; |
|
443
|
0
|
|
|
|
|
|
return \%eids; |
|
444
|
0
|
|
|
|
|
|
} |
|
445
|
0
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
|
|
447
|
0
|
|
|
0
|
|
|
=head2 version |
|
448
|
|
|
|
|
|
|
|
|
449
|
0
|
|
|
0
|
|
|
Accessor method (to be called like a constructor) providing access to C<$VERSION> variable |
|
450
|
0
|
|
|
|
|
|
|
|
451
|
0
|
0
|
|
|
|
|
=cut |
|
452
|
0
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=head2 init_arbitrary_script |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
For scripts that need to manipulate the database directly (i.e. via the data |
|
459
|
|
|
|
|
|
|
model). |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=cut |
|
462
|
0
|
|
|
0
|
1
|
|
|
|
463
|
|
|
|
|
|
|
my ( $ARGS ) = @_; |
|
464
|
|
|
|
|
|
|
my $quiet = 0; |
|
465
|
|
|
|
|
|
|
if ( ref( $ARGS ) eq 'HASH' and exists( $ARGS->{quiet} ) ) { |
|
466
|
|
|
|
|
|
|
$quiet = $ARGS->{quiet}; |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
my $app_distro = 'App-Dochazka-REST'; |
|
469
|
|
|
|
|
|
|
my $sitedir = '/etc/dochazka-rest'; |
|
470
|
|
|
|
|
|
|
print "Loading configuration parameters from $sitedir\n" unless $quiet; |
|
471
|
|
|
|
|
|
|
my $status = Web::MREST::init( |
|
472
|
|
|
|
|
|
|
distro => $app_distro, |
|
473
|
|
|
|
|
|
|
sitedir => $sitedir, |
|
474
|
0
|
|
|
0
|
1
|
|
); |
|
475
|
0
|
|
|
|
|
|
die $status->text unless $status->ok; |
|
476
|
0
|
0
|
0
|
|
|
|
|
|
477
|
0
|
|
|
|
|
|
print "Setting up logging\n" unless $quiet; |
|
478
|
|
|
|
|
|
|
my $log_file = normalize_filespec( $site->MREST_LOG_FILE ); |
|
479
|
0
|
|
|
|
|
|
my $should_reset = $site->MREST_LOG_FILE_RESET; |
|
480
|
0
|
|
|
|
|
|
unlink $log_file if $should_reset; |
|
481
|
0
|
0
|
|
|
|
|
Log::Any::Adapter->set( 'File', $log_file ); |
|
482
|
0
|
|
|
|
|
|
my $message = "Logging to $log_file"; |
|
483
|
|
|
|
|
|
|
print "$message\n" unless $quiet; |
|
484
|
|
|
|
|
|
|
$log->info( $message ); |
|
485
|
|
|
|
|
|
|
if ( ! $site->MREST_APPNAME ) { |
|
486
|
0
|
0
|
|
|
|
|
die "Site parameter MREST_APPNAME is undefined - please investigate!"; |
|
487
|
|
|
|
|
|
|
} |
|
488
|
0
|
0
|
|
|
|
|
$log->init( |
|
489
|
0
|
|
|
|
|
|
ident => $site->MREST_APPNAME, |
|
490
|
0
|
|
|
|
|
|
debug_mode => ( $site->MREST_DEBUG_MODE || 0 ), |
|
491
|
0
|
0
|
|
|
|
|
); |
|
492
|
0
|
|
|
|
|
|
|
|
493
|
0
|
|
|
|
|
|
print "Connecting to database\n" unless $quiet; |
|
494
|
0
|
0
|
|
|
|
|
App::Dochazka::REST::ConnBank::init_singleton(); |
|
495
|
0
|
|
|
|
|
|
print "Database is " . conn_status() . "\n" unless $quiet; |
|
496
|
0
|
0
|
|
|
|
|
|
|
497
|
0
|
|
|
|
|
|
$faux_context = { 'dbix_conn' => $dbix_conn, 'current' => { 'eid' => 1 } }; |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
|
|
500
|
0
|
|
0
|
|
|
|
|
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=head1 GLOSSARY OF TERMS |
|
503
|
|
|
|
|
|
|
|
|
504
|
0
|
0
|
|
|
|
|
In Dochazka, some commonly-used terms have special meanings: |
|
505
|
0
|
|
|
|
|
|
|
|
506
|
0
|
0
|
|
|
|
|
=over |
|
507
|
|
|
|
|
|
|
|
|
508
|
0
|
|
|
|
|
|
=item * B<employee> -- |
|
509
|
|
|
|
|
|
|
Regardless of whether they are employees in reality, for the |
|
510
|
|
|
|
|
|
|
purposes of Dochazka employees are the folks whose attendance/time is being |
|
511
|
|
|
|
|
|
|
tracked. Employees are expected to interact with Dochazka using the |
|
512
|
|
|
|
|
|
|
following functions and commands. |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=item * B<administrator> -- |
|
515
|
|
|
|
|
|
|
In Dochazka, administrators are employees with special powers. Certain |
|
516
|
|
|
|
|
|
|
REST/CLI functions are available only to administrators. |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=item * B<CLI client> -- |
|
519
|
|
|
|
|
|
|
CLI stands for Command-Line Interface. The CLI client is the Perl script |
|
520
|
|
|
|
|
|
|
that is run when an employee types C<dochazka> at the bash prompt. |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=item * B<REST server> -- |
|
523
|
|
|
|
|
|
|
REST stands for ... . The REST server is a collection of Perl modules |
|
524
|
|
|
|
|
|
|
running on a server at the site. |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=item * B<site> -- |
|
527
|
|
|
|
|
|
|
In a general sense, the "site" is the company, organization, or place that |
|
528
|
|
|
|
|
|
|
has implemented (installed, configured) Dochazka for attendance/time |
|
529
|
|
|
|
|
|
|
tracking. In a technical sense, a site is a specific instance of the |
|
530
|
|
|
|
|
|
|
Dochazka REST server that CLI clients connect to. |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=back |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=head1 AUTHOR |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
Nathan Cutler, C<< <ncutler@suse.cz> >> |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=head1 BUGS |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
To report bugs or request features, use the GitHub issue tracker at |
|
546
|
|
|
|
|
|
|
L<https://github.com/smithfarm/dochazka-rest/issues>. |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=head1 SUPPORT |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
The full documentation comes with the distro, and can be comfortably |
|
554
|
|
|
|
|
|
|
perused at metacpan.org: |
|
555
|
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
https://metacpan.org/pod/App::Dochazka::REST |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
You can also read the documentation for individual modules using the |
|
559
|
|
|
|
|
|
|
perldoc command, e.g.: |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
perldoc App::Dochazka::REST |
|
562
|
|
|
|
|
|
|
perldoc App::Dochazka::REST::Model::Activity |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
Other resources: |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=over 4 |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=item * GitHub issue tracker (report bugs here) |
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
L<https://github.com/smithfarm/dochazka-rest> |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
L<http://annocpan.org/dist/App-Dochazka-REST> |
|
575
|
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=back |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Copyright (c) 2014-2015, SUSE LLC |
|
584
|
|
|
|
|
|
|
All rights reserved. |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without |
|
587
|
|
|
|
|
|
|
modification, are permitted provided that the following conditions are met: |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this |
|
590
|
|
|
|
|
|
|
list of conditions and the following disclaimer. |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice, |
|
593
|
|
|
|
|
|
|
this list of conditions and the following disclaimer in the documentation |
|
594
|
|
|
|
|
|
|
and/or other materials provided with the distribution. |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
3. Neither the name of SUSE LLC nor the names of its contributors |
|
597
|
|
|
|
|
|
|
may be used to endorse or promote products derived from this software without |
|
598
|
|
|
|
|
|
|
specific prior written permission. |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
601
|
|
|
|
|
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
602
|
|
|
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
603
|
|
|
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
|
604
|
|
|
|
|
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
605
|
|
|
|
|
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
606
|
|
|
|
|
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
607
|
|
|
|
|
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
608
|
|
|
|
|
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
609
|
|
|
|
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=cut |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
1; # End of App::Dochazka::REST |