1 package SGN
::View
::Email
::ErrorEmail
;
3 use Moose
::Util
::TypeConstraints
;
6 use Data
::Visitor
::Callback
;
12 SGN::View::Email::ErrorEmail - Email View for SGN
16 View for sending error emails from SGN. Errors to email should be an
17 arrayref of strings in C<$c-E<gt>stash-E<gt>{email_errors}>.
21 BEGIN { extends
'SGN::View::Email' }
23 before
'process' => sub {
26 # convert the notify_errors stash key into an email stash key for
27 # the generic email view
28 $c->stash->{email
} = $self->make_email( $c );
30 $c->log->debug('sending error email to '.$c->stash->{email
}->{to
}) if $c->debug;
35 =head2 maximum_body_size
37 Maximum size in bytes of the email body to send. Bodies larger than
38 this will be truncated. Default 400,000.
42 has
'maximum_body_size' => (
50 =head2 debug_filter_visitor
52 The L<Data::Visitor::Callback> object being used for filtering. Can
53 be replaced with your own L<Data::Visitor> subclass if desired.
57 has
'debug_filter_visitor' => (
59 isa
=> 'Data::Visitor',
62 sub _build_debug_filter_visitor
{
65 return Data
::Visitor
::Callback
->new(
67 # descend into objects also
68 object
=> 'visit_ref',
70 # render skip_class option as visitor args
73 $class => sub { shift; '('.ref(shift)." object skipped, isa $class)" }
74 } @
{ $self->dump_skip_class }
77 #render any other visitor args
78 %{ $self->dump_visitor_args },
83 =head2 dump_skip_class
85 One or more class names to filter out of objects to be dumped. If an
86 object is-a one of these classes, the dump filtering will replace
87 the object with a string "skipped" message.
89 Can be either an arrayref or a whitespace-separated list of class names.
91 Default: "Catalyst", which will filter out Catalyst context objects.
95 { my $sc = subtype as
'ArrayRef';
96 coerce
$sc, from
'Str', via
{ [ split ] };
97 has
'dump_skip_class' => (
101 default => sub { ['Catalyst'] },
105 =head2 dump_visitor_args
107 Hashref of additional constructor args passed to the
108 L<Data::Visitor::Callback> object used to filter the objects for
109 dumping. Can be used to introduce nearly any kind of additional
114 # replace all scalar values in dumped objects with "chicken"
117 value => sub { 'Chicken' },
123 has
'dump_visitor_args' => (
126 default => sub { {} },
131 Boolean, default true.
133 If set, attempts to do a reverse DNS lookup to
134 resolve the hostname of the client.
138 has
'reverse_dns' => (
146 =head2 make_email( $c )
148 Returns a hashref of error email information, suitable for rendering
149 with L<Catalyst::View::Email>.
154 my ( $self, $c ) = @_;
157 my $type = ( grep $_->is_server_error, @
{$c->stash->{email_errors
}} ) ?
'E' : 'NB';
159 my $subject = '['.$c->config->{name
}."]($type) ".$c->req->uri->path_query;
160 # clamp the subject line to be no longer than 115 chars
161 $subject = substr( $subject, 0, 110 ).'...' if 115 < length $subject;
165 "==== Error(s) ====\n\n",
166 ( map { $error_num++.". $_\n" } @
{$c->stash->{email_errors
}} ),
168 # all the necessary debug information
169 ( map { ("\n==== $_->[0] ====\n\n", $_->[1], "\n") } $self->dump_these_strings( $c ) );
171 if( $self->maximum_body_size < length $body ) {
172 my $truncation_warning =
173 "\n<email body truncated, exceeded configured maximum_body_size of "
174 .$self->maximum_body_size." bytes>\n";
176 $body = substr( $body, 0, $self->maximum_body_size - length $truncation_warning )
177 . $truncation_warning;
181 to
=> $self->default->{to
},
182 from
=> $self->default->{from
},
190 =head2 dump_these_strings( $c )
193 C<['Request', 'string dump'], ['Stash', 'string dump'], ...>
194 for use in debugging output.
196 These are filtered, suitable for debugging output.
200 sub dump_these_strings
{
203 [ 'Summary', $self->summary_text( $c ) ],
204 map [ $_->[0], Data
::Dump
::dump( $self->filter_object_for_dump( $_->[1] ) ) ],
208 # SGN-specific filtering, removing db passwords and cookie encryption
209 # strings from error email output
210 around
'dump_these_strings' => sub {
215 my @ret = $self->$orig(@_);
218 map { Data
::Dump
::dump( $_ ) }
221 @
{$c->config}{qw{ cookie_encryption_key dbpass
}},
222 ( map $_->{password
}, values %{$c->config->{DatabaseConnection
} || {}} ),
225 for my $ret ( @ret ) {
226 for my $redact ( @remove_strings ) {
227 $ret->[1] =~ s/$redact/"<redacted>"/g;
235 =head2 filter_object_for_dump( $object )
237 Return a filtered copy of the given object.
241 sub filter_object_for_dump
{
242 my ( $self, $object ) = @_;
243 $self->debug_filter_visitor->visit( $object );
246 =head2 summary_text( $c )
248 Get an un-indented block of text of the most salient features of the
251 Path_Query: /path/to/request?foo=bar&baz=boo
252 Process ID: <PID of the serving process>
253 User-Agent: <user agent string>
254 Referrer: <referrer string>
259 my ( $self, $c ) = @_;
261 my @client_ips = $c->req->header('X-Forwarded-For')
262 ?
( map { split /\s*,\s*/, $_ } $c->req->header('X-Forwarded-For') )
263 : ( $c->req->address );
264 my $client_addr_string = join ', ', (
268 "$client_ip (".(gethostbyaddr( inet_aton
( $client_ip ), AF_INET
) || 'reverse DNS lookup failed').')'
274 no warnings
'uninitialized';
275 return join '', map "$_\n", (
276 'Request : '.$c->req->method.' '.$c->req->uri,
277 'User-Agent : '.$c->req->user_agent,
278 'Referrer : '.$c->req->referer,
279 'Client Addr: '.$client_addr_string,