I'm trying to fetch all of my news item in XSLT so that only the ones that have the same Interest of my Page are shown on my page. I don't know how I can do this in XSLT. If it was a one to one or one to many I could use Contains() but since a Page object can have one or more field of Interests and my News object too, I can't find a good way to filter my data.
Here's my XML(simplified for the exemple) I have the page field of Interest in the top and all my News items.
interests :
<values>
<value>1169</value>
<value>1174</value>
</values>
<node id="1075" version="b2c2899a-548d-4389-9ea4-12bff6ecb939" parentID="1074" level="4" writerID="0" creatorID="0" nodeType="1071" template="1065" sortOrder="1" createDate="2010-07-06T23:06:57" updateDate="2010-07-12T10:18:17" nodeName="Nouveau site Web" urlName="nouveau-site-web" writerName="Administrator" creatorName="Administrator" nodeTypeAlias="Nouvelle_Publication" path="-1,1057,1058,1074,1075">
<data alias="Title">Nouveau site Internet</data>
<data alias="Interest">1169,1171</data>
<data alias="Resume">Le résumé de la nouvelle.</data>
</node>
<node id="1156" version="68e80332-c9fb-482f-bf03-78a06579f893" parentID="1074" level="4" writerID="0" creatorID="0" nodeType="1071" template="1065" sortOrder="2" createDate="2010-07-08T13:59:39" updateDate="2010-07-09T10:45:42" nodeName="Nouvelle 2" urlName="nouvelle-2" writerName="Administrator" creatorName="Administrator" nodeTypeAlias="Nouvelle_Publication" path="-1,1057,1058,1074,1156">
<data alias="Title">La nouvelle de siecle</data>
<data alias="Interest">1169,1164</data>
<data alias="Resume"></data>
</node>
<node id="1198" version="21741a44-dfc7-4641-b9d2-3317374f141f" parentID="1074" level="4" writerID="0" creatorID="0" nodeType="1071" template="1065" sortOrder="3" createDate="2010-07-12T10:10:49" updateDate="2010-07-12T10:12:14" nodeName="La liste de nouvelles" urlName="la-liste-de-nouvelles" writerName="Administrator" creatorName="Administrator" nodeTypeAlias="Nouvelle_Publication" path="-1,1057,1058,1074,1198">
<data alias="Title">La liste de nouvelles fonctionnent</data>
<data alias="Interest">1171</data>
<data alias="Resume">Voici le résumé de la nouvelle</data>
</node>
<node id="1199" version="33f47410-8948-4aa2-b5ed-6c29d5626c30" parentID="1074" level="4" writerID="0" creatorID="0" nodeType="1071" template="1065" sortOrder="4" createDate="2010-07-12T10:12:35" updateDate="2010-07-12T10:15:37" nodeName="Nouvelle externe" urlName="nouvelle-externe" writerName="Administrator" creatorName="Administrator" nodeTypeAlias="Nouvelle_Publication" path="-1,1057,1058,1074,1199">
<data alias="Title">Un exemple de nouvelle externe</data>
<data alias="Interest">1171</data>
<data alias="Resume">Le site de St-Augustin fait peau neuve...</data>
</node>
Should I just do a UserControl in .Net? I never done à UC that fetch data from Umbraco using the API, is there any good exemple/documentation on the API?
The problem with the if inside my for-each is that I have paging involve on my select so I need to fetch only the needed data so that my paging works. Also, usign the contains wont works because I have an array of field of interest that filter my data source that also have an array of field of interest but I want to have my data if it contains any of the field of interest that filter the data, not all.
Ok I understand the first part of your problem and that is solvable but you will have to go around the houses to do it.
I've done complicated filtering/sorting/paging of XML before using the following solution.
Loop over all your nodes (narrowed down with a good xpath in you for-loop). Create a temp XML string in a XSL variable which contains all the nodes you are interested in. Convert this variable to a node set and store the result in another variable this is the one you will be using for your paging as it should contain only the nodes that are valid. Then sort these nodes when you do your paging. Phew. As usual with XSL it is all about breaking the problem down into more managable chunks and then working with them to build up a nice solution.
<xsl:variable name="TempHotels">
<!-- Render out just XML as a string into this variable, nice and easy to build up -->
<Hotels>
<xsl:for-each select="/hotels/hotel[@BoardBasis = 'Half Board']" />
<xsl:variable name="IsFeaturedHotel">
<!-- do some nasty logic here -->
</xsl:variable>
<Hotel id="@id" name="@name" isFeaturedHotel="{$IsFeaturedHotel}" rating="@starrating" />
</xsl:for-each>
</Hotels>
</xsl:for-each>
<!-- convert our XML string to a usable XML node set -->
<xsl:variable name="Hotels" select="msxml:node-set( $TempHotels )" />
<ul>
<xsl:for-each select="$Hotels/Hotel">
<xsl:sort select="@isFeaturedHotel" />
<xsl:sort select="@rating" />
<xsl:sort select="@name" />
<xsl:if test="position() < $PagingMin and position > $PagingMax">
<!-- Go get all the hotel data for this hotel id -->
<xsl:variable name="Hotel" select="umbraco.library:GetNodeById( @id )" />
<!-- just some demo content -->
<li><xsl:value-of select="$Hotel/@name" /> is rated as <xsl:value-of select="$Hotel/@rating" /> Star</li>
</xsl:if>
</xsl:for-each>
</ul>
Hopefully that gives you the idea and is helpful to your first problem. As for the second can you give us an example? Sorting using contains would only give you nodes that have at least one of the same interests at the top of the pile. Again you could do that sort of thing first to build up your TempNodes variable which you can then use in your "real" for-loop which will do the actual rendering.
Thank you for the great reply! I would like to reward you but I don't have enough karma for now...
I like the idea of looping once in my nodes to create a second node. Your exemple solve my first problem but now I don't understand why sorting (can we sort on a Array or a list in a string) on my News'interest would help me with the filtering. I have my News item witch have a field of interest that can be for exemple "1120, 1175, 1179" and my page field of interest could be something like "1135, 1175" they are both strings. How can I verify that my nes item contains at least one of the node Id(witch represent field of interest) of my current selected page's field of interest?
Also, I saw that, as a best practice, I should use a .Net WebControl when my XSLT gets too complicated, what are youre taughts on that? Do you think that it would be easier to achieve my goals by doing so?
Here is my XML when I do <xsl:copy-of select="$newsItems"/>
<newslist>
<news id="1075" Title="Nouveau site Internet" Resume="Le résumé de la nouvelle." DateParution="2010-07-14T00:00:00" Source="Interne" URLExterne="" />
<news id="1156" Title="La nouvelle de siecle" Resume="" DateParution="" Source="" URLExterne="" />
<news id="1198" Title="La liste de nouvelles fonctionnent" Resume="Voici le résumé de la nouvelle" DateParution="2010-07-12T00:00:00" Source="Interne" URLExterne="" />
<news id="1199" Title="Un exemple de nouvelle externe" Resume="Le site de St-Augustin fait peau neuve..." DateParution="2010-07-09T00:00:00" Source="Externe" URLExterne="http://www.spektrummedia.com/fr/nouvelles/le-site-de-la-ville-de-saint-augustin-de-desmaures-fait-peau-neuve.html" />
</newslist>
Hope this can help anyone else, now I just have to find how to test my field of interest.
Ideally yes I'm a fan of trying to keep complicated logic out of your XSL and use it just for doing what it does well, rendering out content. But there are occassions when you need to stretch XSL a little. You could happily do this in .net and that might be an option especially with the second part of your problem. Trouble is I also am a fan of XSL so I do like to try to solve problem in it ;)
Again there are ways around it. In our temp xml string variable we can add in what ever we like and in my example I have a comment for the logic for the IsFeaturedHotel. This logic could easily be a bit of comparision between the split strings. And again you can do the that part of the logic in another variable if it makes it easier. So following on from my example, I could build up TempHotelsByBoardBasis then follow what with another variable that uses the first as its source to then get HotelsRelatedToUsersChoice which then goes on to feed my paging for-each loop.
1. Do you know where I can find an exemple of a full UC in c# that acces umbraco data using API? I'm new to Umbraco and XSL but not C#... I like it so far but I need to get my stuff working faster ;) I want to compare both solutions and be able to know when to use each one.
but I still don't know how to filter my news items... Is it possible to do a for-each inside a for-each? You can see in my code up there that I put a comment where I think my filter's logic should go, can I do something like this (it's not working right now) :
<!-- Add logic for field of interest -->
<xsl:for-each select="$pageInterests//value">
<xsl:if test="contains( data[@alias='Interest'], current())">
<!-- But I can de add only once ?! -->
<news id="{@id}" Title="{data[@alias='Title']}" Resume="{data[@alias='Resume']}" DateParution="{data[@alias='DateParution']}" Source="{data[@alias='Source']}" URLExterne="{data[@alias='URLExterne']}" />
</xsl:if>
</xsl:for-each>
And thanks a lot again for the help! I appreciate it a lot :)
But what I get is that it's always false and it does not fetch the right field for my news item, don't now why, the field of interest of my page is always the same(cause I'm in the same page) but the ones of my news should change from one to an other..
Page's interests :11691171 News's interests :11691171 Has same interest : false Page's interests :11691171 News's interests :11691171 Has same interest : false Page's interests :11691171 News's interests :11691171 Has same interest : false Page's interests :11691171 News's interests :11691171 Has same interest : false
I switched the HasSameInterest from beeing a property to be part of the test in my if since I don't want them in my list for my paging but the problem is that it's always false... and I'm not good at all in XPath and XSL so I don't understand very well youre query, I get the point but that's it.
So if you are able to use the Split method to get nodesets for your newsitem and your content page, then you should be able to just compare them with a simple "="
Does the "=" will verify if all the value are equal or only one can be in the result? Because right now it does not return any News item... What I want to verify that at least one or more of my value is in the other list of value.
Still not working but it's good to know that the equal will compare as any :)
If I try to simply do this : <xsl:copy-of select="$pageInterests/values/value" /> I get nothing in my viewsources but if I do : <xsl:copy-of select="$pageInterests" /> I get the following : <values><value>1169</value><value>1171</value></values>
What am I missing?
Also (maybe it could help) if I do this : <xsl:copy-of select="$currentPage/ancestor-or-self::root//node [@nodeTypeAlias='Nouvelle_Publication' and umbraco.library:Split(data [@alias = 'Interest'], ',')]" /> I get the following :
I figure it out for the <xsl:copy-of select="$pageInterests/values/value" /> I have to do it without the <values> node but I'd like to know why? If I do <xsl:copy-of select="$pageInterests/value" /> it works and return <value>1169</value><value>1171</value>
But I think it compare with all the nodes and not only the one that I'm working it since I get all my nodes even if they are not suppose to be there.
One more thing, since it's not a good practice for speed to use // in my selector as in this : <xsl:variablename="items"select="$currentPage/ancestor-or-self::root//node [@nodeTypeAlias='Nouvelle_Publication' and $pageInterests/value = umbraco.library:Split(data [@alias = 'Interest'], ',')/value] "/>
How can I change $currentPage/ancestor-or-self::root//node for something better?
From the tree you are describing I am not sure if Home = Content, or if Home is a node of its own, so you would just need to set the @level part to the level that your "Fr" node is on.
Filter on an array
Hi,
I'm trying to fetch all of my news item in XSLT so that only the ones that have the same Interest of my Page are shown on my page. I don't know how I can do this in XSLT. If it was a one to one or one to many I could use Contains() but since a Page object can have one or more field of Interests and my News object too, I can't find a good way to filter my data.
Here's my XML(simplified for the exemple) I have the page field of Interest in the top and all my News items.
As for my XSLT for now it looks like this :
Thanks a lot for the help!
I found that : fn:one-or-more(item,item,...) on this page http://w3schools.com/xpath/xpath_functions.asp but I'm not sure if it can be use in Umbraco.
Should I just do a UserControl in .Net? I never done à UC that fetch data from Umbraco using the API, is there any good exemple/documentation on the API?
Thanks!
Could you not add in an additional sort:
That should give you a sorted list of nodes with matching Interests at the top. Then you can have a xsl:if to only render out the ones set to true?
Alternatively you could put it in your xpath for the for-loop
Hi Peter,
The problem with the if inside my for-each is that I have paging involve on my select so I need to fetch only the needed data so that my paging works. Also, usign the contains wont works because I have an array of field of interest that filter my data source that also have an array of field of interest but I want to have my data if it contains any of the field of interest that filter the data, not all.
Thank you for the reply!
Ok I understand the first part of your problem and that is solvable but you will have to go around the houses to do it.
I've done complicated filtering/sorting/paging of XML before using the following solution.
Loop over all your nodes (narrowed down with a good xpath in you for-loop). Create a temp XML string in a XSL variable which contains all the nodes you are interested in. Convert this variable to a node set and store the result in another variable this is the one you will be using for your paging as it should contain only the nodes that are valid. Then sort these nodes when you do your paging. Phew. As usual with XSL it is all about breaking the problem down into more managable chunks and then working with them to build up a nice solution.
<xsl:variable name="TempHotels"> <!-- Render out just XML as a string into this variable, nice and easy to build up --> <Hotels> <xsl:for-each select="/hotels/hotel[@BoardBasis = 'Half Board']" /> <xsl:variable name="IsFeaturedHotel"> <!-- do some nasty logic here --> </xsl:variable> <Hotel id="@id" name="@name" isFeaturedHotel="{$IsFeaturedHotel}" rating="@starrating" /> </xsl:for-each> </Hotels> </xsl:for-each> <!-- convert our XML string to a usable XML node set --> <xsl:variable name="Hotels" select="msxml:node-set( $TempHotels )" /> <ul> <xsl:for-each select="$Hotels/Hotel"> <xsl:sort select="@isFeaturedHotel" /> <xsl:sort select="@rating" /> <xsl:sort select="@name" /> <xsl:if test="position() < $PagingMin and position > $PagingMax"> <!-- Go get all the hotel data for this hotel id --> <xsl:variable name="Hotel" select="umbraco.library:GetNodeById( @id )" /> <!-- just some demo content --> <li><xsl:value-of select="$Hotel/@name" /> is rated as <xsl:value-of select="$Hotel/@rating" /> Star</li> </xsl:if> </xsl:for-each> </ul>
Hopefully that gives you the idea and is helpful to your first problem. As for the second can you give us an example? Sorting using contains would only give you nodes that have at least one of the same interests at the top of the pile. Again you could do that sort of thing first to build up your TempNodes variable which you can then use in your "real" for-loop which will do the actual rendering.
Pete
Hi Pete,
Thank you for the great reply! I would like to reward you but I don't have enough karma for now...
I like the idea of looping once in my nodes to create a second node. Your exemple solve my first problem but now I don't understand why sorting (can we sort on a Array or a list in a string) on my News'interest would help me with the filtering. I have my News item witch have a field of interest that can be for exemple "1120, 1175, 1179" and my page field of interest could be something like "1135, 1175" they are both strings. How can I verify that my nes item contains at least one of the node Id(witch represent field of interest) of my current selected page's field of interest?
Also, I saw that, as a best practice, I should use a .Net WebControl when my XSLT gets too complicated, what are youre taughts on that? Do you think that it would be easier to achieve my goals by doing so?
Thanks again!
Ok everything works for now except my filter on my field of interest, here's my code since it's not exactly the same as youre's.
XSLT :
Here is my XML when I do <xsl:copy-of select="$newsItems"/>
Hope this can help anyone else, now I just have to find how to test my field of interest.
Ideally yes I'm a fan of trying to keep complicated logic out of your XSL and use it just for doing what it does well, rendering out content. But there are occassions when you need to stretch XSL a little. You could happily do this in .net and that might be an option especially with the second part of your problem. Trouble is I also am a fan of XSL so I do like to try to solve problem in it ;)
I see the trouble with the second part of your problem now. This should come to your rescue our.umbraco.org/wiki/reference/umbracolibrary/split
Again there are ways around it. In our temp xml string variable we can add in what ever we like and in my example I have a comment for the logic for the IsFeaturedHotel. This logic could easily be a bit of comparision between the split strings. And again you can do the that part of the logic in another variable if it makes it easier. So following on from my example, I could build up TempHotelsByBoardBasis then follow what with another variable that uses the first as its source to then get HotelsRelatedToUsersChoice which then goes on to feed my paging for-each loop.
Does that help any?
Good stuff btw, you beat my post by a minute :)
1. Do you know where I can find an exemple of a full UC in c# that acces umbraco data using API? I'm new to Umbraco and XSL but not C#... I like it so far but I need to get my stuff working faster ;) I want to compare both solutions and be able to know when to use each one.
2. I'm already using the split function
but I still don't know how to filter my news items... Is it possible to do a for-each inside a for-each? You can see in my code up there that I put a comment where I think my filter's logic should go, can I do something like this (it's not working right now) :
<!-- Add logic for field of interest --> <xsl:for-each select="$pageInterests//value"> <xsl:if test="contains( data[@alias='Interest'], current())"> <!-- But I can de add only once ?! --> <news id="{@id}" Title="{data[@alias='Title']}" Resume="{data[@alias='Resume']}" DateParution="{data[@alias='DateParution']}" Source="{data[@alias='Source']}" URLExterne="{data[@alias='URLExterne']}" /> </xsl:if> </xsl:for-each>
And thanks a lot again for the help! I appreciate it a lot :)
How about something like this?
I understand what you try to do but it's not working...
I did this to help me "debug" :
But what I get is that it's always false and it does not fetch the right field for my news item, don't now why, the field of interest of my page is always the same(cause I'm in the same page) but the ones of my news should change from one to an other..
Page's interests : 11691171
News's interests : 11691171
Has same interest : false
Page's interests : 11691171
News's interests : 11691171
Has same interest : false
Page's interests : 11691171
News's interests : 11691171
Has same interest : false
Page's interests : 11691171
News's interests : 11691171
Has same interest : false
I switched the HasSameInterest from beeing a property to be part of the test in my if since I don't want them in my list for my paging but the problem is that it's always false... and I'm not good at all in XPath and XSL so I don't understand very well youre query, I get the point but that's it.
No response for my first question (no 1)?
Thx!
Xslt can actually compare nodeset values for you. Sample:
So if you are able to use the Split method to get nodesets for your newsitem and your content page, then you should be able to just compare them with a simple "="
So if we put this into you first post, it should look something like this (not tested):
Hi Morten,
Does the "=" will verify if all the value are equal or only one can be in the result? Because right now it does not return any News item... What I want to verify that at least one or more of my value is in the other list of value.
Thanks for the help!
It will return true if any of the values on the left matches any of the values on the right.
I think I made a mistake in the code. Try this instead:
Still not working but it's good to know that the equal will compare as any :)
If I try to simply do this : <xsl:copy-of select="$pageInterests/values/value" /> I get nothing in my viewsources but if I do : <xsl:copy-of select="$pageInterests" /> I get the following : <values><value>1169</value><value>1171</value></values>
What am I missing?
Also (maybe it could help) if I do this : <xsl:copy-of select="$currentPage/ancestor-or-self::root//node [@nodeTypeAlias='Nouvelle_Publication' and umbraco.library:Split(data [@alias = 'Interest'], ',')]" /> I get the following :
Thank you so much guys, once I'll have my model workings I will be able to build other by myself, at leat I hope so ;)
I figure it out for the <xsl:copy-of select="$pageInterests/values/value" /> I have to do it without the <values> node but I'd like to know why? If I do <xsl:copy-of select="$pageInterests/value" /> it works and return <value>1169</value><value>1171</value>
But I think it compare with all the nodes and not only the one that I'm working it since I get all my nodes even if they are not suppose to be there.
Here's the code as for now :
Oh YEAH! it works! Probably that someone else has change my field of interest in my pages.
Thanks for the help guys! I still need a lot of practice in XSL but I learned a lot with youre help!
Ahh, good. I was wrecking my brain to find the error :)
Peter's solution also works if we change the select for the new one :
One more thing, since it's not a good practice for speed to use // in my selector as in this : <xsl:variable name="items" select="$currentPage/ancestor-or-self::root//node [@nodeTypeAlias='Nouvelle_Publication' and $pageInterests/value = umbraco.library:Split(data [@alias = 'Interest'], ',')/value] "/>
How can I change $currentPage/ancestor-or-self::root//node for something better?
My tree looks like this
Home
-->Fr
-->Page 1
-->Page 2
-->NewsList (witch have his own DocumentType)
--> News 1 (documenType : NewsItem)
--> News 2
And so it goes on...
Thanks again!
It looks like you only want to get the newsitems for the language that you are currently in, so you probably don't want to go all the way to the root.
Try something like this:
$currentPage/ancestor-or-self::node[@level = 1]/descendant::node [@nodeTypeAlias.....
From the tree you are describing I am not sure if Home = Content, or if Home is a node of its own, so you would just need to set the @level part to the level that your "Fr" node is on.
Thanks Morten! Here's my final select :
I have an other question on this XSLT problem, what if I want to verify that Categorie is equal to $category and not that it only contains it?
I try with this but it's not Saving in Umbraco :
Thanks again!
EDITED :
The problem seems to be with my if in my variable because the select on his own is working...
Here's how I got it working :
Thanks to this post : http://our.umbraco.org/forum/developers/extending-umbraco/4139-To-use-a-result-tree-fragment-in-a-path-expression,-first-convert-it-to-a-node-set-using-the-msxslnode-set()-function?p=0#comment40046
is working on a reply...