Umbraco library getMedia parent node property xslt - xslt

How can I get a property from the parent node of a media item using "umbraco.library:GetMedia"?
This allows me to get the current nodes "#nodeName"
<xsl:value-of select="umbraco.library:GetMedia(., 0)/#nodeName" />
I want to get the parent of the current nodes "#nodeName", I've tried the following but it doesn't work:
<xsl:value-of select="umbraco.library:GetMedia(., 0)/../#nodeName" />
Can anyone help me out?
Cheers, JV

With a call to GetMedia() and the #parentID attribute in a variable, I've got it working using the following:
<xsl:template match="/">
<!-- Do not call unless an image was picked -->
<xsl:apply-templates select="$currentPage/image[normalize-space()]" />
</xsl:template>
<xsl:template match="image">
<!-- Note: These WILL fail if no media is selected -->
<xsl:variable name="mediaNode" select="umbraco.library:GetMedia(., false())" />
<xsl:variable name="parentMedia" select="umbraco.library:GetMedia($mediaNode/#parentID, false())" />
<p>
Media: <xsl:value-of select="$mediaNode/#nodeName" />
</p>
<p>
Parent: <xsl:value-of select="$parentMedia/#nodeName" />
</p>
</xsl:template>

Related

Inline element data not appearing where expected after XSL transform

With the following XML
<para>Refer to Table 3 and Figure <grphcref refid="apm00-02-02-000018" shownow="0">6</grphcref>
for the door dimensions and clearances.</para>
and this XSL:
<xsl:template match="prcitem">
<xsl:for-each select="para">
<p>
<xsl:value-of select="." />
<xsl:apply-templates select="./grphcref" />
</p>
</xsl:for-each>
<xsl:apply-templates select="grphcref" />
<xsl:apply-templates select="table" />
<xsl:apply-templates select="unlist" />
</xsl:template>
<xsl:template match="grphcref">
<xsl:variable name="gotoimg" select="concat('#',#refid)"/>
<a href="{$gotoimg}" >
<xsl:value-of select="." /> - <xsl:value-of select="#refid" /> </a>
</xsl:template>
I get:
<p>Refer to Table 3 and Figure 6 for the door dimensions and clearances.
6 - apm00-02-02-000018
when I expected:
<p>Refer to Table 3 and Figure 6 - apm00-02-02-000018
for the door dimensions and clearances.
Can anyone offer guidance as to where I went wrong?
thx
If a para element is supposed to be transformed to a p element then in my view the "natural" way in XSLT is a template
<xsl:template match="para">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
If any other elements need special treatment add a template e.g.
<xsl:template match="grphcref">
<xsl:variable name="gotoimg" select="concat('#',#refid)"/>
<a href="{$gotoimg}" >
<xsl:value-of select="." /> - <xsl:value-of select="#refid" /> </a>
</xsl:template>
Text nodes are copied through to the result by the built-in templates.
The input sample doesn't explain why you mix template matching and for-each and why or whether you need xsl:apply-templates with any particularly selected nodes; as long as the input order should be preserved a simple processing of all child nodes with <xsl:apply-templates/> should suffice.

Move all script tags from the document into the head tag with XSLT

I am using XSLT with a couple of XML files to generate my web page. I want to be able to add a <widget name="example" /> widget tag in my XML, which inserts a widget into the corresponding place in the html. This widget is also defined by an XML file, which defines its html contents and other info.
This goes pretty well, but now the widget needs one or more scripts, which it defines like
<scripts>
<script>jquery<script>
<script>animation</script>
</scripts>
I want to add this script to the head tag of the html, instead of scattering script tags all over the place. The current approach I use for this is the following: I construct my page and put it into a variable, like:
<xsl:template match="/">
<xsl:variable name="document">
<html>
<head>
<title><xsl:value-of select="page/title" /></title>
<xsl:apply-templates select="/page/scripts" mode="content" />
<xsl:apply-templates select="/page/sheets" mode="content" />
</head>
<body>
<div class="header">
<h1><xsl:value-of select="page/title" /></h1>
</div>
<xsl:apply-templates select="$menu/menu" mode="menu" />
<div class="content">
<xsl:apply-templates select="page/content/*" mode="content" />
</div>
</body>
</html>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($document)" mode="collect-resources" />
</xsl:template>
This leaves the scripts tags where they are. After this, I call
<xsl:apply-templates select="exsl:node-set($document)" mode="collect-resources" />
which leads to the following template:
<xsl:template match="head" mode="collect-resources">
<xsl:for-each select="..//scripts">
<xsl:apply-templates select="current()/*" mode="scripts" /> <!-- Convert the scripts defined in this scripts tag into proper HTML script tags. -->
</xsl:for-each>
</xsl:template>
<xsl:template match="scripts" mode="collect-resources" /> <!-- Remove all scripts tags. -->
<xsl:template match="node()" mode="collect-resources">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="./node()" mode="collect-resources"/>
</xsl:copy>
</xsl:template>
Which traverses the entire document two times: one time to collect all the scripts tags to put the scripts in the header, and a second time to remove all scripts tags.
This becomes quite slow for larger documents and is a bit dirty. Is there a way to do this that is neater and/or quicker?
APPENDIX I - widget.xml
<widget>
<scripts>
<script>/widgets/example/main</script>
</scripts>
<content>
<div class="example-widget" />
</content>
</widget>
APPENDIX II - template.xsl
<xsl:template match="widget" mode="content">
<xsl:variable name="path">../widgets/<xsl:value-of select="#name" />/widget.xml</xsl:variable> <!-- The path to the widget xml -->
<xsl:variable name="widget" select="document($path)"/>
<xsl:element name="div">
<xsl:attribute name="class">widget</xsl:attribute>
<xsl:apply-templates select="$widget/widget/scripts" mode="content" /> <!-- Copy the widget script information. -->
<xsl:apply-templates select="$widget/widget/content/*" mode="content" /> <!-- Copy the widget contents. -->
</xsl:element>
</xsl:template>
<xsl:template match="node()" mode="content"> <!-- Copy anything that is not a widget. -->
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates select="./node()" mode="content" />
</xsl:copy>
</xsl:template>
It seems inside your
<head>
<title><xsl:value-of select="page/title" /></title>
<xsl:apply-templates select="/page/scripts" mode="content" />
<xsl:apply-templates select="/page/sheets" mode="content" />
</head>
you want to make sure you process any widgets and their linked scripts directly e.g.
<head>
<title><xsl:value-of select="page/title" /></title>
<xsl:apply-templates select="/page/scripts" mode="content" />
<xsl:apply-templates select="//widget" mode="script"/>
<xsl:apply-templates select="/page/sheets" mode="content" />
</head>
and
<xsl:template match="widget" mode="script">
<xsl:variable name="path">../widgets/<xsl:value-of select="#name" />/widget.xml</xsl:variable> <!-- The path to the widget xml -->
<xsl:variable name="widget" select="document($path)"/>
<xsl:apply-templates select="$widget/widget/scripts" mode="scripts" />
</xsl:template>
and in the other template you would then not output the scripts
<xsl:template match="widget" mode="content">
<xsl:variable name="path">../widgets/<xsl:value-of select="#name" />/widget.xml</xsl:variable> <!-- The path to the widget xml -->
<xsl:variable name="widget" select="document($path)"/>
<xsl:element name="div">
<xsl:attribute name="class">widget</xsl:attribute>
<xsl:apply-templates select="$widget/widget/content/*" mode="content" /> <!-- Copy the widget contents. -->
</xsl:element>
</xsl:template>
You will have to measure whether that performs better.

Force check of each entry when applying template?

I have a template which fetches images and then outputs them into one of two templates.
I would like it to track each individual image and output based on the values of each one. Currently, if one image is wide, it outputs them all according to the wide template. I would rather utilize both templates.
<xsl:template name="get-images">
<xsl:param name="image-entry"/>
<xsl:choose>
<xsl:when test="($image-entry/image/meta/#width) > ($image-entry/image/meta/#height)">
<xsl:apply-templates select="$image-entry/image" mode="wide">
<xsl:with-param name="image-class" select="'full-width'"/>
<xsl:with-param name="caption-class" select="'full-width-caption'"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$image-entry/image" mode="tall">
<xsl:with-param name="image-class" select="'center'"/>
<xsl:with-param name="caption-class" select="'full-width-caption'"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="image" mode="wide">
<xsl:param name="image-class" />
<xsl:param name="caption-class" />
<a href="{$root}/image/full{#path}/{filename}">
<img src="{$root}/image/wide{#path}/{filename}" alt="{../description}" class="{$image-class}"/>
</a>
<p class="{$caption-class}">
Image courtesy of: <xsl:value-of select="../title"/>
</p>
</xsl:template>
<xsl:template match="image" mode="tall">
<xsl:param name="image-class" />
<xsl:param name="caption-class" />
<span class="centered">
<a href="{$root}/image/full{#path}/{filename}">
<img src="{$root}/image/tall{#path}/{filename}" alt="{../description}" class="{$image-class}"/>
</a>
</span>
<p class="{$caption-class}">
Image courtesy of: <xsl:value-of select="../title"/>
</p>
</xsl:template>
Bonus question: How can I ignore the caption if value source doesn't exist?
<article-images field-id="24" subsection-id="5" items="1">
<item id="109" creation-date="2014-04-24T05:16:52+01:00">
<image size="317 KB" path="/uploads" type="image/jpeg">
<filename>funny-lolcat.jpg</filename>
<meta creation="2014-04-24T05:16:52+01:00" width="1600" height="1200" />
</image>
<description mode="formatted"><p>Aww!</p>
</description>
<title handle="" />
<source handle="" />
</item>
</article-images>
Instead of two modes, use one mode and move the wide vs not-wide logic into the template match expressions:
<xsl:template match="image[meta/#width > meta/#height]">
<!-- logic for wide image -->
</xsl:template>
<xsl:template match="image">
<!-- logic for not-wide image -->
</xsl:template>
And now you can just apply templates to all the images in one go without the choose:
<xsl:apply-templates select="$image-entry/image"/>
To ignore the caption if there's no source, I'd move the caption logic into another template matching the source element
<xsl:template match="source" mode="caption">
<p class="full-width-caption">
Image courtesy of: <xsl:value-of select="../title"/>
</p>
</xsl:template>
Then in the main template do:
<xsl:apply-templates select="../source" mode="caption"/>
If there is a source this will produce a caption, if there isn't then it'll produce nothing.
Given the example you've just added to the question it looks like you want to exclude the caption no when the source element "doesn't exist" but rather if it has no value. You can do this by changing the above apply-templates to
<xsl:apply-templates select="../source[string()]" mode="caption" />
This would add a caption for <source handle="">something</source> but not for <source handle="" />.
What this is doing is filtering so we only select the ../source element if the [string()] predicate is true. The string() function returns the "string value" of the context element (the source in this case) and a string in boolean context is treated as false if it is empty and true otherwise. So in this case the effect is to apply templates to the source element only if it has a non-empty value.

Docbook <bibliosource> gets lost in the transformation

I have the following structure
<informalfigure xmlns="http://docbook.org/ns/docbook">
<info>
<bibliosource>Photo: John Doe</bibliosource>
</info>
<mediaobject>
<imageobject>
<imagedata contentdepth="30mm" contentwidth="35mm" fileref="path/to/img.jpg"/>
</imageobject>
</mediaobject>
<caption>
<para>Here goes the caption for the image</para>
</caption>
</informalfigure>
<imagedata> and the <caption> get rendered but the <bibliosource> is gone after the transformation.
I'm using the docbook-xsl-1.77.0/xhtml/docbook.xsl for the transformation... I need some guidance on where/how to change the xslt so that <bibliosource> gets transformed properly.
Thanks!
Here is a simple solution. Add the following to your XHTML customization layer:
<xsl:template match="d:caption">
<div>
<xsl:apply-templates select="." mode="common.html.attributes"/>
<xsl:call-template name="id.attribute"/>
<xsl:if test="#align = 'right' or #align = 'left' or #align='center'">
<xsl:attribute name="align"><xsl:value-of select="#align"/></xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</div>
<div><xsl:value-of select="../d:info/d:bibliosource"/></div> <!-- This line added -->
</xsl:template>
The above works with the namespace-aware XSLT stylesheets (docbook-xsl-ns).
If you use the non-namespace-aware stylesheets (docbook-xsl), the corresponding customization becomes:
<xsl:template match="caption">
<div>
<xsl:apply-templates select="." mode="common.html.attributes"/>
<xsl:call-template name="id.attribute"/>
<xsl:if test="#align = 'right' or #align = 'left' or #align='center'">
<xsl:attribute name="align"><xsl:value-of select="#align"/></xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</div>
<div><xsl:value-of select="../blockinfo/bibliosource"/></div> <!-- This line added; note blockinfo -->
</xsl:template>
The text of the <bibliosource> element will appear in a separate <div> directly below the caption. The original template is in graphics.xsl.

getting the value of the previous elements in other template in xslt

Source:
<Data>
<AB>
<choice>Disclose</choice>
<image>
<img alt="No Image" xlink:href="abcd:202-11587" xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:title="Image" />
</image>
<link>abcd</link>
</AB>
<AB>
<choice>All</choice>
<image>
<img alt="No Image" xlink:href="abcd:202-2202" xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:title="Image" />
</image>
<link>all</link>
</AB>
</Data>
XSLT
<xsl:template match="Data">
<xsl:for-each select="AB">
<xsl:variable name="temp" select="choice"/>
<xsl:choose>
<xsl:when test="$temp='Disclose'">
<xsl:apply-templates select="image/node()"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template match="simple:image/xhtml:img">
<!-- I want to get the the name of the "choice" here-->
<!-- some other process-->
<!-- how to access the value of the <choice> element of that section-->
<!-- how to access <link> element of that section-->
</xsl:template>
Can any one help how to do it.
Firstly, as this may just be an oversight with your code sample, you have specified namespaces in your matching template
<xsl:template match="simple:image/xhtml:img">
However, there are no references to the "simple" namespace in your sample XML, so in this case it should just be the following
<xsl:template match="image/xhtml:img">
But in answer to you question, to get the choice element, because you currently posisioned on the img element, you can search back up the hierarchy, like so
<xsl:value-of select="../../choice" />
The '..' represents the parent element. So, you are going back up to the AB element, and getting its child choice element.
And similarly for the link element
<xsl:value-of select="../../link" />
Note, it doesn't have to be xsl:value-of here, if there were multiple link elements, you could use xsl:apply-templates
<xsl:apply-templates select="../../link" />
And, if you required only link elements that occurred after the parent image element, you could do something like this
<xsl:apply-templates select="../following-sibling::link" />