On a recent project, we had a couple of interesting requirements with regards to link management on the site, one of which was to rid the site of web page file extensions (no page or link should have a .html or other extension, and index pages should also have the index file name removed (so /news/index.html would be shown as /news/).
The site was driven by the Content Delivery OData web service, so the best place to implement this was using a custom tcdl:Link tag. This article I show you how you can easily do this
You might immediately think of a simple solution: Create each page within its own Structure Group, using a default file name (/news/cat-bites-dog/index.html). You can then access the page with the url without the file name or extension (/news/cat-bites-dog). But as I already alluded, you can see that dynamic linking will still return the full url, so dynamic links will require some post-processing. Plus of course, its going to be pretty messy to create a new Structure Group for each page.
Background – links and REL
To set the scene for my solution, here follows a brief overview of the standard publishing/rendering process for links: In templates, links are written out using the tcdl:Link syntax (either automatically using the Link Resolver TBB, or manually with your own code). This is then either transformed to .NET/Java linking syntax (tridion:ComponentLink etc.) or left alone (if you are publishing REL – which we use for our oData driven site). When the the REL content is served via the web service (or the CD API, in the case of REL DCPs), the TCDL tags are rendered, to resolve the links.
The best solution then, is to extend the linking functionality at the TCDL level, by creating our own TCDL tag.
Create a simple TCDL tag
So how do we do this? Its very easy, we just need to create a jar file with our own tag class, and configure it in the cd_dynamic_conf.xml file:
Heres the code for the tag. You need to compile this into a .jar file and put it in the /lib folder of your CD webservice. Note that the code here is basically the same as the standard LinkTagRenderer class, with the processLink function added in.
Configure it
Then update the TCDLEngine part of your cd_dynamic_conf.xml file to refer to this tag. Restart your CD web service application, and you are good to go.
Note that if you are using other standard TCDL tags, then you should add those explicitly here, as mentioned in the online documentation [login required].
Finally we just needed to add some routing logic in our web application, to ensure that the extension-less url mapped to the Tridion published page URL when making the request for the page content.
More custom linking…
Later in the project another link related requirement came through. The site had a large volume of news content, which were published as components only (no pages). These news articles should be accessible using ‘friendly’ urls (so a generic url like /news/article?id=1234 was not OK, but /news/cat-bites-dog/1234 was). The url to use was metadata on the news component – we auto generated it from the news headline, but of course editors could edit it if they wished.
Implementing this was rather easy given that we already have the extended link TCDL tag. In our extended tag logic, if a component link does not resolve (as would be the case for the component which is not on a page, but only published as a DCP), we load the component meta. If this is null, then component is not published (so we should not show a link), however if it is not null, and the component schema id is that for news, we load the friendly url from the custom meta, and write out a link using this url, and the component id. Note in real life, we create a SchemaId-to-url-pattern mapping rather than hardcoding the schema id and url pattern as in the example below:
All that remained then, was to add some logic into our web application, to re-route the friendly url (/news/cat-bites-dog/1234) to a page which displays the appropriate news article.
Lastly a quick note on the use of an ID in the URL: We add the component id to the URL for two reasons. Firstly it means we can retrieve the appropriate article to show without a broker query on the url metadata. Secondly, if the editor changes the friendly url, old URLs (bookmarked, or stored in search indices) will still work. If you wanted to be really correct, your routing logic could match the friendly url processed with the custom meta, and set a HTTP 301 (moved permanently) status if they do not match
And thats it – not too much code, but a solution that works really well if you are using a CD webservice driven site, or even if you are making heavy use of REL DCPs in a standard site. For more ‘traditional’ sites I would try extending the logic in the Java/.NET linking taglib/web controls – see also my article on how to do this in .NET.
3 thoughts on “Cleaning and Extending Dynamic Linking”
I remember writing Custom CWA tags for one of client in Java, where we had extended ComponentLink class and also allowing additional tag attributes to be passed via Tridion templates.
Good revision for me
This was an eye opening article for me. Thanks Will.
Is there any reason why you chose to implement the interface instead of extending the base class (as per the online docs)? With the base LinkTagRenderer class we can call super within each of the methods ensuring we get the latest supported base code instead of relying on our own implementation. We also probably wouldn’t need to override any methods except generateComponentLink().
That would have been the ideal solution, and I did try that, however the attributes are private (and only have public set methods and not get methods) and the generateXXXLink methods are also private, so its difficult to access the things we need from the subclass. Also, even if they were public, generateXXXLink methods call GetLinkAsString which returns the whole anchor tag, whereas I want to manipulate the URL beforehand, so I need to call GetLink(), to get a Link object, update the URL (using setUrl) and then call toString() to get the full link output.
I remember writing Custom CWA tags for one of client in Java, where we had extended ComponentLink class and also allowing additional tag attributes to be passed via Tridion templates.
Good revision for me
This was an eye opening article for me. Thanks Will.
Is there any reason why you chose to implement the interface instead of extending the base class (as per the online docs)? With the base LinkTagRenderer class we can call super within each of the methods ensuring we get the latest supported base code instead of relying on our own implementation. We also probably wouldn’t need to override any methods except generateComponentLink().
That would have been the ideal solution, and I did try that, however the attributes are private (and only have public set methods and not get methods) and the generateXXXLink methods are also private, so its difficult to access the things we need from the subclass. Also, even if they were public, generateXXXLink methods call GetLinkAsString which returns the whole anchor tag, whereas I want to manipulate the URL beforehand, so I need to call GetLink(), to get a Link object, update the URL (using setUrl) and then call toString() to get the full link output.