This blog post describes how you can use the ItemNamingRules Sitecore Shared Source project to apply the rules engine to control item names using the Sitecore ASP.NET CMS.
This is a repost of http://sitecorejohn.spaces.live.com/blog/cns!960125F1D4A59952!444.entry.
You can use the rules engine to control item names. For example, you may want to enforce character limits in the names of items that correspond to URLs. For more information about the rules engine, see the Rules Engine Cookbook on the Sitecore Developer Network (SDN). For instructions to use the solution described in this post, see the ItemNamingRules documentation. You may also want to read some of my previous posts, including Sitecore Rules Engine and Conditional Rendering, Intercepting Item Updates with Sitecore, and Sitecore Shared Source NewsMover Categorizes News by Date.
Sitecore supports at least five techniques to control item names:
You can continue to use settings, events, pipelines, and validation to control item names. But with 6.1, you can use the rules engine as well.
A rule consists of one or more conditions and one or more actions. When Sitecore evaluates a rule, if the conditions specified in that rule evaluate to true, then Sitecore invokes the actions specified in that rule.
For the condition part of this rule, users might want to apply the rule to one or more specific items, to all items based on a data template, to an item and its descendants, to the descendants of an item, to items matching a query, or to some other collection of items. I assume that the default conditions that ship with the product meet the most common requirements. Two conditions that might be helpful when determining whether or not to rename an item: whether the item has layout details for the default device, and whether the item has layout details for any device.
Regarding the action part of the rule, there would be numerous parameters to specify for a single action. The solution should not force the user to specify parameters they don’t use. But because actions are not aware of each other, the user might have to specify the same parameters to multiple actions. More importantly, the user has to understand the impact of ordering actions. For example, it doesn’t make sense to ensure a minimum name length before removing invalid characters.
I thought about merging certain actions and hard-coding certain parameters, but I generally like a flexible approach. Then I started prototyping, and realized that the flexible approach was more work for both me and any user. So I merged some of the actions (the strong words indicate parameters):
It’s honestly a coincidence that this simplification eliminates all of the duplicate parameters that I would otherwise have to pass to additional actions. I also sorted the actions so that the user interface displays them in the order in which the user should add them to the rule.
I started out with a base class for actions that rename items.
Then I created classes for each action.
Before entering any parameters, my rule looks like this:
And after entering parameters:
Now, when I save any item that has layout details for any device, whether I save that item through the UI or through an API call, Sitecore replaces invalid characters with underscores, merges sequences of underscores into a single underscore, trims underscores from the beginning and the end of the item name, appends to the item name to ensure at least seven characters, appends a timestamp to the item name if required to make it unique among its siblings, and ensures that the item name does not exceed 35 characters.
Warning: Like almost any customization, this could have dangerous repercussions, especially if configured incorrectly (for example, by renaming any item in the wrong branch, or in the Core database). Additionally, I didn’t significantly test this before posting, and there are definitely still some issues to think about. For example, you probably don’t want to rename the home item, you don’t want these rules to apply to the __Standard values of data templates (even though they may contain layout details), etc. But enough people ask about this topic that I thought it was time to describe this approach. After some refinement, I will try to make this a shared source project.
Hi John, After reading your article, this handy trick seemed very easy to implement, but I'm having some problems. The new rule I created using the provided actions in ItemNamingRules don't seem to take effect when I insert a new item into sitecore/content/home. Do you know what am doing wrong? Thank you in advance, Reka
@Reka: I think the most likely cause is probably a typo in the type signature. I suggest checking the Sitecore log from around the time that you create the item - you might see something indicating that .NET cannot instantiate a designated type. If that doesn't shed any light on the issue, I would describe the issue on the Shared Source forum on SDN: sdn.sitecore.net/.../ShowForum.aspx
I was facing a similar issue where the renaming action was not being implemented. I was able to resolve this by changing the following conditional statement in RenamingAction.cs from... if ((!String.IsNullOrEmpty(site.StartItem)) && String.Compare(site.StartItem, item.Paths.FullPath, true) != 0) { return; } ...to... if ((!String.IsNullOrEmpty(site.StartItem)) && String.Compare(site.StartItem, item.Paths.FullPath, true) == 0) { return; } I believe it is only when the item is the same as the site's start item (i.e., String.Compare(...) == 0) do you want to hit the return statement.
Hi Valerie and John, Yes, this solved my problem too! But I was too embarrassed to post it here. :) Thank you for sharing and the support!
Sorry, I'm not sure how that defect got through - I tested this and even demonstrated it at dreamcore. I'm concerned that I might have checked in old code, or made some stupid changes right before checking in, so there may be other issues with the solution. That specific code was intented to avoid renaming the home item for any managed site because the web.config file references those items by name and they don't appear in URLs. I changed it to the following and performed more lightweight testing with 6.4.1 101221, which seemed to indicate that this works: if (String.Compare(site.RootPath + site.StartItem, item.Paths.FullPath, true) == 0) I realize it seems strange to put conditional logic in an action, but I didn't want to require the user to remember to select another condition to define "if the item has layout details AND it's not the home item for any managed site". I have checked updated code into the Sitecore Shared Source repository. I also updated the documentation - removed HasLayoutDetailsForSpecificDevice (that code wasn't in the repository anyway, and I've apparently deleted my local copy), moved the condition definition items from /sitecore/system/Settings/Rules/Common/Conditions to /sitecore/system/Settings/Rules/Common/Conditions/Fields (maybe 6.4.1 added this structure), updated the text for HasLayoutDetailsForDefaultDevice (it was inadvertently copied from HasLayoutDetailsForAnyDevice), and updated the regex example, which seemed to be invalid, resulting in an infinite loop. Obviously, use anything I write at your own risk...again, my apologies.
Using the rules engine in a custom context: www.sitecore.net/.../Using-the-Sitecore-Rules-Engine-in-a-Custom-Context-Setting-the-Context-Device.aspx
John, Thank you for this article, it has added another tool to my ever growing bag of Sitecore techniques. Something else I thought worthy of sharing. I've updated my local ReplaceInvalidCharacters class to accept a null value for ReplaceWith. I just needed this.ReplaceWith = this.ReplaceWith ?? string.Empty; instead of the assertion and wrapped all of the while loops in if (this.ReplaceWith!=string.Empty) This works perfect for me.
Project moved to marketplace.sitecore.net/.../Item_Naming_rules.aspx
In some cases, the Sitecore.Data.Items.ItemUtil.ProposeValidItemName() method may be of use.
Is that possible to show Content Name/Path of content in Validation result dialog window?