Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I programmatically escape dashes in Sitecore queries using FullPath?

I'm attempting to expand upon a custom Sitecore command to determine if the current item has a parent item matching a certain template id.

I know the query should ideally be as simple as ./ancestor::*[@@templateid='{26710865-F082-4714-876B-D5E1F386792F}'] if the item is the context, or /sitecore/content/home/full/path/to/the-item/ancestor::*[@@templateid='{26710865-F082-4714-876B-D5E1F386792F}']

Unfortunately the item path includes dashes which need to be escaped like /sitecore/content/home/full/path/to/#the-item#/ancestor::*[@@templateid='{26710865-F082-4714-876B-D5E1F386792F}'].

However, ideally I'd like to just use the full path of the item, since it's available as item.Paths.FullPath.

Given an item, what's the best way to write a query containing it's full path, and escaping any dashes that might be contained within?

like image 975
James Skemp Avatar asked May 02 '16 20:05

James Skemp


2 Answers

I've never seen any Sitecore util class or any other out of the box code which does what you need. It's funny that Sitecore does have a util method which returns false (yes, it does return false) but doesn't have a method which escapes item path so it can be used in query.

You may find code which does what you want (written by Anders Laub) here: https://blog.istern.dk/2014/10/29/escaping-dashes-in-sitecore-queries-datasource-query-update/

And if you're too lazy to click the link, I've copied to code here:

private string EscapeItemNamesWithDashes(string queryPath)
{
  if (!queryPath.Contains("-"))
   return queryPath;

  var strArray = queryPath.Split(new char[] { '/' });
  for (int i = 0; i < strArray.Length; i++)
  {
    if (strArray[i].IndexOf('-') > 0)
    strArray[i] = "#" + strArray[i] + "#";
  }
  return string.Join("/", strArray);
}

If you want to escape also space character, you can try this regex:

string escapedPath = Regex.Replace(myItem.Paths.FullPath, "[^/]*[- ][^/]*", "#$0#");

Also you should remember that there are some reserved words in Sitecore which should be escaped as well. Those are (copied from http://www.newguid.net/sitecore/2012/escape-characterswords-in-a-sitecore-query/ ):

  • ancestor
  • and
  • child
  • descendant
  • div
  • false
  • following
  • mod
  • or
  • parent
  • preceding
  • self
  • true
  • xor
like image 118
Marek Musielak Avatar answered Sep 28 '22 07:09

Marek Musielak


Assumptions

Assuming you have a reference to a Sitecore.Data.Items.Item (called item) and you would like to find an ancestor with a given Sitecore.Data.ID (called id), there are a number of ways you can access the ancestor.

Using Linq

In a typical Sitecore setup without any custom libs, I would typically use a bit of Linq so as to avoid encoding issues in XPath.

ancestor =
    item
        .Axes
        .GetAncestors()
        .FirstOrDefault(ancestor => ancestor.TemplateID == id);

Using Closest

I use a bespoke framework for Sitecore development that involves a wide variety of extension methods and customized item generation. To avoid the overhead of accessing all the ancestors before filtering, I would use the Closest extension method:

public static Item Closest(this Item source, Func<Item, bool> test)
{
    Item cur;
    for (cur = source; cur != null; cur = cur.Parent)
    if (test(cur))
        break;
    return cur;
}

Accessing the ancestor would then be:

ancestor =
    item
        .Closest(ancestor => ancestor.TemplateID == id);

(Actually, I typically use code that looks like)

ancestor = (ITemplateNameItem) item.Closest(Is.Type<ITemplateNameItem>);

Using XPath

I usually avoid XPath and only use it as a tool of last resort because it often makes code harder to read, introduces encoding issues such as the one you're faced with in this question, and has a hard limit on the number of items that can be returned.

That said, Sitecore has many tools available for searching with XPath, and in some circumstances it does simplify things.

The trick to fixing item paths that contain spaces is: Don't use item paths.

Instead, you can safely use the item's ID, with no more context necessary because it's an absolute reference to the item. It's also guaranteed to follow a specific format.

var query =
    string.Format(
        "//{0}/ancestor::*[@@templateid='{1}']",
        item.ID.ToString(),
        id.ToString());
/* alternatively
var query =
    string.Format(
        "{0}/ancestor::*[@@templateid='{1}']",
        item.Paths.LongID,
        id.ToString());
*/
ancestor =
    item
        .Database
        .SelectSingleItem(query);

Using Sitecore.Data.Query.Query

As I mentioned previously, Sitecore has many tools available for searching with XPath. One of these tools is Sitecore.Data.Query.Query. The SelectItems and SelectSingleItem methods have additional optional parameters, one of which is a Sitecore.Data.Items.Item as a contextNode.

Passing an item in as the second parameter uses the item as the context for the XPath query.

var query =
    string.Format(
        "./ancestor::*[@@templateid='{0}']",
        id.ToString());
ancestor =
    Sitecore
        .Data
        .Query
        .Query
        .SelectSingleItem(query, item);
like image 24
zzzzBov Avatar answered Sep 28 '22 08:09

zzzzBov