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 )