| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Pepper::Utilities; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
$Pepper::Utilities::VERSION = '1.5'; |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# for utf8 support with JSON |
|
6
|
1
|
|
|
1
|
|
634
|
use utf8; |
|
|
1
|
|
|
|
|
14
|
|
|
|
1
|
|
|
|
|
6
|
|
|
7
|
1
|
|
|
1
|
|
607
|
use Encode qw( encode_utf8 ); |
|
|
1
|
|
|
|
|
15477
|
|
|
|
1
|
|
|
|
|
72
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# for encoding and decoding JSON |
|
10
|
1
|
|
|
1
|
|
7
|
use Cpanel::JSON::XS; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
72
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# for logging via logger() |
|
13
|
1
|
|
|
1
|
|
2052
|
use Path::Tiny; |
|
|
1
|
|
|
|
|
11176
|
|
|
|
1
|
|
|
|
|
53
|
|
|
14
|
1
|
|
|
1
|
|
629
|
use Data::Dumper; |
|
|
1
|
|
|
|
|
6218
|
|
|
|
1
|
|
|
|
|
61
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# need some date/time toys |
|
17
|
1
|
|
|
1
|
|
507
|
use Date::Format; |
|
|
1
|
|
|
|
|
7894
|
|
|
|
1
|
|
|
|
|
69
|
|
|
18
|
1
|
|
|
1
|
|
1675
|
use DateTime; |
|
|
1
|
|
|
|
|
574271
|
|
|
|
1
|
|
|
|
|
49
|
|
|
19
|
1
|
|
|
1
|
|
1752
|
use Date::Manip::Date; |
|
|
1
|
|
|
|
|
85958
|
|
|
|
1
|
|
|
|
|
48
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# support template toolkit templates |
|
22
|
1
|
|
|
1
|
|
635
|
use Template; |
|
|
1
|
|
|
|
|
19176
|
|
|
|
1
|
|
|
|
|
36
|
|
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# for being a good person |
|
25
|
1
|
|
|
1
|
|
7
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
20
|
|
|
26
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
3786
|
|
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub new { |
|
29
|
1
|
|
|
1
|
0
|
922
|
my ($class, $args) = @_; |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# make the object |
|
32
|
|
|
|
|
|
|
my $self = bless { |
|
33
|
|
|
|
|
|
|
'request' => $$args{request}, |
|
34
|
|
|
|
|
|
|
'response' => $$args{response}, |
|
35
|
|
|
|
|
|
|
'json_coder' => Cpanel::JSON::XS->new->utf8->allow_nonref->allow_blessed, |
|
36
|
|
|
|
|
|
|
'config_file' => $ENV{HOME}.'/pepper/config/pepper.cfg', |
|
37
|
1
|
|
|
|
|
29
|
'pepper_directory' => $ENV{HOME}.'/pepper', |
|
38
|
|
|
|
|
|
|
}, $class; |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# read in the system configuration |
|
41
|
1
|
50
|
|
|
|
6
|
$self->read_system_configuration() if !$$args{skip_config}; |
|
42
|
|
|
|
|
|
|
|
|
43
|
1
|
|
|
|
|
3
|
return $self; |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
### START METHODS FOR GENERATING RESPONSES AND LOGS |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# method to deliver html & json out to the client; |
|
49
|
|
|
|
|
|
|
# this must be in here to be available even if not in plack mode |
|
50
|
|
|
|
|
|
|
sub send_response { |
|
51
|
0
|
|
|
0
|
0
|
0
|
my ($self, $content, $stop_here, $content_type, $content_filename) = @_; |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# if not in Plack/PSGI land, we will skip working with $self->{response} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# $content needs to be one of a text/html string, an ARRAYREF or a HASHREF |
|
56
|
0
|
|
|
|
|
0
|
my $ref_type = ref($content); |
|
57
|
|
|
|
|
|
|
|
|
58
|
0
|
|
|
|
|
0
|
my ($access_message, $error_id, $access_error, $die_text, $display_error_message, $html_generator, $error_html); |
|
59
|
|
|
|
|
|
|
|
|
60
|
0
|
|
0
|
|
|
0
|
$stop_here ||= 0; # don't want an uninitiated value |
|
61
|
0
|
0
|
0
|
|
|
0
|
if ($stop_here == 1 || $stop_here == 3) { # if $stop_here is a 1 or 3, we are stopping due to an error condition |
|
62
|
|
|
|
|
|
|
# if it is plain text, we should most likely log the error message sent to us |
|
63
|
|
|
|
|
|
|
# and just present the error ID |
|
64
|
|
|
|
|
|
|
# exception is if you're a developer running a script; in that case, |
|
65
|
|
|
|
|
|
|
# set the 'development_server' in your system configuration |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# note access errors for display below |
|
68
|
0
|
0
|
|
|
|
0
|
$access_error = 1 if $content =~ /^Access\:/; |
|
69
|
|
|
|
|
|
|
|
|
70
|
0
|
0
|
|
|
|
0
|
if (length($content)) { |
|
71
|
0
|
|
|
|
|
0
|
$error_id = $self->logger($content,'fatals'); # 'these errors go into the 'fatals' log |
|
72
|
|
|
|
|
|
|
# send an accurate response code |
|
73
|
0
|
|
|
|
|
0
|
$self->{response}->status(500); |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# unless we are on the dev server or it's the no-app message, present the error ID instead |
|
76
|
0
|
0
|
0
|
|
|
0
|
if ($self->{config}{development_server} eq 'Y' || $content =~ /^No application exists/) { |
|
77
|
0
|
|
|
|
|
0
|
$display_error_message = $content; |
|
78
|
|
|
|
|
|
|
# need period at the end |
|
79
|
0
|
0
|
|
|
|
0
|
$display_error_message .= '.' if $display_error_message !~ /(\.|\?|\!)$/; |
|
80
|
|
|
|
|
|
|
} else { # hide the error |
|
81
|
0
|
|
|
|
|
0
|
$content = 'Execution failed; error ID: '.$error_id."\n"; |
|
82
|
0
|
|
|
|
|
0
|
$ref_type = ''; # make sure it gets treated as plain text; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# if we are in API mode, let's send back JSON |
|
86
|
0
|
0
|
|
|
|
0
|
if ($self->{auth_token}) { |
|
|
|
0
|
|
|
|
|
|
|
87
|
0
|
|
|
|
|
0
|
$ref_type = "HASH" ; |
|
88
|
0
|
|
|
|
|
0
|
$content = { |
|
89
|
|
|
|
|
|
|
'status' => 'Error', |
|
90
|
|
|
|
|
|
|
'error_id' => $error_id, |
|
91
|
|
|
|
|
|
|
'display_error_message' => $display_error_message, |
|
92
|
|
|
|
|
|
|
}; |
|
93
|
|
|
|
|
|
|
# developers see the actual message |
|
94
|
0
|
0
|
|
|
|
0
|
$$content{display_error_message} = $display_error_message if $display_error_message; |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# if we are in Web UI mode, pipe it out to the user as HTML; |
|
97
|
|
|
|
|
|
|
} elsif ($self->{request}) { |
|
98
|
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
0
|
$self->send_response($content); |
|
100
|
|
|
|
|
|
|
|
|
101
|
0
|
0
|
|
|
|
0
|
if ($self->{db}) { # if we connected to the DB, end our transaction |
|
102
|
0
|
|
|
|
|
0
|
$self->{db}->do_sql('rollback'); |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# do not continue if in the inner eval{} loop |
|
106
|
0
|
0
|
|
|
|
0
|
if ($stop_here == 1) { |
|
107
|
0
|
|
|
|
|
0
|
die 'Execution stopped: '.$content; |
|
108
|
|
|
|
|
|
|
} else { # if $stop_here == 3, then we are in a 'superfatal' from pepper.psgi |
|
109
|
0
|
|
|
|
|
0
|
return; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# if they sent a valid content type, no need to change it |
|
118
|
0
|
0
|
0
|
|
|
0
|
if ($content_type && $content_type =~ /\//) { |
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# nothing to do here |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
} elsif ($ref_type eq "HASH" || $ref_type eq "ARRAY") { # make it into json |
|
122
|
0
|
|
|
|
|
0
|
$content_type = 'application/json'; |
|
123
|
0
|
|
|
|
|
0
|
$content = $self->json_from_perl($content); |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
} elsif ($content =~ /^\/\/ This is Javascript./) { # it is 99% likely to be Javascript |
|
126
|
0
|
|
|
|
|
0
|
$content_type = 'text/javascript'; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
} elsif ($content =~ /^\/\* This is CSS./) { # it is 99% likely to be CSS |
|
129
|
0
|
|
|
|
|
0
|
$content_type = 'text/css'; |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
} elsif ($content =~ /<\S+>/) { # it is 99% likely to be HTML |
|
132
|
0
|
|
|
|
|
0
|
$content_type = 'text/html'; |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
} elsif (!$ref_type && length($content)) { # it is plain text |
|
135
|
0
|
|
|
|
|
0
|
$content_type = 'text/plain'; |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
} else { # anything else? something of a mistake, panic a little |
|
138
|
0
|
|
|
|
|
0
|
$content_type = 'text/plain'; |
|
139
|
0
|
|
|
|
|
0
|
$content = 'ERROR: The resulting content was not deliverable.'; |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# if in Plack, pack the response for delivery |
|
144
|
0
|
0
|
|
|
|
0
|
if ($self->{response}) { |
|
145
|
0
|
|
|
|
|
0
|
$self->{response}->content_type($content_type); |
|
146
|
|
|
|
|
|
|
# is this an error? Change from 200 to 500, if not done so already |
|
147
|
0
|
0
|
0
|
|
|
0
|
if ($content =~ /^(ERROR|Execution failed)/ && $self->{response}->status() eq '200') { |
|
148
|
0
|
|
|
|
|
0
|
$self->{response}->status(500); |
|
149
|
|
|
|
|
|
|
} |
|
150
|
0
|
0
|
0
|
|
|
0
|
if ($content_filename && $content_type !~ /^image/) { |
|
151
|
0
|
|
|
|
|
0
|
$self->{response}->header('Content-Disposition' => 'attachment; filename="'.$content_filename.'"'); |
|
152
|
|
|
|
|
|
|
} |
|
153
|
0
|
|
|
|
|
0
|
$self->{response}->body($content); |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
} else { # print to stdout |
|
156
|
0
|
|
|
|
|
0
|
print $content; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
|
|
159
|
0
|
0
|
|
|
|
0
|
if ($stop_here == 1) { # if they want us to stop here, do so; we should be in an eval{} loop to catch this |
|
160
|
0
|
|
|
|
|
0
|
$die_text = "Execution stopped."; |
|
161
|
0
|
0
|
|
|
|
0
|
$die_text .= '; Error ID: '.$error_id if $error_id; |
|
162
|
0
|
0
|
|
|
|
0
|
$self->{db}->do_sql('rollback') if $self->{db}; # end our transaction |
|
163
|
0
|
|
|
|
|
0
|
die $die_text; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# subroutine to process a template via template toolkit |
|
169
|
|
|
|
|
|
|
# this is for server-side processing of templates |
|
170
|
|
|
|
|
|
|
sub template_process { |
|
171
|
1
|
|
|
1
|
0
|
4
|
my ($self, $args) = @_; |
|
172
|
|
|
|
|
|
|
# $$args can contain: include_path, template_file, template_text, template_vars, send_out, save_file, stop_here |
|
173
|
|
|
|
|
|
|
# it *must* include either template_text or template_file |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# declare vars |
|
176
|
1
|
|
|
|
|
2
|
my ($output, $tt, $tt_error); |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# default include path |
|
179
|
1
|
50
|
|
|
|
4
|
if (!$$args{include_path}) { |
|
|
|
0
|
|
|
|
|
|
|
180
|
1
|
|
|
|
|
4
|
$$args{include_path} = $self->{pepper_directory}.'/template/'; |
|
181
|
|
|
|
|
|
|
} elsif ($$args{include_path} !~ /\/$/) { # make sure of trailing / |
|
182
|
0
|
|
|
|
|
0
|
$$args{include_path} .= '/'; |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# $$args{tag_style} = 'star', 'template' or similiar |
|
186
|
|
|
|
|
|
|
# see https://metacpan.org/pod/Template#TAG_STYLE |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# default tag_style to regular, [% %] |
|
189
|
1
|
|
50
|
|
|
7
|
$$args{tag_style} ||= 'template'; |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# crank up the template toolkit object, and set it up to save to the $output variable |
|
192
|
1
|
|
|
|
|
2
|
$output = ''; |
|
193
|
|
|
|
|
|
|
$tt = Template->new({ |
|
194
|
|
|
|
|
|
|
ENCODING => 'utf8', |
|
195
|
|
|
|
|
|
|
INCLUDE_PATH => $$args{include_path}, |
|
196
|
|
|
|
|
|
|
OUTPUT => \$output, |
|
197
|
|
|
|
|
|
|
TAG_STYLE => $$args{tag_style}, |
|
198
|
1
|
|
33
|
|
|
21
|
}) || $self->send_response("$Template::ERROR",1); |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# process the template |
|
201
|
1
|
50
|
|
|
|
23451
|
if ($$args{template_file}) { |
|
|
|
50
|
|
|
|
|
|
|
202
|
0
|
|
|
|
|
0
|
$tt->process( $$args{template_file}, $$args{template_vars}, $output, {binmode => ':encoding(utf8)'} ); |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
} elsif ($$args{template_text}) { |
|
205
|
1
|
|
|
|
|
8
|
$tt->process( \$$args{template_text}, $$args{template_vars}, $output, {binmode => ':encoding(utf8)'} ); |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
} else { # one or the other |
|
208
|
0
|
|
|
|
|
0
|
$self->send_response("Error: you must provide either template_file or template_text",1); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# make sure to throw error if there is one |
|
212
|
1
|
|
|
|
|
29769
|
$tt_error = $tt->error(); |
|
213
|
1
|
50
|
|
|
|
18
|
$self->send_response("Template Error in $$args{template_file}: $tt_error",1) if $tt_error; |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# send it out to the client, save to the filesystem, or return to the caller |
|
216
|
1
|
50
|
|
|
|
6
|
if ($$args{send_out}) { # output to the client |
|
|
|
50
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# the '2' tells mr_zebra to avoid logging an error |
|
219
|
0
|
|
|
|
|
0
|
$self->send_response($output,2); |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
} elsif ($$args{save_file}) { # save to the filesystem |
|
222
|
0
|
|
|
|
|
0
|
$self->filer( $$args{save_file}, 'write', $output); |
|
223
|
0
|
|
|
|
|
0
|
return $$args{save_file}; # just kick back the file name |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
} else { # just return |
|
226
|
1
|
|
|
|
|
5
|
return $output; |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# method to log messages under the 'log' directory |
|
231
|
|
|
|
|
|
|
sub logger { |
|
232
|
|
|
|
|
|
|
# takes three args: the message itself (required), the log_type (optional, one word), |
|
233
|
|
|
|
|
|
|
# and an optional log location/directory |
|
234
|
0
|
|
|
0
|
0
|
0
|
my ($self, $log_message, $log_type, $log_directory) = @_; |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
# return if no message sent; no point |
|
237
|
0
|
0
|
|
|
|
0
|
return if !$log_message; |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# default is 'errors' log type |
|
240
|
0
|
|
0
|
|
|
0
|
$log_type ||= 'errors'; |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# no spaces or special chars in that $log_type |
|
243
|
0
|
|
|
|
|
0
|
$log_type =~ s/[^a-z0-9\_]//gi; |
|
244
|
|
|
|
|
|
|
|
|
245
|
0
|
|
|
|
|
0
|
my ($error_id, $todays_date, $current_time, $log_file, $now); |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# how about a nice error ID |
|
248
|
0
|
|
|
|
|
0
|
$error_id = $self->random_string(15); |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# what is today's date and current time |
|
251
|
0
|
|
|
|
|
0
|
$now = time(); # this is the unix epoch / also a quick-find id of the error |
|
252
|
0
|
|
|
|
|
0
|
$todays_date = $self->time_to_date($now,'to_date_db','utc'); |
|
253
|
0
|
|
|
|
|
0
|
$current_time = $self->time_to_date($now,'to_datetime_iso','utc'); |
|
254
|
0
|
|
|
|
|
0
|
$current_time =~ s/\s//g; # no spaces |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# target log file - did they provide a target log_directory? |
|
257
|
0
|
0
|
0
|
|
|
0
|
if ($log_directory && -d $log_directory) { # yes |
|
258
|
0
|
|
|
|
|
0
|
$log_file = $log_directory.'/'.$log_type.'-'.$todays_date.'.log'; |
|
259
|
|
|
|
|
|
|
} else { # nope, take default |
|
260
|
0
|
|
|
|
|
0
|
$log_file = $self->{pepper_directory}.'/log/'.$log_type.'-'.$todays_date.'.log'; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# sometimes time() adds a \n |
|
264
|
0
|
|
|
|
|
0
|
$log_message =~ s/\n//; |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# if they sent a hash or array, it's a developer doing testing. use Dumper() to output it |
|
267
|
0
|
0
|
0
|
|
|
0
|
if (ref($log_message) eq 'HASH' || ref($log_message) eq 'ARRAY') { |
|
268
|
0
|
|
|
|
|
0
|
$log_message = Dumper($log_message); |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# if we have the plack object (created via pack_luggage()), append to the $log_message |
|
272
|
0
|
0
|
|
|
|
0
|
if ($self->{request}) { |
|
273
|
0
|
|
|
|
|
0
|
$log_message .= ' | https://'.$self->{request}->env->{HTTP_HOST}.$self->{request}->request_uri(); |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# append to our log file via Path::Tiny |
|
277
|
0
|
|
|
|
|
0
|
path($log_file)->append_raw( 'ID: '.$error_id.' | '.$current_time.': '.$log_message."\n" ); |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# return the code/epoch for an innocent-looking display and for fast lookup |
|
280
|
0
|
|
|
|
|
0
|
return $error_id; |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
### START GENERAL UTILITIES |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# simple routine to get a DateTime object for a timestamp, e.g. 2016-09-04 16:30 |
|
286
|
|
|
|
|
|
|
sub get_datetime_object { |
|
287
|
1
|
|
|
1
|
0
|
4
|
my ($self, $time_string, $time_zone_name) = @_; |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# default timezone is New York |
|
290
|
1
|
|
|
|
|
2
|
$time_zone_name = $self->{time_zone_name}; |
|
291
|
1
|
|
50
|
|
|
3
|
$time_zone_name ||= 'America/New_York'; |
|
292
|
|
|
|
|
|
|
|
|
293
|
1
|
|
|
|
|
2
|
my ($dt, $year, $month, $day, $hour, $minute, $second); |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# be willing to just accept the date and presume midnight |
|
296
|
1
|
50
|
|
|
|
7
|
if ($time_string =~ /^\d{4}-\d{2}-\d{2}$/) { |
|
297
|
0
|
|
|
|
|
0
|
$time_string .= ' 00:00:00'; |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# i will generally just send minutes; we want to support seconds too, and default to 00 seconds |
|
301
|
1
|
50
|
|
|
|
5
|
if ($time_string =~ /\s\d{2}:\d{2}$/) { |
|
302
|
1
|
|
|
|
|
3
|
$time_string .= ':00'; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# if that timestring is not right, just get one for 'now' |
|
306
|
1
|
50
|
|
|
|
6
|
if ($time_string !~ /^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/) { |
|
307
|
|
|
|
|
|
|
|
|
308
|
0
|
|
|
|
|
0
|
$dt = DateTime->from_epoch( |
|
309
|
|
|
|
|
|
|
epoch => time(), |
|
310
|
|
|
|
|
|
|
time_zone => $time_zone_name, |
|
311
|
|
|
|
|
|
|
); |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# otherwise, get a custom datetime object |
|
314
|
|
|
|
|
|
|
} else { |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# have to slice-and-dice it a bit to make sure DateTime is happy |
|
317
|
1
|
|
|
|
|
3
|
$time_string =~ s/-0/-/g; |
|
318
|
1
|
|
|
|
|
10
|
($year,$month,$day,$hour,$minute,$second) = split /-|\s|:/, $time_string; |
|
319
|
1
|
|
|
|
|
4
|
$hour =~ s/^0//; |
|
320
|
1
|
|
|
|
|
3
|
$minute =~ s/^0//; |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
# try to set up the DateTime object, wrapping in eval in case they send an invalid time |
|
323
|
|
|
|
|
|
|
# (which happens if you go for 2am on a 'spring-forward' day |
|
324
|
1
|
|
|
|
|
3
|
eval { |
|
325
|
1
|
|
|
|
|
11
|
$dt = DateTime->new( |
|
326
|
|
|
|
|
|
|
year => $year, |
|
327
|
|
|
|
|
|
|
month => $month, |
|
328
|
|
|
|
|
|
|
day => $day, |
|
329
|
|
|
|
|
|
|
hour => $hour, |
|
330
|
|
|
|
|
|
|
minute => $minute, |
|
331
|
|
|
|
|
|
|
second => $second, |
|
332
|
|
|
|
|
|
|
time_zone => $time_zone_name, |
|
333
|
|
|
|
|
|
|
); |
|
334
|
|
|
|
|
|
|
}; |
|
335
|
|
|
|
|
|
|
|
|
336
|
1
|
50
|
|
|
|
716
|
if ($@) { # if they called for an invalid time, just move ahead and hour and try again |
|
337
|
0
|
|
|
|
|
0
|
$hour++; |
|
338
|
0
|
|
|
|
|
0
|
$dt = DateTime->new( |
|
339
|
|
|
|
|
|
|
year => $year, |
|
340
|
|
|
|
|
|
|
month => $month, |
|
341
|
|
|
|
|
|
|
day => $day, |
|
342
|
|
|
|
|
|
|
hour => $hour, |
|
343
|
|
|
|
|
|
|
minute => $minute, |
|
344
|
|
|
|
|
|
|
second => $second, |
|
345
|
|
|
|
|
|
|
time_zone => $time_zone_name, |
|
346
|
|
|
|
|
|
|
); |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# send it out |
|
352
|
1
|
|
|
|
|
2
|
return $dt; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# method to read/write/append to a file via Path::Tiny |
|
356
|
|
|
|
|
|
|
sub filer { |
|
357
|
|
|
|
|
|
|
# required arg is the full path to the file |
|
358
|
|
|
|
|
|
|
# optional second arg is the operation: read, write, or append. default to 'read' |
|
359
|
|
|
|
|
|
|
# optional third arg is the content for write or append operations |
|
360
|
0
|
|
|
0
|
0
|
0
|
my ($self, $file_location, $operation, $content) = @_; |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# return if no good file path |
|
363
|
0
|
0
|
|
|
|
0
|
return if !$file_location; |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# default operation is 'read' |
|
366
|
0
|
0
|
0
|
|
|
0
|
$operation = 'read' if !$operation || $operation !~ /read|write|append|basename/; |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# return if write or append and no content |
|
369
|
0
|
0
|
0
|
|
|
0
|
return if $operation !~ /read|basename/ && !$content; |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# do the operations |
|
372
|
0
|
0
|
|
|
|
0
|
if ($operation eq 'read') { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
|
|
374
|
0
|
|
|
|
|
0
|
$content = path($file_location)->slurp_raw; |
|
375
|
0
|
|
|
|
|
0
|
return $content; |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
} elsif ($operation eq 'write') { |
|
378
|
|
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
0
|
path($file_location)->spew_raw( $content ); |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
} elsif ($operation eq 'append') { |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# make sure the new content ends with a \n |
|
384
|
0
|
0
|
|
|
|
0
|
$content .= "\n" if $content !~ /\n$/; |
|
385
|
|
|
|
|
|
|
|
|
386
|
0
|
|
|
|
|
0
|
path($file_location)->append_raw( $content ); |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
} elsif ($operation eq 'basename') { |
|
389
|
|
|
|
|
|
|
|
|
390
|
0
|
|
|
|
|
0
|
return path($file_location)->basename; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
} |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# two json translating methods using the great JSON module |
|
397
|
|
|
|
|
|
|
# First, make perl data structures into JSON objects |
|
398
|
|
|
|
|
|
|
sub json_from_perl { |
|
399
|
1
|
|
|
1
|
0
|
544
|
my ($self, $data_ref) = @_; |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# for this, we shall go UTF8 |
|
402
|
1
|
|
|
|
|
39
|
return $self->{json_coder}->encode( $data_ref ); |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# Second, make JSON objects into Perl structures |
|
406
|
|
|
|
|
|
|
sub json_to_perl { |
|
407
|
1
|
|
|
1
|
0
|
7
|
my ($self, $json_text) = @_; |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# first, let's try via UTF-8 decoding |
|
410
|
1
|
|
|
|
|
9
|
my $json_text_ut8 = encode_utf8( $json_text ); |
|
411
|
1
|
|
|
|
|
3
|
my $perl_hashref = {}; |
|
412
|
1
|
|
|
|
|
2
|
eval { |
|
413
|
1
|
|
|
|
|
18
|
$perl_hashref = $self->{json_coder}->decode( $json_text_ut8 ); |
|
414
|
|
|
|
|
|
|
}; |
|
415
|
|
|
|
|
|
|
|
|
416
|
1
|
|
|
|
|
3
|
return $perl_hashref; |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# utility to generate a random string |
|
420
|
|
|
|
|
|
|
sub random_string { |
|
421
|
0
|
|
|
0
|
0
|
0
|
my ($self, $length, $numbers_only) = @_; |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
# default that to 10 |
|
424
|
0
|
|
0
|
|
|
0
|
$length ||= 10; |
|
425
|
|
|
|
|
|
|
|
|
426
|
0
|
|
|
|
|
0
|
my (@chars,$string); |
|
427
|
|
|
|
|
|
|
|
|
428
|
0
|
0
|
|
|
|
0
|
if ($numbers_only) { # what they want... |
|
429
|
0
|
|
|
|
|
0
|
@chars = ('0'..'9'); |
|
430
|
|
|
|
|
|
|
} else { # both |
|
431
|
0
|
|
|
|
|
0
|
@chars = ('0'..'9', 'A'..'F'); |
|
432
|
|
|
|
|
|
|
} |
|
433
|
|
|
|
|
|
|
|
|
434
|
0
|
|
|
|
|
0
|
while ($length--) { |
|
435
|
0
|
|
|
|
|
0
|
$string .= $chars[rand @chars] |
|
436
|
|
|
|
|
|
|
}; |
|
437
|
|
|
|
|
|
|
|
|
438
|
0
|
|
|
|
|
0
|
return $string; |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
# method to read a JSON file into a hashref |
|
443
|
|
|
|
|
|
|
sub read_json_file { |
|
444
|
0
|
|
|
0
|
0
|
0
|
my ($self, $json_file_path) = @_; |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# we shall give them an empty hashref if nothing else |
|
447
|
0
|
0
|
0
|
|
|
0
|
return {} if !$json_file_path || !(-e $json_file_path); |
|
448
|
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
0
|
my $json_content = $self->filer($json_file_path); |
|
450
|
|
|
|
|
|
|
|
|
451
|
0
|
0
|
|
|
|
0
|
return {} if !$json_content; |
|
452
|
|
|
|
|
|
|
|
|
453
|
0
|
|
|
|
|
0
|
return $self->json_to_perl($json_content); |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# method to save JSON into a file |
|
458
|
|
|
|
|
|
|
sub write_json_file { |
|
459
|
0
|
|
|
0
|
0
|
0
|
my ($self, $json_file_path, $data_structure) = @_; |
|
460
|
|
|
|
|
|
|
|
|
461
|
0
|
0
|
0
|
|
|
0
|
return if !$json_file_path || ref($data_structure) !~ /ARRAY|HASH/; |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
# writing one liners like this does not make me feel beautiful |
|
464
|
0
|
|
|
|
|
0
|
$self->filer($json_file_path, 'write', $self->json_from_perl($data_structure) ); |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# start the timeToDate method, where we convert between UNIX timestamps and human-friendly dates |
|
469
|
|
|
|
|
|
|
sub time_to_date { |
|
470
|
|
|
|
|
|
|
# declare vars & grab args |
|
471
|
1
|
|
|
1
|
0
|
7
|
my ($self, $timestamp, $task, $time_zone_name) = @_; |
|
472
|
1
|
|
|
|
|
3
|
my ($day, $dt, $diff, $month, $templ, $year); |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# default timezone to UTC if no timezone sent or set |
|
475
|
|
|
|
|
|
|
# if they sent a 'utc', force it to be Etc/GMT -- this is for the logger |
|
476
|
1
|
50
|
33
|
|
|
5
|
$time_zone_name = 'Etc/GMT' if !$time_zone_name || $time_zone_name eq 'utc'; |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# allow them to set a default time zone by setting $pepper->{utilities}{time_zone_name} |
|
479
|
|
|
|
|
|
|
# or $ENV{PERL_DATETIME_DEFAULT_TZ} |
|
480
|
1
|
|
0
|
|
|
3
|
$time_zone_name ||= $self->{time_zone_name} || $ENV{PERL_DATETIME_DEFAULT_TZ}; |
|
|
|
|
33
|
|
|
|
|
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# set the time zone if not set |
|
483
|
1
|
|
33
|
|
|
13
|
$self->{time_zone_name} ||= $time_zone_name; |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
# fix up timestamp as necessary |
|
486
|
1
|
50
|
|
|
|
6
|
if (!$timestamp) { # empty timestamp --> default to current timestamp |
|
|
|
50
|
|
|
|
|
|
|
487
|
0
|
|
|
|
|
0
|
$timestamp = time(); |
|
488
|
|
|
|
|
|
|
} elsif ($timestamp =~ /\,/) { # human date...make it YYYY-MM-DD |
|
489
|
0
|
|
|
|
|
0
|
($month,$day,$year) = split /\s/, $timestamp; # get its pieces |
|
490
|
|
|
|
|
|
|
# turn the month into a proper number |
|
491
|
0
|
0
|
|
|
|
0
|
if ($month =~ /Jan/) { $month = "01"; |
|
|
0
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
492
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Feb/) { $month = "02"; |
|
493
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Mar/) { $month = "03"; |
|
494
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Apr/) { $month = "04"; |
|
495
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /May/) { $month = "05"; |
|
496
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Jun/) { $month = "06"; |
|
497
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Jul/) { $month = "07"; |
|
498
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Aug/) { $month = "08"; |
|
499
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Sep/) { $month = "09"; |
|
500
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Oct/) { $month = "10"; |
|
501
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Nov/) { $month = "11"; |
|
502
|
0
|
|
|
|
|
0
|
} elsif ($month =~ /Dec/) { $month = "12"; } |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# remove the comma from the date and make sure it has two digits |
|
505
|
0
|
|
|
|
|
0
|
$day =~ s/\,//; |
|
506
|
0
|
0
|
|
|
|
0
|
$day = '0'.$day if $day < 10; |
|
507
|
|
|
|
|
|
|
|
|
508
|
0
|
|
|
|
|
0
|
$timestamp = $year.'-'.$month.'-'.$day; |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
} |
|
511
|
|
|
|
|
|
|
# if they passed a YYYY-MM-DD date, also we will get a DateTime object |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# need that epoch if a date string was set / parsed |
|
514
|
1
|
50
|
33
|
|
|
9
|
if ($month || $timestamp =~ /-/) { |
|
515
|
1
|
|
|
|
|
6
|
$dt = $self->get_datetime_object($timestamp.' 00:00',$time_zone_name); |
|
516
|
1
|
|
|
|
|
6
|
$timestamp = $dt->epoch; |
|
517
|
1
|
|
|
|
|
31
|
$time_zone_name = 'Etc/GMT'; # don't offset dates, only timestamps |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# default task is the epoch for the first second of the day |
|
521
|
1
|
|
50
|
|
|
4
|
$task ||= 'to_unix_start'; |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# proceed based on $task |
|
524
|
1
|
50
|
33
|
|
|
47
|
if ($task eq "to_unix_start") { # date to unix timestamp -- start of the day |
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
525
|
0
|
|
|
|
|
0
|
return $timestamp; # already done above |
|
526
|
|
|
|
|
|
|
} elsif ($task eq "to_unix_end") { # date to unix timestamp -- end of the day |
|
527
|
0
|
|
|
|
|
0
|
return ($timestamp + 86399); # most done above |
|
528
|
|
|
|
|
|
|
} elsif ($task eq "to_date_db") { # unix timestamp to db-date (YYYY-MM-DD) |
|
529
|
0
|
|
|
|
|
0
|
$templ = '%Y-%m-%d'; |
|
530
|
|
|
|
|
|
|
} elsif (!$task || $task eq "to_date_human") { # unix timestamp to human date (Mon DD, YYYY) |
|
531
|
0
|
|
|
|
|
0
|
($diff) = ($timestamp - time())/15552000; # drop the year if within the last six months |
|
532
|
0
|
0
|
0
|
|
|
0
|
if ($diff > -1 && $diff < 1) { |
|
533
|
0
|
|
|
|
|
0
|
$templ = '%B %e'; |
|
534
|
|
|
|
|
|
|
} else { |
|
535
|
0
|
|
|
|
|
0
|
$templ = '%B %e, %Y'; |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
} elsif ($task eq "to_date_human_full") { # force YYYY in above |
|
538
|
0
|
|
|
|
|
0
|
$templ = '%B %e, %Y'; |
|
539
|
|
|
|
|
|
|
} elsif ($task eq "to_date_human_abbrev") { # shorter month name in above |
|
540
|
0
|
|
|
|
|
0
|
$templ = '%b %e, %Y'; |
|
541
|
|
|
|
|
|
|
} elsif ($task eq "to_date_human_dayname") { # unix timestamp to human date (DayOfWeekName, Mon DD, YYYY) |
|
542
|
0
|
|
|
|
|
0
|
($diff) = ($timestamp - time())/15552000; # drop the year if within the last six months |
|
543
|
0
|
0
|
0
|
|
|
0
|
if ($diff > -1 && $diff < 1) { |
|
544
|
0
|
|
|
|
|
0
|
$templ = '%A, %b %e'; |
|
545
|
|
|
|
|
|
|
} else { |
|
546
|
0
|
|
|
|
|
0
|
$templ = '%A, %b %e, %Y'; |
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
} elsif ($task eq "to_year") { # just want year |
|
549
|
0
|
|
|
|
|
0
|
$templ = '%Y'; |
|
550
|
|
|
|
|
|
|
} elsif ($task eq "to_month" || $task eq "to_month_name") { # unix timestamp to month name (Month YYYY) |
|
551
|
0
|
|
|
|
|
0
|
$templ = '%B %Y'; |
|
552
|
|
|
|
|
|
|
} elsif ($task eq "to_month_abbrev") { # unix timestamp to month abreviation (MonYY, i.e. Sep15) |
|
553
|
0
|
|
|
|
|
0
|
$templ = '%b%y'; |
|
554
|
|
|
|
|
|
|
} elsif ($task eq "to_date_human_time") { # unix timestamp to human date with time (Mon DD, YYYY at HH:MM:SS XM) |
|
555
|
0
|
|
|
|
|
0
|
($diff) = ($timestamp - time())/31536000; |
|
556
|
0
|
0
|
0
|
|
|
0
|
if ($diff >= -1 && $diff <= 1) { |
|
557
|
0
|
|
|
|
|
0
|
$templ = '%b %e at %l:%M%P'; |
|
558
|
|
|
|
|
|
|
} else { |
|
559
|
0
|
|
|
|
|
0
|
$templ = '%b %e, %Y at %l:%M%P'; |
|
560
|
|
|
|
|
|
|
} |
|
561
|
|
|
|
|
|
|
} elsif ($task eq "to_just_human_time") { # unix timestamp to humantime (HH:MM:SS XM) |
|
562
|
0
|
|
|
|
|
0
|
$templ = '%l:%M%P'; |
|
563
|
|
|
|
|
|
|
} elsif ($task eq "to_just_military_time") { # unix timestamp to military time |
|
564
|
0
|
|
|
|
|
0
|
$templ = '%R'; |
|
565
|
|
|
|
|
|
|
} elsif ($task eq "to_datetime_iso") { # ISO-formatted timestamp, i.e. 2016-09-04T16:12:00+00:00 |
|
566
|
0
|
|
|
|
|
0
|
$templ = '%Y-%m-%dT%X%z'; |
|
567
|
|
|
|
|
|
|
} elsif ($task eq "to_day_of_week") { # epoch to day of the week, like 'Saturday' |
|
568
|
1
|
|
|
|
|
3
|
$templ = '%A'; |
|
569
|
|
|
|
|
|
|
} elsif ($task eq "to_day_of_week_numeric") { # 0..6 day of the week |
|
570
|
0
|
|
|
|
|
0
|
$templ = '%w'; |
|
571
|
|
|
|
|
|
|
} |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
# if they sent a time zone, offset the timestamp epoch appropriately |
|
574
|
1
|
50
|
|
|
|
3
|
if ($time_zone_name ne 'Etc/GMT') { |
|
575
|
|
|
|
|
|
|
# have we cached this? |
|
576
|
0
|
0
|
|
|
|
0
|
if (!$self->{tz_offsets}{$time_zone_name}) { |
|
577
|
0
|
|
|
|
|
0
|
$dt = DateTime->from_epoch( |
|
578
|
|
|
|
|
|
|
epoch => $timestamp, |
|
579
|
|
|
|
|
|
|
time_zone => $time_zone_name, |
|
580
|
|
|
|
|
|
|
); |
|
581
|
0
|
|
|
|
|
0
|
$self->{tz_offsets}{$time_zone_name} = $dt->offset; |
|
582
|
|
|
|
|
|
|
} |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
# apply the offset |
|
585
|
0
|
|
|
|
|
0
|
$timestamp += $self->{tz_offsets}{$time_zone_name}; |
|
586
|
|
|
|
|
|
|
} |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# now run the conversion |
|
589
|
1
|
|
|
|
|
6
|
$timestamp = time2str($templ, $timestamp,'GMT'); |
|
590
|
1
|
|
|
|
|
166
|
$timestamp =~ s/ / /g; # remove double spaces; |
|
591
|
1
|
|
|
|
|
5
|
$timestamp =~ s/GMT //; |
|
592
|
1
|
|
|
|
|
16
|
return $timestamp; |
|
593
|
|
|
|
|
|
|
} |
|
594
|
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
### START METHODS FOR pepper setup |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# loads up $self->{config}; auto-called via new() above |
|
598
|
|
|
|
|
|
|
sub read_system_configuration { |
|
599
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
600
|
|
|
|
|
|
|
|
|
601
|
0
|
|
|
|
|
|
my ($the_file, $obfuscated_json, $config_json); |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# kick out if that file does not exist yet |
|
604
|
0
|
0
|
|
|
|
|
if (!(-e $self->{config_file})) { |
|
605
|
0
|
|
|
|
|
|
$self->send_response('ERROR: Can not find system configuration file.',1); |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
# try to read it in |
|
609
|
0
|
|
|
|
|
|
eval { |
|
610
|
0
|
|
|
|
|
|
$obfuscated_json = $self->filer( $self->{config_file} ); |
|
611
|
0
|
|
|
|
|
|
$config_json = pack "h*", $obfuscated_json; |
|
612
|
0
|
|
|
|
|
|
$self->{config} = $self->json_to_perl($config_json); |
|
613
|
|
|
|
|
|
|
}; |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# error out if there was any failure |
|
616
|
0
|
0
|
0
|
|
|
|
if ($@ || ref($self->{config}) ne 'HASH') { |
|
617
|
0
|
|
|
|
|
|
$self->send_response('ERROR: Could not read in system configuration file: '.$@,1); |
|
618
|
|
|
|
|
|
|
} |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
} |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
# save a system config file |
|
623
|
|
|
|
|
|
|
sub write_system_configuration { |
|
624
|
0
|
|
|
0
|
0
|
|
my ($self,$new_config) = @_; |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# convert config to JSON |
|
627
|
0
|
|
|
|
|
|
my $config_json = $self->json_from_perl($new_config); |
|
628
|
|
|
|
|
|
|
# slight obfuscation |
|
629
|
0
|
|
|
|
|
|
my $obfuscated_json = unpack "h*", $config_json; |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
# stash out the file |
|
632
|
0
|
|
|
|
|
|
path( $self->{config_file} )->spew_raw( $obfuscated_json ); |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
# set the permissions |
|
635
|
0
|
|
|
|
|
|
chmod 0600, $self->{config_file} ; |
|
636
|
|
|
|
|
|
|
} |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# method to update the endpoint mapping configs via 'pepper set-endpoint' |
|
639
|
|
|
|
|
|
|
sub set_endpoint_mapping { |
|
640
|
0
|
|
|
0
|
0
|
|
my ($self, $endpoint_uri, $endpoint_handler) = @_; |
|
641
|
|
|
|
|
|
|
|
|
642
|
0
|
0
|
0
|
|
|
|
if (!$endpoint_uri || !$endpoint_handler) { |
|
643
|
0
|
|
|
|
|
|
$self->send_response('Error: Both arguments are required for set_endpoint_mapping()',1); |
|
644
|
|
|
|
|
|
|
} |
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
# did they choose to store in a database table? |
|
647
|
0
|
0
|
|
|
|
|
if ($self->{config}{url_mappings_table}) { |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# make sure that table exists |
|
650
|
0
|
|
|
|
|
|
my ($database_name, $table_name) = split /\./, $self->{config}{url_mappings_table}; |
|
651
|
0
|
|
|
|
|
|
my ($table_exists) = $self->{db}->quick_select(qq{ |
|
652
|
|
|
|
|
|
|
select count(*) from information_schema.tables |
|
653
|
|
|
|
|
|
|
where table_schema=? and table_name=? |
|
654
|
|
|
|
|
|
|
},[ $database_name, $table_name ]); |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
# if the table does not exist, try to make it |
|
657
|
0
|
0
|
|
|
|
|
if (!$table_exists) { |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
# we won't create databases/schema in this library |
|
660
|
0
|
|
|
|
|
|
my ($database_exists) = $self->{db}->quick_select(qq{ |
|
661
|
|
|
|
|
|
|
select count(*) from information_schema.schemata |
|
662
|
|
|
|
|
|
|
where schema_name=? |
|
663
|
|
|
|
|
|
|
},[ $database_name ]); |
|
664
|
|
|
|
|
|
|
|
|
665
|
0
|
0
|
|
|
|
|
if (!$database_exists) { |
|
666
|
0
|
|
|
|
|
|
$self->send_response("Error: Database schema $database_exists does not exist",1); |
|
667
|
|
|
|
|
|
|
} |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
# safe to create the table |
|
670
|
0
|
|
|
|
|
|
$self->{db}->do_sql(qq{ |
|
671
|
|
|
|
|
|
|
create table $self->{config}{url_mappings_table} ( |
|
672
|
|
|
|
|
|
|
endpoint_uri varchar(200) primary key, |
|
673
|
|
|
|
|
|
|
handler_module varchar(200) not null |
|
674
|
|
|
|
|
|
|
) |
|
675
|
|
|
|
|
|
|
}); |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
} |
|
678
|
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
# finally, create the mapping |
|
680
|
0
|
|
|
|
|
|
$self->{db}->do_sql(qq{ |
|
681
|
|
|
|
|
|
|
replace into $self->{config}{url_mappings_table} |
|
682
|
|
|
|
|
|
|
(endpoint_uri, handler_module) values (?, ?) |
|
683
|
|
|
|
|
|
|
}, [$endpoint_uri, $endpoint_handler] ); |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
# save this change |
|
686
|
0
|
|
|
|
|
|
$self->{db}->commit(); |
|
687
|
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
# otherwise, save to a JSON file |
|
689
|
|
|
|
|
|
|
} else { |
|
690
|
|
|
|
|
|
|
|
|
691
|
0
|
|
|
|
|
|
my $url_mappings = $self->read_json_file( $self->{config}{url_mappings_file} ); |
|
692
|
0
|
|
|
|
|
|
$$url_mappings{$endpoint_uri} = $endpoint_handler; |
|
693
|
0
|
|
|
|
|
|
$self->write_json_file( $self->{config}{url_mappings_file}, $url_mappings ); |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
} |
|
696
|
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
# method to delete an endpoint mapping via 'pepper delete-endpoint' |
|
700
|
|
|
|
|
|
|
sub delete_endpoint_mapping { |
|
701
|
0
|
|
|
0
|
0
|
|
my ($self, $endpoint_uri) = @_; |
|
702
|
|
|
|
|
|
|
|
|
703
|
0
|
0
|
|
|
|
|
if (!$endpoint_uri) { |
|
704
|
0
|
|
|
|
|
|
$self->send_response('Error: The endpoint uri must be specified for delete_endpoint_mapping()',1); |
|
705
|
|
|
|
|
|
|
} |
|
706
|
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# did they choose to store in a database table? |
|
708
|
0
|
0
|
|
|
|
|
if ($self->{config}{url_mappings_table}) { |
|
709
|
|
|
|
|
|
|
|
|
710
|
0
|
|
|
|
|
|
$self->{db}->do_sql(qq{ |
|
711
|
|
|
|
|
|
|
delete from $self->{config}{url_mappings_table} |
|
712
|
|
|
|
|
|
|
where endpoint_uri=? |
|
713
|
|
|
|
|
|
|
}, [$endpoint_uri] ); |
|
714
|
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
# save this change |
|
716
|
0
|
|
|
|
|
|
$self->{db}->commit(); |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
# or a JSON file? |
|
719
|
|
|
|
|
|
|
} else { |
|
720
|
|
|
|
|
|
|
|
|
721
|
0
|
|
|
|
|
|
my $url_mappings = $self->read_json_file( $self->{config}{url_mappings_file} ); |
|
722
|
0
|
|
|
|
|
|
delete ( $$url_mappings{$endpoint_uri} ); |
|
723
|
0
|
|
|
|
|
|
$self->write_json_file( $self->{config}{url_mappings_file}, $url_mappings ); |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
} |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
} |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
1; |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
__END__ |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=head1 NAME |
|
734
|
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Pepper::Utilities |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
738
|
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
This package provides useful functions for web services and scripts built using the |
|
740
|
|
|
|
|
|
|
Pepper quick-start kit. These methods can be access via the main 'Pepper' object, |
|
741
|
|
|
|
|
|
|
and are all documented in that package. Please see 'perldoc Pepper' or the main |
|
742
|
|
|
|
|
|
|
documentation on MetaCPAN. |