Dmgdotnet's Blog

Sitecore and stuff

Archive for December, 2011

Link to item with no Read access

Posted by dmgdotnet on December 14, 2011

If you’ve ever restricted read access to content for authors in Sitecore you would have quickly noticed that these users can no longer view these items in any part of the system. This is to be expected however it can be a bit limiting.  The scenario I’ve recently come across is where users are trying to link to content they may not necessarily have the correct permissions to view.  In this case it was a show-stopper so we implemented what turned out to be a reasonably simple solution.

The 2 main locations users link to items are in General Link fields and the Rich Text Editor. There are many others of course however the requirements for this project were for these 2 only.  These areas of the application use xml controls located in ‘/sitecore/shell/applications/dialogs/internallink’  and ‘/sitecore/shell/Controls/Rich Text Editor/InsertLink’ respectively. Both of these files use the TreeviewEx class to render the content tree which in turn references the DataContext class that it uses to populate the tree.  It’s with this DataContext class that we will begin the customisation, first of all we need to point the DataContext to a custom DataView that we will create later.

In both files I changed this

<DataContext ID="InternalLinkDataContext" />

To this

<DataContext ID="InternalLinkDataContext" DataViewName="Link" Parameters="ignoresecurity=true"/>

Sitecore has another nice feature that I used here. There is an override folder found in /sitecore/shell/override that allows you to make modifications to these xml files without changing the actual Sitecore files. As long as the files are named the same, Sitecore will check the override folder first when initialising these controls in the client. This will allow you to make modifications without having to worry about losing the changes during an upgrade.

Next we need to create the DataView class that our DataContext is referencing. There are a few of these already defined in the <dataviews> section of our web.config. These views allow us to pass parameters, in this case I’m going to use the parameter we added to our DataContext above to determine if we should ignore security when fetching items for the view.

public class LinkDataView : MasterDataView
{
  private bool _ignoreSecurity;

  public override void Initialize(string parameters)
  {
    base.Initialize(parameters);
    var str = new UrlString(parameters);
    bool.TryParse(StringUtil.GetString(new[] { str["ignoresecurity"] }), out _ignoreSecurity);
  }

We then need to override 4 methods to check for our security bool, if true we want to ignore security.

protected override void GetChildItems(ItemCollection items, Item item)
{
  Error.AssertObject(items, "items");
  if (item == null) return;

  ChildList children;
  if (!Settings.ContentEditor.CheckSecurityOnTreeNodes || _ignoreSecurity)
  {
    children = item.GetChildren(ChildListOptions.IgnoreSecurity);
  }
  else
  {
    children = item.Children;
  }
  items.AddRange(children.ToArray());

}

protected override Item GetItemFromID(string id, Language language, Version version)
{
  if (_ignoreSecurity)
  {
    using (new Sitecore.SecurityModel.SecurityDisabler())
    {
      return base.GetItemFromID(id, language, version);
    }
  }

  return base.GetItemFromID(id, language, version);
}

public override bool HasChildren(Item item, string filter)
{
  Sitecore.SecurityModel.SecurityCheck enable;
  Assert.ArgumentNotNull(item, "item");
  Assert.ArgumentNotNull(filter, "filter");
  if (filter.Length != 0)
  {
    return (GetChildren(item, string.Empty, true, 0, 0, filter).Count > 0);
  }
  if (!Settings.ContentEditor.CheckHasChildrenOnTreeNodes)
  {
    return true;
  }
  if (Settings.ContentEditor.CheckSecurityOnTreeNodes && !_ignoreSecurity)
  {
    enable = Sitecore.SecurityModel.SecurityCheck.Enable;
  }
  else
  {
    enable = Sitecore.SecurityModel.SecurityCheck.Disable;
  }
  return ItemManager.HasChildren(item, enable);
}

protected override Item GetParentItem(Item item)

{
  if (item != null)
  {
    var enable = _ignoreSecurity ? Sitecore.SecurityModel.SecurityCheck.Disable : Sitecore.SecurityModel.SecurityCheck.Enable;
    return ItemManager.GetParent(item, enable);
  }
  return null;
}

We now need to get this class into our configuration. This is easily achieved using an include file in /app_config/include which again will help us in the future during upgrades.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <dataviews>
      <dataview name="Link" assembly="Sitecore.Custom" type="Sitecore.Custom.Component.LinkDataView" Parameters="" />
    </dataviews>
  </sitecore>
</configuration>

The last piece to our puzzle is to do with the TreeviewEx control itself. After a bit of digging around I discovered that when you click the ‘+’ button to expand the tree, an Ajax call is made to a webform to retrieve the children of the section you are expanding. In this case it is a core Sitecore file that needs to be changed to get our module working; not ideal for upgrading but the change works. I created a new class to become the codebehind for this file:

public class TreeviewExPage : Page
{
  // Methods
  protected void Page_Load(object sender, EventArgs e)
  {
    Assert.ArgumentNotNull(sender, "sender");
    Assert.ArgumentNotNull(e, "e");
    var child = new TreeviewEx();
    Controls.Add(child);
    child.ID = WebUtil.GetQueryString("treeid");
    var queryString = WebUtil.GetQueryString("db", Client.ContentDatabase.Name);
    var database = Factory.GetDatabase(queryString);
    Assert.IsNotNull(database, queryString);
    var itemId = ShortID.DecodeID(WebUtil.GetQueryString("id"));
    Item item;
    using(new Sitecore.SecurityModel.SecurityDisabler())
    {
      item = database.GetItem(itemId);
    }
    if (item != null)
    {
      child.ParentItem = item;
    }
  }
}

To use this we just need to update the inherits attribute of our aspx page being used for the Ajax call found in /sitecore/shell/Controls/TreeviewEx/TreeviewEx.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TreeviewEx.aspx.cs" Inherits="Sitecore.Custom.Component.TreeviewExPage" %>

Robert’s your fathers brother

Posted in Sitecore | Tagged: | Leave a Comment »