package Text::MicroMason::TemplateDir;

use strict;
use File::Spec;
use Cwd;

######################################################################

sub prepare {
    my ( $self, $src_type, $src_data ) = @_;
    
    return $self->NEXT('prepare', $src_type, $src_data ) 
        unless $src_type eq 'file';
    
    my $path = $self->resolve_path($src_data);
    return $self->NEXT('prepare', 'file' => $path, source_file => $path );
}

sub resolve_path {
    my ($self, $src_data) = @_;

    my $current = $self->{source_file};
    my $rootdir = $self->template_root();
    
    my $base = File::Spec->file_name_is_absolute($src_data) || 
        ! $current ? $rootdir : ( File::Spec->splitpath( $current ) )[1];

    return File::Spec->catfile( $base, $src_data );
}

sub template_root {
    my $self = shift;
    return $self->{template_root} || '.' unless @_;
    
    $self->{template_root} = shift;
}

sub cache_key {
    my $self = shift;
    my ($src_type, $src_data, %options) = @_;
    return $self->NEXT('cache_key', @_) unless $src_type eq 'file';
    return  $self->resolve_path($src_data);
}


# $contents = $mason->read_file( $filename );
sub read_file {
    my ( $self, $file ) = @_;
    
    if ( my $root = $self->{strict_root} ) {
        $root = $self->template_root if $root eq '1';
        my $path = Cwd::abs_path($file);
        my $root_path = Cwd::abs_path($root)
            or $self->croak_msg("Text::MicroMason::TemplateDir: Strict root '$root' doesn't seem to exist"); 
        # warn "Checking for '$root_path' in '$path' (file $file)\n";
        ( $path =~ /\A\Q$root_path\E/)
            or $self->croak_msg("Text::MicroMason::TemplateDir: Template '$path' not in required base path '$root_path'");
    }
    
    return $self->NEXT('read_file', $file );
}

######################################################################

1;

######################################################################

=head1 NAME

Text::MicroMason::TemplateDir - Use Base Directory and Relative Paths


=head1 SYNOPSIS

Instead of using this class directly, pass its name to be mixed in:

    use Text::MicroMason;
    my $mason = Text::MicroMason->new( -TemplateDir, template_root=>'/foo' );

Use the standard compile and execute methods to parse and evaluate templates:

  print $mason->compile( file=>$filepath )->( 'name'=>'Dave' );
  print $mason->execute( file=>$filepath, 'name'=>'Dave' );

Templates stored in files are looked up relative to the template root:

    print $mason->execute( file=>"includes/greeting.msn", 'name'=>'Charles');

When including other files into a template you can use relative paths:

    <& ../includes/greeting.msn, name => 'Alice' &>


=head1 DESCRIPTION

This module changes the resolution of files passed to compile() and execute() to be relative to a base directory path or to the currently executing template.


=head2 Supported Attributes

=over 4

=item template_root

Base directory from which to find templates.

=item strict_root

Optional directory beyond which not to read files. If set to 1, uses
template_root, Causes read_file to croak if any filename outside of the
root is provided. (Note that this is not a chroot jail and only affects
attempts to load a file as a template; for greater security see the
chroot() builtin and L<Text::MicroMason::Safe>.)

=back

=head2 Private Methods

=over 4

=item prepare

Intercepts uses of file templates and applies the base-path adjustment.

=item read_file 

Intercepts file access to check for strict_root.

=back

=head2 EXCEPTIONS

The following additional exceptions are generated by
Text::MicroMason::TemplateDir when appropriate:

=over 4

=item *

Text::MicroMason::TemplateDir: Strict root '%s' doesn't seem to exist

The strict_root directory (or template_root if strict_root is '1')
doesn't seem to exist. Strict root checking uses Cwd's abs_path(), and
requires the strict_root directory to exist at the time the check is
performed.

=item *

Text::MicroMason::TemplatePath: Template '%s' not in required base path '%s'

The template found in the configured template path was not within the
configured strict_root directory. This may be caused by requesting an
absolute template filename not within strict_root, or by specifying a
strict_root which does not match the configured template path.

=back

=head1 SEE ALSO

For an overview of this templating framework, see L<Text::MicroMason>.

This is a mixin class intended for use with L<Text::MicroMason::Base>.

For distribution, installation, support, copyright and license 
information, see L<Text::MicroMason::Docs::ReadMe>.

=cut

