SharePoint 2013 Blog: sort posts by authors - sorting

I'm trying to make a custom filter which sorts on authors (users) in the SharePoint 2013 blog. I'm working with XSL dataview and it's exported to a web part. I have a column called Author which gets the information from Created By. When I click on a user in the web part it shows all posts instead of the selected author. The URL is mysite/default.aspx?Author=FirstName LastName".
Code (default.aspx):
<xsl:template match="/" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:SharePoint="Microsoft.SharePoint.WebControls">
<xsl:call-template name="dvt_1"/>
</xsl:template>
<xsl:template name="dvt_1">
<xsl:variable name="dvt_StyleName">RepForm3</xsl:variable>
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row[not(#Author.title=preceding-sibling::Row/#Author.title)]" />
<xsl:call-template name="dvt_1.header">
<xsl:with-param name="Rows" select="$Rows" />
</xsl:call-template>
<div class="blogRefineByAuthorContainer">
<xsl:call-template name="dvt_1.body">
<xsl:with-param name="Rows" select="$Rows"/>
</xsl:call-template>
</div>
</xsl:template>
<xsl:template name="dvt_1.header">
<xsl:param name="Rows" />
<div class="blogRefineByAuthorEveryAuthor">Everyone</div>
</xsl:template>
<xsl:template name="dvt_1.body">
<xsl:param name="Rows"/>
<xsl:for-each select="$Rows">
<xsl:call-template name="dvt_1.rowview" />
</xsl:for-each>
</xsl:template>
<xsl:template name="dvt_1.rowview">
<div class="blogRefineByAuthorAuthorTitle">
<xsl:value-of select="#Author.title" />
</div>
</xsl:template>
Any ideas of what it could be? Between the first and last name there is a space in the URL, but I don't think it makes any sense. I have tried with %20 without any result.

Related

Split variables separated by a comma, and perform an action on each

I am using XSLT v1.0 and running it through Microsoft Visual Studio.
I have data (which will be different in every node) but is structured like this:
<ItemGroupData ItemGroupOID="DDG4">
<ItemDataString ItemOID="DDLOCC" AuditRecordID="AR.8452551">5,8,9,2,3</ItemDataString>
<ItemDataString ItemOID="DDLOCL" AuditRecordID="AR.8452551">1,7</ItemDataString>
<ItemDataString ItemOID="DDLOCR" AuditRecordID="AR.8452551">1</ItemDataString>
</ItemGroupData>
There can be any number of values separated by commas in each of the 3 fields.
I am trying to split the data so I can work with each individual integer, and have tried the method suggested by Dimitre Novatchev here: split function in xslt 1.0, but it keeps giving me the error:
"'template' is not a recognized extension element. An error occurred at (0,0).".
The new split/mark templates I created are inside my overall template, which is being used to convert my XML to a CSV file.
Can I have a template within a template? Or do I need to define it outside the main template? Bit of a N00b with XML so any help would be greatly appreciated.
My new templates (note the processedItem bit has been simplified for demonstration purposes):
<xsl:template match="mark">
<xsl:variable name="vrtfSplit">
<xsl:apply-templates/>
</xsl:variable>
<xsl:for-each select="ext:node-set($vrtfSplit)/*">
<processedItem>
<xsl:if test="$varLOCOID='DDLOCL'">
<xsl:value-of select="current() * 100"/>
</xsl:if>
<xsl:if test="$varLOCOID='DDLOCC'">
<xsl:value-of select="current() * 10"/>
</xsl:if>
<xsl:if test="$varLOCOID='DDLOCR'">
<xsl:value-of select="current() * 150"/>
</xsl:if>
</processedItem>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()" name="split">
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText) > 0">
<item>
<xsl:value-of select="substring-before(concat($pText, ','), ',')"/>
</item>
<xsl:call-template name="split">
<xsl:with-param name="pText" select="substring-after($pText, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Consider the following example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/ItemGroupData">
<output>
<xsl:apply-templates/>
</output>
</xsl:template>
<xsl:template match="ItemDataString">
<items>
<xsl:call-template name="tokenize-and-process">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="factor">
<xsl:choose>
<xsl:when test="#ItemOID='DDLOCL'">100</xsl:when>
<xsl:when test="#ItemOID='DDLOCC'">10</xsl:when>
<xsl:when test="#ItemOID='DDLOCR'">150</xsl:when>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</items>
</xsl:template>
<xsl:template name="tokenize-and-process">
<xsl:param name="text"/>
<xsl:param name="factor" select="1"/>
<xsl:param name="delimiter" select="','"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<item>
<xsl:value-of select="$token * $factor"/>
</item>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize-and-process">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
<xsl:with-param name="factor" select="$factor"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Applied to your example input XML, the result will be:
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<items>
<item>50</item>
<item>80</item>
<item>90</item>
<item>20</item>
<item>30</item>
</items>
<items>
<item>100</item>
<item>700</item>
</items>
<items>
<item>150</item>
</items>
</output>
P.S. No, a template cannot be a child of another template.

XSLT IF evaluation rules

From the XML file :
<store >
<tools>
<tool IDT="T1">
<container>B1</container>
<container>B2</container>
</tool>
<tool IDT="T2">
<container>B1</container>
</tool>
<tool IDT="T3">
<container>B2</container>
</tool>
</tools>
<boxes>
<box IDB="B1" height="10" width="20" length="30" weight="4"/>
<box IDB="B2" height="5" width="40" length="30" weight="2"/>
</boxes>
</store>
I try to display for each box the list of tools that go into each box. For that, I wrote the following XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output
method="html"
encoding="UTF-8"
doctype-public="-//W3C//DTD HTML 4.01//EN"
doctype-system="http://www.w3.org/TR/html4/strict.dtd"
indent="yes" />
<xsl:template match="/">
<html>
<head>
<title>Boxes contents</title>
<link type="text/css" rel="stylesheet" href="styles.css" />
</head>
<body>
<h1>Boxes contents</h1>
<ul>
<xsl:apply-templates select="/store/boxes/box" />
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="box" >
<li><xsl:text>Box </xsl:text>
<xsl:value-of select="#ID"/>
<xsl:text>contains the following tools : </xsl:text>
</li>
<xsl:call-template name="findTools" >
<xsl:with-param name="currentBOX" select="#IDB"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="findTools" >
<xsl:param name="currentBOX" />
<xsl:for-each select="/store/tools/tool/container" >
<xsl:if test="container = $currentBOX" >
<br><xsl:value-of select="#IDT"/></br>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When I do it, I never see the tools. In debug under OXYGEN, I see that the IF is never true. I do not understand why? I start in XPath and XSLT, thanks for your help
You already are at a <container> element inside the <xsl:for-each>. There are no children, so selecting another <container> inside the <xsl:if> won't return anything.
You mean to execute your check from the <tool> node.
<xsl:for-each select="/store/tools/tool">
<xsl:if test="container = $currentBOX">
<xsl:value-of select="#IDT"/><br />
</xsl:if>
</xsl:for-each>
which is easier written as
<xsl:for-each select="/store/tools/tool[container = $currentBOX]">
<xsl:value-of select="#IDT"/><br />
</xsl:for-each>
Overall a more straight-forward way to write the two templates would be this:
<xsl:template match="box">
<li>
<xsl:text>Box </xsl:text>
<xsl:value-of select="#ID"/>
<xsl:text>contains the following tools : </xsl:text>
</li>
<xsl:apply-templates select="/store/tools/tool[container = current()/#IDB]" />
</xsl:template>
<xsl:template match="tool">
<xsl:value-of select="#IDT"/><br />
</xsl:template>
And alternatively you can use an <xsl:key> to index <tool> elements by their <container> value:
<xsl:key name="kToolByContainer" match="/store/tools/tool" use="container" />
<xsl:template match="box">
<li>
<xsl:text>Box </xsl:text>
<xsl:value-of select="#ID"/>
<xsl:text>contains the following tools : </xsl:text>
</li>
<xsl:apply-templates select="key('kToolByContainer', #IDB)" />
</xsl:template>
<xsl:template match="tool">
<xsl:value-of select="#IDT"/><br />
</xsl:template>

How can be get CDATA data by xslt?

I am able to parse xml to json by
http://www.armbruster-it.org/index.php/12-it/pl-sql/12-oracle-xml-and-json-goodies
But when any CDATA come it fail . Can you tell me the rule of xslt so that i can copy all data that is written in CDATA as it is in json .
> select
> packagename.itstar_xml_util.xml2json(xmltype('<ArticleDetail><![CDATA[<P
> class=MsoNormal style="MARGIN: 0in 0in 0pt"></P>]]></ArticleDetail>'))
> FROM DUAL;
This is data
<ArticleDetail><![CDATA[<P class=MsoNormal style="MARGIN: 0in 0in 0pt"></P>]]></ArticleDetail>
The xslt is
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="no" omit-xml-declaration="yes" method="text" encoding="UTF-8" media-type="text/x-json"/>
<xsl:strip-space elements="*"/>
<!--contant-->
<xsl:variable name="d">0123456789</xsl:variable>
<!-- ignore document text -->
<xsl:template match="text()[preceding-sibling::node() or following-sibling::node()]"/>
<!-- string -->
<xsl:template match="text()">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="."/>
</xsl:call-template>
</xsl:template>
<!-- Main template for escaping strings; used by above template and for object-properties
Responsibilities: placed quotes around string, and chain up to next filter, escape-bs-string -->
<xsl:template name="escape-string">
<xsl:param name="s"/>
<xsl:text>"</xsl:text>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
<xsl:text>"</xsl:text>
</xsl:template>
<!-- Escape the backslash (\) before everything else. -->
<xsl:template name="escape-bs-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,''\'')">
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="concat(substring-before($s,''\''),''\\'')"/>
</xsl:call-template>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="substring-after($s,''\'')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Escape the double quote ("). -->
<xsl:template name="escape-quot-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,'';'')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'';''),''"'')"/>
</xsl:call-template>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="substring-after($s,'';'')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Replace tab, line feed and/or carriage return by its matching escape code. Can''t escape backslash
or double quote here, because they don''t replace characters (; becomes \t), but they prefix
characters (\ becomes \\). Besides, backslash should be seperate anyway, because it should be
processed first. This function can''t do that. -->
<xsl:template name="encode-string">
<xsl:param name="s"/>
<xsl:choose>
<!-- tab -->
<xsl:when test="contains($s,'';'')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'';''),''\t'',substring-after($s,'';''))"/>
</xsl:call-template>
</xsl:when>
<!-- line feed -->
<xsl:when test="contains($s,'';'')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'';''),''\n'',substring-after($s,'';''))"/>
</xsl:call-template>
</xsl:when>
<!-- carriage return -->
<xsl:when test="contains($s,'';'')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'';''),''\r'',substring-after($s,'';''))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$s"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- number (no support for javascript mantise) -->
<xsl:template match="text()[not(string(number())=''NaN'')]">
<xsl:value-of select="."/>
</xsl:template>
<!-- boolean, case-insensitive -->
<xsl:template match="text()[translate(.,''TRUE'',''true'')=''true'']">true</xsl:template>
<xsl:template match="text()[translate(.,''FALSE'',''false'')=''false'']">false</xsl:template>
<!-- item:null -->
<xsl:template match="*[count(child::node())=0]">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="local-name()"/>
</xsl:call-template>
<xsl:text>:null</xsl:text>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">}</xsl:if> <!-- MBR 30.01.2010: added this line as it appeared to be missing from stylesheet -->
</xsl:template>
<!-- object -->
<xsl:template match="*" name="base">
<xsl:if test="not(preceding-sibling::*)">{</xsl:if>
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="name()"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<xsl:apply-templates select="child::node()"/>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">}</xsl:if>
</xsl:template>
<!-- array -->
<xsl:template match="*[count(../*[name(../*)=name(.)])=count(../*) and count(../*)>1]">
<xsl:if test="not(preceding-sibling::*)">[</xsl:if>
<xsl:choose>
<xsl:when test="not(child::node())">
<xsl:text>null</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="child::node()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">]</xsl:if>
</xsl:template>
<!-- convert root element to an anonymous container -->
<xsl:template match="/">
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>

Sales Order Taxes For Dynamics GP

In Microsoft Dynamics GP I need to set the tax on individual orders as taxable/non-taxable (cannot be done on customer record).
For some reason no matter what I pass into the CreateSalesOrder call for the web services, it will not save the tax information.
I've tried:
Using the CreateSalesInvoice policy which has had "Create Taxes Behavior" set to "Taxes will be provided"
Overrode policies using "Use Header Level Taxes Behavior" and "Create Taxes Behavior" all 4 modes have been tried.
Provided taxes as a total on the Sales Order
Provided taxes as a tax detail on the sales order
Provided taxes as a total on the line item
Provided taxes as a tax detail on the sales order
Set the TaxScheduleKey on the order
Set the TaxScheduleKey on the line items
Taxes just get blanked out in GP, it's infuriating... Any ideas? I do have access to the Dynamics database but I'd really rather not go that route if possible.
My research has led me to believe that this is broken (every thread on this subject ends with no answers) and Microsoft isn't going to fix it, but that really hurts automation something awful.
Ran into this issue today. A little background:
After trying everything outlined above, modifying policies - particularly, Sales Document - Create Sales Document policies, and becoming frustrated at there being no policies for Create Sales Order to allow taxes to be specified, I came across this MSDN article about how the GP Services could be designed or extended: https://msdn.microsoft.com/en-us/library/dd996499.aspx
Not interested in going quite that far, I found - "Program Files\Microsoft Dynamics\GPWebServices\XSLT", in particular, the "Microsoft.Dynamics.GP.SalesOrderCreate.xslt" file.
As it comes installed with GreatPlains, there are no transforms in place to utilize incoming tax data at all (no surprise, given what had been tried). USINGHEADERLEVELTAXES is set to 0.
Combining the Tax lines from the "Microsoft.Dynamics.GP.SalesInvoiceCreate.xslt" file into the SalesOrderCreate.xslt file, you can modify the XML sent into eConnect for SalesOrderCreate to properly create the Taxes.
The below example uses/was tested for header level tax behavior.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mbs="http://schemas.microsoft.com/dynamics/2006/01"
xmlns:gputil="urn:Microsoft.Dynamics.GP.TransformUtilities"
version="1.0">
<xsl:import href="Microsoft.Dynamics.GP.SalesCreateUpdateLibrary.xslt"/>
<xsl:import href="Microsoft.Dynamics.GP.StandardLibrary.xslt"/>
<xsl:variable name="CompanyId">
<xsl:value-of select="/SalesOrder/mbs:Context/mbs:OrganizationKey/mbs:Id"/>
</xsl:variable>
<xsl:template match ="/">
<eConnect xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:apply-templates />
</eConnect>
</xsl:template>
<xsl:template match="SalesOrder">
<SOPTransactionType>
<xsl:apply-templates select="Lines/SalesOrderLine/Serials/SalesLineSerial">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="Lines/SalesOrderLine/Components/SalesOrderComponent/Serials/SalesComponentSerial">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="Lines/SalesOrderLine/Lots/SalesLineLot">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="Lines/SalesOrderLine/Components/SalesOrderComponent/Lots/SalesComponentLot">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="Lines/SalesOrderLine" />
<xsl:apply-templates select="Lines/SalesOrderLine/Components/SalesOrderComponent" />
<xsl:apply-templates select="TrackingNumbers/SalesTrackingNumber" />
<xsl:apply-templates select="Taxes/SalesDocumentTax" />
<xsl:apply-templates select="Commissions/SalesCommission" />
<xsl:apply-templates select="Payments/SalesPayment">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="UserDefined" />
<xsl:apply-templates select="Lines/SalesOrderLine/Bins/SalesLineBin" />
<xsl:apply-templates select="Lines/SalesOrderLine/Components/SalesOrderComponent/Bins/SalesComponentBin" />
<xsl:call-template name="SalesHeader" />
<xsl:apply-templates select="ProcessHolds/SalesProcessHold">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
</xsl:apply-templates>
</SOPTransactionType>
</xsl:template>
<xsl:template match="SalesOrderLine">
<taSopLineIvcInsert>
<xsl:call-template name="CreateUpdateLine">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
<xsl:with-param name="CompanyId">
<xsl:value-of select="$CompanyId" />
</xsl:with-param>
</xsl:call-template>
<xsl:if test="QuantityToBackorder/Value != ''">
<QTYTBAOR>
<xsl:value-of select="QuantityToBackorder/Value" />
</QTYTBAOR>
</xsl:if>
<xsl:if test="QuantityToInvoice/Value != ''">
<QUANTITY>
<xsl:value-of select="QuantityToInvoice/Value" />
</QUANTITY>
</xsl:if>
<xsl:if test="QuantityCanceled/Value != ''">
<QTYCANCE>
<xsl:value-of select="QuantityCanceled/Value" />
</QTYCANCE>
</xsl:if>
<xsl:if test="QuantityFulfilled/Value != ''">
<QTYFULFI>
<xsl:value-of select="QuantityFulfilled/Value" />
</QTYFULFI>
</xsl:if>
<xsl:if test="TaxAmount/Value != ''">
<TAXAMNT>
<xsl:value-of select="TaxAmount/Value" />
</TAXAMNT>
</xsl:if>
<RecreateDist>0</RecreateDist>
</taSopLineIvcInsert>
</xsl:template>
<xsl:template match="SalesOrderComponent">
<taSopLineIvcInsertComponent>
<xsl:call-template name="CreateUpdateComponent">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
</xsl:call-template>
<xsl:if test="QuantityToBackorder/Value != ''">
<QTYTBAOR>
<xsl:value-of select="QuantityToBackorder/Value" />
</QTYTBAOR>
</xsl:if>
<xsl:if test="QuantityToInvoice/Value != ''">
<QUANTITY>
<xsl:value-of select="QuantityToInvoice/Value" />
</QUANTITY>
</xsl:if>
<xsl:if test="QuantityCanceled/Value != ''">
<QTYCANCE>
<xsl:value-of select="QuantityCanceled/Value" />
</QTYCANCE>
</xsl:if>
<xsl:if test="QuantityFulfilled/Value != ''">
<QTYFULFI>
<xsl:value-of select="QuantityFulfilled/Value" />
</QTYFULFI>
</xsl:if>
</taSopLineIvcInsertComponent>
</xsl:template>
<xsl:template name="SalesHeader">
<taSopHdrIvcInsert>
<xsl:call-template name="CreateUpdateDocument">
<xsl:with-param name="UpdateIfExists">0</xsl:with-param>
<xsl:with-param name="CompanyId">
<xsl:value-of select="$CompanyId" />
</xsl:with-param>
</xsl:call-template>
<xsl:if test="PaymentAmount/Value != ''">
<PYMTRCVD>
<xsl:value-of select="PaymentAmount/Value" />
</PYMTRCVD>
</xsl:if>
<xsl:if test="TaxAmount/Value != ''">
<TAXAMNT>
<xsl:value-of select="TaxAmount/Value" />
</TAXAMNT>
</xsl:if>
<USINGHEADERLEVELTAXES>1</USINGHEADERLEVELTAXES>
<CREATEDIST>0</CREATEDIST>
<CREATETAXES>0</CREATETAXES>
</taSopHdrIvcInsert>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 How to get distinct values

I had an XML somewhat similar to what is shown below. I had to find the unique categories. There are lot of easy ways in XSLT 2.0. But I had to stick to 1.0 :( . After several struggle I found the solution. I thought of sharing. Might help somebody. Please improve my answer. I appreciate.
<root>
<category>
this is Games category
</category>
<category>
this is Books category
</category>
<category>
this is Food category
</category>
<category>
this is Games category
</category>
<category>
this is Books category
</category>
<category>
this is Food category
</category>
<category>
this is Travel category
</category>
<category>
this is Travel category
</category>
</root>
Solution. I have added in the answer section. Thanks.
Solution
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="test">
<xsl:call-template name="delimitedvalues">
<xsl:with-param name="paramvalues" select="//category" />
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="distinctvalues">
<xsl:with-param name="values" select="$test" />
</xsl:call-template>
</xsl:template>
<xsl:template name="distinctvalues">
<xsl:param name="values"/>
<xsl:variable name="firstvalue" select="substring-before($values, ',')"/>
<xsl:variable name="restofvalue" select="substring-after($values, ',')"/>
<xsl:if test="contains($values, ',') = false">
<xsl:value-of select="$values"/>
</xsl:if>
<xsl:if test="contains($restofvalue, $firstvalue) = false">
<xsl:value-of select="$firstvalue"/>
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:if test="$restofvalue != ''">
<xsl:call-template name="distinctvalues">
<xsl:with-param name="values" select="$restofvalue" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="delimitedvalues">
<xsl:param name="paramvalues" />
<xsl:value-of select="substring-before(substring-after($paramvalues,'this is '),' category')"/>
<xsl:if test="$paramvalues/following::category">
<xsl:text>,</xsl:text>
<xsl:call-template name="delimitedvalues">
<xsl:with-param name="paramvalues" select="$paramvalues/following::category" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output
Games,Books,Food,Travel
Source Code
http://www.xsltcake.com/slices/0iWpyI

Resources