Skip Ribbon Commands
Skip to main content

Robin | zevenseas | SharePoint Blog

:

The zevenseas Community > Blogs > Robin | zevenseas | SharePoint Blog > Posts > Yet Another SharePoint Gem : ForwardLinks and BackwardLinks
September 30
Yet Another SharePoint Gem : ForwardLinks and BackwardLinks

I always wondered what the properties ForwardLinks and BackwardLinks were meant for when I browsed through the properties of a SPListItem and SPFile.. and now I finally know! It’s another gem that SharePoint has.. let me show you how the product team has exposed this in the Manage Content & Structure page :

image

If look closely, there are two types of links.. ‘uses’ and ‘used by’(hence the Forward- and BackwardLinks). But let’s take a step back.. it seems that SharePoint knows that links are being used in content! Let’s test this using a very simple example, a blank site, using a Content Editor WebPart and an image stored in a document library.

image

image

image

image

So we have this.. now let’s take a look in code what the backward and forwardlinks are in the default.aspx and in the image..

image

image

And now for the magic part.. let’s move the picture to another library and be amazed..

image

And the source of the Content Editor WebPart looks like this :

image

Sweet eh? And I’ve never known this until now (and that’s the thing I really like about SharePoint..  the ability that is has to amaze you, even after so many years ;) .. now the good thing is that this functionality works for every SharePoint content control.. but (and here it comes) it doesn’t work for third party content controls like the Telerik Editor. And what are we recommending most of the time when it comes around rich HTML editing in SharePoint.. yes.. “get rid of the out of the box control and replace it with Telerik’s one”.  Question is then, how we do enable this awesome link functionality then?

Now instead of trying to manipulate the BackwardLinks and ForwardLinks collection (which we can’t because they are read-only) by some awesome complex code, why not let SharePoint handle this.. how?

Let me show you!

First..  let’s agree on the following fact, the Telerik field controls/webparts are used on (publishing) pages. Meaning that we columns (fields) at our disposal to (ab)use, remember that out of the box content controls do work with the linking? What if we could store the data from the webpart into a RichHTML field that the user doesn’t know of.. All we need now is an event to kick off when the user saves the page, so we just look no further than the eventreceivers like the ItemUpdated one on the Pages library..  To sum it up in some steps :

  1. User adds an image to the Telerik WebPart
  2. User saves the page
  3. EventReceiver kicks in..
    1. EventReceiver checks if an RichHtmlField exists, if not it will create it and makes it hidden, otherwise it does nothing
    2. EventReceiver get’s the actual page from the ListItem, opens it up and loops through the WebParts, checks if it is a Telerik one and gets the content and places this in the RichHTML field.
    3. EventReceivers updates the ListItem
  4. User checks the Content & Structure page and is amazed!

Here’s the eventreceiver looks like in code

public class PageReceiver : SPItemEventReceiver
{
    private const string LinksFieldName = "_Links";
    private string actualHtml;

    public override void ItemUpdated(SPItemEventProperties properties)
    {
        base.ItemUpdated(properties);
        this.DisableEventFiring();
        
        SPWeb web = properties.OpenWeb();
        SPList list = web.Lists[properties.ListId];

        if (!list.Fields.ContainsField(LinksFieldName))
        { 
            list.AddField("_Links", "_Links", SPFieldType.Note, false);
            SPFieldMultiLineText linksField = (SPFieldMultiLineText)list.Fields[LinksFieldName];
            linksField.RichText = true;
            linksField.RichTextMode = SPRichTextMode.FullHtml;
            linksField.RestrictedMode = false;
            linksField.Hidden = true;
            linksField.Update();
        }

        SPListItem listItem = list.GetItemById(properties.ListItemId);

        SPFile actualPage = listItem.File;


        using (SPLimitedWebPartManager limitedWebPartManager = 
            web.GetLimitedWebPartManager(actualPage.ServerRelativeUrl, 
            System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared))
        {
            for (int x = 0; x <= limitedWebPartManager.WebParts.Count - 1; x++)
            {
                if (limitedWebPartManager.WebParts[x] is CustomTelerikWebPart)
                {
                    CustomTelerikWebPart telerikWebPart = 
                        (CustomTelerikWebPart)limitedWebPartManager.WebParts[x];                        
                    actualHtml 
                        += "||" 
                        + telerikWebPart.StorageKey.ToString() 
                        + telerikWebPart.Text;
                }
            }
        }

        listItem[LinksFieldName] = actualHtml;
        listItem.SystemUpdate();

        this.EnableEventFiring();
    }
}

As you’ve probably have noticed is that I’m referring to a custom Telerik WebPart.. the reason why is that it’s quite nice to keep the stored HTML in sync (if the image is moved to another location within the sitecollection, it would be quite nice that the link is also replaced in the editor). To keep it really short, I’ve opened up Reflector looked at the webpart that Telerik has created and created my own version of it and only modified the Text property with this piece of code : 

[Browsable(false), DefaultValue(""), FriendlyName("Text"), Description("Text Property"), WebPartStorage(Storage.Personal)]
public virtual string Text
{
    get
    {
        if (Page == null)
            return (this._text ?? "");
        if (Page.IsPostBack)
            return (this._text ?? "");

        if (SPContext.Current == null)
            return (this._text ?? "");
        
        if (SPContext.Current.Item == null)
             return (this._text ?? "");
            
        SPListItem item = SPContext.Current.ListItem;
        if (!item.Fields.ContainsField("_Links"))
              return (this._text ?? "");

        string[] textValues = item["_Links"].ToString().Split(new String[] { "||" }, StringSplitOptions.None);
        foreach(string textValue in textValues)
        {
            if (string.IsNullOrEmpty(textValue))
                continue;

            string[] webpartTextValue = textValue.Split(new String[] { this.StorageKey.ToString() }, StringSplitOptions.None);
            if (webpartTextValue.Length > 1)
                return webpartTextValue[1];                        
        }
        
         return (this._text ?? "");
    }
    set
    {
        this._text = value;
    }
}

There you have it… hope you learned something.. because I sure did! :)

Comments

There are no comments for this post.
 

 Statistics

 
Views: 4249
Comments: 0
Tags:
Published:1462 Days Ago