Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Sebastiaan Janssen 5045 posts 15476 karma points MVP admin hq
    Jul 22, 2010 @ 15:03
    Sebastiaan Janssen
    0

    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:

    <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">
        <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>
        <data alias="googleAnalyticsCode">UA-15104021-5</data>
    </node>
    
    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:
    $currentPage/ancestor::node/data[@alias='googleAnalyticsCode']
    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.
  • Jamie Howarth 306 posts 773 karma points c-trib
    Jul 22, 2010 @ 15:10
    Jamie Howarth
    0

    Hey,

    Either this:

    $currentPage/ancestor::node/data[@alias='googleAnalyticsCode' and text() != '']

    Or:

    $currentPage/ancestor::node[@level=1]/data[@alias='googleAnalyticsCode']

    should do the trick.

    Best,

    Benjamin

  • Folkert 82 posts 212 karma points
    Jul 22, 2010 @ 15:13
    Folkert
    0

    I assume there only one node for defining the GA code, so try to get the nodeID by:

    <xsl:value-of select="umbraco.library:GetXmlNodeById(1784)"/>
  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Jul 22, 2010 @ 15:15
    Lee Kelleher
    2

    Hey ,

    Could there be a Google Analytics code on a different node/level? If so you want a recursive check, try this XPath...

    $currentPage/ancestor-or-self::node[string(data[@alias='googleAnalyticsCode']) != ''][1]/data[@alias='googleAnalyticsCode']

    Cheers, Lee.

  • Sebastiaan Janssen 5045 posts 15476 karma points MVP admin hq
    Jul 22, 2010 @ 15:16
    Sebastiaan Janssen
    0

    Thanks Benjamin, that's the most important issue solved, but.. WHY? lol!

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Jul 22, 2010 @ 15:17
    Lee Kelleher
    0

    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?

  • Sebastiaan Janssen 5045 posts 15476 karma points MVP admin hq
    Jul 22, 2010 @ 15:19
    Sebastiaan Janssen
    0

    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! ;)

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Jul 22, 2010 @ 15:24
    Lee Kelleher
    0

    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.

  • Sebastiaan Janssen 5045 posts 15476 karma points MVP admin hq
    Jul 22, 2010 @ 15:25
    Sebastiaan Janssen
    1

    I know! The homepage is always on level 2 though, so no worries! :-)

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Jul 22, 2010 @ 15:27
    Lee Kelleher
    0

    Cool... all bases covered! :-D

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 7x admin c-trib
    Jul 29, 2010 @ 00:00
    Chriztian Steinmeier
    5

     

    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:

    $currentPage/ancestor::node/data[@alias='googleAnalyticsCode']

    - you will get a set of all of $currentPage's ancestor nodes' google property; something like this:

    <data alias="googleAnalyticsCode">UA-15104021-5</data>
    <data alias="googleAnalyticsCode"></data>
    <data alias="googleAnalyticsCode"></data>

    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

    $currentPage/ancestor::node[data[@alias = 'googleAnalyticsCode']][normalize-space()]

    2. Get the 1st in that set (i.e., closest ancestor with a value)

    $currentPage/ancestor::node[data[@alias = 'googleAnalyticsCode']][normalize-space()][1]

    3. Get the value of the desired property

    $currentPage/ancestor::node[data[@alias = 'googleAnalyticsCode'][normalize-space()]][1]/data[@alias = 'googleAnalyticsCode]

    So we end up with this:

    <xsl:value-of select="$currentPage/ancestor::node[data[@alias = 'googleAnalyticsCode'][normalize-space()]][1]/data[@alias = 'googleAnalyticsCode]" />

     

    - or in 4.5 lingo:

    <xsl:value-of select="$currentPage/ancestor::*[googleAnalyticsCode[normalize-space()]][1]/googleAnalyticsCode" />

     

    (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

     

  • criticalmaas 10 posts 28 karma points
    Jul 29, 2010 @ 01:09
    criticalmaas
    0

    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?

    <xsl:value-of select="$currentPage/ancestor::googleAnalyticsCode[normalize-space(text()) != '']" />

    If it is, it might eliminate the need for the wild card and sub filtering. just checking.

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 7x admin c-trib
    Jul 29, 2010 @ 01:23
    Chriztian Steinmeier
    1

    Hi criticalmaas,

    Nope - actually there's a couple of things wrong with that:

    1. The googleAnalyticsCode is a property and thus, will never appear in the ancestor tree
    2. 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.
    3. Doing normalize-space() != '' just isn't necessary - if the result of normalize-space() is an empty string the predicate will return false()
    /Chriztian 

  • criticalmaas 10 posts 28 karma points
    Jul 29, 2010 @ 01:30
    criticalmaas
    0

    Great! Thanks for the clarification!

  • Sebastiaan Janssen 5045 posts 15476 karma points MVP admin hq
    Aug 11, 2010 @ 15:23
    Sebastiaan Janssen
    0

    I'm back.. Chriztian, I've managed to break your excellent suggestion somehow! So this works great for most sites:

    <xsl:value-of select="$currentPage/ancestor::node[data[@alias = 'googleAnalyticsCode'][normalize-space()]][1]/data[@alias = 'googleAnalyticsCode']" />

    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:

    <xsl:value-of select="$currentPage/ancestor::node[@level = 1]/data [@alias='googleAnalyticsCode']" />

    What could still be going wrong?

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 7x admin c-trib
    Aug 11, 2010 @ 21:08
    Chriztian Steinmeier
    0

    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

Please Sign in or register to post replies

Write your reply to:

Draft