Cannot include sub xsl files within main xsl file based on if statement [duplicate] - xslt

The <xsl:import> and <xsl:include> elements seem to behave quite specific.
What I am trying to do:
<xsl:import href="{$base}/themes/{/settings/active_theme}/styles.xsl" />
I want to allow loading different themes for my application. I have a settings in my App which stores the "currently active theme" folder name in a xml node.
Unfortunately the code above won't work.
Does anybody know about a workaround to achieve what I want to do?
edit:
just confirmed with a XSLT guru via Twitter... there's no nice way of doing this. Easiest solution in my case will probably be to seperate frontend and backend stylesheets and load them individually to the XSLTProcessor...

xsl:import assembles the stylesheet prior to execution. The stylesheet can't modify itself while it is executing, which is what you are trying to achieve.
If you have three variants of a stylesheet for use in different circumstances, represented by three modules A.xsl, B.xsl, and C.xsl, then instead of trying to import one of these into the module common.xsl that contains all the common code, you need to invert the structure: each of A.xsl, B.xsl, and C.xsl should import common.xsl, and you should select A.xsl, B.xsl, or C.xsl as the principal stylesheet module when initiating the transformation.

What I am trying to do:
<xsl:import href="{$base}/themes/{/settings/active_theme}/styles.xsl" />
This isn't allowed in any version (1.0, 2.0, or 3.0) of XSLT.
In XSLT 2.0 (and up) one may use the use-when attribute, but the conditions that may be specified are very limited.
One non-XSLT solution is to load the importing XSLT stylesheet as an XmlDocument and use the DOM API to set href attribute to the really wanted value -- only then invoke the transformation.

Related

How to break caching on exist-db of included XSLs in Transform

I have a large set of XSLs that we recently went through and implemented a shared XSL template with common bits. We included an xsl:include in all the main XSLs now to pull these in. We had no issues at first until we started to make changes to the shared XSL.
For information, the whole system is web based, calling queries to dynamically format documents in the database given different XSLs through XSL FO and RenderX.
The main transform is:
let $fo := util:expand(transform:transform($articles, doc("/db/Customer/data/edit/xsl/Custbatch.xsl"), $parameters))
That XSL (Custbatch.xsl) has:
<xsl:include href="Custshared.v1.xsl"/>
If we make an edit to "Custshared.v1.xsl" is not reflected in the result because it is obvious that "Custshared.v1.xsl" is being cached and used. We know this because as you can see the name now includes "v1". If we make a change and change all the references say from v1 to v2, it all works. But this seems a bit ridiculous as that means we have to change the 18 XSLs that include this XSL or do something silly like restart the database.
So, what am I missing in the setup or controller.xql (which has the following on all not matched paths), to get things not to cache. I assume that is all internal so this setting likely does not matter. Is there some other setting in the config that does?
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<cache-control cache="no"/>
</dispatch>
In reading the document here: http://exist-db.org/exist/apps/doc/xsl-transform.xml, it states:
"The stylesheet will be compiled into a template using the standard Java APIs (javax.xml.transform). The template is shared between all instances of the function and will only be reloaded if modified since its last invocation."
However, if I change an included XSL, it is not being used.
Update #1
I even went as far as creating a query that returns the XSL that is included, then I use:
<xsl:include href="http://localhost/get-include-xsl.xq"/>
This does work as formatting is not broken, but changing the underlying XSL yields the same result. So even that Xquery result is cached.
Update #2
And yes, through some simple test all is proven.
If I make any change to the root template (like add a meaningless space) and run, it does include the changes made in the include. If I only change the included XSL, no changes happen.
So lacking anything else, we could always write a Xquery that basically touches all the main templates after a change is made to the include template. Seems so wrong as a workaround.
Update #3
So the workaround we are currently using is that we have an unused "variable" in the XSL (version) and when we update the shared template, we execute that query which basically updates the value in that variable. At least it's only one XQuery and maybe we should attach to a trigger.
There is a setting in $exist-db-root$/conf.xml for the XSL transformer where you can turn off caching: <transformer class="net.sf.saxon.TransformerFactoryImpl" caching="no"> (The default is 'yes')

Is it possible to generate both HTML and Wiki markup at the same time using XSLT?

I would like to generate both HTML and Wiki markup at the same time using XSLT (from an XML source document) - just wondering if it's possible. It would be nice if I could use the same XSLT to do both rather than writing/maintaining two separate files.
The HTML report will be for general viewing, and the Wiki markup will be published to Confluence.
If you want to create more than one result document using a single stylesheet than XSLT 2.0 and later support that using xsl:result-document, see the specification http://www.w3.org/TR/xslt20/#creating-result-trees. As you then want to process the same elements twice, you usually also make use of modes to separate the different processing, e.g. use one mode to produce HTML, the other mode to produce Wiki markup.
With pure XSLT 1.0 you can only create a single result document, however, some XSLT 1.0 processors, like Xalan (http://xml.apache.org/xalan-j/extensions_xsltc.html#redirect_ext) or xsltproc (http://exslt.org/exsl/elements/document/index.html) support an extension to create more than one result document.

Include user control .ascx into xslt

I've created .ascx user control and I'm trying to find a way for including it into xslt rendering. How can I do this? I'm doing it for Sitecore. I thought maybe create a placeholder, but placeholders cannot be defined in renderings. I appreciate any help you can provide.
It's not possible to include ASCX file into xslt file because: XSLT transforms XML to HTML or to XML or to plain text but not to ASP.NET pages.
You can include xslt file into ascx but not ascx into xslt file. The best way is to change your xslt file into ascx file, and to include there with placeholders or directly .
I'd suggest to avoid using XSLT.
They seem pretty easy to use, but it's really hard to refactor the code.
Well, it's not possible to call user controls(.ascx) directly from XSLT files. However depending upon what you want to achieve, you can call .net methods, called XSLT extension methods, from XSLT file. For instance, you may need to write code similar to below to call custom .Net GetData() method.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:customObject="urn:yourNamespace">
<new-data>
<xsl:value-of select="customObject:GetData()"/>
</new-data>
...
Of course the type needs to be registered before it can be used. Type registration can be done into web.config or dynamically by calling AddExtensionObjectMethod of XSLTArgumentList class.
Sitecore offers XSLT extension controls too and unlike extension methods, it isn't a .net feature. XSL extension controls are XML elements in XSL renderings that correspond to .NET classes. For example, the XSL extension control corresponds to the
Sitecore.Web.UI.XslControls.Text .NET class. It will be consumed something like this in XSLT file:
XSL extension controls are standalone elements in the XSL code.
To register a custom type, add following to element in web.config:
<extension mode="on" type="NamespaceName.ClassName, AssemblyName" namespace="http://www.w3.org/1999/XSL/Transform" singleInstance="true"/>
Reference: http://sdn.sitecore.net/upload/sitecore6/64/presentation_component_xsl_reference_sc62-64-a4.pdf

XSL include based on XSL:WHEN condition

I have a scenario here I have multiple xsl designed for different type of XML files. Now I have some application ID that is passed to my XSL library with now I want to load different xsl based on this application ID values.
Like if my application ID is 1
if application ID is 2
how can I do this???
Please help
In XSLT, xsl:include and xsl:import must be top-level elements, as said in the specifications (here for version 1.0).
That means that you can not condition the loading of another XSL file based on the XML you are applying the XSL to.

preproccesing in XSLT

is it at all possible to 'pre-proccess' in XSLT?
with preprocessing i mean updating the (in memory representation) of the source tree.
is this possible, or do i need to do multiple transforms for it.
use case:
we have Docbook reference manuals for out clients but for certain clients these need different 'skins' (different images etc). so what i was hoping to do is transform the image fileref path depending on a parameter. then apply the rest of the normal Docbook XSL templates.
Expanding on Eamon's answer...
In the case of either XSLT 1.0 or 2.0, you'd start by putting the intermediate (pre-processed) result in an <xsl:variable> element, declared either globally (top-level) or locally (inside a template).
<xsl:variable name="intermediate-result">
<!-- code to create pre-processed result, e.g.: -->
<xsl:apply-templates mode="pre-process"/>
</xsl:variable>
In XSLT 2.0, the value of the $intermediate-result variable is a node sequence consisting of one document node (was called "root node" in XSLT/XPath 1.0). You can access and use it just as you would any other variable, e.g., select="$intermediate-result/doc"
But in XSLT 1.0, the value of the $intermediate-result variable is not a first-class node-set. Instead, it's something called a "result tree fragment". It behaves like a node-set containing one root node, but you're restricted in how you can use it. You can copy it and get its string-value, but you can't drill down using XPath, as in select="$intermediate-result/doc". To do that, you must first convert it to a first-class node-set using your processor's node-set() extension function. In Saxon 6.5, libxslt, and 4xslt, you can use exsl:node-set() (as in Eamon's answer). In MSXML, you'd need to use msxsl:node-set(), where xmlns:msxsl="urn:schemas-microsoft-com:xslt", and in Xalan, I believe it's called xalan:nodeset() (without the hyphen, but you'll have to Google for the namespace URI). For example: select="exsl:node-set($intermediate-result)/doc"
XSLT 2.0 simply abolished the result tree fragment, making node-set() unnecessary.
This is not possible with standards compliant XSLT 1.0. It is possible in every actual implementation I've used, however. The extensions with which to do that differ by engine, however. It is also possible in standard XSLT 2.0 (which is in any case much easier to work with - so if you can, just use that).
If your xslt processor supports EXSLT, the exsl:node-set() function does what you're looking for. msxml has an identically named extension function as well (but with a different namespace uri, the functions are unfortunately not trivially compatible).
Since you are trying to generate slightly different output from the same DocBook XML source, you might want to look into the "profiling" (conditional markup) support in DocBook XSL stylesheets. See Chapter 26 in DocBook XSL: The Complete Guide by Bob Stayton:
Profiling is the term used in DocBook
to describe conditional text.
Conditional text means you can create
a single XML document with some
elements marked as conditional. When
you process such a document, you can
specify which conditions apply for
that version of output, and the
stylesheet will include or exclude the
marked text to satisfy the conditions.
This feature is useful when you need
to produce more than one version of a
document, and the versions differ in
minor ways.
For example, to use different images for, say, Windows and Mac versions of the same document, you might have a DocBook XML fragment like this:
<figure>
<title>The Foo dialog</title>
<mediaobject>
<imageobject os="windows">
<imagedata fileref="screenshots/windows/foo.png"/>
</imageobject>
<imageobject os="mac">
<imagedata fileref="screenshots/mac/foo.png"/>
</imageobject>
</mediaobject>
</figure>
Then, you would use the profiling-enabled versions of the DocBook XSL stylesheets with the profile.os parameter set to windows or mac.
Maybe you should use XSLT "OOP" methods here. Put all the common templates to all clients in a stylesheet, and create an stylesheet for each client with specific templates overriding common ones. Import the common stylesheet within the specific ones with xsl:import, and you'll do only one processing by calling the stylesheet corresponding to a client.