package Apache2::PageKit::Content;
# $Id: Content.pm,v 1.46 2004/05/03 13:48:29 borisz Exp $
use strict;
use vars qw($CONTENT $PKIT_VIEW $COMPONENT_ID_DIR $PAGE_ID $INCLUDE_MTIMES);
sub new {
my $class = shift;
my $self = { @_ };
bless $self, $class;
return $self;
}
sub generate_template {
my ($content, $page_id, $component_id, $pkit_view, $input_param_obj, $component_params) = @_;
unless(exists $INC{'XML/LibXSLT.pm'}){
eval {
require XML::LibXSLT;
};
if ($@) {
die "Cannot find template file $pkit_view/$component_id.tmpl or XML::LibXSLT is required to process $content->{content_dir}/$component_id.xml";
}
}
$CONTENT = $content;
$PKIT_VIEW = $pkit_view;
$PAGE_ID = $page_id;
($COMPONENT_ID_DIR = $component_id) =~ s![^/]*$!!;
$INCLUDE_MTIMES = $content->{include_mtimes};
# XSLT file
my $xml_file = "$content->{content_dir}/$component_id.xml";
unless(-f $xml_file){
die "Cannot find xml file $content->{content_dir}/$component_id.xml or
template file $pkit_view/$component_id.tmpl";
}
# my $xml_mtime = (stat($xml_file))[9];
# $INCLUDE_MTIMES->{$xml_file} = $xml_mtime;
my $parser = XML::LibXML->new( ext_ent_handler => \&open_uri );
# call backs so that we can note the mtimes of dependant files
$parser->match_callback(\&match_uri);
$parser->open_callback(\&open_uri);
$parser->close_callback(\&close_uri);
$parser->read_callback(\&read_uri);
my $xp = $parser->parse_file("/$component_id.xml");
my @pi_nodes = $xp->findnodes("processing-instruction('xml-stylesheet')");
my @stylesheet_hrefs;
for my $pi_node (@pi_nodes){
my $pi_str = $pi_node->getData;
my ($stylesheet_href) = ($pi_str =~ m!href="([^"]*)"!);
push @stylesheet_hrefs, $stylesheet_href;
}
# for now, just use first stylesheet... we'll add multiple stylesheets later
unless ($stylesheet_hrefs[0]){
die qq{must specify <?xml-stylesheet href="file.xsl"?> in $xml_file};
}
my $stylesheet_file = $stylesheet_hrefs[0];
# my $stylesheet_mtime = (stat(_))[9];
# $INCLUDE_MTIMES->{$stylesheet_file} = $stylesheet_mtime;
my $stylesheet_parser = XML::LibXML->new();
my $stylesheet_xp = $stylesheet_parser->parse_file($stylesheet_file);
# for caching pages including the params info (that way extrenous parameters
# won't be taken into account when counting)
# META: do i only need to cache top level params from top level stylesheet?
for my $node ($stylesheet_xp->findnodes(q{node()[name() = 'xsl:stylesheet']/node()[name() = 'xsl:param']})->get_nodelist){
my $param_name = $node->getAttribute('name');
$Apache2::PageKit::Content::PAGE_ID_XSL_PARAMS->{$PAGE_ID}->{$param_name} = 1;
}
my $xslt = XML::LibXSLT->new();
my $source = $xp; # we parsed the source xmlfile already
my $style_doc = $parser->parse_file($stylesheet_file);
my $stylesheet = $xslt->parse_stylesheet($style_doc);
my @params = map { $_, $input_param_obj->param($_) } $input_param_obj->param;
my $results = $stylesheet->transform($source, XML::LibXSLT::xpath_to_string( @params, %$component_params ));
# my $content_type = $stylesheet->media_type;
# my $encoding = $stylesheet->output_encoding;
my $output = $stylesheet->output_string($results);
return \$output;
}
sub process_template {
my ($content, $component_id, $template_ref) = @_;
my $lang_tmpl = {};
$INCLUDE_MTIMES = {};
# this pattern is not very accurate, but only as quick check if a run of XPathTemplate is needed
my $content_pattern = ( $content->{relaxed_parser} eq 'yes' ) ? '<(?:!--)?\s*CONTENT_(VAR|LOOP|IF|UNLESS)\s+' : '<CONTENT_(VAR|LOOP|IF|UNLESS)\s+';
if($$template_ref =~ m!$content_pattern!i){
# XPathTemplate template
my $xpt = HTML::Template::XPath->new( default_lang => $content->{default_lang},
root_dir => $content->{content_dir},
relaxed_parser => $content->{relaxed_parser},
template_class => $content->{template_class},
);
$lang_tmpl = $xpt->process_all_lang(xpt_scalarref => $template_ref,
xml_filename => "$component_id.xml");
my $file_mtimes = $xpt->file_mtimes;
while (my ($k, $v) = each %$file_mtimes){
# $v (mtime) can be undef in case where the content came from
# xpath document function. This is not changed in HTML::Template::XPath
# since otherwise the H:T:X try to put the file into his mtimes hash
# over and over
$content->{include_mtimes}->{$k} = $v if $v;
}
} else {
$lang_tmpl->{$content->{default_lang}} = $template_ref;
}
return $lang_tmpl;
}
sub _rel2abs {
my ($rel_uri) = @_;
if($rel_uri =~ m!\.xml!){
my $content_dir = $CONTENT->{content_dir};
if($rel_uri =~ m!^/!){
return "$content_dir$rel_uri";
} else {
# return relative to component_id_dir
my $abs_uri = "$content_dir/$COMPONENT_ID_DIR$rel_uri";
while ($abs_uri =~ s![^/]*/\.\./!!) {};
return $abs_uri;
}
} elsif($rel_uri =~ m!\.xsl!){
my $view_dir = $CONTENT->{view_dir};
my $stylesheet_file;
if($rel_uri =~ m!^/!){
$stylesheet_file = "$view_dir/$PKIT_VIEW$rel_uri";
unless( -f $stylesheet_file){
$stylesheet_file = "$view_dir/Default$rel_uri";
}
} else {
# return relative to component_id_dir
$stylesheet_file = "$view_dir/$PKIT_VIEW/$COMPONENT_ID_DIR$rel_uri";
while ($stylesheet_file =~ s![^/]*/\.\./!!) {};
unless( -f $stylesheet_file){
$stylesheet_file = "$view_dir/Default/$COMPONENT_ID_DIR$rel_uri";
while ($stylesheet_file =~ s![^/]*/\.\./!!) {};
}
}
die "Stylesheet $stylesheet_file doesn't exist" unless ( -f $stylesheet_file );
return $stylesheet_file;
} else {
die "$rel_uri does not end in .xml or .xsl. All Content XML files must have
xml suffix and all View XSLT files must have xsl suffix.";
}
}
sub match_uri {
my $uri = shift;
return $uri !~ /(^\w+:)|(catalog$)/;
}
sub open_uri {
my $uri = shift;
my $abs_uri = _rel2abs($uri);
open my $xml, "$abs_uri" or die "XML file $abs_uri doesn't exist";
binmode $xml;
local($/) = undef;
my $xml_str = <$xml>;
close $xml;
my $mtime = (stat(_))[9];
$INCLUDE_MTIMES->{$abs_uri} = $mtime;
# we avoid to use any XML::LibXML parser inside the callbackroutines.
return $xml_str;
}
sub read_uri {
return substr($_[0], 0, $_[1], "");
}
sub close_uri {}
1;