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.