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

Please Sign in or register to post replies

Write your reply to:

Draft