Apache-JAF
view release on metacpan or search on metacpan
lib/Apache/JAF.pm view on Meta::CPAN
use Apache::Request ();
use Apache::Constants qw( :common REDIRECT );
use Apache::File ();
use Data::Dumper qw( Dumper );
use File::Find ();
our $WIN32 = $^O =~ /win32/i;
our $RX = $WIN32 ? qr/\:(?!(?:\/|\\))/ : qr/\:/;
our $VERSION = do { my @r = (q$Revision: 0.21 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
# Constructor
################################################################################
sub new {
my ($ref, $r) = @_;
my $self = {};
#
# use as template provider
#
if (ref($r) eq 'HASH') {
$self = $ref->SUPER::new($r);
bless ($self, $ref);
return $self;
}
#
# use as framework
#
$r = Apache::Request->instance($r);
bless ($self, $ref);
# r - request (filter-aware)
$self->{filter} = lc $r->dir_config('Filter') eq 'on';
$self->{r} = $self->{filter} ? $r->filter_register() : $r;
my $prefix = $r->dir_config('Apache_JAF_Prefix') || 0;
# prefix - path|number of subdirs that must be removed from uri
$prefix = ($prefix =~ /^\/(.*)$/) ? scalar(my @tmp = split '/', $1) : int($prefix);
# uri - reference to array that contains uri plitted by '/'
my @uri = split '/', $self->{r}->uri;
shift @uri if $prefix;
splice @uri, 0, ($prefix || 1);
if (@uri) {
$uri[-1] =~ s/\.html?$//i;
my $i = 0;
while ($i < @uri) {
splice @uri, $i, 2 if $uri[$i] =~ /^\w+:$/ && !$uri[$i+1];
$i++;
}
}
$self->{uri} = \@uri;
# res - result hash, that passed to the template
$self->{res} = {};
# for complex-name-handlers change '_' in handler name to '/' to provide
# real document tree in Templates folder
$self->{expand_path} = 1;
# Level of warnings that will be written to the server's error_log
# every next level includes all options from previous
# 0: critical errors only
# 1: request processing line
# 2: client request
# 3: response headers
# 4: template variables
# 9: loading additional handlers
# 10: processed template
$self->{debug_level} = $self->{r}->dir_config('Apache_JAF_Debug') || 0;
# Default response status and content-type
$self->{status} = NOT_FOUND;
$self->{type} = 'text/html';
# Default template and includes extensions
$self->{template_ext} = '.html';
$self->{include_ext} = '.inc';
$self->{default_include} = 'default';
# pre- and post-process templates (without extensions)
$self->{header} = 'header';
$self->{footer} = 'footer';
$self->{pre_chomp} = $self->{post_chomp} = $self->{trim} = 1;
# Templates folder
$self->{templates} = $self->{r}->dir_config('Apache_JAF_Templates');
# Modules folder
$self->{modules} = $self->{r}->dir_config('Apache_JAF_Modules');
# Compiled templates folder
$self->{compile_dir} = $self->{r}->dir_config('Apache_JAF_Compiled') || '/tmp';
# This method must be implemented in derived class and must
# provide $self->{handler} property
$self->{handler} = $self->setup_handler();
return undef unless $self->{handler};
# Log real and uri without prefix
$self->warn(1, "Starting $ref for " . $self->{r}->uri);
$self->warn(1, "URI: /" . join '/', @{$self->{uri}});
$self->warn(2, 'Request: ' . $self->{r}->as_string());
# Load handlers if $HANDLERS_LOADED flag is unset or
# we are in debug mode ($self->{debug_level} > 0)
# reload modified templates also
my $package = ref $self;
{ no strict 'refs';
$self->load_handlers($package, $self->{modules}) if $self->{debug_level} || !${ "${package}::HANDLERS_LOADED" };
$self->load_templates($package, $self->{templates}, 1) if $self->{debug_level} && ${ "${package}::SELF_PROVIDER" };
}
# {page} key in result hash equals to current handler
$self->{res}{page} = $self->{handler};
return $self
}
# Load handlers and temlates during compile-time...
lib/Apache/JAF.pm view on Meta::CPAN
$self = $self->new($r) unless ref($self);
unless ($self) {
$self->warn(0, "Can't create handler object!");
return SERVER_ERROR;
}
my $result;
$self->{status} = $self->site_handler();
$result = $self->process_template() if $self->{status} == OK && $self->{type} =~ /^text/ && !$self->{r}->header_only;
if ($self->{status} == OK) {
$self->{r}->send_http_header($self->{type});
return $self->{status} if $self->{r}->header_only;
if ($self->{type} =~ /^text/) {
#
# Apache::Filter->print() must(?) be patched for printing referenced scalars
#
$self->{r}->print($self->{filter} ? $$result : $result);
} else {
#
# if handler set $self->{type} other than text/(html|plain)
# then data must be send to the client by on_send_..._data method
#
my $method = "on_send_${\($self->{handler})}_data";
$self->$method(@{$self->{uri}}) if $self->can($method);
}
}
$self->warn(3, 'Response headers: ' . Dumper {($self->{status} == OK) ? $self->{r}->headers_out() : $self->{r}->err_headers_out()});
$self->warn(1, sprintf 'Request processed in %0.3f sec', Time::HiRes::time() - $time) if $time;
my $status = $self->{status};
undef $result;
undef $self;
return $status
}
# Global Apache::JAF handler. If you want some stuff before (and|or) after
# running handler you must override it like that:
#
# sub site_handler {
# my $self = shift;
#
# [before stuff goes here]
#
# $self->{status} = $self->SUPER::site_handler(@_);
#
# [after stuff goes here]
#
# return $self->{status}
# }
################################################################################
sub site_handler {
my ($self) = @_;
my ($method, $last_modified, $cache, $mtime);
foreach (($method, $last_modified, $cache) = map { $_ . $self->{handler} } qw(do_ last_modified_ cache_)) {
$_ =~ tr{/}{_} if $self->{expand_path};
}
$self->warn(1, "Handler method: $method");
$mtime = $self->last_modified(@{$self->{uri}});
$mtime = $self->$last_modified(@{$self->{uri}}) if $self->can($last_modified);
if ($mtime) {
$self->{r}->update_mtime($mtime);
$self->{r}->set_last_modified;
$self->{status} = $self->{r}->meets_conditions;
return $self->{status} unless $self->{status} == OK;
}
if ($self->can($method)) {
#
# process template with handler
#
$self->warn(1, "Can do $method: Y");
my $cstat = $self->cache(@{$self->{uri}});
$cstat = $self->$cache(@{$self->{uri}}) if $self->can($cache);
if ($cstat) {
$self->{status} = $cstat;
} else {
$self->{status} = $self->$method(@{$self->{uri}})
}
$self->{handler} =~ tr{_}{/} if $self->{expand_path} && $self->{type} =~ /^text/;
$self->warn(1, 'Content-type: ' . $self->{type});
} else {
#
# process template without handler (defaults variables only, header and footer)
#
$self->warn(1, "Can do $method: N");
$self->{status} = OK unless $self->{status} == SERVER_ERROR;
}
return $self->{status};
}
### Additional utility methods for getting params
sub param {
my ($self, $p) = @_;
my @params = map { $_ = JAF::Util::trim($_); length > 0 ? $_ : undef} ($self->{r}->param($p));
return $params[0];
}
sub upload_fh {
my ($self, $p) = @_;
if($self->param($p)) {
my $upl = $self->{r}->upload($p);
return $upl->fh if($upl && $upl->fh)
}
return undef
}
### Methods for simplify handlers for download content instead of viewing it
sub disable_header { undef $_[0]->{header} }
sub disable_footer { undef $_[0]->{footer} }
sub disable_header_footer { $_[0]->disable_header(); $_[0]->disable_footer(); }
sub download_type { $_[0]->{type} = 'application/x-force-download'; }
sub download_it { $_[0]->disable_header_footer(); $_[0]->download_type(); }
### methods for JAF database editing
sub default_record_edit {
my ($self, $tbl, $options) = @_;
if ($self->{r}->method() eq 'POST' && $self->param('act') eq 'edit') {
$tbl->update({
$tbl->{key} => $self->param($tbl->{key}),
map {defined $self->{r}->param($_) ? ($_ => $self->param($_)) : $options->{checkbox} && exists $options->{checkbox}{$_} ? ($_ => $options->{checkbox}{$_}) : ()} @{$tbl->{cols}}
}, $options);
}
}
sub default_table_edit {
my ($self, $tbl, $options) = @_;
if ($self->{r}->method() eq 'POST' && $self->param('act') eq 'edit') {
for (my $i=1; defined $self->param("$tbl->{key}_$i"); $i++) {
$tbl->delete({
$tbl->{key} => $self->param("$tbl->{key}_$i")
}, $options) if $self->param("dowhat_$i") eq 'del';
$tbl->update({
$tbl->{key} => $self->param("$tbl->{key}_$i"),
lib/Apache/JAF.pm view on Meta::CPAN
Usually this "mini-handler" is I<very> simple. It have to be implemented as package method with
C<do_I<E<lt>handler nameE<gt>>> name. You have to fill C<$self-E<gt>{res}> hash with
result and return Apache constant according to handler's logic (C<OK>, C<NOT_FOUND>,
C<FORBIDDEN> and so on). The sample is shown in L<"SYNOPSIS">.
=item 4
If the previous step fulfills correctly, and C<$self-E<gt>{type}> property is C<text/*> then
result of processing template returns to client. If type of result is not
like text, one more method is needed to implement: C<on_send_I<E<lt>handeler nameE<gt>>_data>.
It must return binary data to client. This way you may create handlers for
dynamic generation of images, M$ Excel workbooks and any other type of data.
=back
=head2 Apache::JAF methods
=over 4
=item setup_handler
This method you must override in your Apache::JAF descendant. You must return handler's
name (that will be called as I<do_E<lt>handler nameE<gt>> method later) from it depending
on URI requested by user. You may set site-wide properties such as I<debug_level>, I<header>
or I<footer>, templates and includes extensions and so on. If handler name depends on
application logic implemented in modeller then you have to create modeller in this method
and store it in I<m> property for later use.
The primary I<setup_handler> is shown in L<"SYNOPSIS">.
=item site_handler
You can override this method to provide common tasks for each of your page-handlers. For
example you may create instance of modeller class, provide some custom
authorization/authentication or sessions handling and so on. You must call
C<$selfE<gt>SUPER::site_handler> and return C<$self-E<gt>{status}> from it.
=back
=head2 Apache::JAF properties
=over 4
=item r
Current C<Apache::Request> object.
=item filter
Using C<Apache::Filter> flag.
=item uri
Reference to the array of current URI (splitted by slash). Usually you need to modify
it in L<"setup_handler"> method to determine page's handler name. Remained array will be passed
to the page-handler method as a list of parameters.
=item res
Hash reference that holds page-handler results.
=item expand_path
Boolean flag for complex-name-handlers changes '_' to '/' in handler's name.
It provides real-like document tree in the templates folder.
=item debug_level
Look at B<Apache_JAF_Debug> in L<"CONFIGURATION"> section.
=item status
Default handler status is C<NOT_FOUND>.
=item type
Default content-type is C<text/html>. You can call C<$self-E<gt>download_type()>
for set unexisting MIME-type to force browser download content instead of viewing it.
=item template_ext, include_ext
Default template extension is C<.html>.
Default include template extension is C<.inc>.
=item default_include
Site-wide include template. Default value is... C<default>.
=item header, footer
Site-wide pre- and post-include templates. Defalut values are C<header> and C<footer>.
I<Note:>You must undef this properies if you want create page-template without it. For
example for page in pop-up window (C<disable_header>, C<disable_footer>, and
C<disable_header_footer> methods).
=item templates
Path to the templates folder. You may have different sets of templates for different
views of results generated by your page-handlers.
=item handler
Result of C<setup_handler> method is stored here for later use.
=item I<other properites>
For internal use only.
=back
=head2 Implementing handlers
Page handlers are simple.
Their methods are with C<do_E<lt>handler nameE<gt>> name. You have to
analyse given parameters, fill out C<$self-E<gt>{res}> hash with handler results that will
be processed with template and return one of C<Apache::Constants>. Usually it's C<OK>,
but may be C<NOT_FOUND> if parameters passed to handlers are invalid for some reason.
Look into F<examples/*> folder in the distribution package for some guidelines.
=head2 Templates structure and syntax
( run in 0.788 second using v1.01-cache-2.11-cpan-97f6503c9c8 )