Template-XML-2.17/0000755000175000017500000000000010563116312013216 5ustar abwabw00000000000000Template-XML-2.17/t/0000755000175000017500000000000010563116312013461 5ustar abwabw00000000000000Template-XML-2.17/t/xml/0000755000175000017500000000000010563116312014261 5ustar abwabw00000000000000Template-XML-2.17/t/xml/example.xml0000644000175000017500000000041210563113666016444 0ustar abwabw00000000000000
Template-XML-2.17/t/test/0000755000175000017500000000000010563116312014440 5ustar abwabw00000000000000Template-XML-2.17/t/test/xml/0000755000175000017500000000000010563116312015240 5ustar abwabw00000000000000Template-XML-2.17/t/test/xml/testfile.xml0000644000175000017500000000036310563113666017614 0ustar abwabw00000000000000
Template-XML-2.17/t/test/xml/example.rdf0000644000175000017500000000122610563113666017402 0ustar abwabw00000000000000 Template Toolkit XML::RSS Plugin http://template-toolkit.org/plugins/XML/RSS The XML::RSS Plugin for the Template Toolkit Test Image http://www.myorg.org/images/test.png I Read the News Today http://oh.boy.com/ I am the Walrus http://goo.goo.ga.joob.org/ Template-XML-2.17/t/xpath.t0000644000175000017500000000771610563113666015016 0ustar abwabw00000000000000#============================================================= -*-perl-*- # # t/xpath.t # # Test the XML::XPath plugin. # # Written by Andy Wardley # # Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. # Copyright (C) 1998-2000 Canon Research Centre Europe Ltd. # # This is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # $Id: xpath.t,v 2.10 2003/04/11 12:28:28 darren Exp $ # #======================================================================== use strict; use lib qw( ./lib ../lib ); use Template; use Template::Test; use Cwd qw( abs_path ); $^W = 1; # I hate having to do this my $shut_up_warnings = $XML::XPath::VERSION; eval "use XML::XPath"; if ($@ || $XML::XPath::VERSION < 1.0) { skip_all('XML::XPath v1.0 or later not installed'); } # account for script being run in distribution root or 't' directory my $file = abs_path( -d 't' ? 't/test/xml' : 'test/xml' ); $file .= '/testfile.xml'; test_expect(\*DATA, undef, { 'xmlfile' => $file }); __END__ -- test -- [% TRY; USE xpath = XML.XPath('no_such_file'); xpath.find('/foo/bar'); CATCH; "ok"; END %] -- expect -- ok -- test -- [% USE xpath = XML.XPath(xmlfile) -%] [% FOREACH page = xpath.findnodes('/website/section/page') -%] page: [% page.getAttribute('title') %] [% END %] -- expect -- page: The Foo Page page: The Bar Page page: The Baz Page -- test -- [% USE xpath = XML.XPath(file => xmlfile) -%] [% FOREACH page = xpath.findnodes('/website/section/page') -%] page: [% page.getAttribute('title') %] [% END %] -- expect -- page: The Foo Page page: The Bar Page page: The Baz Page -- test -- [% USE xpath = XML.XPath(filename => xmlfile) -%] [% FOREACH page = xpath.findnodes('/website/section/page') -%] page: [% page.getAttribute('title') %] [% END %] -- expect -- page: The Foo Page page: The Bar Page page: The Baz Page -- test -- [% xmltext = BLOCK %]
This is the foo section, here is some bold text.
This is the bar section, here is some italic text
[% END -%] [% USE xpath = XML.XPath(xmltext) -%] ... [% FOREACH section = xpath.findnodes('/html/body/section') -%] [% section.string_value %] [% END %] -- expect -- ... This is the foo section, here is some bold text. This is the bar section, here is some italic text -- test -- [% xmltext = BLOCK -%] one two [% END -%] [% VIEW xview notfound='xmlstring' -%] [% BLOCK foo -%] FOO { [%- item.content(view) -%] } [% END -%] [% BLOCK bar -%] BAR(baz="[% item.getAttribute('baz') %]") { [%- item.content(view) -%] } [% END -%] [% BLOCK list -%] LIST: [%- item.content(view) -%] [% END -%] [% BLOCK item -%] * [% item.content(view) -%] [% END -%] [% BLOCK xmlstring; item.toString; END %] [% BLOCK text; item; END %] [% END -%] [%- USE xpath = XML.XPath(xmltext); foo = xpath.findnodes('/foo'); xview.print(foo); -%] -- expect -- FOO { BAR(baz="10") { LIST: * one * two } } -- test -- [% xmltext = BLOCK -%] one two [% END -%] [% VIEW xview notfound='xmlstring' -%] [% BLOCK item -%] * [% item.content(view) -%] [% END -%] [% BLOCK xmlstring; item.starttag; item.content(view); item.endtag; END %] [% BLOCK text; item; END %] [% END -%] [%- USE xpath = XML.XPath(xmltext); foo = xpath.findnodes('/foo'); xview.print(foo); -%] -- expect -- * one * two -- test -- [% xmltext = BLOCK -%] [% END -%] [% USE xp = XML.XPath(xml => xmltext); xp.find("/greeting[@type='hello']/@what") %] -- expect -- world -- test -- [% xmltext = BLOCK -%] world [% END -%] [% USE xp = XML.XPath(text => xmltext); xp.find("/hello"); %] -- expect -- world Template-XML-2.17/t/dom.t0000644000175000017500000001134710563113666014444 0ustar abwabw00000000000000#============================================================= -*-perl-*- # # t/dom.t # # Test the XML::DOM plugin. # # Written by Andy Wardley # # Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. # Copyright (C) 1998-2000 Canon Research Centre Europe Ltd. # # This is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # $Id: dom.t,v 2.8 2002/08/12 11:07:14 abw Exp $ # #======================================================================== use strict; use warnings; use lib qw( ./lib ../lib ); use Template; use Template::Test; use Template::Plugin::XML; use Cwd qw( abs_path ); #$Template::Test::DEBUG = 1; $Template::Test::PRESERVE = 1; # I hate having to do this my $shut_up_warnings = $XML::DOM::VERSION; # make sure we've got XML::DOM installed eval "use XML::DOM"; skip_all("XML::DOM v1.27 or later not installed") if $@ || $XML::DOM::VERSION < 1.27; # disable LibXML $Template::Plugin::XML::LIBXML = 0; # account for script being run in distribution root or 't' directory my $file = abs_path( -d 't' ? 't/test/xml' : 'test/xml' ); $file .= '/testfile.xml'; test_expect(\*DATA, undef, { 'xmlfile' => $file }); __END__ -- test -- -- name: libxml disabled -- [% USE XML; XML.libxml ? 'error: libxml should be disabled' : 'libxml disabled' %] -- expect -- libxml disabled #------------------------------------------------------------------------ # get XML::DOM via XML plugin #------------------------------------------------------------------------ -- test -- [% USE XML; TRY; file = XML.file(name = 'no_such_file.xml'); dom = file.dom; CATCH; error.as_string.replace('(?s::.*)', ''); END -%] -- expect -- XML.File error - failed to parse no_such_file.xml -- test -- [% USE XML; TRY; dom = XML.file('no_such_file.xml').dom; CATCH; error.as_string.replace('(?s::.*)', ''); END; -%] -- expect -- XML.File error - failed to parse no_such_file.xml -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(file => xmlfile) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page #------------------------------------------------------------------------ # get XML::DOM direct via XML.DOM plugin #------------------------------------------------------------------------ -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(xmlfile) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% USE XML; dom = XML.dom; doc = dom.parse(xmlfile) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(file => xmlfile) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(filename => xmlfile) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% global.xmltext = BLOCK %]
Hello World!
[% END -%] [% USE dom = XML.DOM -%] [% doc = dom.parse(global.xmltext) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(text => global.xmltext) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(xml => global.xmltext) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page #------------------------------------------------------------------------ # TODO: test views #------------------------------------------------------------------------ Template-XML-2.17/t/rss.t0000644000175000017500000000443010563113666014467 0ustar abwabw00000000000000#============================================================= -*-perl-*- # # t/rss.t # # Test the XML::RSS plugin. # # Written by Andy Wardley # # Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. # Copyright (C) 1998-2000 Canon Research Centre Europe Ltd. # # This is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # $Id: rss.t,v 2.4 2003/04/11 12:26:03 darren Exp $ # #======================================================================== use strict; use lib qw( lib ../lib ); use Template; use Template::Test; use Cwd qw( abs_path ); $^W = 1; # I hate having to do this my $shut_up_warnings = $XML::RSS::VERSION; eval "use XML::RSS"; skip_all('XML::RSS v 0.9 or later not installed') if $@ || ($] == 5.006 && $XML::RSS::VERSION < 0.9); # account for script being run in distribution root or 't' directory my $file = abs_path( -d 't' ? 't/test/xml' : 'test/xml' ); $file .= '/example.rdf'; local *RSS; open RSS, $file or die "Can't open $file: $!"; my $data = join "" => ; close RSS; test_expect(\*DATA, undef, { 'newsfile' => $file, 'newsdata' => $data }); __END__ -- test -- [% USE news = XML.RSS(newsfile) -%] [% FOREACH item = news.items -%] * [% item.title %] [% item.link %] [% END %] -- expect -- * I Read the News Today http://oh.boy.com/ * I am the Walrus http://goo.goo.ga.joob.org/ -- test -- [% USE news = XML.RSS(newsfile) -%] [% news.channel.title %] [% news.channel.link %] -- expect -- Template Toolkit XML::RSS Plugin http://template-toolkit.org/plugins/XML/RSS -- test -- [% USE news = XML.RSS(newsfile) -%] [% news.image.title %] [% news.image.url %] -- expect -- Test Image http://www.myorg.org/images/test.png -- test -- [% USE news = XML.RSS(newsdata) -%] [% FOREACH item = news.items -%] * [% item.title %] [% item.link %] [% END %] -- expect -- * I Read the News Today http://oh.boy.com/ * I am the Walrus http://goo.goo.ga.joob.org/ -- test -- [% USE news = XML.RSS(newsdata) -%] [% news.channel.title %] [% news.channel.link %] -- expect -- Template Toolkit XML::RSS Plugin http://template-toolkit.org/plugins/XML/RSS -- test -- [% USE news = XML.RSS(newsdata) -%] [% news.image.title %] [% news.image.url %] -- expect -- Test Image http://www.myorg.org/images/test.png Template-XML-2.17/t/xml.t0000644000175000017500000002133010563116254014452 0ustar abwabw00000000000000#============================================================= -*-perl-*- # # t/xml.t # # Test the XML plugin. # # Written by Andy Wardley # # Copyright (C) 1996-2006 Andy Wardley. All Rights Reserved. # # This is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # $Id: dom.t,v 2.8 2002/08/12 11:07:14 abw Exp $ # #======================================================================== use strict; use warnings; use lib qw( ./lib ../lib ); use Template; use Template::Test; use Cwd qw( abs_path ); my $dir = abs_path( -d 't' ? 't/test/xml' : 'test/xml' ); my $file = 'example.xml'; my $libxml = eval { require XML::LibXML }; my $vars = { dir => $dir, file => $file, libxml => $libxml, debug_on => sub { $Template::Plugin::XML::DEBUG = 1 }, debug_off => sub { $Template::Plugin::XML::DEBUG = 0 }, libxml_on => sub { $Template::Plugin::XML::LIBXML = 1 }, libxml_off => sub { $Template::Plugin::XML::LIBXML = 0 }, libxml_save => sub { $libxml = $Template::Plugin::XML::LIBXML }, libxml_restore => sub { $Template::Plugin::XML::LIBXML = $libxml }, }; test_expect(\*DATA, undef, $vars); __END__ #------------------------------------------------------------------------ # test the $DEBUG package variable sets debugging on/off by default # unless overridden by a debug named parameter #------------------------------------------------------------------------ -- test -- [% CALL debug_on; USE XML; 'debugging is '; XML.debug ? 'on' : 'off' -%] -- expect -- debugging is on -- test -- [% CALL debug_off; USE XML; 'debugging is '; XML.debug ? 'on' : 'off' -%] -- expect -- debugging is off -- test -- [% USE XML(debug=1); 'debugging is '; XML.debug ? 'on' : 'off' %] -- expect -- debugging is on -- test -- [% CALL debug_on; USE XML(debug=0); 'debugging is '; XML.debug ? 'on' : 'off' %] -- expect -- debugging is off #------------------------------------------------------------------------ # test to see if $LIBXML is defined (if XML::LibXML is available). It # should match whatever the libxml is set to. Also check that we can # disable with a parameter and also that we get an error if we try to # enable it when it's not available. #------------------------------------------------------------------------ -- test -- [% USE XML; 'libxml is '; XML.libxml ? 'enabled' : 'disabled' -%] -- expect -- -- process -- libxml is [% libxml ? 'enabled' : 'disabled' %] -- test -- [% CALL libxml_off; USE XML; 'libxml is '; XML.libxml ? 'enabled' : 'disabled'; -%] -- expect -- libxml is disabled -- test -- [% USE XML(libxml = 0); 'libxml is '; XML.libxml ? 'enabled' : 'disabled' -%] -- expect -- libxml is disabled -- test -- [% CALL libxml_off; TRY; USE XML(libxml=1); "should have got 'XML::LibXML not available' error but didn't"; CATCH; "good, we got an error: $error"; END -%] -- expect -- good, we got an error: XML error - XML::LibXML is not available # if libxml is available then check we can set and get various options # such as expand_entities -- test -- [% IF libxml; CALL libxml_restore; USE XML(expand_entities=1); "expanding: $XML.libxml.expand_entities\n"; CALL XML.libxml.expand_entities(0); "expanding: $XML.libxml.expand_entities\n"; ELSE; "no libxml"; END -%] -- expect -- -- process -- [% IF libxml -%] expanding: 1 expanding: 0 [% ELSE -%] no libxml [% END %] -- stop -- -- test -- [% USE XML(file='xmlfile.xml') -%] [% XML.type %]: [% XML.source %] -- expect -- file: xmlfile.xml -- test -- [% USE XML %] got libxml? [% XML.libxml ? 'yes' : 'no' %] -- expect -- -- process -- Hmmm: [% libxml ? 'yes' : 'no' %] -- stop -- # load a file via the file() method -- test -- [% USE XML; xfile = XML.file(file); xfile.name # a Template::Plugin::XML::File object -%] -- expect -- -- process -- [% file %] -- stop -- -- test -- [% USE XML; XML.type or 'No type'; %] -- expect -- No type # specify directory as constructor parameter -- test -- [% USE XML( dir => dir ); xfile = XML.file(file); -%] name: [% xfile.name %] dir: [% xfile.dir %] -- expect -- -- process -- name: [% file %] dir: [% dir %] # specify directory via dir() method -- test -- [% USE XML; xdir = XML.dir(dir); # Template::Plugin::XML::Directory xfile = XML.file(file); -%] path: [% xdir.path %] name: [% xfile.name %] dir: [% xfile.dir %] -- expect -- -- process -- path: [% dir %] name: [% file %] dir: [% dir %] # specify file via single argument to constructor method -- test -- [% USE xfile = XML(file) -%] name: [% xfile.name %] -- expect -- -- process -- name: [% file %] # specify file via named parameter to constructor method -- test -- [% USE xfile = XML( file = file ) -%] name: [% xfile.name %] -- expect -- -- process -- name: [% file %] # specify file and dir via named params to constructor method -- test -- [% USE xfile = XML( dir=dir, file=file ) -%] name: [% xfile.name %] dir: [% xfile.dir %] -- expect -- -- process -- name: [% file %] dir: [% dir %] -- stop -- # TODO: more tests... -- test -- [% XML.dom(file) %] and [% XML.dom(file=file) %] -- test -- [% xdir.dom(file) %] and [% xdir.dom(file=file) %] -- test -- [% xfile.dom %] => T::P::XML::DOM # repeat above tests for xpath, simple and rss # these tests below are copied from dom.t for reference -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(filename => xmlfile) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% global.xmltext = BLOCK %]
Hello World!
[% END -%] [% USE dom = XML.DOM -%] [% doc = dom.parse(global.xmltext) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(text => global.xmltext) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% USE dom = XML.DOM -%] [% doc = dom.parse(xml => global.xmltext) -%] [% FOREACH tag = doc.getElementsByTagName('page') -%] * [% tag.href %] [% tag.title %] [% END %] -- expect -- * /foo/bar The Foo Page * /bar/baz The Bar Page * /baz/qux The Baz Page -- test -- [% USE parser = XML.DOM -%] [% doc = parser.parse(global.xmltext) -%] [% FOREACH node = doc.getElementsByTagName('section') -%] [% node.toTemplate %] [% END %] [% BLOCK section -%] Section name: [% node.name %] title: [% node.title %] [% node.childrenToTemplate -%] [% END %] [% BLOCK page -%] [% node.title %] [% node.childrenToTemplate -%] [% END %] [% BLOCK msg -%] [% node.childrenToTemplate(verbose=1) %] [% END %] -- expect -- Section name: alpha title: The Alpha Zone The Foo Page Hello World! The Bar Page The Baz Page -- test -- [% xmltext = BLOCK %]
> Andy Wardley This is the first page This is the second page
This is the first page in section b This is the second page in section b
[% END -%] [% USE parser = XML.DOM -%] [% doc = parser.parse(xmltext) -%] [% node.allChildrenToTemplate(default='anynode') FOREACH node = doc.getChildNodes %] [% BLOCK section -%] SECTION [% node.id %]: [% node.title %] [% children -%] END OF SECTION [% node.id %] [% END %] [% BLOCK page -%] PAGE: [% node.title %] [% node.children -%] END OF PAGE [% END %] [% BLOCK head -%] HEADER: [% node.toString; prune %]END_HEADER [% END %] [% BLOCK anynode -%] [% node.toString; node.prune %] [% END %] -- expect -- SECTION a: First Section PAGE: page 1 HEADER: Andy WardleyEND_HEADER This is the first page END OF PAGE PAGE: page 2 END OF PAGE END OF SECTION a SECTION b: Second Section PAGE: page 1 END OF PAGE PAGE: page 2 END OF PAGE END OF SECTION b Template-XML-2.17/t/xmlsimple.t0000644000175000017500000000372410563113666015677 0ustar abwabw00000000000000#============================================================= -*-perl-*- # # t/xmlsimple.t # # Test the XML::Simple plugin. # # Written by Kenny Gatdula # # Copyright (C) 2004 Kenny Gatdula. All Rights Reserved. # # This is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # $Id: xmlsimple.t,v 1.2 2004/10/04 10:03:52 abw Exp $ # #======================================================================== use strict; use lib qw( ./lib ../lib ); use Template::Test; use Cwd qw( abs_path ); $^W = 1; $Template::Test::DEBUG = 0; #$Template::Test::DEBUG = 1; #$Template::Parser::DEBUG = 1; #$Template::Directive::PRETTY = 1; my $tt1 = Template->new({ INCLUDE_PATH => [ qw( t/test/lib test/lib ) ], ABSOLUTE => 1, }); ok(1); eval "use XML::Simple"; if ($@ || $XML::Simple::VERSION < 2) { skip_all('XML::Simple v2.0 or later not installed'); } # account for script being run in distribution root or 't' directory my $file = abs_path( -d 't' ? 't/test/xml' : 'test/xml' ); $file .= '/testfile.xml'; test_expect(\*DATA, $tt1, { 'xmlfile' => $file }); __END__ -- test -- [% TRY; USE xmlsimple = XML.Simple('no_such_file'); CATCH; error; END %] -- expect -- file error - no_such_file: not found -- test -- [% USE xml = XML.Simple(xmlfile) -%] [% xml.section.name -%] -- expect -- alpha -- test -- [% USE xs = XML.Simple -%] [% xml = xs.XMLin(xmlfile) -%] [% xml.section.title -%] -- expect -- The Alpha Zone -- test -- [% USE XML; xs = XML.simple -%] [% xml = xs.XMLin(xmlfile) -%] [% xml.section.title -%] -- expect -- The Alpha Zone -- test -- [% USE xs = XML.Simple -%] [% xml = xs.XMLin(xmlfile) -%] [% xmlout = xs.XMLout(xml) -%] [% xmlout -%] -- expect --
Template-XML-2.17/t/xmlstyle.t0000644000175000017500000001216410563113666015544 0ustar abwabw00000000000000#============================================================= -*-perl-*- # # t/xmlstyle.t # # Test the XML::Style plugin. # # Written by Andy Wardley # # Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. # Copyright (C) 1998-2001 Canon Research Centre Europe Ltd. # # This is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # $Id: xmlstyle.t,v 1.5 2003/03/17 22:34:14 abw Exp $ # #======================================================================== use strict; use lib qw( ./lib ../lib ../blib/arch ); use Template; use Template::Test; use Cwd qw( abs_path ); $^W = 1; $Template::Test::PRESERVE = 1; test_expect(\*DATA); __END__ -- test -- [% USE xmlstyle -%] [% FILTER xmlstyle -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE xmlstyle foo = { element = 'bar' } -%] [% FILTER xmlstyle -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE xmlstyle foo = { element = 'baz' } -%] [% FILTER xmlstyle -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE xmlstyle -%] [% FILTER xmlstyle foo = { element = 'wiz' } -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE xmlstyle foo = { element = 'bar' } -%] [% FILTER xmlstyle foo = { element = 'baz' } -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE xmlstyle foo = { element = 'oof' } -%] [% FILTER xmlstyle bar = { element = 'rab' } -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE xmlstyle -%] [% FILTER xmlstyle foo = { attributes = { wiz = 'waz' } } -%] The foo [%- END %] -- expect -- The foo -- test -- [% USE xmlstyle foo = { attributes = { wiz = 'waz' } }-%] [% FILTER xmlstyle bar = { attributes = { biz = 'boz' } } -%] The foo blam [%- END %] -- expect -- The foo blam -- test -- [% USE xmlstyle list = { element = 'ul' pre_start = "

\n" post_start = "\n" pre_end = "\n" post_end = "\n

" attributes = { class = 'mylist' } } item = { element = 'li' post_start = '' pre_end = '' post_end = "\n
" attributes = { class = 'myitem' } } -%] [% FILTER xmlstyle -%] The First Item The Second Item The Third Item [%- END %] -- expect --

  • The First Item

  • The Second Item

  • The Third Item

#------------------------------------------------------------------------ # test use of plugin filter via variable #------------------------------------------------------------------------ -- test -- [% USE xmlstyle foo = { element = 'bar' } -%] [% FILTER $xmlstyle -%] The foo [%- END %] -- expect -- The foo -- test -- [% USE xmlstyle foo = { element = 'bar' } -%] [% FILTER $xmlstyle bar = { element = 'baz' } -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE zap = xmlstyle foo = { element = 'bar' } -%] [% FILTER $zap bar = { element = 'baz' } -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE zap = xmlstyle foo = { element = 'bar' } -%] [% FILTER $zap 'blaml' bar = { element = 'baz' } -%] The foo The bar [%- END %] -- expect -- The foo The bar -- test -- [% USE xmlstyle 'zap' -%] [% FILTER zap bar = { element = 'baz' } -%] The foo The bar [%- END %] -- expect -- The foo The bar #------------------------------------------------------------------------ # an example based on one from Tony Bowden posted to the mailing list #------------------------------------------------------------------------ -- test -- [% USE xmlstyle video = { element = 'table' attributes = { class='videoTable' }, } title = { pre_start = "\n Title:\n " element = 'td' attributes = { class='videoTitle' } post_end = "\n " } price = { pre_start = "\n Price:\n " element = 'td' attributes = { class='videoPrice' } post_end = "\n " } ; FILTER xmlstyle -%] [% END %] -- expect --
Title: Buffy Series 1
Price: 10.99
Template-XML-2.17/t/file.t0000644000175000017500000001017210563113666014577 0ustar abwabw00000000000000#============================================================= -*-perl-*- # # t/file.t # # Test the XML::File plugin. # # Written by Andy Wardley # # Copyright (C) 1996-2006 Andy Wardley. All Rights Reserved. # # This is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # $Id: dom.t,v 2.8 2002/08/12 11:07:14 abw Exp $ # #======================================================================== use strict; use warnings; use lib qw( ./lib ../lib ); use Template; use Template::Test; use Cwd qw( abs_path ); local *FP; my $dir = abs_path( -d 't' ? 't/xml' : 'xml' ); my $file = 'example.xml'; my $path = File::Spec->catfile($dir, $file); open(FP, $path) || die "cannot open $path: $!"; my $vars = { dir => $dir, file => $file, path => $path, debug_on => sub { $Template::Plugin::XML::File::DEBUG = 1 }, debug_off => sub { $Template::Plugin::XML::File::DEBUG = 0 }, handle => \*FP, }; test_expect(\*DATA, undef, $vars); close(FP); __END__ #------------------------------------------------------------------------ # test the $DEBUG package variable sets debugging on/off by default # unless overridden by a debug named parameter #------------------------------------------------------------------------ -- test -- [% CALL debug_on; USE xf = XML.File('foo'); 'debugging is '; xf.debug ? 'on' : 'off' -%] -- expect -- debugging is on -- test -- [% CALL debug_off; USE xf = XML.File('foo'); 'debugging is '; xf.debug ? 'on' : 'off' -%] -- expect -- debugging is off -- test -- [% USE xf = XML.File('foo', debug=1); 'debugging is '; xf.debug ? 'on' : 'off' %] -- expect -- debugging is on -- test -- [% CALL debug_on; USE xf=XML('foo', debug=0); 'debugging is '; xf.debug ? 'on' : 'off' %] -- expect -- debugging is off #------------------------------------------------------------------------ # test the use of the positional argument to specify file name or handle #------------------------------------------------------------------------ -- test -- [% USE xf = XML.File(file) -%] type: [% xf.type or 'no type' %] name: [% xf.name or 'no name' %] handle: [% xf.handle or 'no handle' %] -- expect -- -- process -- type: name name: [% file %] handle: no handle -- test -- [% USE xf = XML.File(handle) -%] type: [% xf.type or 'no type' %] name: [% xf.name or 'no name' %] handle: [% xf.handle or 'no handle' %] -- expect -- -- process -- type: handle name: no name handle: [% handle %] #------------------------------------------------------------------------ # test the use of named parameters for file name #------------------------------------------------------------------------ -- test -- [% USE xf = XML.File(file=file) -%] [% xf.type %]: [% xf.name %] -- expect -- -- process -- name: [% file %] -- test -- [% USE xf = XML.File(name=file) -%] [% xf.type %]: [% xf.name %] -- expect -- -- process -- name: [% file %] -- test -- [% USE xf = XML.File(xml_file=file) -%] [% xf.type %]: [% xf.name %] -- expect -- -- process -- name: [% file %] #------------------------------------------------------------------------ # test the use of named parameters for file handle #------------------------------------------------------------------------ -- test -- [% USE xf = XML.File(fh=handle) -%] [% xf.type %]: [% xf.handle %] -- expect -- -- process -- handle: [% handle %] -- test -- [% USE xf = XML.File(handle=handle) -%] [% xf.type %]: [% xf.handle %] -- expect -- -- process -- handle: [% handle %] -- test -- [% USE xf = XML.File(xml_fh=handle) -%] [% xf.type %]: [% xf.handle %] -- expect -- -- process -- handle: [% handle %] #------------------------------------------------------------------------ # test file() method #------------------------------------------------------------------------ -- test -- [% USE XML; file = XML.file(file) -%] file: [% file.name %] -- expect -- -- process -- file: [% file %] #------------------------------------------------------------------------ # TODO: dom(), xpath() and other methods. #------------------------------------------------------------------------ Template-XML-2.17/t/domview.t0000644000175000017500000000373310563113666015337 0ustar abwabw00000000000000#============================================================= -*-perl-*- # # t/domview.t # # Test the XML::DOM plugin presenting via a VIEW # # Written by Andy Wardley # # Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. # Copyright (C) 1998-2001 Canon Research Centre Europe Ltd. # # This is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # $Id: domview.t,v 2.3 2002/08/12 11:07:14 abw Exp $ # #======================================================================== use strict; use lib qw( ./lib ../lib ); use Template; use Template::Test; use Cwd qw( abs_path ); $^W = 1; #$Template::Test::DEBUG = 1; #$Template::Test::PRESERVE = 1; # I hate having to do this my $shut_up_warnings = $XML::DOM::VERSION; eval "use XML::DOM"; if ($@ || $XML::DOM::VERSION < 1.27) { skip_all("XML::DOM v1.27 or later not installed"); } test_expect(\*DATA); __END__ -- test -- [% xmltext = BLOCK -%]

Blah blah.

  • Item 1
  • item 2

...
[% END -%] [% USE dom = XML.DOM; doc = dom.parse(text => xmltext); report = doc.getElementsByTagName('report') -%] [% VIEW report_view notfound='xmlstring' %] # handler block for a ... element [% BLOCK report; item.content(view); END %] # handler block for a
...
element [% BLOCK section -%]

[% item.title %]

[% item.content(view) -%] [% END -%] # default template block converts item to string representation [% BLOCK xmlstring; item.toString; END %] # block to generate simple text [% BLOCK text; item; END %] [% END -%] REPORT: [% report_view.print(report) | trim %] -- expect -- REPORT:

Introduction

Blah blah.

  • Item 1
  • item 2

The Gory Details

... Template-XML-2.17/lib/0000755000175000017500000000000010563116312013764 5ustar abwabw00000000000000Template-XML-2.17/lib/Template/0000755000175000017500000000000010563116312015537 5ustar abwabw00000000000000Template-XML-2.17/lib/Template/Plugin/0000755000175000017500000000000010563116312016775 5ustar abwabw00000000000000Template-XML-2.17/lib/Template/Plugin/XML/0000755000175000017500000000000010563116312017435 5ustar abwabw00000000000000Template-XML-2.17/lib/Template/Plugin/XML/DOM.pm0000644000175000017500000003137210563113667020432 0ustar abwabw00000000000000#============================================================= -*-Perl-*- # # Template::Plugin::XML::DOM # # DESCRIPTION # Simple Template Toolkit plugin interfacing to the XML::DOM.pm module. # # AUTHORS # Andy Wardley # Simon Matthews # # COPYRIGHT # Copyright (C) 2000-2006 Andy Wardley, Simon Matthews. # # This module is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # #============================================================================ package Template::Plugin::XML::DOM; use strict; use warnings; use base 'Template::Plugin'; use Template::Plugin::XML; use XML::DOM; our $VERSION = 2.70; our $DEBUG = 0 unless defined $DEBUG; #------------------------------------------------------------------------ # new($context, \%config) # # Constructor method for XML::DOM plugin. Creates an XML::DOM::Parser # object and initialise plugin configuration. #------------------------------------------------------------------------ sub new { my $class = shift; my $context = shift; my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; my $parser ||= XML::DOM::Parser->new(%$args) || return $class->throw("failed to create XML::DOM::Parser\n"); bless { PARSER => $parser, DOCS => [ ], CONTEXT => $context, }, $class; } #------------------------------------------------------------------------ # parse($content, \%named_params) # # Parses an XML stream, provided as the first positional argument (assumed # to be a filename unless it contains a '<' character) or specified in # the named parameter hash as one of 'text', 'xml' (same as text), 'file' # or 'filename'. #------------------------------------------------------------------------ sub parse { my $self = shift; my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; my $parser = $self->{ PARSER }; my ($content, $about, $method, $doc); # determine the input source from a positional parameter (may be a # filename or XML text if it contains a '<' character) or by using # named parameters which may specify one of 'file', 'filename', 'text' # or 'xml' if ($content = shift) { if ($content =~ /\{ text } || $args->{ xml }) { $about = 'xml text'; $method = 'parse'; } elsif ($content = $args->{ file } || $args->{ filename }) { $about = "xml file $content"; $method = 'parsefile'; } else { return $self->throw('no filename or xml text specified'); } # parse the input source using the appropriate method determined above eval { $doc = $parser->$method($content) } and not $@ or return $self->throw("failed to parse $about: $@"); # update XML::DOM::Document _UserData to contain config details $doc->[ XML::DOM::Node::_UserData ] = { map { ( $_ => $self->{ $_ } ) } qw( _CONTEXT ), }; push(@{ $self->{ _DOCS } }, $doc); return $doc; } #------------------------------------------------------------------------ # throw($errmsg) # # Raised a Template::Exception of type XML.DOM via die(). #------------------------------------------------------------------------ sub throw { my $self = shift; die $Template::Plugin::XML::EXCEPTION->new( 'XML.DOM' => join('', @_) ); } #------------------------------------------------------------------------ # DESTROY # # Cleanup method which calls dispose() on any and all DOM documents # created by this object. Also breaks any circular references that # may exist with the context object. #------------------------------------------------------------------------ sub DESTROY { my $self = shift; # call dispose() on each document produced by this parser foreach my $doc (@{ $self->{ DOCS } }) { if (ref $doc) { undef $doc->[ XML::DOM::Node::_UserData ]->{ CONTEXT }; $doc->dispose(); } } delete $self->{ CONTEXT }; delete $self->{ PARSER }; } #======================================================================== package XML::DOM::Node; #======================================================================== #------------------------------------------------------------------------ # present($view) # # Method to present node via a view (supercedes all that messy toTemplate # stuff below). #------------------------------------------------------------------------ sub present { my ($self, $view) = @_; if ($self->getNodeType() == XML::DOM::ELEMENT_NODE) { # it's an element $view->view($self->getTagName(), $self); } else { my $text = $self->toString(); $view->view('text', $text); } } sub content { my ($self, $view) = @_; my $output = ''; foreach my $node (@{ $self->getChildNodes }) { $output .= $node->present($view); } return $output; } #======================================================================== package XML::DOM::Element; #======================================================================== use vars qw( $AUTOLOAD ); sub AUTOLOAD { my $self = shift; my $method = $AUTOLOAD; my $attrib; $method =~ s/.*:://; return if $method eq 'DESTROY'; my $doc = $self->getOwnerDocument() || $self; my $data = $doc->[ XML::DOM::Node::_UserData ]; # call 'content' or 'prune' callbacks, if defined (see _template_node()) return &$attrib() if ($method =~ /^children|prune$/) && defined($attrib = $data->{ "_TT_\U$method" }) && ref $attrib eq 'CODE'; return $attrib if defined ($attrib = $self->getAttribute($method)); return ''; } 1; __END__ =head1 NAME Template::Plugin::XML::DOM - Plugin interface to XML::DOM =head1 SYNOPSIS # load plugin [% USE dom = XML.DOM %] # also provide XML::Parser options [% USE dom = XML.DOM(ProtocolEncoding = 'ISO-8859-1') %] # parse an XML file [% doc = dom.parse(filename) %] [% doc = dom.parse(file = filename) %] # parse XML text [% doc = dom.parse(xmltext) %] [% doc = dom.parse(text = xmltext) %] # call any XML::DOM methods on document/element nodes [% FOREACH node = doc.getElementsByTagName('report') %] * [% node.getAttribute('title') %] # or [% node.title %] [% END %] # define VIEW to present node(s) [% VIEW report notfound='xmlstring' %] # handler block for a ... element [% BLOCK report %] [% item.content(view) %] [% END %] # handler block for a
...
element [% BLOCK section %]

[% item.title %]

[% item.content(view) %] [% END %] # default template block converts item to string [% BLOCK xmlstring; item.toString; END %] # block to generate simple text [% BLOCK text; item; END %] [% END %] # now present node (and children) via view [% report.print(node) %] # or print node content via view [% node.content(report) %] # following methods are soon to be deprecated in favour of views [% node.toTemplate %] [% node.childrenToTemplate %] [% node.allChildrenToTemplate %] =head1 DESCRIPTION This is a Template Toolkit plugin interfacing to the XML::DOM module. The plugin loads the XML::DOM module and creates an XML::DOM::Parser object which is stored internally. The parse() method can then be called on the plugin to parse an XML stream into a DOM document. [% USE dom = XML.DOM %] [% doc = dom.parse('/tmp/myxmlfile') %] The XML::DOM plugin object (i.e. 'dom' in these examples) acts as a sentinel for the documents it creates ('doc' and any others). When the plugin object goes out of scope at the end of the current template, it will automatically call dispose() on any documents that it has created. Note that if you dispose of the the plugin object before the end of the block (i.e. by assigning a new value to the 'dom' variable) then the documents will also be disposed at that point and should not be used thereafter. [% USE dom = XML.DOM %] [% doc = dom.parse('/tmp/myfile') %] [% dom = 'new value' %] # releases XML.DOM plugin and calls # dispose() on 'doc', so don't use it! The plugin constructor will also accept configuration options destined for the XML::Parser object: [% USE dom = XML.DOM(ProtocolEncoding = 'ISO-8859-1') %] =head1 METHODS =head2 parse() The parse() method accepts a positional parameter which contains a filename or XML string. It is assumed to be a filename unless it contains a E character. [% xmlfile = '/tmp/foo.xml' %] [% doc = dom.parse(xmlfile) %] [% xmltext = BLOCK %] ... [% END %] [% doc = dom.parse(xmltext) %] The named parameters 'file' (or 'filename') and 'text' (or 'xml') can also be used: [% doc = dom.parse(file = xmlfile) %] [% doc = dom.parse(text = xmltext) %] The parse() method returns an instance of the XML::DOM::Document object representing the parsed document in DOM form. You can then call any XML::DOM methods on the document node and other nodes that its methods may return. See L for full details. [% FOREACH node = doc.getElementsByTagName('CODEBASE') %] * [% node.getAttribute('href') %] [% END %] This plugin also provides an AUTOLOAD method for XML::DOM::Node which calls getAttribute() for any undefined methods. Thus, you can use the short form of [% node.attrib %] in place of [% node.getAttribute('attrib') %] =head1 PRESENTING DOM NODES USING VIEWS You can define a VIEW to present all or part of a DOM tree by automatically mapping elements onto templates. Consider a source document like the following:

Blah blah.

  • Item 1
  • item 2

...
We can load it up via the XML::DOM plugin and fetch the node for the EreportE element. [% USE dom = XML.DOM; doc = dom.parse(file = filename); report = doc.getElementsByTagName('report') %] We can then define a VIEW as follows to present this document fragment in a particular way. The L documentation contains further details on the VIEW directive and various configuration options it supports. [% VIEW report_view notfound='xmlstring' %] # handler block for a ... element [% BLOCK report %] [% item.content(view) %] [% END %] # handler block for a
...
element [% BLOCK section %]

[% item.title %]

[% item.content(view) %] [% END %] # default template block converts item to string representation [% BLOCK xmlstring; item.toString; END %] # block to generate simple text [% BLOCK text; item; END %] [% END %] Each BLOCK defined within the VIEW represents a presentation style for a particular element or elements. The current node is available via the 'item' variable. Elements that contain other content can generate it according to the current view by calling [% item.content(view) %]. Elements that don't have a specific template defined are mapped to the 'xmlstring' template via the 'notfound' parameter specified in the VIEW header. This replicates the node as an XML string, effectively allowing general XML/XHTML markup to be passed through unmodified. To present the report node via the view, we simply call: [% report_view.print(report) %] The output from the above example would look something like this:

Introduction

Blah blah.

  • Item 1
  • item 2

The Gory Details

... To print just the content of the report node (i.e. don't process the 'report' template for the report node), you can call: [% report.content(report_view) %] =head1 AUTHORS This plugin module was written by Andy Wardley and Simon Matthews. The XML::DOM module is by Enno Derksen and Clark Cooper. It extends the the XML::Parser module, also by Clark Cooper which itself is built on James Clark's expat library. =head1 COPYRIGHT Copyright (C) 2000-2006 Andy Wardley, Simon Matthews. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L Template-XML-2.17/lib/Template/Plugin/XML/View.pm0000644000175000017500000002101210563113667020713 0ustar abwabw00000000000000#============================================================= -*-Perl-*- # # Template::Plugin::XML::View # # DESCRIPTION # Template Toolkit plugin to parse XML and generate a view by raising # events to a Template::View object for each element in the XML source. # # -- UNDER CONSTRUCTION -- NOT INCLUDED IN THE MAIN DISTRIBUTION -- # # AUTHOR # Andy Wardley # # COPYRIGHT # Copyright (C) 2001-2004 Andy Wardley. All Rights Reserved. # # This module is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # REVISION # $Id: View.pm,v 2.4 2003/03/17 22:29:16 abw Exp $ # #============================================================================ package Template::Plugin::XML::View; require 5.004; use strict; use Template::Plugin; use XML::Parser; use base qw( Template::Plugin ); use vars qw( $VERSION $DEBUG $XML_PARSER_ARGS $ELEMENT ); $VERSION = sprintf("%d.%02d", q$Revision: 2.4 $ =~ /(\d+)\.(\d+)/); $DEBUG = 0 unless defined $DEBUG; $XML_PARSER_ARGS = { ErrorContext => 4, Namespaces => 1, ParseParamEnt => 1, NoExpand => 1, }; $ELEMENT = 'Template::Plugin::XML::View::Element'; #------------------------------------------------------------------------ # new($context, $file_or_text, \%config) #------------------------------------------------------------------------ sub new { my $class = shift; my $context = shift; my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; my ($input, $about); # determine the input source from a positional parameter (may be a # filename or XML text if it contains a '<' character) or by using # named parameters which may specify one of 'file', 'filename', 'text' # or 'xml' if ($input = shift) { if ($input =~ /\file_contents($input); } } elsif ($input = $args->{ text } || $args->{ xml }) { $about = 'xml text'; } elsif ($input = $args->{ file } || $args->{ filename }) { $about = "xml file $input"; $input = $class->file_contents($input); } else { $class->throw('no filename or xml text specified'); } # munge input to protect entity refs $input =~ s/&/&/g; my $xpargs = { map { exists $args->{$_} ? ( $_, $args->{ $_ } ) : ( $_, $XML_PARSER_ARGS->{ $_ } ) } keys %$XML_PARSER_ARGS, }; my $parser = XML::Parser->new( %$xpargs, Style => 'Template::Plugin::XML::View::Parser', Handlers => { Init => sub { my $expat = shift; DEBUG("[Init]\n") if $DEBUG; $expat->{ _TT2_XVIEW_TEXT } = ''; $expat->{ _TT2_XVIEW_RESULT } = ''; $expat->{ _TT2_XVIEW_CONTEXT } = $context; $expat->{ _TT2_XVIEW_STACK } = [ ]; }, }, ); my $result = $parser->parse($input); DEBUG("result: $result\n") if $DEBUG; return $result; } sub file_contents { my ($self, $file) = @_; my $text; local *FP; local $/ = undef; open(FP, $file) || $self->throw("cannot read XML file $file: $!"); $text = ; close(FP); return $text; } #------------------------------------------------------------------------ # _throw($errmsg) # # Raise a Template::Exception of type XML.View via die(). #------------------------------------------------------------------------ sub throw { my ($self, $error) = @_; die (Template::Exception->new('XML.View', $error)); } sub DEBUG { print STDERR @_ }; #======================================================================== # Template::Plugin::XML::View::Parser # # Package defines subroutines which are called by the XML::Parser # instance. They manipulate a stack of T-::P-::XML::View::Element # objects which each represent nested elements currently under parse # at any time, with the innermost element object on top of the stack. # These subs call the element() #======================================================================== package Template::Plugin::XML::View::Parser; use vars qw( $DEBUG $ELEMENT ); *DEBUG = \*Template::Plugin::XML::View::DEBUG; $ELEMENT = 'Template::Plugin::XML::View::Element'; sub Start { my ($expat, $name, %attr) = @_; my $attr = \%attr; # flush any character content Text($expat) if length $expat->{ _TT2_XVIEW_TEXT }; if ($DEBUG) { my $iattr = join(' ', map { "$_=\"$attr{$_}\"" } keys %attr); $attr = " $attr" if $attr; DEBUG("[Start] <$name$attr>\n"); } my $stack = $expat->{ _TT2_XVIEW_STACK }; my $element = $ELEMENT->new($name, \%attr) || $stack->[-1]->throw($ELEMENT->error()); push(@$stack, $element); } sub End { my ($expat, $name) = @_; # flush any character content Text($expat) if length $expat->{ _TT2_XVIEW_TEXT }; DEBUG("[End] \n") if $DEBUG; my $stack = $expat->{ _TT2_XVIEW_STACK }; my $top = pop(@$stack); my $end = $top->end($expat, $name) || $top->throw($top->error()); if (@$stack) { $stack->[-1]->child($expat, $name, $end); } else { DEBUG("popped last handler off stack\n") if $DEBUG; # die "corrupt stack\n"; $expat->{ _TT2_XVIEW_RESULT } = $end; } } sub Char { my ($expat, $char) = @_; DEBUG("[Char] [$char]\n") if $DEBUG; # push character content onto buffer $expat->{ _TT2_XVIEW_TEXT } .= $char; } #------------------------------------------------------------------------ # Text() # # This is an extension subroutine which we're using to buffer chunks # of Char input into complete text blocks. These then get notified to # the parent in one happy bundle rather than several scraggly lumps. #------------------------------------------------------------------------ sub Text { my $expat = shift; my $text = $expat->{ _TT2_XVIEW_TEXT }; if ($DEBUG) { my $dbgtext = $text; $dbgtext =~ s/\n/\\n/g; DEBUG("[Text] [$dbgtext]\n") if $DEBUG; } $expat->{ _TT2_XVIEW_STACK }->[-1]->text($expat, $text); $expat->{ _TT2_XVIEW_TEXT } = ''; } sub Final { my $expat = shift; return $expat->{ _TT2_XVIEW_RESULT } || die "no result\n"; my $stack = $expat->{ _TT2_XVIEW_STACK }; my $top = pop(@$stack) || die "corrupt stack in Final"; my $end = $top->end($expat) || $top->throw($top->error()); my $r = $expat->{ _TT2_XVIEW_RESULT } || die "no result\n";# $end; DEBUG("[Final] => [$r]\n") if $DEBUG; return $r; } #======================================================================== # Template::Plugin::XML::View::Element # # Implements a parser handler for representing each element in the #======================================================================== package Template::Plugin::XML::View::Element; sub new { my ($class, $name, $attr) = @_; bless { name => $name, attr => $attr, content => [ ], }, $class; } # called to receive character content sub text { my $self = shift; my $expat = shift; push(@{ $self->{ content } }, @_); } # called to receive completed child element sub child { my ($self, $expat, $name, $child) = @_; push(@{ $self->{ content } }, $child); } # called at end of element sub end { my ($self, $expat, $name) = @_; return $self; } # generate element as XML sub xml { my $self = shift; my $name = $self->{ name }; # generate XML representation of attributes my $attr = $self->{ attr }; $attr = join(' ', map { "$_=\"$attr->{$_}\""; } keys %$attr); $attr = " $attr" if length $attr; # generate XML representation of content my $content = $self->{ content }; $content = join(' ', map { ref $_ ? $_->xml() : $_; } @$content); # generate complete XML element return length $content ? "<${name}${attr}>$content" : "<${name}${attr} />"; } sub present { my ($self, $view) = @_; my $vars = { %$self, %{ $self->{ attr } }, element => $self, content => sub { $self->content($view) }, }; $view->include($self->{ name }, $vars) } sub content { my ($self, $view) = @_; return $self->{ content } unless $view; my $output = ''; foreach my $node (@{ $self->{ content } }) { $output .= ref $node ? $node->present($view) : $node; } return $output; } 1; __END__ Template-XML-2.17/lib/Template/Plugin/XML/XPath.pm0000644000175000017500000001566410563114017021033 0ustar abwabw00000000000000#============================================================= -*-Perl-*- # # Template::Plugin::XML::XPath # # DESCRIPTION # Template Toolkit plugin interfacing to the XML::XPath.pm module. # # AUTHOR # Andy Wardley # # COPYRIGHT # Copyright (C) 2000-2006 Andy Wardley. All Rights Reserved. # # This module is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # #============================================================================ package Template::Plugin::XML::XPath; use strict; use warnings; use Template::Exception; use base 'Template::Plugin'; use XML::XPath; our $VERSION = 2.71; #------------------------------------------------------------------------ # new($context, \%config) # # Constructor method for XML::XPath plugin. Creates an XML::XPath # object and initialises plugin configuration. #------------------------------------------------------------------------ sub new { my $class = shift; my $context = shift; my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; my ($content, $about); # determine the input source from a positional parameter (may be a # filename or XML text if it contains a '<' character) or by using # named parameters which may specify one of 'file', 'filename', 'text' # or 'xml' if ($content = shift) { if ($content =~ /\{ xml } = $content; } else { $about = "xml file $content"; $args->{ filename } = $content; } } elsif ($content = $args->{ text } || $args->{ xml }) { $about = 'xml text'; $args->{ xml } = $content; } elsif ($content = $args->{ file } || $args->{ filename }) { $about = "xml file $content"; $args->{ filename } = $content; } else { return $class->_throw('no filename or xml text specified'); } return XML::XPath->new(%$args) or $class->_throw("failed to create XML::XPath::Parser\n"); } #------------------------------------------------------------------------ # _throw($errmsg) # # Raise a Template::Exception of type XML.XPath via die(). #------------------------------------------------------------------------ sub _throw { my ($self, $error) = @_; die (Template::Exception->new('XML.XPath', $error)); } #======================================================================== package XML::XPath::Node::Element; #======================================================================== #------------------------------------------------------------------------ # present($view) # # Method to present an element node via a view. #------------------------------------------------------------------------ sub present { my ($self, $view) = @_; $view->view($self->getName(), $self); } sub content { my ($self, $view) = @_; my $output = ''; foreach my $node (@{ $self->getChildNodes }) { $output .= $node->present($view); } return $output; } #---------------------------------------------------------------------- # starttag(), endtag() # # Methods to output the start & end tag, e.g. & #---------------------------------------------------------------------- sub starttag { my ($self) = @_; my $output = "<". $self->getName(); foreach my $attr ($self->getAttributes()) { $output .= $attr->toString(); } $output .= ">"; return $output; } sub endtag { my ($self) = @_; return "getName() . ">"; } #======================================================================== package XML::XPath::Node::Text; #======================================================================== #------------------------------------------------------------------------ # present($view) # # Method to present a text node via a view. #------------------------------------------------------------------------ sub present { my ($self, $view) = @_; $view->view('text', $self->string_value); } #======================================================================== package XML::XPath::Node::Comment; #======================================================================== sub present { return ''; } sub starttag { return ''; } sub endtag { return ''; } 1; __END__ =head1 NAME Template::Plugin::XML::XPath - Plugin interface to XML::XPath =head1 SYNOPSIS # load plugin and specify XML file to parse [% USE xpath = XML.XPath(xmlfile) %] [% USE xpath = XML.XPath(file => xmlfile) %] [% USE xpath = XML.XPath(filename => xmlfile) %] # load plugin and specify XML text to parse [% USE xpath = XML.XPath(xmltext) %] [% USE xpath = XML.XPath(xml => xmltext) %] [% USE xpath = XML.XPath(text => xmltext) %] # then call any XPath methods (see XML::XPath docs) [% FOREACH page = xpath.findnodes('/html/body/page') %] [% page.getAttribute('title') %] [% END %] # define VIEW to present node(s) [% VIEW repview notfound='xmlstring' %] # handler block for a ... element [% BLOCK report %] [% item.content(view) %] [% END %] # handler block for a
...
element [% BLOCK section %]

[% item.getAttribute('title') | html %]

[% item.content(view) %] [% END %] # default template block passes tags through and renders # out the children recursivly [% BLOCK xmlstring; item.starttag; item.content(view); item.endtag; END %] # block to generate simple text [% BLOCK text; item | html; END %] [% END %] # now present node (and children) via view [% repview.print(page) %] # or print node content via view [% page.content(repview) %] =head1 DESCRIPTION This is a Template Toolkit plugin interfacing to the XML::XPath module. All methods implemented by the XML::XPath modules are available. In addition, the XML::XPath::Node::Element module implements present($view) and content($view) methods method for seamless integration with Template Toolkit VIEWs. The XML::XPath::Node::Text module is also adorned with a present($view) method which presents itself via the view using the 'text' template. To aid the reconstruction of XML, methods starttag and endtag are added to XML::XPath::Node::Element which return the start and end tag for that element. This means that you can easily do: [% item.starttag %][% item.content(view) %][% item.endtag %] To render out the start tag, followed by the content rendered in the view "view", followed by the end tag. =head1 AUTHORS This plugin module was written by Andy Wardley. The XML::XPath module is by Matt Sergeant. =head1 COPYRIGHT Copyright (C) 1996-2006 Andy Wardley. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L Template-XML-2.17/lib/Template/Plugin/XML/RSS.pm0000644000175000017500000001006310563113667020454 0ustar abwabw00000000000000#============================================================= -*-Perl-*- # # Template::Plugin::XML::RSS # # DESCRIPTION # Template Toolkit plugin which interfaces to Jonathan Eisenzopf's # XML::RSS module. RSS is the Rich Site Summary format. # # AUTHOR # Andy Wardley # # COPYRIGHT # Copyright (C) 2000-2006 Andy Wardley. All Rights Reserved. # # This module is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # #============================================================================ package Template::Plugin::XML::RSS; use strict; use warnings; use base 'Template::Plugin'; use XML::RSS; our $VERSION = 2.66; sub new { my ($class, $context, $filename) = @_; return $class->fail('No filename specified') unless $filename; my $rss = XML::RSS->new or return $class->fail('failed to create XML::RSS'); # Attempt to determine if $filename is an XML string or # a filename. Based on code from the XML.XPath plugin. eval { if ($filename =~ /\parse($filename); } else { $rss->parsefile($filename) } } and not $@ or return $class->fail("failed to parse $filename: $@"); return $rss; } 1; __END__ =head1 NAME Template::Plugin::XML::RSS - Plugin interface to XML::RSS =head1 SYNOPSIS [% USE news = XML.RSS('news.rdf') %] [% FOREACH item IN news.items %] [% item.title %] [% item.link %] [% END %] =head1 DESCRIPTION This Template Toolkit plugin provides a simple interface to the XML::RSS module. [% USE news = XML.RSS('mysite.rdf') %] It creates an XML::RSS object, which is then used to parse the RSS file specified as a parameter in the USE directive. A reference to the XML::RSS object is then returned. An RSS (Rich Site Summary) file is typically used to store short news 'headlines' describing different links within a site. This example is extracted from http://slashdot.org/slashdot.rdf. Slashdot:News for Nerds. Stuff that Matters. http://slashdot.org News for Nerds. Stuff that Matters Slashdot http://slashdot.org/images/slashdotlg.gif http://slashdot.org DVD CCA Battle Continues Next Week http://slashdot.org/article.pl?sid=00/01/12/2051208 Matrox to fund DRI Development http://slashdot.org/article.pl?sid=00/01/13/0718219 Mike Shaver Leaving Netscape http://slashdot.org/article.pl?sid=00/01/13/0711258 The attributes of the channel and image elements can be retrieved directly from the plugin object using the familiar dotted compound notation: [% news.channel.title %] [% news.channel.link %] [% news.channel.etc... %] [% news.image.title %] [% news.image.url %] [% news.image.link %] [% news.image.etc... %] The list of news items can be retrieved using the 'items' method: [% FOREACH item IN news.items %] [% item.title %] [% item.link %] [% END %] =head1 AUTHORS This plugin was written by Andy Wardley inspired by an article in Web Techniques by Randal Schwartz. The XML::RSS module, which implements all of the functionality that this plugin delegates to, was written by Jonathan Eisenzopf. =head1 COPYRIGHT Copyright (C) 1996-2006 Andy Wardley. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L Template-XML-2.17/lib/Template/Plugin/XML/LibXML.pm0000644000175000017500000004653510563113667021111 0ustar abwabw00000000000000#============================================================= -*-Perl-*- # # Template::Plugin::XML::LibXML # # DESCRIPTION # Template Toolkit plugin interfacing to the XML::LibXML.pm module. # # AUTHORS # Mark Fowler # Andy Wardley # # COPYRIGHT # Copyright (C) 2002-3 Mark Fowler, 2006 Andy Wardley. # All Rights Reserved. # # This module is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # #============================================================================ package Template::Plugin::XML::LibXML; require 5.004; use strict; use warnings; use base 'Template::Plugin'; use Template::Plugin::XML; use XML::LibXML; # load the recommended (but not manditory) openhandle routine # for filehandle detection. BEGIN { eval "use Scalar::Util qw(openhandle)" } our $VERSION = 2.00; # these are a list of combatibilty mappings from names that were used # (or logical extensions of those names for html) in the XML::XPath # plugin. Though we're using existing names, I want you to be able # to still use the old names. Very DWIM use constant TYPE => { 'xml' => 'string', 'text' => 'string', 'filename' => 'file', 'html' => 'html_string', 'html_text' => 'html_string', 'html_file' => 'html_file', 'html_filename' => 'html_file', }; #------------------------------------------------------------------------ # new($context, \%config) # # Constructor method for XML::LibXML plugin. Creates an XML::LibXML # object and initialises plugin configuration. #------------------------------------------------------------------------ sub new { my $class = shift; my $context = shift; my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; my $type; # how we're going to get out data my $content; # a ref to the data local $_; # work out what data we should process if (@_) { # ah, we got positional data. $content = \$_[0]; # remember where it is $type = _guess_type($_[0]); # guess what type we're doing } else { # okay, the data must be in the named parameters # first up we'll just try the method names. You really should # supply the arguments like this you know. foreach (qw(string file fh html_string html_file html_fh)) { if ($args->{ $_ }) { $content = \$args->{ $_ }; # remember where it is delete $args->{ $_ }; # don't pass on parameter though $type = $_; # remember what type we're doing last; # skip to the end } } unless ($type) { # last ditch effort. In this case we'll try some of the names # that the XML::XPath plugin uses. We might strike lucky foreach (keys %{ &TYPE }) { if ($args->{ $_ }) { $content = \$args->{ $_ }; # remember where it is delete $args->{ $_ }; # don't pass on parameter though $type = &TYPE->{ $_ }; # remember what type we're doing last; # skip to the end } } } } # return an error if we didn't get a response back return $class->_throw('no filename, handle or text specified') unless $type; # create a parser my $parser = XML::LibXML->new(); # set the options foreach my $method (keys %$args) { # try setting the method eval { $parser->$method($args->{$method}) }; # if there's a problem throw a Tempalte::Exception $self->throw("option '$method' not supported") if $@; } # parse my $method = "parse_$type"; return $parser->$method($$content); } #------------------------------------------------------------------------ # _guess_type($string) # # Guesses what type of data this is #------------------------------------------------------------------------ sub _guess_type { # look for a filehandle return "fh" if _openhandle($_[0]); # okay, look for the xml declaration at the start return "string" if $_[0] =~ m/^\<\?xml/; # okay, look for the html declaration anywhere in the doc return "html_string" if $_[0] =~ m//i; # okay, does this contain a "<" symbol, and declare it to be # xml if it's got one, though they should use "new( 'XML.LibXML' => join('', @_) ); } #------------------------------------------------------------------------ # _openhandle($scalar) # # Determines if this is probably an open filehandle or not. # # uses openhandle from Scalar::Util if we have it. #------------------------------------------------------------------------ sub _openhandle ($) { return openhandle($_[0]) if defined(&openhandle); # poor man's openhandle return defined(fileno $_[0]); } #======================================================================== package XML::LibXML::Node; #======================================================================== #----------------------------------------------------------------------- # present($view) # # Method to present an node via a view, using the block that has the # same localname. #----------------------------------------------------------------------- # note, should this worry about namespaces? Probably. Hmm. sub present { my ($self, $view) = @_; my $localname = $self->localname(); # convert anything that isn't A-Za-z1-9 to _. All those years # of working on i18n and this throws it all away. I suck. $localname =~ s/[^A-Za-z0-9]/_/g; # render out with the block matching the hacked version of localname $view->view($localname, $self); } #----------------------------------------------------------------------- # content($view) # # Method present the node's children via a view #----------------------------------------------------------------------- sub content { my ($self, $view) = @_; my $output = ''; foreach my $node ($self->childNodes ) { $output .= $node->present($view); } return $output; } #---------------------------------------------------------------------- # starttag(), endtag() # # Methods to output the start & end tag, e.g. # and #---------------------------------------------------------------------- sub starttag { my ($self) = @_; my $output = "<". $self->nodeName(); foreach my $attr ($self->attributes) { $output .= $attr->toString(); } $output .= ">"; return $output; } sub endtag { my ($self) = @_; return "nodeName() . ">"; } #======================================================================== package XML::LibXML::Document; #======================================================================== #------------------------------------------------------------------------ # present($view) # # Method to present a document node via a view. #------------------------------------------------------------------------ sub present { my ($self, $view) = @_; # okay, just start rendering from the first element, ignore the pi # and all that $self->documentElement->present($view); } #======================================================================== package XML::LibXML::Text; #======================================================================== #------------------------------------------------------------------------ # present($view) # # Method to present a text node via a view. #------------------------------------------------------------------------ sub present { my ($self, $view) = @_; $view->view('text', $self->data); # same as $self->nodeData } #======================================================================== package XML::LibXML::NodeList; #======================================================================== #------------------------------------------------------------------------ # present($view) # # Method to present a node list via a view. This is only normally useful # when you call outside of TT as findnodes will be called in list context # normally #------------------------------------------------------------------------ sub present { my ($self, $view) = @_; my $output = ''; foreach my $node ($self->get_nodelist ) { $output .= $node->present($view); } return $output; } #package debug; #sub debug #{ # local $^W; # my $nodename; # eval { $nodename = $_[0]->nodeName(); }; # my $methodname = (caller(1))[3]; # $methodname =~ s/.*:://; # # print STDERR "${nodename}'s $methodname: ". # (join ",", (map { ref } @_)) . # "\n"; #} 1; __END__ =head1 NAME Template::Plugin::XML::LibXML - XML::LibXML Template Toolkit Plugin =head1 SYNOPSIS [% USE docroot = XML.LibXML("helloworld.xml") %] The message is: [% docroot.find("/greeting/text") %] =head1 DESCRIPTION This module provides a plugin for the XML::LibXML module. It can be utilised the same as any other Template Toolkit plugin, by using a USE statement from within a Template. The use statment will return a reference to root node of the parsed document =head2 Specifying a Data Source The plugin is capable of using either a string, a filename or a filehandle as a source for either XML data, or HTML data which will be converted to XHTML internally. The USE statement can take one or more arguments to specify what XML should be processed. If only one argument is passed then the plugin will attempt to guess how what it has been passed should be interpreted. When it is forced to guess what type of data it is used the routine will first look for an open filehandle, which if it finds it will assume it's a filehandle to a file containing XML. Failing this (in decreasing order) it will look for the chars "" tag (and assume it's HTML string,) look for a "<" (and assume it's XML string without a header,) or assume what it's been passed is the filename to a file containing XML. In the interests of being explicit, you may specify the type of data you are loading using the same names as in the B documentation: # value contains the xml string [% USE docroot = XML.LibXML(string => value) %] # value contains the filename of the xml [% USE docroot = XML.LibXML(file => value) %] # value contains an open filehandle to some xml [% USE docroot = XML.LibXML(fh => value) %] # value contains the html string [% USE docroot = XML.LibXML(html_string => value) %] # value contains the filename of the html [% USE docroot = XML.LibXML(html_file => value) %] # value contains an open filehandle to some html [% USE docroot = XML.LibXML(html_fh => value) %] Or, if you want you can use similar names to that the XML.XPath plugin uses: # value contains the xml string [% USE docroot = XML.LibXML(xml => value) %] or [% USE docroot = XML.LibXML(text => value) %] # value contains the filename of the xml [% USE docroot = XML.LibXML(filename => value) %] # value contains the html string [% USE docroot = XML.LibXML(html => value) %] or [% USE docroot = XML.LibXML(html_text => value) %] # value contains the filename of the html [% USE docroot = XML.LibXML(html_file => value) %] [% USE docroot = XML.LibXML(html_filename => value) %] You can provide extra arguments which will be used to set parser options. See L for details on these. I will repeat the following warning however: "LibXML options are global (unfortunately this is a limitation of the underlying implementation, not this interface)...Note that even two forked processes will share some of the same options, so be careful out there!" # turn off expanding entities [% USE docroot = XML.LibXML("file.xml", expand_entities => 0); =head2 Obtaining Parts of an XML Document XML::LibXML provides two simple mechanisms for obtaining sections of the XML document, both of which can be used from within the Template Toolkit The first of these is to use a XPath statement. Simple values can be found with the C routine: # get the title attribute of the first page node # (note xpath starts counting from one not zero) [% docroot.findvalue("/website/section/page[1]/@title"); %] # get the text contained within a node [% htmldoc.findvalue("/html/body/h1[1]/text()") %] Nodes of the xml document can be found with the C # get all the pages ('pages' is a list of nodes) [% pages = docroot.findnodes("/website/section/page") %] # get the first page (as TT folds single elements arrays # to scalars, 'page1' is the one node that matched) [% page1 = docroot.findnodes("/website/section/page[1]") %] Then further xpath commands can then be applied to those nodes in turn: # get the title attribute of the first page [% page1.findvalue("@title") %] An alternative approach is to use individual method calls to move around the tree. So the above could be written: # get the text of the h1 node [% htmlroot.documentElement .getElementsByLocalName("body").first .getElementsByLocalName("h1").first .textContent %] # get the title of the first page [% docroot.documentElement .getElementsByLocalName("section").first .getElementsByLocalName("page").first .getAttribute("title") %] You should use the technique that makes the most since in the particular situation. These approaches can even be mixed: # get the first page title [% page1 = htmlroot.findnodes("/website/section/page[1]"); page1.getAttribute("title") %] Much more information can be found in L. =head2 Rendering XML The simplest way to use this plugin is simply to extract each value you want to print by hand The title of the first page is '[% docroot.findvalue("/website/section/page[1]/@title") %]' or The title of the first page is '[% docroot.documentElement .getElementsByLocalName("section").first .getElementsByLocalName("page").first .getAttribute("title") %]' You might want to discard whitespace from text areas. XPath can remove all leading and following whitespace, and condense all multiple spaces in the text to single spaces.

[% htmlroot.findvalue("normalize-space( /html/body/p[1]/text() )" | html %]

Note that, as above, when we're inserting the values extracted into a XML or HTML document we have to be careful to re-encode the attributes we need to escape with something like the html filter. A slightly more advanced technique is to extract a whole node and use the toString method call on it to convert it to a string of XML. This is most useful when you are extracting an existing chunk of XML en mass, as things like EbE and EiE tags will be passed thought correctly and entities will be encoded suitably (for example '"' will be turned into '"') # get the second paragraph and insert it here as XML [% htmlroot.findnodes("/html/body/p[2]").toString %] The most powerful technique is to use a view (as defined by the VIEW keyword) to recursively render out the XML. By loading this plugin C methods will be created in the B classes and subclasses. Calling C on a node with a VIEW will cause it to be rendered by the view block matching the local name of that node (with any non alphanumeric charecters turned to underscores. So a EauthorE tag will be rendered by the 'author' block. Text nodes will call the 'text' block with the text of the node. As the blocks can refer back to both the node it was called with and the view they can choose to recursively render out it's children using the view again. To better facilitate this technique the extra methods C (recreate a string of the starting tag, including attributes,) C (recreate a string of the ending tag) and C (when called with a view, will render by calling all the children of that node in turn with that view) have been added. This is probably best shown with a well commented example: # create the view [% VIEW myview notfound => 'passthru' %] # default tag that will recreate the tag 'as is' meaning # that unknown tags will 'passed though' by the view [% BLOCK passthru; item.starttag; item.content(view); item.endtag; END %] # convert all sections to headed paragraphs [% BLOCK section %]

[% item.getAttribute("title") %]

[% item.content(view) %]

[% END %] # urls link to themselves [% BLOCK url %] [% item.content(view) %] [% END %] # email link to themselves with mailtos [% BLOCK email %] [% item.content(view) %] [% END %] # make pod links bold [% BLOCK pod %] [% item.content(view) %] [% END %] # render text, re-encoding the attributes as we go [% BLOCK text; item | html; END %] # render arrays out [% BLOCK list; FOREACH i = item; view.print(i); END ; END %] [% END %] # use it to render the paragraphs [% USE doc = XML.LibXML("mydoc.xml") %] [% doc.findvalue("/doc/page[1]/@title") %] [% sections = doc.findnodes("/doc/page[1]/section"); FOREACH section = sections %] [% section.present(myview); END %] =head1 BUGS In order to detect if a scalar is an open filehandle (which is used if the USE isn't explicit about it's data source) this plugin uses the C routine from B. If you do not have B installed, or the version of B is sufficiently old that it does not support the C routine then a much cruder C check will be employed. Bugs may be reported either via the CPAN RT at http://rt.cpan.org/NoAuth/Bugs.html?Dist=Template-Plugin-XML-LibXML or via the Template Toolkit mailing list: http://www.template-toolkit.org/mailman/listinfo/templates or direct to the author =head1 AUTHOR Written by Mark Fowler Copyright Mark Fowler 2002-3, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. This module wouldn't have been possible without the wonderful work that has been put into the libxml library by the gnome team or the equally wonderful work put in by Matt Sergeant and Christian Glahn in creating XML::LibXML. =head1 SEE ALSO L