Extending Sitecore 8.1 – Adding support for anchors in internal links
Technical | Sitecore
Drazen Janjicek

By Drazen Janjicek | Principal Consultant 18.03.2016.

Extending Sitecore 8.1 – Adding support for anchors in internal links

Modern Content Management Systems (CMS) like Sitecore are far more than simple content storage’s. They provide an application framework that stores the content, hosts the published web pages and takes care of link management between internal pages of the website.

An often seen requirement is linking to an internal page and jumping directly to a particular section on that page. Commonly this is done using HTML anchors. All the content editor has to do is making sure that the rendered hyperlink contains a reference to the upfront created anchor.

Sitecore provides a field type called General Link that can be used to target web pages while meeting the requirement of jumping directly to a particular section. This can be done through the Insert external link dialog of that field. The content editor needs to enter a link text in the description field and the full URL including the anchor in the URL field. This approach works great for external web pages but is not recommended for internal pages.

Link Management within Sitecore is done by not storing hard-coded URL’s but rather the unique identifier of the target item. This allows renaming the target item or relocating it within the content tree without breaking the stored hyperlink information. The content editor would need to keep track of all general link fields that target a particular Sitecore item and update each field manually to make sure the hyperlink renders a valid URL on the website. A better approach is using the Insert Link dialog of the General Link field. Here the content editor selects the target item either in the tree view or search view (depending on the Sitecore version installed). The link field then stores the identifier of the item and everything is good.

But is it?

Unfortunately the latest releases of Sitecore 8.x don’t offer any means of storing anchor information for internal links. The Insert Link dialog offers input fields for everything but anchors. Sitecore is able to render such hyperlinks on the website but storing the necessary information is not supported.

The original dialog

Fortunately though Sitecore is built in a way that it allows developers to extend it from the core and this includes extending out-of-the-box dialogs like this one. In Sitecore 8.x these dialogs are implemented with SPEAK (https://doc.sitecore.net/speak) and this makes it actually pretty easy. This blog post intends to explain all necessary steps for extending Sitecore 8.x to support this requirement.

The dialog is represented through the /sitecore/client/Applications/Dialogs/InsertLinkViaTreeDialog item in the core database and the presentation layer allows it to be rendered in a browser – just like any other content item served on the website. The main difference is that Sitecore does not support editing the presentation layer through the content editor or page editor. This task can be exclusively done with Sitecore Rocks (http://vsplugins.sitecore.net/), a Visual Studio extension that every serious Sitecore developer should be familiar with.

Okay, so using Rocks we first need to see how this dialog is built up.

The presentation layer of the dialog configurable through Sitecore Rocks

As we can see the presentation layer contains renderings of a certain type, a placeholder reference and possibly a configured data-source to work with. Nothing special here…

Each row in the field set of the dialog is represented through a Border rendering that in the end renders as a div element, a Label rendering for the displayed field text and, based on the needed functionality, another rendering for the actual input. In this blog post we’re going to add these three elements in order to support anchors.

  1. We add a new rendering of type Border and name it AnchorRow. This rendering should be assigned the NameContainer.Content placeholder key
  2. We add a new rendering of type Label and name it AnchorLbl. This rendering should be assigned the AnchorRow.Content placeholder key
  3. We add a new rendering of type Textbox and name it Anchor. This rendering should be also assigned the AnchorRow.Content placeholder key
The rendering components we need

If we now go to the content editor in Sitecore and open the Insert Link dialog of the General Link field we should see the new controls. What’s missing here is the label text. The properties of the AnchorLbl rendering allow setting the text directly in the Text field or configuring a data-source item instead which is a best practice since day one.

Below the /sitecore/client/Applications/Dialogs/InsertLinkViaTreeDialog/PageSettings item we can see that items are already defined for all kinds of label texts or other binding values. Here we can simply duplicate an item, rename it to something meaningful like Anchor and provide the label text.

Setting the label value

This new item must be assigned to the DataSource field of the AnchorLbl rendering. Now we should have text for the label and an input field for the anchor information. But when we click Insert in the Insert Link dialog this information is not stored. Sitecore doesn’t know yet what to do with this new input field. The presentation layer of the InsertLinkViaTreeDialog contains another rendering of type PageCode and looking at the properties of that rendering reveals that it contains references to code files. In this case a JavaScript file (for handling the CustomUrl field of the dialog) and a class reference of type Sitecore.Speak.Applications.InsertLinkDialogTree, Sitecore.Speak.Applications. This class defines all properties and logic of the dialog. While looking at the internals of this class (using our buddy Reflector) we see that the class cannot simply be extended so we need to create a new class that inherits from Sitecore.Web.PageCodes. PageCodeBase, Sitecore.Speak.Client. We name this class InsertLinkDialogTreePageCode.

The next step would be to copy all source code from the original class because we don’t want to re-invent the wheel. All we need to do is add support for our new input field.

  1. Add a new public property of type Sitecore.Mvc.Presentation.Rendering, Sitecore.Mvc and name it Anchor
  2. In the ReadQueryParamsAndUpdatePlaceholders method pull the anchor information of the General Link field and assign its value to the text property of our Anchor field in the dialog
  3. Configure the PageCode rendering to use this new class instead of its original implementation
Set the anchor attribute

So now we can display some existing value in the new anchor field when the dialog is opened. But how do we get it there in the first place? The content editor opens the dialog and the anchor field is blank. He enters the proper value, e.g. #my-anchor and clicks the Insert button. Looking at the raw value of the field shows that the anchor has not been added. Something is missing here…

The raw value of the original general link field

If we look one more time at the presentation details of the dialog we can see a rendering of type Rule named InsertLinkButtonRule and it's assigned to the InsertLinkButton button. This means this rule is invoked when the button is clicked and, based on its outcome, an action is triggered and this should do something. Good old Sitecore Rules Engine… The InsertLinkButtonRule rule targets a rule definition as can be seen in the RuleItemId property. The targeted item contains a Rule field with the actual condition that must be met with the appropriate action.

The condition is straight forward, it always evaluates a true value meaning the assigned action will always be triggered.

The assigned action definition can be found at /sitecore/client/Business Component Library/version 1/Layouts/Renderings/Resources/Rule/Rules/Actions/MakeInternalLinkFromTreeView.

It says:

set the hyperlink display text to [targetDisplayTextID,,,textControl1]’s text property, query value to [targetQueryID,,,textControl2]’s text property, target window value to [targetWindowID,,,comboBox1]’s selectedItem property, custom URL value to [customUrlID,,,textControl3]’s text property, alternative text to [targetAltTextID,,,textControl4]’s text property, style to [targetStyleID,,,textControl5]’s text property, id to [targetControlID,,,treeView1]’s selectedItemId property and path value to [targetPathID,,,treeView2]’s selectedNode’s [targetPathProperty,,,path] property

This definition contains all relevant fields from the dialog, but it lacks our new anchor field. Let’s change that:

set the hyperlink display text to [targetDisplayTextID,,,textControl1]’s text property, query value to [targetQueryID,,,textControl2]’s text property, target window value to [targetWindowID,,,comboBox1]’s selectedItem property, custom URL value to [customUrlID,,,textControl3]’s text property, alternative text to [targetAltTextID,,,textControl4]’s text property, style to [targetStyleID,,,textControl5]’s text property, anchor target name to [targetAnchorID,,,textControl6]’s text property, id to [targetControlID,,,treeView1]’s selectedItemId property and path value to [targetPathID,,,treeView2]’s selectedNode’s [targetPathProperty,,,path] property

After updating this definition we can go back to the InsertLinkButtonRuleDefinition item and edit the rule. Set the textControl6 value to Anchor (the name of our new input field in the dialog).

So the next time the Insert button is being clicked we will pull the value of the new Anchor textbox field and then we should be good, right? Not completely… We still don’t store the value in our General Link field. One more step is required.

Sitecore uses a JavaScript file located on the file-system at \sitecore\shell\client\Speak\Layouts\Renderings\Resources\Rules\ConditionsAndActions\Actions called MakeInternalLinkFromTreeView.js to generate the XML representation of the General Link field value. This file we need to extend a bit as well.

  1. Define a variable called targetAnchorID (this name we used in our rule definition) and assign it the value of context.app[args.targetAnchorID]
  2. Extend the value of the template variable with an anchor attribute
  3. Store the anchor value retrieved from the dialog in the anchor attribute
Set the target anchor variable

The General Link field should now store the anchor information in the field value. Add the anchor value through the Insert Link dialog and verify the raw value of the field.

So let’s summarize what we did:

  1. We extended the dialog with renderings for the anchor information
  2. We created a page code implementation that works with the new field
  3. We extended the rule definition to pass the field value to the SPEAK application context
  4. We extended the XML representation of the General Link field to store the anchor field value

In the end not that much work in order to meet this common requirement of supporting hyperlink anchors with Sitecore 8.x.

This solution has been implemented in Sitecore Experience Platform 8.1 rev. 160302 (8.1 Update-2).

Update Oct 6th 2016: As of today the module is available in the marketplace.

Drazen Janjicek

By Drazen Janjicek | Principal Consultant 18.03.2016.