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 debug_filter_visitor
37 The L<Data::Visitor::Callback> object being used for filtering. Can
38 be replaced with your own L<Data::Visitor> subclass if desired.
42 has
'debug_filter_visitor' => (
44 isa
=> 'Data::Visitor',
47 sub _build_debug_filter_visitor
{
50 return Data
::Visitor
::Callback
->new(
52 # descend into objects also
53 object
=> 'visit_ref',
55 # render skip_class option as visitor args
58 $class => sub { shift; '('.ref(shift)." object skipped, isa $class)" }
59 } @
{ $self->dump_skip_class }
62 #render any other visitor args
63 %{ $self->dump_visitor_args },
68 =head2 dump_skip_class
70 One or more class names to filter out of objects to be dumped. If an
71 object is-a one of these classes, the dump filtering will replace
72 the object with a string "skipped" message.
74 Can be either an arrayref or a whitespace-separated list of class names.
76 Default: "Catalyst", which will filter out Catalyst context objects.
80 { my $sc = subtype as
'ArrayRef';
81 coerce
$sc, from
'Str', via
{ [ split ] };
82 has
'dump_skip_class' => (
86 default => sub { ['Catalyst'] },
90 =head2 dump_visitor_args
92 Hashref of additional constructor args passed to the
93 L<Data::Visitor::Callback> object used to filter the objects for
94 dumping. Can be used to introduce nearly any kind of additional
99 # replace all scalar values in dumped objects with "chicken"
102 value => sub { 'Chicken' },
108 has
'dump_visitor_args' => (
111 default => sub { {} },
116 Boolean, default true.
118 If set, attempts to do a reverse DNS lookup to
119 resolve the hostname of the client.
123 has
'reverse_dns' => (
131 =head2 make_email( $c )
133 Returns a hashref of error email information, suitable for rendering
134 with L<Catalyst::View::Email>.
139 my ( $self, $c ) = @_;
142 my $type = ( grep $_->is_server_error, @
{$c->stash->{email_errors
}} ) ?
'E' : 'NB';
144 my $subject = '['.$c->config->{name
}."]($type) ".$c->req->uri->path_query;
145 # clamp the subject line to be no longer than 115 chars
146 $subject = substr( $subject, 0, 110 ).'...' if 115 < length $subject;
150 "==== Error(s) ====\n\n",
151 ( map { $error_num++.". $_\n" } @
{$c->stash->{email_errors
}} ),
153 # all the necessary debug information
154 ( map { ("\n==== $_->[0] ====\n\n", $_->[1], "\n") } $self->dump_these_strings( $c ) );
157 to
=> $self->default->{to
},
158 from
=> $self->default->{from
},
166 =head2 dump_these_strings( $c )
169 C<['Request', 'string dump'], ['Stash', 'string dump'], ...>
170 for use in debugging output.
172 These are filtered, suitable for debugging output.
176 sub dump_these_strings
{
179 [ 'Summary', $self->summary_text( $c ) ],
180 map [ $_->[0], Data
::Dump
::dump( $self->filter_object_for_dump( $_->[1] ) ) ],
184 # SGN-specific filtering, removing db passwords and cookie encryption
185 # strings from error email output
186 around
'dump_these_strings' => sub {
191 my @ret = $self->$orig(@_);
194 map { Data
::Dump
::dump( $_ ) }
197 @
{$c->config}{qw{ cookie_encryption_key dbpass
}},
198 ( map $_->{password
}, values %{$c->config->{DatabaseConnection
} || {}} ),
201 for my $ret ( @ret ) {
202 for my $redact ( @remove_strings ) {
203 $ret->[1] =~ s/$redact/"<redacted>"/g;
211 =head2 filter_object_for_dump( $object )
213 Return a filtered copy of the given object.
217 sub filter_object_for_dump
{
218 my ( $self, $object ) = @_;
219 $self->debug_filter_visitor->visit( $object );
222 =head2 summary_text( $c )
224 Get an un-indented block of text of the most salient features of the
227 Path_Query: /path/to/request?foo=bar&baz=boo
228 Process ID: <PID of the serving process>
229 User-Agent: <user agent string>
230 Referrer: <referrer string>
235 my ( $self, $c ) = @_;
237 my @client_ips = $c->req->header('X-Forwarded-For')
238 ?
( map { split /\s*,\s*/, $_ } $c->req->header('X-Forwarded-For') )
239 : ( $c->req->address );
240 my $client_addr_string = join ', ', (
244 "$client_ip (".(gethostbyaddr( inet_aton
( $client_ip ), AF_INET
) || 'reverse DNS lookup failed').')'
250 no warnings
'uninitialized';
251 return join '', map "$_\n", (
252 'Request : '.$c->req->method.' '.$c->req->uri,
253 'User-Agent : '.$c->req->user_agent,
254 'Referrer : '.$c->req->referer,
255 'Client Addr: '.$client_addr_string,