The first googleAnalyticsCode property moves all the way down. And so what, right? It still expresses the exact same intention and is perfectly valid XML.
Except.. the XSLT parser doesn't think so.
Imagine that I am in node 1794, the third level, and I have this XPath:
This works great when the first piece of XML is in place, I get "UA-15104021-5". But after I publish, the same XPath will give me an empty result, because it stops at the googleAnalyticsCode on level 2 instead of level 1.
Two simple questions:
1. Why?!? (I'm a bit frustrated ;-))
2. How can I fix this? I don't want to add [@level = 1] to the node, but I want something that says: walk up the tree and if the googleAnalyticsCode is NOT empty, then stop.
As for the why? I have no idea! From my experience (looking through my various "umbraco.config" files), the <data> tags always come before the <node> tags ... are you using v4.5?
Folkert: No, that's why I did the recursive call it could be defined on any level. I never do put a hard ID in anywhere anyway, it's not flexible.
Lee: Yes there is. Benjamin's solution is a bit easier to read and works great as well, thanks though, good one! It's 4.0.3.. maybe it's a bug that has been fixed, but semantically I still thing that I was right and the XSLT parsers was wrong! ;)
Sebastiaan, sure it's easier to read, but will it work on the homepage node? (@level=1) As the XPath is looking for an ancestor::node; you'd need to replace that with ancestor-or-self::node.
Just came back from a nice vacation in Sweden with no internet, and saw your tweet regarding this. I know I'm horribly late to the game, but nevertheless, seeing nobody adressing your loud "why?", I thought I'd chip in the missing piece(s):
As for the "why?", I'll try to explain it here (I don't know why the properties move, but I do know why you get a different result in XSLT):
XPath expressions always return sets, so when you say:
The important part is that these are returned in document order - so that's part of the answer (why they shift when the property change location in the source).
The second half of the answer is that when you use xsl:value-of (in XSLT 1.0) on a set of nodes (which you may or may not be aware of that you are in this case), it will only return the value of the first node in that set, which is what tricks us all into forgetting that we're actually dealing with a set of nodes.
Benjamin's solution works - but again, if more than one ancestor has a value in the googleAnalyticsCode property, you'll get the one that's first in document order - which you've seen isn't necessarily the one you think it is (or the one you're after).
To safeguard against accidentally getting the wrong result in a situation like this, you should ask for:
1. All ancestors that has a non-empty value for the property you want
(As for using the normalize-space() function, I've found it's the most reliable way of detecting if an element has no real content, e.g., if the element for some reason has a couple of newlines/spaces or any other whitespace it'll still work, which the blah != '' won't).
Nope - actually there's a couple of things wrong with that:
The googleAnalyticsCode is a property and thus, will never appear in the ancestor tree
When you specify text() you implicitly ask for child::text() - specifying nothing actually gets you descendant::text(), so suddenly you can get weird results if there's also element content.
Doing normalize-space() != '' just isn't necessary - if the result of normalize-space() is an empty string the predicate will return false()
I'd say that you need to examine the raw XML data very closely - If the first one doesn't give you anything but the second does (for the same XML data) it suggests to me that there's a node with a 'googleAnalyticsCode' property that's not empty, somewhere below the one on level 1. You sure there's not a <br/> or something else in there?
OR - there is actually a node with TWO 'googleAnalyticsCode' properties, where the first one is blank :-)
XSLT fails after publish
I thought I was pretty well versed in XSLT by now, but this is a new one for me!
When I publish my root document in Umbraco (level 1, parentId -1), the document's properties move from the top to the bottom, example:
<node id="1784" version="1f445e8b-919f-4da5-88eb-b218cbd81a3d" parentID="-1" level="1" writerID="0" creatorID="0" nodeType="1070" template="0" sortOrder="5" createDate="2009-10-26T15:49:43" updateDate="2010-07-22T10:42:32" nodeName="delfttoer.nl" urlName="delfttoernl" writerName="Administrator" creatorName="Administrator" nodeTypeAlias="ContentFolder" path="-1,1784"> <data alias="googleAnalyticsCode">UA-15104021-5</data> <node id="1785" version="53bc9b6f-d0f5-46d9-b783-84a13ae05026" parentID="1784" level="2" writerID="4" creatorID="0" nodeType="1070" template="0" sortOrder="1" createDate="2009-10-26T15:49:43" updateDate="2010-06-30T12:21:57" nodeName="Panoramas" urlName="panoramas" writerName="Delft" creatorName="Administrator" nodeTypeAlias="ContentFolder" path="-1,1784,1785"> <data alias="googleAnalyticsCode"></data> <data alias="bodyText">Test</data> <node id="1794" version="f219e1b6-3840-4467-a46d-7c6781834a36" parentID="1793" level="3" writerID="0" creatorID="0" nodeType="1145" template="1144" sortOrder="1" createDate="2009-10-26T15:49:44" updateDate="2009-12-22T11:40:54" nodeName="Highlights" urlName="highlights" writerName="Administrator" creatorName="Administrator" nodeTypeAlias="Categorie" path="-1,1784,1792,1793,1794"> <data alias="categoryIcon"></data> </node> </node> </node>
This is a (highly simplified) version of my umbraco.config as you can see, the googleAnalyticsCode property is right below the root node at level 1.
Now, after I click publish, this is what happens every time:
Hey,
Either this:
Or:
should do the trick.
Best,
Benjamin
I assume there only one node for defining the GA code, so try to get the nodeID by:
Hey ,
Could there be a Google Analytics code on a different node/level? If so you want a recursive check, try this XPath...
Cheers, Lee.
Thanks Benjamin, that's the most important issue solved, but.. WHY? lol!
As for the why? I have no idea! From my experience (looking through my various "umbraco.config" files), the <data> tags always come before the <node> tags ... are you using v4.5?
Folkert: No, that's why I did the recursive call it could be defined on any level. I never do put a hard ID in anywhere anyway, it's not flexible.
Lee: Yes there is. Benjamin's solution is a bit easier to read and works great as well, thanks though, good one! It's 4.0.3.. maybe it's a bug that has been fixed, but semantically I still thing that I was right and the XSLT parsers was wrong! ;)
Sebastiaan, sure it's easier to read, but will it work on the homepage node? (@level=1) As the XPath is looking for an ancestor::node; you'd need to replace that with ancestor-or-self::node.
Cheers, Lee.
I know! The homepage is always on level 2 though, so no worries! :-)
Cool... all bases covered! :-D
Hi Sebastiaan,
Just came back from a nice vacation in Sweden with no internet, and saw your tweet regarding this. I know I'm horribly late to the game, but nevertheless, seeing nobody adressing your loud "why?", I thought I'd chip in the missing piece(s):
As for the "why?", I'll try to explain it here (I don't know why the properties move, but I do know why you get a different result in XSLT):
XPath expressions always return sets, so when you say:
- you will get a set of all of $currentPage's ancestor nodes' google property; something like this:
The important part is that these are returned in document order - so that's part of the answer (why they shift when the property change location in the source).
The second half of the answer is that when you use xsl:value-of (in XSLT 1.0) on a set of nodes (which you may or may not be aware of that you are in this case), it will only return the value of the first node in that set, which is what tricks us all into forgetting that we're actually dealing with a set of nodes.
Benjamin's solution works - but again, if more than one ancestor has a value in the googleAnalyticsCode property, you'll get the one that's first in document order - which you've seen isn't necessarily the one you think it is (or the one you're after).
To safeguard against accidentally getting the wrong result in a situation like this, you should ask for:
1. All ancestors that has a non-empty value for the property you want
2. Get the 1st in that set (i.e., closest ancestor with a value)
3. Get the value of the desired property
So we end up with this:
- or in 4.5 lingo:
(As for using the normalize-space() function, I've found it's the most reliable way of detecting if an element has no real content, e.g., if the element for some reason has a couple of newlines/spaces or any other whitespace it'll still work, which the blah != '' won't).
/Chriztian
Great work! I see what you mean about additional white space characters coming into play.
quick question: [I don't have an environment at my disposal to test but] would this also be an alternative?
If it is, it might eliminate the need for the wild card and sub filtering. just checking.
Hi criticalmaas,
Nope - actually there's a couple of things wrong with that:
Great! Thanks for the clarification!
I'm back.. Chriztian, I've managed to break your excellent suggestion somehow! So this works great for most sites:
But, on some sites it works at first (before the properties are being moved down in the XML file, after that it just doesn't return anything.
For now I've had to resort to doing this:
What could still be going wrong?
Hi Sebastiaan,
I'd say that you need to examine the raw XML data very closely - If the first one doesn't give you anything but the second does (for the same XML data) it suggests to me that there's a node with a 'googleAnalyticsCode' property that's not empty, somewhere below the one on level 1. You sure there's not a <br/> or something else in there?
OR - there is actually a node with TWO 'googleAnalyticsCode' properties, where the first one is blank :-)
/Chriztian
is working on a reply...