xslt hyperlink, separate the url and descption - sharepoint

I have a list in sharepoint with a hyperlink column.
I'm putting this list into xml and applying xslt to it.
the xml is producing output in the form of:
<link>http://www.foo.com, http://www.foo.com</link>
how can i display this link using xslt?
thanks

How about:
<xsl:template match="link">
<a href="{substring-before(.,',')}">
<xsl:value-of select="substring-after(.,',')"/>
</a>
</xsl:template>

For XSLT 2.0
<xsl:template match="link">
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="substring-before(.,',')"/>
</xsl:attribute>
<xsl:value-of select="substring-after(.,',')"/>
</xsl:element>
</xsl:template>
Although it makes it slightly less readable, the extended syntax is considered good practice when stylesheets become large. Literal Result Elements are not as easy to manipulate via XPath as xsl:element/xsl:attribute

Related

How to keep my topics printing consecutively, without an empty page after each topic

DITA 3.6
Oxygen XML Editor 23.1
The "DITA for Print" book nor any other source, so far, has helped me produce a pdf where the topics are consecutive rather than having a blank page after them. To each topicref in the ditamap, I added outputclass="page-break-avoid". To each topic element in each file, I added outputclass="page-break-avoid".
Should I add something in an xsl file? Can you point me to the answer?
The blank page is generated from the definition fo:page-sequence/#force-page-number property.
To change not to generate blank-page, you can override org.dita.pdf2/cfg/fo/attrs/commons-attr.xsl.
<xsl:attribute-set name="__force__page__count">
<xsl:attribute name="force-page-count">
<xsl:choose>
<xsl:when test="/*[contains(#class, ' bookmap/bookmap ')]">
<xsl:value-of select="'even'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'auto'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:attribute-set>
Change the value even to auto will satisfy your needs.
Steps to customize
I'm not familiar with PDF2 plug-in. So this may not the standard way. But I could customize it by following steps.
Make customization XSL file
[DITA-OT]/plugins/org.dita.pdf2/Customization/fo/attrs/commons-attr.xsl
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:rx="http://www.renderx.com/XSL/Extensions"
version="2.0">
<xsl:attribute-set name="__force__page__count">
<xsl:attribute name="force-page-count">
<xsl:choose>
<xsl:when test="/*[contains(#class, ' bookmap/bookmap ')]">
<xsl:value-of select="'auto'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'auto'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:attribute-set>
</xsl:stylesheet>
Include it into [DITA-OT]/plugins/org.dita.pdf2/Customization/fo/attrs/custom.xsl
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="2.0">
<xsl:include href="commons-attr.xsl"/>
</xsl:stylesheet>
Customize [DITA-OT]/plugins/org.dita.pdf2/Customization/catalog.xml
<?xml version="1.0" encoding="utf-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
<!-- Custom attributes entry -->
<uri name="cfg:fo/attrs/custom.xsl" uri="fo/attrs/custom.xsl"/>
</catalog>
Specify customization.dir property with [DITA-OT]\plugins\org.dita.pdf2\Customization in Oxygen transformation scenario.
Apply transformation scenario from Oxygen.
I've got the following temp/topic.fo. The fo:page-sequence/#force-page-count became auto and in the result PDF, the redundant blank pages are removed.

XSL if test string comparison not working

I have some XML this looks like this:
<region class="TableInfo">
text
</region>
<region>
text
</region>
I want to write XSL that only preserves that part without the class="TableInfo".
I've tried a number of different ways, including:
<xsl:for-each select="region[class!='TableInfo']">
</xsl:for-each>
and
<xsl:for-each select="region">
<xsl:if test="not(class=&apos;TableInfo&apos;)">
</xsl:if>
</xsl:for-each>
and several variations thereof. it seems like it's somehow evaluating as a value rather than a string, because when I set it up as an != test, all the content gets deleted, and when I set it up as a not(), nothing gets deleted. any help?
thanks!
<xsl:for-each select="region[not(#class='TableInfo')]">
</xsl:for-each>
You forgot the # on class, so you were trying to check for class elements instead of the attributes. And apparently the != is not working as well, so I swapped in the not() function instead.
From a stylistic point, I would also suggest looking into using templates that match the region elements so you can use apply-templates instead of a for-each.
The identity rule is your friend (and of course, you need to specify the attribute class, not a "class" element):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*"><xsl:apply-templates/></xsl:template>
<xsl:template match="region[#class='TableInfo']"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML (fragment wrapped into a single top element to make it a well-formed XML document):
<region>
text
</region>

Truncating HTML attribute value in SharePoint DataFormWebPart

I'm using DataFormWebPart to display all announcements in the SharePoint site collections. It uses SPDataSouce with DataSourceMode set to CrossList and it works OK. The text of the announcement comes from the XML attribute:
<xsl:value-of disable-output-escaping="yes" select="#Body" />
Now I need to limit this text to, say, 250 characters. Of course, I cannot truncate it as a simple string as it could produce invalid HTML. I needed something like ddwrt:Limit but HTML aware.
Any ideas, please?
I think you want to display 250 characters in the page, please use this script
<xsl:if test="string-length(#Body) <= 250">
<xsl:value-of select="#Body"/>
</xsl:if>
<xsl:if test="string-length(#Body) > 250">
<xsl:value-of select="substring(#Body,0,250)"/>....
</xsl:if>
I found a very simple solution for this,try this instead!
<xsl:value-of select="substring(#Body, 1, 250 + string-length(substring-before(substring(#Body, 250),' ')))" />

Can an XSLT parse a string of text?

This is my first time using XSLT. I'm trying to create a file that will convert an XML data file exported from a program I use to an HTML report.
One of the element's value is path to an image file, but the path generated is an absolute path such as
C:\Documents and Settings\me\Desktop\xml export\cd000402.jpg
but I want a relative path to just the file name.
Is there some way through the XLST file to parse out the file name?
XPath contains the substring-after function which returns the string following the first occurrence of another string. This isn't enough by itself, but a template such as the following might do it:
<xsl:template name="filename-only">
<xsl:param name="path" />
<xsl:choose>
<xsl:when test="contains($path, '\')">
<xsl:call-template name="filename-only">
<xsl:with-param name="path" select="substring-after($path, '\')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$path" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The set of string functions available is not terribly extensive, but I've found that it is good enough for most applications that you'll need in XSLT.
This is a little beyond the scope of the question, but Michael Kay has an excellent paper on using XSLT 2 to parse pure text into XML.
Yes, see a general LR(1) parser implemented in XSLT 2.0. (just in 245 lines).
I have implemented with it a parser for JSON and a parser for XPath 2.0 -- entirely in XSLT.
XSLT with the help of XPath 2.0 and its various string functions helps it deal with this type of things.
Example:
Assuming the path [to jpg file] mentioned in question comes from an xml snippet similar to
...
<For_HTML>
<Image1>
<Path>C:\Documents and Settings\me\Desktop\xml export\cd000402.jpg</Path>
<Description>Photo of the parking lot</Description>
<Width>123</Width>
...
</Image1>
</For_HTML>
XSLT snippet would look something like
<xsl:template match='//For_HTML/Image1'>
<img src='http://myNewServer.com/ImageBin/{substring-after(./Path,"\xml export\")}'
alt='{./Description}'
width=' .... you got the idea'
/>
</xsl:template>
Note: didn't have time to test; but that looks about right.
It is possible. I've developed the following script:
http://barsand.wordpress.com/2012/06/19/can-an-xslt-parse-a-string-of-text/

Restrict a string to whitelisted characters using XSLT 1.0

Question
Using XSLT 1.0, given a string with arbitrary characters how can I get back a string that meets the following rules.
First character must be one of these: a-z, A-Z, colon, or underscore
All other characters must be any of those above or 0-9, period, or hyphen
If any character does not meet the above rules, replace it with an underscore
Background
In an XSLT I'm translating some attributes into elements, but I need to be sure the attribute doesn't contain any values that can't be used in an element name. I don't care much about the integrity of the attribute being converted to the name as long as it's being converted predictably. I also don't need to compensate for every valid character in an element name (there's a bunch).
The problem I was having was with the attributes having spaces coming in, which the translate function can easily convert to underscores:
translate(#name,' ','_')
But soon after I found some of the attributes using slashes, so I have to add that now too. This will quickly get out of hand. I want to be able to define a whitelist of allowed characters, and replace any non-allowed characters with an underscore, but translate works as by replacing from a blacklist.
You could write a recursive template to do this, working through the characters in the string one by one, testing them and changing them if necessary. Something like:
<xsl:template name="normalizeName">
<xsl:param name="name" />
<xsl:param name="isFirst" select="true()" />
<xsl:if test="$name != ''">
<xsl:variable name="first" select="substring($name, 1, 1)" />
<xsl:variable name="rest" select="substring($name, 2)" />
<xsl:choose>
<xsl:when test="contains('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:_', $first) or
(not($first) and contains('0123456789.-', $first))">
<xsl:value-of select="$first" />
</xsl:when>
<xsl:otherwise>
<xsl:text>_</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="normalizeName">
<xsl:with-param name="name" select="$rest" />
<xsl:with-param name="isFirst" select="false()" />
</xsl:call-template>
</xsl:if>
</xsl:template>
However, there is shorter way of doing this if you're prepared for some hackery. First declare some variables:
<xsl:variable name="underscores"
select="'_______________________________________________________'" />
<xsl:variable name="initialNameChars"
select="'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:_'" />
<xsl:variable name="nameChars"
select="concat($initialNameChars, '0123456789.-')" />
Now the technique is to take the name and identify the characters that aren't legal by replacing all the characters in the name that are legal with nothing. You can do this with the translate() function. Once you've got the set of illegal characters that appear in the string, you can replace them with underscores using the translate() function again. Here's the template:
<xsl:template name="normalizeName">
<xsl:param name="name" />
<xsl:variable name="first" select="substring($name, 1, 1)" />
<xsl:variable name="rest" select="substring($name, 2)" />
<xsl:variable name="illegalFirst"
select="translate($first, $initialNameChars, '')" />
<xsl:variable name="illegalRest"
select="translate($rest, $nameChars, '')" />
<xsl:value-of select="concat(translate($first, $illegalFirst, $underscores),
translate($rest, $illegalRest, $underscores))" />
</xsl:template>
The only thing you have to watch out for is that the string of underscores needs to be long enough to cover all the illegal characters that might appear within a single name. Making it the same length as the longest name you're likely to encounter will do the trick (though probably you could get away with it being a lot shorter).
UPDATE:
I wanted to add to this answer. In order to generate required length underscore string you can use this template.
<!--Generate string with given number of replacement-->
<xsl:template name="gen-replacement">
<xsl:param name="n"/>
<xsl:if test="$n > 0">
<xsl:call-template name="gen-replacement">
<xsl:with-param name="n" select="$n - 1"/>
</xsl:call-template>
<xsl:text>_</xsl:text>
</xsl:if>
</xsl:template>
And call it when you need to generate underscores:
<xsl:variable name="replacement"><xsl:call-template name="gen-replacement"><xsl:with-param name="n" select="string-length($value)"/></xsl:call-template></xsl:variable>
As far as Im aware XSLT 1.0 doesnt have a builtin for this. XSLT 2.0 allows you to use regexes, though Im sure your all too aware of that.
If, on the off chance your using the MS parser, you can write .NET extension libraries that you can leverage in your XSLT and I wrote about this some months ago here.
If your using something like Saxon, I am pretty certain they also provide ways of coding your own extensions, and they may indeed have an extension of their own already, but Im unfamiliar with that engine.
Hope this helps.
As another alternative there is a string function that might work for you in the XSLT standard library. http://xsltsl.sourceforge.net/string.html#template.str:string-match

Resources