Apache-ErrorControl

 view release on metacpan or  search on metacpan

ErrorControl.pm  view on Meta::CPAN

  my %tmpl_args = ();
  if ($self->{template_dir}) {
    $tmpl_args{path} = $self->{template_dir};
  }

  my $tmpl = new HTML::Template::Set(
    %tmpl_args,
    filename      => $template,
    cache         => TRUE,
#    debug         => TRUE,
    associate_env => TRUE
  );
  # }}}


  # Setup Template Params {{{

  # build a hash of the params, so we dont try and set anything that doesnt
  # exist in the template
  my %params;
  map { $params{$_} = TRUE } $tmpl->param();

  # build a list of emails to send emails to, unless DisableEmail is turned on,
  # there isnt an EmailOn for this error code or the MTA_Prog is not defined.
  my (@email) = ();
  unless ($disable_email) {
    if (exists $email_on{$self->{error_code}} and $MTA_Prog) {
      my @email_to = $r->dir_config()->get('EmailTo');
      if (@email_to) {
        # push the emails specified in EmailTo
        foreach my $email (@email_to) {
          next unless ($email);
          push(@email, $email);
        }
      }
      my $email_server_admin = $r->dir_config('EmailServerAdmin');
      if ($email_server_admin) {
        # default to adding the server_admin to the @email if there are no
        # EmailOn's or EmailServerAdmin is on
        my $server_admin = $s->server_admin();
        unless (grep(/^\Q$server_admin\E$/, @email)) {
          push(@email, $server_admin);
        }
      }
      if (exists $params{'webmaster_email'}) {
        # if a webmaster_email is specified add it to the @email, unless it
        # already exists in the array
        my $webmaster_email = $tmpl->param('webmaster_email');

        # lets not add the webmaster_email if its in EmailTo
        unless (grep(/^\Q$webmaster_email\E$/, @email)) {
          push(@email, $webmaster_email);
        }
      }
    }
  }

  my $notes = ($r->prev()) ? $r->prev()->notes() : undef;

  # set the current error_code's TMPL_IF on (if the TMPL_IF exists)
  #  i.e. <TMPL_IF NAME="404">
  if (exists $params{$self->{error_code}}) {
    $tmpl->param( $self->{error_code} => TRUE );
  } elsif (exists $params{'unknown_error'}) {
    $tmpl->param( unknown_error => TRUE );
  }
  # set the error_code TMPL_VAR
  #   i.e. <TMPL_VAR NAME="error_code"> (which is substituted with 404)
  if (exists $params{error_code}) {
    $tmpl->param( error_code => $self->{error_code} );
  }
  # set the error_note if its defined
  if (exists $params{'error_notes'} 
  and $notes and exists $notes->{'error-notes'}) {
    $tmpl->param( error_notes => $notes->{'error-notes'} );
  }

  # load the 'date_format' from the template if its set
  if (exists $params{date_format}) {
    $date_format = $tmpl->param('date_format');
  }

  # make the date string (formatted or default)
  my $formatted_date;
  if (exists $params{date}) {
    my $date = new Class::Date(time);
    if ($date_format) {
      $formatted_date = $date->strftime($date_format);
    } else {
      $formatted_date = $date->string();
    }
    $tmpl->param( date => $formatted_date );
  }

  # build the 'requestor' TMPL_VAR (made up from the remote host/ip)
  my $requestor;
  my $remote_host = $c->remote_host();
  my $remote_ip   = $c->remote_ip();
  if ($remote_host and $remote_ip) {
    $requestor = $remote_host. ' ('. $remote_ip. ')';
  } elsif ($remote_ip) {
    $requestor = $remote_ip;
  } else {
    $requestor = 'unknown';
  }

  # use the r->user in the requestor if its available
  if ($c->user()) {
    $requestor = $c->user(). ' ('. $requestor. ')';
  }

  if (exists $params{requestor}) {
    $tmpl->param( requestor => $requestor );
  }

  # build the 'base_url' TMPL_VAR (made up from the server_name etc)
  my $base_url;
  if (exists $ENV{'HTTPS'} and $ENV{'HTTPS'}) {
    $base_url = 'https://';
  } else {
    $base_url = 'http://';
  }
  $base_url .= $s->server_hostname();

  if (exists $params{base_url}) {
    $tmpl->param( base_url => $base_url );
  }

ErrorControl.pm  view on Meta::CPAN

  foreach my $path (@paths) {
    if (defined $error_code) {
      if (-f $path. '/'. $error_code) {
        return $path. '/'. $error_code;
      } elsif (-f $path. '/'. $error_code. '.html') {
        return $path. '/'. $error_code. '.html';
      } elsif (-f $path. '/'. $error_code. '.tmpl') {
        return $path. '/'. $error_code. '.tmpl';
      }
    }
    if (-f $path. '/allerrors') {
      return $path. '/allerrors';
    } elsif (-f $path. '/allerrors.html') {
      return $path. '/allerrors.html';
    } elsif (-f $path. '/allerrors.tmpl') {
      return $path. '/allerrors.tmpl';
    }
  }

  if (exists $self->{default_template} and $self->{default_template}) {
    if (-f $self->{default_template}) {
      return $self->{default_template};
    } elsif (-f $self->{template_dir}. '/'. $self->{default_template}) {
      return $self->{template_dir}. '/'. $self->{default_template};
    } elsif (-f $self->{document_root}. '/'. $self->{default_template}) {
      return $self->{document_root}. '/'. $self->{default_template};
    }
  }
}
# }}}

1;

END { }

__END__

=pod

=head1 NAME

Apache::ErrorControl - Apache Handler for Templating Apache Error Documents

=head1 SYNOPSIS

in your httpd.conf

  PerlModule Apache::ErrorControl

  <Location /error>
    SetHandler perl-script
    PerlHandler Apache::ErrorControl

    PerlSetVar TemplateDir /usr/local/apache/templates
  </Location>

  ErrorDocument 400 /error
  ErrorDocument 401 /error
  ErrorDocument 402 /error
  ErrorDocument 403 /error
  ErrorDocument 404 /error
  ErrorDocument 500 /error

in your template (allerrors.tmpl):

  <TMPL_SET NAME="webmaster_email">dj@boxen.net</TMPL_SET>

  <HTML>
    <HEAD>
      <TITLE>Error <TMPL_VAR NAME="error_code"></TITLE>
    </HEAD>

    <BODY>
      <TMPL_IF NAME="404">
        <H1>Error 404: File Not Found</H1>
        <HR><BR>

        <p>The file you were looking for is not here, we must have
          deleted it - or you just might be mentally retarded</p>
      </TMPL_IF>
      <TMPL_IF NAME="500">
        <H1>Error 500: Internal Server Error</H1>
        <HR><BR>

        <p>We are currently experiencing problems with our server,
          please call back later</p>
      </TMPL_IF>

      <p><b>Time of Error:</b> <TMPL_VAR NAME="date"></p>
      <p><b>Requested From:</b> <TMPL_VAR NAME="requestor"></p>
      <p><b>Requested URL:</b> <TMPL_VAR NAME="request_url"></p>
      <p><b>Website Base URL:</b> <TMPL_VAR NAME="base_url"></p>
      <p><b>Contact Email:</b> support@mouse.com</p>
    </BODY>
  </HTML>

=head1 DESCRIPTION

This mod_perl content handler will make templating your ErrorDocument pages
easy. Basically you add a couple of entries to your httpd.conf file restart
apache, make your template and your cruising.

The module uses L<HTML::Template::Set> (which is essentially HTML::Template
with the ability to use TMPL_SET tags). So for help templating your error
pages please see: L<HTML::Template::Set> and L<HTML::Template>. Also check
the B<OPTIONS> section of this documentation for available TMPL_SET/TMPL_IF
and TMPL_VAR params.

By default when an error 500 (internal server error) is encountered an error
email is sent about it. the addresses emailed depend on the options specified.
please see the B<OPTIONS> section for help configuring this. you can also
extend the system to send error emails on more than just internal server
errors, please see the B<EmailOn> option for how to do this.

Templates are looked up in the following order: the I<document root> is scanned
for 'allerrors', 'allerrors.tmpl', I<error code> or I<error code>.tmpl. if
no templates are found the B<TemplateDir> is scanned for the same files. if
no templates are found the B<DefaultTemplate> is used and if its not set
the system 'B<die>s'.

Because so many places are checked for the templates its possible to have
one global error handler and have different templates for each virtual host
and also allow for defaults. It also means you can have a general catch-all
template (allerrors/allerrors.tmpl) as well as single templates (i.e. 500.tmpl).
Generally I just use allerrors.tmpl and use TMPL_IF's to display custom content
per error message, but you can set it up any way you want.

=head1 MOTIVATION

I wanted to write a mod_perl handler so I could template error messages.
I also wanted to make it extensible enough that I could have a global error
handler and it would cover all the virtual webservers and have different
templates for each of them - ergo - the birth of Apache::ErrorControl.

=head1 TESTING

ErrorControl.pm  view on Meta::CPAN


=back

=head2 TEMPLATE

=head3 TMPL_SET

=over 4

=item *

B<webmaster_email> - this paramater allows you to set add an email address
to send error messages to on a per-template basis.

  <TMPL_SET NAME="webmaster_email">dj@abc.com</TMPL_SET>

=item *

B<date_format> - this option overrides the B<DateFormat> HTTPD CONF entry
on a per-template basis.

  <TMPL_SET NAME="date_format">%d-%m-%Y %H:%M:S</TMPL_SET>

=back

=head3 TMPL_VAR / TMPL_IF

=over 4

=item *

B<requestor> - the requestor of the page either "user (hostname (ip))",
"user (ip)", "hostname (ip)" or "ip", depending if their ip resolves or not.
NB: unless you have "HostnameLookups On" in you httpd.conf you will never
see the users hostname.

  <TMPL_VAR NAME="requestor">

=item *

B<base_url> - the base url of the website, i.e. http://www.abc.com

  <TMPL_VAR NAME="base_url">

=item *

B<request_url> - the full request url including arguments, i.e.
http://www.abc.com/stuff/stuffed.cgi?abc=yes&no=yes

  <TMPL_VAR NAME="request_url">

=item *

B<date> - the date/time of the error (format depending on the
B<DateFormat> / B<date_format>.

  <TMPL_VAR NAME="date">

=item *

B<error_code> - the I<error code>, i.e. 404, 403, 500 etc

  <TMPL_VAR NAME="error_code">

=item *

B<*error_code*> - the actual I<error code> itself is set as a param (if the
param exists). if there is no TMPL_IF or TMPL_VAR
defined for the I<error code> encountered the param B<unknown_error> is
turned on (obviously only if it too is defined).
personally I cant see why anyone would ever need B<unknown_error> but ive
added it here anyways.

  <TMPL_IF NAME="404">
    Error 404 - File Not Found
  </TMPL_IF>

=item *

B<unknown_error> - if the B<*error_code*> is not defined as a TMPL_VAR or
TMPL_IF and there is a TMPL_IF / TMPL_VAR by the name of B<unknown_error> it is
set to TRUE (1). as mentioned above I cannot see why anyone would want this.

  <TMPL_IF NAME="unknown_error">
    Error <TMPL_VAR NAME="error_code"> - Unknown
  </TMPL_IF>

=item *

B<error_notes> - the error-notes (C<$r-E<gt>prev()-E<gt>notes('error-notes')>,
see L<Apache>). this is more useful in say a staging environment that has
mod_perl applications. it is the error message, for mod_perl applications
it includes the complete error message but for normal cgi applications
it just includes 'premature end of script headers'.

  <TMPL_IF NAME="error_notes">
    <h2>Error Notes</h2>
    <pre>
    <TMPL_VAR NAME="error_notes">
    </pre>
  </TMPL_IF>

=item *

B<env_*> - all env_* params are available, see L<HTML::Template::Set>
for details.

  <TMPL_VAR NAME="env_server_name">

=back

=head1 OPTIMISATION

As the module loads L<HTML::Template::Set> with the B<cache> option all
templates are automatically cached. To further increase the performance you
should look at the documentation of L<HTML::Template> for instructions on how
to pre-load your templates under mod_perl. If you do not use this methodology
L<HTML::Template> will only cache your templates per apache child process and
upon using them (i.e. before you will notice the benefits of using cache each
apache child process needs to load each template).

=head1 CAVEATS

This module may be missing something that you feel it needs, it has
everything I have wanted though. If you want a feature added please email me
or send me a patch.

One thing to note is that if you go to your handler directly
(i.e. http://www.abc.com/error) the system will return I<FORBIDDEN>. this will
also happen if your I<error code> is ever 200 (I<OK>) (which it should never be
unless you are accessing the handler directly). If you are interested in
testing your templates see the B<TESTING> section.

=head1 BUGS



( run in 0.848 second using v1.01-cache-2.11-cpan-39bf76dae61 )