package Apache::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 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'); $Apache::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+' : '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 wantarray ? ( $lang_tmpl, $skip_xpath_content ) : $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;