You are viewing a plain text version of this content. The canonical link for it is here.
Posted to axkit-dev@xml.apache.org by ma...@apache.org on 2002/01/13 21:45:13 UTC
cvs commit: xml-axkit/t 00basic.t 01provider.t 02language.t 03stylechooser.t 04plugins.t 05mediachooser.t test_module.pl
matts 02/01/13 12:45:12
Added: . AxKit.xs CONTRIB Changes INSTALL MANIFEST
Makefile.PL README SUPPORT TODO axconfig.c
axconfig.h getstyles.c getstyles.h typemap
axkit.org faq.xml features.xml index.xml install.xml
license.xml mailinglist.xml news.xml sidebar.xml
support.xml
axkit.org/docs associating_stylesheets.xml index.xml
introduction.dkb provider-howto.dkb quick_start.dkb
axkit.org/docs/presentations/tpc2001 ax_logo.png redbg.png
tpc-axkit.axp
axkit.org/docs/xpathscript guide.dkb
axkit.org/docs/xsp guide.dkb index.xml
axkit.org/examples cv.css cv.xml cv.xps cv_example.xml
docbook.dkb resume.dtd
axkit.org/img arrow.gif arrow_sel.gif banner2.gif
banner3.gif banner_axkit.gif banner_bg.gif
banner_end.gif bg.gif bullet.gif corner_logo.jpg
corner_logo.png nav_bot.gif nav_left.gif
nav_right.gif nav_top.gif pix.gif rcorner_br.gif
rcorner_wh.gif vsep.gif
axkit.org/stylesheets axkit.css axkit_html.css
body_print_html.xps body_tag_html.xps
docbook_example.xml docbook_print.xps
docbook_screen.xps docbook_tags.xps error.xps
error.xsl irc.xps navbar_html.xps rdf_html.xps
sidebar_html.xps spacer.xps webpage_html.xps
axkit.org/stylesheets/new rdf_html.xps sidebar_html.xps
webpage_html.xps
examples .htaccess default.notxslt default.xps docbook.dkb
docbook_print.xps docbook_screen.xps
docbook_tags.xps index.xml
lib/Apache/AxKit Cache.pm CharsetConv.pm CharsetConv.xs
ConfigReader.pm Exception.pm Language.pm
Makefile.PL Provider.pm typemap
lib/Apache/AxKit/Language AxPoint.pm LibXSLT.pm NotXSLT.pm
PassiveTeX.pm Query.pm Sablot.pm XMLNewsNITF.pm
XMLNewsRDF.pm XPathScript.pm XSLT.pm XSP.pm
lib/Apache/AxKit/Language/XSP TaglibHelper.pm
lib/Apache/AxKit/MediaChooser WAPCheck.pm
lib/Apache/AxKit/Plugin Fragment.pm Passthru.pm
QueryStringCache.pm
lib/Apache/AxKit/Provider File.pm Filter.pm Scalar.pm
lib/Apache/AxKit/StyleChooser Cookie.pm FileSuffix.pm
PathInfo.pm QueryString.pm UserAgent.pm
t 00basic.t 01provider.t 02language.t
03stylechooser.t 04plugins.t 05mediachooser.t
test_module.pl
Log:
Initial file checkin
Revision Changes Path
1.1 xml-axkit/AxKit.xs
Index: AxKit.xs
===================================================================
/* $Id: AxKit.xs,v 1.1 2002/01/13 20:45:08 matts Exp $ */
#ifdef __cplusplus
extern "C" {
#endif
#ifdef WIN32
#define _INC_DIRENT
#define DIR void
#endif
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#include "axconfig.h"
#include "getstyles.h"
#ifdef __cplusplus
}
#endif
SV * error_str = NULL;
#ifdef HAVE_LIBXML2
char * axBuildURI(pool *p, const char *URI, const char *base)
{
return ap_pstrdup(p, (char *)xmlBuildURI(URI, base));
}
#else
char * axBuildURI(pool *p, const char *URI, const char *base)
{
if (URI[0] != '/') {
return ap_pstrdup(p, ap_make_full_path(p, ap_make_dirstr_parent(p, base), URI));
}
else {
return (char*)URI;
}
}
#endif
pool *
get_startup_pool(void)
{
SV *sv = perl_get_sv("Apache::__POOL", FALSE);
if(sv) {
IV tmp = SvIV((SV*)SvRV(sv));
return (pool *)tmp;
}
return NULL;
}
int
call_method_int(SV * obj, char * method)
{
dSP;
int cnt;
int results = -1;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 1);
PUSHs(obj);
PUTBACK;
cnt = perl_call_method(method, G_SCALAR);
SPAGAIN;
if (cnt != 1) {
croak("read method call failed");
}
results = POPi;
FREETMPS;
LEAVE;
return results;
}
SV *
call_method_sv(SV * obj, char * method)
{
dSP;
int cnt;
STRLEN n_a;
SV * results;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 1);
PUSHs(obj);
PUTBACK;
cnt = perl_call_method(method, G_SCALAR);
SPAGAIN;
if (cnt != 1) {
croak("read method call failed");
}
results = NEWSV(0,0);
sv_catsv(results, POPs);
FREETMPS;
LEAVE;
/* SvREFCNT_inc(results); */
return results;
}
#define BUFSIZE 1024
MODULE = AxKit PACKAGE = AxKit
PROTOTYPES: DISABLE
BOOT:
# warn("AxKit: BOOT\n");
if (!ap_find_linked_module(ap_find_module_name(&XS_AxKit))) {
# warn("AxKit: add_module()\n");
ap_add_module(&XS_AxKit);
}
ap_register_cleanup(get_startup_pool(), NULL,
remove_module_cleanup, null_cleanup);
void
END ()
CODE:
# warn("AxKit: END\n");
if (ap_find_linked_module(ap_find_module_name(&XS_AxKit))) {
# warn("AxKit: remove_module()\n");
ap_remove_module(&XS_AxKit);
}
SV *
get_config (r)
Apache r
PREINIT:
axkit_dir_config * cfg;
HV * config;
CODE:
cfg = (axkit_dir_config *)
ap_get_module_config(r->per_dir_config, &XS_AxKit);
if (!cfg) {
XSRETURN_UNDEF;
}
config = ax_get_config(cfg);
RETVAL = newRV_noinc((SV*)config);
OUTPUT:
RETVAL
void
load_module (name)
char * name
CODE:
maybe_load_module(name);
void
reconsecrate (obj, class)
SV * obj
char * class
CODE:
maybe_load_module(class);
sv_bless(obj, gv_stashpv(class, FALSE));
char *
build_uri (r, uri, base)
Apache r
char * uri
char * base
CODE:
RETVAL = axBuildURI(r->pool, uri, base);
OUTPUT:
RETVAL
void
Debug (level, ...)
int level
PREINIT:
STRLEN n_a;
request_rec * r;
SV * str;
int debuglevel;
axkit_dir_config * cfg;
CODE:
r = perl_request_rec(NULL);
if (r == NULL) {
return;
}
cfg = (axkit_dir_config *)
ap_get_module_config(r->per_dir_config, &XS_AxKit);
if (level > cfg->debug_level) {
return;
}
str = NEWSV(0, 256);
sv_setpvn(str, "", 0);
if (items > 1) {
int i;
char * last;
for (i = 1; i < (items - 1); i++) {
sv_catpv(str, SvPV(ST(i), n_a));
}
last = SvPV(ST(items - 1), n_a);
if (last[strlen(last)] == '\n') {
sv_catpvn(str, last, strlen(last) - 1);
}
else {
sv_catpv(str, last);
}
}
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, "[AxKit] %s", SvPV(str, n_a));
SvREFCNT_dec(str);
#ifdef HAVE_LIBXML2
MODULE = AxKit PACKAGE = Apache::AxKit::Provider
PROTOTYPES: DISABLE
SV *
_new(class, r, ...)
char * class
SV * r
PREINIT:
HV * hash;
SV * alternate;
STRLEN n_a;
int item_id;
SV * cfg;
SV * key;
int cnt;
SV * obj;
AV * item_store;
CODE:
hash = newHV();
hv_store(hash, "apache", 6, r, 0);
obj = newRV_noinc((SV*)hash);
sv_bless(obj, gv_stashpv(class, 0));
item_store = newAV();
for (item_id = 2; item_id < items; item_id++) {
av_push(item_store, ST(item_id));
}
if (alternate = call_method_sv(perl_get_sv("AxKit::Cfg", FALSE), "ProviderClass")) {
SV * tmp;
sv_bless(obj, gv_stashsv(alternate, 0));
SvREFCNT_dec(alternate);
}
{
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, (items + 1));
PUSHs(obj);
for (item_id = 0; item_id <= av_len(item_store); item_id++) {
PUSHs(*av_fetch(item_store, item_id, 0));
}
PUTBACK;
cnt = perl_call_method("init", G_VOID);
SPAGAIN;
if (cnt != 0) {
croak("init method call failed");
}
POPs;
FREETMPS;
LEAVE;
}
key = call_method_sv(obj, "key");
{
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 1);
PUSHs(key);
PUTBACK;
cnt = perl_call_pv("AxKit::add_depends", G_VOID);
SPAGAIN;
if (cnt != 1) {
croak("add_depends method call failed");
}
POPs;
FREETMPS;
LEAVE;
}
SvREFCNT_dec(key);
SvREFCNT_dec(item_store);
RETVAL = obj;
OUTPUT:
RETVAL
SV *
xs_get_styles_fh(r, ioref)
Apache r
SV * ioref
PREINIT:
axkit_xml_bits results;
xmlParserCtxtPtr ctxt;
char buffer[BUFSIZE];
int read_length;
SV * tbuff;
SV * tsize;
int done = 0;
int ret;
AV * return_array;
CODE:
results.apache = r;
results.xml_stylesheet = newAV();
results.start_element = 0;
results.start_attribs = 0;
results.dtd = 0;
results.publicid = 0;
ret = -1;
error_str = newSVpv("", 0);
xmlInitParser();
xmlDoValidityCheckingDefaultValue = 0;
xmlSubstituteEntitiesDefaultValue = 0;
xmlLoadExtDtdDefaultValue = 0;
read_length = read_perl(ioref, buffer, 4);
if (read_length > 0) {
ctxt = xmlCreatePushParserCtxt(axkitSAXHandler,
NULL, buffer, read_length, "filename");
ctxt->userData = (void*)&results;
while(read_length = read_perl(ioref, buffer, BUFSIZE)) {
xmlParseChunk(ctxt, buffer, read_length, 0);
}
ret = xmlParseChunk(ctxt, buffer, 0, 1);
xmlFreeParserCtxt(ctxt);
}
sv_2mortal(error_str);
xmlCleanupParser();
if (ret == -1) {
croak("xmlParse couldn't read file!");
}
if (ret != XML_ERR_OK && ret != XML_ERR_UNDECLARED_ENTITY) {
STRLEN len;
croak("xmlParse returned error: %d, %s", ret, SvPV(error_str, len));
}
return_array = newAV();
av_push(return_array, newRV_noinc((SV*)results.xml_stylesheet));
av_push(return_array, newSVpv(results.start_element, 0));
av_push(return_array, newRV_noinc((SV*)results.start_attribs));
if (results.dtd != NULL) {
av_push(return_array, newSVpv(results.dtd, 0));
}
else {
av_push(return_array, NEWSV(1,0));
}
if (results.publicid != NULL) {
av_push(return_array, newSVpv(results.publicid, 0));
}
else {
av_push(return_array, NEWSV(1,0));
}
RETVAL = newRV_noinc((SV*)return_array);
OUTPUT:
RETVAL
SV *
xs_get_styles_str(r, xmlstring)
Apache r
SV * xmlstring
PREINIT:
axkit_xml_bits results;
xmlParserCtxtPtr ctxt;
int ret;
STRLEN len;
char * ptr;
AV * return_array;
CODE:
results.apache = r;
results.xml_stylesheet = newAV();
results.start_element = 0;
results.dtd = 0;
results.publicid = 0;
ptr = SvPV(xmlstring, len);
error_str = newSVpv("", 0);
xmlInitParser();
xmlDoValidityCheckingDefaultValue = 0;
xmlSubstituteEntitiesDefaultValue = 0;
xmlLoadExtDtdDefaultValue = 0;
if (!ptr || len < 4) {
XSRETURN_UNDEF;
}
ret = xmlSAXUserParseMemory(axkitSAXHandler, (void*)&results, ptr, len);
sv_2mortal(error_str);
xmlCleanupParser();
if (ret != XML_ERR_OK && ret != XML_ERR_UNDECLARED_ENTITY) {
croak("xmlParse returned error: %d, %s", ret, SvPV(error_str, len));
}
return_array = newAV();
av_push(return_array, newRV_noinc((SV*)results.xml_stylesheet));
av_push(return_array, newSVpv(results.start_element, 0));
av_push(return_array, newRV_noinc((SV*)results.start_attribs));
if (results.dtd != NULL) {
av_push(return_array, newSVpv(results.dtd, 0));
}
else {
av_push(return_array, NEWSV(1,0));
}
if (results.publicid != NULL) {
av_push(return_array, newSVpv(results.publicid, 0));
}
else {
av_push(return_array, NEWSV(1,0));
}
RETVAL = newRV_noinc((SV*)return_array);
OUTPUT:
RETVAL
#endif /* HAVE_LIBXML2 */
1.1 xml-axkit/CONTRIB
Index: CONTRIB
===================================================================
The following people have contributed to AxKit:
Kip Hampton (moral support, Util taglib, docs, StyleChoosers)
Vincent Cagnard (Apache::Filter support (although it was re-done))
Owen Stenseth (output filtering single point of exit, and others)
Honza Pazdziora (many small patches)
Brian Wheeler (Filter fixes, XPathScript improvements, AxAddDynamicProcessor)
If I've missed anyone, please correct this list as my memory
is poor.
If you wish to contribute to AxKit, please join the axkit-dev
mailing list, by sending a mail to:
axkit-dev-subscribe@xml.apache.org
The list will show you all check-ins and allow you to discuss
the features you want, and how to go about implementing them. Please
bring things up there before randomly sending in a patch.
Also note that all code submitted to inclusion in the AxKit project
becomes the intellectual property of the Apache Software
Foundation, and you may have no claims on code submitted to
the project. However we will list you as a contributor for larger
patches.
Please also see the development guidelines on http://xml.apache.org/
1.1 xml-axkit/Changes
Index: Changes
===================================================================
1.4
- Config directives no longer implemented by Apache::ExtUtils
- New TaglibHelper module, makes writing XSP taglibs almost trivial.
- Build checks for iconv library - should make things smoother on *BSD
- Known bug in Filter provider is fixed
- Major memory leak cleanup
- Added LibXSLT language module (for use with XML::LibXSLT). This is
over twice as fast as XML::Sablotron, and more compliant.
- Added AxAddURIProcessor config directive
- Added AxLogDeclines config directive (replaces PerlSetVar equivalent)
- Added import_templates() to XPathScript
- XSP now uses SAX to generate code
- Sablot.pm allows you to set the outgoing mime type
- Many fixes to AxKit.xs compiled directives (hopefully making it work
for more people now)
- Removed eval{} stuff around XML parsing in Provider.pm as it seemed to
be causing segfaults in Perl 5.6.1
- Fixes to LibXSLT language module to make it work with new versions of
XML::LibXSLT (and the core libxslt library).
- Cleaned up error handling, and error stylesheets significantly
- Removed Storable from XPathScript (to get control of the segfaults)
- Implemented has_changed() for all "cache" bits
- Improved error handling with an AxStackTrace config directive
allowing the Error Stylesheet to get a full stack trace.
- Doc fixes to stop pod2man complaining.
- AxKit now adds AxKit/Version to your Server string (for netcraft!)
- Switched xml_string to pnotes so you can have binary nulls in the output
(e.g. for PDFs).
- Added some configuration tidbits to INSTALL
- Much better (though not 100% there) test harness code
- Major fixes to Makefile.PL (to work better on *BSD and Win32)
- XML::Parser no longer needed if you have libxml2 installed. This will
allow AxKit to work with Apaches that have expat enabled!
- XSP pages can implement a has_changed() function, which allows you to
control the caching of the results (can have significant performance
increases on dynamic pages)
- Providers responsible for returning declined (this allows for non-file
providers to not end up in a 404 when doing e.g. passthru)
- Added AxNoCache option to turn off caching on purpose.
- split AxKit.xs into more managable files
- Better error messages from CharsetConv (iconv)
- Added a PDF slideshow builder called AxPoint
- Sablot fix for changing content-type.
- Much cleaner handling of character set conversions
- AxKit::Apache->request() added (similar to Apache->request())
- Cache maintains content-type more sanely.
- Many, many minor bug fixes.
1.3
- Re-written XSP engine
- Much better dependencies checking
- XPathScript adds import_template() function and other minor changes
- Better shared memory usage when using directives in httpd.conf
- All XSP namespaces updated to Apache normalised versions (these are
http://apache.org/xsp/<module>/<version> although these may well
change again shortly)
1.2
- XPathScript can interpolate "{xpath}" in $t hash
- reduced required modules list
- Removed Sablotron from AxKit.pm due to some segfaults
- Fixed segfaults in aborted conversions with iconv
- Added AxResetProcessors
- Improved Apache::Filter support
- Much improved installer
- Many XSP changes/fixes. Now uses XML::XPath DOM tree.
- Memoize methods that call stat() in Cache and Providers
- Many miscellaneous improvements
1.1
- Uses iconv rather than Unicode::* modules to do character
set conversions
- Fix for AxMediaType/AxStyleName bug (Owen Stenseth)
- Fix for compilation under mod_perl 1.24_01
- Fix for relative URI's and file:// sub requests
- Minor stylesheet cache bug fixed
- Updated Filter provider for Apache::Filter >= 1.13
- Fix for filehandle leak
- Fix for Virtual Host Stash bug.
1.1 xml-axkit/INSTALL
Index: INSTALL
===================================================================
Installation of AxKit has been designed to be as simple as
possible (but we might not always get it right!).
If you do not have Apache and mod_perl installed, please scroll
down to "INSTALLATION OF MOD_PERL/APACHE".
CPAN SHELL WAY
The easiest way to install it is from the CPAN shell:
$ su
$ perl -MCPAN -e shell
cpan> install AxKit
This will download AxKit, and install any pre-requisite
modules before installing it. You will also be asked questions
regarding optional modules that you may wish to install.
WITHOUT THE CPAN SHELL
If you do not have network access (due to firewalls), installation
is also fairly simple, it follows the common perl installation
idiom:
$ perl Makefile.PL
$ make
$ make test
$ su
$ make install
The installer will check pre-requisite modules and automatically
install anything that is an absolute requirement before running.
INSTALLATION OF MOD_PERL/APACHE
Note that AxKit WILL NOT install Apache and mod_perl for you. That
is simply because these are extremely large and complex packages,
and you should install them yourself. However some people have
experienced problems with their installation of mod_perl (for
example segmentation faults). So here is my recipe for a stable
installation:
Download mod_perl from http://perl.apache.org/
Download Apache from http://www.apache.org/httpd.html
Extract both archives in a suitable location (I use /tmp)
Do not extract one archive in the other's directory - this
will cause you problems.
Now cd to the mod_perl directory, and enter the following:
$ perl Makefile.PL \
> EVERYTHING=1 \
> USE_APACI=1 \
> DYNAMIC=1 \
> APACHE_PREFIX=/opt/apache \
> APACHE_SRC=../apache_1.3.12/src \
> DO_HTTPD=1 \
> APACI_ARGS="--enable-module=so --enable-shared=info
> --enable-shared=proxy --enable-shared=rewrite
> --enable-shared=log_agent"
$ make
$ su
$ make install
Note that in the above, the $ and > are part of your shell prompt,
not part of the commands to enter!
Also note that the paths for the APACHE_SRC and APACHE_PREFIX should
be fixed to the version of apache you downloaded, and the location
you wish to install Apache into, respectively.
ADVANCED INSTALLATION TIPS
The following options can be passed on the command line to AxKit's
Makefile.PL:
DEBUG=1
This option turns on lots of output from the running of Makefile.PL,
use it in case you have problems installing AxKit and wish to
send email to axkit-users@axkit.org with your problem.
EXPAT_OPTS="..."
Setting this option is only relevant if you do not already have
libexpat installed. It lists options to be passed to libexpat's
./configure command. Such options might be "--prefix=/usr" which
will install libexpat in /usr/lib, rather than the default
location (which will likely be /usr/local/lib).
LIBS="-L/path/to/expat/lib -lexpat"
Allows you to set your library search path.
INC="-I/path/to/expat/include"
Allows you to set the include search path.
NO_DIRECTIVES=1
This option turns off AxKit's apache configuration directives,
which means you will have to set these via PerlSetVar instead.
This is useful because sometimes AxKit and mod_php can cause
conflicts when using configuration directives (and results
in segfaults). This is hopefully fixed in mod_perl 1.25.
Note that you *must* manually remove all traces of AxKit from
your /usr/lib/perl5/site_perl directory before re-running
Makefile.PL with NO_DIRECTIVES=1 because unfortunately this
causes AxKit to be installed in a completely different location,
yet mod_perl will pick up the original copy compiled with
directives, which is probably not what you want at all.
CONFIGURATION
In general you should read the AxKit Quick Start guide on
http://axkit.org/, but for the impatient, read on.
Once you have AxKit installed, you need to configure Apache to
associate XML files with AxKit. In doing so, there are a few
considerations to make. If your installation is only going to be
handling XML files (ex: no PHP or cgi requests) then it's safe to
include the following directives in your httpd.conf:
# note the PerlModule line has to be *outside* any
# <Files>, <Location>, <Directory> or <VirtualHost>
# sections.
PerlModule AxKit
SetHandler axkit
AxCacheDir /tmp/axkit_cache/
AxAddStyleMap text/xsl Apache::AxKit::Language::LibXSLT
If, however, you are using other content handler modules, such
as PHP or mod_cgi to send content to the browser, you will more
than likely want to use AddHandler:
PerlModule AxKit
AddHandler axkit .xml
AddHandler axkit .xsp
Another symptom that you want to wrap your configuration in a conditional
block is if you turn on debugging (see perldoc AxKit for AxDebugLevel),
request a test cgi program, receive the plain text version of the test
cgi and see something similar to the following in your error logs:
[warn] [AxKit] : handler called for /cgi-bin/test-cgi
[warn] [AxKit] : checking if we process this resource
[warn] [AxKit] [DECLINED] '/usr/local/www/cgi-bin/test-cgi' not
recognized as XML
GETTING HELP
If you are still stuck getting this to work, first of all, see the
AxKit FAQ at http://axkit.org/faq.xml - especially if your Apache
is segfaulting when using AxKit.
Then please send problem reports to axkit-users@axkit.org along with
information about what version of the following items you are running:
AxKit
mod_perl
Apache
Perl
Operating System
However please note that installation problems regarding mod_perl,
Sablotron, or Apache should be directed to the appropriate places
for those modules, not to the axkit-users mailing list. Thank you.
Please do not send email direct to matt@sergeant.org for problems
with AxKit. Thank you.
IF EVERYTHING WORKS
Congratulations! You have a running AxKit. Now please visit
http://axkit.org/docs/quick_start.dkb for a guide to your first pages
with AxKit.
1.1 xml-axkit/MANIFEST
Index: MANIFEST
===================================================================
MANIFEST
README
Makefile.PL
AxKit.pm
AxKit.xs
AxKit.spec
axconfig.h
axconfig.c
getstyles.h
getstyles.c
CONTRIB
INSTALL
SUPPORT
Changes
typemap
t/00basic.t
t/01provider.t
t/02language.t
t/03stylechooser.t
t/04plugins.t
t/05mediachooser.t
t/test_module.pl
lib/Apache/AxKit/StyleChooser/QueryString.pm
lib/Apache/AxKit/StyleChooser/PathInfo.pm
lib/Apache/AxKit/StyleChooser/FileSuffix.pm
lib/Apache/AxKit/StyleChooser/Cookie.pm
lib/Apache/AxKit/StyleChooser/UserAgent.pm
lib/Apache/AxKit/MediaChooser/WAPCheck.pm
lib/Apache/AxKit/Plugin/Passthru.pm
lib/Apache/AxKit/Plugin/Fragment.pm
lib/Apache/AxKit/Plugin/QueryStringCache.pm
lib/Apache/AxKit/Provider.pm
lib/Apache/AxKit/Provider/File.pm
lib/Apache/AxKit/Provider/Scalar.pm
lib/Apache/AxKit/Provider/Filter.pm
lib/Apache/AxKit/Language.pm
lib/Apache/AxKit/Language/XPathScript.pm
lib/Apache/AxKit/Language/AxPoint.pm
lib/Apache/AxKit/Language/Sablot.pm
lib/Apache/AxKit/Language/LibXSLT.pm
lib/Apache/AxKit/Language/PassiveTeX.pm
lib/Apache/AxKit/Language/XMLNewsRDF.pm
lib/Apache/AxKit/Language/XMLNewsNITF.pm
lib/Apache/AxKit/Language/XSP.pm
lib/Apache/AxKit/Language/XSP/TaglibHelper.pm
lib/Apache/AxKit/ConfigReader.pm
lib/Apache/AxKit/Cache.pm
lib/Apache/AxKit/Exception.pm
lib/Apache/AxKit/CharsetConv.pm
lib/Apache/AxKit/CharsetConv.xs
lib/Apache/AxKit/Makefile.PL
lib/Apache/AxKit/typemap
examples/.htaccess
examples/index.xml
examples/default.xps
examples/default.notxslt
examples/docbook_tags.xps
examples/docbook_screen.xps
examples/docbook_print.xps
examples/docbook.dkb
axkit.org/index.xml
axkit.org/faq.xml
axkit.org/features.xml
axkit.org/install.xml
axkit.org/license.xml
axkit.org/news.xml
axkit.org/mailinglist.xml
axkit.org/support.xml
axkit.org/sidebar_new.xml
axkit.org/sidebar.xml
axkit.org/img/arrow.gif
axkit.org/img/arrow_sel.gif
axkit.org/img/banner2.gif
axkit.org/img/banner3.gif
axkit.org/img/banner_axkit.gif
axkit.org/img/banner_bg.gif
axkit.org/img/banner_end.gif
axkit.org/img/bg.gif
axkit.org/img/bullet.gif
axkit.org/img/corner_logo.jpg
axkit.org/img/corner_logo.png
axkit.org/img/nav_bot.gif
axkit.org/img/nav_left.gif
axkit.org/img/nav_right.gif
axkit.org/img/nav_top.gif
axkit.org/img/pix.gif
axkit.org/img/rcorner_br.gif
axkit.org/img/rcorner_wh.gif
axkit.org/img/vsep.gif
axkit.org/docs/associating_stylesheets.xml
axkit.org/docs/index.xml
axkit.org/docs/introduction.dkb
axkit.org/docs/provider-howto.dkb
axkit.org/docs/quick_start.dkb
axkit.org/docs/xpathscript/guide.dkb
axkit.org/docs/xsp/guide.dkb
axkit.org/docs/xsp/index.xml
axkit.org/docs/xsp/sqltaglib.dkb
axkit.org/docs/presentations/tpc2001/tpc-axkit.axp
axkit.org/docs/presentations/tpc2001/redbg.png
axkit.org/docs/presentations/tpc2001/ax_logo.png
axkit.org/examples/cv.xml
axkit.org/examples/cv_example.xml
axkit.org/examples/cv.css
axkit.org/examples/cv.xps
axkit.org/examples/docbook.dkb
axkit.org/examples/resume.dtd
axkit.org/stylesheets/axkit.css
axkit.org/stylesheets/axkit_html.css
axkit.org/stylesheets/body_print_html.xps
axkit.org/stylesheets/body_tag_html.xps
axkit.org/stylesheets/docbook_example.xml
axkit.org/stylesheets/docbook_print.xps
axkit.org/stylesheets/docbook_screen.xps
axkit.org/stylesheets/docbook_tags.xps
axkit.org/stylesheets/error.xps
axkit.org/stylesheets/error.xsl
axkit.org/stylesheets/irc.xps
axkit.org/stylesheets/navbar_html.xps
axkit.org/stylesheets/new/rdf_html.xps
axkit.org/stylesheets/new/sidebar_html.xps
axkit.org/stylesheets/new/webpage_html.xps
axkit.org/stylesheets/rdf_html.xps
axkit.org/stylesheets/sidebar_html.xps
axkit.org/stylesheets/webpage_html.xps
axkit.org/stylesheets/spacer.xps
expat-1.95.1/Changes
expat-1.95.1/COPYING
expat-1.95.1/MANIFEST
expat-1.95.1/Makefile.in
expat-1.95.1/README
expat-1.95.1/aclocal.m4
expat-1.95.1/config.hin
expat-1.95.1/configure
expat-1.95.1/configure.in
expat-1.95.1/conftools/config.guess
expat-1.95.1/conftools/config.sub
expat-1.95.1/conftools/install-sh
expat-1.95.1/conftools/ltconfig
expat-1.95.1/conftools/ltmain.sh
expat-1.95.1/conftools/missing
expat-1.95.1/conftools/mkinstalldirs
expat-1.95.1/doc/reference.html
expat-1.95.1/doc/style.css
expat-1.95.1/examples/Makefile.in
expat-1.95.1/examples/outline.c
expat-1.95.1/lib/Makefile.in
expat-1.95.1/lib/ascii.h
expat-1.95.1/lib/asciitab.h
expat-1.95.1/lib/expat.dsp
expat-1.95.1/lib/expat.h
expat-1.95.1/lib/iasciitab.h
expat-1.95.1/lib/latin1tab.h
expat-1.95.1/lib/nametab.h
expat-1.95.1/lib/utf8tab.h
expat-1.95.1/lib/xmlparse.c
expat-1.95.1/lib/xmlrole.c
expat-1.95.1/lib/xmlrole.h
expat-1.95.1/lib/xmltok.c
expat-1.95.1/lib/xmltok.h
expat-1.95.1/lib/xmltok_impl.c
expat-1.95.1/lib/xmltok_impl.h
expat-1.95.1/lib/xmltok_ns.c
expat-1.95.1/lib/winconfig.h
expat-1.95.1/xmlwf/Makefile.in
expat-1.95.1/xmlwf/codepage.c
expat-1.95.1/xmlwf/codepage.h
expat-1.95.1/xmlwf/ct.c
expat-1.95.1/xmlwf/filemap.h
expat-1.95.1/xmlwf/readfilemap.c
expat-1.95.1/xmlwf/unixfilemap.c
expat-1.95.1/xmlwf/wfcheck.c
expat-1.95.1/xmlwf/wfcheck.h
expat-1.95.1/xmlwf/wfcheckmessage.c
expat-1.95.1/xmlwf/win32filemap.c
expat-1.95.1/xmlwf/xmlfile.c
expat-1.95.1/xmlwf/xmlfile.h
expat-1.95.1/xmlwf/xmlmime.c
expat-1.95.1/xmlwf/xmlmime.h
expat-1.95.1/xmlwf/xmltchar.h
expat-1.95.1/xmlwf/xmlurl.h
expat-1.95.1/xmlwf/xmlwf.c
expat-1.95.1/xmlwf/xmlwf.dsp
expat-1.95.1/xmlwf/xmlwin32url.cxx
XML-Parser-2.30/samples/xmlstats
XML-Parser-2.30/samples/REC-xml-19980210.xml
XML-Parser-2.30/samples/canontst.xml
XML-Parser-2.30/samples/xmlfilter
XML-Parser-2.30/samples/canonical
XML-Parser-2.30/samples/ctest.dtd
XML-Parser-2.30/samples/xmlcomments
XML-Parser-2.30/t/ext.ent
XML-Parser-2.30/t/encoding.t
XML-Parser-2.30/t/stream.t
XML-Parser-2.30/t/foo.dtd
XML-Parser-2.30/t/partial.t
XML-Parser-2.30/t/cdata.t
XML-Parser-2.30/t/namespaces.t
XML-Parser-2.30/t/file.t
XML-Parser-2.30/t/finish.t
XML-Parser-2.30/t/astress.t
XML-Parser-2.30/t/external_ent.t
XML-Parser-2.30/t/defaulted.t
XML-Parser-2.30/t/ext2.ent
XML-Parser-2.30/t/skip.t
XML-Parser-2.30/t/parament.t
XML-Parser-2.30/t/decl.t
XML-Parser-2.30/Parser/Encodings/windows-1250.enc
XML-Parser-2.30/Parser/Encodings/euc-kr.enc
XML-Parser-2.30/Parser/Encodings/Japanese_Encodings.msg
XML-Parser-2.30/Parser/Encodings/x-sjis-unicode.enc
XML-Parser-2.30/Parser/Encodings/iso-8859-2.enc
XML-Parser-2.30/Parser/Encodings/iso-8859-3.enc
XML-Parser-2.30/Parser/Encodings/iso-8859-4.enc
XML-Parser-2.30/Parser/Encodings/iso-8859-5.enc
XML-Parser-2.30/Parser/Encodings/x-euc-jp-jisx0221.enc
XML-Parser-2.30/Parser/Encodings/iso-8859-7.enc
XML-Parser-2.30/Parser/Encodings/iso-8859-8.enc
XML-Parser-2.30/Parser/Encodings/iso-8859-9.enc
XML-Parser-2.30/Parser/Encodings/x-sjis-cp932.enc
XML-Parser-2.30/Parser/Encodings/x-euc-jp-unicode.enc
XML-Parser-2.30/Parser/Encodings/x-sjis-jdk117.enc
XML-Parser-2.30/Parser/Encodings/big5.enc
XML-Parser-2.30/Parser/Encodings/README
XML-Parser-2.30/Parser/Encodings/x-sjis-jisx0221.enc
XML-Parser-2.30/Parser/LWPExternEnt.pl
XML-Parser-2.30/Expat/Expat.xs
XML-Parser-2.30/Expat/typemap
XML-Parser-2.30/Expat/Makefile.PL
XML-Parser-2.30/Expat/encoding.h
XML-Parser-2.30/Expat/Expat.pm
XML-Parser-2.30/MANIFEST
XML-Parser-2.30/Changes
XML-Parser-2.30/Makefile.PL
XML-Parser-2.30/README
XML-Parser-2.30/Parser.pm
Digest-MD5-2.12/MD2/MD2.pm
Digest-MD5-2.12/MD2/MD2.xs
Digest-MD5-2.12/MD2/t/badfile.t
Digest-MD5-2.12/MD2/t/md2.t
Digest-MD5-2.12/MD2/rfc1319.txt
Digest-MD5-2.12/MD2/typemap
Digest-MD5-2.12/MD2/Makefile.PL
Digest-MD5-2.12/SHA1/Makefile.PL
Digest-MD5-2.12/SHA1/t/sha1.t
Digest-MD5-2.12/SHA1/t/badfile.t
Digest-MD5-2.12/SHA1/t/sha.t
Digest-MD5-2.12/SHA1/SHA1.pm
Digest-MD5-2.12/SHA1/SHA1.xs
Digest-MD5-2.12/SHA1/lib/SHA.pm
Digest-MD5-2.12/SHA1/fip180-1.html
Digest-MD5-2.12/SHA1/fip180-1.gif
Digest-MD5-2.12/SHA1/typemap
Digest-MD5-2.12/t/digest.t
Digest-MD5-2.12/t/badfile.t
Digest-MD5-2.12/t/rfc2202.t
Digest-MD5-2.12/t/md5.t
Digest-MD5-2.12/t/files.t
Digest-MD5-2.12/t/md5-aaa.t
Digest-MD5-2.12/rfc2104.txt
Digest-MD5-2.12/MD5.pm
Digest-MD5-2.12/lib/Digest.pm
Digest-MD5-2.12/lib/Digest/HMAC_MD5.pm
Digest-MD5-2.12/lib/Digest/HMAC_SHA1.pm
Digest-MD5-2.12/lib/Digest/HMAC.pm
Digest-MD5-2.12/lib/MD5.pm
Digest-MD5-2.12/hints/dec_osf.pl
Digest-MD5-2.12/hints/irix_6.pl
Digest-MD5-2.12/MANIFEST
Digest-MD5-2.12/Changes
Digest-MD5-2.12/MD5.xs
Digest-MD5-2.12/typemap
Digest-MD5-2.12/examples/twdigest.pl
Digest-MD5-2.12/examples/mddriver.pl
Digest-MD5-2.12/Makefile.PL
Digest-MD5-2.12/README
Digest-MD5-2.12/rfc1321.txt
Compress-Zlib-1.09/t/examples.t
Compress-Zlib-1.09/t/zlib.t
Compress-Zlib-1.09/README
Compress-Zlib-1.09/examples/gzgrep
Compress-Zlib-1.09/examples/gzcat
Compress-Zlib-1.09/examples/filtdef
Compress-Zlib-1.09/examples/filtinf
Compress-Zlib-1.09/examples/gzstream
Compress-Zlib-1.09/MANIFEST
Compress-Zlib-1.09/ANNOUNCE
Compress-Zlib-1.09/Zlib.xs
Compress-Zlib-1.09/typemap
Compress-Zlib-1.09/config.in
Compress-Zlib-1.09/Makefile.PL
Compress-Zlib-1.09/Makefile.NT
Compress-Zlib-1.09/Zlib.pm
Error-0.13/t/02order.t
Error-0.13/t/01throw.t
Error-0.13/Error.pm
Error-0.13/MANIFEST
Error-0.13/ChangeLog
Error-0.13/Makefile.PL
Error-0.13/Error.ppd
Error-0.13/example
Error-0.13/README
libapreq-0.31_03/CREDITS
libapreq-0.31_03/Changes
libapreq-0.31_03/INSTALL
libapreq-0.31_03/LICENSE
libapreq-0.31_03/MANIFEST
libapreq-0.31_03/Makefile.PL
libapreq-0.31_03/Makefile.am
libapreq-0.31_03/README
libapreq-0.31_03/ToDo
libapreq-0.31_03/acconfig.h
libapreq-0.31_03/acinclude.m4
libapreq-0.31_03/aclocal.m4
libapreq-0.31_03/config.guess
libapreq-0.31_03/config.sub
libapreq-0.31_03/configure.in
libapreq-0.31_03/install-sh
libapreq-0.31_03/libapreq.pod
libapreq-0.31_03/libtool
libapreq-0.31_03/ltconfig
libapreq-0.31_03/ltmain.sh
libapreq-0.31_03/mkinstalldirs
libapreq-0.31_03/typemap
libapreq-0.31_03/Cookie/Cookie.pm
libapreq-0.31_03/Cookie/Cookie.xs
libapreq-0.31_03/Cookie/Makefile.PL
libapreq-0.31_03/Request/Makefile.PL
libapreq-0.31_03/Request/Request.pm
libapreq-0.31_03/Request/Request.xs
libapreq-0.31_03/c/Makefile.PL
libapreq-0.31_03/c/Makefile.am
libapreq-0.31_03/c/Makefile.noperl
libapreq-0.31_03/c/apache_cookie.c
libapreq-0.31_03/c/apache_cookie.h
libapreq-0.31_03/c/apache_multipart_buffer.c
libapreq-0.31_03/c/apache_multipart_buffer.h
libapreq-0.31_03/c/apache_request.c
libapreq-0.31_03/c/apache_request.h
libapreq-0.31_03/c/libapreq_config.h.in
libapreq-0.31_03/c/stamp-h.in
libapreq-0.31_03/c/Makefile.in
libapreq-0.31_03/eg/c/testapreq/Makefile.apxs
libapreq-0.31_03/eg/c/testapreq/Makefile.tmpl
libapreq-0.31_03/eg/c/testapreq/mod_testapreq.c
libapreq-0.31_03/eg/c/testapreq/mod_testapreq.module
libapreq-0.31_03/eg/perl/cookie.pl
libapreq-0.31_03/eg/perl/file_upload.pl
libapreq-0.31_03/lib/Apache/libapreq.pm
libapreq-0.31_03/Makefile.in
libapreq-0.31_03/configure
1.1 xml-axkit/Makefile.PL
Index: Makefile.PL
===================================================================
# $Id: Makefile.PL,v 1.1 2002/01/13 20:45:08 matts Exp $
package AxKit;
# file Makefile.PL
use ExtUtils::MakeMaker;
use Text::Wrap ();
use Cwd;
require 5.005;
sub wrap ($)
{
my $text = shift;
$text =~ s/^\s+//;
$text =~ s/\s+$//;
Text::Wrap::wrap('', '', $text) . "\n\n";
}
my @DIRS = ('./lib/Apache/AxKit', './Apache-MimeXML');
my $from_dir = cwd;
my $expat_dir = "expat-1.95.1";
######################################################
# Standard bits required for have_library and friends
my %config;
$|=1; # flush output
while($_ = shift @ARGV) {
my ($k, $v) = split /=/, $_, 2;
$config{$k} = $v;
}
$DEBUG = delete $config{DEBUG};
######################################################
if (!have_module('mod_perl', '1.17')) {
die wrap(<<DEATH);
AxKit requires mod_perl to be able to even run. You do not appear to
have mod_perl installed (or your mod_perl is earlier than version 1.17).
You can download mod_perl from http://perl.apache.org/
DEATH
}
require Apache::src;
my $install_expat = 0;
unless (have_module('XML::Parser', '2.27')) {
# XML::Parser not installed
if (!have_library("expat")) {
print "compiling expat... ";
eval {
my $expat_opts = delete $config{EXPAT_OPTS};
xsystem("cd $expat_dir && ./configure $expat_opts && make");
$install_expat = 1;
$config{"EXPATINCPATH"} = MM->catdir($from_dir, $expat_dir, 'lib');
$config{"EXPATLIBPATH"} = MM->catdir($from_dir, $expat_dir, 'lib', '.libs');
print "ok\n";
};
if ($@) {
print "failed\n";
die "Compilation of expat failed. Re-run with DEBUG=1 to see why.\n";
}
}
push @DIRS, 'XML-Parser-2.30';
}
unless (have_module('Digest::MD5', '2.09')) {
push @DIRS, 'Digest-MD5-2.12';
}
unless (have_module('Compress::Zlib')) {
push @DIRS, 'Compress-Zlib-1.09';
}
unless (have_module('Error', '0.13')) {
push @DIRS, 'Error-0.13';
}
unless (have_module('Apache::Request', '0.31_03')) {
push @DIRS, 'libapreq-0.31_03';
}
test_module('XML::XPath', '1.00', <<"REASON");
XML::XPath 1.00 or higher is required for XPathScript
to work. You don't have to install it, but you won't be able
to use XPathScript stylesheets without it.
REASON
test_module('HTTP::GHTTP', '1.00', <<"REASON");
HTTP::GHTTP is used for retrieving remote URIs within AxKit.
We use HTTP::GHTTP because it is significantly faster and lighter
than LWP, however AxKit will use LWP if it cannot locate
HTTP::GHTTP.
REASON
test_module('XML::Sablotron', '0.40', <<"REASON");
XML::Sablotron 0.40 or higher is required for our Sablotron
XSLT processor to work. You don't have to install it,
but you won't be able to use Apache::AxKit::Language::Sablot without it.
REASON
test_module('XML::LibXML', '1.31', <<"REASON");
XML::LibXML 1.31 or higher is required for the XSP engine
to work. You don't have to install it, but you won't
be able to use XSP pages without it.
REASON
test_module('XML::LibXSLT', '1.31', <<"REASON");
XML::LibXSLT 1.31 or higher is required for our LibXSLT
XSLT processor to work. You don't have to install it,
but you won't be able to use Apache::AxKit::Language::LibXSLT without it.
REASON
eval {
print "running xml2-config... ";
my $libs = backtick('xml2-config --libs');
$config{LIBS} .= " $libs" if $libs;
my $inc = backtick('xml2-config --cflags');
$config{INC} .= " $inc" if $inc;
print "ok\n";
};
if ($@) {
print "failed (will try to find libxml2 anyway)\n";
}
if (have_library('xml2')) {
$config{DEFINE} .= ' -DHAVE_LIBXML2';
$config{LIBS} .= ' -lxml2 -lz';
}
elsif (!$is_Win32) {
my $apache;
print <<EOT;
We will now check if your apache has XML symbols in it, to be sure that
AxKit will work under your version of Apache. If you do not wish to
perform this check, please just enter 0 (zero) below.
EOT
while (!is_apache($apache)) {
$apache = prompt("Path to apache httpd?", "/usr/local/apache/bin/httpd");
last if $apache eq "0";
}
if ($apache ne "0") {
print "checking for expat symbols in apache... ";
eval {
my $symbols = backtick("grep -i xml $apache");
if ($symbols) {
die wrap(<<DEATH);
Your Apache has XML symbols in it. This means it will segfault when you
try and use it with XML::Parser. Your options are to either recompile
Apache, or to install libxml2 from http://www.xmlsoft.org and re-install
AxKit. However if you do not recompile Apache without expat/xml support,
you will not be able to use XPathScript, XSP, or any other modules that
make use of XML::Parser. Please see the AxKit FAQ on http://axkit.org
for more details.
DEATH
}
};
print "ok\n";
}
}
if ($is_Win32) {
if (!have_module('mod_perl', '1.24_01')) {
die wrap(<<DEATH);
AxKit on Win32 requires mod_perl greater than 1.24_01 in order to
get the paths to the Apache and mod_perl build directories.
DEATH
}
require Apache::MyConfig;
$config{INC} .= qq{ -I"$Apache::MyConfig::Setup{MODPERL_INC}/../.." }
. qq{ -I"$Apache::MyConfig::Setup{APACHE_INC}" }
. qq{ -I"$Apache::MyConfig::Setup{APACHE_INC}/../os/win32" };
$config{CCFLAGS} = $Config{ccflags} .
' -D_WINSOCK2API_ -D_MSWSOCK_ -D_INC_SIGNAL -D_INC_MALLOC ';
$config{LIBS} =
qq{ -L"$Apache::MyConfig::Setup{APACHE_LIB}" -lApacheCore } .
qq{ -L"$Apache::MyConfig::Setup{MODPERL_LIB}" -lmod_perl};
}
$config{INC} .= ' ' . Apache::src->new->inc;
if ($DEBUG) {
print "calling WriteMakefile with config:\n";
foreach my $k (keys %config) {
print "$k = $config{$k}\n";
}
}
foreach my $k (keys %config) {
push @ARGV, "$k=$config{$k}";
}
%config = () if $] > 5.00560;
rm_f($DEVNULL) if $is_Win32;
WriteMakefile(
'NAME' => __PACKAGE__,
'VERSION_FROM' => 'AxKit.pm',
'AUTHOR' => 'AxKit.com Limited - http://axkit.com/',
'ABSTRACT' => 'AxKit is an XML Application Server for mod_perl',
'DIR' => [ @DIRS ],
'OBJECT' => '$(O_FILES)',
%config,
);
#################################################################
# Functions
#################################################################
use Config;
use Cwd;
use Symbol;
use File::Spec;
use vars qw/$DEVNULL $is_Win32/;
BEGIN {
$is_Win32 = ($^O =~ /Win32/);
if ($is_Win32) {
$DEVNULL = 'DEVNULL';
}
else {
$DEVNULL = eval { File::Spec->devnull };
if ($@) { $DEVNULL = '/dev/null' }
}
}
sub rm_f {
my @files = @_;
my @realfiles;
foreach (@files) {
push @realfiles, glob($_);
}
if (@realfiles) {
chmod(0777, @realfiles);
unlink(@realfiles);
}
}
sub rm_fr {
my @files = @_;
my @realfiles;
foreach (@files) {
push @realfiles, glob($_);
}
foreach my $file (@realfiles) {
if (-d $file) {
# warn("$file is a directory\n");
rm_fr("$file/*");
rm_fr("$file/.exists");
rmdir($file) || die "Couldn't remove $file: $!";
}
else {
# warn("removing $file\n");
chmod(0777, $file);
unlink($file);
}
}
}
sub xsystem {
my $command = shift;
if ($DEBUG) {
print $command, "\n";
if (system($command) != 0) {
die "system call to '$command' failed";
}
return 1;
}
open(OLDOUT, ">&STDOUT");
open(OLDERR, ">&STDERR");
open(STDOUT, ">$DEVNULL");
open(STDERR, ">$DEVNULL");
my $retval = system($command);
open(STDOUT, ">&OLDOUT");
open(STDERR, ">&OLDERR");
if ($retval != 0) {
die "system call to '$command' failed";
}
return 1;
}
sub backtick {
my $command = shift;
if ($DEBUG) {
print $command, "\n";
my $results = `$command`;
chomp $results;
if ($? != 0) {
die "backticks call to '$command' failed";
}
return $results;
}
open(OLDOUT, ">&STDOUT");
open(OLDERR, ">&STDERR");
open(STDOUT, ">$DEVNULL");
open(STDERR, ">$DEVNULL");
my $results = `$command`;
my $retval = $?;
open(STDOUT, ">&OLDOUT");
open(STDERR, ">&OLDERR");
if ($retval != 0) {
die "backticks call to '$command' failed";
}
chomp $results;
return $results;
}
sub try_link0 {
my ($src, $opt) = @_;
my $cfile = gensym();
# local $config{LIBS};
# $config{LIBS} .= $opt;
unless (mkdir(".testlink", 0777)) {
rm_fr(".testlink");
mkdir(".testlink", 0777) || die "Cannot create .testlink dir: $!";
}
chdir(".testlink");
open($cfile, ">Conftest.xs") || die "Cannot write to file Conftest.xs: $!";
print $cfile <<EOT;
#ifdef __cplusplus
extern "C" {
#endif
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#ifdef __cplusplus
}
#endif
EOT
print $cfile $src;
print $cfile <<EOT;
MODULE = Conftest PACKAGE = Conftest
PROTOTYPES: DISABLE
EOT
close($cfile);
open($cfile, ">Conftest.pm") || die "Cannot write to file Conftest.pm: $!";
print $cfile <<'EOT';
package Conftest;
$VERSION = 1.0;
require DynaLoader;
@ISA = ('DynaLoader');
bootstrap Conftest $VERSION;
1;
EOT
close($cfile);
open($cfile, ">Makefile.PL") || die "Cannot write to file Makefile.PL: $!";
print $cfile <<'EOT';
use ExtUtils::MakeMaker;
my %config;
while($_ = shift @ARGV) {
my ($k, $v) = split /=/, $_, 2;
$config{$k} = $v;
}
WriteMakefile(NAME => "Conftest", VERSION_FROM => "Conftest.pm", %config);
EOT
close($cfile);
open($cfile, ">test.pl") || die "Cannot write to file test.pl: $!";
print $cfile <<EOT;
use Test; BEGIN { plan tests => 1; } END { ok(\$loaded) }
use Conftest; \$loaded++;
EOT
close($cfile);
xsystem("$^X Makefile.PL " . join(' ', map { "'$_=$config{$_}'" } keys %config));
xsystem("$Config{make} test 'OTHERLDFLAGS=$opt'");
}
sub try_link {
my $start_dir = cwd();
my $result = eval {
try_link0(@_);
};
warn $@ if $DEBUG && $@;
chdir($start_dir);
rm_fr(".testlink");
return $result;
}
sub have_library {
my ($lib, $func) = (@_, "blank");
printf("checking for %s() in -l%s... ", $func, $lib) if $func ne "blank";
printf("looking for -l%s... ", $lib) if $func eq "blank";
my $result;
if ($func) {
my $libs = $is_Win32 ? " $lib.lib " : "-l$lib";
if ($is_Win32) {
$result = try_link(<<"SRC", $libs);
#include <windows.h>
#include <winsock.h>
blank() { return 0; }
int t() { ${func}(); return 0; }
SRC
unless ($result) {
$result = try_link(<<"SRC", $libs);
#include <windows.h>
#include <winsock.h>
blank() { return 0; }
int t() { void ((*p)()); p = (void ((*)()))${func}; return 0; }
SRC
}
}
else {
$result = try_link(<<"SRC", $libs);
blank() { return 0; }
int t() { ${func}(); return 0; }
SRC
}
}
unless ($result) {
print "no\n";
return 0;
}
if ($func ne "main") {
$config{DEFINE} .= uc(" -Dhave_$func");
}
print "yes\n";
return 1;
}
sub have_module {
my ($module, $version) = (@_, 0);
printf("checking for module %s >= version %s... ", $module, $version);
print "eval(\"package Foo; use $module $version;\")\n" if $DEBUG;
eval "package Foo; use $module $version;";
if ($@) {
print $@ if $DEBUG;
print "no\n";
return 0;
}
print "yes\n";
return 1;
}
sub test_module {
my ($module, $version, $reason) = @_;
unless (have_module($module, $version)) {
if ($INC{'CPAN.pm'}) {
if (prompt("Add $module to list of modules to install?", 'Y') =~ /^y/i) {
$config{PREREQ_PM}{$module} = $version;
}
}
else {
print wrap($reason);
}
return 0;
}
return 1;
}
sub MY::install {
package MY;
my $self = shift;
my $install = $self->SUPER::install(@_);
if ($install_expat) {
$install =~ s/(install :: .*)$/$1 expat_install/m;
$install .= <<INSTALL;
expat_install :
\t\@cd $expat_dir && \$(SHELL) -c \$(MAKE) install
INSTALL
}
return $install;
}
sub is_apache {
my $path = shift;
return unless $path;
if (-e $path && -x _) {
my $version = eval { backtick("$path -v") };
if (!$@) {
$version =~ /Server version: Apache/
&& return 1;
}
}
warn("No apache httpd found at $path\n");
return;
}
1.1 xml-axkit/README
Index: README
===================================================================
INTRODUCTION
AxKit is an XML Application Server for Apache. It provides on-the-fly
conversion from XML to any format, such as HTML, WAP or text using
either W3C standard techniques, or flexible custom code. AxKit also
uses a built-in Perl interpreter to provide some amazingly powerful
techniques for XML transformation.
The emphasis with AxKit is on separation of content from presentation.
The pipelining technique that AxKit uses allows content to be converted
to a presentable format in stages, allowing certain platforms to see
data differently to others. AxKit allows web designers to focus on web
site design, content developers to work on a purely content basis, and
webmasters to focus on their core competencies.
AxKit is based on plugin components. This allows the web site developer
to create a completely custom XML Application Server based around the
AxKit API. AxKit can either automatically provide caching facilities,
or you can create your own cache handler, so that XML transformations
(which can be time consuming) only happen when required.
The toolkit also provides ability to build component based web sites,
and dynamic content. Database integration is fully supported, allowing
either deliver of XML from a database, or XML generation from a
database query. Dynamic web components can be built using the Perl
language, making the possibilities as infinite as CGI scripts, without
the potential mess that CGI programming can cause.
HOME PAGE
The web site for AxKit is http://axkit.org/. There you will find lots
more documentation and example code. The complete source for axkit.org
is included in this distribution in the axkit.org directory.
1.1 xml-axkit/SUPPORT
Index: SUPPORT
===================================================================
What to do if it all goes pear shaped...
========================================
If you have trouble getting AxKit working how you want it to work, then
please subscribe to and use the axkit-users mailing list. Details of this
are available at http://axkit.org/mailinglist.xml
If your problem is more serious, for example if AxKit segfaults, then
please follow these instructions:
Edit your AxKit's Makefile, find the OPTIMIZE line, and add -g to it.
Recompile AxKit (run "make test install")
Make sure apache is stopped.
run:
$ gdb /path/to/apache/bin/httpd
gdb version blah blah...
(gdb) run -X
Now make a request on your server. When it segfaults, gdb will stop and
tell you where... but I need more info... so type:
(gdb) bt
That will give you a backtrace.
Now email that to axkit-users@axkit.org, along with your Perl version,
Apache version, and AxKit version. It may also be helpful to include your
list of LoadModule lines from httpd.conf, and the section where you have
all the Ax* directives.
But please try the latest release of AxKit before sending it in - and if
you're testing a beta, please try CVS first (see the CONTRIB file for
details).
1.1 xml-axkit/TODO
<<Binary file>>
1.1 xml-axkit/axconfig.c
Index: axconfig.c
===================================================================
/* $Id: axconfig.c,v 1.1 2002/01/13 20:45:08 matts Exp $ */
#ifndef WIN32
#include <modules/perl/mod_perl.h>
#include <httpd.h>
#include <http_config.h>
#endif
#include "axconfig.h"
#ifdef WIN32
#define ax_preload_module(name)
#else
static void ax_preload_module(char **name)
{
if(ind(*name, ' ') >= 0) return;
if(**name == '-' && ++*name) return;
if(**name == '+') ++*name;
else if(!PERL_AUTOPRELOAD) return;
if(!PERL_RUNNING()) return;
maybe_load_module(*name);
}
#endif
extern SV *error_str;
static SV *module2file(char *name)
{
SV *sv = newSVpv(name,0);
char *s;
for (s = SvPVX(sv); *s; s++) {
if (*s == ':' && s[1] == ':') {
*s = '/';
Move(s+2, s+1, strlen(s+2)+1, char);
--SvCUR(sv);
}
}
sv_catpvn(sv, ".pm", 3);
return sv;
}
static I32 module_is_loaded(SV *key)
{
I32 retval = FALSE;
if((key && hv_exists_ent(GvHV(incgv), key, FALSE)))
retval = TRUE;
return retval;
}
void
maybe_load_module(char * name)
{
STRLEN len;
SV * sv_file = module2file(name);
char * ch_file = SvPV(sv_file, len);
if(!module_is_loaded(sv_file)) {
perl_require_pv(ch_file);
if (SvTRUE(ERRSV)) {
SvREFCNT_dec(sv_file);
croak("AxKit::load_module failed: %s", SvPV(ERRSV, len));
}
}
SvREFCNT_dec(sv_file);
}
void
ax_cleanup_av(void * av_v)
{
AV * my_av = (AV*)av_v;
/* warn("cleanup_av : %d : %d\n", SvREFCNT((SV*)my_av), my_av); */
SvREFCNT_dec((SV*)my_av);
}
void
ax_cleanup_hv(void * hv_v)
{
HV * my_hv = (HV*)hv_v;
/* warn("cleanup_hv : %d : %d\n", SvREFCNT((SV*)my_hv), my_hv); */
SvREFCNT_dec((SV*)my_hv);
}
axkit_dir_config *
new_axkit_dir_config (pool *p)
{
axkit_dir_config *new =
(axkit_dir_config *) ap_palloc(p, sizeof(axkit_dir_config));
new->translate_output = -1;
new->gzip_output = -1;
new->log_declines = -1;
new->stack_trace = -1;
new->no_cache = -1;
new->debug_level = -1;
new->dependency_checks = -1;
new->reset_processors = 0;
new->reset_output_transformers = 0;
new->reset_plugins = 0;
new->cache_dir = 0;
new->config_reader_module = 0;
new->provider_module = 0;
new->styleprovider_module = 0;
new->default_style = 0;
new->default_media = 0;
new->cache_module = 0;
new->output_charset = 0;
/* complex types */
new->type_map = NULL;
new->processors = NULL;
new->dynamic_processors = NULL;
new->xsp_taglibs = NULL;
new->current_styles = NULL;
new->current_medias = NULL;
new->error_stylesheet = NULL;
new->output_transformers = NULL;
new->current_plugins = NULL;
/*
warn("[AxKit] created new dir_config:\n"
"location: %d\n"
"new.translate_output: %d\n"
"new.gzip_output: %d\n"
"new.log_declines: %d\n"
"new.stack_trace: %d\n"
"new.reset_processors: %d\n"
"new.cache_dir: %d\n"
"new.config_reader_module: %d\n"
"new.provider_module: %d\n"
"new.default_style: %d\n"
"new.default_media: %d\n"
"new.cache_module: %d\n"
"new.output_charset: %d\n"
"new.debug_level: %d\n"
"new.type_map: %d\n"
"new.processors: %d\n"
"new.dynamic_processors: %d\n"
"new.xsp_taglibs: %d\n"
"new.current_styles: %d\n"
"new.current_medias: %d\n"
"new.error_stylesheet: %d\n"
"new.output_transformers: %d\n"
,
new,
new->translate_output,
new->gzip_output,
new->log_declines,
new->stack_trace,
new->reset_processors,
new->cache_dir,
new->config_reader_module,
new->provider_module,
new->default_style,
new->default_media,
new->cache_module,
new->output_charset,
new->debug_level,
new->type_map,
new->processors,
new->dynamic_processors,
new->xsp_taglibs,
new->current_styles,
new->current_medias,
new->error_stylesheet,
new->output_transformers
);
*/
return new;
}
void *
create_axkit_dir_config (pool *p, char *dummy)
{
axkit_dir_config *new = new_axkit_dir_config(p);
new->type_map = newHV();
hv_store(new->type_map, "application/x-xpathscript", 25,
newSVpv("Apache::AxKit::Language::XPathScript", 0), 0);
hv_store(new->type_map, "application/x-xsp", 17,
newSVpv("Apache::AxKit::Language::XSP", 0), 0);
ap_register_cleanup(p, (void*)new->type_map, ax_cleanup_hv, ap_null_cleanup);
new->processors = newHV();
ap_register_cleanup(p, (void*)new->processors, ax_cleanup_hv, ap_null_cleanup);
new->dynamic_processors = newAV();
ap_register_cleanup(p, (void*)new->dynamic_processors, ax_cleanup_av, ap_null_cleanup);
new->xsp_taglibs = newHV();
ap_register_cleanup(p, (void*)new->xsp_taglibs, ax_cleanup_hv, ap_null_cleanup);
new->output_transformers = newAV();
ap_register_cleanup(p, (void*)new->output_transformers, ax_cleanup_av, ap_null_cleanup);
new->current_styles = newAV();
av_push(new->current_styles, newSVpv("#default", 0));
ap_register_cleanup(p, (void*)new->current_styles, ax_cleanup_av, ap_null_cleanup);
new->current_medias = newAV();
av_push(new->current_medias, newSVpv("screen", 0));
ap_register_cleanup(p, (void*)new->current_medias, ax_cleanup_av, ap_null_cleanup);
new->error_stylesheet = newAV();
ap_register_cleanup(p, (void*)new->error_stylesheet, ax_cleanup_av, ap_null_cleanup);
new->current_plugins = newAV();
ap_register_cleanup(p, (void*)new->current_plugins, ax_cleanup_av, ap_null_cleanup);
/* warn("create dir config: %d\n", new); */
return new;
}
void
store_in_hv2 (HV * my_hv, SV * one, SV * two, SV * value)
{
char * key1;
char * key2;
STRLEN len;
HV * sub1;
AV * sub2;
key1 = SvPV(one, len);
if (!hv_exists(my_hv, key1, len)) {
sub1 = newHV();
hv_store(my_hv, key1, len, newRV_noinc((SV*)sub1), 0);
}
else {
SV ** sub1p = hv_fetch(my_hv, key1, len, 0);
if (sub1p == NULL) {
croak("shouldn't happen");
}
sub1 = (HV*)SvRV(*sub1p);
}
key2 = SvPV(two, len);
if (!hv_exists(sub1, key2, len)) {
sub2 = newAV();
hv_store(sub1, key2, len, newRV_noinc((SV*)sub2), 0);
}
else {
SV ** sub2p = hv_fetch(sub1, key2, len, 0);
if (sub2p == NULL) {
croak("shouldn't happen");
}
sub2 = (AV*)SvRV(*sub2p);
}
/* warn("adding processor in %s/%s with refs: %d\n", key1, key2, SvREFCNT(value)); */
av_push(sub2, value);
}
static void *
merge_axkit_dir_config (pool *p, void *parent_dirv, void *subdirv)
{
axkit_dir_config *parent_dir = (axkit_dir_config *)parent_dirv;
axkit_dir_config *subdir = (axkit_dir_config *)subdirv;
axkit_dir_config *new = new_axkit_dir_config(p);
/* Brian Wheeler found that sometimes parent is NULL */
if (parent_dir == NULL) {
parent_dir = create_axkit_dir_config(p, "");
}
/* warn("merge : %d with %d\n", parent_dir, subdir); */
/*
warn("[AxKit] merge: parent dir_config:\n"
"location: %d\n"
"parent_dir.translate_output: %d\n"
"parent_dir.gzip_output: %d\n"
"parent_dir.log_declines: %d\n"
"parent_dir.stack_trace: %d\n"
"parent_dir.reset_processors: %d\n"
"parent_dir.cache_dir: %d\n"
"parent_dir.config_reader_module: %d\n"
"parent_dir.provider_module: %d\n"
"parent_dir.default_style: %d\n"
"parent_dir.default_media: %d\n"
"parent_dir.cache_module: %d\n"
"parent_dir.output_charset: %d\n"
"parent_dir.debug_level: %d\n"
"parent_dir.type_map: %d\n"
"parent_dir.processors: %d\n"
"parent_dir.dynamic_processors: %d\n"
"parent_dir.xsp_taglibs: %d\n"
"parent_dir.current_styles: %d\n"
"parent_dir.current_medias: %d\n"
"parent_dir.error_stylesheet: %d\n"
"parent_dir.output_transformers: %d\n"
,
parent_dir,
parent_dir->translate_output,
parent_dir->gzip_output,
parent_dir->log_declines,
parent_dir->stack_trace,
parent_dir->reset_processors,
parent_dir->cache_dir,
parent_dir->config_reader_module,
parent_dir->provider_module,
parent_dir->default_style,
parent_dir->default_media,
parent_dir->cache_module,
parent_dir->output_charset,
parent_dir->debug_level,
parent_dir->type_map,
parent_dir->processors,
parent_dir->dynamic_processors,
parent_dir->xsp_taglibs,
parent_dir->current_styles,
parent_dir->current_medias,
parent_dir->error_stylesheet,
parent_dir->output_transformers
);
warn("[AxKit] created new dir_config:\n"
"location: %d\n"
"subdir.translate_output: %d\n"
"subdir.gzip_output: %d\n"
"subdir.log_declines: %d\n"
"subdir.stack_trace: %d\n"
"subdir.reset_processors: %d\n"
"subdir.cache_dir: %d\n"
"subdir.config_reader_module: %d\n"
"subdir.provider_module: %d\n"
"subdir.default_style: %d\n"
"subdir.default_media: %d\n"
"subdir.cache_module: %d\n"
"subdir.output_charset: %d\n"
"subdir.debug_level: %d\n"
"subdir.type_map: %d\n"
"subdir.processors: %d\n"
"subdir.dynamic_processors: %d\n"
"subdir.xsp_taglibs: %d\n"
"subdir.current_styles: %d\n"
"subdir.current_medias: %d\n"
"subdir.error_stylesheet: %d\n"
"subdir.output_transformers: %d\n"
,
subdir,
subdir->translate_output,
subdir->gzip_output,
subdir->log_declines,
subdir->stack_trace,
subdir->reset_processors,
subdir->cache_dir,
subdir->config_reader_module,
subdir->provider_module,
subdir->default_style,
subdir->default_media,
subdir->cache_module,
subdir->output_charset,
subdir->debug_level,
subdir->type_map,
subdir->processors,
subdir->dynamic_processors,
subdir->xsp_taglibs,
subdir->current_styles,
subdir->current_medias,
subdir->error_stylesheet,
subdir->output_transformers
);
*/
/* flat params */
if (subdir->cache_dir) {
new->cache_dir = ap_pstrdup(p, subdir->cache_dir);
}
else if (parent_dir->cache_dir) {
new->cache_dir = ap_pstrdup(p, parent_dir->cache_dir);
}
if (subdir->config_reader_module) {
new->config_reader_module = ap_pstrdup(p, subdir->config_reader_module);
}
else if (parent_dir->config_reader_module) {
new->config_reader_module = ap_pstrdup(p, parent_dir->config_reader_module);
}
if (subdir->provider_module) {
new->provider_module = ap_pstrdup(p, subdir->provider_module);
}
else if (parent_dir->provider_module) {
new->provider_module = ap_pstrdup(p, parent_dir->provider_module);
}
if (subdir->styleprovider_module) {
new->styleprovider_module = ap_pstrdup(p, subdir->styleprovider_module);
}
else if (parent_dir->styleprovider_module) {
new->styleprovider_module = ap_pstrdup(p, parent_dir->styleprovider_module);
}
if (subdir->default_style) {
new->default_style = ap_pstrdup(p, subdir->default_style);
}
else if (parent_dir->default_style) {
new->default_style = ap_pstrdup(p, parent_dir->default_style);
}
if (subdir->default_media) {
new->default_media = ap_pstrdup(p, subdir->default_media);
}
else if (parent_dir->default_media) {
new->default_media = ap_pstrdup(p, parent_dir->default_media);
}
if (subdir->cache_module) {
new->cache_module = ap_pstrdup(p, subdir->cache_module);
}
else if (parent_dir->cache_module) {
new->cache_module = ap_pstrdup(p, parent_dir->cache_module);
}
if (subdir->output_charset) {
new->output_charset = ap_pstrdup(p, subdir->output_charset);
}
else if (parent_dir->output_charset) {
new->output_charset = ap_pstrdup(p, parent_dir->output_charset);
}
new->debug_level =
subdir->debug_level != -1 ? subdir->debug_level :
parent_dir->debug_level;
new->translate_output =
subdir->translate_output != -1 ? subdir->translate_output :
parent_dir->translate_output;
new->gzip_output =
subdir->gzip_output != -1 ? subdir->gzip_output :
parent_dir->gzip_output;
new->stack_trace =
subdir->stack_trace != -1 ? subdir->stack_trace :
parent_dir->stack_trace;
new->log_declines =
subdir->log_declines != -1 ? subdir->log_declines :
parent_dir->log_declines;
new->no_cache =
subdir->no_cache != -1 ? subdir->no_cache :
parent_dir->no_cache;
new->dependency_checks =
subdir->dependency_checks != -1 ? subdir->dependency_checks :
parent_dir->dependency_checks;
/* complex types */
{
/* cfg->error_stylesheet */
AV * from = NULL;
new->error_stylesheet = newAV();
if (av_len(subdir->error_stylesheet) >= 0) {
from = subdir->error_stylesheet;
}
else if (av_len(parent_dir->error_stylesheet) >= 0) {
from = parent_dir->error_stylesheet;
}
if (from) {
char *mime;
char *stylesheet;
SV ** avitem;
STRLEN len;
avitem = av_fetch(from, 0, 0);
mime = ap_pstrdup(p, SvPV(*avitem, len));
av_push(new->error_stylesheet, newSVpvn(mime, strlen(mime)));
avitem = av_fetch(from, 1, 0);
stylesheet = ap_pstrdup(p, SvPV(*avitem, len));
av_push(new->error_stylesheet, newSVpvn(stylesheet, strlen(stylesheet)));
}
ap_register_cleanup(p, (void*)new->error_stylesheet, ax_cleanup_av, ap_null_cleanup);
}
{
/* cfg->type_map */
char * key;
I32 len;
SV * val;
new->type_map = newHV();
hv_iterinit(parent_dir->type_map);
while (val = hv_iternextsv(parent_dir->type_map, &key, &len)) {
char * cval;
STRLEN clen;
cval = ap_pstrdup(p, SvPV(val, clen));
hv_store(new->type_map, key, len, newSVpvn(cval, clen), 0);
}
hv_iterinit(subdir->type_map);
while (val = hv_iternextsv(subdir->type_map, &key, &len)) {
char * cval;
STRLEN clen;
cval = ap_pstrdup(p, SvPV(val, clen));
hv_store(new->type_map, key, len, newSVpvn(cval, clen), 0);
}
ap_register_cleanup(p, (void*)new->type_map, ax_cleanup_hv, ap_null_cleanup);
}
{
/* cfg->dynamic_processors */
new->dynamic_processors = newAV();
if (av_len(subdir->dynamic_processors) >= 0) {
I32 key = 0;
for(key = 0; key <= av_len(subdir->dynamic_processors); key++) {
SV ** val = av_fetch(subdir->dynamic_processors, key, 0);
if (val != NULL) {
char * cval;
STRLEN len;
cval = ap_pstrdup(p, SvPV(*val, len));
av_push(new->dynamic_processors, newSVpvn(cval, strlen(cval)));
}
}
}
else {
I32 key = 0;
for(key = 0; key <= av_len(parent_dir->dynamic_processors); key++) {
SV ** val = av_fetch(parent_dir->dynamic_processors, key, 0);
if (val != NULL) {
char * cval;
STRLEN len;
cval = ap_pstrdup(p, SvPV(*val, len));
av_push(new->dynamic_processors, newSVpvn(cval, strlen(cval)));
}
}
}
ap_register_cleanup(p, (void*)new->dynamic_processors, ax_cleanup_av, ap_null_cleanup);
}
{
/* cfg->xsp_taglibs */
new->xsp_taglibs = newHV();
if (HvKEYS(subdir->xsp_taglibs)) {
SV * val;
char * key;
I32 len;
hv_iterinit(subdir->xsp_taglibs);
while (val = hv_iternextsv(subdir->xsp_taglibs, &key, &len)) {
hv_store(new->xsp_taglibs, key, len, newSViv(1), 0);
}
}
else {
SV * val;
char * key;
I32 len;
hv_iterinit(parent_dir->xsp_taglibs);
while (val = hv_iternextsv(parent_dir->xsp_taglibs, &key, &len)) {
hv_store(new->xsp_taglibs, key, len, newSViv(1), 0);
}
}
ap_register_cleanup(p, (void*)new->xsp_taglibs, ax_cleanup_hv, ap_null_cleanup);
}
{
/* cfg->processors */
SV * val;
I32 len;
char * key;
new->processors = newHV();
if (!subdir->reset_processors) {
hv_iterinit(parent_dir->processors);
while (val = hv_iternextsv(parent_dir->processors, &key, &len)) {
SV * subval;
I32 sublen;
char * subkey;
HV * subhash = (HV*)SvRV(val);
hv_iterinit(subhash);
while (subval = hv_iternextsv(subhash, &subkey, &sublen)) {
SV * one;
SV * two;
AV * ary;
I32 ary_len;
I32 i;
one = newSVpvn(ap_pstrdup(p, key), len);
two = newSVpvn(ap_pstrdup(p, subkey), sublen);
ary = (AV*)SvRV(subval);
ary_len = av_len(ary);
if (ary_len >= 0) {
for (i = 0; i <= ary_len; i++) {
SV ** elem = av_fetch(ary, i, 0);
if (elem) {
store_in_hv2(new->processors, one, two, newSVsv(*elem));
}
}
}
SvREFCNT_dec(one);
SvREFCNT_dec(two);
}
}
}
hv_iterinit(subdir->processors);
while (val = hv_iternextsv(subdir->processors, &key, &len)) {
SV * subval;
I32 sublen;
char * subkey;
HV * subhash = (HV*)SvRV(val);
hv_iterinit(subhash);
while (subval = hv_iternextsv(subhash, &subkey, &sublen)) {
SV * one;
SV * two;
AV * ary;
I32 ary_len;
I32 i;
one = newSVpvn(ap_pstrdup(p, key), len);
two = newSVpvn(ap_pstrdup(p, subkey), sublen);
ary = (AV*)SvRV(subval);
ary_len = av_len(ary);
if (ary_len >= 0) {
for (i = 0; i <= ary_len; i++) {
SV ** elem = av_fetch(ary, i, 0);
if (elem) {
store_in_hv2(new->processors, one, two, newSVsv(*elem));
}
}
}
SvREFCNT_dec(one);
SvREFCNT_dec(two);
}
}
ap_register_cleanup(p, (void*)new->processors, ax_cleanup_hv, ap_null_cleanup);
}
{
/* cfg->output_transformers */
new->output_transformers = newAV();
if(!subdir->reset_output_transformers) {
I32 key = 0;
for(key = 0; key <= av_len(parent_dir->output_transformers); key++) {
SV ** val = av_fetch(parent_dir->output_transformers, key, 0);
if (val != NULL) {
char * cval;
STRLEN len;
cval = ap_pstrdup(p, SvPV(*val, len));
av_push(new->output_transformers, newSVpvn(cval, strlen(cval)));
}
}
}
if (av_len(subdir->output_transformers) >= 0) {
I32 key = 0;
for(key = 0; key <= av_len(subdir->output_transformers); key++) {
SV ** val = av_fetch(subdir->output_transformers, key, 0);
if (val != NULL) {
char * cval;
STRLEN len;
cval = ap_pstrdup(p, SvPV(*val, len));
av_push(new->output_transformers, newSVpvn(cval, strlen(cval)));
}
}
}
ap_register_cleanup(p, (void*)new->output_transformers, ax_cleanup_av, ap_null_cleanup);
}
{
/* cfg->current_plugins */
new->current_plugins = newAV();
if(!subdir->reset_plugins) {
I32 key = 0;
for(key = 0; key <= av_len(parent_dir->current_plugins); key++) {
SV ** val = av_fetch(parent_dir->current_plugins, key, 0);
if (val != NULL) {
char * cval;
STRLEN len;
cval = ap_pstrdup(p, SvPV(*val, len));
av_push(new->current_plugins, newSVpvn(cval, strlen(cval)));
}
}
}
if (av_len(subdir->current_plugins) >= 0) {
I32 key = 0;
for(key = 0; key <= av_len(subdir->current_plugins); key++) {
SV ** val = av_fetch(subdir->current_plugins, key, 0);
if (val != NULL) {
char * cval;
STRLEN len;
cval = ap_pstrdup(p, SvPV(*val, len));
av_push(new->current_plugins, newSVpvn(cval, strlen(cval)));
}
}
}
ap_register_cleanup(p, (void*)new->current_plugins, ax_cleanup_av, ap_null_cleanup);
}
new->current_styles = newAV();
av_push(new->current_styles, newSVpv("#default", 0));
ap_register_cleanup(p, (void*)new->current_styles, ax_cleanup_av, ap_null_cleanup);
new->current_medias = newAV();
av_push(new->current_medias, newSVpv("screen", 0));
ap_register_cleanup(p, (void*)new->current_medias, ax_cleanup_av, ap_null_cleanup);
/*
warn("merge results: %d\n"
"typemap: %d\n"
"processors: %d\n"
"dynamic: %d\n"
"taglibs: %d\n"
"c_styles: %d\n"
"c_medias: %d\n"
"out_trans: %d\n"
,
new,
new->type_map,
new->processors,
new->dynamic_processors,
new->xsp_taglibs,
new->current_styles,
new->current_medias,
new->output_transformers
);
*/
return new;
}
/* return string is an error. Return NULL if OK */
CHAR_P
ax_add_type_processor (cmd_parms *cmd, axkit_dir_config *ax,
char *mt, char *sty, char *option)
{
AV *processor = newAV();
SV **cur_media = av_fetch(ax->current_medias, 0, 0);
SV **cur_style = av_fetch(ax->current_styles, 0, 0);
SV *type = newSVpv((char*)cmd->info, 0);
SV *mime = newSVpv(mt, 0);
SV *style = newSVpv(sty, 0);
SV *opt_sv = newSVpv(option, 0);
av_push(processor, type);
av_push(processor, mime);
av_push(processor, style);
av_push(processor, opt_sv);
store_in_hv2(ax->processors, *cur_media, *cur_style, newRV_noinc((SV*)processor));
return NULL;
}
CHAR_P
ax_add_processor (cmd_parms *cmd, axkit_dir_config *ax, char *mt, char *sty)
{
return ax_add_type_processor(cmd, ax, mt, sty, "");
}
CHAR_P
ax_add_dynamic_processor (cmd_parms *cmd, axkit_dir_config *ax,
char *module)
{
SV * mod_sv = newSVpv(module, 0);
av_push(ax->dynamic_processors, mod_sv);
return NULL;
}
CHAR_P
ax_reset_processors (cmd_parms *cmd, axkit_dir_config *ax)
{
ax->reset_processors++;
return NULL;
}
CHAR_P
ax_error_stylesheet (cmd_parms *cmd, axkit_dir_config *ax, char *mime, char *stylesheet)
{
av_push(ax->error_stylesheet, newSVpvn(mime, strlen(mime)));
av_push(ax->error_stylesheet, newSVpvn(stylesheet, strlen(stylesheet)));
return NULL;
}
CHAR_P
ax_media_type (cmd_parms *cmd, axkit_dir_config *ax, char *media)
{
const char *pos = media;
char * nextword;
int count = 0;
char line [MAX_STRING_LEN];
void * oldconf;
char *endp = strrchr(media, '>');
if (!endp) {
return "Syntax error: no terminal \">\" sign";
}
*endp = '\0';
while (*pos && (nextword = ap_getword_conf(cmd->pool, &pos))) {
SV *media_sv = newSVpv(nextword, 0);
if (count++ > 0) {
return "Syntax error: <AxMediaType> only takes one parameter";
}
av_unshift(ax->current_medias, 1);
av_store(ax->current_medias, 0, media_sv);
}
oldconf = ap_get_module_config(cmd->server->lookup_defaults, &XS_AxKit);
ap_set_module_config(cmd->server->lookup_defaults, &XS_AxKit, (void*)ax);
while (!ap_cfg_getline(line, MAX_STRING_LEN, cmd->config_file)) {
const char *errmsg;
if (!strcasecmp(line, "</AxMediaType>")) {
SV * ignore = av_shift(ax->current_medias);
SvREFCNT_dec(ignore);
break;
}
#ifndef WIN32
errmsg = (const char *)ap_handle_command(cmd, cmd->server->lookup_defaults, line);
if (errmsg) {
return errmsg;
}
#endif
}
ap_set_module_config(cmd->server->lookup_defaults, &XS_AxKit, oldconf);
return NULL;
}
CHAR_P
ax_media_type_end (cmd_parms *cmd, axkit_dir_config *ax)
{
return "</AxMediaType> with no beginning <AxMediaType> tag";
}
CHAR_P
ax_style_name (cmd_parms *cmd, axkit_dir_config *ax, char *style)
{
const char *pos = style;
char * nextword;
int count = 0;
char line [MAX_STRING_LEN];
void * oldconf;
char *endp = strrchr(style, '>');
if (!endp) {
return "Syntax error: no terminal \">\" sign";
}
*endp = '\0';
while (*pos && (nextword = ap_getword_conf(cmd->pool, &pos))) {
SV *style_sv = newSVpv(nextword, 0);
if (count++ > 0) {
return "Syntax error: <AxStyleName> only takes one parameter";
}
av_unshift(ax->current_styles, 1);
av_store(ax->current_styles, 0, style_sv);
}
oldconf = ap_get_module_config(cmd->server->lookup_defaults, &XS_AxKit);
ap_set_module_config(cmd->server->lookup_defaults, &XS_AxKit, (void*)ax);
while (!ap_cfg_getline(line, MAX_STRING_LEN, cmd->config_file)) {
const char *errmsg;
if (!strcasecmp(line, "</AxStyleName>")) {
SV * ignore = av_shift(ax->current_styles);
SvREFCNT_dec(ignore);
break;
}
#ifndef WIN32
errmsg = (const char *)ap_handle_command(cmd, cmd->server->lookup_defaults, line);
if (errmsg) {
return errmsg;
}
#endif
}
ap_set_module_config(cmd->server->lookup_defaults, &XS_AxKit, oldconf);
return NULL;
}
CHAR_P
ax_style_name_end (cmd_parms *cmd, axkit_dir_config *ax)
{
return "</AxStyleName> with no beginning <AxStyleName> tag";
}
CHAR_P
ax_add_style_map (cmd_parms *cmd, axkit_dir_config *ax,
char *mt, char *module)
{
SV *module_sv;
STRLEN len;
ax_preload_module(&module);
len = strlen(mt);
module_sv = newSVpv(module, 0);
hv_store(ax->type_map, mt, len, module_sv, 0);
return NULL;
}
CHAR_P
ax_reset_style_map (cmd_parms *cmd, axkit_dir_config *ax)
{
hv_clear(ax->type_map);
return NULL;
}
CHAR_P
ax_add_xsp_taglib (cmd_parms *cmd, axkit_dir_config *ax, char *module)
{
STRLEN len;
ax_preload_module(&module);
len = strlen(module);
hv_store(ax->xsp_taglibs, module, len, newSViv(1), 0);
return NULL;
}
CHAR_P
ax_add_output_transformer (cmd_parms *cmd, axkit_dir_config *ax, char *module)
{
SV * mod_sv = newSVpv(module, 0);
av_push(ax->output_transformers, mod_sv);
return NULL;
}
CHAR_P
ax_reset_output_transformers (cmd_parms *cmd, axkit_dir_config *ax)
{
ax->reset_output_transformers++;
return NULL;
}
CHAR_P
ax_add_plugin (cmd_parms *cmd, axkit_dir_config *ax, char *module)
{
ax_preload_module(&module);
av_push(ax->current_plugins, newSVpv(module, 0));
return NULL;
}
CHAR_P
ax_reset_plugins (cmd_parms *cmd, axkit_dir_config *ax)
{
ax->reset_plugins++;
return NULL;
}
static CHAR_P
ax_set_module_slot (cmd_parms *cmd, char *struct_ptr, char *arg)
{
int offset;
ax_preload_module(&arg);
/* warn("ax_set_module_slot: %d\n", arg); */
offset = (int) (long) cmd->info;
*(char **) (struct_ptr + offset) = arg;
return NULL;
}
static CHAR_P
ax_set_debug_level (cmd_parms *cmd, axkit_dir_config *ax, char *arg)
{
ax->debug_level = atoi(arg);
return NULL;
}
void axkit_module_init(server_rec *s, pool *p)
{
STRLEN len = 0;
SV * serverstring;
char * serverstringc;
serverstring = perl_get_sv("AxKit::ServerString", TRUE | GV_ADDMULTI);
serverstringc = SvPV(serverstring, len);
/* warn("Adding server string: %s\n", serverstringc); */
ap_add_version_component(serverstringc);
}
static int axkit_handler(request_rec *r)
{
int retval;
SV * handler_sv = newSVpv("AxKit::fast_handler", 0);
SV * cfg = perl_get_sv("AxKit::Cfg", FALSE);
SV * cache = perl_get_sv("AxKit::Cache", FALSE);
SV * hs = perl_get_sv("AxKit::HeadersSent", FALSE);
SV * debuglevel = perl_get_sv("AxKit::DebugLevel", FALSE);
SV * errorlevel = perl_get_sv("Error::Debug", FALSE);
ENTER;
save_item(cfg);
save_item(cache);
save_item(hs);
save_item(debuglevel);
save_item(errorlevel);
retval = perl_call_handler(handler_sv, (request_rec *)r, Nullav);
LEAVE;
SvREFCNT_dec(handler_sv);
return retval;
}
static command_rec axkit_mod_cmds[] = {
{ "AxAddProcessor", ax_add_processor,
(void*)"NORMAL", OR_ALL, TAKE2,
"a mime type and a stylesheet to use" },
{ "AxAddDocTypeProcessor", ax_add_type_processor,
(void*)"DocType", OR_ALL, TAKE3,
"a mime type, a stylesheet, and an XML public identifier" },
{ "AxAddDTDProcessor", ax_add_type_processor,
(void*)"DTD", OR_ALL, TAKE3,
"a mime type, a stylesheet, and a dtd filename" },
{ "AxAddDynamicProcessor", ax_add_dynamic_processor,
NULL, OR_ALL, TAKE1,
"a package name" },
{ "AxAddRootProcessor", ax_add_type_processor,
(void*)"Root", OR_ALL, TAKE3,
"a mime type, a stylesheet, and a root element" },
{ "AxAddURIProcessor", ax_add_type_processor,
(void*)"URI", OR_ALL, TAKE3,
"a mime type, a stylesheet, and a Perl regexp to match the URI" },
{ "AxResetProcessors", ax_reset_processors,
NULL, OR_ALL, NO_ARGS,
"reset the list of processors" },
{ "<AxMediaType", ax_media_type,
NULL, OR_ALL, RAW_ARGS,
"Media type block" },
{ "</AxMediaType>", ax_media_type_end,
NULL, OR_ALL, NO_ARGS,
"End of media type block" },
{ "<AxStyleName", ax_style_name,
NULL, OR_ALL, RAW_ARGS,
"Style name block" },
{ "</AxStyleName>", ax_style_name_end,
NULL, OR_ALL, NO_ARGS,
"End of Style name block" },
{ "AxAddStyleMap", ax_add_style_map,
NULL, OR_ALL, TAKE2,
"a mime type and a module name to use" },
{ "AxResetStyleMap", ax_reset_style_map,
NULL, OR_ALL, NO_ARGS,
"reset the styles" },
{ "AxCacheDir", ap_set_string_slot,
(void *)XtOffsetOf(axkit_dir_config, cache_dir),
OR_ALL, TAKE1,
"directory to store cache files" },
{ "AxConfigReader", ax_set_module_slot,
(void *)XtOffsetOf(axkit_dir_config, config_reader_module),
OR_ALL, TAKE1,
"alternative module to use for reading configuration" },
{ "AxProvider", ax_set_module_slot,
(void *)XtOffsetOf(axkit_dir_config, provider_module),
OR_ALL, TAKE1,
"alternative module to use for reading the xml" },
{ "AxStyleProvider", ax_set_module_slot,
(void *)XtOffsetOf(axkit_dir_config, styleprovider_module),
OR_ALL, TAKE1,
"alternative module to use for reading the stylesheet" },
{ "AxStyle", ap_set_string_slot,
(void *)XtOffsetOf(axkit_dir_config, default_style),
OR_ALL, TAKE1,
"a default stylesheet (title) to use" },
{ "AxMedia", ap_set_string_slot,
(void *)XtOffsetOf(axkit_dir_config, default_media),
OR_ALL, TAKE1,
"a default media to use other than screen" },
{ "AxAddOutputTransformer", ax_add_output_transformer,
NULL, OR_ALL, TAKE1,
"An output transformer function, qualified with package name." },
{ "AxResetOutputTransformers", ax_reset_output_transformers,
NULL, OR_ALL, NO_ARGS,
"Reset list of output transformers." },
{ "AxCacheModule", ax_set_module_slot,
(void *)XtOffsetOf(axkit_dir_config, cache_module),
OR_ALL, TAKE1,
"alternative cache module" },
{ "AxDebugLevel", ax_set_debug_level,
NULL, OR_ALL, TAKE1,
"debug level (0 == none, higher numbers == more debugging)" },
{ "AxTranslateOutput", ap_set_flag_slot,
(void *)XtOffsetOf(axkit_dir_config, translate_output),
OR_ALL, FLAG,
"On or Off [default] to automatically change character set on output" },
{ "AxOutputCharset", ap_set_string_slot,
(void *)XtOffsetOf(axkit_dir_config, output_charset),
OR_ALL, TAKE1,
"character set used by iconv" },
{ "AxGzipOutput", ap_set_flag_slot,
(void *)XtOffsetOf(axkit_dir_config, gzip_output),
OR_ALL, FLAG,
"On or Off [default] to gzip the output" },
{ "AxLogDeclines", ap_set_flag_slot,
(void *)XtOffsetOf(axkit_dir_config, log_declines),
OR_ALL, FLAG,
"On or Off [default] to log why AxKit declined to process the resource" },
{ "AxStackTrace", ap_set_flag_slot,
(void *)XtOffsetOf(axkit_dir_config, stack_trace),
OR_ALL, FLAG,
"On or Off [default] to maintain a stack trace with exceptions" },
{ "AxNoCache", ap_set_flag_slot,
(void *)XtOffsetOf(axkit_dir_config, no_cache),
OR_ALL, FLAG,
"On or Off [default] to not cache results" },
{ "AxErrorStylesheet", ax_error_stylesheet,
NULL, OR_ALL, TAKE2,
"mime type and Error Stylesheet to use for displaying errors" },
{ "AxAddXSPTaglib", ax_add_xsp_taglib,
NULL, OR_ALL, TAKE1,
"module that provides a taglib functionality" },
{ "AxDependencyChecks", ap_set_flag_slot,
(void *)XtOffsetOf(axkit_dir_config, dependency_checks),
OR_ALL, FLAG,
"On [default] or Off to disable dependency checking" },
{ "AxAddPlugin", ax_add_plugin,
NULL, OR_ALL, TAKE1,
"module that implements a plugin" },
{ "AxResetPlugins", ax_reset_plugins,
NULL, OR_ALL, NO_ARGS,
"reset the list of plugins" },
{ NULL }
};
static const handler_rec axkit_handlers[] =
{
{"axkit", axkit_handler},
{NULL}
};
module MODULE_VAR_EXPORT XS_AxKit = {
STANDARD_MODULE_STUFF,
axkit_module_init, /* module initializer */
create_axkit_dir_config, /* per-directory config creator */
merge_axkit_dir_config, /* dir config merger */
NULL, /* server config creator */
NULL, /* server config merger */
axkit_mod_cmds, /* command table */
axkit_handlers, /* [7] list of handlers */
NULL, /* [2] filename-to-URI translation */
NULL, /* [5] check/validate user_id */
NULL, /* [6] check user_id is valid *here* */
NULL, /* [4] check access by host address */
NULL, /* [7] MIME type checker/setter */
NULL, /* [8] fixups */
NULL, /* [10] logger */
NULL, /* [3] header parser */
NULL, /* process initializer */
NULL, /* process exit/cleanup */
NULL, /* [1] post read_request handling */
};
HV *
ax_get_config (axkit_dir_config * cfg)
{
HV * retval;
retval = newHV();
if (cfg->cache_dir) {
hv_store(retval, "CacheDir",
8, (newSVpv(cfg->cache_dir, 0)), 0);
}
if (cfg->config_reader_module) {
hv_store(retval, "ConfigReader",
12, (newSVpv(cfg->config_reader_module, 0)), 0);
}
if (cfg->provider_module) {
hv_store(retval, "Provider",
8, (newSVpv(cfg->provider_module, 0)), 0);
}
if (cfg->styleprovider_module) {
hv_store(retval, "StyleProvider",
13, (newSVpv(cfg->styleprovider_module, 0)), 0);
}
if (cfg->default_style) {
hv_store(retval, "Style",
5, (newSVpv(cfg->default_style, 0)), 0);
}
if (cfg->default_media) {
hv_store(retval, "Media",
5, (newSVpv(cfg->default_media, 0)), 0);
}
if (cfg->cache_module) {
hv_store(retval, "CacheModule",
11, (newSVpv(cfg->cache_module, 0)), 0);
}
if (cfg->output_charset) {
hv_store(retval, "OutputCharset",
13, (newSVpv(cfg->output_charset, 0)), 0);
}
if (cfg->debug_level) {
hv_store(retval, "DebugLevel",
10, (newSViv(cfg->debug_level)), 0);
}
if (cfg->translate_output != -1) {
hv_store(retval, "TranslateOutput",
15, (newSViv(cfg->translate_output)), 0);
}
if (cfg->gzip_output != -1) {
hv_store(retval, "GzipOutput",
10, (newSViv(cfg->gzip_output)), 0);
}
if (cfg->log_declines != -1) {
hv_store(retval, "LogDeclines",
11, (newSViv(cfg->log_declines)), 0);
}
if (cfg->stack_trace != -1) {
hv_store(retval, "StackTrace",
10, (newSViv(cfg->stack_trace)), 0);
}
if (cfg->no_cache != -1) {
hv_store(retval, "NoCache",
7, (newSViv(cfg->no_cache)), 0);
}
if (cfg->dependency_checks != -1) {
hv_store(retval, "DependencyChecks",
16, (newSViv(cfg->dependency_checks)), 0);
}
else {
hv_store(retval, "DependencyChecks",
16, (newSViv(1)), 0);
}
hv_store(retval, "OutputTransformers",
18, newRV_inc((SV*)cfg->output_transformers), 0);
hv_store(retval, "ErrorStylesheet",
15, newRV_inc((SV*)cfg->error_stylesheet), 0);
hv_store(retval, "StyleMap",
8, newRV_inc((SV*)cfg->type_map), 0);
hv_store(retval, "Processors",
10, newRV_inc((SV*)cfg->processors), 0);
hv_store(retval, "DynamicProcessors",
17, newRV_inc((SV*)cfg->dynamic_processors), 0);
hv_store(retval, "XSPTaglibs",
10, newRV_inc((SV*)cfg->xsp_taglibs), 0);
hv_store(retval, "Plugins",
7, newRV_inc((SV*)cfg->current_plugins), 0);
return retval;
}
void remove_module_cleanup(void * ignore)
{
if (ap_find_linked_module(ap_find_module_name(&XS_AxKit))) {
ap_remove_module(&XS_AxKit);
}
/* make sure BOOT section is re-run on restarts */
(void)hv_delete(GvHV(incgv), "AxKit.pm", 8, G_DISCARD);
}
/* Diff for styles being relative to .htaccess:
Index: axconfig.c
===================================================================
RCS file: /home/cvs/AxKit/axconfig.c,v
retrieving revision 1.2
diff -r1.2 axconfig.c
2a3
> #define CORE_PRIVATE
3a5
> #include "axconfig.h"
6c8
< #include "axconfig.h"
---
> #include "include/http_core.h"
24a27,30
> char *
> ax_resolve_uri(char * base, char * uri)
> {
> }
626a633
> SV *style;
634d640
< SV *style = newSVpv(sty, 0);
636a643,665
> core_server_config *server_conf =
> ap_get_module_config(cmd->server->module_config, &core_module);
>
> if (sty[0] != '/'
> && strcmp(sty, ".") != 0
> && strstr(cmd->config_file->name, server_conf->access_name))
> {
> style = newSVpv(
> ap_pstrcat(cmd->pool, "file://",
> ap_make_full_path(cmd->pool,
> ap_make_dirstr_parent(
> cmd->pool,
> cmd->config_file->name
> ), sty
> ), NULL
> ), 0);
> }
> else {
> style = newSVpv(sty, 0);
> }
>
> warn("style: %s\n", SvPV_nolen(style));
>
674,675c703,725
< av_push(ax->error_stylesheet, newSVpvn(mime, strlen(mime)));
< av_push(ax->error_stylesheet, newSVpvn(stylesheet, strlen(stylesheet)));
---
> core_server_config *server_conf =
> ap_get_module_config(cmd->server->module_config, &core_module);
>
> av_push(ax->error_stylesheet, newSVpvn(mime, 0));
>
> if (stylesheet[0] != '/'
> && strstr(cmd->config_file->name, server_conf->access_name))
> {
> av_push(ax->error_stylesheet,
> newSVpv(
> ap_pstrcat(cmd->pool, "file://",
> ap_make_full_path(cmd->pool,
> ap_make_dirstr_parent(
> cmd->pool,
> cmd->config_file->name
> ), stylesheet
> ), NULL
> ), 0)
> );
> }
> else {
> av_push(ax->error_stylesheet, newSVpv(stylesheet, 0));
> }
*/
1.1 xml-axkit/axconfig.h
Index: axconfig.h
===================================================================
/* $Id: axconfig.h,v 1.1 2002/01/13 20:45:08 matts Exp $ */
#ifdef WIN32
#define _INC_DIRENT
#define DIR void
#endif
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#ifdef WIN32
#ifdef lstat
#define apache_lstat lstat
#undef lstat
#endif
#include "modules/perl/mod_perl.h"
#ifdef apache_lstat
#undef lstat
#define lstat apache_lstat
#undef apache_lstat
#endif
#else
#include <httpd.h>
#include <http_config.h>
/* SUNPRO C does not know something about __attribute__ */
#ifdef __SUNPRO_C
#include <http_core.h>
#include <http_main.h>
#include <http_protocol.h>
#include <http_request.h>
#include <http_log.h>
#endif
#endif
typedef struct {
/* simple types */
char * cache_dir;
char * config_reader_module;
char * provider_module;
char * styleprovider_module;
char * default_style;
char * default_media;
char * cache_module;
char * output_charset;
int debug_level;
int translate_output;
int gzip_output;
int reset_processors;
int log_declines;
int stack_trace;
int no_cache;
int dependency_checks;
int reset_output_transformers;
int reset_plugins;
/* complex types */
HV * type_map; /* mime type => module mapping */
HV * processors; /* processor map */
AV * dynamic_processors; /* dynamic processor map */
HV * xsp_taglibs;
AV * current_styles;
AV * current_medias;
AV * error_stylesheet;
AV * output_transformers;
AV * current_plugins;
} axkit_dir_config;
extern module MODULE_VAR_EXPORT XS_AxKit;
void remove_module_cleanup(void * ignore);
HV * ax_get_config (axkit_dir_config * cfg);
void maybe_load_module (char * name);
1.1 xml-axkit/getstyles.c
Index: getstyles.c
===================================================================
/* $Id: getstyles.c,v 1.1 2002/01/13 20:45:08 matts Exp $ */
#ifdef HAVE_LIBXML2
#include "getstyles.h"
static void doctypeHandler(void *ctx,
const xmlChar *name,
const xmlChar *ExternalID,
const xmlChar *SystemID)
{
axkit_xml_bits * xml_bits = (axkit_xml_bits*)ctx;
/* warn("doctypeHandler: %s\n", name); */
if (xml_bits->start_element != NULL) {
return;
}
xml_bits->dtd = ap_pstrdup(xml_bits->apache->pool, ExternalID);
xml_bits->publicid = ap_pstrdup(xml_bits->apache->pool, SystemID);
}
static void startElementHandler(void *ctx,
const xmlChar *name,
const xmlChar **atts)
{
axkit_xml_bits * xml_bits;
Apache r;
int i = 0;
xml_bits = (axkit_xml_bits*)ctx;
/* warn("startElement: %s\n", name); */
if (xml_bits->start_element != NULL) {
return;
}
r = xml_bits->apache;
xml_bits->start_element = ap_pstrdup(r->pool, name);
xml_bits->start_attribs = newAV();
if (atts != NULL) {
for (i = 0;(atts[i] != NULL);i++) {
av_push(xml_bits->start_attribs, newSVpv(ap_pstrdup(r->pool, atts[i]), 0));
}
}
}
static void processingInstructionHandler(
void *ctx,
const xmlChar *target,
const xmlChar *data)
{
axkit_xml_bits * xml_bits = (axkit_xml_bits*)ctx;
if (xml_bits->start_element != NULL) {
return;
}
if (strncmp(target, "xml-stylesheet", 14) == 0) {
av_push(xml_bits->xml_stylesheet,
newSVpv(ap_pstrdup(xml_bits->apache->pool, data), 0));
}
}
static void errorHandler(
void *ctx,
const char *msg,
...)
{
va_list args;
SV * sv;
sv = NEWSV(0,0);
va_start(args, msg);
sv_vsetpvfn(sv, msg, strlen(msg), &args, NULL, 0, NULL);
va_end(args);
sv_catsv(error_str, sv);
SvREFCNT_dec(sv);
}
xmlEntityPtr getAxEntity(void *user_data, const xmlChar *name) {
xmlEntityPtr predef = xmlGetPredefinedEntity(name);
if (predef != NULL) {
/* warn("default entity: %s\n", name); */
return predef;
}
/* warn("non-default entity: %s\n", name); */
return blankEntity;
}
int
read_perl (SV * ioref, char * buffer, int len)
{
dSP;
int cnt;
SV * read_results;
STRLEN read_length;
char * chars;
SV * tbuff = NEWSV(0,0);
SV * tsize = newSViv(len);
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 3);
PUSHs(ioref);
PUSHs(sv_2mortal(tbuff));
PUSHs(sv_2mortal(tsize));
PUTBACK;
cnt = perl_call_method("read", G_SCALAR);
SPAGAIN;
if (cnt != 1) {
croak("read method call failed");
}
read_results = POPs;
if (!SvOK(read_results)) {
croak("read error");
}
read_length = SvIV(read_results);
chars = SvPV(tbuff, read_length);
strncpy(buffer, chars, read_length);
/* terminate by NUL in case chars > buffer */
buffer[len - 1] = 0;
FREETMPS;
LEAVE;
return read_length;
}
xmlSAXHandler axkitSAXHandlerStruct = {
doctypeHandler,
NULL,
NULL,
NULL,
NULL,
getAxEntity,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
startElementHandler,
NULL,
NULL,
NULL,
NULL,
processingInstructionHandler,
NULL,
errorHandler, /* warnings */
errorHandler, /* errors */
errorHandler, /* fatal errors */
getAxEntity,
NULL,
doctypeHandler
};
xmlSAXHandlerPtr axkitSAXHandler = &axkitSAXHandlerStruct;
xmlEntity blankEntityStruct = {
#ifndef XML_WITHOUT_CORBA
NULL, /* for Corba, must be first ! */
#endif
XML_ENTITY_DECL, /* XML_ENTITY_DECL, must be second ! */
NULL, /* Attribute name */
NULL, /* NULL */
NULL, /* NULL */
NULL, /* -> DTD */
NULL, /* next sibling link */
NULL, /* previous sibling link */
NULL, /* the containing document */
NULL, /* content without ref substitution */
NULL, /* content or ndata if unparsed */
0, /* the content length */
XML_EXTERNAL_GENERAL_PARSED_ENTITY, /* The entity type */
NULL, /* External identifier for PUBLIC */
NULL, /* URI for a SYSTEM or PUBLIC Entity */
NULL, /* unused */
NULL /* the full URI as computed */
};
xmlEntityPtr blankEntity = &blankEntityStruct;
#endif /* HAVE_LIBXML2 */
1.1 xml-axkit/getstyles.h
Index: getstyles.h
===================================================================
/* $Id: getstyles.h,v 1.1 2002/01/13 20:45:08 matts Exp $ */
#ifndef WIN32
#include <modules/perl/mod_perl.h>
#endif
#ifdef HAVE_LIBXML2
#include <libxml/xmlversion.h>
#include <libxml/xmlmemory.h>
#include <libxml/debugXML.h>
#include <libxml/HTMLtree.h>
#include <libxml/xmlerror.h>
#ifdef VMS
extern int xmlDoValidityCheckingDefaultVal;
#define xmlDoValidityCheckingDefaultValue xmlDoValidityCheckingDefaultVal
extern int xmlSubstituteEntitiesDefaultVal;
#define xmlSubstituteEntitiesDefaultValue xmlSubstituteEntitiesDefaultVal
#else
extern int xmlDoValidityCheckingDefaultValue;
extern int xmlSubstituteEntitiesDefaultValue;
#endif
extern int xmlGetWarningsDefaultValue;
extern int xmlKeepBlanksDefaultValue;
extern int xmlLoadExtDtdDefaultValue;
extern int xmlPedanticParserDefaultValue;
typedef struct {
Apache apache;
AV * xml_stylesheet;
char * start_element;
AV * start_attribs;
char * dtd;
char * publicid;
} axkit_xml_bits;
extern xmlSAXHandler axkitSAXHandlerStruct;
extern xmlSAXHandlerPtr axkitSAXHandler;
extern SV * error_str;
extern xmlEntityPtr blankEntity;
int read_perl (SV * ioref, char * buffer, int len);
#endif /* HAVE_LIBXML2 */
1.1 xml-axkit/typemap
Index: typemap
===================================================================
TYPEMAP
Apache T_APACHEOBJ
HV * T_HvRV
OUTPUT
T_APACHEOBJ
sv_setref_pv($arg, \"${ntype}\", (void*)$var);
T_HvRV
$arg = newRV((SV*)$var);
INPUT
T_HvRV
if( SvROK($arg) && (SvTYPE(SvRV($arg)) == SVt_PVHV) )
$var = (HV*)SvRV( $arg );
else {
warn( \"${Package}::$func_name() -- $var is not an HV reference\" );
XSRETURN_UNDEF;
}
T_APACHEOBJ
r = sv2request_rec($arg, \"$ntype\", cv)
1.1 xml-axkit/axkit.org/faq.xml
Index: faq.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="application/x-xpathscript"
href="/stylesheets/webpage_html.xps"
title="default"?>
<!DOCTYPE webpage [
<!ENTITY sidebar SYSTEM "/sidebar.xml">
<!ENTITY news SYSTEM "/news.xml">
]>
<webpage>
<head>
<title>AxKit - FAQ</title>
</head>
<body>
<section>
<title>Frequently Asked Questions about AxKit</title>
<subsection>
<title>Upon startup of Apache I get the error:
"Can't locate loadable object for module Apache::Log in
@INC"</title>
<para>
AxKit uses Apache::Log for its logging in some places. This
gives us some extra flexibility. However it is not built into
mod_perl by default - you need to compile it in explicitly.
The simplest way to do this is to just compile all the extra
bits of mod_perl:
<pre>
# in the modperl directory
perl Makefile.PL EVERYTHING=1
make
make test install
</pre>
See also the various INSTALL documents that ship with
mod_perl, and also the <a
href="http://perl.apache.org/guide/">mod_perl guide</a>.
</para>
</subsection>
<subsection>
<title>I install AxKit and Apache segfaults when it
starts</title>
<para>
The usual cause is currently a conflict between differing
versions of expat that are being loaded at the same time. The
problem is too deep to go into in a simple FAQ (although I'll
try and find a link to an explanation in a future version of
this FAQ). Basically the current work around is to remove
expat from your Apache (it is built in by default, and is used
for mod_dav), and to be careful with PHP, which can also cause
these sorts of conflicts. As a developer I respect that
neither of these are particularly great long term work
arounds, and I'm trying to bring the community of developers
together to solve this problem permanently.
</para>
<para>
First of all, to find out if this is your problem, execute the
command: <code>strings /path/to/apache/bin/httpd | grep -i
XML</code>. If there are any results at all then you are going
to see these segfaults until you recompile Apache (and probably
mod_perl too)
</para>
<para>
To compile Apache without expat, simply supply the option:
<pre>RULE_EXPAT=NO</pre>
to <code>./configure</code> when you build Apache. Alternatively if you build
Apache with mod_perl (with mod_perl's DO_HTTPD option) it will
supply this option automatically for you. See also the AxKit
INSTALL file for a recipe for building Apache and mod_perl
together that works.
</para>
<para>
If none of this helps, then you may be having platform
difficulties with the custom configuration directives that
AxKit creates. Hopefully all of these have been solved
already, but there may be some conflicts with PHP with these.
If you absolutely can't do without PHP then you'll need to
completely remove AxKit and all the binary components from
your perl lib directory (check using "perl -V" on the command
line) by hand (sorry, but there is no "make uninstall"
command). Then re-compile AxKit using the following:
<pre>perl Makefile.PL NO_DIRECTIVES=1
make
make test install</pre>
Then start Apache, and hopefully this will fix the problem. We
hope that a later version of mod_perl may not require this
hack. When AxKit is installed with the NO_DIRECTIVES option,
all configuration is done via PerlSetVar. Please contact the
AxKit-users mailing list if you need to use this option so
that we can provide more documentation where needed.
</para>
</subsection>
<subsection>
<title>Which Processor?</title>
<para>
AxKit ships out of the box with two processors for using
XSLT, and several other stylesheet language modules. The choice
between XML::XSLT and XML::Sablotron should generally be based
on whether or not XML::Sablotron works on your platform, since
it is a more complete implementation of XSLT and is faster than
XML::XSLT (which is a pure perl implementation). Sablotron is
known to work on Linux and Solaris, and other platforms are
coming online all the time.
</para>
</subsection>
<subsection>
<title>Bizarre copy of ARRAY in aassign (sic)</title>
<para>
This is actually a bug in Perl 5.6. See <a href="http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2000-06/msg00200.html">this post to perl5-porters</a> for a patch.
</para>
</subsection>
<subsection>
<title>Can I run AxKit on Windows?</title>
<para>
Yes, although it is not considered a primary platform, so you
are mostly on your own with this. You can install it via
ActiveState's PPM as follows:
<pre>
ppm install
http://theoryx5.uwinnipeg.ca/ppmpackages/mod_perl-1.25_1.3.17-axkit.ppd
</pre>
Note that will install mod_perl too. You can install it without
mod_perl using the AxKit.ppd package at the same location. If
you have further questions, please contact the AxKit Users <ulink
url="/mailinglist.xml">mailing list</ulink>.
</para>
</subsection>
<subsection>
<title>AxKit fails on looking up stylesheets</title>
<para>
If you get an error message in your log with "OK" in it, this
may be caused by having CGI::Carp, or some other module that
installs a $SIG{__DIE__} handler in a broken manner. Make sure
you don't have CGI::Carp in your startup.pl or any other place.
</para>
</subsection>
<subsection>
<title>AxKit on Debian</title>
<para>
AxKit has "problems" detecting iconv (and probably libxml2) on Debian. We're
not sure what is causing this yet, so in order to get around these problems
you will need to edit the Makefile.PL files (in the root directory, and
in lib/Apache/AxKit/) to comment out the checks for these libraries and just
enforce their usage regardless. Sorry for the inconvenience - we're working
on it, and would be interested in patches.
</para>
</subsection>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/features.xml
Index: features.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="application/x-xpathscript"
href="/stylesheets/webpage_html.xps"
title="default"?>
<!DOCTYPE webpage [
<!ENTITY sidebar SYSTEM "/sidebar.xml">
<!ENTITY news SYSTEM "/news.xml">
]>
<webpage>
<head>
<title>AxKit - Features</title>
</head>
<body>
<section>
<title>Summary of AxKit Features</title>
<para>
XSLT based pipelined XML transformations
</para>
<para>
Standards base stylesheet selection criteria
</para>
<para>
Output in more than 180 different character sets (although
browser support means that only about 10 of those are useful).
</para>
<para>
XSP support for the Perl language, including Perl DBI based SQL
taglib
</para>
<para>
Built in Perl interpreter - no more CGI overhead
</para>
<para>
Plug-in API - All components are replaceable with custom components.
</para>
<para>
Support for almost every platform around: Linux, Solaris, HPUX,
AIX, Windows NT, BeOS, *BSD, VMS.
</para>
<para>
Intelligent caching technology means your pages will be
delivered as fast as static pages
</para>
<para>
Deliver to different media using the sames set of URLs for all
media types. Media types supported are: Screen (web browsers),
Handheld (such as palmtop computers and mobile phones), TV (for
example Sky's "Open", or Microsoft's WebTV), braille, aural,
print, projection and tty (for text-only browsers).
</para>
<para>
GZipped output encoding - great for slower links
</para>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/index.xml
Index: index.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="application/x-xpathscript"
href="/stylesheets/webpage_html.xps"
title="default"?>
<?xml-stylesheet type="application/x-xpathscript"
href="/stylesheets/new/webpage_html.xps"
title="new"
alternate="yes"?>
<!DOCTYPE webpage [
<!ENTITY sidebar SYSTEM "/sidebar.xml">
<!ENTITY news SYSTEM "/news.xml">
]>
<webpage>
<head>
<title>Apache AxKit</title>
</head>
<body>
<section>
<title>Welcome to AxKit!</title>
<para><em>/'aks/-kit</em></para>
<para>
Apache AxKit is an XML Application Server for <ulink
url="http://www.apache.org/httpd.html">Apache</ulink>. It provides
on-the-fly conversion from XML to <ulink
url="http://www.w3.org/TR/REC-html40/types.html#type-media-descriptors"
>any format</ulink>, such as HTML, WAP
or text using either <ulink url="http://www.w3.org/">W3C</ulink>
standard techniques, or flexible custom code. AxKit also
uses a built-in Perl interpreter to provide some amazingly
powerful techniques for XML transformation.
</para>
<para>
AxKit makes the separation of content and presentation a
practical reality. The pipelining technique that AxKit uses allows
content to be converted to a presentable format in stages,
allowing certain platforms to see data differently to others.
AxKit allows web designers to focus on web site design,
content developers to work on a purely content basis, and
webmasters to focus on their core competencies.
</para>
<para>
Ax<emphasis>Kit</emphasis> is based on components that
are replaceable. This allows the web site developer to create a
completely custom XML Application Server based around the AxKit
API. AxKit can either automatically provide caching facilities,
or you can create your own cache handler, so that XML
transformations (which can be time consuming) only happen when
required.
</para>
<para>
The toolkit also provides ability to build component based web
sites, and dynamic content. Database integration is fully
supported, allowing either delivery of XML from a database, or
XML generation from a database query. Dynamic web components can
be built using the Perl language, making the possibilities as
infinite as CGI scripts, without the potential mess that CGI
programming can cause.
</para>
<para>
For more information please follow the links on this page.
</para>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/install.xml
Index: install.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="application/x-xpathscript"
href="/stylesheets/webpage_html.xps"
title="default"?>
<!DOCTYPE webpage [
<!ENTITY sidebar SYSTEM "/sidebar.xml">
<!ENTITY news SYSTEM "/news.xml">
]>
<webpage>
<head>
<title>AxKit - Installation</title>
</head>
<body>
<section>
<title>Installing AxKit</title>
<para>
Please see the "Introduction to AxKit" article in the
Documentation section.
</para>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/license.xml
Index: license.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="application/x-xpathscript"
href="/stylesheets/webpage_html.xps"
title="default"?>
<!DOCTYPE webpage [
<!ENTITY sidebar SYSTEM "/sidebar.xml">
<!ENTITY news SYSTEM "/news.xml">
]>
<webpage>
<head>
<title>AxKit - License</title>
</head>
<body>
<section>
<title>License for AxKit</title>
<para>
Since AxKit is now an official <a
href="http://apache.org/">Apache</a> project,
it is now licensed under the Apache license,
which you can find <a
href="http://www.opensource.org/licenses/apachepl.html">here</a>.
</para>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/mailinglist.xml
Index: mailinglist.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="/stylesheets/webpage_html.xps"
type="application/x-xpathscript" title="default"?>
<webpage>
<head>
<title>AxKit - Mailing List</title>
</head>
<body>
<section>
<title>Mailing List</title>
<para>
AxKit has a flourishing mailing list for both support and
development of AxKit. Any questions you have about AxKit are best
asked on the list, rather than directly emailing someone.
</para>
<para>
To join the AxKit mailing list, send a blank email to: <ulink
url="mailto:axkit-users-subscribe@axkit.org?subject=Just%20Hit%20Send..."
>axkit-users-subscribe@axkit.org</ulink>.
</para>
<para>
Alternatively you can use this web form to either join or leave
the list:
<form method="POST" action="/cgi-bin/ml-sub-request-axkit">
<table>
<tr><td>
<input type="radio" name="list_request" value="subscribe" checked="checked"></input>Subscribe<br></br>
<input type="radio" name="list_request" value="unsubscribe"></input>Unsubscribe
</td><td>Email Address: <input type="text" name="user_email" size="40" maxlength="100"></input></td><td><input type="submit" value="submit"></input></td></tr>
</table>
<input type="hidden" name="listtarget" value="axkit-users"></input>
<input type="hidden" name="acktitle" value="Thank You!"></input>
<input type="hidden" name="acktext" value="Thanks for your interest! Click <a href="/">here</a> to return to the main page."></input>
</form>
</para>
<subsection>
<title>List Archive</title>
<para>
The AxKit mailing list archive is online at <ulink
url="http://axkit.org/cgi-bin/ezmlm-cgi/3"
>http://axkit.org/cgi-bin/ezmlm-cgi/3</ulink>.
</para>
</subsection>
</section>
<section>
<title>Developers List</title>
<para>
There is also a developers mailing list. This list is strictly for
core AxKit developers, and is not for general questions about the
usage of AxKit. Here we discuss the architecture and new features,
and the list also recieves a list of all new check-ins to the code
repository along with changelog details.
</para>
<para>
To join AxKit-Devel, send a blank email to: <ulink
url="mailto:axkit-devel-subscribe@axkit.org?subject=Just%20Hit%20Send..."
>axkit-devel-subscribe@axkit.org</ulink>.
</para>
<para>
Alternatively you can use this web form either join or leave
the list:
<form method="POST" action="/cgi-bin/ml-sub-request-axkit">
<table>
<tr><td>
<input type="radio" name="list_request" value="subscribe" checked="checked"></input>Subscribe<br></br>
<input type="radio" name="list_request" value="unsubscribe"></input>Unsubscribe
</td><td>Email Address: <input type="text" name="user_email" size="40" maxlength="100"></input></td><td><input type="submit" value="submit"></input></td></tr>
</table>
<input type="hidden" name="listtarget" value="axkit-devel"></input>
<input type="hidden" name="acktitle" value="Thank You!"></input>
<input type="hidden" name="acktext" value="Thanks for your interest! Click <a href="/">here</a> to return to the main page."></input>
</form>
</para>
<subsection>
<title>List Archive</title>
<para>
The AxKit Devel mailing list archive is online at <ulink
url="http://axkit.org/cgi-bin/ezmlm-cgi/4"
>http://axkit.org/cgi-bin/ezmlm-cgi/4</ulink>.
</para>
</subsection>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/news.xml
Index: news.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
"http://my.netscape.com/publish/formats/rss-0.91.dtd">
<rss version="0.91">
<channel>
<title>xmlhack</title>
<link>http://www.xmlhack.com</link>
<description>Developer news from the XML community</description>
<language>en-us</language>
<copyright>Copyright 1999-2001, xmlhack team.</copyright>
<managingEditor>editor@xmlhack.com</managingEditor>
<webMaster>webmaster@xmlhack.com</webMaster>
<image>
<title>xmlhack</title>
<url>http://www.xmlhack.com/images/mynetscape88.gif</url>
<link>http://www.xmlhack.com</link>
<width>88</width>
<height>31</height>
<description>News, opinions, tips and issues concerning XML development</description>
</image>
<item>
<title>XSLT, XPath, and XQuery drafts</title>
<link>http://www.xmlhack.com/read.php?item=1493</link>
<description>The W3C has given developers a huge pile of holiday reading, with first drafts of XSLT 2.0 and XPath 2.0, as well as a collection of revised query drafts.</description>
<category>XSLT</category>
<category>Databases</category>
</item>
<item>
<title>Does XQuery fit all?</title>
<link>http://www.xmlhack.com/read.php?item=1492</link>
<description>While XQuery is usually considered an inappropriate tool for querying RDF collections or Topic Maps, Jonathan Robie demonstrated at XML 2001 that using XQuery functions over their normalized forms can be practical and not especially complex.</description>
<category>Databases</category>
<category>RDF</category>
</item>
<item>
<title>XBeans in second release</title>
<link>http://www.xmlhack.com/read.php?item=1491</link>
<description>XBeans.org has issued a second release of XBeans, improving its JavaBean and DOM-based data flows with a GUI customizer tool for making translations between the vocabularies expected by various XBeans.</description>
<category>Java</category>
<category>XML</category>
</item>
<item>
<title>Five challenges for XML</title>
<link>http://www.xmlhack.com/read.php?item=1490</link>
<description>James Clark, the first
recipient of the IDEAlliance XML Cup Award and opening keynote speaker at XML
2001, gave a lively description of the five challenges facing the XML community.</description>
<category>Community</category>
<category>Comment</category>
</item>
<item>
<title>XML 2001: open and balanced</title>
<link>http://www.xmlhack.com/read.php?item=1489</link>
<description>XML 2001, opened by its new chair Lauren Wood, marked the arrival of XML schema languages and a new competition between the W3C and ISO for XML standardization.</description>
<category>Community</category>
<category>Schemas</category>
</item>
<item>
<title>XML 1.1 Working Draft Published</title>
<link>http://www.xmlhack.com/read.php?item=1488</link>
<description>The W3C have announced the first public version of a Working
Draft of XML 1.1. The new draft has been issued to
meet the 'Blueberry' requirements published earlier this
year to much community debate.</description>
<category>XML</category>
<category>W3C</category>
</item>
</channel>
</rss>
1.1 xml-axkit/axkit.org/sidebar.xml
Index: sidebar.xml
===================================================================
<sb:sidebar xmlns:sb="http://axkit.org/NS/sidebar-1.0">
<sb:section>
<sb:title>About AxKit</sb:title>
<sb:item>
<sb:title>Index</sb:title>
<sb:url>/index.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>Features</sb:title>
<sb:url>/features.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>Installation</sb:title>
<sb:url>/install.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>Daily Churn</sb:title>
<sb:url>/irclog.xml</sb:url>
</sb:item>
</sb:section>
<sb:section>
<sb:title>Getting AxKit</sb:title>
<sb:item>
<sb:title>License</sb:title>
<sb:url>/license.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>Download</sb:title>
<sb:url>/download/</sb:url>
</sb:item>
<sb:item>
<sb:title>Mailing List</sb:title>
<sb:url>/mailinglist.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>Contribute</sb:title>
<sb:url>/contribute.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>Support</sb:title>
<sb:url>/support.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>Bugs</sb:title>
<sb:url>http://rt.cpan.org/NoAuth/Bugs.html?Dist=AxKit</sb:url>
</sb:item>
</sb:section>
<sb:section>
<sb:title>Documentation</sb:title>
<sb:item>
<sb:title>Quick Start Guide</sb:title>
<sb:url>/docs/quick_start.dkb</sb:url>
</sb:item>
<sb:item>
<sb:title>Introduction to AxKit</sb:title>
<sb:url>/docs/introduction.dkb</sb:url>
</sb:item>
<sb:item>
<sb:title>Associating XML Files with Stylesheet</sb:title>
<sb:url>/docs/associating_stylesheets.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>XPathScript Guide</sb:title>
<sb:url>/docs/xpathscript/guide.dkb</sb:url>
</sb:item>
<sb:subsection expand="no">
<sb:title>XSP</sb:title>
<sb:url>/docs/xsp/index.xml</sb:url>
<sb:item>
<sb:title>XSP Guide</sb:title>
<sb:url>/docs/xsp/guide.dkb</sb:url>
</sb:item>
<sb:item>
<sb:title>SQL Taglib</sb:title>
<sb:url>/docs/xsp/sqltaglib.dkb</sb:url>
</sb:item>
<sb:item>
<sb:title>Writing Taglibs</sb:title>
<sb:url>/docs/xsp/writing_taglibs.dkb</sb:url>
</sb:item>
</sb:subsection>
<sb:item>
<sb:title>FAQ</sb:title>
<sb:url>/faq.xml</sb:url>
</sb:item>
<sb:item>
<sb:title>Deutsches Artikel @ iX</sb:title>
<sb:url>http://www.heise.de/ix/artikel/2001/03/167/</sb:url>
</sb:item>
<sb:subsection expand="no">
<sb:title>Presentations</sb:title>
<sb:url>/docs/presentations/index.xml</sb:url>
<sb:item>
<sb:title>TPC 2001 Presentations</sb:title>
<sb:url>/docs/presentations/tpc2001/</sb:url>
</sb:item>
</sb:subsection>
<sb:item>
<sb:title>Provider HOWTO</sb:title>
<sb:url>/docs/provider-howto.dkb</sb:url>
</sb:item>
</sb:section>
<sb:section>
<sb:title>Examples</sb:title>
<sb:item>
<sb:title>DocBook Example</sb:title>
<sb:url>/examples/docbook.dkb</sb:url>
</sb:item>
<sb:item>
<sb:title>CV Example</sb:title>
<sb:url>/examples/cv_example.xml</sb:url>
</sb:item>
</sb:section>
</sb:sidebar>
1.1 xml-axkit/axkit.org/support.xml
Index: support.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="application/x-xpathscript"
href="/stylesheets/webpage_html.xps"
title="default"?>
<!DOCTYPE webpage [
<!ENTITY sidebar SYSTEM "/sidebar.xml">
<!ENTITY news SYSTEM "/news.xml">
]>
<webpage>
<head>
<title>AxKit - Support</title>
</head>
<body>
<section>
<title>Commercial Support for AxKit</title>
<para>
Commercial support for AxKit is available from AxKit.com
Ltd (the developers of AxKit). The commercial support site for
AxKit is <ulink url="http://axkit.com/">axkit.com</ulink>.
</para>
<para>
While this level of support is available, we urge you to try the
free support channels listed below.
</para>
</section>
<section>
<title>Free Support</title>
<para>
AxKit has excellent free community based support, where the core
development team regularly participate by answering questions.
There are two main avenues for this support: a mailing list, and
an IRC chat channel.
</para>
<subsection>
<title>Mailing List</title>
<para>
The AxKit mailing list has its <ulink url="mailinglist.xml">own
page</ulink> with subscription and archive details.
</para>
</subsection>
<subsection>
<title>IRC</title>
<para>
The AxKit IRC channel is available on the Rhizomatic IRC
servers at irc.rhizomatic.net:6667. The channel name is
#axkit. We're a friendly bunch there, but don't worry if
you see us shouting <ulink url="http://berjon.com/dahut.txt">DAHUUUUT!!!</ulink> at eachother from time to time.
</para>
</subsection>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/docs/associating_stylesheets.xml
Index: associating_stylesheets.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="/stylesheets/webpage_html.xps" type="application/x-xpathscript"?>
<webpage>
<head>
<title>How AxKit Picks Stylesheets and/or Processors</title>
</head>
<body>
<section>
<title>Introduction</title>
<p>
AxKit works mainly by associating stylesheets (and by inference, a
stylesheet processor), with XML files when you request the XML file. The
process is defined in <a
href="http://www.w3.org/TR/REC-html40">REC-html40</a>, however the links
are confusing so I hope to clear things up a bit here.
</p>
<p>
Please note that while the specification we're talking about is the HTML
4 specification, this has nothing specifically to do with HTML. The <a
href="http://www.w3.org/TR/xml-stylesheet">xml-stylesheet</a> document
simply references the HTML 4 spec to save re-iterating what has already
been specified elsewhere.
</p>
</section>
<section>
<title>How it works</title>
<p>
When an XML file is encountered (determined by AxKit's XMLFinder module
- which checks first for a .xml extension, then for a <?xml?>
processing instruction), AxKit needs to determine how to process that
file. To do so it needs a list of stylesheets to process that file
against. The stylesheet type (a MIME type) determines the module used to
actually process the XML file together with the Stylesheet. It's also
worth noting that a stylesheet file isn't necessary if using a
DefaultStyleMap - this benefit could be useful, for example, for a
Cocoon-like XSP page where there is no stylesheet to interact with.
</p>
<p>
First, the <?xml-stylesheet?> processing instructions are considered
</p>
<p>
If the media doesn't match it is discarded immediately. Media types in
<?xml-stylesheet?> are processed as follows: First they are split
on /,\s*/. Then each element of the resulting array is stripped of
characters following and including the first non-ascii-alphanumeric
character. So "printer and dpi > 90" gets turned into "printer". Then they
are grepped for
/^screen|tty|printer|handheld|braille|tv|projection|aural|all$/ (case
sensitive). Finally the match test: A media type of "all" always matches
regardless of what is in preferred_media. Otherwise, a case sensitive
match is done (in perl terms, the "eq" operator). Provided a media
matches, then we go on to determine if this stylesheet should be used
depending on the title="..." attribute and the
alternate="..." attribute. The terms "Persistant", "Preferred" and
"Alternate" below are straight from REC-html40.
</p>
<subsection>
<title>Persistant Stylesheets</title>
<p>
If there is no title and alternate="no" (or no
alternate="..." attribute) then the stylesheet is added to the front of the list
of stylesheets - this is a persistant stylesheet and is _always_ applied
first (unless there are other persistant stylesheets, in which case this
is applied after those).
</p>
</subsection>
<subsection>
<title>Preferred Stylesheets</title>
<p>
If there is a title and alternate="no" (or no alternate attrib) then the
stylesheet is pushed onto the list of stylesheets to apply if there is
either no preferred_style, or preferred_style matches.
</p>
</subsection>
<subsection>
<title>Alternate Stylesheets</title>
<p>
If there is a title and alternate="yes", then the stylesheet is pushed
onto the list of stylesheets to apply if there is a preferred_style and it
matches the title.
</p>
</subsection>
<subsection>
<title>If all else fails</title>
<p>
Finally, just to be safe, StyleFinder maintains a list of
media="screen" stylesheets. If there are no stylesheets in the list at the
end of processing, the "screen" ones are used. Note though that the same
rules as above apply there too, and media="screen" is just used as a last
resort.
</p>
</subsection>
</section>
<section>
<title>And Finally...</title>
<p>
So when the parser that reads the <?xml-stylesheet?>'s returns, if it
returns a list of stylesheets, then fine. If not, we read
$cfg->DefaultStyleMap, the default implementation of which is to read the
AxAddDefaultStyleMap options in your httpd.conf or .htaccess. The details
of AxAddDefaultStyleMap are in the AxKit manpage.
</p>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/docs/index.xml
Index: index.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="application/x-xpathscript"
href="/stylesheets/webpage_html.xps"
title="default"?>
<!DOCTYPE webpage [
<!ENTITY sidebar SYSTEM "/sidebar.xml">
<!ENTITY news SYSTEM "/news.xml">
]>
<webpage>
<head>
<title>AxKit - Documentation</title>
</head>
<body>
<section>
<title>Documentation</title>
<para>
AxKit is a complex and powerful system, although getting started
in AxKit is as easy as 1,2,3. The documentation here hopes to
guide you through AxKit right from simple tasks such as
transforming XML files using stylesheets, up to extending the
AxKit application server to suit your needs.
</para>
<para>
Please start with the getting started with AxKit guide. More
advanced XMLers might be interested in XPathScript - an
alternative transformation language to XSLT. If XSLT were
likened to awk, XPathScript would be Perl.
</para>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/docs/introduction.dkb
Index: introduction.dkb
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="/stylesheets/docbook_screen.xps" type="application/x-xpathscript"
title="default"?>
<?xml-stylesheet href="/stylesheets/docbook_print.xps"
type="application/x-xpathscript" title="print" alternate="yes"?>
<!DOCTYPE article [
<!ENTITY prompt "% ">
]>
<article>
<artheader>
<title>An Introduction to AxKit</title>
<author>
<firstname>Matt</firstname>
<surname>Sergeant</surname>
<affiliation>
<address><email>matt@sergeant.org</email></address>
</affiliation>
</author>
<copyright>
<year>2000</year>
<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
</copyright>
<abstract>
<para>An introduction to AxKit, the XML Application Server for
Apache</para>
</abstract>
</artheader>
<sect1>
<title>Introduction</title>
<para>
XML has, in theory, solved one of the problems facing web site
developers: How to develop a consistent look across your site using a
template/stylesheet system. Unfortunately a lot of the solution to that
problem still remains out of reach of the majority of web sites. One
major piece of the puzzle that still has not been perfected is the
authoring stage. Authoring XML for non-XML savvy designers is still
problematic. Some tools exist to solve this problem, such as XMetaL, but
as first generation tools these still have rather large weak spots. I
expect to see this side of the puzzle solved with this seasons software
releases - I hope I'm not wrong.
</para>
<para>
The other side of the puzzle for web developers is delivery. Its going
to be an extremely long time until all clients support some sort of
client side transformation. And I remain unconvinced this is the right
way to do it. Take XSLT as an example; the Apache group are right now
trying to develop a way to do XSLT without building an in-memory tree
structure of the XML document. However, as an implementor of XPath
(Perl's XML::XPath module) myself, I think they are going to find this
a really tough nut to crack. Not only that, but all this parsing is
extremely resource intensive, and I think that's the wrong model to be
looking at, especially when we want to deliver to handheld devices.
</para>
<para>
That leaves server side transformation. There are several available
options for this. The most immediately obvious is static transformation.
However that can start to become a maintainence nightmare. Then there
are application servers that operate within their own world, such as
Enhydra and Zope. These are excellent solutions for shops that already
use those solutions. Finally there is <application>Cocoon</application>,
a truly awsome technology,
now part of the <application><ulink url="http://www.apache.org/">Apache
project</ulink></application>. Cocoon is a full blown Application
server built around XML technology. Part of Cocoon is a system which
associates stylesheets with XML files. See <ulink
url="http://www.w3.org/TR/xml-stylesheet">http://www.w3.org/TR/xml-stylesheet</ulink>
for details on this method. I don't personally have any gripes with
Cocoon (I do have gripes with Java, but thats another issue). To an
extent AxKit emulates Cocoon (although in reality, Cocoon and AxKit are
just implementing suggested standards).
</para>
<para>
AxKit fits into this picture by providing simple intuitive ways for web
developers to deliver XML to clients in different media formats and
stylesheets. AxKit also, very much like Cocoon, provides caching
facilities built into its core, so that only at single points of change
will AxKit attempt to re-create the document being delivered. Unless, of
course, the developer explicitly decides not to cache the document.
Unlike Cocoon though, AxKit is built in perl, and integrates extremely
tightly with Apache. AxKit also provides some of the technology natively
that Cocoon 2 is going to deliver (AxKit also doesn't provide some of
the technology that Cocoon does deliver!).
</para>
</sect1>
<sect1>
<title>Why Choose AxKit?</title>
<para>
AxKit is based on a plugin architecture. This allows the developer to
very quickly design modules based on currently available technology to
achieve: New stylesheet languages, new methods for delivering alternate
stylesheets and new methods for determining media types. Because it's
built in perl, these sort of plugins are incredibly simple to develop.
Not long after releasing AxKit, a developer wrote a file suffix
stylesheet chooser module, which returns different stylesheets if the
user requests file.xml.html or file.xml.text, in just 15 lines of code.
This plugin architecture also makes developing new stylesheet modules
very simple, using some of the readily available code in Perl's
excellent CPAN (the Comprehensive Perl Archive Network). A stylesheet
module to deliver XML-News files as HTML would only take a few lines of
code based on David Megginson's XMLNews::HTMLTemplate module, and AxKit
works out all the nuances of caching for you.
</para>
<para>
Another important part of this is that AxKit is
<emphasis>pragmatic</emphasis> about what it delivers to clients. It
doesn't have to be HTML, or XHTML, strict HTML 4, or indeed compliant to
any particular standard. This decision was made because no matter what,
clients are still not going to upgrade their browsers just because you
want them to. So AxKit says that you can deliver XML or XHTML if you
want to (and the tools are there for you to do so), but its just as easy
to deliver any other format.
</para>
<para>
AxKit comes with a number of pre-built stylesheet modules, including
two XSLT modules: one built around Perl's XML::XSLT module, a DOM based
XSLT implementation that is in the beginning stages, and one built
around Ginger Alliance Ltd's Sablotron XSLT library, which is a much
more complete XSLT implementation built in C++, and is extremely fast.
For the closet XSLT haters out there (come on - I know there are quite
a few!) there's XPathScript - a language that takes some of the good
features of XSLT, such as node matching and finding using XPath, and
combines it with the power of ASP-like code integration and in-built
Perl scripting. XPathScript also compiles your stylesheet into native
perl code whenever it changes, so execution times are very good for XML
stylesheet processing. As an example of XPathScript's power, I've
created a DocBook stylesheet that dynamically can show separate sections
of a DocBook/XML file.
</para>
<para>
The core of AxKit is also very quick. Delivering cached results
it runs at about 80% of the speed of Apache. It achieves this primarily
because it's built in mod_perl. The tight coupling with Apache that
mod_perl provides means that an awful lot of the code is running in
compiled C. In order to deliver cached results, AxKit just tells Apache
where to find the cached file, and that it doesn't want to handle it.
Apache comes up with the goods at its usual lightning speed.
</para>
<para>
Finally, AxKit works hand-in-hand with Apache. So any webmaster skills
will not go to waste. Cocoon 2 is about to deliver a sitemap feature,
whereby you don't have to use <literal><?xml-stylesheet?></literal> processing
instructions everywhere to build up your site. AxKit already provides
this, and integrates directly with Apache's
<literal><Files></literal>,
<literal><Location></literal> and <literal><Directory></literal>
directives. All AxKit's configuration takes this
approach, so you never have to teach a webmaster any new tricks to build
up your XML site.
</para>
</sect1>
<sect1>
<title>Putting it all together</title>
<para>
In simple terms, how does AxKit work? AxKit registers with Apache a "handler". In
Apache terms this is a module that works in a particular part of the request
phase (which cover things like Authentication, Type checking, Response,
and Logging). When a request for a file comes in, AxKit does some very
quick checking to see if the file is XML. The main checks performed are
to see if the file extension is <filename>.xml</filename>, and/or to check the first few
characters of the file for the <literal><?xml?></literal> declaration. If the file is
not XML, AxKit lets Apache deliver the file as it would normally. Note
that using Apache's configuration methods described above, it's quite
possible to apply this only to certain parts of your web site.
</para>
<para>
When an XML file is detected, the next step is to call any plugin
modules that determine the media type and/or stylesheet preference.
Media type chooser plugins normally look at the User-Agent header, or
at the Accept header, however its possible to use any method at
all to determine the media type. Stylesheet choosers exist currently
based on Path Info (this is a path following the filename, so you could
request <filename>myfile.xml/mystyle</filename>), querystring (for example
<filename>myfile.xml?style=mystyle</filename>), and file suffix
(<filename>myfile.xml.mystyle</filename>).
</para>
<para>
The final part, and the most significant part, is the plumbing together
of all the stylesheets with the XML file in the right order,
implementing cascading where appropriate, and also to "do the right
thing" with regards to the cache. One "leg-up" we have on Cocoon here is
that AxKit invalidates the cache when external entities (parsed or
unparsed) change too. This allows modular stylesheets to change only
part of their make-up and ensure that changes to these sub-components
cause a re-build of the cache.
</para>
<sect2>
<title>Mapping XML Files to Stylesheets</title>
<para>
AxKit uses two separate methods for mapping XML files to stylesheets.
The primary method is to use the W3C recommendation at <ulink
url="http://www.w3.org/TR/xml-stylesheet">http://www.w3.org/TR/xml-stylesheet</ulink>.
This specifies that a <literal><?xml-stylesheet?></literal> processing instruction at
the beginning of the xml file (after the <literal><?xml?></literal> declaration, and
before the first element) defines the location and type of the
stylesheet. The actual details of how all this works are defined in
<ulink url="http://www.w3.org/TR/REC-html40">TR/REC-html40</ulink>
(which has just recently been superceded by html 4.01). The second
method of mapping XML files to stylesheets is used when no usable
<literal><?xml-stylesheet?></literal> directives are found in the XML
file. This uses a <option>DefaultStyleMap</option>
option in your Apache configuration files. These directives can be used
anywhere within Apache's <literal><Files></literal>,
<literal><Location></literal>, <literal><Directory></literal> and
<filename>.htaccess</filename> configuration system. In this way it's possible to define
complex mapping rules for different file types and locations in
whichever manner pleases you.
</para>
<para>
AxKit then uses the type of the stylesheet (in the
<literal>type="..."</literal> attribute
of the <literal><?xml-stylesheet?></literal> directive, or the first parameter of the
<option>AxAddDefaultStyleMap</option> option) to decide on a module to use to process that
type of file. Again this is slightly different to
<application>Cocoon 1.x</application>, which requires
special <literal><?cocoon?></literal> directives to be added to your XML files to
determine the processor module to use. The type is then mapped to a
module using another Apache configuration option:
<option>AxAddStyleMap</option>. Again,
this directive can appear anywhere within Apache's configuration
structure. This allows you to try different modules for your processing
of the same file (for example, you might like to try both XSLT
processors to see which suits your needs best).
</para>
</sect2>
<sect2>
<title>Choosing a Stylesheet</title>
<para>
In the course of examining the options of which stylesheets to choose,
often a single XML file (or a <option>DefaultStyleMap</option> - see above) can provide
more than one option. There are two important parts of this to consider.
The first is choosing from multiple stylesheets based on media type, and
stylesheet preference. The Media type of a stylesheet must always match the
requested media type, or be of media type <literal>"all"</literal>, however it's worth
noting here that <application>Cocoon</application> provides many alternative media types to the
W3C's specification list, such as "wap", "lynx", "explorer" and
"netscape". The merits of this are debatable. The stylesheet preference
is based on 3 types of stylesheet: A persistant stylesheet, a preferred
stylesheet and an alternate stylesheet. Persistant stylesheets
declarations contain no <literal>title="..."</literal> attribute, preferred stylesheets
contain a <literal>title="..."</literal> attribute, but have
<literal>alternate="no"</literal> (or no
<literal>alternate="..."</literal> attribute), and alternate stylesheets contain a
<literal>title="..."</literal> attribute and have explicitly set
<literal>alternate="yes"</literal>.
</para>
<para>
AxKit always applies persistant stylesheets, and will apply alternate
stylesheets only if a plugin has determined that one should be
displayed, otherwise the preferred stylesheets are used. This all seems
rather confusing and long winded, but it allows a very modular system,
and also allows for wonderful flexibility in choosing stylesheets for
users. For example, a plugin could connect to a database and retrieve
the correct alternate stylesheet for a particular user based on an
authentication token. This would allow users to change the whole look of
their favourite web site, and AxKit will do all the hard work for you.
</para>
</sect2>
<sect2>
<title>Cascading Stylesheets</title>
<para>
It's easy to get confused by the term "stylesheet" here. A quick read of
this might make it seem like all they are good for is transforming
static XML files into further static XML files. This is especially the
case if all you can picture is XSL(T) (or even CSS). However stylesheets
in AxKit's terms can do anything, provided you can build a Language
module to parse it. The concept of stylesheets in AxKit replace all the
stages in Cocoon: Producer, Processor and Formatter. So it becomes
possible to, just as in Cocoon, return database results, format add
tags, and format the result to WAP, HTML or any possible format.
</para>
<para>
The term cascading here therefore refers to the case of one stylesheet's
results "cascading" into the next. With AxKit there are a number of ways
to achieve that. The first and simplest method is to have all your
stylesheets based on DOM, and produce DOM trees. When all the
stylesheets have finished processing, AxKit takes care to dispose of
your DOM tree and output the results to the user agent.
</para>
<para>
The second method of cascading is to simply cascade the textual results
of your output. This is necessary with modules like Sablotron where
there is no DOM tree available. Modules further down the processing
stream are able to parse this string directly (provided they are
designed to work this way) as XML, and continue processing.
</para>
<para>
The final, and possibly most interesting method, is to use "end-to-end
SAX". This is where AxKit sets up a chain of SAX handlers to process the
document with. AxKit stylesheet languages based on SAX are responsible
for simply sending on SAX events to the next SAX handler up the chain
(they are provided a SAX handler to pass events to on construction). The
final SAX handler in the chain simply outputs its results to the
browser. This doesn't sound particularly interesting, until you consider
that this end-to-end system starts outputting data to the browser
immediately as soon as parsing begins. This system allows database
modules to not build DOM trees in memory, which can be resource
consuming, but to simply fire SAX events, and the output from the
database will appear as results are available. Cocoon 2 will have a
system similar, if not identical, to this.
</para>
</sect2>
</sect1>
<sect1>
<title>A Simple Setup Example</title>
<para>
Setting up AxKit is simple. I don't believe in tools like this being
hard to use or even hard to setup. Provided you can use an editor and
modify a few Apache configuration files, setup should be a breeze.
</para>
<para>
Unfortunately AxKit requires mod_perl, so there is an extra component to
install first. Installation of mod_perl can be complex, depending upon
your setup. To that end I will just provide a link: <ulink
url="http://perl.apache.org/guide/install.html">The mod_perl Guide -
Installation</ulink>.
</para>
<para>
Now onto AxKit itself. First, installing the required perl modules is very simple. Download
AxKit (see link below), extract the archive and change to the directory created. Then
simply type:
<informalexample>
<screen>
&prompt;<userinput>perl Makefile.PL</userinput>
&prompt;<userinput>make</userinput>
&prompt;<userinput>make test</userinput>
&prompt;<userinput>make install</userinput>
</screen>
</informalexample>
If you don't have apxs in your path, mod_perl versions below 1.24 will
produce a warning at the first step. This warning can be ignored.
</para>
<para>
Next up, editing Apache's configuration files. First you need to enable
AxKit so that Apache understands AxKit's configuration directives, so
add the following line to your httpd.conf file:
<informalexample>
<screen>
PerlModule AxKit
</screen>
</informalexample>
Finally, you can add in the core of AxKit - handler itself. This
can be added to any .htaccess file, or to your httpd.conf file:
<informalexample>
<screen>
SetHandler perl-script
PerlHandler AxKit
AxAddStyleMap text/xsl Apache::AxKit::Language::Sablot
</screen>
</informalexample>
The last line there associates the type "text/xsl" with the stylesheet
module specified.
</para>
<para>
Now you're ready to start serving up XML files. Check out the example
files in the AxKit distribution, these should get you started.
</para>
</sect1>
<sect1>
<title>Conclusions</title>
<para>
AxKit provides web developers with the tools they need to deliver
complex systems quickly, and eases them into the development process. It
gives them the power to develop their own system for stylesheet decision
making and also the flexibility to design completely new stylesheet
languages. All of this while integrating tightly with Apache, providing
a fast, scalable and well architectured system. But then, I'm biased.
</para>
<para>
AxKit is not finished yet, however the majority of the features
described above are built and working very reliably. The most
significant things missing from AxKit are SAX based stylesheet
languages (which just need to be designed and built - which I have a
number of ideas for), and alternate ways to generate the initial XML
file (which cocoon calls "Producers"). These will be coming in a future
release. Being free software I hope people will jump in and help. We
have the beginnings of an active mailing list, where you can vote on
features, or help develop them, or simply lurk. We're moving extremely
quickly with the features. Developing in Perl allows us to do this,
while still maintaining readable code (something I deem very important
- so don't assume because it's written in Perl that it's going to be a
ball of spaghetti!). If there's something you'd like to see in AxKit,
please join the mailing list and participate with us.
</para>
</sect1>
<sect1>
<title>Links</title>
<para>
The following are links relevant to this article:
<itemizedlist>
<listitem><ulink url="http://axkit.org/">AxKit</ulink> - The
main homepage for AxKit.</listitem>
</itemizedlist>
</para>
</sect1>
</article>
1.1 xml-axkit/axkit.org/docs/provider-howto.dkb
Index: provider-howto.dkb
===================================================================
<?xml version="1.0" standalone="no"?>
<?xml-stylesheet href="/stylesheets/docbook_screen.xps"
type="application/x-xpathscript"
media="screen"
title="default" ?>
<?xml-stylesheet href="/stylesheets/docbook_screen_fancy.xps"
type="application/x-xpathscript"
media="screen"
title="fancy"
alternate="yes" ?>
<?xml-stylesheet href="/stylesheets/docbook_print.xps"
type="application/x-xpathscript"
media="screen"
title="print"
alternate="yes" ?>
<?xml-stylesheet href="/stylesheets/docbook_handheld.xps"
type="application/x-xpathscript"
media="handheld"
title="default"?>
<article label="ProviderHOWTO">
<artheader>
<title>AxKit Provider HOWTO</title>
<author>
<firstname>Riccardo</firstname>
<surname>Cambiassi</surname>
<email>brujah@infodrome.net</email>
</author>
<abstract>
<para>A quite complete HOWTO on writing Providers for AxKit</para>
</abstract>
</artheader>
<sect1>
<title>Introduction</title>
<para>
AxKit is an XML Application Server for Apache. It provides on-the-fly conversion from XML to any format, such as HTML, WAP or text using either W3C standard techniques, or flexible custom code. AxKit also uses a built-in Perl interpreter to provide some amazingly powerful techniques for XML transformation. For more information on AxKit see <ulink url="http://www.axkit.org">www.axkit.org</ulink>.
</para>
<para>
One of the most interesting features of AxKit is it's object architecture: it makes easy (and quite fun) to extend it to meet your own needs.
From the AxKit manpage we learn that we can operate on three aspects of AxKit behaviour:
<itemizedlist>
<listitem>
<emphasis>AxConfigReader</emphasis> returns information about various configuration options.
</listitem>
<listitem>
<emphasis>AxProvider</emphasis> is the means by wich AxKit gets its resources from.
</listitem>
<listitem>
<emphasis>AxCacheModule</emphasis> is responsible for storing cache data for later retrieval.
</listitem>
</itemizedlist>
The special feature that we'll exploit in this document is the extension of the <emphasis>Provider</emphasis> module.
</para>
</sect1>
<sect1>
<title>Overview of AxKit Providers</title>
<para>All Providers descends from <application>Apache::AxKit::Provider</application> Module.
From its manpage (and a bit of hacking) we learn that it relies upon the following (main) methods:
<itemizedlist>
<listitem>
<application>new()</application>
- creates a new Provider object; normally this method should be defined only in the parent class (<application>Apache::AxKit::Provider</application>). To handle custom initialization, look at <application>init()</application> below.
</listitem>
<listitem>
<application>init()</application>
- intialize the provider; it is called by <application>new()</application>. Here the Provider module has the chance to carry out custom initialization procedures. The standard behaviour is to do nothing.
<note>The <application>init()</application> method should accept argument as follows: the first is always the request object; then we have a list of key => value pairs
containing either 'uri' or 'file' key for the desired resource id and, in case of an external entity, 'rel' containing the Provider object for the main document. [is this correct ???].</note>
</listitem>
<listitem>
<application>process() *</application>
- answers the question "Shall we process this request?". Should return 1 if it can process the resource or die it it cannot, eventually <emphasis>throwing</emphasis> an opportune exception (see below).
</listitem>
<listitem>
<application>exists() *</application>
- answers the question: "Does the resource exist?". Return 1 if it exists.
</listitem>
<listitem>
<application>mtime() *</application>
- answers the question "How old is this resource?". Return the modification time in days before the current time. It's used to test the validity of cached data.
</listitem>
<listitem>
<application>get_fh() *</application>
- returns an open filehandle for the resource (or die if that's not possible).
</listitem>
<listitem>
<application>get_strref() *</application>
- returns a reference to a scalar containing the resource; note that at least one of get_fh or get_strref must work.
</listitem>
<listitem>
<application>key() *</application>
- returns an unique identifier for this resource.
</listitem>
<listitem>
<application>get_styles()</application>
- extract stylesheets and external entities from the resource.
</listitem>
<listitem>
<application>get_ext_ent_handler() </application>
- return a reference to be used instead of XML::Parser's default external entity handler.
</listitem>
</itemizedlist>
<note>All methods marked with * are not defined in <application>Apache::AxKit::Provider</application>, so each real Provider will have to implement their own.</note>
</para>
<sect2>
<title>How do Providers work?</title>
<para>Throughout the processing of a request, whenever AxKit needs to fetch a resource, it creates an <application>Apache::AxKit::Provider</application> object for the desired resource.
This is a generic (highlevel) object whose job is not limited to define standard methods (see above) but also to verify which actual Provider is in charge and <application>reconsecrate()</application> to it.
To change the default Provider you can use the <emphasis>AxProvider</emphasis> directive or simply set the variable with <emphasis>PerlSetVar</emphasis> directive from your httpd.conf:
<programlisting>
# either
AxProvider Apache::AxKit::Provider::File
# or
PerlSetVar AxProvider Apache::AxKit::Provider::File
</programlisting>
</para>
<para>
A new Provider is created in order to get:
<itemizedlist>
<listitem>The XML document</listitem>
<listitem>Every stylesheets</listitem>
<listitem>Every external entity</listitem>
</itemizedlist>
</para>
<example>
<para>For example, let's suppose we have requested the following document at the url http://localhost/sample.xml</para>
<programlisting>
<?xml version="1.0" ?>
<xml-stylesheet href="/sample.xsl" type="text/xsl"?>
<page>
<para>Hello World!</para>
</page>
</programlisting>
From a quite high level point of view, AxKit will:
<orderedlist>
<listitem>Create a Provider for this resource [my $provider = Apache::AxKit::Provider->new($r)]</listitem>
<listitem>Check if to proceed in processing this resource [if ($provider->process())]</listitem>
<listitem>Get the resource [$provider->get_fh or $provider->get_strref()]</listitem>
<listitem>Parse the XML to extract all stylesheet and external entities and, for each resource:</listitem>
<orderedlist>
<listitem>Create a Provider for this resource, specifying the 'uri' in case of a stylesheet or either 'file' or 'uri' in case of an external entity.
In our example we have just one stylesheet ('/sample.xsl') and no external entities.</listitem>
<listitem>Check if we can process this resource</listitem>
<listitem>Get the resource</listitem>
</orderedlist>
<listitem>Once AxKit has got all resources it will use Language Processors to apply the stylesheet to the document, and then delivery the result to the browser.</listitem>
</orderedlist>
</example>
</sect2>
</sect1>
<sect1>
<title>Standard Providers</title>
<para>What follows is a list of the Providers that come with the standard distribution of AxKit.</para>
<sect2>
<title>File</title>
<para>That's the default. It gets input from files (surprise!) relative to Apache's DocumentRoot. This is also the most complete Provider in that it defines all features and IMO the best starting place for everyone who wants to develop a new Provider.
</para>
<para>
It defines:
<itemizedlist>
<listitem> get_fh()</listitem>
<listitem> get_strref()</listitem>
<listitem>key()</listitem>
<listitem>exists()</listitem>
<listitem>process()</listitem>
<listitem>mtime()</listitem>
</itemizedlist>
and redefines:
<itemizedlist>
<listitem>init().</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Scalar</title>
<para>This is a basic provider, gets input from a scalar variable.
AxKit uses this Provider in order to handle Error messages.</para>
<para>
It defines all standard methods:
<itemizedlist>
<listitem>process()</listitem>
<listitem>exists()</listitem>
<listitem>mtime()</listitem>
<listitem>get_fh</listitem>
<listitem>get_strref()</listitem>
<listitem>key()</listitem>
</itemizedlist>
and redefines:
<itemizedlist>
<listitem>new()</listitem>
<listitem>init()</listitem>
<listitem>apache_request</listitem>
<listitem>get_styles()</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Filter</title>
<para>The most exotic Provider and, quite surprisingly, the most simple: it works with <application>Apache::Filter</application> in order to get data from another <emphasis>PerlHandler</emphasis>. This requires other Handlers to be "Filter aware". By the time of this writing, this applies to:
<itemizedlist>
<listitem>Apache::Registry</listitem>
<listitem>Apache::SSI</listitem>
<listitem>Apache::ASP</listitem>
<listitem>HTML::Mason</listitem>
<listitem>Apache::SimpleReplace</listitem>
</itemizedlist>
The Filter Provider is derived from File Provider and redefines just:
<itemizedlist>
<listitem>init()</listitem>
<listitem>get_fh()</listitem>
<listitem>get_strref()</listitem>
<listitem>mtime()</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
<sect1>
<title>Using Filter Provider</title>
<para>Here we'll discuss how to use Filter Provider in order to exploit Apache::Filter</para>
<sect2>
<title>Apache::Filter</title>
<para>To enable the Filter chain, you will have to operate on both the Apache configuration and the single handlers (here called Filters). The following piece of code is borrowed from Apache::Filter manpage:</para>
<programlisting>
#### In httpd.conf:
PerlModule Apache::Filter
# That's it - this isn't a handler.
<Files ~ "*\.blah">
SetHandler perl-script
PerlSetVar Filter On
PerlHandler Filter1 Filter2 Filter3
</Files>
#### In Filter1, Filter2, and Filter3:
$r = $r->filter_register(); # Required
my $fh = $r->filter_input(); # Optional (you might not need the input FH)
while (<$fh>) {
s/ something / something else /;
print;
}
</programlisting>
<para>As we noticed before, currently the following public modules are Filter-aware.</para>
<itemizedlist>
<listitem>
Apache::Registry (using Apache::RegistryFilter, included with Apache::Filter)
</listitem>
<listitem>
Apache::SSI
</listitem>
<listitem>
Apache::ASP
</listitem>
<listitem>
HTML::Mason
</listitem>
<listitem>
Apache::SimpleReplace
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>... with simple CGIs</title>
<para>How to use Apache::RegistryFilter and AxKit. This is pretty simple: just add in httpd.conf:</para>
<programlisting>
PerlModule Apache::RegistryFilter
PerlModule AxKit
<Location /filter>
SetHandler perl-script
PerlSetVar Filter On
AxProvider Apache::AxKit::Provider::Filter
PerlHandler Apache::RegistryFilter AxKit
</Location>
</programlisting>
<para>Then write a CGI that will generate your xml document and put it in the $DocumentRoot/filter/ directory. What follows is a minimalistic example:</para>
<programlisting>
#!/usr/bin/perl
print <<EOT;
<?xml version="1.0"?>
<?xml-stylesheet href="plain.xsl"
type="text/xsl"?>
<html>
<head>
<title>AxKit/Simple CGI filter test</title>
</head>
<body>
<table bgcolor="#FFFFFF">
EOT
print map { "<tr><td>$_</td><td>$ENV{$_}</td></tr>" } keys %ENV;
print <<EOT;
</table>
</body>
</html>
EOT
</programlisting>
<para>Finally, you will need a stylesheet to process the xml generated by the CGI. Here's an example:</para>
<programlisting>
<?xml version="1.0" ?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
>
<xsl:output
method="html"
indent="yes"
encoding="ISO-8859-1"
/>
<xsl:template match="/">
<xsl:apply-templates select="/html/*"/>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="./@*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
</programlisting>
</sect2>
<sect2>
<title>... with Apache::ASP</title>
<para><emphasis>Apache::ASP</emphasis> provides an Active Server Pages port to the Apache Web Server with Perl as the host scripting language. For more information about Apache::ASP, see <ulink url="http://www.apache-asp.org">Apache ASP homepage</ulink>.
</para>
<para>To enable it to work with the AxKit Filter Provider, just add the following in httpd.conf:</para>
<programlisting>
PerlModule Apache::Filter
PerlModule Apache::ASP
PerlModule AxKit
<Location /filter>
SetHandler perl-script
PerlSetVar Filter On
AxProvider Apache::AxKit::Provider::Filter
PerlHandler Apache::ASP AxKit
</Location>
</programlisting>
<para>Then create a sample ASP page to generate the xml. Here is a minimalistic example:</para>
<programlisting>
<?xml version="1.0"?>
<?xml-stylesheet href="plain.xsl"
type="text/xsl"?>
<html>
<head>
<title>AxKit / Apache::ASP filter test</title>
</head>
<body bgcolor="#000000" text="#CCCCCC">
<h3>Environment Variables:</h3>
<table border="1" width="100%" cellspacing="0" cellpadding="0">
<%
my $env = $Request->ServerVariables;
$Response->Write ( map { "<tr><th>$_</th><td>$env->{$_}</td></tr>" } keys %$env );
%>
</table>
</body>
</html>
</programlisting>
</sect2>
<!--
<sect2>
<title>... with HTML::Mason</title>
<para>Mason is a tool for building, serving and managing large websites. For more information about Mason, take a look at <ulink url="http://www.masonhq.com">Mason HeadQuarters</ulink>.</para>
</sect2>
-->
</sect1>
<sect1>
<title>Provider Internals</title>
<para>Some advanced tips about AxKit. Everything you'll find in this chapter is to be considered just an overview on the subject and is included here just to let the reader better understand some of the main topics covered in this paper.</para>
<!--
<sect2><title>Use of Apache notes ($r->p?notes)</title>
<para>headers_sent, passthru and so on...</para>
</sect2>
-->
<sect2>
<title>AxKit::Apache object</title>
<para>AxKit redefines the request object through the <emphasis>AxKit::Apache</emphasis> package.</para>
<para>This (re)defines the following methods:
<itemizedlist>
<listitem>content-type</listitem>
<listitem>print</listitem>
<listitem>no_cache</listitem>
<listitem>send_http_header</listitem>
</itemizedlist>
</para>
<para>The changes in the methods have mostly to do with cache handling and shouldn't be of much interest to you. Just note that with the <emphasis>no_cache</emphasis> method you can disable AxKit's own cache too.</para>
</sect2>
<sect2>
<title>External Entity Handler</title>
<para>As we stated before, it is possible to define a custom External Entity Handler in the Provider module. This
happens through the <emphasis>get_ext_ent_handler()</emphasis> routine. The default behaviour is to fetch remote <emphasis>http:</emphasis> entities with HTTP::GHTTP and local (unknown or no scheme) ones with the current AxKit Provider.</para>
</sect2>
<sect2>
<title>Apache::AxKit::Exception</title>
<para>AxKit uses a subclass of <emphasis>Error</emphasis> to handle Exceptions. This implements the try / catch / otherwise / finally primitives. You can use them to handle (or recover from) errors in a clean way. </para>
<para>The Apache::AxKit::Exception package defines the following types of exception:
<itemizedlist>
<listitem>Apache::AxKit::Exception</listitem>
<listitem>Apache::AxKit::Exception::Declined</listitem>
<listitem>Apache::AxKit::Exception::Error</listitem>
<listitem>Apache::AxKit::Exception::OK</listitem>
<listitem>Apache::AxKit::Exception::Retval</listitem>
<listitem>Apache::AxKit::Exception::IO</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
<sect1>
<title>Writing a simple provider: DBI</title>
<para>Here's a complete example of an <application>AxKit Provider</application> from scratch.</para>
<para>This is called <emphasis>Apache::AxKit::Provider::DBI</emphasis> and is designed to get its data from a DataBase.
In my example, I used as data source a MySQL installed on localhost, with the 'test' db and a 'guest' user with password 'guest'. Here is the table definition and default values:
</para>
<programlisting>
# Table definition
CREATE TABLE blocks (
id varchar(255) DEFAULT '' NOT NULL,
block TEXT,
PRIMARY KEY (id)
);
# Sample code
INSERT INTO blocks VALUES ('/index.xml',
'<?xml version="1.0"?>
<page>
<title>Hello world</title>
</page>');
INSERT INTO blocks VALUES ('/index.xsl',
'<?xml version="1.0" ?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="title">
title = <xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>');
</programlisting>
What follows is the code for the provider:
<programlisting>
#TODO:
# - insert some decent comments
# - write down a sample external entity handler
# - explain better the get_mtime() thing.
package Apache::AxKit::Provider::DBI;
use strict;
use vars qw/@ISA/;
@ISA = ('Apache::AxKit::Provider');
use Apache;
use Apache::Log;
use Apache::AxKit::Exception;
use Apache::AxKit::Provider;
use Apache::AxKit::Provider::File;
use Apache::MimeXML;
use Apache::Constants;
use DBI;
# sub: Init
# Here we do some initialization stuff.
sub init {
my $self = shift;
my (%p) = @_;
if ($p{uri}) {
# called from :
# process_request ($styleprovider = Apache::AxKit::Provider->new)
# check_resource_mtimes ($ent_provider = ... )
# [...]
$self->{id} = $p{uri};
}
elsif ($p{file}) {
$self->{id} = $p{file};
}
else {
$self->{id} = $self->{apache}->filename();
}
}
# sub: get_fh
# we don't want to handle files, so we just throw an exception here.
sub get_fh {
throw Apache::AxKit::Exception::IO(
-text => "Can't get fh for DBI filehandle"
);
}
# sub: get_strref
# since we refused to work with file handles, we HAVE to define this.
sub get_strref {
my $self = shift;
# Connect to the DB and query it.
my $dbh = DBI->connect("dbi:mysql:test",'guest', 'guest');
my $sth = $dbh->prepare("SELECT block FROM blocks WHERE id='".$self->{id}."'");
$sth->execute;
# Now get the data and disconnect from the DB
my $res = $sth->fetchrow();
$dbh->disconnect;
return \$res;
}
# sub: mtime
# This should return the modification time of the resource, for simplicity here we decrement it everytime we are called
# so that resources are never considere cacheable.
use vars qw/$mtime/;
$mtime = 0;
sub mtime {
my $self=shift;
return --$time; #borrowed from Scalar Provider
}
# sub: process
sub process {
my $self = shift;
# For simplicity, let's assume our DB entry always exists
return 1;
}
# sub: key
# should return a unique identifier for the resource.
# Let's assume the id from the uri is a good one.
sub key {
my $self = shift;
return $self->{id};
}
# sub: exists
# should return 1 only if the resource actually exists. Let's cheat for now.
sub exists {
my $self = shift;
return 1;
}
1;
</programlisting>
to enable it, just modify your httpd.conf as follows:
<programlisting>
<Location />
PerlHandler AxKit
AxProvider Apache::AxKit::Provider::DBI
</Location >
</programlisting>
</sect1>
</article>
1.1 xml-axkit/axkit.org/docs/quick_start.dkb
Index: quick_start.dkb
===================================================================
<?xml version="1.0"?>
<?xml-stylesheet href="/stylesheets/docbook_screen.xps"
type="application/x-xpathscript"
media="screen"
title="default"
?>
<?xml-stylesheet href="/stylesheets/docbook_print.xps"
type="application/x-xpathscript"
title="print"
alternate="yes"
?>
<!DOCTYPE article [
<!ENTITY prompt "% ">
]>
<article>
<artheader>
<title>AxKit Quickstart Guide</title>
<author>
<firstname>Kip</firstname>
<surname>Hampton</surname>
<affiliation>
<address><email>khampton@totalcinema.com</email></address>
</affiliation>
</author>
<abstract>
<para>
This document is a <emphasis>quick start</emphasis> guide designed to help developers
deliver transformed XML with AxKit as quickly as possible. As such, many of the
finer points of AxKit's extremely robust interface will not be covered. For a more
descriptive introduction, please see Matt's <ulink url=
"http://axkit.org/docs/introduction.dkb">Introduction to AxKit</ulink>.
</para>
<para>
</para>
</abstract>
</artheader>
<sect1>
<title>Installing and Configuring AxKit</title>
<para>
This guide presumes that you already have an
<ulink url ="http://www.apache.org/httpd.html">Apache</ulink> httpd server running
<ulink url="http://perl.apache.org/">mod_perl</ulink>. If this is not
the case, please visit the <ulink url
="http://www.apache.org/httpd.html">Apache
</ulink> and <ulink url="http://perl.apache.org/">mod_perl</ulink> pages for
more information.
</para>
<sect2>
<title>Step 1 - Download and Install AxKit</title>
<para>
First, visit the AxKit <ulink url="http://www.axkit.org/download/">
download directory</ulink> and grab the latest tarball. Once you've
downloaded the source, type the following:
<informalexample>
<programlisting>
&prompt; <userinput>tar -zxvf AxKit-x.xx.tar.gz</userinput>
&prompt; <userinput>cd AxKit-x.xx/</userinput>
&prompt; <userinput>perl Makefile.PL</userinput>
&prompt; <userinput>make</userinput>
&prompt; <userinput>make test</userinput>
&prompt; <userinput>make install</userinput>
</programlisting>
</informalexample>
If <literal>perl Makefile.PL</literal> warns about missing modules, make a note of these
dependencies and install the corresponding Perl packages before proceeding with the AxKit
installation. AxKit will not be properly installed, otherwise.
</para>
</sect2>
<sect2>
<title>Step 2 - Edit Your httpd.conf File</title>
<para>
Add the following lines to your httpd.conf:
<informalexample>
<programlisting>
PerlModule AxKit
SetHandler perl-script
PerlHandler AxKit
AxAddStyleMap application/x-xpathscript Apache::AxKit::Language::XPathScript
# add the following only if you intend to install Sablotron
AxAddStyleMap text/xsl Apache::AxKit::Language::Sablot
</programlisting>
</informalexample>
</para>
<para>
Don't worry that this looks like AxKit will deliver all of your files,
if it doesn't detect XML at the URL you give it, it will let the httpd
deliver it as normal. If you're still concerned, put <emphasis>all but the first</emphasis>
configuration directive in a <ulink
url="http://www.apache.org/docs/mod/core.html#location"
><literal><Location></literal></ulink> section. Note that the first
line: <literal>PerlModule AxKit</literal> <emphasis>MUST</emphasis>
occur in your httpd.conf so that it is parsed in the parent httpd
process. This is because it adds configuration directives to the entire
Apache httpd. If you do not do this you will get SEGFAULTs.
</para>
<para>
Now, stop and restart your Apache server and assuming you've had no trouble
with the steps above, you are now ready to begin publishing transformed XML
with AxKit!
</para>
</sect2>
</sect1>
<sect1>
<title>Installing the Sablotron XSLT Processor (Optional)</title>
<para>
This section assumes that you will be installing the Sablotron XSLT processor along with
AxKit. If you want to see AxKit in action but do not want to install Sablotron you may safely
skip to the <ulink url="quick_start.dkb?section=3">next section</ulink>.
</para>
<sect2>
<title>Step 1 - Download and Install the Sablotron XSLT
Processor</title>
<para>
Next, fetch either the source or appropriate binary distribution of the
Sablotron XSLT Processor from <ulink url="http://www.gingerall.com/">
www.gingerall.com</ulink>. As of version 0.50, Sablotron requires the shared library version
of the Expat XML parser to be installed before installing Sablotron. Visit <ulink url=
"http://sourceforge.net/projects/expat/">the Expat project page</ulink> for sources and
installation instructions.
</para>
<para>
Installing Sablotron from the source:
<informalexample>
<programlisting>
&prompt; <userinput>tar -zxvf Sablot-x.xx.tar.gz</userinput>
&prompt; <userinput>cd Sablot-x.xx/</userinput>
&prompt; <userinput>./configure</userinput>
&prompt; <userinput>make</userinput>
&prompt; <userinput>make install</userinput>
</programlisting>
</informalexample>
Please note that Sablotron build process described here covers versions 0.50. Consult the
INSTALL and README files that shipped with the version you downloaded for details
and possible changes.
</para>
</sect2>
<sect2>
<title>Step 2 - Install the XML::Sablotron Perl Library</title>
<para>
Next, install XML::Sablotron. You can either install the package by hand or use the CPAN
shell. To install using the CPAN shell type:
<informalexample>
<programlisting>
&prompt; <userinput>perl -MCPAN -e shell</userinput>
&prompt; <userinput>install XML::Sablotron</userinput>
</programlisting>
</informalexample>
To install the packages yourself, get the latest version of the XML::Sablotron Perl package
from <ulink url="http://www.gingerall.com/">www.gingerall.com</ulink>, cd to the
directory you downloaded the tarball to and type:
<informalexample>
<programlisting>
&prompt; <userinput>tar -zxvf XML-Sablot-x.xx.tar.gz</userinput>
&prompt; <userinput>mv XML-Sablot-x.xx/ Sablot-x.xx/</userinput>
&prompt; <userinput>cd Sablot-x.xx/XML-Sablot-x.xx/</userinput>
&prompt; <userinput>perl Makefile.PL</userinput>
&prompt; <userinput>make</userinput>
&prompt; <userinput>make test</userinput>
&prompt; <userinput>make install</userinput>
</programlisting>
</informalexample>
</para>
</sect2>
</sect1>
<sect1>
<title>Sample Document Transformations</title>
<para>
Now, we're going to see how AxKit works by transforming an XML file containing data about
Camelids (note the dubious Perl reference) into HTML.
</para>
<sect2>
<title>Step 1 - A Sample XML Document</title>
<para>
First, you will need a sample XML file. Open the text editor of your choice
and type the following:
<informalexample>
<programlisting><![CDATA[
<?xml version="1.0"?>
<dromedaries>
<species name="Camel">
<humps>1 or 2</humps>
<disposition>Cranky</disposition>
</species>
<species name="Llama">
<humps>1 (sort of)</humps>
<disposition>Aloof</disposition>
</species>
<species name="Alpaca">
<humps>(see Llama)</humps>
<disposition>Friendly</disposition>
</species>
</dromedaries>
]]></programlisting>
</informalexample>
Save this file as test.xml.
</para>
</sect2>
<sect2>
<title>Step 2 - Create a Stylesheet</title>
<para>
Now, create the stylesheet to transform your XML document. If you have chosen to install
Sablotron or one of the other XSLT processors that AxKit supports you may use either the XSLT
or XPathScript samples below. If you have not installed an XSLT processor skip directly to
the XPathScript example.
</para>
</sect2>
<sect2>
<title>Using XSLT</title>
<para>
Start a new file and type the following:
<informalexample>
<programlisting><![CDATA[
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<html>
<head><title>Know Your Dromedaries</title></head>
<body>
<table bgcolor="eeeeee" border="1">
<tr>
<th>Species</th>
<th>No of Humps</th>
<th>Disposition</th>
</tr>
<xsl:for-each select="dromedaries">
<xsl:apply-templates select="./species" />
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="species">
<tr>
<td><xsl:value-of select="@name" /></td>
<td><xsl:value-of select="./humps" /></td>
<td><xsl:value-of select="./disposition" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
]]></programlisting>
</informalexample>
Save this file as test.xsl.
</para>
</sect2>
<sect2>
<title>Using XPathScript</title>
<para>
Create a new file and type:
<informalexample>
<programlisting><![CDATA[
<%
$t->{'humps'}{'pre'} = "<td>";
$t->{'humps'}{'post'} = "</td>";
$t->{'disposition'}{'pre'} = "<td>";
$t->{'disposition'}{'post'} = "</td>";
$t->{'species'}{testcode} = sub {
my $node = shift;
my $t = shift;
$t->{pre} = '<tr><td>' . findvalue('@name', $node) . '</td>';
$t->{post} = "</tr>";
return 1;
}
%>
<html>
<head>
<title>Know Your Dromedaries</title>
</head>
<body bgcolor="white">
<table bgcolor="eeeeee" border="1">
<tr><th>Species</th><th>No. of Humps</th><th>Disposition</th></tr>
<%= apply_templates('/dromedaries/species') %>
</table>
</body>
</html>
]]></programlisting>
</informalexample>
Save this file as test.xps.
</para>
</sect2>
<sect2>
<title>Step 3 - Associate the XML Document with your Stylesheet</title>
<para>
Next, re-open the test.xml file and add the following just after the <?xml version="1.0"?>
declaration. If you have selected the XSLT example, add:
<informalexample>
<programlisting><![CDATA[
<?xml-stylesheet href="test.xsl" type="text/xsl"?>
]]></programlisting>
</informalexample>
Or, if you have chosen the XPathScript sample, use:
<informalexample>
<programlisting><![CDATA[
<?xml-stylesheet href="test.xps" type="application/x-xpathscript"?>
]]></programlisting>
</informalexample>
Note that this line is telling AxKit which stylesheet to use and
which handler to use for that stylesheet. Now, save the test.xml
file and quit your editor.
</para>
</sect2>
<sect2>
<title>Step 4 - Finishing Up</title>
<para>
You are now ready to deliver your little zoological XML data file as
formatted HTML! Just copy or move the XML and stylesheet files into the
same directory under your httpd DocumentRoot. Then, point your browser to
http://your-server.com/path/to/test.xml and you should see your data nicely
formatted in an HTML table.
</para>
<para>
Congratulations, you are now well on the road to adding XML, XSLT, XPathScript and AxKit
to your developmental toolbelt. For more information about AxKit's advanced
features, please visit the <ulink url="http://www.axkit.org/">AxKit homepage</ulink>.
</para>
</sect2>
<sect2>
<title>If Things Go Wrong</title>
<para>
As I stated in the introduction, this is document is designed to walk someone through the
AxKit instalation process, on to serving transformed documents as quickly and simply as
possible. Depending upon your setup, installing AxKit may require some special attention to
get it to sanely co-exist with some of the other tools that you may be using. If you've had
any trouble while following the steps outlined here, please consult the <ulink url=
"http://axkit.org/faq.xml">AxKit FAQ</ulink>, and the AxKit User's <ulink url=
"http://axkit.org/cgi-bin/ezmlm-cgi/4">mailing list archives</ulink>.
</para>
</sect2>
</sect1>
<sect1>
<title>Other Resources</title>
<sect2>
<title>mod_perl</title>
<para>
<itemizedlist>
<listitem><ulink url="http://perl.apache.org">mod_perl Homepage</ulink>
</listitem>
<listitem><ulink url="http://perl.apache.org/guide/">mod_perl Guide
</ulink>
</listitem>
<listitem><ulink url="http://www.modperl.com/">Writing Apache Modules
with Perl and C</ulink>
</listitem>
<listitem><ulink url="http://take23.org/">mod_perl News and Information</ulink>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>XSLT</title>
<para>
<itemizedlist>
<listitem><ulink url="http://www.w3.org/Style/XSL/">W3C Specification
</ulink></listitem>
<listitem><ulink url="http://www.xml.com/pub/Guide/XSLT">XSLT Resources at XML.com
</ulink></listitem>
<listitem><ulink url="http://www.mulberrytech.com/quickref/index.html">Mulberry
Technologies' XPath and XSLT quick reference card (PDF).</ulink></listitem>
<listitem><ulink url="http://www.zvon.org/xxl/XSLTreference/Output/index.html">An
expanded XSLT reference from zvon.org.</ulink></listitem>
<listitem><ulink url="http://www.xslt.com/">XSLT.com</ulink></listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>XPathScript</title>
<para>
<itemizedlist>
<listitem><ulink url="http://axkit.org/docs/xpathscript/guide.dkb">XPathScript - A Viable
Alternative to XSLT?</ulink></listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
</article>
1.1 xml-axkit/axkit.org/docs/presentations/tpc2001/ax_logo.png
<<Binary file>>
1.1 xml-axkit/axkit.org/docs/presentations/tpc2001/redbg.png
<<Binary file>>
1.1 xml-axkit/axkit.org/docs/presentations/tpc2001/tpc-axkit.axp
Index: tpc-axkit.axp
===================================================================
<?xml version="1.0"?>
<slideshow>
<title>AxKit</title>
<metadata>
<speaker>Matt Sergeant</speaker>
<email>matt@axkit.com</email>
<organisation>AxKit.com Ltd</organisation>
<link>http://axkit.com/</link>
<logo scale="0.4">ax_logo.png</logo>
<background>redbg.png</background>
</metadata>
<slide>
<title>Introduction</title>
<point level="1">Perl's XML Capabilities</point>
<point level="1">AxKit intro</point>
<point level="1">AxKit static sites</point>
<point level="1">AxKit dynamic sites (XSP)</point>
<point level="1">Advanced AxKit</point>
</slide>
<slideset>
<title>XML with Perl Introduction</title>
<slide>
<title>Basis : XML::Parser</title>
<point level="1">XS wrapper around expat</point>
<point level="1">expat - James Clark</point>
<point level="1">XML::Parser - originally by Larry Wall</point>
<point level="2">Now maintained by Clark Cooper</point>
<point level="1">Allows different "styles" of parsing</point>
<point level="1">Default style is callback/event based stream parsing</point>
<point level="1">Also implements a "push" parser</point>
</slide>
<slide>
<title>XML::Parser usage</title>
<point level="1">SAX-like API</point>
<point level="1">register callback handler methods</point>
<point level="2">start tag</point>
<point level="2">end tag</point>
<point level="2">characters</point>
<point level="2">comments</point>
<point level="2">processing instructions</point>
<point level="2">... and more</point>
<point level="1">Non validating XML parser</point>
<point level="1">dies (throws an exception) on bad XML</point>
</slide>
<slide>
<title>XML::Parser code</title>
<source_code><![CDATA[
my $p = XML::Parser->new(
Handlers => {
Start => \&start_tag,
End => \&end_tag,
# add more handlers here
});
$p->parsefile("foo.xml");
exit(0);
sub start_tag {
my ($expat, $tag, %attribs) = @_;
print "Start tag: $tag\n";
}
sub end_tag {
my ($expat, $tag) = @_;
print "End tag: $tag\n";
}
]]></source_code>
</slide>
<slide>
<title>XML::Parser - maintaining state</title>
<source_code><![CDATA[
my $ctxt = MyParser->new();
my $p = XML::Parser->new(
Handlers => {
Start => sub { $ctxt->parse_start(@_) },
End => sub { $ctxt->parse_end(@_) }
});
$p->parse("<some><xml>here</xml></some>");
package MyParser;
sub new {
return bless {}, shift;
}
sub parse_start {
my ($self, $expat, $tag, %attribs) = @_;
$self->{some_state} = "Open $tag";
}
sub parse_end {
my ($self, $expat, $tag) = @_;
print "Last state: $self->{some_state}\n";
}
]]></source_code>
</slide>
<slide>
<title>PYX</title>
<point level="1">Turns XML into a stream of text events</point>
<point level="1">Based on SGML ESIS streams</point>
<point level="2">)start</point>
<point level="2">(end</point>
<point level="2">Aattribute=value</point>
<point level="2">-text</point>
<point level="2">?processing instruction</point>
<point level="1">Comments are dropped</point>
</slide>
<slide>
<title>PYX example</title>
<source_code>
$ pyx presentation.xml | more
(slideshow
-\n
-
(title
-Developing XML Applications with Perl and AxKit
)title
-\n
-
(metadata
-\n
-
(speaker
-Matt Sergeant
)speaker
-\n
-
(email
-matt@axkit.com
)email
...
</source_code>
</slide>
<slide>
<title>PYX usage</title>
<point level="1">pyx xmlfile | something | pyxw</point>
<point level="1">pyx generates PYX streams</point>
<point level="1">pyxw generates XML from PYX</point>
<point level="1">pyxhtml parses HTML to PYX</point>
</slide>
<slide>
<title>HTML Tidy, or html2xml</title>
<point level="1">Java utility by Dave Ragget of the W3C</point>
<point level="1">Tidy converts HTML to XHTML</point>
<point level="1">PYX can do the same:</point>
<point level="2">pyxhtml file.html | pyxw > file.xhtml</point>
<point level="1">pyxhtml uses HTML::TreeBuilder</point>
</slide>
<slide>
<title>XPath intro</title>
<point level="1">W3C Standard for locating nodes within an XML document</point>
<point level="1">A subset of XPath is used for "matching" nodes in XSLT</point>
<point level="1">Looks like directory paths: /path/to/node</point>
<point level="1">But that's an abbreviated syntax...</point>
<point level="2">/child::path/child::to/child::node</point>
<point level="1">Full grammar containing expressions, calculations, functions, etc</point>
</slide>
<slide>
<title>XPath Examples</title>
<point level="1">Find title of XHTML document</point>
<source_code>/html/head/title</source_code>
<point level="1">Find all hrefs in <a> tags</point>
<source_code>/html/body/descendant::a[@href]/@href</source_code>
</slide>
<slide>
<title>XML::XPath</title>
<point level="1">Full implementation of W3C XPath on a DOM-like API</point>
<point level="1">Easy to use:</point>
<source_code><![CDATA[
my $xp = XML::XPath->new(filename => "foo.xml");
print $xp->findvalue("/html/head/title");
]]></source_code>
<point level="1">Can also process HTML:</point>
<source_code><![CDATA[
my $xp = XML::XPath->new(
filename => "pyxhtml foo.html | pyxw |");
]]></source_code>
</slide>
<slide>
<title>XML::XPath continued</title>
<point level="1">Every node has methods findvalue(), findnodes() and find()</point>
<source_code><![CDATA[
my $xp = XML::XPath->new(filename => "foo.xml");
foreach my $link ($xp->findnodes('/html/body/a[@href]')) {
print "Link: ", $link->findvalue('@href'), "\n";
# or :
# print "Link: ", $link->getAttribute('href'), "\n";
}
]]></source_code>
<point level="1">Not 100% DOM compatible, but close</point>
</slide>
<slide>
<title>XML::XPath Implementation</title>
<point level="1">XML::Parser and SAX parsers build an in-memory tree</point>
<point level="1">Hand-built parser for XPath syntax (rather than YACC based parser)</point>
<point level="1">Garbage Collection yet still has circular references (and works on Perl 5.005)</point>
<image>pointers.png</image>
</slide>
<slide>
<title>XML to XML Conversions</title>
<point level="1">Use XSLT (or XPathScript, see later)</point>
<point level="1">Either use the command line (e.g. xsltproc from libxslt)</point>
<point level="1">... or call from Perl:</point>
<source_code><![CDATA[
use XML::LibXSLT;
use XML::LibXML;
my $parser = XML::LibXML->new();
my $xslt = XML::LibXSLT->new();
my $source_dom = $parser->parsefile('foo.xml');
my $style_dom = $parser->parsefile('bar.xsl');
my $stylesheet = $xslt->parse_stylesheet($style_dom);
my $results = $stylesheet->transform($source_dom);
print $stylesheet->output_string($results);
]]></source_code>
</slide>
<slide>
<title>SAX Filters</title>
<point level="1">SAX events passed between SAX handlers</point>
<point level="1">e.g. tags to lower case:</point>
<source_code><![CDATA[
my $ya = XML::Handler::YAWriter->new( AsFile => '-' );
my $lc = XML::Filter::ToLower->new( Handler => $ya );
my $parser = XML::Parser::PerlSAX->new( Handler => $lc );
package XML::Filter::ToLower;
@ISA = ('XML::Filter::Base');
sub start_element {
my ($self, $element) = @_;
$element->{Name} = lc($element->{Name});
$self->{Handler}->start_element($element);
}
sub end_element {
my ($self, $element) = @_;
$element->{Name} = lc($element->{Name});
$self->{Handler}->end_element($element);
}
]]></source_code>
</slide>
<slide>
<title>SAX Filters (cont)</title>
<point>Almost any level of complexity possible</point>
<point>Chain as many filters together as you like</point>
<point>Fast way of processing XML</point>
<point level="2">We don't have to build a tree in memory</point>
<point>Possible to output to HTML using XML::Handler::HTMLWriter</point>
</slide>
</slideset>
<slideset>
<title>AxKit Introduction</title>
<slide>
<title>AxKit Introduction</title>
<point level="1">XML Publishing Framework</point>
<point level="1">XML Application Server</point>
<point level="1">Designed for Content Diversification</point>
<point level="2">Same content, delivered differently</point>
<point level="2">HTML, WAP, PDF, SOAP, etc</point>
<point level="1">Built in C, Perl, and mod_perl/Apache</point>
<point level="1">Plugin Architecture, allows operation to be changed easily</point>
</slide>
<slide>
<title>How does it work?</title>
<point level="1">Apache handle</point>
<point level="1">Embedded perl interpreter via mod_perl</point>
<point level="1">Apache Config directives</point>
<point level="1">Transformation Pipeline</point>
<point level="1">Language Modules implement stylesheets and other engines</point>
<point level="1">XSP for Dynamic Content</point>
<point level="1">Caching</point>
<point level="1">Apache::Filter Support</point>
</slide>
<slide>
<title>mod_perl</title>
<point level="1">mod_perl used to implement most of AxKit</point>
<point level="1">... though more code being ported to C all the time</point>
<point level="1">Gives us bytecode compiled Perl</point>
<point level="1">Makes development faster</point>
</slide>
<slide>
<title>Configuration Directives</title>
<point level="1">Config directives written in the C code</point>
<point level="1">Implemented on top of Apache</point>
<point level="1">Fits in with httpd.conf and .htaccess files</point>
<point level="1">28 Config directives in total</point>
<point level="1">Setup the pipeline, debugging options, and various other flags</point>
</slide>
<slide>
<title>Config Directives examples</title>
<source_code><![CDATA[
AxDebugLevel 5
AxCacheDir /tmp/axkit_cache
<Location /apps/calendar>
AxAddProcessor application/x-xsp .
<AxMediaType handheld>
AxAddProcessor text/xsl /styles/cal2wml.xsl
</AxMediaType>
<AxMediaType screen>
AxAddProcessor text/xsl /styles/cal2html.xsl
</AxMediaType>
</Location>
]]></source_code>
</slide>
<slide>
<title>Transformation Pipeline</title>
<point level="1">AxKit is a pipeline engine</point>
<point level="1">Output of one stage goes to input of the next</point>
<point level="1">Allows us to build up our application in stages</point>
<point level="1">Pipeline passed as one of:</point>
<point level="2">String</point>
<point level="2">DOM Tree</point>
<point level="2">SAX events</point>
</slide>
<slide>
<title>Language Modules</title>
<point level="1">Every stage in the pipeline implemented by a language module</point>
<point level="1">Language modules can either use a stylesheet for transformation or not</point>
<point level="2">Examples of modules not using a stylesheet are "XSL:FO to PDF", XSP, and the module which produces this slideshow</point>
<point level="1">Different implementations of same language possible, e.g. XSLT</point>
<point level="2">XML::LibXSLT</point>
<point level="2">XML::Sablotron</point>
<point level="2">XML::XSLT</point>
<point level="2">XML::Xalan</point>
<point level="2">XML::Transformiix</point>
</slide>
<slide>
<title>Languages modules available</title>
<point level="1">XSLT</point>
<point level="1">XPathScript</point>
<point level="1">XSP</point>
<point level="1">AxPoint (this slideshow)</point>
<point level="1">XMLNews::HTMLTemplate</point>
<point level="1">Template Toolkit w/XPath plugin</point>
</slide>
<slide>
<title>Caching</title>
<point level="1">XML Transformation can be slow, caching vital to making a fast XML Publishing Engine</point>
<point level="1">Cache uses the filesystem, not memory or DBM files</point>
<point level="1">Cache results of transformation pipeline, where appropriate</point>
<point level="1">Cache results of dynamic processing by implementing has_changed() method</point>
<point level="1">Using filesystem allows Apache to deliver the cache directly:</point>
<source_code>Apache->request->filename = <cache-file>;
return DECLINED;
</source_code>
</slide>
<slide>
<title>Cache (cont.)</title>
<point level="1">Fastest possible implementation of a cache</point>
<point level="1">Default storage place is .xmlstyle_cache directory in same directory as XML file</point>
<point level="1">Overridable using AxCacheDir directive</point>
<point level="1">Files named as MD5 hash of XML filename, media type, style name and optionally run-time parameters that may affect the cache</point>
<point level="1">Does use large amounts of space, so can be turned off</point>
</slide>
<slide>
<title>Non Cache-able content</title>
<point level="1">POST requests usually not cacheable</point>
<point level="1">XSP pages usually not cacheable</point>
<point level="1">Can turn off the cache in Perl code:</point>
<source_code>$r->no_cache(1);</source_code>
<point level="1">Cache can be modified using plugins, so cache can be based on phase of the moon</point>
</slide>
<slide>
<title>Apache::Filter Support</title>
<point level="1">Alternate plugin "Provider" module</point>
<point level="1">AxKit can get XML from previous mod_perl handler</point>
<point level="1">Works with Apache::ASP, HTML::Mason, etc</point>
</slide>
<slide>
<title>Apache::Filter example</title>
<point level="1">ASP Script</point>
<source_code><![CDATA[
<?xml version="1.0"?>
<?xml-stylesheet href="/foo.xsl" type="text/xsl"?>
<page>
<head><title>Hello World</title></head>
<body>
<section>
<title>Test Page</title>
<para>
Hello World
</para>
<para>
The time is now: <%= localtime %>
</para>
</body>
</page>
]]></source_code>
</slide>
<slide>
<title>Apache::Filter example (stylesheet)</title>
<source_code><![CDATA[
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head><title><xsl:value-of select="/page/head/title"/> </title></head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="section">
<h1><xsl:value-of select="title"/></h1>
<xsl:apply-templates select="para"/>
</xsl:template>
<xsl:template match="para">
<p><xsl:apply-templates/></p>
</xsl:template>
</xsl:stylesheet>
]]></source_code>
</slide>
<slide>
<title>Apache::Filter example (results)</title>
<source_code><![CDATA[
<html>
<head><title>Hello World</title></head>
<body>
<h1>Test Page</h1>
<p>
Hello World
</p>
<p>
The time is now: Tue May 8 07:41:33 2001
</p>
</body>
</html>
]]></source_code>
</slide>
<slide>
<title>Gzipped Output</title>
<point level="1">Many HTTP/1.1 browsers will accept gzip encoded results (modulo bugs)</point>
<point level="1">AxKit can check Accept-Encoding header for this capability</point>
<point level="1">Will then gzip content before delivery</point>
<point level="1">Cache stores regular and gzipped copy</point>
<point level="1">Reduces used bandwidth by up to 85%</point>
</slide>
<slide>
<title>Developer's Perspective</title>
<point level="1">Two ways to tell AxKit how to process XML</point>
<point level="2">Processing Instructions in the XML</point>
<source_code><![CDATA[
<?xml-stylesheet href="/styles/foo.xsl" type="text/xsl"?>
]]></source_code>
<point level="2">Configuration Directives in httpd.conf</point>
<source_code><![CDATA[
AxAddProcessor text/xsl /styles/foo.xsl
]]></source_code>
<point level="1">PIs act as override mechanism</point>
<point level="1">Also PIs are sometimes appropriate, e.g. for XSP files</point>
</slide>
<slide>
<title>Processing Instructions</title>
<point level="1">Follows the W3C xml-stylesheet REC</point>
<point level="1">Works almost identically to HTML's LINK element</point>
<point level="1">Processing instruction is just <?target data?>, so attributes are called "psuedo attributes"</point>
<point level="1">Allowed attributes:</point>
<point level="2">href - the location of the stylesheet</point>
<point level="2">type - the mime type of the stylesheet</point>
<point level="2">alternate - yes or no, specifies alternate styling</point>
<point level="2">media - media type this stylesheet applies to</point>
<point level="2">title - group this stylesheet belongs to</point>
<point level="1">Only href and type are required</point>
</slide>
<slide>
<title>Config Directives</title>
<point level="1">More complex and more powerful than PIs</point>
<source_code><![CDATA[
AxAddProcessor text/xsl /styles/foo.xsl
AxAddRootProcessor text/xsl /styles/article.xsl article
AxAddDocTypeProcessor text/xsl /styles/docbook.xsl \
"-//OASIS//DTD DocBook XML V4.1.2//EN"
AxAddDTDProcessor text/xsl /styles/bar.xsl /dtds/bar.dtd
AxAddURIProcessor text/xsl /styles/book.xsl \
"book.*\.xml$"
]]></source_code>
<point level="1">Fully documented in perldoc AxKit</point>
</slide>
<slide>
<title>AxKit vs Cocoon</title>
<point level="1">Cocoon is a Servlet implementing some of the same functionality</point>
<point level="1">AxKit is tied to Apache</point>
<point level="1">Cocoon 2 [alpha] adds features like AxKit config directives</point>
<point level="1">Both implement XSP, and collaborate on the spec</point>
<point level="1">Both open source</point>
<point level="1">Perl and C vs Java</point>
<point level="1">Use what fits your environment</point>
</slide>
</slideset>
<slideset>
<title>AxKit for Static Content</title>
<slide>
<title>XML Publishing</title>
<point level="1">Using XML for the source of all our web site's content</point>
<point level="1">Semantically rich original data</point>
<point level="1">Delivery to different media types</point>
<point level="2">Example - slides</point>
<point level="3">Deliver to HTML</point>
<point level="3">Or PDF</point>
<point level="3">Or plain text</point>
<point level="2">AxKit can detect via plugins, which version the user wants</point>
</slide>
<slide>
<title>XML Based Content</title>
<point level="1">DocBook</point>
<point level="1">XHTML</point>
<point level="1">TEI XML</point>
<point level="1">RSS</point>
<point level="1">MyML - invent your own!</point>
<point level="1">Or use Provider interface...</point>
<point level="2">OpenOffice</point>
<point level="2">MS Word (via libwv)</point>
<point level="2">Content Management System</point>
<point level="1">Or use other mod_perl modules (Mason, ASP, etc) to generate XML</point>
</slide>
<slide>
<title>The Value of Semantics</title>
<point level="1">HTML has low semantic value</point>
<point level="1">Example point - farming the web</point>
<point level="1">XML provides the ability to add value to our content</point>
<point level="1">But we still need to deliver it as HTML</point>
<point level="1">AxKit can give you the best of both worlds</point>
</slide>
<slide>
<title>Alternative Media</title>
<point level="1">The web is no longer just web browsers on PCs</point>
<point level="1">TV is becoming huge, Phone/Handheld is already huge</point>
<point level="1">But the cost of entry into different media types is still high</point>
<point level="1">Using XML for our content reduces that cost of entry</point>
<point level="2">And it will reduce your long term costs</point>
</slide>
<slide>
<title>Media vs Styles</title>
<point level="1">Same concept, different reasons</point>
<point level="1">Use different styles for the same media device</point>
<point level="1">e.g. No graphics HTML version, printable version,
PDF version. All delivered to browsers on PCs</point>
<point level="1">Some applications in personalisation</point>
</slide>
<slide>
<title>The Key - Stylesheets</title>
<point level="1">Stylesheets translate XML on the fly to different formats</point>
<point level="1">Different stylesheets used for different media types</point>
<point level="1">... and for different styles of the same media type</point>
<point level="1">Generally, content is cached for increased performance</point>
</slide>
<slide>
<title>What is the benefit to you?</title>
<point level="1">Web Developers benefit because of separation of concerns</point>
<point level="1">Content</point>
<point level="1">Presentation</point>
<point level="1">Logic</point>
<point level="1">Site Management</point>
</slide>
<slide>
<title>Development Scalability</title>
<point level="1">Long term site management is a mess without separation of concerns</point>
<point level="1">Templates vs print print print</point>
<point level="2">But we can do better</point>
<point level="1">Different devices throws a spanner in the works</point>
<point level="1">Ideal is for content creators to focus on content, developers to focus on logic, designers to focus on presentation, and webmasters to focus on site management</point>
<point level="1">Unfortunately this is still idealistic, but much better than print print print</point>
</slide>
<slide>
<title>Content Authoring</title>
<point level="1">XML Editors</point>
<point level="1">WYSIWYM Editors (What You See Is What You Mean)</point>
<point level="1">foo2xml can also be used (e.g. pod2xml)</point>
<point level="1">Mostly commercial offerings: Adept, XMeTaL, XMLSpy</point>
<point level="1">Open source options: OpenOffice, Emacs+psgml, jedit</point>
<point level="1">Some re-training may be required</point>
</slide>
<slide>
<title>Presentation Layer</title>
<point level="1">Currently the hardest part of converting from static HTML sites</point>
<point level="1">Some commercial tools available for writing XSLT stylesheets</point>
<point level="1">Sometimes templates generated using HTML tools (e.g. Frontpage) possible</point>
<point level="1">Often best way is to get a design, then have a stylesheet guru convert that</point>
</slide>
<slide>
<title>Business Logic</title>
<point level="1">AxKit uses taglib concept</point>
<point level="1">Logic wrapped in XML tags</point>
<source_code><order_fluglebar quantity="1"/></source_code>
<point level="1">Designers can drop these tags into their XML</point>
<point level="1">Logic tags output XML, not HTML</point>
<point level="1">Thus logic can be used for any output media/style</point>
<point level="1">See next section (Dynamic Content)</point>
</slide>
<slide>
<title>Site Management</title>
<point level="1">AxKit uses Apache config directives in httpd.conf/.htaccess</point>
<point level="1">Allows you to use your web master skills</point>
<point level="1">Also allows flexible configuration based on <Location>, <Files>, and <Directory> directives</point>
</slide>
<slide>
<title>XSLT Overview</title>
<point level="1">Reformation of transformation part of DSSSL into XML (from Scheme)</point>
<point level="1">Functional language (no side effects)</point>
<point level="1">Rules based ("apply this template when we see this element")</point>
<point level="1">Implemented in AxKit via:</point>
<point level="2">XML::LibXSLT (very fast compliant processor from Red Hat Labs</point>
<point level="2">XML::Sablotron (lightweight processor from Ginger Alliance</point>
<point level="2">Others: XML::Xalan, XML::XSLT, XML::Transformiix</point>
</slide>
<slide>
<title>XPathScript Overview</title>
<point level="1">Combination of:</point>
<point level="2">Perl</point>
<point level="2">ASP <% %> delimiters</point>
<point level="2">XML::XPath for locating nodes in the source XML</point>
<point level="2">Declarative (Rules based) processing</point>
<point level="1">Not a functional language</point>
<point level="1">Side effects allowed</point>
<point level="1">Full access to Apache API</point>
</slide>
<slide>
<title>XPathScript example (xml)</title>
<source_code><![CDATA[
<employees>
<employee>
<name>
<firstname>Roger</firstname>
<lastname>Rabbit</lastname>
</name>
<department>Humour</department>
</employee>
<employee>
<name>
<firstname>Jessica</firstname>
<lastname>Rabbit</lastname>
</name>
<department>Modelling</department>
</employee>
</employees>
]]></source_code>
</slide>
<slide>
<title>XPathScript example (stylesheet)</title>
<source_code><![CDATA[
<html>
<head><title>Employee List</title></head>
<body>
<h1>Employees at Acme Corp.</h1>
<% foreach my $employee (findnodes('/employees/employee')) { %>
<b><%= $employee->findvalue("name/lastname") %>,
<%= $employee->findvalue("name/firstname") %></b>
works in the <%= $employee->findvalue('department') %>
department.
<hr>
<% } %>
</body>
</html>
]]></source_code>
</slide>
<slide>
<title>XPathScript example (results)</title>
<source_code><![CDATA[
<html>
<head><title>Employee List</title></head>
<body>
<h1>Employees at Acme Corp.</h1>
<b>Rabbit, Roger</b> works in the Humour department
<hr>
<b>Rabbit, Jessica</b> works in the Modelling department
<hr>
</body>
</html>
]]></source_code>
</slide>
<slide>
<title>XPathScript - Processing Documents</title>
<point level="1">Transforming Data is very different to transforming Documents</point>
<point level="1">Documents have mixed content</point>
<point level="1">Needs rules based processing</point>
<point level="1">XPathScript implements a feature rich declarative processing system</point>
</slide>
<slide>
<title>XPathScript - Declarative Templates</title>
<point level="1">The $t hash reference</point>
<source_code><![CDATA[
$t->{'para'}{pre} = '<p>';
$t->{'para'}{post} = '</p>';
]]></source_code>
<point level="1">Matches element names (unlike XSLT)</point>
<point level="1">XPathScript applies the rules as it traverses the document tree</point>
</slide>
<slide>
<title>XPathScript - $t subkeys</title>
<point level="1">Sub-keys of $t specify what to do</point>
<point level="1">Keys who's value is something to add to the ouput:</point>
<point level="2">pre</point>
<point level="2">post</point>
<point level="2">prechildren</point>
<point level="2">postchildren</point>
<point level="2">prechild</point>
<point level="2">postchild</point>
<point level="1">Other keys:</point>
<point level="2">showtag</point>
<point level="2">testcode</point>
</slide>
<slide>
<title>XPathScript - testcode</title>
<point level="1">testcode value is a code ref</point>
<point level="1">Executed every time an element with that name is visited</point>
<point level="1">Gives access to XML::XPath API on that node, and a local copy of $t</point>
<source_code><![CDATA[
$t->{'a'}{testcode} = sub {
my ($node, $t2) = @_;
if ($node->findvalue('@name')) {
# process as anchor
}
else {
# process as link
}
return DO_SELF_AND_KIDS;
};
]]></source_code>
</slide>
<slide>
<title>XPathScript tech details</title>
<point level="1">Script is compiled into Perl code</point>
<point level="1">Compiled once on first hit. Only re-compiled if changed</point>
<point level="1">Imports some functions: apply_templates(), findnodes(), findvalue()</point>
<point level="1">Fast enough for most real-time transformation needs (e.g. for dynamic content</point>
</slide>
<slide>
<title>Complete Example</title>
<point level="1">Take23.org - mod_perl news and resources</point>
<point level="1">Uses RSS 1.0 for all headlines and syndication</point>
<point level="1">Static Content, but looks dynamic</point>
<point level="1">Accepts content for articles in:</point>
<point level="2">XHTML</point>
<point level="2">DocBook</point>
<point level="2">POD (via pod2xml)</point>
<point level="2">OpenOffice 614 XML</point>
</slide>
<slide>
<title>Take23 - how it works</title>
<point level="1">httpd.conf contains AxAddRootProcessor directives</point>
<point level="1">Stylesheets written in XPathScript</point>
<point level="1">Stylesheets componentized to maximize reuse</point>
<point level="1">Uses OpenOffice provider module from AxKit.com to read OpenOffice files as XML</point>
<point level="1">News items created using AxKit::NewsMaker, a lightweight RSS/News content management system</point>
<point level="1">Cached content delivered at static HTML speeds</point>
</slide>
</slideset>
<slideset>
<title>AxKit for Dynamic Content</title>
<slide>
<title>XSP is the Key</title>
<point level="1">XSP is an XML language invented by the Cocoon Project at Apache</point>
<point level="1">XSP is implementation language agnostic</point>
<point level="2">Cocoon implements using Java</point>
<point level="2">AxKit implements using Perl</point>
<point level="1">XSP allows you to embed code in XML</point>
<point level="1">XSP is a page-based language like PHP and ASP</point>
<point level="1">... but XSP is better because you generate XML, maintaining separation of concerns</point>
</slide>
<slide>
<title>XSP Example</title>
<source_code><![CDATA[
<xsp:page xmlns:xsp="http://www.apache.org/1999/XSP/Core"
language="Perl">
<time>
<xsp:expr>localtime(time)</xsp:expr>
</time>
</xsp:page>
]]></source_code>
<point level="1">Which generates:</point>
<source_code><![CDATA[
<time>
Tue May 8 10:20:03 2001
</time>
]]></source_code>
<point level="1">Note: we then transform to HTML in the next part of the pipeline</point>
</slide>
<slide>
<title>XSP Lowdown</title>
<point level="1">XSP implements 11 core tags</point>
<point level="2">page</point>
<point level="2">structure</point>
<point level="2">import</point>
<point level="2">content</point>
<point level="2">logic</point>
<point level="2">element</point>
<point level="2">attribute</point>
<point level="2">pi</point>
<point level="2">comment</point>
<point level="2">text</point>
<point level="2">expr</point>
<point level="1">(plus a couple of other minor/supporting tags)</point>
</slide>
<slide>
<title>XSP and Namespaces</title>
<point level="1">XML Namespaces are key to how XSP works</point>
<source_code>
<prefix:tag xmlns:prefix="URI">
</source_code>
<point level="1">Each namespace can either implement custom functionality</point>
<point level="1">... or just be part of the output</point>
<point level="1">We call the namespaces "Tag Libraries", or taglibs for short</point>
</slide>
<slide>
<title>More XSP Examples</title>
<source_code><![CDATA[
<xsp:page xmlns:xsp="http://www.apache.org/1999/XSP/Core"
language="Perl">
<xsp:structure>
<xsp:import>Time::Piece</xsp:import>
</xsp:structure>
<xsp:logic>
sub afternoon {
return localtime->hour > 12;
}
</xsp:logic>
<page>
<xsp:logic>
if (afternoon()) {
<xsp:content>
Good <em>Afternoon!!!</em>
</xsp:content>
}
else {
<xsp:content>
Good <em>Morning!!!</em>
</xsp:content>
}
</xsp:logic>
</page>
</xsp:page>
]]></source_code>
</slide>
<slide>
<title>XSP Implementation</title>
<point level="1">SAX Parser, callbacks generate Perl code</point>
<point level="1">Perl compiled into a unique class</point>
<point level="1">handler() method wraps non-XSP top level tags (<page> tag in previous example)</point>
<point level="1">xsp:logic outside of top level tags can generate local functions</point>
<point level="2">mod_perl developers will know how handy this is to avoid closure problems</point>
<point level="1">Parser code implements "namespace dispatch"</point>
<point level="1">taglib implementations register with the core XSP parser engine's namespace dispatch system</point>
</slide>
<slide>
<title>XSP Taglibs</title>
<point level="1">Taglibs implement custom functionality</point>
<point level="1">Usually this is business logic</point>
<point level="1">Some low level taglibs exist on CPAN:</point>
<point level="2">Param</point>
<point level="2">Utils</point>
<point level="2">Cookie</point>
<point level="2">ParamAttrib</point>
<point level="2">SendMail</point>
<point level="2">Exceptions</point>
<point level="2">ESQL</point>
</slide>
<slide>
<title>Implementing Taglibs</title>
<point level="1">3 ways to implement taglibs (in decreasing levels of difficulty)</point>
<point level="1">1. Write a Stylesheet</point>
<point level="2">this transforms the taglib tags into core XSP tags</point>
<point level="1">2. Write a SAX parser</point>
<point level="2">here the parser has to write perl code</point>
<point level="1">3. Use the AxKit TaglibHelper module</point>
<point level="2">here you just write a Perl module, naming the functions after the tags you want to implement</point>
<point level="1">Most of the taglibs on CPAN use method 2 at the moment, simply because of when TaglibHelper appeared</point>
</slide>
<slide>
<title>Param Taglib</title>
<source_code><![CDATA[
<xsp:page
xmlns:xsp="http://apache.org/xsp/core/v1"
xmlns:param="http://axkit.org/NS/xsp/param/v1"
language="Perl"
>
<page>
<xsp:logic>
if (<param:name/>) {
<xsp:content>
Your name is: <param:name/>
</xsp:content>
}
else {
<xsp:content>
<form>
Enter your name: <input type="text" name="name" />
<input type="submit"/>
</form>
</xsp:content>
}
</xsp:logic>
</page>
</xsp:page>
]]></source_code>
</slide>
<slide>
<title>Things to note...</title>
<point level="1">Freely mixed Perl and XML</point>
<point level="2">No <% ... %> to introduce one or the other</point>
<point level="2">An XSP page is pure XML</point>
<point level="1">That has consequences:</point>
<source_code><![CDATA[
<xsp:logic>
my $page = <param:page/>
if ($page < 3) { # INVALID CODE. < not allowed in XML
...
}
</xsp:logic>
]]></source_code>
<point level="1">Could iritate some people</point>
<point level="1">Easy to fix this case. Use 3 > $page</point>
</slide>
<slide>
<title>Cookie Taglib</title>
<source_code><![CDATA[
<xsp:page
xmlns:xsp="http://apache.org/xsp/core/v1"
xmlns:cookie="http://axkit.org/NS/xsp/cookie/v1"
language="Perl"
>
<page>
<xsp:logic>
my $value;
if ($value = <cookie:fetch name="count"/>) {
$value++;
}
else {
$value = 1;
}
</xsp:logic>
<cookie:create name="count">
<cookie:value><xsp:expr>$value</xsp:expr></cookie:value>
</cookie:create>
<p>Cookie value: <xsp:expr>$value</xsp:expr></p>
</page>
</xsp:page>
]]></source_code>
</slide>
<slide>
<title>More things to note</title>
<point level="1">XSP extends Perl's notion of DWIM (Do What I Mean)</point>
<point level="1">Note how the cookie:value is taken from a Perl expression</point>
<point level="1">It could equally have been hard coded. Witness these three versions:</point>
<source_code><![CDATA[
<cookie:value>3</cookie:value>
<cookie:value><xsp:expr>2 + 1</xsp:expr></cookie:value>
<cookie:value><param:cookie_value/></cookie:value>
]]></source_code>
<point level="1">All produce valid code</point>
</slide>
<slide>
<title>SendMail Taglib</title>
<source_code><![CDATA[
<xsp:page (namespace stuff removed for space issues)>
<page>
<xsp:logic>
if (!<param:email/>) {
<p>You forgot to supply an email address!</p>
}
else {
if (<param:subopt/> eq "sub") {
$to = "axkit-users-subscribe@axkit.org";
}
elsif (<param:subopt/> eq "unsub") {
$to = "axkit-users-unsubscribe@axkit.org";
}
<mail:send-mail>
<mail:from><param:user_email/></mail:from>
<mail:to><xsp:expr>$to</xsp:expr></mail:to>
<mail:body>Subscribe or Unsubscribe <param:user_email/></mail:body>
</mail:send-mail>
<p>(un)subscription request sent</p>
}
</xsp:logic>
</page>
</xsp:page>
]]></source_code>
</slide>
<slide>
<title>Exceptions Taglib</title>
<point level="1">Previous slides lacking error handling</point>
<point level="1">All AxKit taglibs throw exceptions on error</point>
<point level="1">Need Exception taglib to catch them</point>
<source_code><![CDATA[
<except:try>
... # code that throws exceptions
<except:catch>
<error>An Error Occured: <except:message/></error>
</except:catch>
</except:try>
]]></source_code>
</slide>
<slide>
<title>ESQL Taglib</title>
<point level="1">The mother of all taglibs</point>
<point level="1">Executes SQL against DBI connections</point>
<point level="1">Add Apache::DBI in the mix for cached connections</point>
<point level="1">Options for SQL with or without results</point>
<point level="1">Columns can be retrieved all at once, or one at a time</point>
<point level="1">Allows emulation of nested queries (e.g. for MySQL)</point>
</slide>
<slide>
<title>ESQL Example</title>
<source_code><![CDATA[
<xsp:page
language="Perl"
xmlns:xsp="http://apache.org/xsp/core/v1"
xmlns:esql="http://apache.org/xsp/SQL/v2"
xmlns:except="http://axkit.org/NS/xsp/exception/v1"
xmlns:param="http://axkit.org/NS/xsp/param/v1"
indent-result="no"
>
<addresses>
<esql:connection>
<!-- connection parameters (normally use entities for these) -->
<esql:driver>Pg</esql:driver>
<esql:dburl>dbname=phonebook</esql:dburl>
<esql:username>postgres</esql:username>
<esql:password></esql:password>
<except:try>
<esql:execute-query>
]]></source_code>
</slide>
<slide>
<title>ESQL Example (cont)</title>
<source_code><![CDATA[
<xsp:logic>
if (<param:address_id/>) {
<esql:query>
SELECT * FROM address WHERE id =
<esql:parameter><param:address_id/></esql:parameter>
</esql:query>
}
else {
<esql:query>
SELECT * FROM address
</esql:query>
}
</xsp:logic>
<esql:results>
<esql:row-results>
<address>
<esql:get-columns/>
</address>
</esql:row-results>
</esql:results>
</esql:execute-query>
]]></source_code>
</slide>
<slide>
<title>ESQL Example (cont)</title>
<source_code><![CDATA[
<except:catch>
Error Occured: <except:message/>
</except:catch>
</except:try>
</esql:connection>
</addresses>
</xsp:page>
]]></source_code>
</slide>
<slide>
<title>ESQL Example (results)</title>
<source_code><![CDATA[
<addresses>
<address>
<id>2</id>
<last_name>Sergeant</last_name>
<first_name>Matt</first_name>
<title>Mr</title>
<company>AxKit.com Ltd</company>
<email>matt@axkit.com</email>
<classification_id>1</classification_id>
</address>
</addresses>
]]></source_code>
</slide>
<slide>
<title>PerForm</title>
<point>Easy Form handling</point>
<point>Should allow for same form to be presented to WML or other formats. But HTML is the priority</point>
<point>Callback based</point>
<point>Supports validation, loading, start/end form</point>
<point>Generates XML abstract form, not HTML or XHTML form</point>
<point>Example XSLT supplied for transforming to HTML</point>
</slide>
<slide>
<title>PerForm (example)</title>
<source_code><![CDATA[
<xsp:page>
<xsp:logic>
...
</xsl:logic>
<html>
<head><title>Index</title></head>
<body>
<f:form name="view">
<h2>View Items</h2>
<f:single-select name="types"/>
<f:submit name="go" value="Go!"/>
</f:form>
</body>
</html>
</xsp:page>
]]></source_code>
<point>Callbacks implemented in top-level <xsp:logic> section</point>
</slide>
<slide>
<title>PerForm (example cont)</title>
<point>Callbacks:</point>
<source_code><![CDATA[
sub load_types {
my ($ctxt, $selected) = @_;
my $db = AxKit::RHNews::DB->instance();
my @types = $db->get_asset_types();
return $selected, "All Types" => "0", map { $_->[1] => $_->[1] } @types;
}
use Apache::Util ();
sub submit_go {
my ($ctxt) = @_;
return "view_asset.xsp?type=" . Apache::Util::escape_uri($ctxt->{Form}{types});
}
]]></source_code>
</slide>
<slide>
<title>Building your XSP Based App</title>
<point level="1">Hide logic in taglibs</point>
<point level="1">Or hide logic in Perl modules called from <xsp:logic> and <xsp:expr> sections</point>
<point level="1">Generate XML, not HTML</point>
<point level="1">Process to HTML in a further pipeline transformation</point>
</slide>
</slideset>
<slideset>
<title>Advanced AxKit</title>
<slide>
<title>Alternate Providers</title>
<point>Builtins: File, Filter, Scalar</point>
<point>Others: e.g. OpenOffice</point>
<point>API:</point>
<point level="2">init( %params )</point>
<point level="2">key() = string</point>
<point level="2">exists() = ?</point>
<point level="2">process() = ?</point>
<point level="2">decline(%args)</point>
<point level="2">mtime() = int</point>
<point level="2">get_fh() = FH</point>
<point level="2">get_strref() = strref</point>
<point level="2">has_changed($mtime) = ?</point>
<point level="2">get_ext_ent_handler() = subref</point>
<point level="2">get_styles($media, $style)</point>
</slide>
<slide>
<title>Example: Ever expiring file</title>
<source_code><![CDATA[
package Apache::AxKit::Provider::ExpireFile;
use Apache::AxKit::Provider::File;
@ISA = ('Apache::AxKit::Provider::File');
sub has_changed {
my ($self, $mtime) = @_;
return 1;
}
1;
]]></source_code>
<point>httpd.conf</point>
<source_code><![CDATA[
AxProvider Apache::AxKit::Provider::ExpireFile
]]></source_code>
</slide>
<slide>
<title>Alternate ConfigReaders</title>
<point level="2">new($r)</point>
<point level="2">StyleMap() = hashref</point>
<point level="2">CacheDir() = string</point>
<point level="2">ProviderClass() = string</point>
<point level="2">PreferredStyle() = string</point>
<point level="2">PreferredMedia() = string</point>
<point level="2">CacheModule() = string</point>
<point level="2">DebugLevel() = int</point>
<point level="2">StackTrace() = ?</point>
<point level="2">LogDeclines() = ?</point>
<point level="2">OutputCharset() = string</point>
<point level="2">ErrorStyles() = complex</point>
<point level="2">GzipOutput() = ?</point>
<point level="2">DoGzip() = ?</point>
<point level="2">GetMatchingProcessors() = complex</point>
<point level="2">XSPTaglibs() = list</point>
</slide>
<slide>
<title>Alternate Cache Module</title>
<point>Possibly store cache in a database</point>
<point>API:</point>
<point level="2">new($r, $xmlfile, @parts)</point>
<point level="2">write($string)</point>
<point level="2">read() = string</point>
<point level="2">get_fh() = FH</point>
<point level="2">deliver()</point>
<point level="2">reset()</point>
<point level="2">has_changed($mtime) = ?</point>
<point level="2">mtime()</point>
<point level="2">exists() = ?</point>
<point level="2">key() = string</point>
<point level="2">no_cache($on)</point>
</slide>
</slideset>
<slide>
<title>AxKit Future Plans</title>
<point level="1">Content Management System, for easier site building and maintainence</point>
<point level="1">Apache 2.0 Port</point>
<point level="1">More SAX work, more C work</point>
<point level="1">Allow JSP taglibs to work</point>
</slide>
<slide>
<title>Conclusions</title>
<point level="1">Perl and XML are a powerful combination</point>
<point level="1">XPath and XSLT add to the mix...</point>
<point level="1">AxKit can reduce your long term costs</point>
<point level="2">In site re-design</point>
<point level="2">and in content re-purposing</point>
<point level="1">Open Source equal to commercial alternatives</point>
</slide>
<slide>
<title>Resources and contact</title>
<point level="1">AxKit: http://axkit.org/</point>
<point level="1">CPAN: http://search.cpan.org</point>
<point level="1">libxml and libxslt: http://www.xmlsoft.org</point>
<point level="1">Sablotron: http://www.gingerall.com</point>
<point level="1">XPath and XSLT Tutorials: http://zvon.org</point>
</slide>
</slideshow>
1.1 xml-axkit/axkit.org/docs/xpathscript/guide.dkb
Index: guide.dkb
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="/stylesheets/docbook_screen.xps" type="application/x-xpathscript"
title="default"?>
<?xml-stylesheet href="/stylesheets/docbook_print.xps"
type="application/x-xpathscript" title="print" alternate="yes"?>
<!DOCTYPE article [
<!ENTITY prompt "% ">
<!ENTITY XPath "<ulink url='http://www.w3.org/TR/xpath'>XPath</ulink>">
<!ATTLIST sect1 id ID #IMPLIED>
<!ATTLIST sect2 id ID #IMPLIED>
]>
<article>
<artheader>
<title>XPathScript - A Viable Alternative to XSLT?</title>
<author>
<firstname>Matt</firstname>
<surname>Sergeant</surname>
<affiliation>
<address><email>matt@sergeant.org</email></address>
</affiliation>
</author>
<copyright>
<year>2000</year>
<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
</copyright>
<abstract>
<para>This guide gives an introduction to the features of
XPathScript, a template processor that is part of <ulink
url="http://xml.sergeant.org/axkit/">AxKit</ulink> which provides
full programming facilities alongside XPath based node resolution.
It also features code / template separation using the
ASP <literal><% %></literal> paradigm.</para>
</abstract>
</artheader>
<!-- ################################### -->
<sect1>
<title>Introduction</title>
<para>
XPathScript is a stylesheet language for translating XML files into some
other format. It has only a few features, but by combining those
features with the power and flexibility of Perl, XPathScript is a very
capable system. Like all XML stylesheet languages, including XSLT, an
XPathScript stylesheet is always executed in the context of a source XML
file. In many cases the source XML file will actually define what
stylesheets to use via the <literal><?xml-stylesheet?></literal>
processing instruction.
</para>
<para>
XPathScript was concieved as part of AxKit - an application server
environment for Apache servers running mod_perl (XML.com ran my <ulink
url="http://www.xml.com/pub/a/2000/05/24/axkit/index.html">
Introduction to AxKit</ulink> article in May). Its
primary goal was to achieve the sorts of transformations that XSLT can
do, without being restricted by XSLT's XML based syntax, and to provide
full programming facilities within that environment. I also wanted
XPathScript to be completely agnostic about output formats, without
having to program in special after-effect filters. The result is a
language for server-side transformation that provides the power and flexibility of
XSLT combined with the full capabilities of the Perl language, and the
ability to produce stylesheets in any ASP capable editor or ordinary
text editor. The above Introduction to AxKit is recommended reading
before reading this guide.
</para>
</sect1>
<!-- ################################### -->
<sect1>
<title>The Syntax</title>
<para>
XPathScript follows the basic ASP syntax of introducing code with the
<literal><% %></literal> delimiters. Here's a brief example of a
fully compatible XPathScript stylesheet:
<informalexample>
<programlisting><![CDATA[
<html>
<body>
<%= 5+5 %>
</body>
</html>
]]></programlisting>
</informalexample>
This simply outputs the value 10 in a HTML document. The delimiters used
here are the <literal><%= %></literal> delimiters, which are slightly
different in that they send the results of the expression to the browser
(or to the next processing stage in AxKit).
Of course this example does absolutely nothing with the source XML file
which is completely separate from this stylesheet. Here's another
example:
<informalexample>
<programlisting><![CDATA[
<html>
<body>
<% $foo = 'World' %>
Hello
<%= $foo %> !!!
</body>
</html>
]]></programlisting>
</informalexample>
This outputs the text <literal>"Hello World !!!"</literal>. Again, we're
not actually doing anything here with our source document, so all XML
files using this stylesheet will look identical. This seems rather
uninteresting until we discover the library of functions that are
accesible to our XPathScript stylesheets for accessing the source
document contents.
</para>
</sect1>
<!-- ################################### -->
<sect1>
<title>The XPathScript API</title>
<para>
Along with the code delimiters XPathScript provides stylesheet
developers with a full API for accessing and transforming the source XML
file. This API can be used in conjunction with the delimiters above to
provide a stylesheet language that is as powerful as XSLT, and yet
provides all the features of a full programming language (in this case,
Perl, but I'm certain that other implementations such as Python or Java
would be possible).
</para>
<sect2 id="extract_values">
<title>Extracting Values</title>
<para>
A simple example to get us started, is to use the API to bring in the
title from a docbook article. A docbook article title looks like this:
<informalexample>
<programlisting><![CDATA[
<article>
<artheader>
<title>XPathScript - A Viable Alternative to XSLT?</title>
...
]]></programlisting>
</informalexample>
The XPath expression to retrieve the text in the title element is:
<informalexample>
<programlisting><![CDATA[
/article/artheader/title/text()
]]></programlisting>
</informalexample>
Putting this all together to make this text into the HTML title we get
the following XPathScript stylesheet:
<informalexample>
<programlisting><![CDATA[
<html>
<head>
<title><%= findvalue("/article/artheader/title/text()") %></title>
</head>
<body>
This was a DocBook Article. We're only extracting the title for now!
<p>
The title was: <%= findvalue("/article/artheader/title/text()") %>
</body>
</html>
]]></programlisting>
</informalexample>
</para>
<para>
There are lots of features to the expression syntax we used to find that
"node", and this syntax is called &XPath;. This is a W3C standard for
finding and matching XML document nodes. The standard is fairly readable
and is at <ulink
url="http://www.w3.org/TR/xpath">http://www.w3.org/TR/xpath</ulink>
alternatively I can recommend <ulink
url="http://www.arbortext.com/Think_Tank/Norm_s_Column/Issue_One/Issue_One.html"
>Norm Walsh's XPath introduction</ulink> which covers a slightly older
version of the specification, but I didn't notice anything in the
article that is missing or different from the current recommendation.
</para>
</sect2>
<sect2>
<title>Extracting Nodes</title>
<para>
The above example showed us how to extract single values, but what if we
have a list of things we wish to extract values from? Here's how we
might get a table of contents from docbook article sections:
<informalexample>
<programlisting><![CDATA[
...
<%
for my $sect1 (findnodes("/article/sect1")) {
print $sect1->findvalue("title/text()"), "<br>\n";
for my $sect2 ($sect1->findnodes("sect2")) {
print " + ", $sect2->findvalue("title/text()"), "<br>\n";
for my $sect3 ($sect2->findnodes("sect3")) {
print " + + ", $sect3->findvalue("title/text()"), "<br>\n";
}
}
}
%>
...
]]></programlisting>
</informalexample>
This gives us a table of contents down to three levels (adding links to
the actual part of the document is left as an exercise). The first call
to findnodes gives use all sect1 nodes that are children of the root
element (article). The &XPath; expressions following that are relative to the
current node. You can see that by the absence of the leading
<literal>/</literal>. Again, &XPath; is a very interesting query language,
and you would be best to visit <ulink
url="http://www.w3.org/TR/xpath">the XPath specification</ulink> to
learn more.
</para>
<para>
Note that in the above we don't use the global function findnodes()
after finding the sect1 nodes, instead we call the node method
findnodes(), which does exactly the same thing, but makes the node you
are calling from the context of the XPath expression.
</para>
</sect2>
<sect2>
<title>Declarative Templates</title>
<para>
The examples up to now have all covered a concept of a single global
template with a search/replace type functionality from the source XML
document. This is a powerful concept in itself, especially when combined
with loops and the ability to change the context of searches. But that
style of template is limited in utility to well structured data, rather
than processing large documents. In order to ease the processing of
documents, XPathScript includes a declarative template processing model
too, so that you can simply specify the format for a particular element
and let XPathScript do the work for you.
</para>
<para>
In order to support this method, XPathScript introduces one more API
function: <literal>apply_templates()</literal>. The name is intended to
appeal to people already familiar with XSLT. The
<literal>apply_templates()</literal> function takes either a list of
start nodes, or an &XPath; expression (that must result in a node set) and
optional context. Starting at the start nodes it traverses the document
tree applying the templates defined by the <literal>$t</literal> hash
reference.
</para>
<para>
First a simple example to introduce this feature. Lets assume for a
moment that our source XML file is valid XHTML, and we want to change
all anchor links to italics. Here is the very simple XPathScript
template that will do that:
<informalexample>
<programlisting><![CDATA[
<%
$t->{'a'}{pre} = '<i>';
$t->{'a'}{post} = '</i>';
$t->{'a'}{showtag} = 1;
%>
<%= apply_templates() %>
]]></programlisting>
</informalexample>
Note that <literal>apply_templates()</literal> has to be output using
<literal><%= %></literal>. That's because
<literal>apply_templates()</literal> actually outputs a string
representation of the transformation, it doesn't do the output to the
browser for you.
</para>
<para>
The first thing this example does is sets up a hash reference
<literal>$t</literal> that XPathScript knows about (lets call it
magical). The keys of <literal>$t</literal> are element names (including
namespace prefix if we are using namespaces). The hash can have the
following sub-keys:
<itemizedlist>
<listitem><literal>pre</literal></listitem>
<listitem><literal>post</literal></listitem>
<listitem><literal>showtag</literal></listitem>
<listitem><literal>testcode</literal></listitem>
</itemizedlist>
We'll cover <literal>testcode</literal> in more depth later in <xref
linkend="template_sect"/>, but for now know that it is a place holder
for code that allows for more complex templates.
</para>
<para>
Unlike XSLT's declarative transformation syntax, the keys of
<literal>$t</literal> do <emphasis>not</emphasis> specify &XPath; match
expressions. Instead they are simple element names. This is a trade off
of speed of execution over flexibility. Perl hash lookups are extremely
quick compared to XPath matching. Luckily because of the
<literal>testcode</literal> option, more complex matches are quite
possible with XPathScript.
</para>
<para>
The simple explanation for now is that <literal>pre</literal> specifies
output to appear before the tag, <literal>post</literal> specifies
output to appear after the tag, and <literal>showtag</literal> specifies
that the tag itself should be output as well as the pre and post values.
</para>
</sect2>
</sect1>
<!-- ################################### -->
<sect1>
<title>A Complete Example</title>
<para>
Now lets put all of these ideas together into a (almost) complete
example. This is part of the stylesheet I use to process my docbook articles
online:
<informalexample>
<programlisting><![CDATA[
<!--#include file="docbook_tags.xps"-->
<%
my %links;
my $linkid = 0;
$t->{'ulink'}{testcode} = sub {
my $node = shift;
my $t = shift;
my $url = findvalue('@url', $node);
if (!exists $links{$url}) {
$linkid++;
$links{$url} = $linkid;
}
my $link_number = $links{$url};
$t->{pre} = "<i><a href=\"$url\">";
$t->{post} = " [$link_number]</a></i>";
return 1;
};
%>
<html>
<head>
<title><%= findvalue('/article/artheader/title/text()') %></title>
</head>
<body bgcolor="white">
<%
# display title/TOC page
print apply_templates('/article/artheader/*');
%>
<hr>
<%
# display particular page
foreach my $section (findnodes("/article/sect1")) {
print apply_templates($section);
}
%>
<h1>List of Links</h1>
<table border="1">
<th>URL</th>
<%
for my $link (sort {$links{$a} <=> $links{$b}} keys %links) {
%>
<tr>
<td><%= "[$links{$link}] $link" %></td>
</tr>
<% } %>
</table>
</body>
</html>
]]></programlisting>
</informalexample>
The very first line there imports a library of tags that are shared
between this stylesheet, and one that is easier for web viewing with
clickable links between sections (which can be downloaded <ulink
url="http://xml.sergeant.org/docbook_screen.xps">here</ulink>). The
import system is based on Server Side Includes (SSI) although only SSI
file includes are supported at this time (SSI virtual includes can be
implemented using mod_include). Here is part of the docbook_tags.xps
file:
<informalexample>
<programlisting><![CDATA[
<%
$t->{'attribution'}{pre} = "<i>";
$t->{'attribution'}{post} = "</i><br>\n";
$t->{'para'}{pre} = '<p>';
$t->{'para'}{post} = '</p>';
$t->{'ulink'}{testcode} = sub {
my $node = shift;
my $t = shift;
$t->{pre} = "<i><a href=\"" .
findvalue('./@url', $node) . "\">";
$t->{post} = '</a></i>';
return 1;
};
$t->{'title'}{testcode} = sub {
my $node = shift;
my $t = shift;
if (findvalue('parent::blockquote', $node)) {
$t->{pre} = "<b>";
$t->{post} = "</b><br>\n";
}
elsif (findvalue('parent::artheader', $node)) {
$t->{pre} = "<h1>";
$t->{post} = "</h1>";
}
else {
my $parent = findvalue('name(..)', $node);
if (my ($level) = $parent =~ m/sect(\d+)$/) {
$t->{pre} = "<h$level>";
$t->{post} = "</h$level>";
}
}
return 1;
};
%>
]]></programlisting>
</informalexample>
</para>
<para>
We go into detail of what is happening in this example in the next
section.
</para>
</sect1>
<!-- ################################### -->
<sect1>
<title>Stepping Through the Example</title>
<para>
Careful readers will note that the first thing we see is a
<literal>$t</literal> specification for <literal><ulink></literal>
tags, and you'll also note that the included
<filename>docbook_tags.xps</filename> contains a specification for
<literal><ulink></literal>. The reason is to override the default
behaviour for ulink tags in the print version of my articles to contain
a reference that we can use later in a list of links. We can also see
that this specification uses a <literal>testcode</literal> parameter
that we haven't encountered before. We'll see how and why that's used
later in <xref linkend="template_sect"/>.
</para>
<para>
Next we see the <literal>findvalue()</literal> function used exactly as
we already saw in <xref linkend="extract_values"/>.
</para>
<para>
Then we have a section with a comment marked: "display Title/TOC page".
This uses the <literal>apply_templates()</literal> function with an
&XPath; expression. Note that rather than use the <literal><%=
%></literal> delimiters around the apply_templates() call, we simply use
the print function. This has the same effect, and is used here to show
the flexibility in this approach.
</para>
<para>
The main part of the code loops through all sect1 tags, and calls
apply_templates on those nodes. Note how this is another demonstration
of Perl's TMTOWTDI (There's More Than One Way To Do It) approach - the
same code could have been written:
<informalexample>
<programlisting><![CDATA[
<%= apply_templates("/article/sect1") %>
]]></programlisting>
</informalexample>
</para>
<para>
Finally, because this is the print version of our article, we provide a
list of links so that people viewing a printed version of this article
can type in those links, and they can also refer to the link by
reference number, as we saw earlier. We use the hash of links in the
<literal>%links</literal> variable that we built in the
<literal>testcode</literal> handler for our <literal>ulink</literal>
template.
</para>
<para>
The other file, <filename>docbook_tags.xps</filename>, is included only
in part here, to demonstrate a few of the transformations we're applying
to various docbook article tags. We can see that we're turning
<literal><para></literal> tags into <literal><p></literal> tags,
and doing some more complex processing with <literal>testcode</literal>
to <literal><title></literal> tags. We'll see in <xref
linkend="template_sect"/> exactly what <literal>testcode</literal>
allows us to achieve.
</para>
</sect1>
<!-- ################################### -->
<sect1 id="template_sect">
<title>The Template Hash</title>
<para>
The <literal>apply_templates()</literal> function iterates over the
nodes of your XML file applying the templates in the
<literal>$t</literal> hash reference. This is the most important feature
of XPathScript, because it allows you to define the appearance for
individual tags without having to do it programmatically. This is the
declarative part of XPathScript. There is an important point to make
here: XSLT is a purely declarative syntax, and people are having to work
procedural code into XSLT via work arounds. XPathScript takes a much
more pragmatic approach (much like Perl itself) - it is both declarative
and procedural, allowing you the flexibility to use real code for real
problems. It is important to note that apply_templates returns a string,
so you must either use <literal>print apply_templates()</literal> if
using it from a Perl section of code, or via <literal><%=
apply_templates() %></literal>.
</para>
<para>
The keys of <literal>$t</literal> are the names of the elements, including namespace
prefixes. When you call <literal>apply_templates()</literal>, every
element visited is looked up in the <literal>$t</literal> hash, and the
template items stored in that hash are applied to the node. It's worth
noting at this point, that unlike XSLT, XPathScript does not perform
tree transformations from one tree to another. It simply sends its
output to the browser directly. This has advantages and disadvantages,
but they are beyond the scope of this guide.
</para>
<para>
The following sub-keys define the transformation:
<itemizedlist>
<listitem><literal>pre</literal> - the output to occur before the
tag.</listitem>
<listitem><literal>post</literal> - the output to occur after the
tag.</listitem>
<listitem><literal>prechildren</literal> - the output to occur before
the children of this tag are output.</listitem>
<listitem><literal>postchildren</literal> - the output to occur after
the children of this tag are output.</listitem>
<listitem><literal>prechild</literal> - the output to occur before
each child of this tag.</listitem>
<listitem><literal>postchild</literal> - the output to occur after
each child of this tag.</listitem>
<listitem><literal>showtag</literal> - set to a true value to display
the tag as well as the pre and post values. If unset or false the tag
itself is not displayed.</listitem>
<listitem><literal>testcode</literal> - code to execute upon visiting
this tag. See below.</listitem>
</itemizedlist>
The showtag option is mostly equivalent to the XSLT
<literal><xsl:copy></literal> tag, only less verbose. The pre and post
options are useful because generally in transformations we want to
specify what comes before and after a tag. For example, to change an
HTML A tag to be in italics, but still have the link, we would use the
following:
<informalexample>
<programlisting><![CDATA[
$t->{A}{pre} = "<i>";
$t->{A}{post} = "</i>";
$t->{A}{showtag} = 1;
]]></programlisting>
</informalexample>
</para>
<sect2>
<title>"testcode"</title>
<para>
The <literal>testcode</literal> option is where we perform really
powerful transformations. Its how we can do more complex tests on the
node that are available in XPath, and locally modify the transformation
based on what we find.
</para>
<para>
The value stored in <literal>testcode</literal> is simply a reference to
a subroutine. In Perl these are incredibly simple to create using the
anonymous sub keyword (note that these are often erroneously called
closures, but they only become closures if they reference a lexical
variable outside the scope of the subroutine itself). The sub is called
every time one of these elements is visited. The subroutine is passed two parameters:
The node itself, and an empty hash reference that you can populate using
the <literal>pre</literal>, <literal>post</literal>,
<literal>prechildren</literal>, <literal>prechild</literal>,
<literal>postchildren</literal>, <literal>postchild</literal> and
<literal>showtag</literal> values that we've discussed already. Unlike
the global <literal>$t</literal> hashref you don't have to first
specify the element name as a key. Here's the
<literal><ulink></literal> example from the global tags code above:
<informalexample>
<programlisting><![CDATA[
$t->{'ulink'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} = '<i><a href="' . findvalue('@url', $node) . '">';
$t->{post} = '</a></i>';
return 1;
};
]]></programlisting>
</informalexample>
The equivalent XSLT code looks like this:
<informalexample>
<programlisting><![CDATA[
<xsl:template match="ulink">
<i><a>
<xsl:attribute name="href">
<xsl:value-of select="@url"/>
</xsl:attribute>
<xsl:apply-templates/>
</a></i>
</xsl:template>
]]></programlisting>
</informalexample>
Note in the XPathScript above that the inner <literal>$t</literal> is
lexically scoped, so changes to it don't affect the outer
<literal>$t</literal>. To save some confusion we might have named that
variable <literal>$localtransforms</literal>, but some people like
myself hate typing... ;-)
</para>
<para>
The return value from the testcode is also important. A return value of
1 means to process this node and continue processing all the children of
this node. A return value of -1 means to process this node and stop, and
a return value of 0 means do not process this node at all. This is
useful in conditional tests, where you may not wish to process the nodes
under certain conditions. You may also use a return code of a consisting
of a string that is an XPath expression. See <xref
linkend="mini_reference"/> for more information.
</para>
<para>
It is important to note that we can do things here based on XPath
lookups just as we can in XSLT. While it is a little more verbose than a
simple XSLT pattern match, the trade off is in performance. An example
is in XSLT you might match <literal>artheader/title</literal> and
elsewhere you might match <literal>title[name(..) != "artheader"</literal>.
In XPathScript we can only match "title" in the
template hash. But we can use the testcode section to extend the match:
<informalexample>
<programlisting><![CDATA[
$t->{'title'}{testcode} = sub {
my $node = shift;
my $t = shift;
if (findvalue('parent::blockquote', $node)) {
$t->{pre} = "<b>";
$t->{post} = "</b><br>\n";
}
elsif (findvalue('parent::artheader', $node)) {
$t->{pre} = "<h1>";
$t->{post} = "</h1>";
}
else {
my $parent = findvalue('name(..)', $node);
if (my ($level) = $parent =~ m/sect(\d+)$/) {
$t->{pre} = "<h$level>";
$t->{post} = "</h$level>";
}
}
return 1;
};
]]></programlisting>
</informalexample>
Here we check what the parent node is before performing our modification
to the local <literal>$t</literal> hashref. Specifically note the
utility of being able to perform Perl regular expressions to extract
values.
</para>
</sect2>
<sect2>
<title>Copying styles</title>
<para>
One really neat feature of XPathScript that is really hard to do with
XSLT is to be able to copy a style completely:
<informalexample>
<programlisting><![CDATA[
<%
$t->{'foo'}{pre} = "<i>";
$t->{'foo'}{post} = "</i>";
$t->{'foo'}{showtag} = 1;
$t->{'bar'} = $t->{'foo'};
%>
]]></programlisting>
</informalexample>
While this would be possible in XSLT using entities, it's certainly not
very practical or neat. With XPathScript many tags can share the same
template. Be careful though - this is a reference copy, not a deep copy,
so the following may not do what you think it should:
<informalexample>
<programlisting><![CDATA[
<%
$t->{'foo'}{pre} = "<i>";
$t->{'foo'}{post} = "</i>";
$t->{'foo'}{showtag} = 1;
$t->{'bar'} = $t->{'foo'};
$t->{'bar'}{post} = "</i><br>";
%>
]]></programlisting>
</informalexample>
Because this is a reference, the last line there changes the values for
'foo' as well as 'bar'.
</para>
</sect2>
<sect2>
<title>A "Catch All"?</title>
<para>
Does XPathScript have a "catch all" option for elements that I don't
have a <literal>$t</literal> entry for? Yes, of course! Simply set
<literal>$t->{'*'}</literal> to the template you want to execute. You
can even do some really clever things, such as using the
<literal>testcode</literal> section to output a warning to the Apache
error log about an unrecognised tag, rather than having to place some
output in the resulting document and bother your users!
</para>
<para>
This feature was introduced in AxKit 0.94.
</para>
</sect2>
<sect2>
<title>Interpolation</title>
<para>
Adding attributes or other data into the translated nodes is non-trivial
using this setup. It requires you to drop down into testcode. Here's an
example of turning <literal><link url="..."></literal> tags into
HTML <literal><a></literal> tags:
<informalexample>
<programlisting><![CDATA[
<%
$t->{'link'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} = '<a href="' . $node->findvalue('@url') . '">';
$t->{post} = '</a>';
return 1;
};
%>
]]></programlisting>
</informalexample>
This is obviously rather verbose.
</para>
<para>
To make this a little simpler, in XPathScript as of AxKit 1.1, we have
introduced interpolation of the replacement strings, much the same as
you can do with XSLT attributes. Here is the appropriate
<literal>$t</literal> entry as of AxKit 1.1:
<informalexample>
<programlisting><![CDATA[
<%
$t->{'link'}{pre} = '<a href="{@url}">';
$t->{'link'}{post} = '</a>';
%>
]]></programlisting>
</informalexample>
The curly brackets <literal>{}</literal> delimit an XPath expression on
which findvalue is called using the current node as the context. Any
XPath expression should be valid within those delimiters.
</para>
<para>
As a backwards compatibility measure, and to ensure efficiency is
defaulted, interpolation only occurs when you have the following
somewhere in your Apache configuration defined for the current request:
<informalexample>
<programlisting><![CDATA[
PerlSetVar AxXPSInterpolate 1
]]></programlisting>
</informalexample>
You can also turn off interpolation temporarily in your script using the
global variable <literal>$XPathScript::DoNotInterpolate</literal>. Set
that to a true value to turn off interpolation. Be careful to only do
that locally (using the perl <literal>local</literal> keyword) to ensure
it doesn't remain set for the next invocation of the script.
</para>
</sect2>
</sect1>
<!-- ################################### -->
<sect1>
<title>Writing Dynamic Content</title>
<para>
Because XPathScript has full access to all the perl builtins, you can
very easily create dynamic content with XPathScript. There is only 1
caveat though: The AxKit cache works on the basis of the timestamp of
the original XML file. This means that your XPathScript code will only
be executed when the XML resource that is being requested actually
changes.
</para>
<para>
To work around this limitation you simply need to tell AxKit that this
stylesheet contains dynamic content, and therefore the output should not
be cached. The syntax for this duplicates the Apache API for telling
proxy servers not to cache the output:
<informalexample>
<programlisting>
<%
...
$r->no_cache(1);
...
%>
</programlisting>
</informalexample>
</para>
</sect1>
<!-- ################################### -->
<sect1 id="mini_reference">
<title>An XPathScript Mini-Reference</title>
<para>
Code is separated from output in XPathScript using the <literal><%
%></literal> delimiters.
</para>
<para>
Perl expression results can be sent to the browser either using
<literal>print()</literal> if inside a <literal><% %></literal>
section, or via <literal><%= <emphasis>code</emphasis> %></literal>.
</para>
<para>
The following XPath functions are imported for your use:
<itemizedlist>
<listitem><literal>findnodes($path, [$context])</literal></listitem>
<listitem><literal>findvalue($path, [$context])</literal></listitem>
<listitem><literal>findnodes_as_string($path, [$context])</literal></listitem>
<listitem><literal>apply_templates( $path, [$context])</literal></listitem>
<listitem><literal>apply_templates( @nodes )</literal></listitem>
<listitem><literal>import_template( $uri )</literal></listitem>
</itemizedlist>
</para>
<para>
The first three methods are documented more completely in the XML::XPath manual
pages.
</para>
<para>
Apply templates examines the contents of the local <literal>$t</literal>
hash reference for elements names. For example, when encountering a
<literal><foo></literal> element via apply_templates, XPathScript
will try to find a transformation hash in the key
<literal>$t->{'foo'}</literal>.
</para>
<para>
Import template can be used to pull in an external XPathScript template
file. <literal>$uri</literal> should be a path to the stylesheet to be
included. The function returns an anonymous subroutine that when
executed will run the stylesheet. The anonymous subroutine takes two
arguments, which makes it ideal to plug into a
<literal>testcode</literal> entry, for example:
<informalexample>
<programlisting>
$t->{BODY}{testcode} = import_template("/xps/bodystyle.xps");
</programlisting>
</informalexample>
Inside the imported stylesheet, you will be referencing the same
<literal>$t</literal> as the parent stylesheet. You can get at the
usual <literal>testcode</literal> version of <literal>$t</literal> by
using <literal>$real_local_t</literal>.
</para>
<para>
If you want to include a stylesheet anyway (not as part of a testcode
setup), just write it as normal, and include a line like this in the
parent stylesheet:
<informalexample>
<programlisting>
import_template("/xps/bodystyle.xps")->();
</programlisting>
</informalexample>
</para>
<para>
The value in <literal>$t->{'foo'}</literal> above is a hash reference
with the following optional keys:
<itemizedlist>
<listitem>pre</listitem>
<listitem>post</listitem>
<listitem>prechildren</listitem>
<listitem>postchildren</listitem>
<listitem>prechild</listitem>
<listitem>postchild</listitem>
<listitem>showtag</listitem>
<listitem>testcode</listitem>
</itemizedlist>
</para>
<para>
If a value is <emphasis>not</emphasis> found in <literal>$t</literal> for
the current element, then the element is output verbatim, and
apply_templates performed on all its children. Except in the case where
a <literal>$t->{'*'}</literal> value exists, which is a "catchall"
transformation specification. This might be a useful place to add some
testcode to output a warning to the error log.
</para>
<para>
If a value <emphasis>is</emphasis> found in <literal>$t</literal> for
the current element then the tag itself is not displayed unless
<literal>$t->{<element_name>}{showtag}</literal> is set to a true
value.
</para>
<para>
<literal>testcode</literal> is a reference to a subroutine (often
constructed as an anonymous subroutine). The subroutine is called with
two parameters: The current node and a localised hash reference to store
new transformations for this node and this node only. The return value
from this subroutine must be one of:
<itemizedlist>
<listitem><literal>1</literal> - process this node and all children</listitem>
<listitem><literal>-1</literal> - process this node but not the children of this
node</listitem>
<listitem><literal>0</literal> - do not process this node or its children</listitem>
<listitem><literal>'string'</literal> - any string (other than
<literal>"1"</literal>, <literal>"0"</literal> or <literal>"-1"</literal>)
is equivalent to <literal>1</literal>, except rather than processing
the node's children, it processes the nodes found by executing
<literal>findnodes('string', $node)</literal> where
<literal>$node</literal> is the current node. Obviously
<literal>'string'</literal> has to be a valid XPath expression.</listitem>
</itemizedlist>
</para>
<para>
XPathScript stylesheets can be modularised using SSI #include
directives. The code in #included files is added verbatim into the
current code at the position of the include. This allows you to use this
fact to override defaults (as we saw in the first example where the
template for ulink is overridden).
</para>
</sect1>
<!-- ################################### -->
<sect1>
<title>Using XPathScript to Write XSP TagLibs</title>
<para>
XSP is an alternative server side XML programming API. It is not a
stylesheet system though - the XSP page is executed directly without a
stylesheet. XSP was originally incorporated into the <ulink
url="http://xml.apache.org/cocoon">Cocoon</ulink> application
framework, and AxKit included XSP capabilities because it's a very
interesting and useful tool.
</para>
<para>
One of the interesting things about XSP is the ability to write taglibs
using some form of stylesheet transformation language. A taglib is a
separate sheet of tags that have special meaning to your code. They can
execute external functions or simply be used in a similar way to
external parsed entities. Here's the classic example of a usage of a
taglib from the Cocoon documentation (slightly modified from the
original):
<informalexample>
<programlisting><![CDATA[
<xsp:page
language="Perl"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:example="http://www.plenix.com/DTD/XSP/Example"
>
<page title="Time of Day">
<p>
To the best of my knowledge, it's now
<!-- Substitute time of day here -->
<example:time-of-day format="%y/%m/%d %r"/>
</p>
</page>
</xsp:page>
]]></programlisting>
</informalexample>
</para>
<para>
Here the <literal><example:time-of-day></literal> tag gets converted
at run time to the current time using the <literal>strftime</literal>
format specified in the format attribute.
</para>
<para>
A taglib implementation is a stylesheet that is evaluated against this
file prior to passing it to the XSP processor. The stylesheet converts
the tags that it recognises into pure XSP code (see <ulink
url="http://xml.apache.org/cocoon/xsp.html">http://xml.apache.org/cocoon/xsp.html</ulink>
for more information on XSP). While this seems a rather redundant
feature, it allows even further separation between code and design.
Designers can just introduce these special tags, without worrying about
the logic behind them.
</para>
<para>
The Cocoon recommendation is to write taglibs using XSLT. This works
well, but the code often looks confusing. My recommendation for AxKit is
to use XPathScript. Here's our implementation of the time-of-day tag
using XPathScript:
<informalexample>
<programlisting><![CDATA[
<%
$t->{'xsp:page'}{prechildren} = <<EOXML;
<xsp:structure>
<xsp:include>POSIX</xsp:include>
</xsp:structure>
EOXML
$t->{'example:time-of-day'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} =
'<xsp:expr>
POSIX::strftime("' . findvalue('@format', $node) . '", localtime)
</xsp:expr>';
return 1;
};
%>
<%= apply_templates() %>
]]></programlisting>
</informalexample>
This is a rather trivial example of a taglib, but hopefully it
introduces the possibilities of further extending your tag library.
</para>
<para>
In order to enable this tag library, we simply make the taglib
stylesheet the first in our stylesheet cascade:
<informalexample>
<programlisting><![CDATA[
<?xml version="1.0"?>
<?xml-stylesheet type="application/x-xpathscript" href="example.taglib"?>
<?xml-stylesheet type="application/x-xsp" href="."?>
<?xml-stylesheet type="text/xsl" href="example.xsl"?>
<xsp:page
language="Perl"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:example="http://www.plenix.com/DTD/XSP/Example"
>
<page title="Time of Day">
<p>
To the best of my knowledge, it's now
<!-- Substitute time of day here -->
<example:time-of-day format="%y/%m/%d %r"/>
</p>
</page>
</xsp:page>
]]></programlisting>
</informalexample>
Note that the XSP script is executed using the stylesheet processing
instruction, with a stylesheet of ".". This stylesheet could be anything
in the case of XSP, since there is actually no stylesheet associated
with it, and the "." is merely a convention.
</para>
<para>
For comparison, here's the equivalent XSLT based taglib:
<informalexample>
<programlisting><![CDATA[
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:example="http://www.plenix.com/DTD/XSP/Example"
>
<xsl:template match="xsp:page">
<xsp:page>
<xsl:copy>
<xsl:apply-templates select="@*"/>
</xsl:copy>
<xsp:structure>
<xsp:include>POSIX</xsp:include>
</xsp:structure>
<xsl:apply-templates/>
</xsp:page>
</xsl:template>
<xsl:template match="example:time-of-day">
<xsp:expr>
POSIX::strftime("<xsl:value-of select="@format"/>", localtime)
</xsp:expr>
</xsl:template>
<xsl:template match="@*|node()" priority="-1">
<xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
</xsl:template>
</xsl:stylesheet>
]]></programlisting>
</informalexample>
Some people may find one version easier to work with than the other,
although I personally prefer the simplicity of XPathScript.
</para>
</sect1>
</article>
1.1 xml-axkit/axkit.org/docs/xsp/guide.dkb
Index: guide.dkb
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="/stylesheets/docbook_screen.xps" type="application/x-xpathscript"
title="default"?>
<?xml-stylesheet href="/stylesheets/docbook_print.xps"
type="application/x-xpathscript" title="print" alternate="yes"?>
<!DOCTYPE article [
<!ENTITY prompt "% ">
]>
<article>
<artheader>
<title>Using XSP in AxKit</title>
<author>
<firstname>Matt</firstname>
<surname>Sergeant</surname>
<affiliation>
<address><email>matt@sergeant.org</email></address>
</affiliation>
</author>
<copyright>
<year>2000</year>
<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
</copyright>
<abstract>
<para>This article covers the XSP technology within AxKit, how to
get started using XSP to create dynamic pages, and a full guide to
the XSP syntax.</para>
</abstract>
</artheader>
<sect1>
<title>Introduction</title>
<para>
XSP is a server-side technology for embedding code into XML. It belongs
to the same family of products as ASP and JSP. However XSP focuses
completely on XML, and contains methods for generating XML that make
life for the XML programmer easier. XSP is designed to work in the early
stages of the XML processing pipeline, often as the very first stage in
the processing pipeline. Generally the output from XSP goes on to
further stage processing to deliver HTML or WAP. XSP also has powerful
database tools that deliver their output in XML for further
manipulation.
</para>
<para>
A simple XSP example that generates the current time looks like this:
<informalexample>
<programlisting><![CDATA[
<xsp:page xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns="uri://axkit.org/NS/MyHomePage"
language="Perl">
<page>
<body>
<heading>Hello World</heading>
It is now: <xsp:expr>scalar localtime</xsp:expr>
</body>
</page>
</xsp:page>
]]></programlisting>
</informalexample>
</para>
<para>
And the output from that example is:
<informalexample>
<programlisting><![CDATA[
<page xmlns="uri://axkit.org/NS/MyHomePage">
<body>
<heading>Hello World</heading>
It is now: Sat Jun 24 10:46:51 2000
</body>
</page>
]]></programlisting>
</informalexample>
</para>
<para>
While this isn't going to be displayable by any current browser (unless
we're talking about client side XSLT or even CSS), AxKit pipes this into
the next stage of transformation, which is likely to be an XSLT
processor, to generate HTML or any other format.
</para>
<para>
Using the XSP constructs, like <literal><xsp:expr></literal> we can build up
complex rules and code structures to generate the desired output. XSP
includes several tags for making life easier for generating XML, as
we'll see later.
</para>
</sect1>
<sect1>
<title>The Elements of XSP</title>
<para>
We've already mentioned that XSP includes tags for generating XML, and
it should be noted that the above <literal><xsp:expr></literal> does
not generate text, but a DOM text node. In this section we'll see what
tags XSP offers to us, and later we'll see how to put them all together.
</para>
<para>
First though, its important to know a little bit about how XSP works
behind the scenes withing AxKit. Astute readers will have noted that the
<literal><xsp:page></literal> tag included a
<literal>language="Perl"</literal> attribute. This is because XSP is
language independant. While AxKit only implements XSP with the Perl
language, <ulink url="http://xml.apache.org/cocoon">Cocoon</ulink> has
implemented support for Java and Javascript, and other languages are
coming online with the Cocoon project soon.
</para>
<para>
When AxKit parses the XSP page it generates a Perl class - an object
oriented Perl module. This class can be considered to have three main
sections:
<itemizedlist>
<listitem>structure - this currently only defines libraries that your
XSP code might use, although it is left open for other structural
items</listitem>
<listitem>logic - this section is for any logic in addition to that
which you wish to embed directly within the output. This generally
includes function definitions and global variables (although see <xref
linkend="global_vars_not_global"/> for some important information on
global variables)</listitem>
<listitem>content - the main section of XSP occurs
as soon as the XSP parser sees a non-XSP tag (which is
called the "User root" tag).</listitem>
</itemizedlist>
A breakdown of how this might look to a perl programmer is as follows:
<informalexample>
<programlisting><![CDATA[
package MyModule;
# begin structure
use MyLib;
# end structure
# begin logic
sub foo {
return "bar";
}
# end logic
# begin content
sub handler {
# generate DOM tree of content output
...
}
# end content
1;
]]></programlisting>
</informalexample>
</para>
<para>
When an XSP page is executed, assuming the code is already compiled,
AxKit attempts to execute the <literal>handler()</literal> function.
</para>
<para>
Now that we know what sort of code is behind the execution of XSP, lets
get deeper into those sections.
</para>
<sect2>
<title>Structure</title>
<para>
Structure tags may appear only between the root
<literal><xsp:page></literal> tag and the user root tag. The syntax
of the structure tag is:
<informalexample>
<programlisting><![CDATA[
<xsp:structure>
<xsp:include>MyLib</xsp:include>
</xsp:structure>
]]></programlisting>
</informalexample>
</para>
<para>
This adds the following to the structure section of the compiled class:
<informalexample>
<programlisting>
use MyLib;
</programlisting>
</informalexample>
</para>
<para>
Currently <literal><xsp:include></literal> is the only tag provided
for use within the structure section.
</para>
</sect2>
<sect2>
<title>Logic</title>
<para>
The logic section is freeform, and should generally be used along with
an accompanying <literal><![CDATA[...]]></literal> declaration to
ensure that your code is not treated as XML. Note that unlike some
languages, perl may be particularly prone to having the string
<literal>"]]>"</literal> somewhere in the code, so be aware of that as
you work in CDATA sections. A simple solution is to change that to
<literal>"]] >"</literal> - note the extra space.
</para>
<para>
An example of a logic section might be the definition of a function for
providing the current time:
<informalexample>
<programlisting><![CDATA[
<xsp:structure>
<xsp:include>Time::Object</xsp:include>
</xsp:structure>
<xsp:logic><![CDATA[
sub mytime {
my ($time) = @_;
$time ||= time;
return Time::Object->new($time);
}
]]>]]><![CDATA[</xsp:logic>
]]></programlisting>
</informalexample>
</para>
</sect2>
<sect2>
<title>Content</title>
<para>
The content section consists of automatically generated code based on the
non-xsp tags in the document, along with code that has been generated
based on the xsp tags.
</para>
<para>
Currently XSP is DOM based (there are plans to make a SAX based XSP for
the obvious performance benefits, but that hasn't been realised yet),
and as such for each non-xsp entity in the XML resource, the XSP parser
generates code that will create a DOM node. When it sees a tag it
generates code to make an element node, when it sees text it generates
code to make a text node, and so on. The idea though is not to worry
about the code that the XSP parser is generating, and focus on the
output that you want.
</para>
<para>
Since the content section is where most of the work and most of the
important parts of your code will occur, we've reserved a whole section
of this document to talk about it.
</para>
</sect2>
</sect1>
<sect1>
<title>XSP's XML building tags, or "the Content section"</title>
<para>
Everything in XSP works around namespaces, so be sure you understand
them before trying to start working with XSP. A good place to learn
about namespaces is <ulink url="http://www.xml.com/">XML.com</ulink>,
where they have some excellent links to namespaces resources, and some
good articles.
</para>
<para>
As soon as XSP sees an element that does not belong to the XSP namespace
it knows to
start the "content section". This is the main part of the code that
generates XML nodes for passing to the next processing stage. Every node
prior to the user root node is special to XSP (because by definition,
they are part of the XSP namespace). It is also worth noting
here that an actual <literal><xsp:page></literal> root element is not
a requirement of XSP, however without it there's no way to create logic
or structure sections.
</para>
<sect2>
<title>Generating Elements and Attributes</title>
<para>
Any non XSP tags are generally passed on to the next processing stage
verbatim. However it is possible to use logic to determine whether or
not certain tags appear:
<informalexample>
<programlisting><![CDATA[
<xsp:logic>
if (somecondition()) {
<xsp:content>
<true_tag/>
</xsp:content>
} else {
<xsp:content>
<false_tag/>
</xsp:content>
}
</xsp:logic>
]]></programlisting>
</informalexample>
This example is based on the familiar concept that was brought to use by
ASP and similar server side scripting languages, where we must break out
of the code section in order to send tags to the browser. The
<literal><xsp:content></literal> tag allows us to temporarily break
out of the code section that is provided by
<literal><xsp:logic></literal>. Note that <literal><xsp:logic></literal> is the same tag as we
used outside of the user root element to provide class level logic.
</para>
<para>
This is rather verbose, so there's another way to achieve the same
effect, and this is using the XSP element generator tags:
<informalexample>
<programlisting><![CDATA[
<xsp:logic>
if (somecondition()) {
<xsp:element name="true_tag"/>
} else {
<xsp:element name="false_tag"/>
}
</xsp:logic>
]]></programlisting>
</informalexample>
</para>
<para>
Finally we can get <emphasis>even</emphasis> easier. Because the XML
parser knows the difference between tags and text, we can simply use the
tag on its own:
</para>
<para>
What's interesting about this is that we don't actually need to break
out of the "code" mode (the <literal><xsp:content></literal> tag
here) to generate the XML tag - the XML parser actually does the hard
work for us of figuring out what is a tag and what is Perl. Provided
that you are aware of the limitations of this, such as not being able to
use a CDATA section when we want to do this, and being aware that
<literal><</literal> and <literal>&</literal> signs need to be
escaped, it is a very powerful concept.
</para>
<para>
Building attributes is equally simple:
<informalexample>
<programlisting><![CDATA[
<xsp:content>
if (somecondition()) {
<xsp:element name="true_tag">
<xsp:attribute name="myattr">My Value</xsp:attribute>
</xsp:element>
} else {
<xsp:element name="false_tag">
<xsp:attribute name="myattr">Other Value</xsp:attribute>
</xsp:element>
}
</xsp:content>
]]></programlisting>
</informalexample>
Note how the tag to generate the attribute occurs as a child of the tag
that creates the element.
</para>
<para>
Putting this all together now, given that
<literal>somecondition()</literal> returns true, the resulting output
is:
<informalexample>
<programlisting><![CDATA[
<true_tag myattr="My Value">
</true_tag>
]]></programlisting>
</informalexample>
Not particularly interesting, until we realise that this is just one
stage in the processing pipeline, and the next stage is probably an XSLT
stylesheet that transforms <literal><true_tag></literal> into
something with meaning to the browser, such as a table or text of some
sort.
</para>
</sect2>
<sect2>
<title>Generating comments, text and processing instructions</title>
<para>
Generation of nodes is not limited to elements (and attributes). We can
use the same techniques to generate comments, text and processing
instructions. The benefits of this seem dubious, until you realise that
again we can generate these things without breaking out of the "code"
section, and just use XML tags.
</para>
<para>
The tags to generate these types of nodes follow the same pattern as
generating elements, so I'll cover this with a single example:
<informalexample>
<programlisting><![CDATA[
<xsp:content>
# some perl code ...
<xsp:text>Hello World</xsp:text>
<xsp:comment>Why am I here?</xsp:comment>
<xsp:pi target="process-me">With some data</xsp:pi>
</xsp:content>
]]></programlisting>
</informalexample>
The result from the above is:
<informalexample>
<programlisting><![CDATA[
Hello World
<!--Why am I here?-->
<?process-me With some data?>
]]></programlisting>
</informalexample>
</para>
</sect2>
</sect1>
<sect1>
<title>Real World Example</title>
</sect1>
<sect1>
<title>Conclusion</title>
<para>
We've seen the capabilities of XML Server Pages, and we've seen a simple
real world example, however we've only really touched upon the power of
XSP. The real power actually comes in when we get into
<emphasis>taglibs</emphasis>. Taglibs allow us to design custom tags
that we can insert into our pages, that provide us with extended
functionality. This allows us to finally reach towards the ubiquitous
goal of having developers work on the taglibs, and designers work on
XSP's by inserting your company's custom tags.
</para>
<para>
Taglibs provide the real power of XSP, and yet their implementation is
not rocket science, in fact anyone who has already been reading other
AxKit documentation will be intimately familiar with the tools to
generate taglibs: <emphasis>stylesheets</emphasis>. We use stylesheets
to build taglibs by developing stylesheets that transform our custom
tags, <emphasis>and just our custom tags</emphasis> into valid XSP code,
we can leverage our current development tools to build dynamic XML
applications. Please read the section on the SQL taglib to see how we
can build XSP's that connect to relational databases.
</para>
</sect1>
</article>
1.1 xml-axkit/axkit.org/docs/xsp/index.xml
Index: index.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="/stylesheets/webpage_html.xps"
type="application/x-xpathscript" title="default"?>
<webpage>
<head>
<title>AxKit - XSP Documentation</title>
</head>
<body>
<section>
<title>XSP Documentation</title>
<para>
XSP is an XML dialect for executing code in the web server that
utilises embedded code, extensible tag libraries, and
transformation techniques.
</para>
<para>
XSP was originally invented for the <ulink
url="http://xml.apache.org/cocoon/">Cocoon project</ulink>. By
using their standard, scripts that are built for Cocoon are easier
to port to AxKit (although its worth noting that Cocoon does not
support XSP's written in Perl, nor does AxKit support XSP's
written in Java at the time of this writing).
</para>
<para>
Please see the links in the sidebar to find the rest of the XSP
documentation.
</para>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/examples/cv.css
Index: cv.css
===================================================================
1.1 xml-axkit/axkit.org/examples/cv.xml
Index: cv.xml
===================================================================
<?xml version='1.0'?>
<?xml-stylesheet
href="cv.xps"
type="application/x-xpathscript"
title="default"
alternate="no"?>
<?xml-stylesheet
href="cv-text.xps"
type="application/x-xpathscript"
title="text"
alternate="yes"?>
<resume>
<person>
<persname>
<title>Mr</title>
<forename>Matt</forename>
<middlename>David</middlename>
<surname>Sergeant</surname>
</persname>
<address>
<line>161 Waverley Crescent</line>
<line>Livingston</line>
<line>West Lothian</line>
<postcode>EH54 8JT</postcode>
<country>Scotland</country>
</address>
<phone type="Home">01506 419 813</phone>
<phone type="Mobile">07957 593 007</phone>
<phone type="Mobile(2)">07718 933 770</phone>
<phone type="Fax">01506 419 813</phone>
<email>matt@sergeant.org</email>
<sms>matt-sms@sergeant.org</sms>
<web_site>http://sergeant.org</web_site>
<dob>1974/04/01</dob>
</person>
<skills>
<area name="Languages">
<item time="5 years">Perl 5</item>
<item time="3 years">XML</item>
<item time="6 months">Java (JDK 1.2, Visual J++)</item>
<item>JavaScript</item>
<item time="3 years">ASP</item>
<item time="5 years">HTML</item>
<item time="3 years">(Visual) C++ (all versions)</item>
</area>
<area name="Protocols">
<item>HTTP</item>
<item>CGI</item>
<item>DCOM</item>
<item>SMTP</item>
<item>TCP/IP</item>
<item>NNTP</item>
</area>
<area name="Software">
<item>Linux (all versions)</item>
<item>Apache (1.3)</item>
<item>mod_perl</item>
<item>CVS, RCS, VSS and PVCS</item>
<item>NT (all versions)</item>
<item>IIS (3 & 4)</item>
</area>
<area name="Databases">
<item time="2 years">Oracle</item>
<item time="3 years">Sybase</item>
<item time="2 years">MS SQL Server</item>
<item time="1 year">MySQL</item>
<item time="1 year">PostgreSQL</item>
<item time="3 years">Access</item>
</area>
</skills>
<education>
<establishment>
<name>University of Durham, St Cuthbert's Society</name>
<address>
<line>12 South Bailey</line>
<line>Durham</line>
<postcode>DH1 3EE</postcode>
<country>England</country>
</address>
<datefrom>1992/09</datefrom>
<dateto>1996/06</dateto>
<qualification>
<name>BSc Computer Science</name>
<grade>2nd Class, Level 2</grade>
</qualification>
</establishment>
<establishment>
<name>Huntington Comprehensive School</name>
<address>
<line>Huntington</line>
<line>York</line>
<postcode></postcode>
<country>England</country>
</address>
<datefrom></datefrom>
<dateto></dateto>
<qualification>
<name>A Level Maths</name>
<grade>A</grade>
</qualification>
<qualification>
<name>A Level Physics</name>
<grade>A</grade>
</qualification>
<qualification>
<name>A Level Economics</name>
<grade>D</grade>
</qualification>
</establishment>
</education>
<experience>
<employee>
<name>AxKit.com Limited</name>
<address>
<line>161 Waverley Crescent</line>
<line>Livingston</line>
<line>West Lothian</line>
<postcode>EH54 8JT</postcode>
<country>Scotland</country>
</address>
<datefrom>2000/08</datefrom>
<dateto>Present</dateto>
<role>Chief Technology Officer and Developer</role>
<details>
CTO and lead developer on a project to build a leading edge
open source XML application server and content management
system. AxKit is now one of the leading products in the open
source community for building XML backed web sites, and has been
used to successfully build many web sites providing alternate
views for pages (such as WAP or HTML) and a friendly XML based
development environment. The technologies built and designed
by myself include XPathScript and the core engine inside AxKit.
I also ported Cocoon's XSP technology to Perl for use in AxKit.
</details>
</employee>
<employee>
<name>O'Reilly and Associates</name>
<address>
<line>90 Sherman Street</line>
<line>Cambridge</line>
<line>MA</line>
<postcode>02140</postcode>
<country>United States of America</country>
</address>
<datefrom>2000/03</datefrom>
<dateto>Present</dateto>
<role>Lead Developer</role>
<details>
Developing a commercial Perl based Web Discussion system, with
Web, Email and NNTP interfaces. The project was a complete, from
scratch development that I personally designed, architected and
developed (with one further developer helping later). The
technologies used included Object Oriented (OO) Perl5, mod_perl,
DBI, MySQL, PostgreSQL, Oracle, Sybase, module design, Apache, multi
threaded kernel development, Email and NNTP message parsing
(Internet RFC's 821, 977, 1341, 1641 and 2980), and much more.
</details>
</employee>
<employee>
<name>Deutsche Bank - Wood MacKenzie</name>
<address>
<line>74-77 Queen Street</line>
<line>Edinburgh</line>
<country>Scotland</country>
</address>
<datefrom>1999/10</datefrom>
<dateto>2000/03</dateto>
<role>Perl Consultant</role>
<details>
This role involved consulting on the development of a
product using the MediaSurface application server
product. Involving all aspects of development and
project management. In particular working towards
extending MediaSurface to have better templating
facilities and XML support.
</details>
</employee>
<employee>
<name>British Broadcasting Corporation</name>
<address>
<line>Bush House</line>
<line>Aldwich</line>
<line>London</line>
<country>England</country>
</address>
<datefrom>1999/07/01</datefrom>
<dateto>1999/08/31</dateto>
<role>Web Developer and Web Consultant</role>
<details>
Investigating several new technologies for the BBC's high profile web site
(the most used content based web site in the UK) and developing modules for
use on the site. One module is now used on every page on the BBC web site.
Investigations into new technology covered search engines, personalisation
technology, database backends and mod_perl. All work was conducted using Perl.
</details>
</employee>
<employee>
<name>Ericsson Mobile Communications (UK) Limited</name>
<address>
<line>The Keytech Centre</line>
<line>Ashwood Way</line>
<line>Basingstoke</line>
<line>Hants</line>
<postcode>RG23 8BG</postcode>
<country>England</country>
</address>
<datefrom>1998/04/06</datefrom>
<dateto>1999/06/30</dateto>
<role>Contract Developer</role>
<details>
Developing an MMI for a "black box" device. Using Java over a tiny web server. Working
initially with Visual J++, then with Sun JDK. Following on, working in Development IT
doing internet/intranet development. Leading the development teams to create interactive
business functionality. Key developments include large amounts of XML work, designing
XML search tools (based on XQL and XML-QL), XML re-formatting tools (as a pre-cursor
to XSL - see my web site), online XML editing protocols, and indexing techniques. The
culmination of this work has been a global XML-based timesheet application, to be
deployed all over the world to Ericsson employees. Some of this work has been made
available to members of the Perl community via the Perl-XML mailing list and CPAN.
</details>
</employee>
<employee>
<name>O'Reilly and Associates</name>
<address>
<line>90 Sherman Street</line>
<line>Cambridge</line>
<line>MA</line>
<postcode>02140</postcode>
<country>United States of America</country>
</address>
<datefrom>1998/12</datefrom>
<dateto>Present</dateto>
<role>Author</role>
<details>
Co-Authoring a new O'Reilly book called "XML applications with Perl". This will
detail ways to use
perl to process and generate XML. It will included details on XML::Parser, and the
many modules that use it. Details of my CGI to XML gateway, are also included and
some of my other XML modules. This work is in my "spare" time!
[Addendum: This book has now been cancelled due to lack of time
for all three authors. This item remains here for reference]
</details>
</employee>
<employee>
<name>O'Reilly and Associates</name>
<address>
<line>90 Sherman Street</line>
<line>Cambridge</line>
<line>MA</line>
<postcode>02140</postcode>
<country>United States of America</country>
</address>
<datefrom>1999/03</datefrom>
<dateto>1999/04</dateto>
<role>System Architect / Developer</role>
<details>
Developing the NNTP module for O'Reilly's WebBoard suite of software (see
http://webboard.oreilly.com). This involved designing a new system from the
ground up to do NNTP to a backend database, implementing authentication and
ensuring performance was scalable. WebBoard 4.0 was released in June 1999.
</details>
</employee>
<employee>
<name>Integrated Sales Systems Limited</name>
<address>
<line>Regal House</line>
<line>70 London Road</line>
<line>Twickenham</line>
<postcode>TW1 3QS</postcode>
<country>England</country>
</address>
<datefrom>1996/06/05</datefrom>
<dateto>1998/04/01</dateto>
<role>Software Engineer, Senior Software Engineer, Team Leader, Project Leader</role>
<details>
Employeed again as a graduate I rapidly rose through the company, taking on
projects such as a major upgrade for Searle Pharmaceuticals (mostly development work),
Designing and implementing Comms security over RAS (working with the Win32 RAS and TCP/IP
functions in C++), Internationalisation in the Oxygen
application, performance improvements and internal tools. While working in
Client Development I worked both on and off site for major corporations including
Monsanto Searle Pharmaceuticals, Schering Plough, Lombard Business Finance, Astra
Pharmaceuticals and Royal & Sun Alliance. A major project
while at ISS was developing their intranet. This rapidly grew from a project
running on my own workstation, into a dedicated web server, serving up thousands
of documents, and running some of the core infrastructure at the company. The server
was a test bed for many new web tools - beta testing ASP (when it was called Denali),
working with HiP Communications to bring Perl to Win32, and testing out the new IIS 4.
</details>
</employee>
<employee>
<name>Integrated Sales Systems Limited</name>
<address>
<line>Regal House</line>
<line>70 London Road</line>
<line>Twickenham</line>
<postcode>TW1 3QS</postcode>
<country>England</country>
</address>
<datefrom>1995/06/01</datefrom>
<dateto>1995/09/01</dateto>
<role>Junior Software Engineer</role>
<details>
Working over the summer at this lively new software house. I worked in small
teams helping to realise their first Windows version of their aging DOS
application. I worked in Visual C++ and T-SQL. I also designed a database consolidation
routine called "Behemoth".
</details>
</employee>
</experience>
<speaking>
<engagement>
<conference>O'Reilly Open Source Software Convention 2001, San
Diego</conference>
<talk>AxKit - XML Application Serving with mod_perl</talk>
<talk>mod_perl as an HTTP RPC Daemon</talk>
<talk>Exception Handling in mod_perl</talk>
<talk>Cross Database Perl Applications</talk>
<talk>Orchard: a new API for XML</talk>
</engagement>
<engagement>
<conference>Open Source Web Solutions 2001, Stockholm</conference>
<talk>Developing XML applications with Perl and AxKit</talk>
<talk>Building a web portal with XML and Perl</talk>
</engagement>
<engagement>
<conference>XML Dev Con 2001 New York</conference>
<talk>Building XML Based Web Sites with Open Source Tools</talk>
</engagement>
<engagement>
<conference>Apache Con 2001 San Jose</conference>
<talk>AxKit - the XML Application server for Apache</talk>
</engagement>
<engagement>
<conference>XML Dev Con Europe 2001 London</conference>
<talk>Building XML Based Web Sites with Open Source Tools</talk>
</engagement>
<engagement>
<conference>XML Dev Con Fall 2000 San Jose</conference>
<talk>Building XML Based Web Sites with Open Source Tools</talk>
</engagement>
<engagement>
<conference>XML Dev Con Fall 2000 San Jose</conference>
<talk>Generating XML From Form Data</talk>
</engagement>
<engagement>
<conference>Apache Con Europe 2000 London</conference>
<talk>AxKit - the XML Application server for Apache</talk>
</engagement>
<engagement>
<conference>XML Dev Con 2000 New York</conference>
<talk>Generating XML From Form Data</talk>
</engagement>
<engagement>
<conference>XML Dev Con 2000 New York</conference>
<talk>XML Little Languages (compilation, optimisation and execution
of XML languages)</talk>
</engagement>
</speaking>
<training>
<course>
<name>Oracle Performance Tuning</name>
<date></date>
<trainee>Oracle Training</trainee>
</course>
<course>
<name>Oracle Advanced SQL</name>
<date></date>
<trainee>Oracle Training</trainee>
</course>
<course>
<name>Object Oriented Design</name>
<date></date>
<trainee>QA Training</trainee>
</course>
</training>
</resume>
1.1 xml-axkit/axkit.org/examples/cv.xps
Index: cv.xps
===================================================================
<html>
<head>
<%
my $name = join(' ',
findvalue('/resume/person/persname/forename'),
findvalue('/resume/person/persname/surname'));
%>
<title><%= $name %> - CV</title>
<link rel="stylesheet" type="text/css" href="cv.css">
</head>
<body bgcolor="white">
<h1 align="center"><%= $name %></h1>
<center>
<hr width="100%"></hr>
<i>
<%
my ($addr) = findnodes('/resume/person/address');
print join(' / ', findvalues('line', $addr), findvalue('postcode', $addr), findvalue('country', $addr) );
%>
<br><b>Tel:</b>
<%
foreach my $tel (findnodes('/resume/person/phone')) {
print " <i>", findvalue('@type', $tel), ":</i> ";
print findvalue('./text()', $tel);
}
%>
<br>
<b>e-mail:</b><a href="mailto:<%= findvalue('/resume/person/email') %>"><%= findvalue('/resume/person/email') %></a>
/
<b>gsm sms:</b><a href="mailto:<%= findvalue('/resume/person/sms') %>"><%= findvalue('/resume/person/sms') %></a>
<p><b>DoB:</b> <%= findvalue('/resume/person/dob') %>
</center>
<br>
<b><font size="+1">Education</font></b>
<center><hr width="100%"></center>
<!-- show most recent education only -->
<%
my ($edu) = findnodes('/resume/education/establishment[1]');
%>
<table>
<tr>
<td width="25"> </td>
<td width="400"><%= findvalue('name', $edu) %></td>
<td><i>Graduated:</i> <%= findvalue('dateto', $edu) %></td>
</tr>
<tr>
<td></td>
<td><%= findvalue('qualification/name', $edu) %></td>
<td></td>
</tr>
</table>
<br>
<b><font size="+1">Key Skills</font></b>
<hr width="100%">
<table width="100%" cellpadding="1" cellspacing="0" border="1">
<tr valign="top">
<%
my @areas = findnodes('/resume/skills/area');
foreach my $area (@areas) {
%>
<td width="<%= 100 / @areas %>%">
<table width="100%" cellpadding="1" cellspacing="0" border="0">
<tr><th><%= findvalue('@name', $area) %></th></tr>
<%
foreach my $skill (findnodes('item', $area)) {
%>
<tr>
<td><%= findvalue('./text()', $skill) %></td>
</tr>
<%
}
%>
</table>
</td>
<%
}
%>
</tr>
</table>
<br>
<b><font size="+1">Current Work</font></b>
<hr width="100%">
<table>
<tr>
<td width="25"> </td>
<td width="400"><b><%= findvalue('/resume/experience/employee[1]/name') %></b></td>
</tr>
</table>
<br>
<b><font size="+1">Work Experience</font></b>
<hr width="100%">
<table>
<%
foreach my $employee (findnodes('/resume/experience/employee[position() <= 7]')) {
%>
<tr>
<td width="25"> </td>
<td><b><%= findvalue('name', $employee) %></b></td>
<td><b><%= findvalue('datefrom', $employee) %> - <%= findvalue('dateto', $employee) %></b></td>
</tr>
<tr>
<td width="25"> </td>
<td colspan="2"><%= findvalue('details', $employee) %><br></td>
</tr>
<%
}
%>
</table>
<b><font size="+1">Speaking Engagements</font></b>
<hr width="100%">
As one of the world's leading Perl and XML developers, I am invited to
speak at many conferences each year. These are the talks I have given or
will be giving in the coming year:
<table>
<%
$t->{'talk'}{showtag} = 0;
foreach my $engagement (findnodes('/resume/speaking/engagement')) {
%>
<tr>
<td width="25"> </td>
<td><%= $engagement->findvalue('conference') %> <i>(<%= join(', ', map { apply_templates($_) } $engagement->findnodes('talk')) %>)</i></td>
</tr>
<%
}
%>
</table>
<b><font size="+1">Training</font></b>
<hr width="100%">
<table>
<%
foreach my $course (findnodes('/resume/training/course')) {
%>
<tr>
<td width="25"> </td>
<td><%= findvalue('name', $course) %> <i>(<%= findvalue('trainee', $course) %>)</i></td>
</tr>
<%
}
%>
</table>
</body>
</html>
1.1 xml-axkit/axkit.org/examples/cv_example.xml
Index: cv_example.xml
===================================================================
<?xml version="1.0"?>
<?xml-stylesheet href="/stylesheets/webpage_html.xps" type="application/x-xpathscript"?>
<webpage>
<head>
<title>CV Example using XPathScript</title>
</head>
<body>
<section>
<title>Finally I get around to updating my CV!</title>
<p>
People ask me at least once a week to see my updated CV, but I keep it
on a different ISP's server and never get around to updating it. This
isn't because I don't want people to see it - but because I'm always
working on something far more interesting :-)
</p>
<p>
However I figured it makes a good example for XPathScript. So here is
<a href="cv.xps">the XPathScript source code</a>. The original XML file is available by
adding the querystring passthru=1 to the XML file. Link <a
href="cv.xml?passthru=1">here</a>. The passthru option is another
example of an AxKit plugin. The translated version created by this
stylesheet is available <a href="cv.xml">here</a>.
</p>
</section>
</body>
</webpage>
1.1 xml-axkit/axkit.org/examples/docbook.dkb
Index: docbook.dkb
===================================================================
<?xml version="1.0"?>
<?xml-stylesheet href="/stylesheets/docbook_screen.xps"
type="application/x-xpathscript"
media="screen"
title="default" ?>
<?xml-stylesheet href="/stylesheets/docbook_screen_fancy.xps"
type="application/x-xpathscript"
media="screen"
title="fancy"
alternate="yes" ?>
<?xml-stylesheet href="/stylesheets/docbook_print.xps"
type="application/x-xpathscript"
media="screen"
title="print"
alternate="yes" ?>
<?xml-stylesheet href="/stylesheets/docbook_handheld.xps"
type="application/x-xpathscript"
media="handheld"
title="default"?>
<article>
<artheader>
<title>A complete DocBook XPathScript Example</title>
<author>
<firstname>Matt</firstname>
<surname>Sergeant</surname>
<affiliation>
<address><email>matt@sergeant.org</email></address>
</affiliation>
</author>
<copyright>
<year>2000</year>
<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
</copyright>
<!--
<pubdate role="rcs">$Date: 2002/01/13 20:45:09 $</pubdate>
<releaseinfo>$Id: docbook.dkb,v 1.1 2002/01/13 20:45:09 matts Exp $</releaseinfo>
-->
<abstract>
<para>A complete example of a docbook stylesheet using AxKit</para>
</abstract>
</artheader>
<sect1>
<title>Introduction</title>
<para>
This aims to be a complete example of rendering DocBook documents
using <ulink url="http://axkit.org/">AxKit</ulink> and
XPathScript. It offers several different styles to render the document
in: Normal, Fancy and Printer. And also offers an alternative media to
render the document for: handheld. How you select the different styles
and media will be detailed later in this document.
</para>
<para>
First a disclaimer: I'm a complete "newbie" to docbook. I also haven't
tried rendering this document with the standard docbook tools, so I may
have some elements wrong. If I do, you have my appologies. Hopefully you
might be encouraged enough by this to fix the problems I've introduced
and do it right, as it were.
</para>
<para>
Note: This example remains unfinished. Please see the "Introduction to
AxKit" on the AxKit web site for a current better example of using
DocBook.
</para>
</sect1>
</article>
1.1 xml-axkit/axkit.org/examples/resume.dtd
Index: resume.dtd
===================================================================
<!-- Resume/CV DTD -->
<!--
This is my first shot at a resume dtd. It hopefully encompasses quite a bit of
detail so that you can create complex or simple resume's. It also should cover
a fair bit of international issues. Let me know if it doesn't.
Changes:
Added time attribute to skill items, so you can say how long
you've had that skill.
Fixed spelling of professional.
Made phone type attribute IMPLIED instead of REQUIRED.
Ditto for publication.
Matt Sergeant, matt@sergeant.org
-->
<!ELEMENT resume ( person,
skills?,
education?,
professional_organisations?,
publications?,
experience?,
training?,
interests?,
references?)>
<!ELEMENT person (persname,address,phone*,email*,web_site?,dob?)>
<!ELEMENT persname (title,forename,middlename?,surname)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT forename (#PCDATA)>
<!ELEMENT middlename (#PCDATA)>
<!ELEMENT surname (#PCDATA)>
<!ELEMENT address (line*,postcode?,country?)>
<!ELEMENT line (#PCDATA)>
<!ELEMENT postcode (#PCDATA)>
<!ELEMENT country (#PCDATA)>
<!ELEMENT phone (#PCDATA)>
<!ATTLIST phone
type CDATA #IMPLIED>
<!ELEMENT email (#PCDATA)>
<!ELEMENT web_site (#PCDATA)>
<!ELEMENT dob (#PCDATA)> <!-- Date of Birth -->
<!ELEMENT skills (area)*>
<!ELEMENT area (item)*>
<!ATTLIST area name CDATA #REQUIRED>
<!ELEMENT item (#PCDATA)>
<!ATTLIST item time CDATA #IMPLIED> <!-- Length of time you've had this skill - e.g. "6 months" -->
<!ELEMENT education (establishment)*>
<!ELEMENT establishment (name,address,datefrom,dateto,level?,qualification*)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT datefrom (#PCDATA)>
<!ELEMENT dateto (#PCDATA)>
<!ELEMENT qualification (name,grade?)>
<!ELEMENT grade (#PCDATA)>
<!ELEMENT professional_organisations (organisation)*>
<!ELEMENT organisation (name,offices_held,datefrom)>
<!ELEMENT offices_held (#PCDATA)>
<!ELEMENT publications (publication)>
<!ELEMENT publication (name,details)>
<!ATTLIST publication
type CDATA #IMPLIED> <!-- Specify book, article, etc -->
<!ELEMENT details ANY> <!-- Expecting markup here? -->
<!ELEMENT experience (employee)*>
<!ELEMENT employee (name,address,datefrom,dateto,role,details)>
<!ELEMENT role (#PCDATA)>
<!ELEMENT training (course)*>
<!ELEMENT course (name,date,trainee?,level?)>
<!ELEMENT date (#PCDATA)>
<!ELEMENT trainee (#PCDATA)>
<!ELEMENT level (#PCDATA)>
<!ELEMENT interests (#PCDATA)> <!-- Someone can fill out structure here if they want -->
<!ELEMENT references (reference)*>
<!ELEMENT reference (person,relationship,address)>
<!ELEMENT relationship (#PCDATA)> <!-- Describe relationship to this person -->
1.1 xml-axkit/axkit.org/img/arrow.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/arrow_sel.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/banner2.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/banner3.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/banner_axkit.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/banner_bg.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/banner_end.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/bg.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/bullet.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/corner_logo.jpg
<<Binary file>>
1.1 xml-axkit/axkit.org/img/corner_logo.png
<<Binary file>>
1.1 xml-axkit/axkit.org/img/nav_bot.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/nav_left.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/nav_right.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/nav_top.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/pix.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/rcorner_br.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/rcorner_wh.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/img/vsep.gif
<<Binary file>>
1.1 xml-axkit/axkit.org/stylesheets/axkit.css
Index: axkit.css
===================================================================
body {
color: #333;
margin: 0px;
font-family: Arial,Helvetica,Helv,sans-serif;
font-size: 12pt;
}
p {
font-family: Arial,Helvetica,Helv,sans-serif;
font-size: 12pt;
}
.by {
font-family: Verdana, sans-serif;
font-size: 10pt;
color: black;
}
H3, .hd {
font-family: Verdana,Helvetica,Helv,sans-serif;
font-size: 15pt;
font-weight: bold;
}
.hd2 {
font-family: Verdana,Helvetica,Helv,sans-serif;
font-size: 14pt;
font-weight: bold;
}
.hd3 {
font-family: Verdana,Helvetica,Helv,sans-serif;
font-size: 12pt;
font-weight: bold;
}
.hd4 {
font-family: Verdana,Helvetica,Helv,sans-serif;
font-size: 10pt;
font-weight: bold;
}
@media screen {
A.plain:hover {
color: transparent;
background: transparent;
}
a:hover {
color: #fc6;
text-decoration: none;
}
}
.toplevel {
color: #306;
font-family: Arial,Helvetica,Helv,sans-serif;
font-size: 11pt;
font-weight: bold;
}
.sublevel {
font-family: Arial,Helvetica,Helv,sans-serif;
font-size: 10pt;
font-weight: bold;
}
.toponomic {
font-family: Arial,Helvetica,Helv,sans-serif;
font-size: 10pt;
font-weight: bold;
}
.copyright {
font-family: Arial,Helvetica,Helv,sans-serif;
font-size: 8pt;
font-style: italic;
}
1.1 xml-axkit/axkit.org/stylesheets/axkit_html.css
Index: axkit_html.css
===================================================================
/*
Generic Layout
*/
BODY {
background: #fff;
font-family: Arial, Helvetica, sans-serif;
}
1.1 xml-axkit/axkit.org/stylesheets/body_print_html.xps
Index: body_print_html.xps
===================================================================
<body
bgcolor="#ffffff" text="#333333" link="#9966cc" vlink="#330066" alink="#eeddff"
marginheight="0" marginwidth="0"
topmargin="0" leftmargin="0">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr style="line-height: 0;">
<td align="left" valign="top" width="190" background="/img/banner_bg.gif"><a
href="http://axkit.org/"><img src="/img/banner3.gif"
width="190" height="84" border="0" vspace="0" hspace="0"
alt="AxKit.org - XML Application Server" /></a></td>
<% my $img_id = int rand(1_000_000); %>
<td align="right" valign="top" width="100%" background="/img/banner_bg.gif"><a
class="plain"
href="http://kansas.valueclick.com/redirect?host=hs0185469&size=468x60&b=<%= $img_id %>&v=0"
target="_top"><img vspace="0" hspace="0" border="1" align="absmiddle"
width="468" height="60" alt="Please visit our sponsor"
src="http://kansas.valueclick.com/cycle?host=hs0185469&size=468x60&b=<%= $img_id %>&noscript=1"
/></a><img src="/img/banner_end.gif"
width="26" height="84" border="0"
vspace="0" hspace="0" alt="" align="absmiddle" /></td>
</tr>
</table>
1.1 xml-axkit/axkit.org/stylesheets/body_tag_html.xps
Index: body_tag_html.xps
===================================================================
<body
bgcolor="#ffffff" text="#333333" link="#9966cc" vlink="#330066" alink="#eeddff"
marginheight="0" marginwidth="0"
topmargin="0" leftmargin="0" background="/img/bg.gif">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr style="line-height: 0;">
<td align="left" valign="top" width="190" background="/img/banner_bg.gif"><img
src="/img/banner3.gif" width="190" height="84" border="0" vspace="0" hspace="0" alt="AxKit.org [logo curtesy of http://xml.com]" /></td>
<% my $img_id = int rand(1_000_000); %>
<td align="right" valign="top" width="100%" background="/img/banner_bg.gif"><a
class="plain"
href="http://kansas.valueclick.com/redirect?host=hs0185469&size=468x60&b=<%= $img_id %>&v=0"
target="_top"><img vspace="0" hspace="0" border="1" align="absmiddle"
width="468" height="60" alt="Please visit our sponsor"
src="http://kansas.valueclick.com/cycle?host=hs0185469&size=468x60&b=<%= $img_id %>&noscript=1"
/></a><img src="/img/banner_end.gif"
width="26" height="84" border="0"
vspace="0" hspace="0" alt="" align="absmiddle" /></td>
</tr>
</table>
1.1 xml-axkit/axkit.org/stylesheets/docbook_example.xml
Index: docbook_example.xml
===================================================================
<?xml version="1.0"?>
<?xml-stylesheet href="/docbook_screen.xps"
type="application/x-xpathscript"
media="screen"
title="default" ?>
<?xml-stylesheet href="/docbook_screen_fancy.xps"
type="application/x-xpathscript"
media="screen"
title="fancy"
alternate="yes" ?>
<?xml-stylesheet href="/docbook_print.xps"
type="application/x-xpathscript"
media="screen"
title="print"
alternate="yes" ?>
<?xml-stylesheet href="/docbook_handheld.xps"
type="application/x-xpathscript"
media="handheld"
title="default"?>
<article>
<artheader>
<title>A complete DocBook XPathScript Example</title>
<author>
<firstname>Matt</firstname>
<surname>Sergeant</surname>
<affiliation>
<address><email>matt@sergeant.org</email></address>
</affiliation>
</author>
<copyright>
<year>2000</year>
<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
</copyright>
<!--
<pubdate role="rcs">$Date: 2002/01/13 20:45:10 $</pubdate>
<releaseinfo>$Id: docbook_example.xml,v 1.1 2002/01/13 20:45:10 matts Exp $</releaseinfo>
-->
<abstract>
<para>A complete example of a docbook stylesheet using the
Apache XML Delivery Toolkit</para>
</abstract>
</artheader>
<sect1>
<title>Introduction</title>
<para>
This aims to be a complete example of rendering DocBook documents
using the <ulink url="http://xml.sergeant.org/axkit/">AxKit</ulink> and
XPathScript. It offers several different styles to render the document
in: Normal, Fancy and Printer. And also offers an alternative media to
render the document for: handheld. How you select the different styles
and media will be detailed later in this document.
</para>
<para>
First a disclaimer: I'm a complete "newbie" to docbook. I also haven't
tried rendering this document with the standard docbook tools, so I may
have some elements wrong. If I do, you have my appologies. Hopefully you
might be encouraged enough by this to fix the problems I've introduced
and do it right, as it were.
</para>
</sect1>
</article>
1.1 xml-axkit/axkit.org/stylesheets/docbook_print.xps
Index: docbook_print.xps
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
<html>
<!--#include file="spacer.xps"-->
<!--#include file="docbook_tags.xps"-->
<%
my %links;
my $linkid = 0;
$t->{'ulink'}{testcode} = sub {
my $node = shift;
my $t = shift;
my $url = findvalue('@url', $node);
if (!exists $links{$url}) {
$linkid++;
$links{$url} = $linkid;
}
my $link_number = $links{$url};
$t->{pre} = "<i><a href=\"$url\">";
$t->{post} = " [$link_number]</a></i>";
return 1;
};
%>
<head>
<title><%= findvalue('/article/artheader/title/text()') %></title>
<link rel="stylesheet" href="/stylesheets/axkit.css" type="text/css" />
</head>
<!--#include file="body_print_html.xps"-->
<table width="100%">
<tr>
<td width="10"><%= spacer(10,0) %></td>
<td width="*" style="font-size: small;">
<%
# display title/TOC page
print apply_templates('/article/artheader/*');
%>
<hr>
<%
# display all pages
foreach my $section (findnodes("/article/sect1")) {
print apply_templates($section);
}
%>
<h1>List of Links</h1>
<table border="1">
<th>URL</th>
<%
for my $link (sort {$links{$a} <=> $links{$b}} keys %links) {
%>
<tr>
<td><%= "[$links{$link}] $link" %></td>
</tr>
<% } %>
</table>
<%
if (@footnotes) {
print "<hr><table cellspacing=\"0\" cellpadding=\"3\">";
print "<caption>Footnotes</caption>";
my $i = 1;
foreach my $footnote (@footnotes) {
print "<tr><td valign=\"top\"><p>$i</p></td><td valign=\"top\"><div class=\"footnote\">$footnote</div></td></tr>";
$i++;
}
print "</table><hr>";
}
%>
</td>
</tr>
</table>
</body>
</html>
1.1 xml-axkit/axkit.org/stylesheets/docbook_screen.xps
Index: docbook_screen.xps
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
<html>
<!--#include file="sidebar_html.xps"-->
<!--#include file="spacer.xps"-->
<%
use Apache::Request;
my $apr = Apache::Request->instance($r);
my $sect = $apr->param('section');
$r->no_cache(1);
my $this_page = $r->uri;
my ($prev, $next);
my $sects = findvalue('count(/article/sect1)');
$sects = "$sects";
if ($sects > 1) {
if ($sect >= $sects) {
$prev = $sects - 1;
}
elsif ($sect <= 1) {
$next = 2;
}
else {
$prev = $sect - 1;
$next = $sect + 1;
}
}
%>
<!--#include file="docbook_tags.xps"-->
<%
$t->{'xref'}{testcode} = sub {
my ($node, $t) = @_;
my $id = findvalue('@linkend', $node);
my ($dest) = findnodes("id('$id')/title");
my $sect = findvalue("count(ancestor::sect1/preceding-sibling::sect1) + 1", $dest);
$t->{pre} = "<b><a href=\"$this_page?section=$sect#";
$t->{pre} .= $id . '">';
$t->{pre} .= findvalue("id('$id')/title/text()");
$t->{post} = '</a></b>';
return -1;
};
%>
<head>
<title><%= findvalue('/article/artheader/title/text()') %></title>
<link rel="stylesheet" href="/stylesheets/axkit.css" type="text/css" />
</head>
<!--#include file="body_tag_html.xps"-->
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<!-- navbar -->
<td align="left" valign="top" width="172">
<!--#include file="navbar_html.xps"-->
</td>
<!-- whitespace -->
<td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
<!-- content -->
<td align="left" valign="top" style="font-size: small;">
<%
if (!$sect) {
# display title/TOC page
print apply_templates('/article/artheader/*');
my $page = 0;
foreach my $sect (findnodes('/article/sect1')) {
$page++;
print "<a href=\"$this_page?section=$page\">";
print findvalue('title/text()', $sect);
print "</a><br>\n";
}
%><hr><%
}
else {
%>
<table width="100%">
<tr>
<td align="left">
<%
if ($prev) {
%>
<a href="<%=$this_page%>?section=<%=$prev%>">Prev</a>
<% } else {
%>
Prev
<% } %>
</td><td align="center"><a href="<%=$this_page%>">Top</a></td><td align="right">
<%
if ($next) {
%>
<a href="<%=$this_page%>?section=<%=$next%>">Next</a>
<% } else { %>
Next
<% } %>
</td></tr></table>
<hr>
<%
# display particular page
foreach my $section (findnodes("/article/sect1[$sect]")) {
# should only be 1
print apply_templates($section);
}
if (@footnotes) {
print "<table cellspacing=\"0\" cellpadding=\"3\">";
print "<caption>Footnotes</caption>";
my $i = 1;
foreach my $footnote (@footnotes) {
print "<tr><td valign=\"top\"><p>$i</p></td><td valign=\"top\"><div class=\"footnote\">$footnote</div></td></tr>";
$i++;
}
print "</table><hr>";
}
}
if ($sect) {
%>
<table width="100%">
<tr>
<td align="left">
<%
if ($prev) {
%>
<a href="<%=$this_page%>?section=<%=$prev%>">Prev</a>
<% } else {
%>
Prev
<% } %>
</td><td align="center"><a href="<%=$this_page%>">Top</a></td><td align="right">
<%
if ($next) {
%>
<a href="<%=$this_page%>?section=<%=$next%>">Next</a>
<% } else { %>
Next
<% } %>
</td></tr></table>
<hr>
<% }
%>
<table width="100%"><tr><td align="right">
<a href="<%=$this_page%>?style=print">Printer Friendly</a>
</td></tr>
<tr><td align="right">
<a href="<%=$this_page%>?passthru=1">Raw XML</a>
</td></tr></table>
</td>
<!-- whitespace -->
<td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
</tr>
</table>
</body>
</html>
1.1 xml-axkit/axkit.org/stylesheets/docbook_tags.xps
Index: docbook_tags.xps
===================================================================
<%
# support ulink URL's
$t->{'ulink'}{testcode} = sub {
my $node = shift;
my $t = shift;
$t->{pre} = "<i><a href=\"" .
findvalue('./@url', $node) . "\">";
$t->{post} = '</a></i>';
return 1;
};
$t->{'title'}{testcode} = sub {
my $node = shift;
my $t = shift;
if (findvalue('parent::blockquote', $node)) {
$t->{pre} = "<b>";
$t->{post} = "</b><br>\n";
}
elsif (findvalue('parent::artheader', $node)) {
$t->{pre} = "<h1>";
$t->{post} = "</h1>";
}
else {
my $parent = findvalue('name(..)', $node);
my ($level) = $parent =~ m/sect(\d+)$/;
$t->{pre} = "<h$level>";
$t->{post} = "</h$level>";
}
return 1;
};
$t->{'attribution'}{pre} = "<i>";
$t->{'attribution'}{post} = "</i><br>\n";
$t->{'para'}{pre} = '<p>';
$t->{'para'}{post} = '</p>';
$t->{'author'}{pre} = '<p>';
$t->{'firstname'}{pre} = '';
$t->{'surname'}{pre} = ' ';
$t->{'surname'}{post} = "<p>\n";
$t->{'email'}{testcode} = sub {
my $node = shift;
$t->{'email'}{pre} = "<a href=\"mailto:" . findvalue('./text()', $node) . "\">";
$t->{'email'}{post} = "</a>";
return 1;
};
$t->{'copyright'}{pre} = "Copyright \xC2\xA9 ";
$t->{'trademark'}{post} = "<super>TM</super>";
$t->{'holder'}{testcode} = sub {
my $node = shift;
$t->{'holder'}{pre} = "<a href=\"" . findvalue('./@role', $node) . "\">";
$t->{'holder'}{post} = "</a>";
return 1;
};
$t->{'abstract'}{pre} = "<p>";
$t->{'sect1'}{post} = "<hr>";
#$XML::XPath::Debug = 1;
$t->{'sect1'}{testcode} = sub {
my ($node, $t) = @_;
if (my $id = findvalue('@id', $node)) {
$t->{pre} = "<a name=\"$id\"></a>";
}
return 1;
};
$t->{'sect2'}{testcode} = $t->{'sect1'}{testcode};
$t->{'sect3'}{testcode} = $t->{'sect1'}{testcode};
$t->{'sect4'}{testcode} = $t->{'sect1'}{testcode};
# lists
$t->{'itemizedlist'}{pre} = '<ul>';
$t->{'itemizedlist'}{post} = '</ul>';
$t->{'orderedlist'}{pre} = '<ol>';
$t->{'orderedlist'}{post} = '</ol>';
$t->{'listitem'}{testcode} = sub {
my ($node, $t2) = @_;
$t2->{pre} = '<li>';
$t2->{post} = '</li>';
return 1;
# return '*[1]/node() | *[position() > 1]';
};
$t->{'procedure'}{pre} = '<ol>';
$t->{'procedure'}{post} = '</ol>';
$t->{'step'}{pre} = '<li>';
$t->{'step'}{post} = '</li>';
# examples, listings, etc.
$t->{'programlisting'}{pre} = '<table width="100%"><tr><td bgcolor="#DDDDDD"><pre>';
$t->{'programlisting'}{post} = '</pre></td></tr></table>';
# need to support <prompt> and other tags within screen...
$t->{'screen'} = $t->{'programlisting'};
$t->{'userinput'}{pre} = '<b>';
$t->{'userinput'}{post} = '</b>';
$t->{'replaceable'}{pre} = '<i>';
$t->{'replaceable'}{post} = '</i>';
$t->{'emphasis'}{pre} = '<i>';
$t->{'emphasis'}{post} = '</i>';
$t->{'application'}{pre} = '<b>';
$t->{'application'}{post} = '</b>';
$t->{'manvolnum'}{pre} = '(';
$t->{'manvolnum'}{post} = ')';
$t->{'option'}{pre} = '<tt>';
$t->{'option'}{post} = '</tt>';
$t->{'filename'} = $t->{'option'};
$t->{'literal'} = $t->{'option'};
# references
$t->{'anchor'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} = '<a name="#';
$t->{pre} .= findvalue('@id', $node);
$t->{pre} .= '">';
$t->{post} = '</a>';
return 1;
};
$t->{'xref'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} = '<b><a href="#';
my $id = findvalue('@linkend', $node);
$t->{pre} .= $id . '">';
$t->{pre} .= findvalue("id('$id')/title/text()");
$t->{post} = '</a></b>';
return -1;
};
$t->{'link'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} = '<a href="#';
$t->{pre} .= findvalue('@linkend', $node);
$t->{pre} .= '">';
$t->{post} = '</a>';
return 1;
};
my @footnotes;
$t->{'footnote'}{testcode} = sub {
my ($node, $t2) = @_;
push @footnotes, apply_templates('node()', $node);
$t2->{pre} = '<sup>' . scalar(@footnotes) . '</sup>';
return -1;
};
%>
1.1 xml-axkit/axkit.org/stylesheets/error.xps
Index: error.xps
===================================================================
<html>
<head><title>AxKit Error</title></head>
<body>
<h1>AxKit Error</h1>
<tt>
<%= findvalue('/error/msg/text()') %>
</tt>
<hr>
<pre>
<% foreach my $node (findnodes('/error/stack_trace/bt')) { %>
<%= $node->findvalue('file') . ' :: ' . $node->findvalue('line') %>
<br>
<% } %>
</pre>
</body>
</html>
1.1 xml-axkit/axkit.org/stylesheets/error.xsl
Index: error.xsl
===================================================================
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
>
<xsl:output method="html"/>
<xsl:template match="bt">
<tr>
<xsl:if test="position() mod 2">
<xsl:attribute name="bgcolor">#eeeeee</xsl:attribute>
</xsl:if>
<td>
<xsl:value-of select="@level"/>
</td>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="file|line">
<td>
<xsl:apply-templates/>
</td>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<title>Server Error</title>
<style type="text/css">
h2, h3, h4, p, i, td, th
{
font-family: Verdana, Helvetica, sans-serif;
}
th
{
color: white;
}
</style>
</head>
<body bgcolor="white">
<h2>Server Error</h2>
<p>
The following error occurred: <xsl:value-of select="/error/msg"/>
</p>
<h3>Stack Trace:</h3>
<table border="0" cellpadding="3" cellspacing="0">
<tr bgcolor="blue"><th>Level</th><th>File</th><th>Line #</th></tr>
<xsl:apply-templates select="/error/stack_trace/*"/>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
1.1 xml-axkit/axkit.org/stylesheets/irc.xps
Index: irc.xps
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
<html>
<!--#include file="sidebar_html.xps"-->
<!--#include file="rdf_html.xps"-->
<!--#include file="spacer.xps"-->
<%
$t->{'dc:description'}{showtag} = 0;
$t->{'link'}{pre} = '<a href="{@url}">';
$t->{'link'}{post} = '</a>';
$t->{'xlink'}{pre} = '<a href="{@uri}">';
$t->{'xlink'}{post} = '</a>';
$t->{'blog:comments'}{showtag} = 0;
$t->{'rdf:Seq'}{testcode} = sub {'rdf:li'};
$t->{'rdf:li'}{showtag} = 0;
$t->{'rdf:li'}{pre} = '<div class="by">{dc:creator} : ';
$t->{'rdf:li'}{post} = '</div>';
$t->{'dc:creator'}{testcode} = sub {0};
%>
<head>
<title><%= findvalue('/rdf:RDF/channel[1]/title') %></title>
<meta name="Author" content="Matt Sergeant" />
<link rel="stylesheet" href="/stylesheets/axkit.css" type="text/css" />
</head>
<!--#include file="body_tag_html.xps"-->
<!-- navbar and content positioning table -->
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<!-- navbar -->
<td align="left" valign="top" width="172">
<!--#include file="navbar_html.xps"-->
</td>
<!-- whitespace -->
<td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
<!-- content -->
<td align="left" valign="top">
<table width="100%" border="0" cellspacing="5" cellpadding="0">
<!-- toponomic marker -->
<tr>
<td align="left" valign="top">
<span class="toponomic">
<a href="http://axkit.org/">http://axkit.org/</a>
::
<%
{
my $uri = $r->uri;
$uri =~ s/^\///;
my ($file) = $uri =~ /([^\/]+)$/;
$uri =~ s/\Q$file\E$//;
my $local = '';
foreach my $part (split('/', $uri)) {
$local .= "/$part/index.xml";
my $title = findvalue("document('$local')/descendant::title[1]");
if ($title eq 'Index') {
print $part, " :: ";
}
else {
print "<a href=\"$local\">", $title, "</a> :: ";
}
$local =~ s/\/index\.xml$//;
}
if ($file ne 'index.xml') {
my $location = "/$file";
$location = "/$uri$file" if $uri;
print "<a href=\"$location\">", findvalue("document('$location')/descendant::title[1]"), "</a> :: ";
}
}
%>
</span>
</td>
</tr>
<!-- line -->
<tr>
<td bgcolor="#660099" style="line-height: 0;"><%= spacer(20,1) %></td>
</tr>
<tr>
<td align="left" valign="top">
<table width="100%" border="0" cellspacing="5" cellpadding="0">
<tr>
<!-- main text -->
<td align="left" valign="top">
<h1>Daily Churn</h1>
<p>
This page contains URLs and comments talked about on the
<a href="/support.xml">AxKit IRC channel</a>. The information is recorded
by an IRC bot written using POE, and written out to an RSS 1.0 file, using
RDF and dublin core metadata.
</p>
<%
my $count = 30;
foreach my $item (findnodes("/rdf:RDF/item[position() <= $count]")) {
if ($item->findvalue('link')) {
%>
<div class="hd"><%= $item->findvalue('dc:creator') %> : <a href="<%= $item->findvalue('link') %>"><%= $item->findvalue('title') %></a></div>
<%
}
else {
%>
<div class="hd"><%= $item->findvalue('title') %></div>
<%
}
%>
<%= apply_templates($item->findnodes('blog:comments')) %>
<br /><br />
<% } %>
<!-- end main -->
</td>
</tr>
</table>
</td>
</tr>
<!-- line -->
<tr>
<td bgcolor="#660099" style="line-height: 0;"><%= spacer(20,1) %></td>
</tr>
<!-- copyright -->
<tr>
<td align="left" valign="top">
<span class="copyright">These pages are copyright © 2000 AxKit.com Ltd</span>
</td>
</tr>
</table>
</td>
<!-- whitespace -->
<td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
</tr>
</table>
</body>
</html>
1.1 xml-axkit/axkit.org/stylesheets/navbar_html.xps
Index: navbar_html.xps
===================================================================
<table width="170" border="0" cellspacing="0" cellpadding="0">
<tr>
<td colspan="3" style="line-height: 0;"><img
src="/img/vsep.gif" width="170" height="25" border="0" vspace="0" hspace="0" alt="--sep--" /></td>
</tr>
<tr>
<td colspan="3" style="line-height: 0;"><img
src="/img/nav_top.gif" width="170" height="12" border="0" vspace="0" hspace="0" alt="Start Navigation" /></td>
</tr>
<tr>
<td background="/img/nav_left.gif"><%= spacer(8,20) %></td>
<td bgcolor="#330066">
<!-- actual navigation -->
<%= apply_templates(scalar findnodes('document("/sidebar.xml")')) %>
</td>
<td background="/img/nav_right.gif"><%= spacer(8,20) %></td>
</tr>
<tr>
<td colspan="3" style="line-height: 0;"><img
src="/img/nav_bot.gif" width="170" height="12" border="0" vspace="0" hspace="0" alt="End Navigation" /></td>
</tr>
</table>
1.1 xml-axkit/axkit.org/stylesheets/rdf_html.xps
Index: rdf_html.xps
===================================================================
<%
$t->{'rss'}{testcode} = sub {
my ($node, $t) = @_;
$t->{'pre'} = q(<table width="190" border="0" cellspacing="0" cellpadding="0">
<tr><td align="center" bgcolor="#330066">
<table width="100%" border="0" cellspacing="1" cellpadding="0">
<tr>
<td align="center" bgcolor="#9966cc"><table border="0" cellpadding="0"
cellspacing="0"><tr><td bgcolor="#660099">) . spacer(1,1) . q(</td><td align="center" bgcolor="white"><a href=");
$t->{'pre'} .= findvalue('channel/image/link/text()', $node);
$t->{'pre'} .= q("><img src=");
$t->{'pre'} .= findvalue('channel/image/url/text()', $node);
$t->{'pre'} .= q(" border="0" vspace="0" hspace="0" alt=");
$t->{'pre'} .= findvalue('channel/image/title/text()', $node);
$t->{'pre'} .= q(" /></a></td><td bgcolor="#660099">) . spacer(1,1) . q(</td></tr></table></td>
</tr>
<tr>
<td>
<table width="100%" border="0" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
<tr>
<td align="left" valign="top">);
$t->{'post'} = q(</td>
</tr>
</table>
</td>
</tr>
</table>
</td></tr></table>);
return 'channel/item[position() < 5]';
};
$t->{'item'}{testcode} = sub {
my ($node, $t) = @_;
if ($node->getParentNode->getName ne 'channel') {
return 1;
}
$t->{'pre'} = '<p><img src="/img/bullet.gif" width="10" height="10" border="0" vspace="0" hspace="0" alt=">>" />
<a href="' . findvalue('link/text()', $node) . '"><b>' .
findvalue('title/text()', $node) . '</b></a> - ' .
findvalue('description/text()', $node);
$t->{'post'} = "</p>\n\n";
return -1;
};
%>
1.1 xml-axkit/axkit.org/stylesheets/sidebar_html.xps
Index: sidebar_html.xps
===================================================================
<%
$t->{'sb:sidebar'}{pre} = q(<table width="154" border="0" cellspacing="0" cellpadding="0" bgcolor="#330066">
<tr>
<td>
<table width="100%" border="0" cellspacing="1" cellpadding="3">);
$t->{'sb:sidebar'}{post} = '</table></td></tr></table>';
$t->{'sb:section'}{pre} = q(<tr>);
$t->{'sb:section'}{post} = q(</span></td></tr>);
$t->{'sb:title'}{pre} = q(<td align="left" valign="top" bgcolor="#eeddff">
<span class="toplevel"><b>);
$t->{'sb:title'}{post} = q(</b></span></td></tr> <tr><td align="left" valign="top" bgcolor="#ffffff">
<span class="sublevel">);
$t->{'sb:item'}{testcode} = sub {
my ($node, $t) = @_;
my $title = findvalue('sb:title/text()', $node);
my $url = findvalue('sb:url/text()', $node);
my $sel = '';
$sel = '_sel' if $r->uri eq $url;
$t->{pre} = qq(<a href="$url"><img src="/img/arrow$sel.gif" width="16" height="14" border="0"
vspace="0" hspace="0" alt="" align="absmiddle" />$title</a><br />);
return -1;
};
my $sbsubpre = q(<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr><td>) . spacer(10, 1) . q(</td><td><span class="sublevel">); # "
$t->{'sb:subsection'}{testcode} = sub {
my ($node, $t) = @_;
if ($node->findvalue('@expand') eq "no") {
my $title = findvalue('sb:title/text()', $node);
my $url = findvalue('sb:url/text()', $node);
my $sel = '';
$sel = '_sel' if $r->uri eq $url;
$t->{pre} = qq(<a href="$url"><img src="/img/arrow$sel.gif" width="16" height="14" border="0"
vspace="0" hspace="0" alt="" align="absmiddle" />$title</a><br />);
if ($r->uri eq $url) {
$t->{pre} .= $sbsubpre;
return 'sb:item';
}
$t->{post} = '';
return -1;
}
else {
return 1;
}
};
$t->{'sb:subsection'}{pre} = $sbsubpre;
$t->{'sb:subsection'}{post} = q(</span></td></tr></table>);
%>
1.1 xml-axkit/axkit.org/stylesheets/spacer.xps
Index: spacer.xps
===================================================================
<%
sub spacer {
my ($width, $height, $align) = @_;
return '<img src="/img/pix.gif"
width="'.$width.'" height="'.$height.'"' . ($align ? " align=\"$align\"" : "") .
' border="0" vspace="0" hspace="0"
alt="" />';
}
%>
1.1 xml-axkit/axkit.org/stylesheets/webpage_html.xps
Index: webpage_html.xps
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
<html>
<!--#include file="sidebar_html.xps"-->
<!--#include file="rdf_html.xps"-->
<!--#include file="spacer.xps"-->
<%
$t->{'section'}{showtag} = 0;
$t->{'title'}{testcode} = sub {
my ($node, $t) = @_;
if ($node->getParentNode->getName eq 'section') {
$t->{pre} = '<h1>';
$t->{post} = '</h1>';
}
elsif ($node->getParentNode->getName eq 'subsection') {
$t->{pre} = '<h2>';
$t->{post} = '</h2>';
}
else {
$t->{showtag} = 1;
}
return 1;
};
$t->{'ulink'}{testcode} = sub {
my ($node, $t) = @_;
my $url = $node->getAttribute('url');
$t->{pre} = "<a href=\"$url\">";
$t->{post} = "</a>";
return 1;
};
$t->{'para'}{pre} = '<p>';
$t->{'para'}{post} = '</p>';
$t->{'emphasis'}{pre} = '<i>';
$t->{'emphasis'}{post} = '</i>';
%>
<head>
<title><%= findvalue('/webpage/head/title/text()') %></title>
<meta name="Author" content="Matt Sergeant" />
<link rel="stylesheet" href="/stylesheets/axkit.css" type="text/css" />
</head>
<!--#include file="body_tag_html.xps"-->
<!-- navbar and content positioning table -->
<table width="100%" border='0' cellspacing='0' cellpadding='0'>
<tr>
<!-- navbar -->
<td align="left" valign="top" width='172'>
<!--#include file="navbar_html.xps"-->
</td>
<!-- whitespace -->
<td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
<!-- content -->
<td align="left" valign="top">
<table width="100%" border="0" cellspacing="5" cellpadding="0">
<!-- toponomic marker -->
<tr>
<td align="left" valign="top">
<span class="toponomic">
<a href="http://axkit.org/">http://axkit.org/</a>
::
<%
{
my $uri = $r->uri;
$uri =~ s/^\///;
my ($file) = $uri =~ /([^\/]+)$/;
$uri =~ s/\Q$file\E$//;
my $local = '';
foreach my $part (split('/', $uri)) {
$local .= "/$part/index.xml";
my $title = findvalue("document('$local')/descendant::title[1]");
if ($title eq 'Index') {
print $part, " :: ";
}
else {
print "<a href=\"$local\">", $title, "</a> :: ";
}
$local =~ s/\/index\.xml$//;
}
if ($file ne 'index.xml') {
my $location = "/$file";
$location = "/$uri$file" if $uri;
print "<a href=\"$location\">", findvalue("document('$location')/descendant::title[1]"), "</a> :: ";
}
}
%>
</span>
</td>
</tr>
<!-- line -->
<tr>
<td bgcolor="#660099" style="line-height: 0;"><%= spacer(20,1) %></td>
</tr>
<tr>
<td align="left" valign="top">
<table width="100%" border="0" cellspacing="5" cellpadding="0">
<tr>
<!-- main text -->
<td align="left" valign="top">
<%= apply_templates('/webpage/body/*') %>
</td>
<%
if ($r->uri eq '/index.xml') {
%>
<td align="left" valign="top" width="170">
<%= apply_templates('document("news.xml")') %>
</td>
<% } %>
</tr>
</table>
</td>
</tr>
<!-- line -->
<tr>
<td bgcolor="#660099" style="line-height: 0;"><%= spacer(20,1) %></td>
</tr>
<!-- copyright -->
<tr>
<td align="left" valign="top">
<span class="copyright">These pages are copyright © 2000 AxKit.com Ltd</span>
</td>
</tr>
</table>
</td>
<!-- whitespace -->
<td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
</tr>
</table>
</body>
</html>
1.1 xml-axkit/axkit.org/stylesheets/new/rdf_html.xps
Index: rdf_html.xps
===================================================================
<%
$t->{'rss'}{testcode} = sub {
my ($node, $t) = @_;
warn "Executing RSS code!\n";
$t->{'pre'} = q(<table width='190' border='0' cellspacing='0' cellpadding='0'>
<tr><td align='center' bgcolor='#330066'>
<table width='100%' border='0' cellspacing='1' cellpadding='0'>
<tr>
<td align='center' bgcolor='#9966cc'><table border='0' cellpadding='0'
cellspacing='0'><tr><td bgcolor='#660099'>) . spacer(1,1) . q(</td><td bgcolor='white'><a href=');
$t->{'pre'} .= findvalue('channel/image/link/text()', $node);
$t->{'pre'} .= q('><img src=');
$t->{'pre'} .= findvalue('channel/image/url/text()', $node);
$t->{'pre'} .= q(' border='0' vspace='0' hspace='0' alt=');
$t->{'pre'} .= findvalue('channel/image/title/text()', $node);
$t->{'pre'} .= q(' /></a></td><td bgcolor='#660099'>) . spacer(1,1) . q(</td></tr></table></td>
</tr>
<tr>
<td>
<table width='100%' border='0' cellspacing='0' cellpadding='5' bgcolor='#ffffff'>
<tr>
<td align='left' valign='top'>);
$t->{'post'} = q(</td>
</tr>
</table>
</td>
</tr>
</table>
</td></tr></table>);
return 'channel/item[position() < 5]';
};
$t->{'item'}{testcode} = sub {
my ($node, $t) = @_;
if ($node->getParentNode->getName ne 'channel') {
return 1;
}
$t->{'pre'} = '<p><img src="/img/bullet.gif" width="10" height="10" border="0" vspace="0" hspace="0" alt=">>" />
<a href="' . findvalue('link/text()', $node) . '"><b>' .
findvalue('title/text()', $node) . '</b></a> - ' .
findvalue('description/text()', $node);
$t->{'post'} = "</p>\n\n";
return -1;
};
%>
1.1 xml-axkit/axkit.org/stylesheets/new/sidebar_html.xps
Index: sidebar_html.xps
===================================================================
<%
$t->{'sb:sidebar'}{pre} = q(<table width='154' border='0' cellspacing='0' cellpadding='0' bgcolor='#330066'>
<tr>
<td>
<table width='100%' border='0' cellspacing='1' cellpadding='3'>);
$t->{'sb:sidebar'}{post} = '</table></td></tr></table>';
$t->{'sb:section'}{pre} = q(<tr>);
$t->{'sb:section'}{post} = q(</td></tr>);
$t->{'sb:title'}{pre} = q(<td align='left' valign='top' bgcolor='#eeddff'>
<span class='toplevel'><b>);
$t->{'sb:title'}{post} = q(</b></span></td></tr> <tr><td align='left' valign='top' bgcolor='#ffffff'>
<span class='sublevel'>);
$t->{'sb:item'}{testcode} = sub {
my ($node, $t) = @_;
my $title = findvalue('sb:title/text()', $node);
my $url = findvalue('sb:url/text()', $node);
my $sel = '';
$sel = '_sel' if $r->uri eq $url;
$t->{pre} = "<a href=\"$url\"><img src='/img/arrow$sel.gif' width='16' height='14' border='0'
vspace='0' hspace='0' alt='' align='absmiddle' />$title</a><br />";
return -1;
};
$t->{'sb:subsection'}{testcode} = sub {
my ($node, $t) = @_;
if (findvalue('@expand', $node) eq "no") {
my $title = findvalue('sb:title/text()', $node);
my $url = findvalue('sb:url/text()', $node);
my $sel = '';
$sel = '_sel' if $r->uri eq $url;
$t->{pre} = "<a href=\"$url\"><img src=\"/img/arrow$sel.gif\" width='16' height='14' border='0'
vspace='0' hspace='0' alt='' align='absmiddle' />$title</a><br />";
$t->{post} = '';
return -1;
}
else {
return 1;
}
};
$t->{'sb:subsection'}{pre} = q(<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr><td width="10">) . spacer(10, 1) . q(</td><td>);
$t->{'sb:subsection'}{post} = q(</td></tr></table>);
%>
1.1 xml-axkit/axkit.org/stylesheets/new/webpage_html.xps
Index: webpage_html.xps
===================================================================
<!--#include file="sidebar_html.xps"-->
<!--#include file="rdf_html.xps"-->
<%
$t->{'section'}{showtag} = 0;
$t->{'title'}{testcode} = sub {
my ($node, $t) = @_;
if ($node->getParentNode->getName eq 'section') {
$t->{pre} = '<h1>';
$t->{post} = '</h1>';
}
elsif ($node->getParentNode->getName eq 'subsection') {
$t->{pre} = '<h2>';
$t->{post} = '</h2>';
}
else {
$t->{showtag} = 1;
}
return 1;
};
$t->{'ulink'}{testcode} = sub {
my ($node, $t) = @_;
my $url = $node->getAttribute('url');
$t->{pre} = "<a href=\"$url\">";
$t->{post} = "</a>";
return 1;
};
$t->{'para'}{pre} = '<p>';
$t->{'para'}{post} = '</p>';
$t->{'emphasis'}{pre} = '<i>';
$t->{'emphasis'}{post} = '</i>';
sub spacer {
my ($width, $height, $align) = @_;
return "<img src='/img/pix.gif'
width='$width' height='$height'" . ($align ? " align='$align'" : '') .
" border='0' vspace='0' hspace='0'
alt=''>";
}
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title><%= findvalue('/webpage/head/title/text()') %></title>
<meta name='Author' content='Matt Sergeant'>
<link rel='stylesheet' href='/stylesheets/axkit.css' type='text/css' />
</head>
<body
bgcolor='#ffffff' text='#333333' link='#9966cc' vlink='#330066' alink='#eeddff'
marginheight='0' marginwidth='0'
topmargin='0' leftmargin='0' background='/img/bg.gif'>
<!-- banner table -->
<table width='100%' border='0' cellspacing='0' cellpadding='0'>
<tr style='line-height: 0;'>
<td align='left' valign='top' width='352' background='/img/banner_bg.gif'><img
src='/img/banner2.gif' width='352' height='84' border='0' vspace='0' hspace='0' alt='AxKit.org [logo curtesy of http://xml.com]' /></td>
<td align='right' valign='top' width='100%' background='/img/banner_bg.gif'><img
src='/img/banner_end.gif' width='26' height='84' border='0' vspace='0' hspace='0' alt='' /></td>
</tr>
</table>
<!-- navbar and content positioning table -->
<table width='100%' border='0' cellspacing='0' cellpadding='0'>
<tr>
<!-- navbar -->
<td align='left' valign='top' width='170'>
<table width='100%' border='0' cellspacing='0' cellpadding='0'>
<tr>
<td colspan='3' style='line-height: 0;'><img
src='/img/vsep.gif' width='170' height='25' border='0' vspace='0' hspace='0' alt='--sep--' /></td>
</tr>
<tr>
<td colspan='3' style='line-height: 0;'><img
src='/img/nav_top.gif' width='170' height='12' border='0' vspace='0' hspace='0' alt='Start Navigation' /></td>
</tr>
<tr>
<td background='/img/nav_left.gif'><%= spacer(8,20) %></td>
<td bgcolor='#330066'>
<!-- actual navigation -->
<%= apply_templates('document("/sidebar_new.xml")') %>
</td>
<td background='/img/nav_right.gif'><%= spacer(8,20) %></td>
</tr>
<tr>
<td colspan='3' style='line-height: 0;'><img
src='/img/nav_bot.gif' width='170' height='12' border='0' vspace='0' hspace='0' alt='End Navigation' /></td>
</tr>
</table>
</td>
<!-- whitespace -->
<td align='left' valign='top' width='10'><%= spacer(10,1) %></td>
<!-- content -->
<td align='left' valign='top'>
<table width='100%' border='0' cellspacing='5' cellpadding='0'>
<!-- toponomic marker -->
<tr>
<td align='left' valign='top'>
<span class='toponomic'>
<a href="http://axkit.org/">http://axkit.org/</a>
::
<%
{
my $uri = $r->uri;
$uri =~ s/^\///;
my ($file) = $uri =~ /([^\/]+)$/;
$uri =~ s/\Q$file\E$//;
my $local = '';
foreach my $part (split('/', $uri)) {
$local .= "/$part/index.xml";
my $title = findvalue("document('$local')//title[1]/text()");
if ($title eq 'Index') {
print $part, " :: ";
}
else {
print "<a href=\"$local\">", , "</a> :: ";
}
$local =~ s/\/index\.xml$//;
}
if ($file ne 'index.xml') {
my $location = "/$file";
$location = "/$uri$file" if $uri;
print "<a href=\"$location\">", findvalue("document('$location')//title[1]/text()"), "</a> :: ";
}
}
%>
</span>
</td>
</tr>
<!-- line -->
<tr>
<td bgcolor='#660099' style='line-height: 0;'><%= spacer(20,1) %></td>
</tr>
<tr>
<td align='left' valign='top'>
<table width='100%' border='0' cellspacing='5' cellpadding='0'>
<tr>
<!-- main text -->
<td align='left' valign='top'>
<%= apply_templates('/webpage/body/*') %>
</td>
<%
if ($r->uri eq '/index.xml') {
%>
<td align='left' valign='top' width='170'>
<%= apply_templates('document("news.xml")') %>
</td>
<% } %>
</tr>
</table>
</td>
</tr>
<!-- line -->
<tr>
<td bgcolor='#660099' style='line-height: 0;'><%= spacer(20,1) %></td>
</tr>
<!-- copyright -->
<tr>
<td align='left' valign='top'>
<span class='copyright'>These pages are copyright © 2000 AxKit.com Ltd</span>
</td>
</tr>
</table>
</td>
<!-- whitespace -->
<td align='left' valign='top' width='10'><%= spacer(10,1) %></td>
</tr>
</table>
</body>
</html>
1.1 xml-axkit/examples/.htaccess
Index: .htaccess
===================================================================
SetHandler perl-script
PerlHandler AxKit
AxAddStyleMap application/x-xpathscript Apache::AxKit::Language::XPathScript
AxAddStyleMap text/xsl Apache::AxKit::Language::Sablot
1.1 xml-axkit/examples/default.notxslt
Index: default.notxslt
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<html xmlns:t="http://sergeant.org/notxslt"
xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title><t:select match="/page/head/title/text()"/></title>
</head>
<body bgcolor="white">
<h1><t:select match="/page/head/title/text()"/></h1>
<t:exec match="set_var('title_no', 0)"/>
<t:for-each match="/page/body/section">
<t:exec match="set_var('title_no', $title_no + 1)"/>
<t:select match="concat('<a href="#', $title_no, '">')"/>
<t:select match="./title/text()"/>
<t:select match="'</a>'"/>
<br class="hack"/>
<t:exec match="set_var('subtitle_no', 0)"/>
<t:for-each match="./subsection">
<t:exec match="set_var('subtitle_no', $subtitle_no + 1)"/>
<t:select match="concat(' - <a href="#', $title_no, '.', $subtitle_no, '">')"/>
<t:select match="./title/text()"/>
<t:select match="'</a>'"/>
<br class="hack"/>
</t:for-each>
</t:for-each>
<t:exec match="set_var('title_no', 0)"/>
<t:for-each match="/page/body/section">
<t:exec match="set_var('title_no', $title_no + 1)"/>
<t:select match="concat('<a name="', $title_no, '"></a>')"/>
<h2><t:select match="./title/text()"/></h2>
<t:for-each match="./title">
<t:exec match="set_var('title_pos', position())"/>
</t:for-each>
<t:select match="./*[(position() > 2) and ( name() != 'subsection' )]"/>
<t:exec match="set_var('subtitle_no', 0)"/>
<t:for-each match="./subsection">
<t:exec match="set_var('subtitle_no', $subtitle_no + 1)"/>
<t:select match="concat('<a name="', $title_no, '.', $subtitle_no, '"></a>')"/>
<h3><t:select match="./title/text()"/></h3>
<t:select match="./*[position() > 2]"/>
<br class="hack"/>
</t:for-each>
</t:for-each>
<br class="hack"/>
<br class="hack"/>
<div align="center">
<small>This page is copyright Fastnet Software Ltd, 2000.
Contact <a href="mailto:matt@sergeant.org">Matt Sergeant</a> for details.</small>
<br class="hack"/>
<small>This page is automatically generated using XML and XPath and a team of small rodents</small>
</div>
</body>
</html>
1.1 xml-axkit/examples/default.xps
Index: default.xps
===================================================================
<%
$t->{'a'}{pre} = '<i>';
$t->{'a'}{post} = '</i>';
$t->{'a'}{showtag} = 1;
my ($i, $j);
$t->{'title'}{testcode} =
sub {
my $node = shift;
if (findvalue('parent::section = true()', $node)) {
$i++; $j = 0;
$t->{'title'}{pre} = "<a name=\"$i\"></a><h2>";
$t->{'title'}{post} = '</h2>';
}
elsif (findvalue('parent::subsection = true()', $node)) {
$j++;
$t->{'title'}{pre} = "<a name=\"$i\.$j\"></a><h3>";
$t->{'title'}{post} = '</h3>';
}
else {
$t->{'title'}{pre} = '<title>';
$t->{'title'}{post} = '</title>';
}
return 1;
};
$t->{'section'}{post} = '<p>';
$t->{'subsection'}{pre} = '<blockquote>';
$t->{'subsection'}{post} = '</blockquote><br>';
%><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<%= apply_templates('/page/head/title') %>
</head>
<body bgcolor="white">
<h1><%= findvalue('/page/head/title/text()') %></h1>
<!-- table of contents -->
<%
my $x = 0;
foreach my $section (findnodes('/page/body/section')) {
$x++;
print "<a href=\"#$x\">", findvalue('./title/text()', $section), "</a><br>\n";
my $y = 0;
foreach my $subsect (findnodes('./subsection', $section)) {
$y++;
print " - <a href=\"#$x.$y\">", findvalue('./title/text()', $subsect), "</a><br>\n";
}
}
%>
<!-- Main document body -->
<%= apply_templates('/page/body/section') %>
<!-- Footer -->
<br>
<div align="center">
<small>This page is copyright Fastnet Software Ltd, 2000.
Contact <a href="mailto:matt@sergeant.org">Matt Sergeant</a>
for details and availability.</small><br>
<small>This page was generated by XPathScript, using the
<a href="http://xml.sergeant.org/axkit/"
>Apache XML Delivery Toolkit</a>.</small>
</div>
</body>
</html>
1.1 xml-axkit/examples/docbook.dkb
Index: docbook.dkb
===================================================================
<?xml version="1.0"?>
<?xml-stylesheet href="/docbook_screen.xps"
type="application/x-xpathscript"
media="screen"
title="default" ?>
<?xml-stylesheet href="/docbook_screen_fancy.xps"
type="application/x-xpathscript"
media="screen"
title="fancy"
alternate="yes" ?>
<?xml-stylesheet href="/docbook_print.xps"
type="application/x-xpathscript"
media="screen"
title="print"
alternate="yes" ?>
<?xml-stylesheet href="/docbook_handheld.xps"
type="application/x-xpathscript"
media="handheld"
title="default"?>
<article>
<artheader>
<title>A complete DocBook XPathScript Example</title>
<author>
<firstname>Matt</firstname>
<surname>Sergeant</surname>
<affiliation>
<address><email>matt@sergeant.org</email></address>
</affiliation>
</author>
<copyright>
<year>2000</year>
<holder role="mailto:matt@sergeant.org">Fastnet Software Ltd</holder>
</copyright>
<!--
<pubdate role="rcs">$Date: 2002/01/13 20:45:11 $</pubdate>
<releaseinfo>$Id: docbook.dkb,v 1.1 2002/01/13 20:45:11 matts Exp $</releaseinfo>
-->
<abstract>
<para>A complete example of a docbook stylesheet using the
Apache XML Delivery Toolkit</para>
</abstract>
</artheader>
<sect1>
<title>Introduction</title>
<para>
This aims to be a complete example of rendering DocBook documents
using the <ulink url="http://xml.sergeant.org/axkit/">AxKit</ulink> and
XPathScript. It offers several different styles to render the document
in: Normal, Fancy and Printer. And also offers an alternative media to
render the document for: handheld. How you select the different styles
and media will be detailed later in this document.
</para>
<para>
First a disclaimer: I'm a complete "newbie" to docbook. I also haven't
tried rendering this document with the standard docbook tools, so I may
have some elements wrong. If I do, you have my appologies. Hopefully you
might be encouraged enough by this to fix the problems I've introduced
and do it right, as it were.
</para>
</sect1>
</article>
1.1 xml-axkit/examples/docbook_print.xps
Index: docbook_print.xps
===================================================================
<!--#include file="docbook_tags.xps"-->
<html>
<head>
<title><%= findvalue('/article/artheader/title/text()') %></title>
</head>
<body bgcolor="white">
<%
# display title/TOC page
print apply_templates('/article/artheader/*');
%>
<hr>
<%
# display particular page
foreach my $section (findnodes("/article/sect1")) {
# should only be 1
print apply_templates($section);
}
%>
</body>
</html>
1.1 xml-axkit/examples/docbook_screen.xps
Index: docbook_screen.xps
===================================================================
<%
use Apache::Request;
my $apr = Apache::Request->new($r);
my $sect = $apr->param('section');
$r->no_cache(1);
my $this_page = $r->uri;
my ($prev, $next);
my $sects = findvalue('count(/article/sect1)');
$sects = "$sects";
if ($sects > 1) {
if ($sect >= $sects) {
$prev = $sects - 1;
}
elsif ($sect <= 1) {
$next = 2;
}
else {
$prev = $sect - 1;
$next = $sect + 1;
}
}
%>
<!--#include file="docbook_tags.xps"-->
<html>
<head>
<title><%= findvalue('/article/artheader/title/text()') %></title>
</head>
<body bgcolor="white">
<%
if (!$sect) {
# display title/TOC page
print apply_templates('/article/artheader/*');
my $page = 0;
foreach my $sect (findnodes('/article/sect1')) {
$page++;
print "<a href=\"$this_page?section=$page\">";
print findvalue('title/text()', $sect);
print "</a><br>\n";
}
%><hr><%
}
else {
%>
<table width="100%">
<tr>
<td align="left">
<%
if ($prev) {
%>
<a href="<%=$this_page%>?section=<%=$prev%>">Prev</a>
<% } else {
%>
Prev
<% } %>
</td><td align="center"><a href="<%=$this_page%>">Top</a></td><td align="right">
<%
if ($next) {
%>
<a href="<%=$this_page%>?section=<%=$next%>">Next</a>
<% } else { %>
Next
<% } %>
</td></tr></table>
<hr>
<%
# display particular page
foreach my $section (findnodes("/article/sect1[$sect]")) {
# should only be 1
print apply_templates($section);
}
}
%>
<table width="100%"><tr><td align="right">
<a href="<%=$this_page%>?style=print">Printer Friendly</a>
</td></tr>
<tr><td align="right">
<a href="<%=$this_page%>?passthru=1">Raw XML</a>
</td></tr></table>
</body>
</html>
1.1 xml-axkit/examples/docbook_tags.xps
Index: docbook_tags.xps
===================================================================
<%
# support ulink URL's
$t->{'ulink'}{testcode} = sub {
my $node = shift;
my $t = shift;
$t->{pre} = "<a href=\"" .
findvalue('./@url', $node) . "\">";
$t->{post} = '</a>';
return 1;
};
$t->{'title'}{testcode} = sub {
my $node = shift;
my $t = shift;
if (findvalue('parent::blockquote', $node)) {
$t->{pre} = "<b>";
$t->{post} = "</b><br>\n";
}
elsif (findvalue('parent::artheader', $node)) {
$t->{pre} = "<h1>";
$t->{post} = "</h1>";
}
else {
my $parent = findvalue('name(..)', $node);
my ($level) = $parent =~ m/sect(\d+)$/;
$t->{pre} = "<h$level>";
$t->{post} = "</h$level>";
}
return 1;
};
$t->{'attribution'}{pre} = "<i>";
$t->{'attribution'}{post} = "</i><br>\n";
$t->{'para'}{pre} = '<p>';
$t->{'para'}{post} = '</p>';
$t->{'author'}{pre} = '<p>';
$t->{'firstname'}{pre} = '';
$t->{'surname'}{pre} = ' ';
$t->{'surname'}{post} = "<p>\n";
$t->{'email'}{testcode} = sub {
my $node = shift;
$t->{'email'}{pre} = "<a href=\"" . findvalue('./text()', $node) . "\">";
$t->{'email'}{post} = "</a>";
return 1;
};
$t->{'copyright'}{pre} = "Copyright \xC2\xA9 ";
$t->{'trademark'}{post} = "<super>TM</super>";
$t->{'holder'}{testcode} = sub {
my $node = shift;
$t->{'holder'}{pre} = "<a href=\"" . findvalue('./@role', $node) . "\">";
$t->{'holder'}{post} = "</a>";
return 1;
};
$t->{'abstract'}{pre} = "<p>";
$t->{'sect1'}{post} = "<hr>";
#$XML::XPath::Debug = 1;
$t->{'sect1'}{testcode} = sub {
my ($node, $t) = @_;
if (my $id = findvalue('./@id', $node)) {
$t->{pre} = "<a name=\"$id\"></a>";
}
return 1;
};
# lists
$t->{'itemizedlist'}{pre} = '<ul>';
$t->{'itemizedlist'}{post} = '</ul>';
$t->{'orderedlist'}{pre} = '<ol>';
$t->{'orderedlist'}{post} = '</ol>';
$t->{'listitem'}{pre} = '<li>';
$t->{'listitem'}{post} = '</li>';
$t->{'procedure'}{pre} = '<ol>';
$t->{'procedure'}{post} = '</ol>';
$t->{'step'}{pre} = '<li>';
$t->{'step'}{post} = '</li>';
# examples, listings, etc.
$t->{'programlisting'}{pre} = '<table width="100%"><tr><td bgcolor="#DDDDDD"><pre>';
$t->{'programlisting'}{post} = '</pre></td></tr></table>';
# need to support <prompt> and other tags within screen...
$t->{'screen'} = $t->{'programlisting'};
$t->{'userinput'}{pre} = '<b>';
$t->{'userinput'}{post} = '</b>';
$t->{'replaceable'}{pre} = '<i>';
$t->{'replaceable'}{post} = '</i>';
$t->{'emphasis'}{pre} = '<i>';
$t->{'emphasis'}{post} = '</i>';
$t->{'application'}{pre} = '<b>';
$t->{'application'}{post} = '</b>';
$t->{'manvolnum'}{pre} = '(';
$t->{'manvolnum'}{post} = ')';
$t->{'option'}{pre} = '<tt>';
$t->{'option'}{post} = '</tt>';
$t->{'filename'} = $t->{'option'};
$t->{'literal'} = $t->{'option'};
# references
$t->{'anchor'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} = '<a name="#';
$t->{pre} .= findvalue('@id', $node);
$t->{pre} .= '">';
$t->{post} = '</a>';
return 1;
};
$t->{'xref'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} = '<a href="#';
my $id = findvalue('@linkend', $node);
$t->{pre} .= $id . '">';
$t->{pre} .= findvalue("id('$id')/title/text()");
$t->{post} = '</a>';
return -1;
};
$t->{'link'}{testcode} = sub {
my ($node, $t) = @_;
$t->{pre} = '<a href="#';
$t->{pre} .= findvalue('@linkend', $node);
$t->{pre} .= '">';
$t->{post} = '</a>';
return 1;
};
%>
1.1 xml-axkit/examples/index.xml
Index: index.xml
===================================================================
<?xml version="1.0"?>
<?xml-stylesheet href="default.xps"
type="application/x-xpathscript"?>
<page>
<head>
<title>Welcome to AxKit!</title>
</head>
<body bgcolor="white">
<section>
<title>Congratulations!</title>
If this page displays as HTML, you have managed to successfully
install AxKit. If not, please check your error log and re-check
your installation.
</section>
<section>
<title>AxKit</title>
AxKit can be found <a href="http://xml.sergeant.org/axkit/">here</a>.
</section>
</body>
</page>
1.1 xml-axkit/lib/Apache/AxKit/Cache.pm
Index: Cache.pm
===================================================================
# $Id: Cache.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Cache;
use strict;
use Apache;
use Apache::Constants qw(OK DECLINED);
use Apache::AxKit::Exception;
use Digest::MD5 ();
use Compress::Zlib;
use Fcntl qw(:flock O_RDWR O_WRONLY O_CREAT O_RDONLY);
# use vars qw/$COUNT/;
sub new {
my $class = shift;
my ($r, $xmlfile, @extras) = @_;
my $gzip = 0;
if ($xmlfile =~ /.gzip/) {
$gzip++;
# @extras = grep(!/gzip/, @extras);
}
my $key = Digest::MD5->new->add(
join(':',
$r->get_server_name,
$r->get_server_port,
$xmlfile,
@extras
))->hexdigest;
AxKit::Debug(7, "Cache: key = $key");
my $primary = substr($key,0,2,'');
my $secondary = substr($key,0,2,'');
# warn "New for: $xmlfile:" . join(':', @extras). "\n";
my $cachedir = $AxKit::Cfg->CacheDir();
my $no_cache;
if ($AxKit::Cfg->NoCache()) {
$no_cache = 1;
}
if (!$no_cache) {
if (!-e $cachedir) {
if (!mkdir($cachedir, 0777)) {
AxKit::Debug(2, "Can't create cache directory '$cachedir': $!");
$no_cache = 1;
}
}
if (!-e "$cachedir/$primary") {
if (!mkdir("$cachedir/$primary", 0777)) {
AxKit::Debug(1, "Can't create cache directory '$cachedir/$primary': $!");
$no_cache = 1;
}
}
if (!-e "$cachedir/$primary/$secondary") {
if (!mkdir("$cachedir/$primary/$secondary", 0777)) {
AxKit::Debug(1, "Can't create cache directory '$cachedir/$primary/$secondary': $!");
$no_cache = 1;
}
}
}
my $self = bless {
apache => $r,
key => $key,
no_cache => $no_cache,
dir => $cachedir,
file => "$cachedir/$primary/$secondary/$key",
gzip => $gzip,
# extras => \@extras,
}, $class;
if (my $alternate = $AxKit::Cfg->CacheModule()) {
AxKit::reconsecrate($self, $alternate);
}
# AxKit::Debug(7, "Cache->new Count: ".++$COUNT);
return $self;
}
sub _get_stats {
my $self = shift;
return if $self->{mtime};
my @stats = stat($self->{file});
my $exists = -e _ && -r _;
$self->{file_exists} = $exists;
$self->{mtime} = $stats[9];
}
# sub DESTROY {
# AxKit::Debug(7, "Cache->DESTROY Count: ".--$COUNT);
# }
sub write {
my $self = shift;
return if $self->{no_cache};
AxKit::Debug(7, "[Cache] writing cache file $self->{file}");
my $fh = Apache->gensym();
my $tmp_filename = $self->{file}."new$$";
if (sysopen($fh, $tmp_filename, O_WRONLY|O_CREAT)) {
# flock($fh, LOCK_EX);
# seek($fh, 0, 0);
# truncate($fh, 0);
print $fh $_[0];
close $fh;
rename($tmp_filename, $self->{file})
|| throw Apache::AxKit::Exception::IO( -text => "Couldn't rename cachefile: $!");
}
else {
throw Apache::AxKit::Exception::IO( -text => "Couldn't open cachefile for writing: $!");
}
if ($self->{gzip} && $AxKit::Cfg->GzipOutput) {
AxKit::Debug(3, 'Creating gzip output cache');
my $fh = Apache->gensym();
if (sysopen($fh, $self->{file}.'new.gz', O_RDWR|O_CREAT)) {
flock($fh, LOCK_EX);
seek($fh, 0, 0);
truncate($fh, 0);
print $fh ''.Compress::Zlib::memGzip($_[0]);
close $fh;
rename($self->{file}.'new.gz', $self->{file}.'.gz')
|| throw Apache::AxKit::Exception::IO( -text => "Couldn't rename gzipped cachefile: $!");
}
else {
throw Apache::AxKit::Exception::IO( -text => "Couldn't open gzipped cachefile for writing: $!");
}
}
}
sub read {
my $self = shift;
return if $self->{no_cache};
my $fh = Apache->gensym();
if (sysopen($fh, $self->{file}, O_RDONLY)) {
flock($fh, LOCK_SH);
local $/;
return <$fh>;
# close($fh);
# close unlocks automatically
}
return '';
}
sub get_fh {
my $self = shift;
return if $self->{no_cache};
my $fh = Apache->gensym();
if (sysopen($fh, $self->{file}, O_RDONLY)) {
flock($fh, LOCK_SH);
return $fh;
}
else {
throw Apache::AxKit::Exception::IO( -text => "Cannot open cache file for reading: $!");
}
}
sub set_type {
my $self = shift;
return if $self->{no_cache};
my $fh = Apache->gensym();
if (sysopen($fh, $self->{file}.'newtype', O_RDWR|O_CREAT)) {
flock($fh, LOCK_EX);
seek($fh, 0, 0);
truncate($fh, 0);
print $fh $_[0];
close $fh;
rename($self->{file}.'newtype', $self->{file}.'.type')
|| throw Apache::AxKit::Exception::IO( -text => "Couldn't rename type cachefile: $!");
}
else {
throw Apache::AxKit::Exception::IO( -text => "Couldn't open type cachefile for writing: $!");
}
}
sub get_type {
my $self = shift;
return if $self->{no_cache};
my $fh = Apache->gensym();
if (sysopen($fh, $self->{file}.'.type', O_RDONLY)) {
flock($fh, LOCK_SH);
local $/;
return <$fh>;
# close($fh);
# close unlocks automatically
}
return '';
}
sub deliver {
my $self = shift;
return if $self->{no_cache};
my $r = $self->{apache};
{
# get content-type
AxKit::Debug(4, "Cache: Getting content-type");
if (my $type = $self->get_type) {
AxKit::Debug(4, "Cache: setting content-type: $type");
$r->content_type($type);
}
}
if ($r->content_type eq 'changeme' && !$r->notes('axkit_passthru_type')) {
$AxKit::Cfg->AllowOutputCharset(1);
$r->content_type('text/html; charset=' . ($AxKit::Cfg->OutputCharset || "UTF-8"));
}
elsif ($r->notes('axkit_passthru_type')) {
$r->content_type($AxKit::OrigType);
}
my ($transformer, $doit) = AxKit::get_output_transformer();
if ($doit) {
AxKit::Debug(4, "Cache: Transforming content and printing to browser");
$r->send_http_header() unless lc($r->dir_config('Filter')) eq 'on';
$r->print( $transformer->( $self->read() ) );
return OK;
}
else {
AxKit::Debug(4, "Cache: Sending untransformed content to browser");
# Make sure we unset PATH_INFO or wierd things can happen!
$ENV{PATH_INFO} = '';
$r->path_info('');
if ($self->{gzip} && $AxKit::Cfg->DoGzip) {
AxKit::Debug(4, 'Cache: Delivering gzipped output');
$r->filename($self->{file}.'.gz');
}
else {
$r->filename($self->{file});
}
return DECLINED;
}
}
sub reset {
my $self = shift;
unlink $self->{file};
}
sub mtime {
my $self = shift;
$self->_get_stats;
return $self->{mtime} if exists $self->{mtime};
return ($self->{mtime} = (stat($self->{file}))[9]);
}
sub has_changed {
my $self = shift;
my $time = shift;
return $self->mtime > $time;
}
sub exists {
my $self = shift;
return if $self->{no_cache};
$self->_get_stats;
return $self->{file_exists} if exists $self->{file_exists};
return ($self->{file_exists} = -e $self->{file});
}
sub key {
my $self = shift;
return $self->{key};
}
sub no_cache {
my $self = shift;
return $self->{no_cache} unless @_;
if ($_[0]) {
AxKit::Debug(8, "Turning off cache!");
$self->{no_cache} = 1;
$self->reset();
}
return $self->{no_cache};
}
1;
1.1 xml-axkit/lib/Apache/AxKit/CharsetConv.pm
Index: CharsetConv.pm
===================================================================
# $Id: CharsetConv.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::CharsetConv;
# Copyright (c) 2000 Michael Piotrowski
# Originally copied from Text::Iconv
use strict;
use vars qw($VERSION @ISA @EXPORT_OK);
require Exporter;
require DynaLoader;
@ISA = qw(Exporter DynaLoader);
@EXPORT_OK = qw( convert );
$VERSION = '1.0';
bootstrap Apache::AxKit::CharsetConv $VERSION;
1;
__END__
1.1 xml-axkit/lib/Apache/AxKit/CharsetConv.xs
Index: CharsetConv.xs
===================================================================
/* $Id: CharsetConv.xs,v 1.1 2002/01/13 20:45:11 matts Exp $ */
/* XSUB for Perl module Apache::AxKit::CharsetConv */
/* Originally from Text::Iconv distribution, */
/* all credits to Michael Piotrowski - this is a verbatim copy */
/* included in AxKit to reduce the number of required extra modules */
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <iconv.h>
#ifdef __cplusplus
}
#endif
/*****************************************************************************/
static int raise_error = 0;
SV *do_conv(iconv_t iconv_handle, SV *string)
{
char *ibuf; /* char* to the content of SV *string */
char *obuf; /* temporary output buffer */
size_t inbytesleft; /* no. of bytes left to convert; initially
this is the length of the input string,
and 0 when the conversion has finished */
size_t outbytesleft; /* no. of bytes in the output buffer */
size_t l_obuf; /* length of the output buffer */
char *icursor; /* current position in the input buffer */
/* The Single UNIX Specification (version 1 and version 2), as well
as the HP-UX documentation from which the XPG iconv specs are
derived, are unclear about the type of the second argument to
iconv() (here called icursor): The manpages say const char **,
while the header files say char **. */
char *ocursor; /* current position in the output buffer */
size_t ret; /* iconv() return value */
SV *perl_str; /* Perl return string */
perl_str = newSVpv("", 0);
/* Get length of input string. That's why we take an SV* instead of
a char*: This way we can convert UCS-2 strings because we know
their length. */
inbytesleft = SvCUR(string);
ibuf = SvPV(string, inbytesleft);
/* Calculate approximate amount of memory needed for the temporary
output buffer and reserve the memory. The idea is to choose it
large enough from the beginning to reduce the number of copy
operations when converting from a single byte to a multibyte
encoding. */
if(inbytesleft <= MB_LEN_MAX)
{
outbytesleft = MB_LEN_MAX + 1;
}
else
{
outbytesleft = 2 * inbytesleft;
}
l_obuf = outbytesleft;
obuf = (char *) New(0, obuf, outbytesleft, char); /* Perl malloc */
/**************************************************************************/
icursor = ibuf;
ocursor = obuf;
/**************************************************************************/
while(inbytesleft != 0)
{
ret = iconv(iconv_handle, (const char**)&icursor, &inbytesleft,
&ocursor, &outbytesleft);
if(ret == (size_t) -1)
{
switch(errno)
{
case EILSEQ:
/* Stop conversion if input character encountered which
does not belong to the input char set */
if (raise_error)
croak("Character not from source char set: %s",
strerror(errno));
Safefree(obuf);
return(&PL_sv_undef);
case EINVAL:
/* Stop conversion if we encounter an incomplete
character or shift sequence */
if (raise_error)
croak("Incomplete character or shift sequence: %s",
strerror(errno));
Safefree(obuf);
return(&PL_sv_undef);
case E2BIG:
/* If the output buffer is not large enough, copy the
converted bytes to the return string, reset the
output buffer and continue */
sv_catpvn(perl_str, obuf, l_obuf - outbytesleft);
ocursor = obuf;
outbytesleft = l_obuf;
break;
default:
if (raise_error)
croak("iconv error: %s", strerror(errno));
Safefree(obuf);
return(&PL_sv_undef);
}
}
}
/* Copy the converted bytes to the return string, and free the
output buffer */
sv_catpvn(perl_str, obuf, l_obuf - outbytesleft);
Safefree(obuf); /* Perl malloc */
return perl_str;
}
typedef iconv_t Apache__AxKit__CharsetConv;
/*****************************************************************************/
/* Perl interface */
MODULE = Apache::AxKit::CharsetConv PACKAGE = Apache::AxKit::CharsetConv PREFIX = iconv_t_
PROTOTYPES: DISABLE
int
raise_error(...)
CODE:
if (items > 0 && SvIOK(ST(0))) /* if called as function */
raise_error = SvIV(ST(0));
if (items > 1 && SvIOK(ST(1))) /* if called as class method */
raise_error = SvIV(ST(1));
RETVAL = raise_error;
OUTPUT:
RETVAL
Apache::AxKit::CharsetConv
new(self, fromcode, tocode)
char *fromcode
char *tocode
CODE:
if((RETVAL = iconv_open(tocode, fromcode)) == (iconv_t)-1)
{
switch(errno)
{
case ENOMEM:
croak("Insufficient memory to initialize conversion: %s -> %s",
fromcode, tocode);
case EINVAL:
croak("Unsupported conversion: %s -> %s", fromcode, tocode);
default:
croak("Couldn't initialize conversion: %s -> %s", fromcode, tocode);
}
}
OUTPUT:
RETVAL
SV*
convert(self, string)
Apache::AxKit::CharsetConv self
SV *string
CODE:
RETVAL = do_conv(self, string);
OUTPUT:
RETVAL
void
DESTROY(self)
Apache::AxKit::CharsetConv self
CODE:
/* printf("Now in Apache::AxKit::CharsetConv::DESTROY\n"); */
(void) iconv_close(self);
1.1 xml-axkit/lib/Apache/AxKit/ConfigReader.pm
Index: ConfigReader.pm
===================================================================
# $Id: ConfigReader.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::ConfigReader;
use strict;
# use vars qw/$COUNT/;
sub new {
my $class = shift;
my $r = shift;
my $cfg = AxKit::get_config($r) || {};
# use Apache::Peek 'Dump';
# Dump($cfg);
# use Data::Dumper;
# $Data::Dumper::Indent = 1;
# warn("Cfg: ", Data::Dumper->Dump([$cfg], ['cfg']));
my $self = bless { apache => $r, cfg => $cfg, output_charset_ok => 0 }, $class;
if (my $alternate = $cfg->{ConfigReader} || $r->dir_config('AxConfigReader')) {
AxKit::reconsecrate($self, $alternate);
}
return $self;
}
# sub DESTROY {
# AxKit::Debug(7, "ConfigReader->DESTROY count: ".--$COUNT);
# }
# returns a hash reference consisting of key = type, value = module
sub StyleMap {
my $self = shift;
if ($self->{cfg}->{StyleMap}) {
return $self->{cfg}->{StyleMap};
}
# no StyleMap, try dir_config
my %hash = split /\s*(?:=>|,)\s*/, $self->{apache}->dir_config('AxStyleMap');
return \%hash;
}
# returns the location of the cache dir
sub CacheDir {
my $self = shift;
if (my $cachedir =
$self->{cfg}->{CacheDir}
||
$self->{apache}->dir_config('AxCacheDir')) {
return $cachedir;
}
use File::Basename;
my $dir = dirname($self->{apache}->filename());
return $dir . "/.xmlstyle_cache";
}
sub ProviderClass {
my $self = shift;
if (my $alternate = $self->{cfg}{Provider} ||
$self->{apache}->dir_config('AxProvider')) {
return $alternate;
}
return 'Apache::AxKit::Provider::File';
}
sub DependencyChecks {
my $self = shift;
return $self->{cfg}{DependencyChecks};
}
sub StyleProviderClass {
my $self = shift;
if (my $alternate = $self->{cfg}{StyleProvider} ||
$self->{apache}->dir_config('AxStyleProvider')) {
return $alternate;
}
return 'Apache::AxKit::Provider::File';
}
sub NoCache {
my $self = shift;
return $self->{cfg}{NoCache} ||
$self->{apache}->dir_config('AxNoCache');
}
sub PreferredStyle {
my $self = shift;
return
$self->{apache}->notes('preferred_style')
||
$self->{cfg}->{Style}
||
$self->{apache}->dir_config('AxPreferredStyle')
||
'';
}
sub PreferredMedia {
my $self = shift;
return
$self->{apache}->notes('preferred_media')
||
$self->{cfg}->{Media}
||
$self->{apache}->dir_config('AxPreferredMedia')
||
'screen';
}
sub CacheModule {
my $self = shift;
return $self->{cfg}{CacheModule}
|| $self->{apache}->dir_config('AxCacheModule');
}
sub DebugLevel {
my $self = shift;
return $self->{cfg}{DebugLevel} ||
$self->{apache}->dir_config('AxDebugLevel') ||
0;
}
sub DebugTime {
my $self = shift;
return $self->{apache}->dir_config('AxDebugTime') || 0;
}
sub StackTrace {
my $self = shift;
return $self->{cfg}{StackTrace} ||
$self->{apache}->dir_config('AxStackTrace') ||
0;
}
sub LogDeclines {
my $self = shift;
return $self->{cfg}{LogDeclines} ||
$self->{apache}->dir_config('AxLogDeclines') ||
0;
}
sub AllowOutputCharset {
my $self = shift;
my $oldval = $self->{output_charset_ok};
if (@_) {
$self->{output_charset_ok} = shift;
}
return $oldval;
}
sub OutputCharset {
my $self = shift;
return unless $self->{output_charset_ok};
# warn "OutputCharset\n";
unless ($self->{cfg}{TranslateOutput} ||
$self->{apache}->dir_config('AxTranslateOutput')) {
return;
}
# warn "Checking OutputCharset\n";
if (my $charset = $self->{cfg}{OutputCharset}
|| $self->{apache}->dir_config('AxOutputCharset')) {
return $charset;
}
# warn "Checking Accept-Charset\n";
# check HTTP_ACCEPT_CHARSET
if (my $ok_charsets = $ENV{HTTP_ACCEPT_CHARSET}) {
my @charsets = split(/,\s*/, $ok_charsets);
my $retcharset;
my $retscore = 0;
foreach my $charset (@charsets) {
my $score;
($charset, $score) = split(/;\s*q=/, $charset, 2);
$score = 1 unless (defined($score) && ($score =~ /^(\+|\-)?\d+(\.\d+)?$/));
if ($score > $retscore || $charset =~ /^utf\-?8$/i) { # we like utf8
$retcharset = $charset;
$retscore = $score;
}
}
$retcharset =~ s/iso/ISO/;
$retcharset =~ s/(us\-)?ascii/US-ASCII/;
return undef if $retcharset =~ /^utf\-?8$/;
return undef if $retcharset eq '*';
# warn "Charset: '$retcharset'\n";
return $retcharset;
}
}
sub ErrorStyles {
my $self = shift;
my $style = $self->{cfg}{ErrorStylesheet};
my ($type, $href);
if (!$style) {
($type, $href) = split(/\s*=>\s*/,
($self->{apache}->dir_config('AxErrorStylesheet') || ''),
2);
return [] unless $href;
}
else {
($type, $href) = @$style;
}
my $style_map = $self->StyleMap;
my $module = $style_map->{ $type };
if (!$module) {
throw Apache::AxKit::Exception::Error(
-text => "ErrorStylesheet: No module mapping found for type '$type'"
);
}
return [{href => $href, type => $type, module => $module}];
}
sub GzipOutput {
my $self = shift;
return $self->{cfg}{GzipOutput}
|| $self->{apache}->dir_config('AxGzipOutput');
}
sub DoGzip {
# should I actually send GZip for this request?
my $self = shift;
return unless $self->GzipOutput;
AxKit::Debug(5, 'Should we zip the output?');
my $r = $self->{apache};
my($can_gzip);
AxKit::Debug(5, 'Getting Vary header');
my @vary;
@vary = $r->header_out('Vary') if $r->header_out('Vary');
push @vary, "Accept-Encoding", "User-Agent";
AxKit::Debug(5, 'Setting Vary header');
$r->header_out('Vary',
join ", ",
@vary
);
my($accept_encoding) = $r->header_in("Accept-Encoding") || '';
$can_gzip = 1 if index($accept_encoding,"gzip")>=0;
unless ($can_gzip) {
my $user_agent = $r->header_in("User-Agent");
if ($user_agent =~ m{
^Mozilla/
\d+
\.
\d+
[\s\[\]\w\-]+
(
\(X11 |
Macint.+PPC,\sNav
)
}x
) {
$can_gzip = 1;
}
}
AxKit::Debug(5, $can_gzip ? 'Setting gzip' : 'Not setting gzip');
$r->header_out('Content-Encoding','gzip') if $can_gzip;
return $can_gzip;
}
sub GetMatchingProcessors {
my $self = shift;
my ($media, $style, $doctype, $dtd, $root, $styles, $provider) = @_;
return @$styles if @$styles;
$style ||= '#default';
my $list = $self->{cfg}{Processors}{$media}{$style};
my $processors = $self->{apache}->dir_config('AxProcessors');
if( $processors ) {
foreach my $processor (split(/\s*,\s*/, $processors) ) {
my ($pmedia, $pstyle, @processor) = split(/\s+/, $processor);
next unless ($pmedia eq $media and $pstyle eq $style);
push (@$list, [ 'NORMAL', @processor ] );
}
}
my @results;
for my $directive (@$list) {
my $type = $directive->[0];
my $style_hash = {
type => $directive->[1],
href => $directive->[2],
title => $style,
};
if ($type eq 'NORMAL') {
push @results, $style_hash;
}
elsif ($type eq 'DocType') {
if ($doctype eq $directive->[3]) {
push @results, $style_hash;
}
}
elsif ($type eq 'DTD') {
if ($dtd eq $directive->[3]) {
push @results, $style_hash;
}
}
elsif ($type eq 'Root') {
if ($root eq $directive->[3]) {
push @results, $style_hash;
}
}
elsif ($type eq 'URI') {
my $uri = $provider->apache_request->uri;
if ($uri =~ /$directive->[3]/) {
push @results, $style_hash;
}
}
else {
warn "Unrecognised directive type: $type";
}
}
# list any dynamically chosen stylesheets here
$list = $self->{cfg}{DynamicProcessors};
foreach my $package (@$list) {
AxKit::load_module($package);
no strict 'refs';
my($handler) = $package.'::handler';
push @results, $handler->($provider, $media, $style,
$doctype, $dtd, $root);
}
return @results;
}
sub XSPTaglibs {
my $self = shift;
my @others;
@others = eval { keys %{ $self->{cfg}{XSPTaglibs} } };
warn $@ if $@;
if (@others) {
return @others;
}
local $^W;
@others = split(/\s+/, $self->{apache}->dir_config('AxAddXSPTaglibs'));
return @others;
}
sub OutputTransformers {
my $self = shift;
my @filters;
if( my $o_t = $self->{cfg}{OutputTransformers} ) {
@filters = @$o_t;
}
else {
@filters = split(/\s+/,
$self->{apache}->dir_config('AxOutputTransformers'));
}
return @filters;
}
sub Plugins {
my $self = shift;
my @plugs;
if (my $plugins = $self->{cfg}{Plugins}) {
@plugs = @$plugins;
}
else {
@plugs = split(/\s+/,
$self->{apache}->dir_config('AxPlugins'));
}
return @plugs;
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Exception.pm
Index: Exception.pm
===================================================================
# $Id: Exception.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Exception;
use Error;
@ISA = ('Error');
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
if ($AxKit::Cfg->StackTrace) {
my $i = $Error::Depth + 1;
my ($pkg, $file, $line) = caller($i++);
my @stacktrace;
while ($pkg) {
push @stacktrace, { '-package' => $pkg, '-file' => $file, '-line' => $line};
($pkg, $file, $line) = caller($i++);
}
$self->{'stacktrace'} = \@stacktrace;
}
return $self;
}
sub stacktrace_list {
my $E = shift;
return $E->{'stacktrace'} || [];
}
use overload 'bool' => 'bool';
sub bool {
my $E = shift;
1;
}
sub value {
my $E = shift;
exists $E->{'-value'} ? $E->{'-value'} : 1;
}
package Apache::AxKit::Exception::Declined;
@ISA = ('Apache::AxKit::Exception');
package Apache::AxKit::Exception::Error;
@ISA = ('Apache::AxKit::Exception');
package Apache::AxKit::Exception::OK;
@ISA = ('Apache::AxKit::Exception');
package Apache::AxKit::Exception::Retval;
@ISA = ('Apache::AxKit::Exception');
package Apache::AxKit::Exception::IO;
@ISA = ('Apache::AxKit::Exception');
# a hack to fix broken Error.pm 0.13 (fixed in 0.14)
package Error;
use overload bool => sub { 1 };
1;
1.1 xml-axkit/lib/Apache/AxKit/Language.pm
Index: Language.pm
===================================================================
# $Id: Language.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language;
use strict;
use Apache::Constants;
sub handler {
my $class = shift;
my ($r, $xml, $style) = @_;
die "Need to subclass handler() method";
}
sub get_mtime {
my $class = shift;
my $provider = shift;
return $provider->mtime();
}
sub stylesheet_exists { 1; }
1;
__END__
=head1 NAME
Apache::AxKit::Language - base class for all processors
=head1 DESCRIPTION
This base class principally provides the get_mtime function for
determining the modification time of the stylesheet. Other modules
are free to override this function - possibly to provide facilities
for determining the minimum mtime of an XML-based stylesheet that includes
external entities.
=cut
1.1 xml-axkit/lib/Apache/AxKit/Makefile.PL
Index: Makefile.PL
===================================================================
# $Id: Makefile.PL,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::CharsetConv;
use ExtUtils::MakeMaker;
######################################################
# Standard bits required for have_library and friends
my %config;
$|=1; # flush output
while($_ = shift @ARGV) {
my ($k, $v) = split /=/, $_, 2;
$config{$k} = $v;
}
$DEBUG = $AxKit::DEBUG || delete $config{DEBUG};
######################################################
if (have_library("iconv", "iconv") || have_library("iconv", "libiconv")) {
# probably FreeBSD or Win32
print "Found libiconv. Might be *BSD or Win32...\n\n";
$config{LIBS} .= " -liconv";
}
elsif (have_library("c", "iconv")) {
# other unix
}
else {
print <<"REASON";
AxKit needs the iconv system library to work properly. This comes by
default with most Unix systems, however it may be that you do not
have the development libraries installed, or possibly that you just
don't have iconv available on your system. For *BSD systems, iconv
is available in your distribution's ports collection. For Win32
systems, a google search will often suffice to find a Win32 library
version.
REASON
exit(0);
}
######################################################
# test iconv() param types for const
# print "checking if iconv() param 2 is const... ";
# my $cfile = gensym();
# open($cfile, ">conftest.c") || die "Cannot write to file conftest.c";
# print $cfile <<"SRC";
# #include "iconv.h"
# int main() { return 0; }
# int t() {
# char *foo = "foo";
# size_t ret;
# iconv_t iconv_handle;
# size_t inbytesleft;
# size_t outbytesleft;
# char *ocursor;
# ret = iconv(iconv_handle, &foo, &inbytesleft,
# &ocursor, &outbytesleft);
# return 0;
# }
# SRC
# close $cfile;
# my $output = $is_Win32 ?
# eval {
# backtick(sprintf($CC, $config{INC})) .
# backtick(sprintf($LINK, $config{LIBS}, " iconv.lib"));
# }
# :
# eval {
# backtick(sprintf($LINK, $config{INC}, $config{LIBS}, '2>&1'));
# };
#
# if ($@) {
# warn $@ if $DEBUG;
# }
# rm_f("conftest*");
# if ($output =~ /warning: passing arg 2/) {
# print "yes\n";
# $config{DEFINE} .= uc(" -Diconv_second_param_is_const");
# }
# else {
# print "no\n";
# }
######################################################
if ($DEBUG) {
print "calling WriteMakefile with config:\n";
foreach my $k (keys %config) {
print "$k = $config{$k}\n";
}
}
foreach my $k (keys %config) {
push @ARGV, "$k=$config{$k}";
}
#%config = () if $] > 5.00560;
rm_f($DEVNULL) if $is_Win32;
WriteMakefile(
'NAME' => 'Apache::AxKit::CharsetConv',
'VERSION_FROM' => 'CharsetConv.pm',
%config,
);
sub MY::constants {
package MY;
my $class = shift;
my $inherited = $class->SUPER::constants(@_);
# warn "CONSTANTS: $inherited\n";
$inherited =~ s|\s\Q../blib\E| ../../../blib|g;
return $inherited;
}
#################################################################
# Functions
#################################################################
use Config;
use Cwd;
use Symbol;
use File::Spec;
use vars qw/$DEVNULL $is_Win32/;
BEGIN {
$is_Win32 = ($^O =~ /Win32/);
if ($is_Win32) {
$DEVNULL = 'DEVNULL';
}
else {
$DEVNULL = eval { File::Spec->devnull };
if ($@) { $DEVNULL = '/dev/null' }
}
}
sub rm_f {
my @files = @_;
my @realfiles;
foreach (@files) {
push @realfiles, glob($_);
}
if (@realfiles) {
chmod(0777, @realfiles);
unlink(@realfiles);
}
}
sub rm_fr {
my @files = @_;
my @realfiles;
foreach (@files) {
push @realfiles, glob($_);
}
foreach my $file (@realfiles) {
if (-d $file) {
# warn("$file is a directory\n");
rm_fr("$file/*");
rm_fr("$file/.exists");
rmdir($file) || die "Couldn't remove $file: $!";
}
else {
# warn("removing $file\n");
chmod(0777, $file);
unlink($file);
}
}
}
sub xsystem {
my $command = shift;
if ($DEBUG) {
print $command, "\n";
if (system($command) != 0) {
die "system call to '$command' failed";
}
return 1;
}
open(OLDOUT, ">&STDOUT");
open(OLDERR, ">&STDERR");
open(STDOUT, ">$DEVNULL");
open(STDERR, ">$DEVNULL");
my $retval = system($command);
open(STDOUT, ">&OLDOUT");
open(STDERR, ">&OLDERR");
if ($retval != 0) {
die "system call to '$command' failed";
}
return 1;
}
sub backtick {
my $command = shift;
if ($DEBUG) {
print $command, "\n";
my $results = `$command`;
chomp $results;
if ($? != 0) {
die "backticks call to '$command' failed";
}
return $results;
}
open(OLDOUT, ">&STDOUT");
open(OLDERR, ">&STDERR");
open(STDOUT, ">$DEVNULL");
open(STDERR, ">$DEVNULL");
my $results = `$command`;
my $retval = $?;
open(STDOUT, ">&OLDOUT");
open(STDERR, ">&OLDERR");
if ($retval != 0) {
die "backticks call to '$command' failed";
}
chomp $results;
return $results;
}
sub try_link0 {
my ($src, $opt) = @_;
my $cfile = gensym();
# local $config{LIBS};
# $config{LIBS} .= $opt;
unless (mkdir(".testlink", 0777)) {
rm_fr(".testlink");
mkdir(".testlink", 0777) || die "Cannot create .testlink dir: $!";
}
chdir(".testlink");
open($cfile, ">Conftest.xs") || die "Cannot write to file Conftest.xs: $!";
print $cfile <<EOT;
#ifdef __cplusplus
extern "C" {
#endif
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#ifdef __cplusplus
}
#endif
EOT
print $cfile $src;
print $cfile <<EOT;
MODULE = Conftest PACKAGE = Conftest
PROTOTYPES: DISABLE
EOT
close($cfile);
open($cfile, ">Conftest.pm") || die "Cannot write to file Conftest.pm: $!";
print $cfile <<'EOT';
package Conftest;
$VERSION = 1.0;
require DynaLoader;
@ISA = ('DynaLoader');
bootstrap Conftest $VERSION;
1;
EOT
close($cfile);
open($cfile, ">Makefile.PL") || die "Cannot write to file Makefile.PL: $!";
print $cfile <<'EOT';
use ExtUtils::MakeMaker;
my %config;
while($_ = shift @ARGV) {
my ($k, $v) = split /=/, $_, 2;
warn("$k = $v\n");
$config{$k} = $v;
}
WriteMakefile(NAME => "Conftest", VERSION_FROM => "Conftest.pm", %config);
EOT
close($cfile);
open($cfile, ">test.pl") || die "Cannot write to file test.pl: $!";
print $cfile <<EOT;
use Test; BEGIN { plan tests => 1; } END { ok(\$loaded) }
use Conftest; \$loaded++;
EOT
close($cfile);
xsystem("$^X Makefile.PL " . join(' ', map { "'$_=$config{$_}'" } keys %config));
xsystem("$Config{make} test 'OTHERLDFLAGS=$opt'");
}
sub try_link {
my $start_dir = cwd();
my $result = eval {
try_link0(@_);
};
warn $@ if $DEBUG && $@;
chdir($start_dir);
rm_fr(".testlink");
return $result;
}
sub have_library {
my ($lib, $func) = (@_, "blank");
printf("checking for %s() in -l%s... ", $func, $lib) if $func ne "blank";
printf("looking for -l%s... ", $lib) if $func eq "blank";
my $result;
if ($func) {
my $libs = $is_Win32 ? " $lib.lib " : "-l$lib";
if ($is_Win32) {
$result = try_link(<<"SRC", $libs);
#include <windows.h>
#include <winsock.h>
blank() { return 0; }
int t() { ${func}(); return 0; }
SRC
unless ($result) {
$result = try_link(<<"SRC", $libs);
#include <windows.h>
#include <winsock.h>
blank() { return 0; }
int t() { void ((*p)()); p = (void ((*)()))${func}; return 0; }
SRC
}
}
else {
$result = try_link(<<"SRC", $libs);
blank() { return 0; }
int t() { ${func}(); return 0; }
SRC
}
}
unless ($result) {
print "no\n";
return 0;
}
if ($func ne "main") {
$config{DEFINE} .= uc(" -Dhave_$func");
}
print "yes\n";
return 1;
}
1.1 xml-axkit/lib/Apache/AxKit/Provider.pm
Index: Provider.pm
===================================================================
# $Id: Provider.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Provider;
use strict;
use Apache::AxKit::Exception;
use Apache::Constants qw(OK DECLINED);
#use XML::Parser;
# use vars qw/$COUNT/;
sub new {
my $class = shift;
my $apache = shift;
my $self = bless { apache => $apache }, $class;
if (my $alternate = $AxKit::Cfg->ProviderClass()) {
AxKit::reconsecrate($self, $alternate);
}
$self->init(@_);
AxKit::add_depends($self->key());
return $self;
}
sub init {
# blank - override to provide functionality
}
# sub DESTROY {
# AxKit::Debug(7, "Provider->DESTROY Count: " . --$COUNT);
# }
sub apache_request {
my $self = shift;
return $self->{apache};
}
sub has_changed {
my $self = shift;
my $time = shift;
return $self->mtime > $time;
}
sub decline {
my $self = shift;
AxKit::Debug(4, "provider declined");
return DECLINED;
}
sub get_ext_ent_handler {
my $self = shift;
return sub {
my ($e, $base, $sysid, $pubid) = @_;
# warn "ext_ent: base => $base, sys => $sysid, pub => $pubid\n";
if ($sysid =~ /^http:/) {
if ($pubid) {
return ''; # do not bring in public DTD's
}
eval {
require HTTP::GHTTP;
};
if ($@) {
require LWP::Simple;
import LWP::Simple;
return get($sysid) || die "Cannot get $sysid";
}
my $r = HTTP::GHTTP->new($sysid);
$r->process_request;
return $r->get_body;
}
elsif ($sysid =~ /^(https|ftp):/) {
if ($pubid) {
return ''; # do not bring in public DTD's
}
die "Cannot download https (SSL) or ftp URL's yet. Patches welcome";
}
# warn "File provider ext_ent_handler called with '$sysid'\n";
my $provider = Apache::AxKit::Provider->new(
AxKit::Apache->request,
uri => $sysid,
# rel => $self,
);
# warn "Got provider with key: ", $provider->key, "\n";
my $str = $provider->get_strref;
# warn "Returning string with length: ", length($$str), "\n";
return $$str;
};
}
sub get_styles {
my $self = shift;
my ($media, $pref_style) = @_;
if ($pref_style eq '#default') {
undef $pref_style;
}
my $xml_styles = [];
my $vals = [];
my $key = $self->key();
# need to extract the following from the XML file:
# DocType Public Identifier
# DTD filename
# Root element name (including namespace)
# use three element array @$vals
if (defined &Apache::AxKit::Provider::xs_get_styles_fh) {
AxKit::Debug(2, "using XS get_styles (libxml2)\n");
my ($xs_styles, $doctype, $dtd, $root) =
$self->xs_get_styles($media, $pref_style);
@$xml_styles = @$xs_styles;
@$vals = ($doctype, $dtd, $root);
}
else {
require XML::Parser;
AxKit::Debug(4, "get_styles: creating XML::Parser");
my $xml_parser = XML::Parser->new(
Namespaces => 1,
ErrorContext => 2,
Handlers => {
Start => \&parse_start,
Doctype => \&parse_dtd,
Proc => \&parse_pi,
},
);
my $to_parse;
eval {
$to_parse = $self->get_fh();
};
if ($@) {
$to_parse = ${ $self->get_strref(); };
}
AxKit::Debug(4, "get_styles: calling XML::Parser->parse('$key')");
$xml_parser->parse(
$to_parse,
XMLStyle_preferred => $pref_style,
XMLStyle_media => $media,
XMLStyle_style => $xml_styles,
XMLStyle_vals => $vals,
XMLStyle_style_screen => [],
);
AxKit::Debug(4, "get_styles: parse returned successfully");
}
# Let GetMatchingProcessors to process the @$styles array
AxKit::Debug(4, "Calling GetMatchingProcessors with ($media, $pref_style, $vals->[0], $vals->[1], $vals->[2])");
my @styles = $AxKit::Cfg->GetMatchingProcessors($media,
$pref_style, @$vals[0 .. 2], $xml_styles, $self);
if (!@styles) {
throw Apache::AxKit::Exception::Declined(
reason => "No styles defined for '$key'"
);
}
# get mime-type => module mapping
my $style_mapping = $AxKit::Cfg->StyleMap;
AxKit::Debug(3, "get_styles: loading style modules");
for my $style (@styles) {
my $mapto;
AxKit::Debug(4, "get_styles: looking for mapping for style type: '$style->{type}'");
if (!( $mapto = $style_mapping->{ $style->{type} } )) {
throw Apache::AxKit::Exception::Declined(
reason => "No implementation mapping available for type '$style->{type}'"
);
}
$style->{module} = $mapto;
# first load module if it's not already loaded.
eval {
AxKit::load_module($mapto);
};
if ($@) {
throw Apache::AxKit::Exception::Error(
-text => "Load of '$mapto' failed with: $@"
);
}
}
return \@styles;
}
sub xs_get_styles {
my $self = shift;
my ($media, $pref_style) = @_;
my $bits;
eval {
my $fh = $self->get_fh();
AxKit::Debug(4, "calling xs_get_styles_fh()\n");
$bits = xs_get_styles_fh($self->apache_request, $fh);
};
if ($@) {
my $strref = $self->get_strref();
AxKit::Debug(4, "calling xs_get_styles_str()\n");
$bits = xs_get_styles_str($self->apache_request, $$strref);
}
my @xml_stylesheet = @{$bits->[0]};
my %attribs = @{$bits->[2]};
my $element = $bits->[1];
# resolve namespaces (libxml doesn't!)
if ($attribs{'xmlns'} && $element !~ /:/) {
$element = "{$attribs{xmlns}}$element";
}
elsif ($element =~ /^(.*):(.*)$/) {
my ($prefix, $el) = ($1, $2);
my $ns = $attribs{"xmlns:$prefix"};
$element = "{$ns}$el";
}
my $e = {
XMLStyle_style => [],
XMLStyle_style_screen => [],
XMLStyle_preferred => $pref_style,
XMLStyle_media => $media,
};
foreach my $pi (@xml_stylesheet) {
parse_pi($e, "xml-stylesheet", $pi);
}
if (!@{$e->{XMLStyle_style}} && !$e->{XMLStyle_style_persistant}) {
if ($e->{XMLStyle_style_screen_persistant}) {
push @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_screen_persistant}};
}
if (@{$e->{XMLStyle_style_screen}}) {
push @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_screen}};
}
}
elsif ($e->{XMLStyle_style_persistant}) {
unshift @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_persistant}};
}
AxKit::Debug(4, "xs_get_styles returned: $bits->[3], $bits->[4], $element\n");
return ($e->{XMLStyle_style}, $bits->[3], $bits->[4], $element);
}
sub parse_start {
my ($e, $el) = @_;
my $ns = $e->namespace($el);
# use James Clark's universal name format
$e->{XMLStyle_vals}[2] = $ns ? "{$ns}$el" : $el;
if (!@{$e->{XMLStyle_style}} && !$e->{XMLStyle_style_persistant}) {
if ($e->{XMLStyle_style_screen_persistant}) {
push @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_screen_persistant}};
}
if (@{$e->{XMLStyle_style_screen}}) {
push @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_screen}};
}
}
elsif ($e->{XMLStyle_style_persistant}) {
unshift @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_persistant}};
}
$e->finish();
}
sub parse_dtd {
my ($e, $name, $sysid, $pubid) = @_;
$e->{XMLStyle_vals}[0] = $pubid;
$e->{XMLStyle_vals}[1] = $sysid;
}
sub parse_pi {
my $e = shift;
my ($target, $data) = @_;
if ($target ne 'xml-stylesheet') {
return;
}
my $style;
$data = ' ' . $data;
while ($data =~ /\G
\s+
(href|type|title|media|charset|alternate)
\s*
=
\s*
(["']) # match quotes "'
([^\2<]*?)
\2 # balance quotes "'
/gcx) {
my ($attr, $val) = ($1, $3);
AxKit::Debug(10, "parse_pi: $attr = $val");
$style->{$attr} = $val;
}
if (!exists($style->{href}) || !exists($style->{type})) {
# href and type are #REQUIRED
AxKit::Debug(3, "pi_get_styles: Invalid <?xml-stylesheet?> processing instruction\n");
return;
}
my $mediamatch = 0;
$style->{media} ||= 'screen'; # default according to TR/REC-html40
$style->{alternate} ||= 'no'; # default according to TR/xml-stylesheet
# See http://www.w3.org/TR/REC-html40/types.html#type-media-descriptors
# for details of what we're doing here.
my @mediatypes = split(/,\s*/, $style->{media});
# strip anything after first non-(A-Za-z0-9\-) character (see REC-html40)
@mediatypes = map { $_ =~ s/[^A-Za-z0-9\-].*$//; $_; } @mediatypes;
# warn "media types are ", join(',', @mediatypes), " [$style->{media}] [$e->{XMLStyle_media}]\n";
# remove unwanted entries
@mediatypes = grep /^(screen|tty|tv|projection|handheld|print|braille|aural|all)$/, @mediatypes;
if (grep { $_ eq $e->{XMLStyle_media} } @mediatypes) {
# found a match for the preferred media type!
# warn "Media matches!\n";
$mediamatch++;
}
if (grep { $_ eq 'all' } @mediatypes) {
# always match on media type "all"
# warn "Media is \"all\"\n";
$mediamatch++;
}
if ($e->{XMLStyle_preferred}) {
# warn "someone picked a \"title\" : $e->{XMLStyle_preferred}. Use persistant and alternate styles\n";
if (
($style->{alternate} eq 'no')
&& (!exists $style->{title})
)
{
# warn "This is a persistant style - always make it first\n";
if ($mediamatch) {
push @{$e->{XMLStyle_style_persistant}}, $style;
}
elsif ($style->{media} eq 'screen') {
# store away in case we need the screen matches
push @{$e->{XMLStyle_style_screen_persistant}}, $style;
}
}
elsif (lc($style->{title}) eq lc($e->{XMLStyle_preferred}))
{
# warn "matching style\n";
if ($mediamatch) {
push @{$e->{XMLStyle_style}}, $style;
}
elsif ($style->{media} eq 'screen') {
push @{$e->{XMLStyle_style_screen}}, $style;
}
}
}
else {
# warn "no \"title\" selected. Use persistent and preferred styles\n";
if (
($style->{alternate} eq 'no')
&& (!exists $style->{title})
)
{
if ($mediamatch) {
# This is the persistant style
push @{ $e->{XMLStyle_style_persistant} }, $style;
}
elsif ($style->{media} eq 'screen') {
push @{$e->{XMLStyle_style_screen_persistant}}, $style;
}
}
elsif (
($style->{alternate} eq 'no')
&& (exists $style->{title})
)
{
if ($mediamatch) {
push @{ $e->{XMLStyle_style} }, $style;
}
elsif ($style->{media} eq 'screen') {
push @{ $e->{XMLStyle_style_screen} }, $style;
}
}
}
}
1;
__END__
=head1 NAME
Apache::AxKit::Provider - base Provider class
=head1 SYNOPSIS
Override the base Provider class and enable it using:
AxProvider MyClass
# alternatively use:
# PerlSetVar AxProvider MyClass
=head1 DESCRIPTION
The Provider class is used to read in the data source for the given URL.
The default Provider is Provider::File, which reads from the filesystem,
although obviously you can read from just about anywhere.
Should you wish to override the default Provider, these are the methods
you need to implement:
=head2 process()
Determine whether or not to process this URL. For example, you don't want
to process a directory request, or if the resource doesn't exist. Return 1
to tell AxKit to process this URL, or die with a Declined exception (with
a reason) if you do not wish to process this URL.
=head2 mtime()
Return the last modification time in days before the current time.
=head2 get_styles()
Extract the stylesheets from the XML resource. Should return an array
ref of styles. The style entries are hashes refs with required keys
'href' and 'type'.
=head2 get_fh()
This method should return an open filehandle, or die if that's not possible.
=head2 get_strref()
This method returns a reference to a scalar containing the contents of the
stylesheet, or die if that's not possible. At least one of get_fh or
get_strref B<must> work.
=head2 key()
This method should return a "key" value that is unique to this URL.
=head2 get_ext_ent_handler()
This should return a sub reference that can be used instead of
XML::Parser's default external entity handler. See the XML::Parser
documentation for what this sub should do (or look at the code in
the File provider).
=head2 exists()
Return 1 if the resource exists (and is readable).
=cut
1.1 xml-axkit/lib/Apache/AxKit/typemap
Index: typemap
===================================================================
TYPEMAP
Apache::AxKit::CharsetConv T_PTROBJ
1.1 xml-axkit/lib/Apache/AxKit/Language/AxPoint.pm
Index: AxPoint.pm
===================================================================
# $Id: AxPoint.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::AxPoint;
@ISA = ('Apache::AxKit::Language');
use strict;
use Apache;
use Apache::Request;
use Apache::AxKit::Language;
use Apache::AxKit::Provider;
use PDFLib 0.02;
use XML::XPath;
use File::Basename ();
my @xindent;
sub stylesheet_exists () { 0; }
sub handler {
my $class = shift;
my ($r, $xml_provider, undef, $last_in_chain) = @_;
my $xpath = XML::XPath->new();
my $source_tree;
my $xml_parser = XML::Parser->new(
ErrorContext => 2,
Namespaces => $XML::XPath::VERSION < 1.07 ? 1 : 0,
ParseParamEnt => 1,
);
my $parser = XML::XPath::XMLParser->new(parser => $xml_parser);
if (my $entity_handler = $xml_provider->get_ext_ent_handler()) {
$xml_parser->setHandlers(
ExternEnt => $entity_handler,
);
}
AxKit::Debug(6, "AxPoint: Getting XML Source");
if (my $dom = $r->pnotes('dom_tree')) {
# dom_tree is an XML::XPath DOM
$source_tree = $dom;
delete $r->pnotes()->{'dom_tree'};
}
elsif (my $xml = $r->pnotes('xml_string')) {
eval {
$source_tree = $parser->parse($xml);
};
if ($@) {
throw Apache::AxKit::Exception::Error(-text => "Parse of xml_string failed: $@");
}
}
else {
$source_tree = get_source_tree($xml_provider, $parser);
}
$xpath->set_context($source_tree);
AxKit::Debug(7, "AxPoint: creating pdf");
my $pdf = PDFLib->new();
$pdf->papersize("slides");
$pdf->set_border_style("solid", 0);
$pdf->info(Title => $xpath->findvalue("/slideshow/title"));
$pdf->info(Creator => $xpath->findvalue("/slideshow/metadata/speaker"));
my ($logo_node) = $xpath->findnodes("/slideshow/metadata/logo");
my ($bg_node) = $xpath->findnodes("/slideshow/metadata/background");
AxKit::Debug(7, "AxPoint: loading main bg/logo images");
my ($logo, $bg);
if ($logo_node) {
$logo = $pdf->load_image(
filename => $logo_node->string_value,
filetype => get_filetype($logo_node->string_value),
);
if (!$logo) {
AxKit::Debug(7, "AxPoint: failed to load logo " . $logo_node->string_value);
$pdf->finish;
die "Cannot load image $logo_node!";
}
}
if ($bg_node) {
$bg = $pdf->load_image(
filename => $bg_node->string_value,
filetype => get_filetype($bg_node->string_value),
);
if (!$bg) {
AxKit::Debug(7, "AxPoint: failed to load logo " . $bg_node->string_value);
$pdf->finish;
die "Cannot load image $bg_node!";
}
}
AxKit::Debug(7, "AxPoint: Creating new_page sub");
my $new_page = sub {
my ($node, $trans) = @_;
$pdf->start_page;
my $transition = $trans || $node->findvalue('ancestor-or-self::*/@transition') || 'replace';
$pdf->set_parameter("transition", lc($transition)) if $transition;
$pdf->add_image(img => $bg, x => 0, y => 0, scale => $bg_node->findvalue('@scale') || 1.0)
if $bg_node;
if ($logo_node) {
my $logo_scale = $logo_node->findvalue('@scale') || 1.0;
my $logo_w = $logo->width * $logo_scale;
$pdf->add_image(img => $logo, x => 612 - $logo_w, y => 0, scale => $logo_scale);
}
$pdf->set_font(face => "Helvetica", size => 18.0);
@xindent = ();
$pdf->set_text_pos(80, 300);
};
AxKit::Debug(7, "AxPoint: creating front page");
# title page
$new_page->($xpath->findnodes('/')->get_node(1));
$pdf->set_font(face => "Helvetica-Bold", size => 24);
my $root_bookmark = $pdf->add_bookmark(text => "Title", open => 1);
$pdf->print_boxed($xpath->findvalue("/slideshow/title"),
x => 20, y => 50, w => 570, h => 300, mode => "center");
$pdf->print_line("");
$pdf->print_line("");
$pdf->print_line("");
$pdf->print_line("");
my ($x, $y) = $pdf->get_text_pos();
$pdf->set_font(face => "Helvetica-Bold", size => 20);
$pdf->add_link(link => "mailto:" . $xpath->findvalue("/slideshow/metadata/email"),
x => 20, y => $y - 10, w => 570, h => 24);
$pdf->print_boxed($xpath->findvalue("/slideshow/metadata/speaker"),
x => 20, y => 40, w => 570, h => $y - 24, mode => "center");
$pdf->print_line("");
(undef, $y) = $pdf->get_text_pos();
$pdf->add_link(link => $xpath->findvalue("/slideshow/metadata/link"),
x => 20, y => $y - 10, w => 570, h => 24);
$pdf->print_boxed($xpath->findvalue("/slideshow/metadata/organisation"),
x => 20, y => 40, w => 570, h => $y - 24, mode => "center");
AxKit::Debug(7, "AxPoint: creating slides");
foreach my $slideset ($xpath->findnodes("/slideshow/*[name() = 'slideset' or name() = 'slide']")) {
if ($slideset->getName() eq 'slide') {
process_slide($pdf, $new_page, $slideset, $root_bookmark);
}
else {
process_slideset($pdf, $new_page, $slideset, $root_bookmark);
}
}
AxKit::Debug(7, "AxPoint: outputting pdf");
$r->content_type("application/pdf");
AxKit::Debug(5, "finish pdf");
$pdf->finish;
$r->print( $pdf->get_buffer );
}
sub process_slideset {
my ($pdf, $new_page, $slideset, $parent_bookmark) = @_;
$new_page->($slideset);
my $slide_bookmark = $pdf->add_bookmark(
text => $slideset->findvalue("title"),
level => 2,
parent_of => $parent_bookmark,
open => 1,
);
$pdf->set_font(face => "Helvetica", size => 24);
$pdf->print_boxed($slideset->findvalue("title"),
x => 20, y => 50, w => 570, h => 200, mode => "center");
my ($x, $y) = $pdf->get_text_pos();
$pdf->add_link(link => $slideset->findvalue('title/@href'),
x => 20, y => $y - 5, w => 570, h => 24) if $slideset->findvalue('title/@href');
if (my $subtitle = $slideset->findvalue("subtitle")) {
$pdf->set_font(face => "Helvetica", size => 18);
$pdf->print_boxed($subtitle,
x => 20, y => 20, w => 570, h => 200, mode => "center");
if (my $href = $slideset->findvalue('subtitle/@href')) {
($x, $y) = $pdf->get_text_pos();
$pdf->add_link(link => $href,
x => 20, y => $y - 5, w => 570, h => 18);
}
}
foreach my $slide ($slideset->findnodes("slide")) {
process_slide($pdf, $new_page, $slide, $slide_bookmark);
}
}
sub process_slide {
my ($pdf, $new_page, $slide, $parent_bookmark, $do_up_to) = @_;
$pdf->end_page;
my @images;
foreach my $image ($slide->findnodes("image")) {
push @images, $pdf->load_image(
filename => $image->string_value,
filetype => get_filetype($image->string_value),
);
}
if ($do_up_to) {
my @nodes = $slide->findnodes("point|source_code|image");
my $do_to_node = $nodes[$do_up_to - 1];
$new_page->($slide, $do_to_node->findvalue('@transition'));
}
else {
$new_page->($slide);
}
my $h = 300;
if (my $title = $slide->findvalue("title")) {
$pdf->add_bookmark(text => $title, level => 3, parent_of => $parent_bookmark) unless $do_up_to;
$pdf->set_font(face => "Helvetica", size => 24);
$pdf->print_boxed($title, x => 20, y => 350,
w => 570, h => 70, mode => "center");
my ($x, $y) = $pdf->get_text_pos();
$pdf->add_link(link => $slide->findvalue('title/@href'),
x => 20, y => $y - 5, w => 570, h => 24) if $slide->findvalue('title/@href');
$h = 370;
}
$pdf->set_text_pos(60, $h);
my $new_do_up_to = 1;
foreach my $item ($slide->findnodes("point|source_code|image")) {
if (!$do_up_to && $item->findvalue('@transition')) {
process_slide($pdf, $new_page, $slide, $parent_bookmark, $new_do_up_to);
}
if ($do_up_to) {
last if $do_up_to == $new_do_up_to;
}
if ($item->getName eq "point") {
point($pdf, $item->findvalue('@level') || 1, $item->string_value, $item->findvalue('@href'));
}
elsif ($item->getName eq 'source_code') {
source_code($pdf, $item->string_value, $item->getAttribute('fontsize'));
}
elsif ($item->getName eq 'image') {
image($pdf, $item->getAttribute('scale') || 1, shift @images, $item->findvalue('@href'));
}
$new_do_up_to++;
}
}
###########################################################
# functions
###########################################################
sub new_page {
my ($pdf, $node, $logo, $bg) = @_;
$pdf->start_page;
my $transition = $node->findvalue('ancestor-or-self::node()/@transition');
$pdf->set_parameter("transition", lc($transition)) if $transition;
$pdf->add_image(img => $bg, x => 0, y => 0, scale => 1.0);
$pdf->add_image(img => $logo, x => 420, y => 0, scale => 0.4);
$pdf->set_font(face => "Helvetica", size => 18.0);
@xindent = ();
$pdf->set_text_pos(80, 300);
}
sub bullet {
my ($pdf, $level) = @_;
my ($char, $size);
if ($level == 1) {
$char = "l";
$size = 18;
}
elsif ($level == 2) {
$char = "u";
$size = 16;
}
elsif ($level == 3) {
$char = "p";
$size = 14;
}
my $leading = $pdf->get_value("leading");
$pdf->set_value("leading", $leading + 4);
$pdf->set_value("leading", $leading + 20) if $level == 1;
$pdf->print_line("");
my ($x, $y) = $pdf->get_text_pos;
if (!@xindent || $level > $xindent[0]{level}) {
unshift @xindent, {level => $level, x => $x};
}
$pdf->set_font(face => "ZapfDingbats", size => $size - 4, encoding => "builtin");
$pdf->print($char);
$pdf->set_font(face => "Helvetica", size => $size);
$pdf->print(" ");
return $size;
}
sub point {
my ($pdf, $level, $text, $href) = @_;
my ($x, $y) = $pdf->get_text_pos;
if (@xindent && $level <= $xindent[0]{level}) {
my $last;
while ($last = shift @xindent) {
if ($last->{level} == $level) {
$pdf->set_text_pos($last->{x}, $y);
$x = $last->{x};
last;
}
}
}
if ($level == 1) {
$pdf->set_text_pos(80, $y);
}
my $size = bullet($pdf, $level);
($x, $y) = $pdf->get_text_pos;
$pdf->print_boxed($text, x => $x, y => 0, w => 570 - $x, h => $y + $size);
$pdf->add_link(link => $href,
x => 20, y => $y - 5 + $level, w => 570, h => $size) if $href;
}
sub source_code {
my ($pdf, $text, $size) = @_;
my ($x, $y) = $pdf->get_text_pos;
$pdf->set_font(face => "Courier", size => $size || 14);
$y -= 10 if @xindent;
$pdf->print_boxed($text, x => 80, y => 0, w => 500, h => $y);
}
sub image {
my ($pdf, $scale, $file_handle, $href) = @_;
$pdf->print_line("");
my ($x, $y) = $pdf->get_text_pos;
my ($imgw, $imgh) = (
$pdf->get_value("imagewidth", $file_handle->img),
$pdf->get_value("imageheight", $file_handle->img)
);
$imgw *= $scale;
$imgh *= $scale;
$pdf->add_image(img => $file_handle,
x => (612 / 2) - ($imgw / 2),
y => ($y - $imgh),
scale => $scale);
$pdf->add_link(link => $href, x => 20, y => $y - $imgh, w => 570, h => $imgh) if $href;
$pdf->set_text_pos($x, $y - $imgh);
}
sub get_filetype {
my $filename = shift;
AxKit::Debug(8, "AxPoint: get_filetype($filename)");
my ($suffix) = $filename =~ /([^\.]+)$/;
$suffix = lc($suffix);
if ($suffix eq 'jpg') {
return 'jpeg';
}
return $suffix;
}
sub get_source_tree {
my ($provider, $parser) = @_;
my $source_tree;
AxKit::Debug(7, "AxPoint: reparsing file");
eval {
my $fh = $provider->get_fh();
# warn("parsing FH $fh with parser $parser\n");
local $/;
my $contents = <$fh>;
# warn("FH contains: $contents\n");
$source_tree = $parser->parse($contents);
# warn("Parse completed\n");
close($fh);
};
if ($@) {
# warn("parse_fh failed\n");
$source_tree = $parser->parse(${ $provider->get_strref() });
}
# warn("get_source_tree = $source_tree\n");
AxKit::Debug(7, "AxPoint: returning source tree");
return $source_tree;
}
1;
__END__
=head1 NAME
AxPoint - An AxKit PDF Slideshow generator
=head1 SYNOPSIS
AxAddStyleMap application/x-axpoint Apache::AxKit::Language::AxPoint
AxAddRootProcessor application/x-axpoint NULL slideshow
=head1 DESCRIPTION
I got sick of not being able to do pretty slideshows about AxKit without
resorting to the bloated OpenOffice. So I decided to write something that
allows you to do simple slideshows with bullet points and a few other
niceties using XML, AxKit, and rendering to PDF.
I discovered that PDF can do transitions, and full screen mode (even on Linux),
and so it makes the perfect medium for doing a slideshow.
Note: This module requires PDFLib.pm from CPAN. It also requires the library
B<pdflib>, which is available under the Alladin license from
http://www.pdflib.com/. The Alladin license does not allow re-distribution
of modified binaries, so it is not strictly open source, but it is free to
use even for commercial use.
=head1 Usage
The SYNOPSIS section describes how to set this thing up in AxKit, so here I
will focus on the syntax of AxPoint XML files. I tend to use the suffix ".axp"
for my AxPoint files, but you are free to use whatever you please.
The easiest way to describe all the features is with a complete presentation
first:
<slideshow transition="glitter">
<title>AxPoint Example</title>
<metadata>
<speaker>Matt Sergeant</speaker>
<email>matt@axkit.com</email>
<organisation>AxKit.com Ltd</organisation>
<link>http://axkit.com/</link>
<logo>ax_logo.png</logo>
<background>bg.png</background>
</metadata>
<slide transition="dissolve">
<title>Top Level Slide</title>
<point>This is a top level slide</point>
<point>It is a child of the <slideshow> tag</point>
<point>Can have it's own transition</point>
</slide>
<slideset transition="blinds">
<title>Slidesets</title>
<subtitle>Slidesets can have subtitles</subtitle>
<slide>
<title>Slideset Example</title>
<point>A slideset groups slides under a particular title</point>
<point>Slidesets can have a transition for the group of slides</point>
</slide>
</slideset>
<slideset>
<title>Slide Tags</title>
<slide>
<title>Bullet Points</title>
<point>Level 1 bullet : <point></point>
<point level="1">Another Level 1 bullet : <point level="1"></point>
<point level="2">Level 2 bullet : <point level="2"></point>
<point level="3">Level 3 bullet : <point level="3"></point>
<point>Back to level 1</point>
</slide>
<slide>
<title>Source Core</title>
<source_code><![CDATA[
Source code
uses fixed font
Don't forget to use CDATA sections so you can
<include/><some/><xml/>
]]></source_code>
</slide>
<slide>
<title>Pictures</title>
<point>Images are very simple, and always centered</point>
<image scale="0.5" href="file_large.jpg">file.jpg</image>
</slide>
</slideset>
<slideset transition="dissolve">
<title href="http://foo.bar/aslidesettitle">Slideset titles can have href attributes</title>
<subtitle href="http://foo.bar/aslidesetsubtitle">Slideset subtitles too</subtitle>
<slide>
<title href="http://foo.bar/aslidetitle">Don't forget links for slide titles</title>
<point href="http://foo.bar/apoint">...and for points of various levels</point>
</slide>
</slideset>
</slideshow>
It's not very complex. And with good reason: generating PDFs can be slow. With this simple schema
we can generate a 100 slide PDF in about 1 second. There are many deficiencies with the tagset,
most significantly that no bold or italics or any coloring can be applied to the text by using
tags. The reason being that this is quite hard to do with PDFLib - you have to do text measurement
yourself and do the word-wrapping yourself, and so on. The way it is setup right now is very
simple to use and implement.
=head1 AUTHOR
Matt Sergeant, matt@axkit.com
=cut
1.1 xml-axkit/lib/Apache/AxKit/Language/LibXSLT.pm
Index: LibXSLT.pm
===================================================================
# $Id: LibXSLT.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::LibXSLT;
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 File::Basename qw(dirname);
@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();
local $XML::LibXML::match_cb = \&match_uri;
local $XML::LibXML::open_cb = \&open_uri;
local $XML::LibXML::read_cb = \&read_uri;
local $XML::LibXML::close_cb = \&close_uri;
if (!$xml_doc && !$xmlstring) {
eval {
my $fh = $xml->get_fh();
$xml_doc = $parser->parse_fh($fh, $r->uri());
};
if ($@) {
$xmlstring = ${$xml->get_strref()};
$xml_doc = $parser->parse_string($xmlstring, $r->uri());
}
}
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 (!$style->has_changed($cache->{mtime})) {
my $changed = 0;
DEPENDS:
foreach my $depends (@{ $cache->{depends} }) {
my $p = Apache::AxKit::Provider->new($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) {
my $style_doc;
reset_depends();
my $style_uri = $style->apache_request->uri();
AxKit::Debug(7, "[LibXSLT] parsing stylesheet $style_uri");
eval {
my $fh = $style->get_fh();
$style_doc = $parser->parse_fh($fh, $style_uri);
};
if ($@) {
my $stylestring = ${$style->get_strref()};
$style_doc = $parser->parse_string($stylestring, $style_uri);
}
$stylesheet = XML::LibXSLT->parse_stylesheet($style_doc);
$style_cache{$style->key()} =
{ style => $stylesheet, mtime => time, depends => [ get_depends() ] };
}
# get request form/querystring parameters
my @params;
my $cgi = Apache::Request->instance($r);
@params = fixup_params(map { $_ => $cgi->param($_) } $cgi->param);
AxKit::Debug(7, "[LibXSLT] performing transformation");
my $results = $stylesheet->transform($xml_doc, @params);
if ($last_in_chain && $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) if $last_in_chain;
$r->pnotes('dom_tree', $results);
# warn "LibXSLT returned $output \n";
# print $stylesheet->output_string($results);
}
sub fixup_params {
my @results;
while (@_) {
push @results, XML::LibXSLT::xpath_to_string(
splice(@_, 0, 2)
);
}
return @results;
}
sub match_uri {
my $uri = shift;
# warn("match: $uri\n");
AxKit::Debug(8, "LibXSLT match_uri: $uri");
return $uri !~ /^\w+:/; # only handle URI's without a scheme
}
sub open_uri {
my $uri = shift;
# warn("open: $uri\n");
my $provider = Apache::AxKit::Provider->new(
AxKit::Apache->request(),
uri => $uri,
);
add_depends($provider->key());
my $str = $provider->get_strref;
return $$str;
}
sub close_uri {
}
sub read_uri {
return substr($_[0], 0, $_[1], "");
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Language/NotXSLT.pm
Index: NotXSLT.pm
===================================================================
package Apache::AxKit::Language::NotXSLT;
# $Id: NotXSLT.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
use strict;
use vars qw(@ISA $VERSION $PREFIX $cache);
$VERSION = '0.04';
use Apache;
use Apache::Constants;
use Apache::File;
use XML::XPath;
use XML::XPath::Node;
use XML::XPath::XMLParser;
use Apache::AxKit::Language;
@ISA = 'Apache::AxKit::Language';
sub handler {
my $class = shift;
my ($r, $xml, $style) = @_;
# warn "In NotXSLT with $xmlfile and $stylesheet\n";
my $source_finder = XML::XPath->new;
my $parser = XML::XPath::XMLParser->new;
my $source_tree;
my $mtime = $xml->mtime();
my $key = $xml->key();
if (exists $cache->{$key} &&
$cache->{$key}{mtime} <= $mtime) {
$source_tree = $cache->{$key}{tree};
}
else {
eval {
$source_tree = $parser->parse(${$xml->get_strref});
};
if ($@) {
die "Parse of '$key' failed with $@";
}
$cache->{$key}{mtime} = $mtime;
$cache->{$key}{tree} = $source_tree;
}
my $template_tree;
$mtime = $style->mtime();
my $stylekey = $style->key();
if (exists $cache->{$stylekey} &&
$cache->{$stylekey}{mtime} <= $mtime) {
$template_tree = $cache->{$stylekey}{tree}
}
else {
eval {
$template_tree = $parser->parse(${$style->get_strref});
};
if ($@) {
die "Parse of stylesheet '$stylekey' failed with $@";
}
$cache->{$stylekey}{mtime} = $mtime;
$cache->{$stylekey}{tree} = $template_tree;
}
my $root_node;
# get namespace prefix for template
# here we search through all children of the root node
# because there could be comments or PI's here.
# The root node MUST contain a xmlns:<prefix>="..."
ROOT:
foreach my $node ($template_tree->getChildNodes) {
if ($node->getNodeType == ELEMENT_NODE) {
# first element node!
$root_node = $node;
foreach my $ns ($node->getNamespaceNodes) {
if ($ns->getExpanded eq 'http://sergeant.org/notxslt') {
$PREFIX = $ns->getPrefix;
last ROOT;
}
}
die "Not a template file - no namespace declaration matching\nhttp://sergeant.org/notxslt in file '$stylekey'";
}
}
my $string = parse($source_finder, $source_tree, $root_node);
$r->print($string);
return OK;
}
sub parse_style {
my $data = shift;
my $style;
while ($data =~ /\G\s*$XML::XPath::Parser::NCName\s*=\s*(["'])([^\2<]*?)\2/gco) {
my ($attr, $val) = ($1, $3);
$style->{$attr} = $val;
}
if (!exists($style->{href}) || !exists($style->{type})) {
warn "Incorrect <?xml-stylesheet?> processing instruction\n";
return;
}
return $style;
}
sub escape {
my $text = shift;
$text =~ s/&/&/g;
$text =~ s/</</g;
$text =~ s/>/>/g;
$text =~ s/"/"/g;
$text =~ s/'/'/g;
return $text;
}
sub parse {
my ($xp, $context, $node) = @_;
# examine node
my $string;
# print "Node type is ", ref($node), "\n";
for ($node->getNodeType) {
if ($_ == ELEMENT_NODE) {
local $^W;
if ($node->getPrefix eq $PREFIX) {
$string .= process_template_node($xp, $context, $node);
}
else {
# gather attributes
my @attribs;
$string .= "<" . $node->getName;
foreach my $attr ($node->getAttributeNodes) {
$string .= $attr->toString;
}
if (@{$node->getChildNodes}) {
$string .= ">";
# process children
foreach my $n ($node->getChildNodes) {
$string .= parse($xp, $context, $n);
}
$string .= "</" . $node->getName . ">";
}
else {
$string .= " />";
}
}
}
elsif ($_ == TEXT_NODE) {
$string .= $node->toString;
}
elsif ($_ == COMMENT_NODE) {
# ignore comments for now.
}
}
return $string;
}
sub process_template_node {
my ($xp, $context, $node) = @_;
my $string;
if ($node->getLocalName eq "select") {
# find match in $xp, and output.
my $match;
foreach my $attrib ($node->getAttributeNodes) {
if ($attrib->getName eq 'match') {
$match = $attrib->getValue;
}
}
die "No 'match' attribute on $PREFIX:select element" unless $match;
my $results = $xp->find($match, $context);
die "Match '$match' failed!\n" unless defined $results;
if ($results->isa('XML::XPath::NodeSet')) {
foreach my $result ($results->get_nodelist) {
$string .= $result->toString;
# $string .= escape(XML::XPath::XMLParser::as_string($result));
}
}
else {
$string .= $results->value;
# $string .= escape($results->value);
}
}
elsif ($node->getLocalName eq "for-each") {
# find match, get matching nodes, loop over matching nodes
my $match;
foreach my $attrib ($node->getAttributeNodes) {
if ($attrib->getName eq 'match') {
$match = $attrib->getValue;
}
}
die "No 'match' attribute on $PREFIX:for-each element" unless $match;
my $results = $xp->find($match, $context);
if ($results->isa('XML::XPath::NodeSet')) {
foreach my $result ($results->get_nodelist) {
foreach my $kid ($node->getChildNodes) {
$string .= parse($xp, $result, $kid);
}
}
}
else {
die "$PREFIX:for-each match doesn't match a set of nodes";
}
}
elsif ($node->getLocalName eq "exec") {
# find match and ignore results.
my $match;
foreach my $attrib ($node->getAttributeNodes) {
if ($attrib->getName eq 'match') {
$match = $attrib->getValue;
}
}
die "No 'match' attribute on $PREFIX:exec element" unless $match;
my $results = $xp->find($match, $context);
}
elsif ($node->getLocalName eq "verbatim") {
my $match;
foreach my $attrib ($node->getAttributeNodes) {
if ($attrib->getName eq 'match') {
$match = $attrib->getValue;
}
}
die "No 'match' attribute on $PREFIX:verbatim element" unless $match;
my $results = $xp->find($match, $context);
if ($results->isa('XML::XPath::NodeSet')) {
foreach my $node ($results->get_nodelist) {
if ($node->getNodeType == ELEMENT_NODE) {
$string .= "<" . $node->getName;
foreach my $attr ($node->getAttributeNodes) {
$string .= $attr->toString;
}
if ($node->getChildNodes) {
$string .= ">";
# process children
foreach my $n ($node->getChildNodes) {
$string .= parse($xp, $context, $n);
}
$string .= "</" . $node->getName . ">";
}
else {
$string .= " />";
}
}
elsif ($node->getNodeType == TEXT_NODE) {
$string .= $node->getData;
}
}
}
else {
die "$PREFIX:verbatim match doesn't match a set of nodes";
}
}
return $string;
}
{
# here we add some functions to the XPath function library.
package XML::XPath::Function;
use XML::XPath::Literal;
# why oh why wasn't set_var implemented in the first place???
sub set_var {
my $self = shift;
my ($node, @params) = @_;
if (@params != 2) {
die "Usage: set_var('name', value)\n";
}
$self->{pp}->set_var($params[0], $params[1]);
}
# and the same goes for sprintf!
# (I think the excuse there is "Java doesn't have sprintf". Bah!)
sub sprintf {
my $self = shift;
my ($node, @params) = @_;
if (!@params) {
die "Usage: sprintf(Literal [, args]*)\n";
}
my @vals = map("$_", @params);
my $format = shift @vals;
my $val = CORE::sprintf($format, @vals);
return XML::XPath::Literal->new($val);
}
}
1;
__END__
=head1 NAME
Apache::AxKit::Language::NotXSLT - Matt's non-xslt template processor
=head1 SYNOPSIS
AxAddStyleMap application/x-notxslt => \
Apache::AxKit::Language::NotXSLT
=head1 DESCRIPTION
This module implements an XML template system that looks a bit like
XSLT, but isn't. Hence the name. It uses XML::XPath, and should be
fast enough to use on small web sites dynamically, rather than using
static transformation.
=head1 AUTHOR
Matt Sergeant, matt@sergeant.org
=head1 SEE ALSO
XML::XPath(1).
=cut
# $Log: NotXSLT.pm,v $
# Revision 1.1 2002/01/13 20:45:11 matts
# Initial file checkin
#
# Revision 1.9 2000/06/12 16:14:26 matt
# Doc fixes
#
# Revision 1.8 2000/06/02 17:07:27 matt
# Fixed to use Providers
#
# Revision 1.7 2000/05/31 11:23:33 matt
# die rather than return DECLINED
#
# Revision 1.6 2000/05/19 15:47:13 matt
# Brown bag release of XSP
# Removed content_type/encoding from other modules
#
# Revision 1.5 2000/05/10 21:21:24 matt
# Support for cascading via xml_string
#
# Revision 1.4 2000/05/08 13:10:31 matt
# Updated to new XML::XPath 0.50
#
# Revision 1.3 2000/05/06 11:11:58 matt
# Implemented Languages as subclass of Language.pm
#
# Revision 1.2 2000/05/02 10:32:05 matt
# Rename to AxKit
#
1.1 xml-axkit/lib/Apache/AxKit/Language/PassiveTeX.pm
Index: PassiveTeX.pm
===================================================================
# $Id: PassiveTeX.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::PassiveTeX;
@ISA = ('Apache::AxKit::Language');
use strict;
use Apache;
use Apache::Request;
use Apache::AxKit::Language;
use Apache::AxKit::Provider;
use File::Copy ();
use File::Temp ();
use File::Path ();
use File::Basename qw(dirname);
use Cwd;
my $olddir;
my $tempdir;
sub stylesheet_exists () { 0; }
sub handler {
my $class = shift;
my ($r, $xml_provider, undef, $last_in_chain) = @_;
$tempdir = File::Temp::tempdir();
if (!$tempdir) {
die "Cannot create tempdir: $!";
}
AxKit::Debug(8, "Got tempdir: $tempdir");
$olddir = cwd;
if (my $dom = $r->pnotes('dom_tree')) {
my $source_text = $dom->toString;
delete $r->pnotes()->{'dom_tree'};
my $fh = Apache->gensym();
chdir($tempdir) || fail("Cannot cd: $!");
open($fh, ">temp.fo") || fail("Cannot write: $!");
print $fh $source_text;
close($fh) || fail("Cannot close: $!");
}
elsif (my $source_text = $r->pnotes('xml_string')) {
# ok...
my $fh = Apache->gensym();
chdir($tempdir) || fail("Cannot cd: $!");
open($fh, ">temp.fo") || fail("Cannot write: $!");
print $fh $source_text;
close($fh) || fail("Cannot close: $!");
}
else {
my $text = eval { ${$xml_provider->get_strref()} };
if ($@) {
my $fh = $xml_provider->get_fh();
chdir($tempdir) || fail("Cannot cd: $!");
File::Copy::copy($fh, "temp.fo");
}
else {
my $fh = Apache->gensym();
chdir($tempdir) || fail("Cannot cd: $!");
open($fh, ">temp.fo") || fail("Cannot write: $!");
print $fh $text;
close($fh) || fail("Cannot close: $!");
}
}
chdir($tempdir) || fail("Cannot cd: $!");
local $ENV{TEXINPUTS} = dirname($r->filename());
AxKit::Debug(8, "About to shell out to pdfxmltex - hope you have passivetex installed...");
my $retval = system("pdfxmltex temp.fo");
$retval >>= 8;
if ($retval) {
fail("pdfxmltex exited with $retval");
}
my $pdfh = Apache->gensym();
open($pdfh, "temp.pdf") || fail("Cannot open PDF: $!");
$AxKit::Cfg->AllowOutputCharset(0);
$r->content_type("application/pdf");
local $/;
$r->print(<$pdfh>);
}
sub cleanup {
chdir $olddir;
File::Path::rmtree($tempdir);
}
sub fail {
cleanup();
die @_;
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Language/Query.pm
Index: Query.pm
===================================================================
package Apache::AxKit::Language::Query;
use strict;
use DBI;
use XML::DOM;
sub handler {
my $r = shift;
my ($xmlfile, $stylesheet) = @_;
my $vals;
open(STYLE, $stylesheet) || die "Can't open stylesheet '$stylesheet': $!";
flock(STYLE, 1);
while(my $line = <STYLE>) {
$line =~ s/\r?\n//; # chomp no good if people editing on Win32
$line =~ s/#.*//; # strip comments
next if $line =~ /^\s*$/; # ignore blank (or whitespace) lines
my ($key, $val) = split(/\s*=\s*/, $line, 2);
next unless $key;
$vals->{$key} = $val;
}
my $dsn;
if ($vals->{CONNECT_STRING}) {
$dsn = $vals->{CONNECT_STRING};
}
else {
$dsn = "dbi:$vals->{DRIVER}:$vals->{CONNECT_EXTRA}";
}
my %attr;
foreach my $key (keys %$vals) {
next unless $key =~ /^ATTR_(.*)$/;
$attr{$1} = $vals->{$key};
}
my $dbh = DBI->connect($dsn, $vals->{USER}, $vals->{PASSWORD}, \%attr);
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Language/Sablot.pm
Index: Sablot.pm
===================================================================
# $Id: Sablot.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::Sablot;
use strict;
use vars qw/@ISA $xslt_processor $handler/;
use XML::Sablotron 0.40 ();
use Apache;
use Apache::Request;
use Apache::Log;
use Apache::AxKit::Language;
@ISA = 'Apache::AxKit::Language';
sub handler {
my $class = shift;
my ($r, $xml, $style) = @_;
my ($xmlstring);
if (my $dom = $r->pnotes('dom_tree')) {
$xmlstring = $dom->toString;
delete $r->pnotes()->{'dom_tree'};
}
else {
$xmlstring = $r->pnotes('xml_string');
}
if (!$xmlstring) {
$xmlstring = eval {${$xml->get_strref()}};
if ($@) {
my $fh = $xml->get_fh();
local $/;
$xmlstring = <$fh>;
}
}
my $stylestring = ${$style->get_strref()};
my $retcode;
# get request form/querystring parameters
my $cgi = Apache::Request->instance($r);
my @xslt_params;
foreach my $param ($cgi->param) {
push @xslt_params, $param, $cgi->param($param);
}
# get and register handler object
$handler->set_apache($r);
$handler->set_ext_ent_handler($xml->get_ext_ent_handler());
$retcode = $xslt_processor->RunProcessor(
"arg:/template", "arg:/xml_resource", "arg:/result",
\@xslt_params,
["template" => $stylestring, "xml_resource" => $xmlstring]
);
$xslt_processor->ClearError();
if ($retcode) {
throw Apache::AxKit::Exception::Declined(
reason => "Sablotron failed to process XML file"
);
}
print $xslt_processor->GetResultArg("result");
$xslt_processor->FreeResultArgs();
}
END {
$xslt_processor->UnregHandler(0, $handler);
$xslt_processor->UnregHandler(1, $handler);
$xslt_processor->UnregHandler(3, $handler);
}
package Apache::AxKit::Language::Sablot::Handler;
sub set_apache {
my $self = shift;
$self->{apache} = shift;
}
sub set_ext_ent_handler {
my $self = shift;
$self->{ext_ent_handler} = shift;
}
my @levels = qw(debug info warn error crit);
sub MHMakeCode {
my $self = shift;
my $processor = shift;
my ($severity, $facility, $code) = @_;
return $code;
}
sub MHLog {
my $self = shift;
my $processor = shift;
my $r = $self->{apache};
my ($code, $level, @fields) = @_;
return 1 unless $r->dir_config('AxSablotLogMessages');
no strict 'refs';
my $method = $levels[$level];
$r->log->$method("[AxKit] [Sablotron] Log: ", join(' :: ', @fields));
return 1;
}
sub MHError {
my $self = shift;
my $processor = shift;
my $r = $self->{apache};
my ($code, $level, @fields) = @_;
no strict 'refs';
my $method = $levels[$level];
$r->log->$method("[AxKit] [Sablotron] [$code] Error: ", join(' :: ', @fields));
return 1;
}
sub SHGetAll {
my $self = shift;
my $processor = shift;
my ($scheme, $rest) = @_;
my $handler = $self->{ext_ent_handler};
my $uri = $scheme . $rest;
$uri =~ s/^axkit\///;
AxKit::Debug(8, "Sablot: Looking up URI: $uri\n");
return $handler->(undef, undef, $uri);
}
sub XHDocumentInfo {
my ($self, $processor, $type, $encoding) = @_;
AxKit::Apache->request->content_type("$type; charset=$encoding");
}
BEGIN {
sub new {
my $class = shift;
return bless {}, $class;
}
package Apache::AxKit::Language::Sablot;
$xslt_processor = XML::Sablotron->new();
$xslt_processor->SetBase("axkit:");
$handler = Apache::AxKit::Language::Sablot::Handler->new();
$xslt_processor->RegHandler(0, $handler);
$xslt_processor->RegHandler(1, $handler);
$xslt_processor->RegHandler(3, $handler);
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Language/XMLNewsNITF.pm
Index: XMLNewsNITF.pm
===================================================================
# $Id: XMLNewsNITF.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::XMLNewsNITF;
use strict;
use vars qw/@ISA/;
use Apache::Constants;
use XMLNews::HTMLTemplate;
use Apache::AxKit::Language;
@ISA = 'Apache::AxKit::Language';
sub handler {
my $class = shift;
my ($r, $xml, $style) = @_;
my $template_processor = XMLNews::HTMLTemplate->new();
$template_processor->readTemplate($style->get_fh());
$template_processor->applyTemplate($r, $xml->get_fh(), undef);
return OK;
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Language/XMLNewsRDF.pm
Index: XMLNewsRDF.pm
===================================================================
# $Id: XMLNewsRDF.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::XMLNewsRDF;
use strict;
use vars qw/@ISA/;
use Apache::Constants;
use XMLNews::HTMLTemplate;
use Apache::AxKit::Language;
@ISA = 'Apache::AxKit::Language';
sub handler {
my $class = shift;
my ($r, $xml, $style) = @_;
my $template_processor = XMLNews::HTMLTemplate->new();
$template_processor->readTemplate($style->get_fh());
$template_processor->applyTemplate($r, undef, $xml->get_fh());
return OK;
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Language/XPathScript.pm
Index: XPathScript.pm
===================================================================
# $Id: XPathScript.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::XPathScript;
use strict;
use vars qw(@ISA $VERSION $stash );
use Apache;
use Apache::File;
use XML::XPath 1.00;
use XML::XPath::XMLParser;
use XML::XPath::Node;
use XML::XPath::NodeSet;
use XML::Parser;
use Apache::AxKit::Provider;
use Apache::AxKit::Language;
use Apache::AxKit::Cache;
use Apache::AxKit::Exception;
use Apache::AxKit::CharsetConv;
@ISA = 'Apache::AxKit::Language';
$VERSION = '0.05';
sub handler {
my $class = shift;
my ($r, $xml_provider, $style_provider) = @_;
my $xpath = XML::XPath->new();
my $source_tree;
my $xml_parser = XML::Parser->new(
ErrorContext => 2,
Namespaces => $XML::XPath::VERSION < 1.07 ? 1 : 0,
ParseParamEnt => 1,
);
my $parser = XML::XPath::XMLParser->new(parser => $xml_parser);
local $Apache::AxKit::Language::XPathScript::local_ent_handler;
if (my $entity_handler = $xml_provider->get_ext_ent_handler()) {
# warn "XPathScript: setting entity_handler\n";
$xml_parser->setHandlers(
ExternEnt => $entity_handler,
);
$Apache::AxKit::Language::XPathScript::local_ent_handler = $entity_handler;
}
AxKit::Debug(6, "XPathScript: Getting XML Source");
if (my $dom = $r->pnotes('dom_tree')) {
# dom_tree is an XML::LibXML DOM
$source_tree = $parser->parse($dom->toString);
delete $r->pnotes()->{'dom_tree'};
}
elsif (my $xml = $r->pnotes('xml_string')) {
# warn "Parsing from string : $xml\n";
eval {
$source_tree = $parser->parse($xml);
};
if ($@) {
throw Apache::AxKit::Exception::Error(-text => "Parse of xml_string failed: $@");
}
}
else {
$source_tree = get_source_tree($xml_provider, $parser);
}
$xpath->set_context($source_tree);
my $mtime = $style_provider->mtime();
my $style_key = $style_provider->key();
my $package = get_package_name($style_key);
AxKit::Debug(6, "Checking stylesheet mtime: $mtime\n");
if ($stash->{$style_key}
&& exists($stash->{$style_key}{mtime})
&& !$style_provider->has_changed($stash->{$style_key}{mtime})
&& check_inc_mtime($stash->{$style_key}{mtime}, $style_provider, $stash->{$style_key}{includes})) {
# cached... just exec.
AxKit::Debug(7, "Using stylesheet cache\n");
}
else {
# recompile stylesheet.
AxKit::Debug(6, "Recompiling stylesheet: $style_key\n");
compile($package, $style_provider);
$stash->{$style_key}{mtime} = get_mtime($class, $style_provider);
}
my $old_status = $r->status;
no strict 'refs';
my $cv = \&{"$package\::handler"};
local $Apache::AxKit::Language::XPathScript::xp = $xpath;
my $t = {};
local $Apache::AxKit::Language::XPathScript::trans = $t;
local $Apache::AxKit::Language::XPathScript::style_provider = $style_provider;
AxKit::Debug(7, "Running XPathScript script\n");
local $^W;
eval {
$cv->($r, $xpath, $t);
};
if ($@) {
AxKit::Debug(1, "XPathScript error: $@");
throw $@;
}
if (!$r->pnotes('xml_string') &&
!$r->dir_config('XPSNoApplyTemplatesOnEmptyOutput')) { # no output? Try apply_templates
print Apache::AxKit::Language::XPathScript::Toys::apply_templates();
}
# warn "Run\n";
$Apache::AxKit::Language::XPathScript::xp = undef;
$Apache::AxKit::Language::XPathScript::trans = undef;
$Apache::AxKit::Language::XPathScript::style_provider = undef;
# warn "Returning $old_status\n";
return $r->status($old_status);
}
sub get_source_tree {
my ($provider, $parser) = @_;
my $source_tree;
AxKit::Debug(7, "XPathScript: reparsing file");
eval {
my $fh = $provider->get_fh();
# warn("parsing FH $fh with parser $parser\n");
local $/;
my $contents = <$fh>;
# warn("FH contains: $contents\n");
$source_tree = $parser->parse($contents);
# warn("Parse completed\n");
close($fh);
};
if ($@) {
# warn("parse_fh failed\n");
$source_tree = $parser->parse(${ $provider->get_strref() });
}
# warn("get_source_tree = $source_tree\n");
return $source_tree;
}
sub check_inc_mtime {
my ($mtime, $provider, $includes) = @_;
for my $inc (@$includes) {
# warn "Checking mtime for $inc\n";
my $inc_provider = Apache::AxKit::Provider->new(
AxKit::Apache->request,
uri => $inc,
rel => $provider,
);
if ($inc_provider->has_changed($mtime)) {
# warn "$inc newer (" . $inc_provider->mtime() . ") than last compile ($mtime) causing recompile\n";
return;
}
}
return 1;
}
sub extract {
my ($provider,$scalar_output) = @_;
my $contents;
eval {
my $fh = $provider->get_fh();
local $/;
$contents = <$fh>;
};
if ($@) {
$contents = ${ $provider->get_strref() };
}
my $r = AxKit::Apache->request();
if (my $charset = $r->dir_config('XPathScriptCharset')) {
AxKit::Debug(8, "XPS: got charset: $charset");
my $map = Apache::AxKit::CharsetConv->new($charset, "utf-8") || die "No such charset: $charset";
$contents = $map->convert($contents);
}
my $key = $provider->key();
$stash->{$key}{includes} = [];
AxKit::Debug(10, "XPathScript: extracting from '$key' contents: $contents\n");
my $script;
my $line = 1;
while ($contents =~ /\G(.*?)(<!--\#include|<%=?)/gcs) {
my ($text, $type) = ($1, $2);
$line += $text =~ tr/\n//;
$text =~ s/\|/\\\|/g;
if($scalar_output) {
$script .= "\$__OUTPUT.=q|$text|;";
} else {
$script .= "print q|$text|;";
}
$script .= "\n#line $line $key\n";
if ($type eq '<%=') {
$contents =~ /\G(.*?)%>/gcs || die "No terminating '%>' after line $line ($key)";
my $perl = $1;
if(!$scalar_output) {
$script .= "print(do { $perl });\n";
} else {
$script .= "\$__OUTPUT.=join('',(do { $perl }));\n";
}
$line += $perl =~ tr/\n//;
}
elsif ($type eq '<!--#include') {
my %params;
while ($contents =~ /\G(\s+(\w+)\s*=\s*(["'])([^\3]*?)\3|\s*-->)/gcs) {
last if $1 eq '-->';
$params{$2} = $4;
}
if (!$params{file}) {
die "No matching file attribute in #include at line $line ($key)";
}
AxKit::Debug(10, "About to include file $params{file}");
$script .= include_file($params{file}, $provider, $scalar_output);
AxKit::Debug(10, "include done");
}
else {
$contents =~ /\G(.*?)%>/gcs || die "No terminating '%>' after line $line ($key)";
my $perl = $1;
$perl =~ s/;?$/;/s; # add on ; if its missing. As in <% $foo = 'Hello' %>
$script .= $perl;
$line += $perl =~ tr/\n//;
}
}
if ($contents =~ /\G(.*)/gcs) {
my ($text) = ($1);
$text =~ s/\|/\\\|/g;
if ($scalar_output) {
$script .= "\$__OUTPUT.=q|$text|;";
} else {
$script .= "print q|$text|;";
}
}
return $script;
}
sub compile {
my ($package, $provider) = @_;
my $script = extract($provider);
my $eval = join('',
'package ',
$package,
'; use Apache qw(exit);',
'use XML::XPath::Node;',
'Apache::AxKit::Language::XPathScript::Toys->import;',
'sub handler {',
'my ($r, $xp, $t) = @_;',
"\n#line 1 " . $provider->key() . "\n",
$script,
"\n}",
);
local $^W;
AxKit::Debug(10, "Compiling script:\n$eval\n");
eval $eval;
if ($@) {
AxKit::Debug(1, "Compilation failed: $@");
throw $@;
}
}
sub include_file {
my ($filename, $provider, $script_output) = @_;
# return if already included
my $key = $provider->key();
return '' if grep {$_ eq $filename} @{$stash->{$key}{includes}};
push @{$stash->{$key}{includes}}, $filename;
my $inc_provider = Apache::AxKit::Provider->new(
AxKit::Apache->request,
uri => $filename,
rel => $provider,
);
return extract($inc_provider, $script_output);
}
sub XML::XPath::Function::document {
# warn "Document function called\n";
return unless $Apache::AxKit::Language::XPathScript::local_ent_handler;
my $self = shift;
my ($node, @params) = @_;
die "document: Function takes 1 parameter\n" unless @params == 1;
my $xml_parser = XML::Parser->new(
ErrorContext => 2,
Namespaces => $XML::XPath::VERSION < 1.07 ? 1 : 0,
# ParseParamEnt => 1,
);
my $parser = XML::XPath::XMLParser->new(parser => $xml_parser);
my $results = XML::XPath::NodeSet->new();
my $uri = $params[0];
my $newdoc;
if ($uri =~ /^\w\w+:/) { # assume it's scheme://foo uri
eval {
# warn "Trying to parse $params[0]\n";
$newdoc = $parser->parse(
$Apache::AxKit::Language::XPathScript::local_ent_handler->(
undef, undef, $uri
)
);
# warn "Parsed OK into $newdoc\n";
};
if (my $E = $@) {
if ($E->isa('Apache::AxKit::Exception::IO')) {
AxKit::Debug(2, $E);
}
else {
throw Apache::AxKit::Exception::Error(-text => "Parse of '$uri' failed: $E");
};
}
}
else {
# warn("Parsing local: $uri\n");
my $provider = Apache::AxKit::Provider->new(
AxKit::Apache->request,
uri => $uri,
);
$newdoc = get_source_tree($provider, $parser);
}
$results->push($newdoc) if $newdoc;
return $results;
}
sub get_mtime {
my $class = shift;
my ($provider) = @_;
# warn "get_mtime\n";
my $mtime = $provider->mtime();
my $filename = $provider->key();
# warn "mtime: $filename = $mtime\n";
if (!$stash->{$filename}) {
# compile stylesheet
compile(get_package_name($filename), $provider);
$stash->{$filename}{mtime} = $mtime;
return 0;
}
for my $inc (@{$stash->{$filename}{includes}}) {
my $inc_provider = Apache::AxKit::Provider->new(
AxKit::Apache->request,
uri => $inc,
rel => $provider,
);
# warn "Checking mtime of $inc\n";
if ($inc_provider->has_changed($mtime)) {
$mtime = $inc_provider->mtime();
}
}
# warn "returning $mtime\n";
return $mtime;
}
sub get_package_name {
my $filename = shift;
# Escape everything into valid perl identifiers
$filename =~ s/([^A-Za-z0-9_\/])/sprintf("_%2x",unpack("C",$1))/eg;
# second pass cares for slashes and words starting with a digit
$filename =~ s{
(/+) # directory
(\d?) # package's first character
}[
"::" . (length $2 ? sprintf("_%2x",unpack("C",$2)) : "")
]egx;
return "Apache::AxKit::Language::XPathScript::ROOT$filename";
}
{
package Apache::AxKit::Language::XPathScript::Toys;
use XML::XPath::Node;
use Apache::AxKit::Exception;
use vars '@ISA', '@EXPORT';
use Exporter;
@ISA = ('Exporter');
@EXPORT = qw(
findnodes
findvalue
findvalues
findnodes_as_string
apply_templates
matches
set_namespace
import_template
DO_SELF_AND_KIDS
DO_SELF_ONLY
DO_NOT_PROCESS
);
sub DO_SELF_AND_KIDS () { return 1; }
sub DO_SELF_ONLY () { return -1; }
sub DO_NOT_PROCESS () { return 0; }
sub MAX_DEPTH () { return 32; }
sub import_template {
my ($filename, $local_changes) = @_;
my ($script) = Apache::AxKit::Language::XPathScript::include_file($filename,$Apache::AxKit::Language::XPathScript::style_provider, 1);
# changes may be local to this imported template, or global (default).
my ($setup_t);
if ($local_changes) {
$setup_t = 'local $Apache::AxKit::Language::XPathScript::trans = clone($Apache::AxKit::Language::XPathScript::trans);';
}
$script = join('',
'use strict;',
'sub { ',
'my ($node, $real_local_t) = @_;',
'local $Apache::AxKit::Language::XPathScript::xp = $node;',
$setup_t,
'my ($t) = $Apache::AxKit::Language::XPathScript::trans;',
'my ($__OUTPUT);',
$script,';',
'$real_local_t->{pre} = $__OUTPUT;',
'return -1;',
'}');
return eval($script);
}
sub findnodes {
$Apache::AxKit::Language::XPathScript::xp->findnodes(@_);
}
sub findvalue {
$Apache::AxKit::Language::XPathScript::xp->findvalue(@_);
}
sub findvalues {
my @nodes = findnodes(@_);
map { findvalue('.', $_) } @nodes;
}
sub findnodes_as_string {
$Apache::AxKit::Language::XPathScript::xp->findnodes_as_string(@_);
}
sub matches {
$Apache::AxKit::Language::XPathScript::xp->matches(@_);
}
sub set_namespace {
eval {
$Apache::AxKit::Language::XPathScript::xp->set_namespace(@_);
};
if ($@) {
AxKit::Debug(3, "set_namespace failed: $@");
}
}
# quieten warnings when compiling this module
sub apply_templates (;$@);
sub apply_templates (;$@) {
unless (@_) {
return apply_templates(findnodes('/'));
}
my ($arg1, @args) = @_;
if (!ref($arg1)) {
# called with a path to find
# warn "apply_templates with path '$arg1'\n";
$arg1 = findnodes($arg1, @args);
# return apply_templates($nodes);
}
my $retval = '';
if (ref($arg1) eq "HASH") {
# warn "apply_templates with a hash\n";
local $Apache::AxKit::Language::XPathScript::trans = $arg1;
return apply_templates(@args);
}
elsif ($arg1->isa('XML::XPath::NodeSet')) {
# warn "apply_templates with a NodeSet\n";
foreach my $node ($arg1->get_nodelist) {
$retval .= translate_node($node);
}
}
else {
# warn "apply_templates with a list of " , 1 + @args, " nodes? : ", ref($arg1), "\n";
$retval .= translate_node($arg1);
foreach my $node (@args) {
$retval .= translate_node($node);
}
}
return $retval;
}
sub _apply_templates {
my @nodes = @_;
my $retval = '';
foreach my $node (@nodes) {
$retval .= translate_node($node);
}
return $retval;
}
sub translate_node {
my $node = shift;
local $^W;
my $translations = $Apache::AxKit::Language::XPathScript::trans;
if ($node->isTextNode) {
my $trans = $translations->{'text()'};
if (!$trans) { return $node->toString; }
if (my $code = $trans->{testcode}) {
my $t = {};
my $retval = $code->($node, $t);
if ($retval && %$t) {
foreach my $tkey (keys %$t) {
$trans->{$tkey} = $t->{$tkey};
}
}
}
return $trans->{pre} . $node->toString . $trans->{post};
}
if (!$node->isElementNode) {
# don't output top-level PI's
if ($node->isPINode) {
my $retstring = eval {
if ($node->getParentNode->getParentNode) {
return $node->toString;
}
return '';
};
return $retstring || '';
}
return $node->toString;
}
# warn "translate_node: ", $node->getName, "\n";
my $node_name = $node->getName;
my $trans = $translations->{$node_name};
if (!$trans) {
$node_name = '*';
$trans = $translations->{$node_name};
}
if (!$trans) {
# warn "Default trans\n";
if (my @children = $node->getChildNodes) {
return start_tag($node) .
_apply_templates(@children) .
end_tag($node);
}
else {
return empty_tag($node);
}
}
local $^W;
my $dokids = 1;
my $search;
my $testcode_output = 0;
my $t = {};
if ($trans->{testcode}) {
# warn "eval testcode\n";
$testcode_output = 1;
my $result;
my $testcode = $trans->{testcode};
my $depth = 0;
while (1) {
$result = $testcode->($node, $t);
# warn "Testcode returned: $result\n";
if (defined($t->{testcode}) &&
ref($t->{testcode}) eq "CODE") {
if ($depth++ > MAX_DEPTH) {
die "Max Depth of ", MAX_DEPTH, " reached on testcode eval!";
}
$testcode = $t->{testcode};
$t = {};
} else {
last;
}
}
# warn "Here with $result\n";
if ($result =~ /\D/) {
$dokids = 0;
$search = $result;
}
elsif ($result == DO_NOT_PROCESS) {
# don't process anything.
return;
}
elsif ($result == DO_SELF_ONLY) {
# -1 means don't do children.
$dokids = 0;
}
elsif ($result == DO_SELF_AND_KIDS) {
# do kids
}
# warn "Here with dokids => $dokids, search => $search\n";
}
local $translations->{$node_name};
# copy old values in
%{$translations->{$node_name}} = %$trans;
if (%$t) {
foreach my $key (keys %$t) {
$translations->{$node_name}{$key} = $t->{$key};
}
$trans = $translations->{$node_name};
}
# default: process children too.
my $pre = interpolate($node, $trans->{pre}, $testcode_output) .
($trans->{showtag} ? start_tag($node) : '') .
interpolate($node, $trans->{prechildren}, $testcode_output);
my $post = interpolate($node, $trans->{postchildren}, $testcode_output) .
($trans->{showtag} ? end_tag($node) : '') .
interpolate($node, $trans->{post}, $testcode_output);
if ($dokids) {
my $middle = '';
for my $kid ($node->getChildNodes()) {
if ($kid->isElementNode) {
$middle .= interpolate($node, $trans->{prechild}) .
_apply_templates($kid) .
interpolate($node, $trans->{postchild});
}
else {
$middle .= _apply_templates($kid);
}
}
return $pre . $middle . $post;
}
elsif ($search) {
my $middle = '';
for my $kid (findnodes($search, $node)) {
if ($kid->isElementNode) {
$middle .= interpolate($node, $trans->{prechild}) .
_apply_templates($kid) .
interpolate($node, $trans->{postchild});
}
else {
$middle .= _apply_templates($kid);
}
}
return $pre . $middle . $post;
}
else {
return $pre . $post;
}
}
sub start_tag {
my ($node) = @_;
my $name = $node->getName;
return '' unless $name;
my $string = "<" . $name;
foreach my $ns ($node->getNamespaceNodes) {
$string .= $ns->toString;
}
foreach my $attr ($node->getAttributeNodes) {
$string .= $attr->toString;
}
$string .= ">";
return $string;
}
sub end_tag {
my ($node) = @_;
if (my $name = $node->getName) {
return "</" . $name . ">";
}
else {
return '';
}
}
sub empty_tag {
my ($node) = @_;
my $name = $node->getName;
return '' unless $name;
my $string = "<" . $name;
foreach my $ns ($node->getNamespaceNodes) {
$string .= $ns->toString;
}
foreach my $attr ($node->getAttributeNodes) {
$string .= $attr->toString;
}
$string .= " />";
return $string;
}
sub interpolate {
my ($node, $string, $ignore) = @_;
return $string if $XPathScript::DoNotInterpolate || $ignore;
return $string unless AxKit::Apache->request->dir_config('AxXPSInterpolate');
my $new = '';
while ($string =~ m/\G(.*?)\{(.+?)\}/gcs) {
my ($pre, $path) = ($1, $2);
$new .= $pre;
$new .= $node->findvalue($path);
}
$string =~ /\G(.*)/gcs;
$new .= $1 if defined $1;
return $new;
}
# make a clone, but copy subs.
sub clone {
my ($a) = @_;
my ($b);
if (ref($a) eq "HASH") {
$b = {};
foreach my $key (keys(%$a)) {
my ($copy) = clone($a->{$key});
$b->{$key} = $copy;
}
}
else {
# copy as is
$b = $a;
}
return $b;
}
1;
}
1;
__END__
=head1 NAME
Apache::AxKit::Language::XPathScript - An XML Stylesheet Language
=head1 SYNOPSIS
AxAddStyleMap "application/x-xpathscript => \
Apache::AxKit::Language::XPathScript"
=head1 DESCRIPTION
This documentation has been removed. The definitive reference for
XPathScript is now at http://axkit.org/docs/xpathscript/guide.dkb
in DocBook format.
=cut
1.1 xml-axkit/lib/Apache/AxKit/Language/XSLT.pm
Index: XSLT.pm
===================================================================
# $Id: XSLT.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::XSLT;
use strict;
use XML::XSLT;
use Apache::AxKit::Language;
use vars qw/@ISA/;
@ISA = 'Apache::AxKit::Language';
sub handler {
my $class = shift;
my ($r, $xml, $style) = @_;
# warn "Parsing stylefile '$stylefile'\n";
my $parser = XML::XSLT->new($style->get_strref(), "STRING");
if (my $dom_tree = $r->pnotes('dom_tree')) {
# warn "Parsing dom_tree: ", $dom_tree->toString, "\n";
my $xml_string = $dom_tree->toString;
delete $r->pnotes()->{'dom_tree'};
$parser->transform_document($xml_string, "STRING");
}
elsif (my $xmlstr = $r->pnotes('xml_string')) {
# warn "Parsing string:\n$xml\n";
$parser->transform_document($xmlstr, "STRING");
}
else {
# warn "Parsing file '$xmlfile'\n";
$parser->transform_document($xml->get_strref(), "STRING");
}
$r->pnotes('xml_string', $parser->result_tree()->toString);
$parser->dispose();
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Language/XSP.pm
Index: XSP.pm
===================================================================
# $Id: XSP.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
package Apache::AxKit::Language::XSP;
use strict;
use Apache::AxKit::Language;
use Apache::Request;
use Apache::AxKit::Exception;
use Apache::AxKit::Cache;
use vars qw/@ISA/;
@ISA = ('Apache::AxKit::Language');
sub stylesheet_exists () { 0; }
sub get_mtime {
return 30; # 30 days in the cache?
}
my $cache;
# useful for debugging - not actually used by AxKit:
# sub get_code {
# my $filename = shift;
#
# # cannot register - no $AxKit::Cfg...
# # _register_me_and_others();
# __PACKAGE__->register();
#
# my $package = get_package_name($filename);
# my $parser = get_parser($package, $filename);
# return $parser->parsefile($filename);
# }
sub handler {
my $class = shift;
my ($r, $xml, undef, $last_in_chain) = @_;
_register_me_and_others();
# warn "XSP Parse: $xmlfile\n";
my $key = $xml->key();
my $package = get_package_name($key);
my $handler = AxKit::XSP::SAXHandler->new_handler(
XSP_Package => $package,
XSP_Line => $key,
);
my $parser = AxKit::XSP::SAXParser->new(
provider => $xml,
Handler => $handler,
);
local $Apache::AxKit::Language::XSP::ResNamespaces = $r->dir_config('XSPResNamespaces');
my $to_eval;
eval {
if (my $dom_tree = $r->pnotes('dom_tree')) {
AxKit::Debug(5, 'XSP: parsing dom_tree');
$to_eval = $parser->parse($dom_tree->toString);
delete $r->pnotes()->{'dom_tree'};
}
elsif (my $xmlstr = $r->pnotes('xml_string')) {
if ($r->no_cache()
|| !defined &{"${package}::handler"}) {
AxKit::Debug(5, 'XSP: parsing xml_string');
$to_eval = $parser->parse($xmlstr);
}
else {
AxKit::Debug(5, 'XSP: not reparsing xml_string (cached)');
}
}
else {
# check mtime.
my $mtime = $xml->mtime();
no strict 'refs';
if (exists($cache->{$key})
&& !$xml->has_changed($cache->{$key}{mtime})
&& defined &{"${package}::handler"}
)
{
# cached
AxKit::Debug(5, 'XSP: xsp script cached');
}
else {
AxKit::Debug(5, 'XSP: parsing fh');
$to_eval = eval {
$parser->parse($xml->get_fh());
} || $parser->parse(${ $xml->get_strref() });
$cache->{$key}{mtime} = $mtime;
}
}
};
if ($@) {
throw Apache::AxKit::Exception::Error(
-text => "Parse of '$key' failed: $@"
);
}
if ($to_eval) {
undef &{"${package}::handler"};
AxKit::Debug(5, 'Recompiling XSP script');
AxKit::Debug(10, $to_eval);
eval $to_eval;
AxKit::Debug(5, 'XSP Compilation finished');
if ($@) {
my $line = 1;
$to_eval =~ s/\n/"\n".++$line." "/eg;
warn("Script:\n1 $to_eval\n");
die "Failed to parse: $@";
}
}
no strict 'refs';
my $cv = \&{"$package\::handler"};
my $cgi = Apache::Request->instance($r);
$r->no_cache(1);
my $xsp_cache = Apache::AxKit::Cache->new($r, $package, $package->cache_params($r, $cgi));
if (!$package->has_changed($xsp_cache->mtime()) &&
!$xml->has_changed($xsp_cache->mtime())) {
AxKit::Debug(3, "XSP results cached");
$r->print($xsp_cache->read);
return;
}
my $dom = $cv->($r, $cgi);
$r->pnotes('dom_tree', $dom);
$r->print($dom->toString) if $last_in_chain;
$xsp_cache->write( $dom->toString );
}
sub register {
my $class = shift;
no strict 'refs';
$class->register_taglib(${"${class}::NS"});
}
sub _register_me_and_others {
# warn "Loading taglibs\n";
foreach my $package ($AxKit::Cfg->XSPTaglibs()) {
# warn "Registering taglib: $package\n";
AxKit::load_module($package);
$package->register();
}
}
sub register_taglib {
my $class = shift;
my $namespace = shift;
# warn "Register taglib: $namespace => $class\n";
$Apache::AxKit::Language::XSP::tag_lib{$namespace} = $class;
}
sub is_xsp_namespace {
my ($ns) = @_;
# a uri of the form "res:perl/<spec>" turns into an implicit loading of
# the module indicated by <spec> (after slashes are turned into
# double-colons). an example uri is "res:perl/My/Cool/Module".
if ($Apache::AxKit::Language::XSP::ResNamespaces && $ns =~ m/^res:perl\/(.*)$/) {
my $package = $1;
$package =~ s/\//::/g;
AxKit::load_module($package);
$package->register();
}
return 1 if $Apache::AxKit::Language::XSP::tag_lib{$ns};
}
sub get_package_name {
my $filename = shift;
# Escape everything into valid perl identifiers
$filename =~ s/([^A-Za-z0-9_\/])/sprintf("_%2x",unpack("C",$1))/eg;
# second pass cares for slashes and words starting with a digit
$filename =~ s{
(/+) # directory
(\d?) # package's first character
}[
"::" . (length $2 ? sprintf("_%2x",unpack("C",$2)) : "")
]egx;
return "Apache::AxKit::Language::XSP::ROOT$filename";
}
############################################################
# SAX Handler code
############################################################
package AxKit::XSP::SAXHandler;
sub new_handler {
my ($type, %self) = @_;
return bless \%self, $type;
}
sub start_expr {
my ($e) = @_;
my $element = { Name => "expr",
NamespaceURI => $AxKit::XSP::Core::NS,
Attributes => [ ],
Parent => $e->{Current_Element}->{Parent},
# OldParent => $e->{Current_Element},
};
# warn "start_expr: $e->{Current_Element}->{Name}\n";
$e->start_element($element);
}
sub end_expr {
my ($e) = @_;
my $parent = $e->{Current_Element}->{Parent};
my $element = { Name => "expr",
NamespaceURI => $AxKit::XSP::Core::NS,
Attributes => [ ],
Parent => $parent,
};
# warn "end_expr: $parent->{Name}\n";
$e->end_element($element);
}
sub append_to_script {
my ($e, $code) = @_;
$e->{XSP_Script} .= $code;
}
sub manage_text {
my ($e, $set, $go_back) = @_;
$go_back ||= 0;
my $depth = $e->depth();
if (defined($set) && $set >= 0) {
$e->{XSP_Manage_Text}[$depth - $go_back] = $set;
}
else {
if (defined($set) && $set == -1) {
# called from characters handler, rather than expr
return $e->{XSP_Manage_Text}[$depth];
}
return $e->{XSP_Manage_Text}[$depth - 1];
}
}
sub depth {
my ($e) = @_;
my $element = $e->{Current_Element};
my $depth = 0;
while ($element = $element->{Parent}) {
$depth++;
}
return $depth;
}
sub current_element {
my $e = shift;
my $tag = $e->{Current_Element}{Name};
$tag =~ s/^(.*:)//;
return $tag;
}
sub start_document {
my $e = shift;
$e->{XSP_Script} = join("\n",
"package $e->{XSP_Package}; \@$e->{XSP_Package}::ISA = ('Apache::AxKit::Language::XSP::Page');",
"#line 2 ".$e->{XSP_Line}."\n",
"use Apache;",
"use XML::LibXML;",
);
foreach my $ns (keys %Apache::AxKit::Language::XSP::tag_lib) {
my $pkg = $Apache::AxKit::Language::XSP::tag_lib{$ns};
my $sub;
if (($sub = $pkg->can("start_document")) && ($sub != \&start_document)) {
$e->{XSP_Script} .= $sub->($e);
}
elsif ($sub = $pkg->can("parse_init")) {
$e->{XSP_Script} .= $sub->($e);
}
}
}
sub end_document {
my $e = shift;
foreach my $ns (keys %Apache::AxKit::Language::XSP::tag_lib) {
my $pkg = $Apache::AxKit::Language::XSP::tag_lib{$ns};
my $sub;
if (($sub = $pkg->can("end_document")) && ($sub != \&end_document)) {
$e->{XSP_Script} .= $sub->($e);
}
elsif ($sub = $pkg->can("parse_final")) {
$e->{XSP_Script} .= $sub->($e);
}
}
$e->{XSP_Script} .= "return \$document\n}\n";
return $e->{XSP_Script};
}
sub start_element {
my $e = shift;
my $element = shift;
$element->{Parent} ||= $e->{Current_Element};
$e->{Current_Element} = $element;
my $ns = $element->{NamespaceURI};
# warn "START-NS: $ns : $element->{Name}\n";
my @attribs;
for my $attr (@{$element->{Attributes}}) {
if ($attr->{Name} eq 'xmlns') {
unless (Apache::AxKit::Language::XSP::is_xsp_namespace($attr->{Value})) {
$e->{Current_NS}{'#default'} = $attr->{Value};
}
}
elsif ($attr->{Name} =~ /^xmlns:(.*)$/) {
my $prefix = $1;
unless (Apache::AxKit::Language::XSP::is_xsp_namespace($attr->{Value})) {
$e->{Current_NS}{$prefix} = $attr->{Value};
}
}
else {
push @attribs, $attr;
}
}
$element->{Attributes} = \@attribs;
if (!defined($ns) ||
!exists($Apache::AxKit::Language::XSP::tag_lib{ $ns }))
{
$e->manage_text(0); # set default for non-xsp tags
$e->{XSP_Script} .= AxKit::XSP::DefaultHandler::start_element($e, $element);
}
else {
# local $^W;
$element->{Name} =~ s/^(.*)://;
my $prefix = $1;
my $tag = $element->{Name};
my %attribs;
# this is probably a bad hack to turn xsp:name="value" into name="value"
for my $attr (@{$element->{Attributes}}) {
$attr->{Name} =~ s/^\Q$prefix\E://;
$attribs{$attr->{Name}} = $attr->{Value};
}
$e->manage_text(1); # set default for xsp tags
my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
my $sub;
if (($sub = $pkg->can("start_element")) && ($sub != \&start_element)) {
$e->{XSP_Script} .= $sub->($e, $element);
}
elsif ($sub = $pkg->can("parse_start")) {
$e->{XSP_Script} .= $sub->($e, $tag, %attribs);
}
}
}
sub end_element {
my $e = shift;
my $element = shift;
my $ns = $element->{NamespaceURI};
# warn "END-NS: $ns : $_[0]\n";
if (!defined($ns) ||
!exists($Apache::AxKit::Language::XSP::tag_lib{ $ns }))
{
$e->{XSP_Script} .= AxKit::XSP::DefaultHandler::end_element($e, $element);
}
else {
# local $^W;
$element->{Name} =~ s/^(.*)://;
my $tag = $element->{Name};
my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
my $sub;
if (($sub = $pkg->can("end_element")) && ($sub != \&end_element)) {
$e->{XSP_Script} .= $sub->($e, $element);
}
elsif ($sub = $pkg->can("parse_end")) {
$e->{XSP_Script} .= $sub->($e, $tag);
}
}
$e->{Current_Element} = $element->{Parent} || $e->{Current_Element}->{Parent};
}
sub characters {
my $e = shift;
my $text = shift;
my $ns = $e->{Current_Element}->{NamespaceURI};
# warn "CHAR-NS: $ns\n";
if (!defined($ns) ||
!exists($Apache::AxKit::Language::XSP::tag_lib{ $ns }) ||
!$e->manage_text(-1))
{
$e->{XSP_Script} .= AxKit::XSP::DefaultHandler::characters($e, $text);
}
else {
my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
my $sub;
if (($sub = $pkg->can("characters")) && ($sub != \&characters)) {
$e->{XSP_Script} .= $sub->($e, $text);
}
elsif ($sub = $pkg->can("parse_char")) {
$e->{XSP_Script} .= $sub->($e, $text->{Data});
}
}
}
sub comment {
my $e = shift;
my $comment = shift;
my $ns = $e->{Current_Element}->{NamespaceURI};
if (!defined($ns) ||
!exists($Apache::AxKit::Language::XSP::tag_lib{ $ns }))
{
$e->{XSP_Script} .= AxKit::XSP::DefaultHandler::comment($e, $comment);
}
else {
# local $^W;
my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
my $sub;
if (($sub = $pkg->can("comment")) && ($sub != \&comment)) {
$e->{XSP_Script} .= $sub->($e, $comment);
}
elsif ($sub = $pkg->can("parse_comment")) {
$e->{XSP_Script} .= $sub->($e, $comment->{Data});
}
}
}
sub processing_instruction {
my $e = shift;
my $pi = shift;
my $ns = $e->{Current_Element}->{NamespaceURI};
if (!defined($ns) ||
!exists($Apache::AxKit::Language::XSP::tag_lib{ $ns }))
{
$e->{XSP_Script} .= AxKit::XSP::DefaultHandler::processing_instruction($e, $pi);
}
else {
# local $^W;
my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
my $sub;
if (($sub = $pkg->can("processing_instruction")) && ($sub != \&processing_instruction)) {
$e->{XSP_Script} .= $sub->($e, $pi);
}
elsif ($sub = $pkg->can("parse_pi")) {
$e->{XSP_Script} .= $sub->($e, $pi->{Target}, $pi->{Data});
}
}
}
############################################################
# Functions implementing xsp:* processing
############################################################
package AxKit::XSP::Core;
use vars qw/@ISA $NS/;
@ISA = ('Apache::AxKit::Language::XSP');
$NS = 'http://apache.org/xsp/core/v1';
__PACKAGE__->register();
# hack for backwards compatibility:
__PACKAGE__->register_taglib("http://www.apache.org/1999/XSP/Core");
sub start_document {
return "#initialize xsp namespace\n";
}
sub end_document {
return '';
}
sub comment {
return '';
}
sub processing_instruction {
return '';
}
sub characters {
my ($e, $node) = @_;
local $^W;
my $text = $node->{Data};
# Ricardo writes: "<xsp:expr> produces either an [object]
# _expression_ (not necessarily a String) or a character event depending
# on context. When <xsp:expr> is enclosed in another XSP tag (except
# <xsp:content>), it's replaced by the code it contains. Otherwise it
# should be treated as a text node and, therefore, coerced to String to be
# output through a characters SAX event."
if ($e->current_element() =~ /^(content)$/) {
$text =~ s/\|/\\\|/g;
return <<"EOT";
{
my \$text = \$document->createTextNode(q|$text|);
\$parent->appendChild(\$text);
}
EOT
}
elsif ($e->current_element() =~ /^(attribute|comment|name)$/) {
return '' if ($e->current_element() eq 'attribute' && !$e->{attrib_seen_name});
$text =~ s/^\s*//; $text =~ s/\s*$//;
$text =~ s/\|/\\\|/g;
return ". q|$text|";
}
# return '' unless $e->{XSP_User_Root};
return $text;
}
sub start_element {
my ($e, $node) = @_;
my ($tag, %attribs);
$tag = $node->{Name};
foreach my $attrib (@{$node->{Attributes}}) {
$attribs{$attrib->{Name}} = $attrib->{Value};
}
if ($tag eq 'page') {
if ($attribs{language} && lc($attribs{language}) ne 'perl') {
die "Only Perl XSP pages supported at this time!";
}
local $^W;
if ($attribs{'indent-result'} eq 'yes') {
$e->{XSP_Indent} = 1;
}
}
elsif ($tag eq 'structure') {
}
elsif ($tag eq 'dtd') {
}
elsif ($tag eq 'include') {
return "warn \"xsp:include is deprecated\"; use ";
}
elsif ($tag eq 'content') {
}
elsif ($tag eq 'logic') {
}
elsif ($tag eq 'import') {
return "use ";
}
elsif ($tag eq 'element') {
if (my $name = $attribs{name}) {
$e->manage_text(0);
return '{ my $elem = $document->createElement(q(' . $name . '));' .
'$parent->appendChild($elem); $parent = $elem; }' . "\n";
}
}
elsif ($tag eq 'attribute') {
if (my $name = $attribs{name}) {
$e->{attrib_seen_name} = 1;
return '$parent->setAttribute(q|' . $name . '|, ""';
}
$e->{attrib_seen_name} = 0;
}
elsif ($tag eq 'name') {
return '{ my $name = ""';
}
elsif ($tag eq 'pi') {
}
elsif ($tag eq 'comment') {
return '{ my $comment = $document->createComment(""';
}
elsif ($tag eq 'text') {
return '{ my $text = $document->createTextNode(""';
}
elsif ($tag eq 'expr') {
# warn "expr: -2 = {", $node->{Parent}->{NamespaceURI}, "}", $node->{Parent}->{Name}, "\n";
if (Apache::AxKit::Language::XSP::is_xsp_namespace($node->{Parent}->{NamespaceURI})) {
if (!$e->manage_text() || $node->{Parent}->{Name} =~ /^(.*:)?content$/) {
return <<'EOT';
{
my $text = $document->createTextNode("".do {
EOT
}
elsif ($node->{Parent}->{Name} =~ /^(.*:)?(logic|expr)$/) {
return 'do {';
}
}
else {
return <<'EOT';
{
my $text = $document->createTextNode("".do {
EOT
}
return '. do {';
# warn "start Expr: CurrentEl: ", $e->current_element, "\n";
}
return '';
}
sub end_element {
my ($e, $node) = @_;
my $tag = $node->{Name};
if ($tag eq 'page') {
}
elsif ($tag eq 'structure') {
}
elsif ($tag eq 'dtd') {
}
elsif ($tag eq 'include') {
return ";\n";
}
elsif ($tag eq 'import') {
return ";\n";
}
elsif ($tag eq 'content') {
}
elsif ($tag eq 'logic') {
}
elsif ($tag eq 'element') {
return '$parent = $parent->getParentNode;' . "\n";
}
elsif ($tag eq 'attribute') {
return ');' . "\n";
}
elsif ($tag eq 'name') {
if ($node->{Parent}->{Name} =~ /^(.*:)?element$/) {
$e->manage_text(0, 1);
return '; my $elem = $document->createElement($name);' .
'$parent->appendChild($elem); $parent = $elem; }' . "\n";
}
elsif ($node->{Parent}->{Name} =~ /^(.*:)?attribute$/) {
$e->{attrib_seen_name} = 1;
return '; my $attr = $document->createAttribute($name, ""';
}
else {
die "xsp:name parent node: $node->{Parent}->{Name} not valid";
}
}
elsif ($tag eq 'pi') {
}
elsif ($tag eq 'comment') {
return '); $parent->appendChild($comment); }' . "\n";
}
elsif ($tag eq 'text') {
return '); $parent->appendChild($text); }' . "\n";
}
elsif ($tag eq 'expr') {
# warn "expr: -2 = {", $node->{Parent}->{NamespaceURI}, "}", $node->{Parent}->{Name}, "\n";
if (Apache::AxKit::Language::XSP::is_xsp_namespace($node->{Parent}->{NamespaceURI})) {
if (!$e->manage_text() || $node->{Parent}->{Name} =~ /^(.*:)?content$/) {
return <<'EOT';
}); # xsp tag
$parent->appendChild($text);
}
EOT
}
elsif ($node->{Parent}->{Name} =~ /^(.*:)?(logic|expr)$/) {
return '}';
}
}
else {
return <<'EOT';
}); # non xsp tag
$parent->appendChild($text);
}
EOT
}
return '}';
}
return '';
}
1;
############################################################
## Default (non-xsp-namespace) handlers
############################################################
package AxKit::XSP::DefaultHandler;
sub start_element {
my ($e, $node) = @_;
my $code;
if (!$e->{XSP_User_Root}) {
$e->{XSP_Script} .= join("\n",
'sub handler {',
'my ($r, $cgi) = @_;',
'my $document = XML::LibXML::Document->createDocument("1.0", "UTF-8");',
'my ($parent);',
"\n",
);
$e->{XSP_User_Root} = 1;
$code = '{ my $elem = $document->createElement(q(' . $node->{Name} . '));' .
# $code = '{ my $elem = $document->createElementNS(q(' . ($node->{NamespaceURI} || "") . '), q(' . $node->{Name} . '));' .
'$document->setDocumentElement($elem); $parent = $elem; }' . "\n";
}
else {
$code = '{ my $elem = $document->createElement(q(' . $node->{Name} . '));' .
'$parent->appendChild($elem); $parent = $elem; }' . "\n";
}
for my $attr (@{$node->{Attributes}}) {
$code .= '$parent->setAttribute(q(' . $attr->{Name} .
'), q(' . $attr->{Value} .
'));' . "\n";
}
for my $ns (keys %{$e->{Current_NS}}) {
$code .= '$parent->setAttribute("xmlns:" . q(' . $ns .'), q(' .
$e->{Current_NS}{$ns} . '));';
}
push @{ $e->{NS_Stack} },
{ %{ $e->{Current_NS} || {} } };
$e->{Current_NS} = {};
return $code;
}
sub end_element {
my ($e, $element) = @_;
$e->{Current_NS} = pop @{ $e->{NS_Stack} };
return '$parent = $parent->getParentNode;' . "\n";
}
sub characters {
my ($e, $node) = @_;
my $text = $node->{Data};
return '' unless $e->{XSP_User_Root}; # should not happen!
if (!$e->{XSP_Indent}) {
return '' unless $text =~ /\S/;
}
$text =~ s/\|/\\\|/g;
return '{ my $text = $document->createTextNode(q|' . $text . '|);' .
'$parent->appendChild($text); }' . "\n";
}
sub comment {
return '';
}
sub processing_instruction {
return '';
}
1;
######################################################
## SAXParser
######################################################
package AxKit::XSP::SAXParser;
use XML::LibXML 1.30;
sub new {
my ($type, %self) = @_;
return bless \%self, $type;
}
sub parse {
my ($self, $thing) = @_;
my $doc;
my $parser = XML::LibXML->new();
$parser->expand_entities(1);
if (ref($thing)) {
$doc = $parser->parse_fh($thing);
}
else {
$doc = $parser->parse_string($thing);
}
$doc->process_xinclude;
my $document = { Parent => undef };
$self->{Handler}->start_document($document);
my $root = $doc->getDocumentElement;
if ($root) {
process_node($self->{Handler}, $root);
}
$self->{Handler}->end_document($document);
}
sub process_node {
my ($handler, $node) = @_;
my $node_type = $node->getType();
if ($node_type == XML_COMMENT_NODE) {
$handler->comment( { Data => $node->getData } );
}
elsif ($node_type == XML_TEXT_NODE || $node_type == XML_CDATA_SECTION_NODE) {
# warn($node->getData . "\n");
$handler->characters( { Data => $node->getData } );
}
elsif ($node_type == XML_ELEMENT_NODE) {
# warn("<" . $node->getName . ">\n");
process_element($handler, $node);
# warn("</" . $node->getName . ">\n");
}
elsif ($node_type == XML_ENTITY_REF_NODE) {
foreach my $kid ($node->getChildnodes) {
# warn("child of entity ref: " . $kid->getType() . " called: " . $kid->getName . "\n");
process_node($handler, $kid);
}
}
elsif ($node_type == XML_DOCUMENT_NODE) {
# just get root element. Ignore other cruft.
foreach my $kid ($node->getChildnodes) {
if ($kid->getType() == XML_ELEMENT_NODE) {
process_element($handler, $kid);
last;
}
}
}
else {
warn("unknown node type: $node_type");
}
}
sub process_element {
my ($handler, $element) = @_;
my @attr;
foreach my $attr ($element->getAttributes) {
push @attr, {
Name => $attr->getName,
Value => $attr->getData,
NamespaceURI => $attr->getNamespaceURI,
Prefix => $attr->getPrefix,
LocalName => $attr->getLocalName,
};
}
my $node = {
Name => $element->getName,
Attributes => \@attr,
NamespaceURI => $element->getNamespaceURI,
Prefix => $element->getPrefix,
LocalName => $element->getLocalName,
};
$handler->start_element($node);
foreach my $child ($element->getChildnodes) {
process_node($handler, $child);
}
$handler->end_element($node);
}
############################################################
# Base page class
############################################################
package Apache::AxKit::Language::XSP::Page;
sub has_changed {
my $class = shift;
my $mtime = shift;
return 1;
}
sub cache_params {
my $class = shift;
my ($r, $cgi) = @_;
return '';
}
1;
__END__
=pod
=head1 NAME
Apache::AxKit::Language::XSP - eXtensible Server Pages
=head1 SYNOPSIS
<xsp:page
xmlns:xsp="http://apache.org/xsp/core/v1">
<xsp:structure>
<xsp:import>Time::Object</xsp:import>
</xsp:structure>
<page>
<title>XSP Test</title>
<para>
Hello World!
</para>
<para>
Good
<xsp:logic>
if (localtime->hour >= 12) {
<xsp:content>Afternoon</xsp:content>
}
else {
<xsp:content>Morning</xsp:content>
}
</xsp:logic>
</para>
</page>
</xsp:page>
=head1 DESCRIPTION
XSP implements a tag-based dynamic language that allows you to develop
your own tags, examples include sendmail and sql taglibs. It is AxKit's
way of providing an environment for dynamic pages. XSP is originally part
of the Apache Cocoon project, and so you will see some Apache namespaces
used in XSP.
=head1 Tag Reference
=head2 C<<xsp:page>>
This is the top level element, although it does not have to be. AxKit's
XSP implementation can process XSP pages even if the top level element
is not there, provided you use one of the standard AxKit ways to turn
on XSP processing for that page. See L<AxKit>.
The attribute C<language="Perl"> can be present, to mandate the language.
This is useful if you expect people might mistakenly try and use this
page on a Cocoon system. The default value of this attribute is "Perl".
XSP normally swallows all whitespace in your output. If you don't like
this feature, or it creates invalid output, then you can add the
attribute: C<indent-result="yes">
=head2 C<<xsp:structure>>
parent: <xsp:page>
This element appears at the root level of your page before any non-XSP
tags. It defines page-global "things" in the C<<xsp:logic>> and
C<<xsp:import>> tags.
=head2 C<<xsp:import>>
parent: <xsp:structure>
Use this tag for including modules into your code, for example:
<xsp:structure>
<xsp:import>DBI</xsp:import>
</xsp:structure>
=head2 C<<xsp:logic>>
parent: <xsp:structure>, any
The C<<xsp:logic>> tag introduces some Perl code into your page.
As a child of C<<xsp:structure>>, this element allows you to define
page global variables, or functions that get used in the page. Placing
functions in here allows you to get around the Apache::Registry
closures problem (see the mod_perl guide at http://perl.apache.org/guide
for details).
Elsewhere the perl code contained within the tags is executed on every
view of the XSP page.
B<Warning:> Be careful - the Perl code contained within this tag is still
subject to XML's validity constraints. Most notably to Perl code is that
the & and < characters must be escaped into & and < respectively.
You can get around this to some extent by using CDATA sections. This is
especially relevant if you happen to think something like this will work:
<xsp:logic>
if ($some_condition) {
print "<para>Condition True!</para>";
}
else {
print "<para>Condition False!</para>";
}
</xsp:logic>
The correct way to write that is simply:
<xsp:logic>
if ($some_condition) {
<para>Condition True!</para>
}
else {
<para>Condition False!</para>
}
</xsp:logic>
The reason is that XSP intrinsically knows about XML!
=head2 C<<xsp:content>>
parent: <xsp:logic>
This tag allows you to temporarily "break out" of logic sections to generate
some XML text to go in the output. Using something similar to the above
example, but without the surrounding C<<para>> tag, we have:
<xsp:logic>
if ($some_condition) {
<xsp:content>Condition True!</xsp:content>
}
else {
<xsp:content>Condition False!</xsp:content>
}
</xsp:logic>
=head2 C<<xsp:element>>
This tag generates an element of name equal to the value in the attribute
C<name>. Alternatively you can use a child element C<<xsp:name>> to specify
the name of the element. Text contents of the C<<xsp:element>> are created
as text node children of the new element.
=head2 C<<xsp:attribute>>
Generates an attribute. The name of the attribute can either be specified
in the C<name="..."> attribute, or via a child element C<<xsp:name>>. The
value of the attribute is the text contents of the tag.
=head2 C<<xsp:comment>>
Normally XML comments are stripped from the output. So to add one back in
you can use the C<<xsp:comment>> tag. The contents of the tag are the
value of the comment.
=head2 C<<xsp:text>>
Create a plain text node. The contents of the tag are the text node to be
generated. This is useful when you wish to just generate a text node while
in an C<<xsp:logic>> section.
=head2 C<<xsp:expr>>
This is probably the most useful, and most important (and also the most
complex) tag. An expression is some perl code that executes, and the results
of which are added to the output. Exactly how the results are added to the
output depends very much on context.
The default method for output for an expression is as a text node. So for
example:
<p>
It is now: <xsp:expr>localtime</xsp:expr>
</p>
Will generate a text node containing the time.
If the expression is contained within an XSP namespaces, that is either a
tag in the xsp:* namespace, or a tag implementing a tag library, then an
expression generally does not create a text node, but instead is simply
wrapped in a Perl C<do {}> block, and added to the perl script. However,
there are anti-cases to this. For example if the expression is within
a C<<xsp:content>> tag, then a text node is created.
Needless to say, in every case, C<<xsp:expr>> should just "do the right
thing". If it doesn't, then something (either a taglib or XSP.pm itself)
is broken and you should report a bug.
=head1 DESIGN PATTERNS
Writing your own taglibs can be tricky, because you're using an event
based API to write out Perl code. You may want to take a look at the
Apache::AxKit::Language::XSP::TaglibHelper module, which comes with
AxKit and allows you to easily publish a taglib without writing
XML event code.
These patterns represent the things you may want to achieve when
authoring a tag library "from scratch".
B<1. Your tag is a wrapper around other things.>
Example:
<mail:sendmail>...</mail:sendmail>
Solution:
Start a new block, so that you can store lexical variables, and declare
any variables relevant to your tag:
in parse_start:
if ($tag eq 'sendmail') {
return '{ my ($to, $from, $sender);';
}
Often it will also be relevant to execute that code when you see the end
tag:
in parse_end:
if ($tag eq 'sendmail') {
return 'Mail::Sendmail::sendmail(
to => $to,
from => $from,
sender => $sender
); }';
}
Note there the closing of that original opening block.
B<2. Your tag indicates a parameter for a surrounding taglib.>
Example:
<mail:to>...</mail:to>
Solution:
Having declared the variable as above, you simply set it to the empty
string, with no semi-colon:
in parse_start:
if ($tag eq 'to') {
return '$to = ""';
}
Then in parse_char:
sub parse_char {
my ($e, $text) = @_;
$text =~ s/^\s*//;
$text =~ s/\s*$//;
return '' unless $text;
$text =~ s/\|/\\\|/g;
return ". q|$text|";
}
Note there's no semi-colon at the end of all this, so we add that:
in parse_end:
if ($tag eq 'to') {
return ';';
}
All of this black magic allows other taglibs to set the thing in that
variable using expressions.
B<3. You want your tag to return a scalar (string) that does the right thing
depending on context. For example, generates a Text node in one place or
generates a scalar in another context.>
Solution:
use start_expr(), append_to_script(), end_expr().
Example:
<example:get-datetime format="%Y-%m-%d %H:%M:%S"/>
in parse_start:
if ($tag eq 'get-datetime') {
start_expr($e, $tag); # creates a new { ... } block
my $local_format = lc($attribs{format}) || '%a, %d %b %Y %H:%M:%S %z';
return 'my ($format); $format = q|' . $local_format . '|;';
}
in parse_end:
if ($tag eq 'get-datetime') {
append_to_script($e, 'use Time::Object; localtime->strftime($format);');
end_expr($e);
return '';
}
Explanation:
This is more complex than the first 2 examples, so it warrants some
explanation. I'll go through it step by step.
start_expr(...)
This tells XSP that this really generates a <xsp:expr> tag. Now we don't
really generate that tag, we just execute the handler for it. So what
happens is the <xsp:expr> handler gets called, and it looks to see what
the current calling context is. If its supposed to generate a text node,
it generates some code to do that. If its supposed to generate a scalar, it
does that too. Ultimately both generate a do {} block, so we'll summarise
that by saying the code now becomes:
do {
(the end of the block is generated by end_expr()).
Now the next step (ignoring the simple gathering of the format variable), is
a return, which appends more code onto the generated perl script, so we
get:
do {
my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
Now we immediately receive an end_expr, because this is an empty element
(we'll see why we formatted it this way in #5 below). The first thing we
get is:
append_to_script($e, 'use Time::Object; localtime->strftime($format);');
This does exactly what it says, and the script becomes:
do {
my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
use Time::Object; localtime->strftime($format);
Finally, we call:
end_expr($e);
which closes the do {} block, leaving us with:
do {
my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
use Time::Object; localtime->strftime($format);
}
Now if you execute that in Perl, you'll see the do {} returns the last
statement executed, which is the C<localtime->strftime()> bit there,
thus doing exactly what we wanted.
Note that start_expr, end_expr and append_to_script aren't exported
by default, so you need to do:
use Apache::AxKit::Language::XSP
qw(start_expr end_expr append_to_script);
B<4. Your tag can take as an option either an attribute, or a child tag.>
Example:
<util:include-uri uri="http://server/foo"/>
or
<util:include-uri>
<util:uri><xsp:expr>$some_uri</xsp:expr></util:uri>
</util:include-uri>
Solution:
There are several parts to this. The simplest is to ensure that whitespace
is ignored. We have that dealt with in the example parse_char above. Next
we need to handle that variable. Do this by starting a new block with the
tag, and setting up the variable:
in parse_start:
if ($tag eq 'include-uri') {
my $code = '{ my ($uri);';
if ($attribs{uri}) {
$code .= '$uri = q|' . $attribs{uri} . '|;';
}
return $code;
}
Now if we don't have the attribute, we can expect it to come in the
C<<util:uri>> tag:
in parse_start:
if ($tag eq 'uri') {
return '$uri = ""'; # note the empty string!
}
Now you can see that we're not explicitly setting C<$uri>, that's because the
parse_char we wrote above handles it by returning '. q|$text|'. And if we
have a C<<xsp:expr>> in there, that's handled automagically too.
Now we just need to wrap things up in the end handlers:
in parse_end:
if ($tag eq 'uri') {
return ';';
}
if ($tag eq 'include-uri') {
return 'Taglib::include_uri($uri); # execute the code
} # close the block
';
}
B<5. You want to return a scalar that does the right thing in context, but
also can take a parameter as an attribute I<or> a child tag.>
Example:
<esql:get-column column="user_id"/>
vs
<esql:get-column>
<esql:column><xsp:expr>$some_column</xsp:expr></esql:column>
</esql:get-column>
Solution:
This is a combination of patterns 3 and 4. What we need to do is change
#3 to simply allow our variable to be added as in #4 above:
in parse_start:
if ($tag eq 'get-column') {
start_expr($e, $tag);
my $code = 'my ($col);'
if ($attribs{col}) {
$code .= '$col = q|' . $attribs{col} . '|;';
}
return $code;
}
if ($tag eq 'column') {
return '$col = ""';
}
in parse_end:
if ($tag eq 'column') {
return ';';
}
if ($tag eq 'get-column') {
append_to_script($e, 'Full::Package::get_column($col)');
end_expr($e);
return '';
}
B<6. You have a conditional tag>
Example:
<esql:no-results>
No results!
</esql:no-results>
Solution:
The problem here is that taglibs normally recieve character/text events
so that they can manage variables. With a conditional tag, you want
character events to be handled by the core XSP and generate text events.
So we have a switch for that:
if ($tag eq 'no-results') {
$e->manage_text(0);
return 'if (AxKit::XSP::ESQL::get_count() == 0) {';
}
Turning off manage_text with a zero simply ensures that immediate children
text nodes of this tag don't fire text events to the tag library, but
instead get handled by XSP core, thus creating text nodes (and doing
the right thing, generally).
=head1 <xsp:expr> (and start_expr, end_expr) Notes
B<Do not> consider adding in the 'do {' ... '}' bits yourself. Always
leave this to the start_expr, and end_expr functions. This is because the
implementation could change, and you really don't know better than
the underlying XSP implementation. You have been warned.
=cut
1.1 xml-axkit/lib/Apache/AxKit/Language/XSP/TaglibHelper.pm
Index: TaglibHelper.pm
===================================================================
# $Id: TaglibHelper.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::Language::XSP::TaglibHelper;
@ISA = qw(Apache::AxKit::Language::XSP);
use XML::LibXML;
use strict;
sub parse_char {
my ($e, $text) = @_;
$text =~ s/^\s*//;
$text =~ s/\s*$//;
return '' unless $text;
$text =~ s/\|/\\\|/g;
return ". q|$text|";
}
# Try to find the given function name and see if it's in the "use"ing
# module's list of exported functions. Retuns the function spec if
# if it found it, or undef if it didn't.
sub is_function ($$) {
my ($pkg, $fname) = @_;
no strict;
my @exports = @{"$pkg\::EXPORT_TAGLIB"};
use strict;
foreach my $funspec(@exports) {
return $funspec if $funspec =~ /^$fname *\(/;
}
return undef;
}
sub func_name ($) {
my ($funspec) = @_;
my %opts = func_options($funspec);
return $opts{isreally} if $opts{isreally};
my ($argspec) = ($funspec =~ /^ *(.*?)\(/);
return $argspec;
}
sub func_options ($) {
my ($funspec) = @_;
my @args = split (/:\s*/, $funspec);
shift @args;
my %retval = ();
foreach (@args) {
# | | Bra!
my ($key, $value) = ($_ =~ /(.*)=(.*)/);
$retval{$key} = $value;
}
return %retval;
}
sub required_args ($) {
my ($funspec) = @_;
my ($argspec) = ($funspec =~ /\(\s*([^\);]*)/);
my @retval;
foreach my $arg(split (/,/, $argspec)) {
$arg =~ s/^\s*//g;
$arg =~ s/\s*$//g;
push (@retval, $arg);
}
return @retval;
}
sub optional_args ($) {
my ($funspec) = @_;
my ($argspec) = ($funspec =~ /; *([^\)]*)/);
my @retval;
foreach my $arg(split (/,/, $argspec)) {
$arg =~ s/^\s*//g;
$arg =~ s/\s*$//g;
push (@retval, $arg);
}
return @retval;
}
sub find_arg ($@) {
my $argname = shift;
foreach (@_) { return $_ if /^.$argname/ }
return "";
}
# quieten warnings when compiling
sub handle_result ($$$$$;@);
# The input to this function is the *result* from a taglib function, and
# therefore can be anything. We need to be able to turn it into a set
# of XML tags.
sub handle_result ($$$$$;@) {
my $funspec = shift;
my $lastkey = shift; # If parent was a hash, else this is undef.
my $indentlevel = shift;
my $document = shift;
my $parent = shift;
my $indent = ' ' x $indentlevel;
my %options = func_options($funspec);
if ($options{as_xml}) {
$parent->appendWellBalancedChunk(shift);
return;
}
# if we got more than one result (we assume it's an array),
# we'll act as if we got a single arrayref
# forcearray makes it always think there's an array; useful
# for functions that are returning an array of one value
if ($indentlevel == 0 and ($options{forcearray} or scalar @_ > 1)) {
# if thre's one item, and it's undef, return an arrayref to an empty array
if (scalar @_ == 1 and !defined($_[0])) {
@_ = ([]);
} else {
@_ = ([@_]);
}
}
# break down each arg in the results, possibly call self recursively
foreach my $ref(@_) {
if ( (ref($ref)) =~ /ARRAY/) {
# arrays are hard because they're not keyed except by numbers
# the array list itself will have a wrapper tag of funcname+"-list" if
# we're at indent level 0, or no wrapper if it's below level 0
if ($indentlevel == 0) {
$parent->appendChild($document->createTextNode("\n" . $indent));
my $el;
if ($options{listtag}) {
$el = $document->createElement($options{listtag});
}
else {
my $funcname = $options{'array_uses_hash'} ? $lastkey :
func_name($funspec) || func_name($funspec);
$funcname =~ s/_|\s/-/g; # convert back to XML-style tagnames
$el = $document->createElement("${funcname}-list");
}
$parent->appendChild($el);
$parent = $el;
}
my $id = 1;
foreach my $value(@$ref) {
# each item within an array should have a wrapper "-item" tag
my $item;
if ($options{itemtag}) {
$item = $document->createElement($options{itemtag});
}
else {
my $funcname = $options{'array_uses_hash'} ? $lastkey :
func_name($funspec) || func_name($funspec);
$funcname =~ s/_|\s/-/g; # convert back to XML-style tagnames
$item = $document->createElement("${funcname}-item");
}
$item->setAttribute("id", $id++);
$parent->appendChild($item);
handle_result($funspec, $lastkey, $indentlevel + 1, $document, $item, $value);
}
}
elsif ( (ref($ref)) =~ /HASH/) {
# hashes are relatively easy because they're keyed
$parent->appendChild($document->createTextNode("\n" . $indent));
while (my ($key, $value) = each %$ref) {
my $el = $document->createElement($key);
$parent->appendChild($el);
handle_result($funspec, $key, $indentlevel + 1, $document, $el, $value);
}
}
else {
# not arrayref or not hashref: it's either a scalar or an unsupported
# type, so we'll just dump it as text
# special case: at the highest level of hierarchy, we can just return a
# string because it will be turned automatically into a text node by
# AxKit
if ($indentlevel == 0) {
return $_[0];
}
elsif (defined $_[0]) {
$parent->appendChild($document->createTextNode($_[0]));
}
}
}
}
$main::indent = 0;
# quieten warnings when compiling
sub convert_from_dom ($$);
# This function converts from a DOM tree into a collection of hashes.
# It's used for "*" taglib arguments.
sub convert_from_dom ($$) {
my ($funspec, $node) = @_;
local $main::indent = $main::indent;
$main::indent++;
require Data::Dumper;
# if we're at the first level, we'll ignore our top node, because we know it's just
# a dummy root node
if (UNIVERSAL::isa($node, "XML::LibXML::Element")) {
my @children = ($node->getChildnodes,$node->getAttributes);
my $multiple = 0;
my %mdetect = ();
# look for multiple children with the same name, which
# means we should treat it as an array
foreach (@children) {
# sometimes we get blank text nodes; we don't want 'em
next if UNIVERSAL::isa($_, "XML::LibXML::Text") and $_->getData eq '';
$multiple = 1 if $mdetect{$_->getName};
$mdetect{$_->getName} = 1;
}
# special option might force us to treat it as an array regardless
if (!$multiple && @children == 1) {
my %opts = func_options($funspec);
if (my $taglist = $opts{tagisarray}) {
$multiple = 1
if grep
{ $_ eq $node->getName }
split(/,/, $opts{tagisarray});
}
}
if ($multiple) {
my $retval = [];
foreach (@children) {
# sometimes we get blank text nodes; we don't want 'em
next if UNIVERSAL::isa($_, "XML::LibXML::Text") and $_->getData eq '';
push(@$retval, convert_from_dom($funspec,$_));
}
return $retval;
}
else {
# <list>text</list> converts to { list => 'text' },
# <list><item>text</item></list> converts to { list => { item => 'text' } };
# in other words, if there's only one child, we need to figure out
# if it's a text node or a regular tag
if ((@children > 1) or (@children == 1 and not UNIVERSAL::isa($children[0], "XML::LibXML::Text"))) {
my $retval = {};
foreach (@children) {
my $name = $_->getName;
# sometimes we get blank text nodes; we don't want 'em
next if UNIVERSAL::isa($_, "XML::LibXML::Text") and $_->getData eq '';
$name = 'TEXT' if UNIVERSAL::isa($_, "XML::LibXML::Text");
$retval->{$name} = convert_from_dom($funspec,$_);
}
return $retval;
}
elsif (@children == 1) {
my $retval = convert_from_dom($funspec,$children[0]);
return $retval;
}
}
return "";
}
else {
# we'll just assume it's text for now
return $node->getData;
}
}
@Apache::AxKit::Language::XSP::TaglibHelper::function_stack = ();
sub parse_start {
my ($e, $tag, %attribs) = @_;
# Dashes are more "XML-like" than underscores, but we can't use
# dashes in function or argument names. So we'll just convert them
# arbitrarily here.
$tag =~ s/-/_/g;
my $pkg = caller;
# horrible hack: if the caller is the SAX library directly,
# then we'll just have to assume that we're testing TaglibHelper
$pkg = "Apache::AxKit::Language::XSP::TaglibHelper" if $pkg eq "AxKit::XSP::SAXHandler";
my $funspec = is_function($pkg, $tag);
my $code = "";
if ($funspec) {
my %options = func_options($funspec);
push (@Apache::AxKit::Language::XSP::TaglibHelper::function_stack, $funspec);
$code = "{ my \%_args = ();";
while (my ($key, $value) = each %attribs) {
my $paramspec = find_arg($key, required_args($funspec), optional_args($funspec));
if ($paramspec =~ /^\*/) {
$code .= " die 'Argument $key to function $tag is tree type, and cannot be set in an attribute.';\n";
}
elsif ($paramspec =~ /^\@/) {
$key =~ s/-/_/g;
$value =~ s/\|/\\\|/g;
$code .= " \$_args{$key} ||= []; push \@{\$_args{$key}}, q|$value|;\n";
}
else {
$key =~ s/-/_/g;
$value =~ s/\|/\\\|/g;
$code .= " \$_args{$key} = q|$value|;\n";
}
}
# if it's a "conditional" function (i.e. it wraps around conditional tags)
# we need to pick up the arguments in the attributes only, and execute
# the function here
if ($options{conditional}) {
foreach my $arg(required_args($funspec)) {
$arg =~ s/^.//g; # ignore type specs for now
$code .=
" die 'Required arg \"$arg\" for tag $tag is missing' if not defined \$_args{$arg};\n";
}
$code .= " if ($pkg\::" . func_name($funspec) . "(";
foreach my $arg(required_args($funspec), optional_args($funspec)) {
$arg =~ s/^.//g; # remove type specs from hash references
$code .= "\$_args{$arg},";
}
$code .= ")) {\n";
$e->manage_text(0);
}
else {
$e->start_expr($tag);
}
}
else {
my $funspec =
$Apache::AxKit::Language::XSP::TaglibHelper::function_stack
[$#Apache::AxKit::Language::XSP::TaglibHelper::function_stack];
my $paramspec = find_arg($tag, required_args($funspec), optional_args($funspec));
# if the param is of type '*', then we have to prepare a new DOM tree
# but we default to assuming it's a scalar argument
if ($paramspec =~ /^\*/) {
$code =
" { my \$theparent = \$parent ; \$parent = \$document->createElement('ROOT-$tag'); \$_args{$tag} = \$parent;\n";
$e->manage_text(0);
}
elsif ($paramspec =~ /^\@/) {
if (keys %attribs){
my $attrib_string = '';
while (my ($key, $value) = each %attribs) {
$key =~ s/\'/\\\'/g;
$value =~ s/\'/\\\'/g;
$attrib_string .= "'$key','$value',";
}
$attrib_string =~ s/,$//;
$code = " \$_args{$tag} ||= [];
my \$href = {};
my %h = ($attrib_string);
while (my (\$key,\$value) = each %h){
\$href->{\$key} = \$value;
}
push \@{\$_args{$tag}}, \$href;\n";
}
else{
$code = " \$_args{$tag} ||= []; push \@{\$_args{$tag}}, \"\"\n";
}
}
else {
if (keys %attribs){
my $attrib_string = '';
while (my ($key, $value) = each %attribs) {
$key =~ s/\'/\\\'/g;
$value =~ s/\'/\\\'/g;
$attrib_string .= "'$key','$value',";
}
$attrib_string =~ s/,$//;
$code = " \$_args{$tag} ||= [];
my \$href = {};
my %h = ($attrib_string);
while (my (\$key,\$value) = each %h){
last if \$href->{\$key};
\$href->{\$key} = \$value;
}
\$_args{$tag} = \$href;\n";
}
else{
$code = " \$_args{$tag} = \"\"\n";
}
}
}
return $code;
}
sub parse_end {
my ($e, $tag) = @_;
my $origtag = $tag;
$tag =~ s/-/_/g;
my $pkg = caller;
$pkg = "Apache::AxKit::Language::XSP::TaglibHelper" if $pkg eq "AxKit::XSP::SAXHandler";
my $funspec = is_function($pkg, $tag);
my $code = "";
if ($funspec) {
pop (@Apache::AxKit::Language::XSP::TaglibHelper::function_stack);
my %options = func_options($funspec);
if ($options{conditional}) {
$e->manage_text(1);
return "}}\n";
}
else {
$code = ";";
foreach my $arg(required_args($funspec)) {
$arg =~ s/^.//g; # ignore type specs for now
$code .=
" die 'Required arg \"$arg\" for tag $origtag is missing' if not defined \$_args{$arg};\n";
}
$code .=
" Apache::AxKit::Language::XSP::TaglibHelper::handle_result('$funspec', undef(), 0, \$document, \$parent, $pkg\::"
. func_name($funspec) . "(";
foreach my $arg(required_args($funspec), optional_args($funspec)) {
$arg =~ s/^.//g; # remove type specs from hash references
$code .= "\$_args{$arg},";
}
$code .= "));}\n";
$e->append_to_script($code);
$e->end_expr();
return '';
}
}
else {
# what function are we in?
my $funspec =
$Apache::AxKit::Language::XSP::TaglibHelper::function_stack
[$#Apache::AxKit::Language::XSP::TaglibHelper::function_stack];
my $paramspec = find_arg($tag, required_args($funspec), optional_args($funspec));
# if the param is of type '*', then we restore the old DOM tree
if ($paramspec =~ /^\*/) {
$e->manage_text(1);
$code =
" \$parent = \$theparent; \$_args{$tag} = Apache::AxKit::Language::XSP::TaglibHelper::convert_from_dom('$funspec',\$_args{$tag}); }";
}
return "$code;\n";
}
}
##############################################################################
# a built-in taglib, so we can test the functionality of TaglibHelper
no strict;
$NS = 'http://apache.org/xsp/testtaglibhelper/v1';
@EXPORT_TAGLIB = (
'test_hello($name)',
'test_echo(*whatever)',
'test_echo_array(@array)',
'test_get_person($name)',
'test_get_people($name)',
'test_get_people2($name):listtag=people:itemtag=person',
);
use strict;
# now you declare your functions
sub test_hello ($) {
my ($name) = @_;
return "Hello, $name!";
}
sub test_echo ($) {
my ($whatever) = @_;
return $whatever;
}
sub test_echo_array ($) {
my ($whatever) = @_;
return $whatever;
}
sub test_get_person ($) {
my ($name) = @_;
srand(time + $$) if not $Apache::AxKit::Language::XSP::TaglibHelper::didsrand;
$Apache::AxKit::Language::XSP::TaglibHelper::didsrand = 1;
return {
person => {
name => $name,
age => int(rand(99)),
}
};
}
sub test_get_people ($) {
my ($name) = @_;
return [
test_get_person($name), test_get_person($name . "2"),
test_get_person($name . "3"), test_get_person($name . "4"),
];
}
sub test_get_people2 ($) {
my ($name) = @_;
return (test_get_person($name)->{person}, test_get_person($name . "2")->{person},
test_get_person($name . "3")->{person}, test_get_person($name . "4")->{person},);
}
1;
__END__
=head1 NAME
TaglibHelper - module to make it easier to write a taglib
=head1 SYNOPSIS
Put this code at the top of your taglib module:
# this stuff, you change for each taglib
$NS = 'http://apache.org/xsp/testtaglib/v1';
@EXPORT_TAGLIB = (
'func1($arg1)',
'func2($arg1,$arg2)',
'func3($arg1,$arg2;$optarg)',
'func4($arg1,*treearg)',
'func4($arg1,*treearg):listtag=mylist:itemtag=item',
);
# this stuff you don't
use Apache::AxKit::Language::XSP::TaglibHelper;
sub parse_char { Apache::AxKit::Language::XSP::TaglibHelper::parse_char(@_); }
sub parse_start { Apache::AxKit::Language::XSP::TaglibHelper::parse_start(@_); }
sub parse_end { Apache::AxKit::Language::XSP::TaglibHelper::parse_end(@_); }
use strict;
...and then edit the $NS and @EXPORT_TAGLIB to reflect
your taglib's namespace and list of functions.
=head1 DESCRIPTION
The TaglibHelper module is intended to make it much easier to build
a taglib module than had previously existed. When you create a library
that uses TaglibHelper, you need only to write "regular" functions that
take string arguments (optional arguments are supported) and return
standard Perl data structures like strings and hashrefs.
=head1 FUNCTION SPECIFICATIONS
The @EXPORT_TAGLIB global variable is where you list your exported
functions. It is of the format:
funcname(arguments)[:options]
The C<<arguments>> section contains arguments of the form:
=over 4
=item $argument
An argument that is expected to be a plain string
=item *argument
An argument that can take a XML tree in hashref form
=item @argument
An argument that is expected to be an array of plain strings or an array
of hashrefs if the subtag has attributes
=back
These arguments are separated by commas, and optional args are
separated from required ones by a semicolon. For example,
C<$field1,$field2;$field3,$field4> has required parameters C<field1>
and C<field2>, and optional parameters C<field3> and C<field4>.
The options are colon-separated and give extra hints to TaglibHelper in
places where the default behavior isn't quite what you want. All
options are key/value pairs, formatted as B<key1=value1:key2=value2>,
etc. Currently recognized options are:
=over 4
=item listtag
For functions that return arrays, use the indicated wrapper tag for the
list instead of <funcname>-list
=item itemtag
For functions that return arrays of strings, use the indicated wrapper
tag for the list items instead of <funcname>-item
=item forcearray
For functions that always return an array, you should generally set
this option to "1". the reason is that if your array-returning function
only returns one value in its array, the result won't be treated as an
array otherwise.
=item conditional
The function's return value will not be printed, and instead will be
used to conditionally execute child tags. NOTE that arguments to the
function cannot be brought in via child tags, but instead must come in
via attributes.
=item isreally
This function specification is actually an alias for a perl function of
a different name. For example, a specification of
C<"person($name):isreally=get_person"> allows you to have a tag <ns:person
name="Joe"/> that will resolve to Perl code "get_person('Joe')".
=item as_xml
Set this to true and return a well-balanced chunk of XML, and it will be
parsed and added to the output.
=item array_uses_hash
Set this to true to use the preceding hash key as the prefix to
array tag names. In the situation where complex data structures of
hashes pointing to arrays are returned, then this makes the xml output
more meaningful. Otherwise the default of the itemtag or <funcname>-item
is used.
=back
=head1 EXAMPLE
if you had these two functions:
sub hello ($) {
my ($name) = @_;
return "Hello, $name!";
}
sub get_person ($) {
my ($name) = @_;
return {
person => {
name => $name,
age => 25,
height => 200,
}
}
}
...and you called them with this xsp fragment:
<test:hello>
<test:name>Joe</test:name>
</test:hello>
<test:get-person name="Bob"/>
...you would get this XML result:
Hello, Joe!
<person>
<height>200</height>
<age>25</age>
<name>Bob</name></person>
If your function returned deeper result trees, with hashes containing
hashrefs or something similar, that would be handled fine. There are some
limitations with arrays, however, described in the BUGS AND LIMITATIONS
section.
=head1 STRUCTURED INPUT EXAMPLE
If you wish to send structured data (i.e. not just a scalar) to a taglib
function, use "*" instead of "$" for a variable. The input to a taglib
function specified as "insert_person($pid,*extra)" might be:
<test:insert-person pid="123">
<test:extra>
<weight>123</weight>
<friends>
<pid>3</pid>
<pid>5</pid>
<pid>13</pid>
</friends>
</test:extra>
</test:insert-person>
The function call would be the same as:
insert_function("123", {
weight => 123,
friends => [ 3, 5, 13 ]
}
);
The <friends> container holds repeating tags, notice, and TaglibHelper
figured out automatically that it needs to use an arrayref instead of
hashref for the values. But you'll get unexpected results if you mix
repeating tags and nonrepeating ones:
<test:extra>
<weight>123</weight>
<friend>3</friend>
<friend>5</friend>
<friend>13</friend>
</test:extra>
Just wrap your singular repeated tags with a plural-form tag, in this
case <friends>.
=head1 ARRAY INPUT EXAMPLE
If you wish to send an arbitrary number of values to a taglib function's
parameter, use "@" instead of "$" for the variable in the EXPORT_TAGLIB
header array (but still declare it with "$" in the function declaration).
The parameter will end up turning into an arrayref. For example, you might
have a TaglibHelper header:
listbox($name;$pretty_name,@option,$default,$multiple,$size,$required)
and a Perl declaration:
sub listbox ($$$$$$$) {
my ($name, $pretty_name, $options, $default, $multiple, $size, $required) = @_;
...
}
and an XSP file that calls it:
<test:listbox name="country" pretty_name="Pick a Country" default="" required="1">
<test:option name="Please choose a country" value=""/>
<test:option name="United States" value="US"/>
<test:option name="Canada" value="CA"/>
</test:listbox>
It would turn into this function call:
listbox("country", "Pick a Country", [
{ name => "Please choose a country", value => "" },
{ name => "United States", value => "" },
{ name => "Canada", value => "CA" },
], "", undef, undef, 1);
Hopefully the example is clear enough.
=head1 BUGS AND LIMITATIONS
Arrays and arrayrefs are generally difficult to work with because the
items within the array have no keys other than the index value. As a
result, if you want items within an array to be identified correctly,
you must currently make all array items point to a hashref that contains
the item's key or you must use the optional arguments to give TaglibHelper
enough "hints" to be able to represent the XML tree the way you want.
=head1 AUTHOR
Steve Willer, steve@willer.cc
=head1 SEE ALSO
AxKit.
=cut
1.1 xml-axkit/lib/Apache/AxKit/MediaChooser/WAPCheck.pm
Index: WAPCheck.pm
===================================================================
# $Id: WAPCheck.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::MediaChooser::WAPCheck;
use strict;
use Apache::Constants;
sub handler {
my $r = shift;
my $type;
# warn "WAP Check on $ENV{HTTP_ACCEPT}\n";
# warn " and $ENV{HTTP_USER_AGENT}\n";
local $^W;
if ($ENV{HTTP_ACCEPT} =~ /vnd.wap.wml/i) {
$r->notes('preferred_media', 'handheld');
}
elsif (substr($ENV{HTTP_USER_AGENT},0,4) =~
/(
Noki
| Eric
| WapI
| MC21 # cough spit hack ;-) (I used to work at Ericsson)
| AUR\s
| R380
| UP.B
| WinW
| UPG1
| upsi
| QWAP
| Jigs
| Java
| Alca
| MITS
| MOT-
| My\sS
| WAPJ
| fetc
| ALAV
| Wapa
)/x) {
# warn "set media to handheld!\n";
$r->notes('preferred_media', 'handheld');
}
return OK;
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Plugin/Fragment.pm
Index: Fragment.pm
===================================================================
# $Id: Fragment.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::Plugin::Fragment;
use strict;
use Apache::Constants;
use Apache::AxKit::Language::XPathScript;
use XML::XPath;
use XML::XPath::Parser;
use XML::XPath::XMLParser;
sub handler {
my $r = shift;
# warn "Fragment handler\n";
my $mtime = -M $r->finfo;
my $xmlfile = $r->filename;
my $qs = $ENV{QUERY_STRING};
return DECLINED unless $qs;
return DECLINED if ($qs =~ /^\w+=/);
# warn "qs: $qs\n";
my $source_tree;
my $parser = XML::XPath::XMLParser->new();
my $xp = XML::XPath->new();
if (exists($Apache::AxKit::Language::XPathScript::cache->{$xmlfile})
&& $Apache::AxKit::Language::XPathScript::cache->{$xmlfile}{mtime} <= $mtime) {
$source_tree = $Apache::AxKit::Language::XPathScript::cache->{$xmlfile}{tree};
}
if (!$source_tree) {
eval {
$source_tree = $parser->parsefile($xmlfile);
};
if ($@) {
warn "Parse of '$xmlfile' failed: $@";
return DECLINED;
}
$Apache::AxKit::Language::XPathScript::cache->{$xmlfile} =
{
mtime => $mtime,
tree => $source_tree
};
}
my $query = eval {XML::XPath::Parser->new()->parse($qs) };
if ($@) {
warn "Invalid query '$qs': $@\n";
return DECLINED;
}
my $results = $query->evaluate($source_tree);
$r->no_cache(1);
$r->notes('nocache', 1);
my $toptag = $r->dir_config('XPathFragmentElement') || 'resultset';
if ($results->isa('XML::XPath::NodeSet')) {
# warn "setting xml_string to a nodeset size: ", $results->size, "\n";
$r->pnotes('xml_string', "<$toptag>" . join('', map { $_->toString } $results->get_nodelist) . "</$toptag>");
}
else {
# warn "setting xml_string to a value\n";
$r->pnotes('xml_string', "<$toptag>" . $results->value . "</$toptag>");
}
return OK;
}
1;
__END__
=head1 NAME
Apache::AxKit::Plugin::Fragment - Fragment plugin
=head1 SYNOPSIS
AxAddPlugin Apache::AxKit::Plugin::Fragment
=head1 DESCRIPTION
This module provides direct web access to XML fragments, using an
XPath syntax. By simply providing a querystring containing an
XPath query, this module will set the XML to be parsed to be
the XML nodes returned by the query. The nodes will be wrapped in
either <resultset>...</resultset> or you can specify the outer tag
using:
PerlSetVar XPathFragmentElement foo
to wrap it in <foo>...</foo>.
=head1 USAGE
Simply add this module to the plugin list before StyleFinder:
PerlHandler Apache::AxKit::Plugins::Fragment \
AxKit
Then request a URL as follows:
http://server/myfile.xml?/some/xpath/query
Queries that match the regular expression: ^\w+= are ignored, as are
any invalid XPath queries.
Note that it's important to write your stylesheet to make use of this
capability! If you intend to use this Fragment plugin, you can't assume
that your stylesheet will just magically work. It will have to not make
assumptions about the XML being passed into it. The apply_templates()
method of XPathScript is extremely useful here, as is the xpath query
'name(/child::node())' which identifies the top level element's name.
Here's how I got around this with my first experiments with this:
<!-- Main document body -->
<% if (findvalue('name(/child::node())') eq 'page') { %>
<%= apply_templates('/page/body/section') %>
<% } else { %>
<%= apply_templates('/') %>
<% } %>
Which checks that the top level element is called 'page', otherwise it
simply does apply_templates() on all the nodes.
=cut
1.1 xml-axkit/lib/Apache/AxKit/Plugin/Passthru.pm
Index: Passthru.pm
===================================================================
# $Id: Passthru.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::Plugin::Passthru;
use strict;
use Apache::Constants;
sub handler {
my $r = shift;
my %in = $r->args();
if ($in{passthru}) {
$r->notes('axkit_passthru', 1);
}
if ($in{passthru_type}) {
$r->notes('axkit_passthru_type', 1);
}
return DECLINED;
}
1;
__END__
=head1 NAME
Apache::AxKit::Plugin::Passthru - allow passthru=1 in querystring
=head1 SYNOPSIS
AxAddPlugin Apache::AxKit::Plugin::Passthru
=head1 DESCRIPTION
This module allows AxKit to pass through raw XML without processing
if the request contained the option C<passthru=1> in the querystring.
Simply by referencing your xml files as follows:
http://xml.server.com/myfile.xml?passthru=1
You will recieve the raw XML in myfile.xml, rather than it being
pre-processed by AxKit.
This module is also an example of how this can be done, should you
wish to build your own passthru type module that makes the decision
to pass through based on some other parameter, such as the user agent
in use.
A second function of this module is to allow the content-type of
the requested file to be passed through unchanged. AxKit's default
output content-type is "text/html; charset=utf-8". By enabling
this plugin and requesting a file as:
http://xml.server.com/myfile.xml?passthru_type=1
Then the file's content type (as set by the Apache AddType option),
will be used rather than any values set during the processing of the
file.
=cut
1.1 xml-axkit/lib/Apache/AxKit/Plugin/QueryStringCache.pm
Index: QueryStringCache.pm
===================================================================
# $Id: QueryStringCache.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::Plugin::QueryStringCache;
use strict;
use Apache::Constants qw(OK);
sub handler {
my $r = shift;
$r->notes('axkit_cache_extra', $r->notes('axkit_cache_extra') . $r->args);
return OK;
}
1;
__END__
=head1 NAME
Apache::AxKit::Plugin::QueryStringCache - Cache based on QS
=head1 SYNOPSIS
SetHandler axkit
AxAddPlugin Apache::AxKit::Plugin::QueryStringCache
=head1 DESCRIPTION
By default AxKit does not change it's cache file with a change
in querystring, which can lead to some pretty unexpected behaviour
(and also a number of frequently asked questions). In order
to get around this, use this plugin.
=cut
1.1 xml-axkit/lib/Apache/AxKit/Provider/File.pm
Index: File.pm
===================================================================
# $Id: File.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::Provider::File;
use strict;
use vars qw/@ISA/;
@ISA = ('Apache::AxKit::Provider');
use Apache;
use Apache::Log;
use Apache::Constants qw(HTTP_OK);
use Apache::AxKit::Exception;
use Apache::AxKit::Provider;
use Apache::MimeXML;
use AxKit;
use File::Basename;
use Fcntl qw(O_RDONLY LOCK_SH);
sub init {
my $self = shift;
my (%p) = @_;
my $stats_done;
if ($p{key}) {
$self->{file} = $p{key};
}
else {
if ($p{uri} and $p{uri} =~ s|^file:(//)?||) {
$p{file} = delete $p{uri};
}
if ($p{uri}) {
my $r = $p{rel} ? $p{rel}->apache_request() : $self->apache_request();
AxKit::Debug(8, "[uri] File Provider looking up" . ($p{rel} ? " relative" : "") . " uri $p{uri}");
$self->{apache} = $r->lookup_uri($p{uri});
my $status = $self->{apache}->status();
if ($status != HTTP_OK) {
throw Apache::AxKit::Exception::Error(-text => "Subrequest failed with status: " . $status);
}
$self->{file} = $self->{apache}->filename();
AxKit::Debug(8, "[uri] File Provider set filename to $self->{file}");
}
elsif ($p{file}) {
if ($p{rel} && $p{file} !~ /^\//) {
my $file = $p{rel}->apache_request->filename();
my $dir = File::Basename::dirname($file);
require File::Spec;
$self->{file} = File::Spec->rel2abs($p{file}, $dir);
AxKit::Debug(8, "[file] File Provider set filename to $self->{file}");
}
else {
$self->{file} = $p{file};
}
}
else {
$self->{file} = $self->{apache}->filename();
my @stats = stat($self->{apache}->finfo());
$self->{mtime} = $stats[9];
if (-e _) {
if (-r _ ) {
$self->{file_exists} = 1;
}
if (-d _) {
$self->{is_dir} = 1;
}
else {
$self->{is_dir} = 0;
}
}
$stats_done++;
}
}
if (!$stats_done) {
my @stats = stat($self->{file});
$self->{mtime} = $stats[9];
if (-e _) {
if (-r _ ) {
$self->{file_exists} = 1;
}
if (-d _) {
$self->{is_dir} = 1;
}
else {
$self->{is_dir} = 0;
}
}
}
}
sub _is_dir {
my $self = shift;
return $self->{is_dir} if exists $self->{is_dir};
return -d $self->{file};
}
sub key {
my $self = shift;
return $self->{file};
}
sub exists {
my $self = shift;
return $self->{file_exists} if exists $self->{file_exists};
if (-e $self->{file}) {
if (-r _ ) {
$self->{file_exists} = 1;
return 1;
}
else {
AxKit::Debug(2, "'$self->{file}' not readable");
return;
}
}
return;
}
sub process {
my $self = shift;
my $xmlfile = $self->{file};
unless ($self->exists()) {
AxKit::Debug(5, "file '$xmlfile' does not exist or is not readable");
return 0;
}
if ($self->_is_dir) {
AxKit::Debug(5, "'$xmlfile' is a directory");
return 0;
}
local $^W;
if (($xmlfile =~ /\.xml$/i) ||
($self->{apache}->content_type() =~ /^(text|application)\/xml/) ||
$self->{apache}->pnotes('xml_string') ||
Apache::MimeXML::check_for_xml(eval {$self->get_fh} || ${ $self->get_strref } )
) {
# chdir(dirname($xmlfile));
return 1;
}
AxKit::Debug(5, "'$xmlfile' not recognised as XML");
return 0;
}
sub mtime {
my $self = shift;
return $self->{mtime} if exists $self->{mtime};
return ($self->{mtime} = (stat($self->{file}))[9]);
}
sub get_fh {
my $self = shift;
if (!$self->exists()) {
throw Apache::AxKit::Exception::IO(-text => "File '$self->{file}' does not exist or is not readable");
}
my $filename = $self->{file};
# chdir(dirname($filename));
my $fh = Apache->gensym();
if (sysopen($fh, $filename, O_RDONLY)) {
# seek($fh, 0, 0);
return $fh;
}
throw Apache::AxKit::Exception::IO( -text => "Can't open '$self->{file}': $!" );
}
sub get_strref {
my $self = shift;
my $fh = $self->get_fh();
local $/;
my $contents = <$fh>;
return \$contents
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Provider/Filter.pm
Index: Filter.pm
===================================================================
# $Id: Filter.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::Provider::Filter;
use strict;
use vars qw/@ISA/;
@ISA = ('Apache::AxKit::Provider::File');
# Provider for Apache::Filter
use Apache;
use Apache::Log;
use Apache::AxKit::Exception;
use Apache::AxKit::Provider;
use Apache::AxKit::Provider::File;
use AxKit;
use Apache::Constants;
use File::Basename;
# copied mostly from File provider...
sub init {
my $self = shift;
my (%p) = @_;
if ($p{key} || $p{uri} || $p{file}) {
return $self->SUPER::init(%p);
}
else {
$self->{file} = $self->{apache}->filename();
my ($fh, $status) = Apache->filter_register->filter_input();
throw Apache::AxKit::Exception::Error(
-text => "Bad filter_input status"
) unless $status == OK;
$self->{filter_data} = join('', <$fh>);
}
}
sub get_fh {
my $self = shift;
if ($self->{filter_data}) {
throw Apache::AxKit::Exception::IO(
-text => "Can't get fh for Filter filehandle"
);
}
return $self->SUPER::get_fh();
}
sub get_strref {
my $self = shift;
if (exists $self->{filter_data}) {
my $data = $self->{filter_data};
return \$data;
}
return $self->SUPER::get_strref();
}
sub process {
my $self = shift;
my $xmlfile = $self->{file};
local $^W;
# always process this resource.
chdir(dirname($xmlfile));
return 1;
}
sub exists {
my $self = shift;
return 1 if $self->{filter_data};
return $self->SUPER::exists();
}
sub has_changed () { 1; }
sub mtime {
my $self = shift;
return time(); # always fresh
}
1;
1.1 xml-axkit/lib/Apache/AxKit/Provider/Scalar.pm
Index: Scalar.pm
===================================================================
# $Id: Scalar.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::Provider::Scalar;
use strict;
use vars qw/@ISA/;
@ISA = ('Apache::AxKit::Provider');
use Apache;
use Apache::Log;
use Apache::AxKit::Exception;
use Apache::AxKit::Provider;
use AxKit;
sub new {
my $class = shift;
my $apache = shift;
my $self = bless { apache => $apache }, $class;
eval { $self->init(@_) };
return $self;
}
sub apache_request {
my $self = shift;
return $self->{apache};
}
sub init {
my $self = shift;
$self->{data} = $_[0];
$self->{styles} = $_[1];
# warn "Scalar Provider constructed with: $self->{data}\n";
}
sub process {
my $self = shift;
return 1;
}
sub exists {
my $self = shift;
return 1;
}
sub mtime {
my $self = shift;
return time(); # always fresh
}
sub get_fh {
throw Apache::AxKit::Exception::IO( -text => "Can't get fh for Scalar" );
}
sub get_strref {
my $self = shift;
return \$self->{data};
}
sub key {
my $self = shift;
return 'scalar_provider';
}
sub get_styles {
my $self = shift;
return $self->{styles}, [];
}
1;
1.1 xml-axkit/lib/Apache/AxKit/StyleChooser/Cookie.pm
Index: Cookie.pm
===================================================================
# $Id: Cookie.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::StyleChooser::Cookie;
use strict;
use Apache::Constants;
use Apache::Cookie;
use vars qw($VERSION);
$VERSION = '0.01';
sub handler {
my $r = shift;
my $oreo = Apache::Cookie->fetch; # if dougm can call a cookie method "bake". . .
if ( defined $oreo->{'axkit_preferred_style'} ) {
$r->notes('preferred_style', $oreo->{'axkit_preferred_style'}->value);
}
return DECLINED;
}
1;
__END__
=head1 NAME
Apache::AxKit::StyleChooser::Cookie - Choose stylesheets based on a browser cookie
=head1 SYNOPSIS
PerlHandler Apache::AxKit::StyleChooser::Cookie \
AxKit
=head1 DESCRIPTION
This module checks for the presence of a cookie named
'axkit_preferred_style' and sets the preferred style accordingly.
Remember, use the B<title> attribute in your stylesheet PI to define a
matching style.
=head1 AUTHOR
Kip Hampton, kip@hampton.ws
=head1 SEE ALSO
AxKit.
=cut
1.1 xml-axkit/lib/Apache/AxKit/StyleChooser/FileSuffix.pm
Index: FileSuffix.pm
===================================================================
# $Id: FileSuffix.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::StyleChooser::FileSuffix;
use strict;
use Apache::Constants;
use Apache::URI;
sub handler {
my $r = shift;
my $file = $r->filename();
my($style) = ($file =~ m/\.(\w+?)$/);
if ($style && $file =~ s/\.\w+?$// && -e $file) {
$r->filename($file);
# warn "setting notes: $style\n";
$r->notes('preferred_style', $style);
}
return DECLINED;
}
1;
__END__
=head1 NAME
Apache::AxKit::StyleChooser::FileSuffix - Choose stylesheet using file suffix
=head1 SYNOPSIS
PerlTypeHandler Apache::AxKit::StyleChooser::FileSuffix
SetHandler perl-script
PerlHandler AxKit
=head1 DESCRIPTION
This module lets you pick a stylesheet based on the filename suffix. To use
it, simply add this module to the list of PerlTypeHandlers:
PerlTypeHandler Apache::AxKit::StyleChooser::FileSuffix
SetHandler perl-script
PerlHandler AxKit
Then simply by referencing your xml files as follows:
http://xml.server.com/myfile.xml.printable
You will recieve the alternate stylesheets with title "printable". See
the HTML 4.0 specification for more details on stylesheet choice.
Note that this installation is different from the other StyleChoosers,
because the file we're requesting here doesn't actually exist.
Thanks to Salve J. Nilsen <sj...@foo.no> for this module.
=cut
1.1 xml-axkit/lib/Apache/AxKit/StyleChooser/PathInfo.pm
Index: PathInfo.pm
===================================================================
# $Id: PathInfo.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::StyleChooser::PathInfo;
use strict;
use Apache::Constants;
use Apache::URI;
sub handler {
my $r = shift;
my $style = $r->path_info();
if ($style && $style ne '/') {
$r->path_info('');
my $uri = $r->uri();
$uri =~ s/\Q$style\E$//;
$r->uri($uri);
my $uri2 = Apache::URI->parse($r);
$r->header_out('Content-Base', $uri2->unparse);
$r->header_out('Content-Location', $uri2->unparse);
$style =~ s/^\///;
# warn "setting notes: $style\n";
$r->notes('preferred_style', $style);
}
return DECLINED;
}
1;
__END__
=head1 NAME
Apache::AxKit::StyleChooser::PathInfo - Choose stylesheet using PATH_INFO
=head1 SYNOPSIS
PerlHandler Apache::AxKit::StyleChooser::PathInfo \
AxKit
=head1 DESCRIPTION
This module lets you pick a stylesheet based on the extra PATH_INFO. To use
it, simply add this module to the list of PerlHandlers prior to
AxKit:
PerlHandler Apache::AxKit::StyleChooser::PathInfo \
AxKit
Then simply by referencing your xml files as follows:
http://xml.server.com/myfile.xml/printable
You will recieve the alternate stylesheets with title "printable". See
the HTML 4.0 specification for more details on stylesheet choice.
=cut
1.1 xml-axkit/lib/Apache/AxKit/StyleChooser/QueryString.pm
Index: QueryString.pm
===================================================================
# $Id: QueryString.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::StyleChooser::QueryString;
use strict;
use Apache::Constants;
sub handler {
my $r = shift;
my %in = $r->args();
if ($in{style}) {
$r->notes('preferred_style', $in{style});
}
return DECLINED;
}
1;
__END__
=head1 NAME
Apache::AxKit::StyleChooser::QueryString - Choose stylesheet using querystring
=head1 SYNOPSIS
PerlHandler Apache::AxKit::StyleChooser::QueryString \
AxKit
=head1 DESCRIPTION
This module lets you pick a stylesheet based on the querystring. To use
it, simply add this module to the list of PerlHandlers prior to
the main AxKit handler:
PerlHandler Apache::AxKit::StyleChooser::QueryString \
AxKit
Then simply by referencing your xml files as follows:
http://xml.server.com/myfile.xml?style=printable
You will recieve the alternate stylesheets with title "printable". See
the HTML 4.0 specification for more details on stylesheet choice.
=cut
1.1 xml-axkit/lib/Apache/AxKit/StyleChooser/UserAgent.pm
Index: UserAgent.pm
===================================================================
# $Id: UserAgent.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
package Apache::AxKit::StyleChooser::UserAgent;
use strict;
use vars qw($VERSION);
$VERSION = '0.01';
use Apache::Constants;
sub handler {
my $r = shift;
my @UAMap;
my @aoh = split /\s*,\s*/, $r->dir_config('AxUAStyleMap');
foreach (@aoh) {
push (@UAMap, [ split /\s*=>\s*/, $_ ]);
}
# warn "checking UA: $ENV{HTTP_USER_AGENT}\n";
UA: foreach my $ua (@UAMap) {
if ($ENV{HTTP_USER_AGENT} =~ /$ua->[1]/g) {
# warn "found UA $ua->[1], setting 'preferred_style' to $ua->[0]\n";
$r->notes('preferred_style', $ua->[0]);
last UA;
}
}
return DECLINED;
}
1;
__END__
=head1 NAME
Apache::AxKit::StyleChooser::UserAgent - Choose stylesheets based on the user agent.
=head1 SYNOPSIS
In your .conf or .htaccess file(s):
PerlHandler Apache::AxKit::StyleChooser::UserAgent \
AxKit
PerlSetVar AxUAStyleMap "lynx => Lynx,\
explorer => MSIE,\
opera => Opera,\
netscape => Mozilla"
=head1 DESCRIPTION
This module sets the internal preferred style based on the user agent
string presented by the connecting client.
Remember, use the B<title> attribute in your stylesheet PI to define a
matching style.
=head1 AUTHOR
Kip Hampton, khampton@totalcinema.com
=head1 SEE ALSO
AxKit.
=cut
1.1 xml-axkit/t/00basic.t
Index: 00basic.t
===================================================================
use Test;
BEGIN { plan tests => 2 }
END { ok($loaded) }
use AxKit;
$loaded++;
ok(1);
1.1 xml-axkit/t/01provider.t
Index: 01provider.t
===================================================================
use Test;
BEGIN { plan tests => 4 }
require "t/test_module.pl";
test_module("Apache::AxKit::Provider");
test_module("Apache::AxKit::Provider::File");
test_module("Apache::AxKit::Provider::Scalar");
test_module("Apache::AxKit::Provider::Filter", "Apache::Filter");
1.1 xml-axkit/t/02language.t
Index: 02language.t
===================================================================
use Test;
BEGIN { plan tests => 9 }
require "t/test_module.pl";
test_module("Apache::AxKit::Language::XPathScript", "XML::XPath");
test_module("Apache::AxKit::Language::AxPoint", "PDFLib");
test_module("Apache::AxKit::Language::Sablot", "XML::Sablotron");
test_module("Apache::AxKit::Language::LibXSLT", "XML::LibXSLT");
test_module("Apache::AxKit::Language::XMLNewsRDF", "XMLNews::HTMLTemplate");
test_module("Apache::AxKit::Language::XMLNewsNITF", "XMLNews::HTMLTemplate");
test_module("Apache::AxKit::Language::XSP", "XML::XPath");
test_module("Apache::AxKit::Language::XSP::TaglibHelper", "XML::XPath");
test_module("Apache::AxKit::Language::PassiveTeX");
1.1 xml-axkit/t/03stylechooser.t
Index: 03stylechooser.t
===================================================================
use Test;
BEGIN { plan tests => 5 }
require "t/test_module.pl";
test_module("Apache::AxKit::StyleChooser::FileSuffix");
test_module("Apache::AxKit::StyleChooser::Cookie");
test_module("Apache::AxKit::StyleChooser::PathInfo");
test_module("Apache::AxKit::StyleChooser::QueryString");
test_module("Apache::AxKit::StyleChooser::UserAgent");
1.1 xml-axkit/t/04plugins.t
Index: 04plugins.t
===================================================================
use Test;
BEGIN { plan tests => 3 }
require "t/test_module.pl";
test_module("Apache::AxKit::Plugins::Fragment", "XML::XPath");
test_module("Apache::AxKit::Plugins::Passthru");
test_module("Apache::AxKit::Plugins::QueryStringCache");
1.1 xml-axkit/t/05mediachooser.t
Index: 05mediachooser.t
===================================================================
use Test;
BEGIN { plan tests => 1 }
require "t/test_module.pl";
test_module("Apache::AxKit::MediaChooser::WAPCheck");
1.1 xml-axkit/t/test_module.pl
Index: test_module.pl
===================================================================
sub test_module {
my ($module, @prereqs) = @_;
foreach my $prereq (@prereqs) {
my ($ok, $reason) = load_module($prereq);
if (!$ok) {
skip("Skip($prereq prerequisite module not available : $reason)", 1);
return;
}
}
my ($ok, $reason) = load_module($module);
ok($ok, 1, $reason);
}
sub load_module {
my $module = shift;
$module =~ s/::/\//g;
eval {
require "$module.pm";
};
if ($@) {
return wantarray ? (0, $@) : 0;
}
return 1;
}
1;