The options that you’ve looked at so far for
associating documents with transformative processes are quite
capable. Often, the use of stylesheet processing instructions, or
simple AxKit processor definitions, combined with constraints imposed
by Apache built-in <Files>
,
<Directory>
,
<Location>
, and similar block-level
directives, are all you need to meet the needs of many sites.
However, AxKit offers even more flexibility by providing additional
mechanisms that allow you to combine these low-level style processing
options into logical groups that can be
selected at runtime. In this section, I introduce the concepts and
syntax for creating
named
styles and media types and explain
how these can be used in conjunction with the
StyleChooser and
MediaChooser modules to apply just the
right content transformations under the right circumstances.
The reasons for using named style and media blocks are quite varied. Generally, they are best suited for cases when you need to select a transformation (or a chain of transformations) based on a condition external to the properties of the source XML content itself. Some reasons to use named styles and media blocks include:
- Vendor branding
Your site offers a service, and each customer wants the content presented in a way that matches his unique look and feel.
- User-selected skinning
One size never fits all. You want to offer your visitors the ability to select the style that suits them best.
- Automated metadata extraction
You want to extract important metadata, like abstract summaries, author, title, copyright information, etc., by simply applying an alternative set of styles to your documents.
- Role-specific data transformations
Your customers, vendors, shipping department, and company president all have different needs when they look at your product list. You want to serve all of them from the same XML data source.
- Application state-specific transformations
You want to apply different transformations to your XML data to reflect the current state (or screen) of your online applications.
- Any or all of the above
You want to incorporate any or all of the features listed, plus the ability to provide each option across a series of different client devices (phone, desktop browser, TV, etc.).
AxKit’s named styles and media blocks, in conjunction with StyleChooser and MediaChooser plug-in modules, provide absolute control over when and how your XML content gets transformed. Literally, any condition that can be determined via the Perl programming language can be used to regulate which style processors will be applied to your content. The next two sections examine both named styles and named media, how they can be used together, and how AxKit’s plug-ins can be used to select just the right combination.
A
named
style consists of one or more style processor definitions
grouped together and given a unique name. The name given is used in
conjunction with a StyleChooser or other AxKit plug-in to select the
processors to apply in response to a given request. These modules set
AxKit’s internal
preferred_style
property based on a condition; then, if the name contained in that
property matches the name given a named style block within the scope
of the current request, all styles associated with that name are
applied to the source document.
Named styles are created in one of two
ways: by using the
<AxStyleName>
configuration
directive or by adding the optional title
and
alternate
attributes to an
xml-stylesheet
processing instruction in the
source XML document. When using directive-based named styles, the
<AxStyleName>
configuration block acts as a
container of one or more styling directives and uniquely associates
that set of processors with the given name.
Suppose that you have an XML document that contains detailed data about the list of products offered by an online shop. You want to present a page typical for this kind of application: a list of all products, and links to more detailed information for each. Now, you could use an offline transformation to carve up the larger document into a set of smaller ones, then associate different style processors based on the files’ location on the disk—one for the list view, and one for the detail views. The weakness of that approach, however, is that you must remember to rerun the process every time the product list is updated. AxKit’s named styles provide another option: you can create two named styles that can be applied to the same global list of products—one presents the full list with minimal detail, and one reveals additional details for a specific product. The directives used to set up these named alternate views may look like this:
<Files products.xml> <AxStyleNamelist_view
AxAddProcessor text/xsl /styles/list.xsl </AxStyleName> <AxStyleNamedetail_view
AxAddProcessor text/xsl /styles/detail.xsl </AxStyleName> </Files>
This creates two named styles that AxKit can apply conditionally when
serving the products.xml
document: the
list_view
style, which transforms the content into
the complete list of products, and the detail_view
style, which selects a single record from the global list and shows
more detailed information about that specific item. The same can be
achieved when using xml-stylesheet
processing
instructions by including
title
and alternate
attributes
with the appropriate values:
<?xml-stylesheet type="text/xsl" href="/styles/list.xsl"title="list_view" alternate="yes"
?> <?xml-stylesheet type="text/xsl" href="/styles/detail.xsl"title="detail_view" alternate="yes"
?>
You can now use a StyleChooser plug-in to select between
the two styles. AxKit ships with a number of default StyleChooser
modules that set the preferred style based on commonly used
conditions such as the name of the requesting user agent, a specific
query string parameter, or the value of an HTTP cookie. These modules
are added to AxKit via the
AxAddPlugin
configuration directive. So, for example, if you want to choose
between the list view and detail view of your products document,
based on the query string sent by the browser, you add the following
to your configuration file:
AxAddPlugin Apache::AxKit::StyleChooser::QueryString
With this in place, you can now easily select the different views of
your products document by adding a style
parameter
whose value matches one of your named styles to the query string of a
request to products.xml
:
http://myhost.tld/products.xml?style=detail_view
When using named styles, it is
important to set up a default style to cover cases in which the
StyleChoosers may not set a preferred style or in which the style
name returned does not match any style configured for the current
document. This can be done by adding a style with the name
#default
or by using the
AxStyle
directive to set an existing named style as the default style:
# Fall back to a default style that does not match any # other named style <AxStyleName"#default"
AxAddProcessor test/xsl /styles/list.xsl </AxStyleName> # Does the same, but uses the existing 'list_view' style # as the default style. <AxStyleNamelist_view
AxAddProcessor test/xsl /styles/list.xsl </AxStyleName> AxStylelist_view
You can achieve the same using xml-stylesheet
processing instructions by defining a preferred style (one that has a
title
attribute but not an
alternate
attribute with the value of
yes
):
<?xml-stylesheet type="text/xsl" href="/styles/product_list.xsl" title="product_list"?> <?xml-stylesheet type="text/xsl" href="/styles/product_detail.xsl" title="product_detail" alternate="yes"?>
Another common use for named styles is user-defined “skinning” of a site’s content. Recall your cryptozoology site from Chapter 3. Imagine that this site has become the primary resource on the Web for that topic. Both serious researchers in the area, as well as the hyper-ironic, slumming for a kitschy thrill, frequent it. Both groups share an interest in the site’s content, but their expectations and preferences about how that content is best presented are likely to be quite different. To meet this need, you can create a new, flashier stylesheet to render the content for your hipster visitors and then add a named style to your configuration file that reflects that preference:
# the existing, plain style <AxStyleName plain> AxAddProcessor text/xsl stylesheets/cryptozoo.xsl </AxStyleName> # provides the RSS 1.0 news feed # view of the sightings <AxStyleName rss1> AxAddProcessor text/xsl stylesheets/cryptidsightings_rss.xsl </AxStyleName> # the new, flashy style <AxStyleName flashy> AxAddProcessor text/xsl stylesheets/cryptozoo_flashy.xsl </AxStyleName> # set the plain style as default AxStyle plain # lets the ?style=rss1 interface work for the news feed AxAddPlugin Apache::AxKit::StyleChooser::QueryString
Here, the choice between styles is not so much a short-term, functional one (as in the list/detail view for your products document) but rather a persistent preference that should be reflected each time the reader visits the site. In this case, you are better off giving the user an HTTP cookie and using AxKit’s Cookie StyleChooser.
But wait, you are already using the query string
StyleChooser
to provide the interface to the RSS
feed. You don’t want that link to break. No matter.
Since AxKit plug-ins are executed in the order that they appear in
the configuration, you only need to add the Cookie StyleChooser
before the one that examines the query string.
That way, even if the user has a cookie that sets
AxKit’s
preferred_style
,
its value will be overwritten by the query string
StyleChooser
. Therefore, the functional
transformation that provides the RSS interface will continue to work:
# lets the ?style=rss1 interface work for the news feed # and allows skinning based on the user's cookie AxAddPlugin Apache::AxKit::StyleChooser::Cookie AxAddPlugin Apache::AxKit::StyleChooser::QueryString
What if you decide not to rely solely on AxKit’s order of execution to handle the cases in which the styles may overlap? You can choose instead to write a little plug-in module that combines the two StyleChoosers while giving RSS query string interface top priority. (See Example 4-1.)
Example 4-1. CryptidStyleChooser.pm
package CryptidStyleChooser; # Combine the Cookie and QueryString StyleChoosers, giving # preference to a style named 'rss1' in the query string. use strict; use Apache::Constants qw(OK); use Apache::Cookie; sub handler { my $r = shift; my $preferred_style = undef; # Borrow the key names for the existing StyleChoosers my $query_key = $r->dir_config('AxStyleChooserQueryStringKey') || 'style'; my $cookie_key = $r->dir_config('AxStyleChooserCookieKey') || 'axkit_preferred_style'; my %query_params = $r->args( ); if ( defined ($query_params{$query_key} ) ) { $style = $query_params{$query_key}); # give preference to the rss1 query param by returning if you find it set. if ( $style eq 'rss1' ) { $r->notes('preferred_style', $style); return OK; } } # let the users' cookie override the query string otherwise my $cookies = Apache::Cookie->fetch; . . if ( defined $cookies->{$cookie_key} ) { $style = $cookies->{$cookie_key}->value); } # finally, set AxKit's internal 'preferred_style' if you found a style if ( defined( $style ) ) { $r->notes('preferred_style', $style; } return OK; } 1;
To use your custom StyleChooser
plug-in, you need to install it on your server in a location that
Perl knows about and to replace the former
AxAddPlugin
directives with:
# lets the ?style=rss1 interface work for the news feed # and allows skinning based on the user's cookie AxAddPlugin CryptidStyleChooser
You can now rest assured that the correct stylesheets will be applied to the documents in your cryptid site for each case that arises. In reality, custom StyleChooser plug-ins are rarely required. (In fact, you didn’t really need one here.) By creating a StyleChooser that covers both the presentational and functional transformations for your site, you peeked at just how easy it can be to use named styles to get AxKit to apply exactly the styles that you want, no matter how tricky the requirements first appear.
In addition to named styles, AxKit offers a way to further define the
styles that are conditionally applied to a site’s
resources by associating sets of styling directives with the media
type the requesting client expects. This is achieved using the
<AxMediaType>
container directive.
<AxMediaType screen> AxAddProcessor text/xsl /styles/screen.xsl </AxMediaType>
Similar to the <AxStyleName>
block, the
<AxMediaType>
tells AxKit to examine an
internal property,
preferred_media
,
set by a MediaChooser,
or other plug-in. If the value of that property matches the name
given to an <AxMediaType>
block, the
directives within that block are used to process the current request:
# Set a global style for most client devices <AxMediaType screen> AxAddProcessor text/xsl /styles/screen.xsl </AxMediaType> # But give smaller devices a different look <AxMediaType handheld> AxAddProcessor text/xsl /styles/handheld.xsl </AxMediaType>
Unlike named styles, AxKit sets a default value of
screen
for the
preferred media type, if no
other is found. This behavior can be altered by setting the
AxMedia
directive to the name of the <AxMediaType>
block that you want to use as the default. For example, the following
configures AxKit to use the handheld
media type as
the default within the current context:
AxMedia handheld
Pretend that you want to further enhance your silly cryptozoology site. You want to support visitors using web phones. In this case, you simply create another set of stylesheets that renders the content appropriately for that platform and alter your configuration to reflect the change:
# the previous, screen-only config, now wrapped in an <AxMediaType> block for clarity <AxMediaType screen> <AxStyleName plain> AxAddProcessor text/xsl stylesheets/cryptozoo.xsl </AxStyleName> <AxStyleName rss1> AxAddProcessor text/xsl stylesheets/cryptidsightings_rss.xsl </AxStyleName> <AxStyleName flashy> AxAddProcessor text/xsl stylesheets/cryptozoo_flashy.xsl </AxStyleName> </AxMediaType> # the new <AxMediaType> for handheld support <AxMediaType handheld> <AxStyleName plain> AxAddProcessor text/xsl stylesheets/cryptozoo_handheld.xsl </AxStyleName> <AxStyleName rss1> AxAddProcessor text/xsl stylesheets/cryptidsightings_rss.xsl </AxStyleName> </AxMediaType> AxStyle plain
You can give the same name to two
different-named style blocks in the same configuration
context without collision, so long as they appear as the children of
different media type blocks. Here, you give the default style
contained by the new media type the same name as the default screen
style. This allows the AxStyle
directive to work
as expected for both media types:
# lets the ?style=rss1 interface work for the news feed AxAddPlugin CryptidStyleChooser AxAddPlugin Apache::AxKit::MediaChooser::WAPCheck
With this addition, your absurd little mystery animals’ site becomes quite respectable, technologically speaking. You offer users a choice of styles through which to view the content, an RSS 1.0 news feed of recent cryptid sightings, and full support for surfers using web phones—all this from two XML documents, a few stylesheets, and, most importantly, knowing how to configure AxKit to apply the right style at the right time.
Get XML Publishing with AxKit now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.