One issue that seems to regularly occur in Sitecore builds is pages showing $name in the content. Its the sort of problem that seems to creep into projects the further through you get.
Why does it happen?
When you setup Sitecore templates they are typically built up from several other templates eg:
- A content page is comprised from:
- Page Title field section
- Meta Data field section
- etc
Down the line you decide you want to add ‘Page Content – (Header and Body fields)’ to all pages so you setup a new Field Section template along with corresponding fields and __standard values. So that new pages automatically push the item name to the Header field, you set the __standard values to contain $name.
All this gets published and you then come to check the front end of the site – pages created before adding the Page Content field section now have the new header and body fields but the header field shows $name ๐
The reason being these fields are inheriting their value from __standard values – variables such as $name are processed when items are created. See http://adeneys.wordpress.com/2009/12/29/custom-tokens-and-nvelocity-for-item-creation/ for info on how to create custom replacement tokens. There is also more information on http://learnsitecore.cmsuniverse.net/en/Developers/Articles/2010/08/Standard-values-in-sitecore.aspx
How to solve the problem?
When items are created they are processed by a set of pipelines. An example of this is expandInitialFieldValue pipeline. Out the box, this makes use ofย the MasterVariablesReplacer.
You now have a couple options, either you can call the MasterVariablesReplacer for the problematic items or you could manually script the same functionality. The following code demonstrates the manual approach:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
<%@ Page Language="C#" AutoEventWireup="true" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ Import Namespace="Sitecore.Data.Items" %> <%@ Import Namespace="Sitecore.Data.Fields" %> <%@ Import Namespace="Sitecore.Data" %> <script runat="server"> public void FixNames_Click(object sender, EventArgs e) { //note, this doesn't error check the root guid is valid item Item root = Database.GetDatabase("master").GetItem(new ID(new Guid(RootIdTextBox.Text))); int fixes = 0; using (new Sitecore.SecurityModel.SecurityDisabler()) { fixes = RecurseAndFix(root); } ResultsLiteral.Text = String.Format("<hr />Fixed {0} cases of '$name'", fixes); } private int RecurseAndFix(Item root) { int fixes = 0; if (root.Template != null) { foreach (TemplateFieldItem field in root.Template.Fields) { Field existingField = root.Fields[field.ID]; if (existingField != null) { bool fixMe = false; if (existingField.Value.Equals("$name")) { fixMe = true; } if (existingField.ContainsStandardValue && existingField.GetStandardValue().Equals("$name")) { fixMe = true; } if (fixMe) { using (new EditContext(root)) { existingField.Value = root.Name; fixes++; } } } else { if (root.Template.StandardValues[field.ID].Equals("$name")) { using (new EditContext(root)) { root[field.ID] = root.Name; fixes++; } } } } } foreach (Item child in root.Children) { fixes += RecurseAndFix(child); } return fixes; } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>'$name' Fixer </title> </head> <body> <form id="form1" runat="server"> <p>'$name' Fixer </p> <p> Running the code on this page searches through all the content in the tree and replaces any '$name' values with the item's name. </p> <h2>Note: Be careful, only run on content nodes and children and not templates!</h2> <hr /> <div> Root id: <asp:TextBox runat="server" ID="RootIdTextBox" Width="600"></asp:TextBox> <asp:Button runat="server" OnClick="FixNames_Click" Text="Fix '$name'" /> </div> <asp:Literal runat="server" ID="ResultsLiteral" /> </form> </body> |
Its worth noting this can introduce some interesting challenges if Language Fallback is used on your site. The script above works fine for content items on a single language site. If you need similar functionality when language fallback is in place it may actually be meaningful to set $name = ” for non-primary languages. This will ensure the fallback will occur correctly, rather than finding $name and thinking it is valid content.
Thanks for the post. I didn’t check the code very closely, but couldn’t other tokens than $name appear, such as $id? Couldn’t $name appear in the middle of the field? Is there any chance that $name could appear in item names (such as those created from branch templates), or only in field values? I wonder if you could use the class specified by the MasterVariablesReplacer setting in the web.config file, maybe through the Sitecore.Configuration.Factory.GetMasterVariablesReplacer() method, to handle more cases with less code.
Cheers for the feedback! You are correct, there are other tokens which this wouldn’t catch. In previous projects I’ve never needed to use any tokens apart from $name. To catch all, the expandInitialFieldValue pipeline or the MasterVariablesReplacer would be better option.
I don’t think you could end up with $name in the name of an item after template inheritance is changed. The reason being an items name isn’t related to data stored in its template or __standard values, instead it’s setup when an item is created. Fields differ since they can be added to an item over time.
Please correct me if I’m wrong!!!! ๐
Hi there,
I’ve also experienced this issue where all our contents were displaying $name. The root cause is as follow:
1. The item does not have a version.
2. Template is version in other language.
3. Invarient Language is present in the Language list.
Thanks