Copied to clipboard

Flag this post as spam?

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


  • Alec Griffiths 129 posts 151 karma points
    Oct 04, 2009 @ 21:57
    Alec Griffiths
    0

    How can I convert all relative paths (href's and src's) to absolute ones using the resolve-uri() function?

    The situation is I have some xhtml that contains href's links and image src's that may or may not be absolute. Now for only the ones that are relative given a bace url how can I use xslt to transform all those relative links to absolute ones? I know I should use the resolve-uri() function somehow but I have no idea how I would do this. A sample would help me allot.

    Thanks in advance.

    Alec

  • Kenneth Solberg 227 posts 418 karma points
    Oct 05, 2009 @ 10:03
  • Alec Griffiths 129 posts 151 karma points
    Oct 05, 2009 @ 14:30
    Alec Griffiths
    0

    Hi Kenneth
    Looking at your part of that post, thanks but that doesn’t help me. Also I can already imagine doing it via a .Net control but I really would like to do this with XSLT. There is some xslt in the post that looks along the correct lines but I am not competent with xslt. I would love an example..

    The XHTML that I want to format the links on is not from my site but from another. For example take the following XHTML taken from http://umbraco.org/ imagine it is in a XSLT parameter/variable:

    <ul id="naviList">
        <li><a href="/tour" title="Tour">Tour</a></li>
        <li><a href="/products" title="Products">Products</a></li>
        <li><a href="/videos" title="Videos">Videos</a></li>
        <li><a href="/documentation" title="Documentation">Documentation</a></li>
        <li><a href="/training" title="Training">Training</a></li>
        <li><a href="/solution-providers" title="Solution Providers">Solution Providers</a></li>
        <li><a href="http://www.google.com" title="google">Go To Google</a></li>
    </ul>

    Now imagine I want to resolve the absolute URL’s of the relative paths given the known url: http://umbraco.org/ so my output would be like this:

    <ul id="naviList">
        <li><a href="http://umbraco.org/tour" title="Tour">Tour</a></li>
        <li><a href=" http://umbraco.org/products" title="Products">Products</a></li>
        <li><a href=" http://umbraco.org/videos" title="Videos">Videos</a></li>
        <li><a href=" http://umbraco.org/documentation" title="Documentation">Documentation</a></li>
        <li><a href=" http://umbraco.org/training" title="Training">Training</a></li>
        <li><a href=" http://umbraco.org/solution-providers" title="Solution Providers">Solution Providers</a></li>
        <li><a href="http://www.google.com" title="google">Go To Google</a></li>
    </ul>

    Alec

  • Douglas Robar 3570 posts 4711 karma points MVP ∞ admin c-trib
    Oct 05, 2009 @ 14:47
    Douglas Robar
    1

    Here's how I've done it before in xslt. The idea is to do a pattern search/replace. If the href doesn't start with href="http" (that is, if it starts with href="/") then I know I need to specify it. Same would go for img src= tags.

    In the xslt, you'd have a separate template that you'd call by name. In my example I'm using the current page's bodyText property, but you could just as easily use a macro parameter instead.

     

    <!-- =========================================================== -->

    <xsl:template match="/">
                   <xsl:variable name="content">
                        <xsl:call-template name="fullUrls">
                            <xsl:with-param name="content" select="$currentPage/data [@alias='bodyText']" />
                        </xsl:call-template>
                    </xsl:variable>

                    <xsl:value-of select="$content" />
    </xsl:template>

    <!-- =========================================================== -->

    <xsl:template name="fullUrls">
        <xsl:param name="content" />

        <xsl:variable name="srcPattern"><xsl:text>src="/</xsl:text></xsl:variable>
        <xsl:variable name="srcReplacePattern"><xsl:text>src="http://www.example.com/</xsl:text></xsl:variable>
        <xsl:variable name="hrefPattern"><xsl:text>href="/</xsl:text></xsl:variable>
        <xsl:variable name="hrefReplacePattern"><xsl:text>href="http://www.example.com/</xsl:text></xsl:variable>

        <xsl:variable name="step1" select="umbraco.library:Replace($content, $srcPattern, $srcReplacePattern)"/>
        <xsl:value-of select="umbraco.library:Replace($step1, $hrefPattern, $hrefReplacePattern)"/>
    </xsl:template>

    <!-- =========================================================== -->

     

    cheers,
    doug.

  • Dirk De Grave 4541 posts 6021 karma points MVP 3x admin c-trib
    Oct 05, 2009 @ 14:49
    Dirk De Grave
    0

    Hi Alec,

    Can't you just use the starts-with xslt function to check whether you're dealing with a relative or absolute url path?

     

    Hope this helps.

    Regards,

    /Dirk

  • Alec Griffiths 129 posts 151 karma points
    Oct 05, 2009 @ 19:24
    Alec Griffiths
    0

    @Dirk De Grave
    You may be right but as mentioned I am not competent with xslt so without an example I am quite lost..

    @Douglas
    You are so close I can smell it! However all the tags are getting stripped from my xhtml. You see the variable is a nodeset not text. If I use text it works but I want to keep it as a nodeset and output as xml.. I have tried various permutations of copy-of value-of and disable-output-escaping but my tags still disappear. Here is what I have so far:

     

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#x00A0;"> ]>
    <xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxml="urn:schemas-microsoft-com:xslt"
    xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:parser.html2xml2="urn:parser.html2xml2"
    exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets parser.html2xml2 ">
    <xsl:output method="xml" omit-xml-declaration="yes"/>
    <xsl:param name="currentPage"/>
    <xsl:param name="url" select="/macro/URL" />
    <xsl:param name="xpathQuery" select="/macro/xpathQuery" />
    <xsl:template match="/">
    <xsl:if test="$url!=''">
    <xsl:if test="$xpathQuery!=''">
    <xsl:variable name="content">
    <xsl:call-template name="fullUrls">
    <xsl:with-param name="content" select="parser.html2xml2:Htm2XmlQueryNodeset($url, $xpathQuery)"/>
    </xsl:call-template>
    </xsl:variable>
    <xsl:copy-of select="$content" />
    </xsl:if>
    </xsl:if>
    </xsl:template>
    <xsl:template name="fullUrls">
    <xsl:param name="content" />
    <xsl:variable name="srcPattern"><xsl:text>src="/</xsl:text></xsl:variable>
    <xsl:variable name="srcReplacePattern"><xsl:text>src="http://www.example.com/</xsl:text></xsl:variable>
    <xsl:variable name="hrefPattern"><xsl:text>href="/</xsl:text></xsl:variable>
    <xsl:variable name="hrefReplacePattern"><xsl:text>href="http://www.example.com/</xsl:text></xsl:variable>
    <xsl:variable name="step1" select="umbraco.library:Replace($content, $srcPattern, $srcReplacePattern)"/>
    <xsl:copy-of select="umbraco.library:Replace($content, $hrefPattern, $hrefReplacePattern)"/>
    </xsl:template>
    </xsl:stylesheet>
  • Douglas Robar 3570 posts 4711 karma points MVP ∞ admin c-trib
    Oct 05, 2009 @ 20:39
    Douglas Robar
    0

    You're right... you'll end up converting your nodeset to text during the conversion. You'll then need to convert it back to a node-set when you're done. This is sometimes a bit tricky and can take a bit of trial and error to get just right.

    The basic idea is to take the output of your variable ($content) and turn it back into a nodeset. There's the built-in <xsl:value-of select="msxsl:node-set($content)" /> that should do it, or at least get you close.

    cheers,

    doug.

  • Kenneth Solberg 227 posts 418 karma points
    Oct 06, 2009 @ 10:02
    Kenneth Solberg
    1

    One option is the use the Replace function like this: umbraco.library:Replace($someText, 'href="/', 'href="http://www.somedomain.com') or based on Doug's reply you could transform the X(HT)ML like this:

    <xsl:for-each select="$nodesetVariable/ul [@id = 'naviList']/li">
    <a title="{@title}">
    <xsl:attribute name="href">
    <xsl:if test="begins-with(@href, 'http://')">
    <xsl:value-of select="$someDomain" />
    </xsl:if>
    <xsl:value-of select="@href" />
    </xsl:attribute>
    <xsl:value-of select="." />
    </a>
    </xsl:for-each>
  • Kenneth Solberg 227 posts 418 karma points
    Oct 06, 2009 @ 10:03
    Kenneth Solberg
    1

    erh ... starts-with that is *blush*

  • Dirk De Grave 4541 posts 6021 karma points MVP 3x admin c-trib
    Oct 06, 2009 @ 10:47
    Dirk De Grave
    0

    Here you go, Kenneth's just given you a starts-with example. thanks kenny!

  • Len Dierickx 150 posts 92 karma points
    Oct 06, 2009 @ 12:07
    Len Dierickx
    0

    Hi,

    This is an issue that I have been checking and I think it is a bug in Umbraco.

    You can set the option <useDomainPrefixes>true</useDomainPrefixes> in the config file umbracosettings.config

    to true. Then you have to add the domain name/hostname to all pages on level 2, like this:

    Homepage > add hostname www.domain.com

    * Page1 > add hostname www.domain.com/page1 without the aspx

    * Page2 > add hostname www.domain.com/page2 without the aspx

    Now your niceurl will work and give you an absolute url because it will search for the domain names up to level 2. I have this working on our installation using umbraco 4 and we are trying to change the method that causes this behavior wich is niceurldo in the library.

    This is really a bug in Umbraco.

    thx, Len. 

  • Len Dierickx 150 posts 92 karma points
    Oct 06, 2009 @ 12:08
    Len Dierickx
    0

    mhh, maybe this is not what you need ....

  • Alec Griffiths 129 posts 151 karma points
    Oct 08, 2009 @ 00:38
    Alec Griffiths
    0

    @Douglas & Kenneth
    Thanks for your help I have learned ALLOT over the last few nights (and there is allot more to learn), and your pointers where part of that. I finally have a solution I am happy with although not quite finished.. BTW I was not under pressure on this just wanted to learn something.

    My solution was to make a c# function that emulates the behaviour of the XSLT 2.0 resolve-uri() function. I guess we will have to wait a few more years for MS to support this.

    FYI (and for others) below is my XSLT & C#. I know I should match the node names AND the attributes to prevent issues but the below is not finished yet. I would like to award you both the solution but in the end Kenneth kicked my in the right direction so karma to you. Next time Douglas ?

    Alec

    P.S. Would appreciate feedback (code review etc)

    C#

    public static string resolveUri(string baseUrl, string relstr)
    {
    Uri uriout;
    Uri uribace = new Uri(baseUrl);
    if (Uri.TryCreate(uribace, relstr, out uriout))
    {
    return uriout.ToString();
    }
    else
    {
    return "";
    }
    }
    private static bool TryCreateCleanUri(Uri baseUrl, string relstr, out Uri result)
    {
    if (!Uri.TryCreate(baseUrl, relstr, out result))
    {
    return false;
    }
    else
    {
    return true;
    }
    }

    XSLT

    <xsl:template match="/">
    <!--call the template for our XHTML document-->
    <xsl:apply-templates select="$nodesetVariable/*"/>
    </xsl:template>

    <!--Identity Template: xsl:copy with recursion. This is cool it recursively call itself and all other templates copying every node: Find out more here http://www.xmlplease.com/xsltidentity-->;
    <xsl:template match="@*|node()">
    <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    </xsl:template>

    <!--now if a href attribute is found in any node in the Identity Template we do something with it here-->
    <xsl:template match="@href">
    <!--no chage to the atribute name-->
    <xsl:attribute name="href">
    <!--test if this is a relitive link-->
    <!--only http as we could have https-->
    <xsl:if test="starts-with(., 'http') and $resolveLinks = 'true'">
    <xsl:value-of select="." />
    </xsl:if>
    <!--Call our resolveUri function. This is an xslt extension that comes with the package it uses c# to emulate the resolve-Uri() that is part of XSLT 2.0-->
    <!--I was advised to do a replace by first changing the nodeset to text but I wanted to keep this as a nodeset and I also wanted to nicely resolve relative paths-->
    <!--that climb the uri tree e.g. /../../../somefile.html. Note the '.' means use the value that exists-->
    <xsl:value-of select="parser.html2xml2:resolveUri($url, .)"/>
    </xsl:attribute>
    </xsl:template>
  • Alec Griffiths 129 posts 151 karma points
    Oct 08, 2009 @ 00:40
    Alec Griffiths
    0


    @Dirk
    Thanks for your suggestion too. Yes and no. I wanted to resolve things like /../../page.html also. Whilst bolting this to a url would work I was not happy with it and wanted to navigate the url.

    Alec

  • This forum is in read-only mode while we transition to the new forum.

    You can continue this topic on the new forum by tapping the "Continue discussion" link below.

Please Sign in or register to post replies