package Apache::AxKit::Language::LibXSLTEnhanced; #use 5.008003; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use Apache::AxKit::Language::LibXSLTEnhanced ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our $VERSION = '0.02'; use strict; use vars qw/@ISA $VERSION %DEPENDS/; use XML::LibXSLT 1.30; use XML::LibXML; use Apache; use Apache::Request; use Apache::AxKit::Language; use Apache::AxKit::Provider; use Apache::AxKit::LibXMLSupport; @ISA = 'Apache::AxKit::Language'; $VERSION = 1.0; # this fixes a CPAN.pm bug. Bah! my %style_cache; sub reset_depends { %DEPENDS = (); } sub add_depends { $DEPENDS{shift()}++; } sub get_depends { return keys %DEPENDS; } sub handler { my $class = shift; my ($r, $xml, $style, $last_in_chain) = @_; my ($xmlstring, $xml_doc); AxKit::Debug(7, "[LibXSLT] getting the XML"); if (my $dom = $r->pnotes('dom_tree')) { $xml_doc = $dom; delete $r->pnotes()->{'dom_tree'}; } else { $xmlstring = $r->pnotes('xml_string'); } my $parser = XML::LibXML->new(); $parser->expand_entities(1); local($XML::LibXML::match_cb, $XML::LibXML::open_cb, $XML::LibXML::read_cb, $XML::LibXML::close_cb); Apache::AxKit::LibXMLSupport->reset(); local $Apache::AxKit::LibXMLSupport::provider_cb = sub { my $r = shift; my $provider = Apache::AxKit::Provider->new_content_provider($r); add_depends($provider->key()); return $provider; }; if (!$xml_doc && !$xmlstring) { $xml_doc = $xml->get_dom(); } elsif ($xmlstring) { $xml_doc = $parser->parse_string($xmlstring, $r->uri()); } $xml_doc->process_xinclude(); AxKit::Debug(7, "[LibXSLT] parsing stylesheet"); my $stylesheet; my $cache = $style_cache{$style->key()}; if (ref($cache) eq 'HASH' && !$style->has_changed($cache->{mtime}) && ref($cache->{depends}) eq 'ARRAY') { AxKit::Debug(8, "[LibXSLT] checking if stylesheet is cached"); my $changed = 0; DEPENDS: foreach my $depends (@{ $cache->{depends} }) { my $p = Apache::AxKit::Provider->new_style_provider($r, key => $depends); if ( $p->has_changed( $cache->{mtime} ) ) { $changed = 1; last DEPENDS; } } if (!$changed) { AxKit::Debug(7, "[LibXSLT] stylesheet cached"); $stylesheet = $style_cache{$style->key()}{style}; } } if (!$stylesheet || ref($stylesheet) ne 'XML::LibXSLT::Stylesheet') { reset_depends(); my $style_uri = $style->apache_request->uri(); AxKit::Debug(7, "[LibXSLT] parsing stylesheet $style_uri"); my $style_doc = $style->get_dom(); local($XML::LibXML::match_cb, $XML::LibXML::open_cb, $XML::LibXML::read_cb, $XML::LibXML::close_cb); Apache::AxKit::LibXMLSupport->reset(); local $Apache::AxKit::LibXMLSupport::provider_cb = sub { my $r = shift; my $provider = Apache::AxKit::Provider->new_style_provider($r); add_depends($provider->key()); return $provider; }; $stylesheet = XML::LibXSLT->parse_stylesheet($style_doc); foreach( $r->dir_config->get("LibXSLTFunctionsModule") ) { eval("require $_" ); if( $@ ) { die "Could not load module.\n $@"; } my $function_lib = $_->new(); foreach( $function_lib->getFunctions() ) { XML::LibXSLT->register_function( $function_lib->getNamespace(), $_->[0], $_->[1] ); } } unless ($r->dir_config('AxDisableXSLTStylesheetCache')) { $style_cache{$style->key()} = { style => $stylesheet, mtime => time, depends => [ get_depends() ] }; } } # get request form/querystring parameters my @params = fixup_params($class->get_params($r)); AxKit::Debug(7, "[LibXSLT] performing transformation"); my $results = $stylesheet->transform($xml_doc, @params); AxKit::Debug(7, "[LibXSLT] transformation finished, creating $results"); if ($last_in_chain) { AxKit::Debug(8, "[LibXSLT] outputting to \$r"); if ($XML::LibXSLT::VERSION >= 1.03) { my $encoding = $stylesheet->output_encoding; my $type = $stylesheet->media_type; $r->content_type("$type; charset=$encoding"); } $stylesheet->output_fh($results, $r); } AxKit::Debug(7, "[LibXSLT] storing results in pnotes(dom_tree) ($r)"); $r->pnotes('dom_tree', $results); # warn "LibXSLT returned $output \n"; # print $stylesheet->output_string($results); return Apache::Constants::OK; } sub fixup_params { my @results; while (@_) { push @results, XML::LibXSLT::xpath_to_string( splice(@_, 0, 2) ); } return @results; } 1; # Preloaded methods go here. 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME Apache::AxKit::Language::LibXSLTEnhanced - AxKit extension to load perl callbacks for XSL =head1 SYNOPSIS AxAddStyleMap text/xsl Apache::AxKit::Language::LibXSLTEnhanced PerlAddVar LibXSLTFunctionsModule BestSolution::AddonFunctions =head1 DESCRIPTION This module is working completly like Language::LibXSLT but it support registering perl-functions which can be used in XSL-Stylesheets. To add a Perl-Callbacks you have to use PerlAddVar as shown in synopsis. The module loaded has to inherit from L =head2 EXPORT None by default. =head1 SEE ALSO L, L, L =head1 AUTHOR Tom Schindl, Etom.schindl@bestsolution.atE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004 by Tom Schindl This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut