| John's profileJohn West Blogs about Si...PhotosBlogLists | Help |
|
10/31/2008 C#, .NET, and ASP.NET Coding Standards and Best PracticesI just came across this resource on C# coding standards, which covers some general .NET and ASP.NET best practices: http://weblogs.asp.net/lhunt/pages/CSharp-Coding-Standards-document.aspx Like anything on this subject, this contains some opinion, but it also contains enough useful tips to be worth sharing. 10/20/2008 Editing Controls Do Not Appear in the Sitecore Page EditorThere have been a few forum posts and support cases on this issue recently so I thought it might help to blog about it. If you have deleted the ASP.NET global <form> element from your layout, when you access a content item that uses that layout using the Page Editor, the editing controls do not appear. To correct this issue, re-introduce the <form> element into your layout. The default <form> element used by Sitecore is as follows: <form method="post" runat="server" id="mainform">Insert your controls here.</form> The opening <form> element generally occurs directly after the opening <body> element, and the closing </form> element generally occurs immediately before the closing </body> element, surrounding all of your controls. Visual Studio Debugger Hangs Debugging a Sitecore SolutionI am surprised there are not more forum posts and support cases on this issue, which appears to be an annoying bug in Visual Studio (possibly only 2008). If you have selected the Show All Files toggle in Solution Explorer, and you try to debug by attaching to the ASP.NET worker process, Visual Studio stops responding. To correct this issue, restart Visual Studio, clear the Show All Files toggle, and debug again. Importing and Updating Data with SitecoreWeb Data Source (http://www.webdatasource.com) has published a blog entry of mine, "Importing and Updating Data with Sitecore". You can read it here: http://www.webdatasource.com/2008/10/importing-and-updating-data-with-sitecore/ 10/18/2008 Sorting Sitecore Items by a Field ValueSitecore provides a variety of techniques to allow users and developers to control item sorting. By default, Sitecore sorts the children of every item in alphabetical order by name. Users can sort items in the content tree. For each item, you can override this default by clicking the dialog launcher in the Sort group on the Home tab, and then selecting a sort rule. Sitecore presents items in XML document order - from the top item in the CMS repository down. This order reflects the child sort rule for each item. In an XSL rendering, you can alter the default item order using the <xsl:sort> XSL element. For example, to sort the children of the context item by the date that they were last updated:
<ul>
<xsl:for-each select="$sc_currentitem/item">
<xsl:sort select="sc:fld('__updated',.)" />
<li>
<sc:link>
<xsl:value-of select="@name" />
</sc:link>
</li>
</xsl:for-each>
</ul>
In .NET, you can sort an array of Sitecore.Data.Items.Item objects. One important difference between these two approaches is that in XML, you sort a structure in the XML repository. In .NET, you sort a flat array containing any number of items. To demonstrate this difference, consider a TreelistEx field (or a Checklist field, or a Multilist field, or a Treelist field - any field that stores its value as a pipe-separated list of GUIDs). You could use a TreelistEx field to let the user select some number of related content items, and use a presentation component to link to each of those items. In most cases you want to process the selected items in the order that the user specified them in the field, but in some cases you want to sort them using logic. In .NET you could sort this list of items by almost any criteria, for example the value of a field in each item, by implementing a System.Collections.Generic.IComparer<Sitecore.Data.Items.Item>.
Because the value of a TreelistEx field is a string of GUIDs rather than an XML document, XSL does not provide native facilities to sort this data. This is why Sitecore provides sc:Split() XSL extension method for processing fields that contain a list of pipe-separated GUIDs. The sc:Split() method accepts an item and the name of the field, and returns the IDs as temporary XML structure for easier parsing from XSL. While it might be possible to sort the items referenced by the XML structure returned from sc:Split() using pure XSL, it probably wouldn't be very efficient to write or execute that code, and especially might not be easy for someone else to read. This is probably an appropriate place to use a .NET XSL extension. For more information about XSL extension methods, see the Presentation Component XSL Reference on the Sitecore Developer Network (http://sdn.sitecore.net). To sort the items specified by a TreelistEx field by a field value in each of those items, we need an XSL extension method that accepts three parameters: the item containing the TreelistEx field, a string containing the name of the TreelistEx field, and a string containing the name of the field by which to sort the items. We might want a fourth parameter to reverse the sort order. Because we will be passing the XML representation of an item from the XSL transformation engine to the XSL extension method, our class will inherit from Sitecore.Xml.Xsl.XslHelper so that we can use its GetItem() method, which retrieves the Sitecore.Data.Items.Item for use by .NET code that corresponds to the System.Xml.XPath.XPathNodeIterator() representation of the item that XSL uses. The rest of the .NET code should be pretty straightforward, though maybe it isn't well tested. The little mess at the end of the SortListField() method implementation could be avoided if the GetChildIterator() method of Sitecore.Xml.Xsl.XslHelper was not private. Because Sitecore stores dates in the ISO format corresponding to the .NET format string yyyyMMddTHHmmss, it's probably not necessary to convert these values to dates, but it shouldn't cost much.
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.XPath;
using Sitecore.Data.Items;
namespace Namespace.Xml.Xsl
{
public class XslHelper : Sitecore.Xml.Xsl.XslHelper
{
public virtual XPathNodeIterator SortListField(string listField,
string sortField, XPathNodeIterator ni)
{
return SortListField(listField, sortField, ni, false);
}
public virtual XPathNodeIterator SortListField(string listField,
string sortField, XPathNodeIterator ni, bool reverse)
{
Sitecore.Xml.Packet packet = new Sitecore.Xml.Packet("values");
XPathNodeIterator iterator = ni.Clone();
if(iterator.MoveNext())
{
Sitecore.Data.Items.Item item = GetItem(iterator);
if (item != null)
{
Sitecore.Data.Fields.MultilistField fList = item.Fields[listField];
if (fList!= null)
{
List<Item> itemList = new List<Item>();
foreach(Sitecore.Data.Items.Item pointer in fList.GetItems())
{
if( pointer != null )
{
itemList.Add(pointer);
}
}
Item[] items = itemList.ToArray();
Array.Sort(items, new ItemFieldComparer(sortField));
if ( reverse )
{
Array.Reverse(items);
}
foreach (Sitecore.Data.Items.Item reference in items)
{
packet.AddElement("value", reference.ID.ToString());
}
}
}
}
XPathNavigator navigator = packet.XmlDocument.CreateNavigator()
?? new XmlDocument().CreateNavigator();
navigator.MoveToRoot();
navigator.MoveToFirstChild();
return navigator.SelectChildren(XPathNodeType.Element);
}
}
public class ItemFieldComparer : IComparer<Item>
{
private const int Y_FIRST = -1;
private const int X_EQUALTO_Y = 0;
private const int X_FIRST = 1;
private readonly string _fieldName = null;
public ItemFieldComparer(string fieldName)
{
_fieldName = fieldName;
}
public int Compare(Item x, Item y)
{
if (x == null && y == null)
return X_EQUALTO_Y;
if (y == null)
return X_FIRST;
if (x == null)
return Y_FIRST;
Sitecore.Data.Fields.Field xField = x.Fields[_fieldName];
Sitecore.Data.Fields.Field yField = y.Fields[_fieldName];
if (yField == null && xField == null)
return X_EQUALTO_Y;
if (xField != null
&& (yField == null || String.IsNullOrEmpty(yField.Value))
&& !String.IsNullOrEmpty(xField.Value))
return X_FIRST;
if (yField != null &&
(xField == null || String.IsNullOrEmpty(xField.Value))
&& !String.IsNullOrEmpty(yField.Value))
return Y_FIRST;
if (yField.Value == xField.Value)
return X_EQUALTO_Y;
string fieldType = xField.Type.ToLower();
if (xField.ID == Sitecore.FieldIDs.Sortorder)
fieldType = "integer";
switch (fieldType)
{
case "date":
case "datetime":
return
((Sitecore.Data.Fields.DateField)xField).DateTime.CompareTo(
((Sitecore.Data.Fields.DateField)yField).DateTime);
case "integer":
return Int32.Parse(xField.Value).CompareTo(Int32.Parse(yField.Value));
default:
return xField.Value.CompareTo(yField.Value);
}
}
}
}
This code returns an XML structure something like: <values> <value>GUID</value> ... <value>GUID</value> </values> You can update the default sc namespace to include this method by changing the <extension> element in web.config with namespace http://www.sitecore.net/sc: <extension mode="on" type="Namespace.Xml.Xsl.XslHelper, Assembly" namespace="http://www.sitecore.net/sc" singleInstance="true" /> Then you can then invoke the XSL extension method from XSL renderings as follows, for example to sort the items selected in the field named TreelistExField in the context item by the the value of the field named SortField in each item:
<ul>
<xsl:for-each select="sc:SortListField('TreelistExField','SortField',$sc_currentitem)">
<xsl:for-each select="sc:item(text(),$sc_currentitem)">
<li>
<sc:link>
<xsl:value-of select="@name" />
</sc:link>
</li>
</xsl:for-each>
</xsl:for-each>
</ul>
You can reverse the sort order by passing True as the fourth parameter:
<xsl:for-each select="sc:SortListField('TreelistExField','SortField',$sc_currentitem,true())">
The outer for-each iterates over each of the <value> elements in the XML structure. The inner for-each iterates over the item that corresponds to the GUID within that <value> element. It might seem unusual to for-each over a single element, but actually makes the code more clear by setting the context item within the nested for-each to the referenced item rather than using a variable. Sitecore 6 DocumentationMaybe not everyone knows that Sitecore has released a great deal of documentation for Sitecore 6 on the Sitecore Developer Network (SDN - http://sdn.sitecore.net). In general, the references describe concepts, while the cookbooks describe implementation. For developers, a good place to start is the Sitecore 6 Resources page. I think the best new resources include:
You can download the entire Sitecore 6 API as a Windows Help file. And you can download the Documentation Package containing most of these files. Additionally, I am trying to remember to monitor the Release Notes. You can access information about the various optional Sitecore modules from the SDN Products Page. So far, the feedback on these documents has been quite positive. Use the email link in the footer on SDN to email the Sitecore documentation team with questions, corrections, and suggestions. I will also maintain a list of links to blog posts, forum threads, and other resources that address common issues:
Specific to XSL:
Not really documentation, but some Sitecore Shared Source projects I access frequently:
10/17/2008 Sitecore 6 Link ManagementThe Sitecore 6 Web Content Management System (WCM or CMS) provides new link management features to prevent broken links and images, which detract from the user experience and can affect search engine ranking. A typical HTML Web page may consist of a number of references to other resources, which may or may not exist, possibly on a different Web server. These elements including anchor (<a>) elements that represent links and <img> elements that reference the URLs of images. In addition to anchor and image elements in content maintained by CMS users, presentation components such as Web controls generate links, images, and references to other resources dynamically, such as to provide data-driven navigation. Sitecore can only manage links in the CMS; developers are responsible for the references generated by the presentation components they develop. Any of these elements can reference a resource that does not exist. Broken references in CMS content typically result from:
You can use validation to trap broken external links while the user edits content, or periodically generate a report of broken external links. The CMS can help to manage internal links when a user moves, renames, or deletes a resource in the CMS repository. When a user deletes a resource referenced by another resource, Sitecore prompts the user what to do with those references: break them, remove them, or update them to reference another resource. Sitecore automatically assigns each resource in the CMS repository both a friendly URL and a Global Unique Identifier (GUID). Resources reference other resources using GUIDs instead of paths whenever possible. When a user renames or moves a resource, no other data changes, as the GUID does not change. Sitecore does not need to prompt the user in this condition, as references by GUID do not break. Presentation components transform these GUID references into friendly URLs sent in the response to the Web client. For example, the raw value of a Rich Text Editor (RTE) field containing an image appears as follows: <img src="/~/media/3C6C861508D44B899DE08DC9B84AFF13.ashx?w=200&h=230&as=1" width="200" height="230" alt=""/ >The raw value of a RTE field containing a link to another content item appears as follows: <a href="~/link.aspx?_id=0B16893D260041838FAD668AAA551E21&_z=z">Click here</a>The raw value of an RTE field containing a link to a media item appears as follows: <a href="~/media/87A923D5B58641FEB1949F9ED79E79C3.ashx">Click here</a> XSL extension controls such as <sc:html>, <sc:link>, and <sc:image> automatically transform all GUID references to friendly paths. The sc:field() XSL extension method transforms GUID references to friendly paths, while the sc:fld() extension method returns the raw field value, which may include GUID references. Sitecore .NET developers use various methods to transform GUID references to friendly URLs. The static method Sitecore.Links.LinkManager.GetItemUrl(Sitecore.Data.Items.Item) returns the friendly URL of a content item. For example, to retrieve the URL of the content item requested by the browser: Sitecore.Data.Items.Item item = Sitecore.Context.Item; string url = Sitecore.Links.LinkManager.GetItemUrl(item); The static method Sitecore.Resources.Media.MediaManager.GetMediaUrl(Sitecore.Data.Items.MediaItem) returns the friendly URL of a media item.
For example, to generate a series of <img> elements based on the images selected in the TreelistEx field named FieldName:
Sitecore.Data.Items.Item item = Sitecore.Context.Item;
Sitecore.Data.Fields.MultilistField images = item.Fields["FieldName"];
foreach (Sitecore.Data.Items.Item pointer in images.GetItems())
{
Sitecore.Data.Items.MediaItem media
= new Sitecore.Data.Items.MediaItem(item);
string url = Sitecore.StringUtil.EnsurePrefix('/',
Sitecore.Resources.Media.MediaManager.GetMediaUrl(media));
}
Note that Sitecore does not include the leading slash character ("/"),resulting in URLs that can exceed browser, Web server, or operating system limits for path lengths. Always ensure media URLs sent to the browser begin with the slash character. The static method Sitecore.Links.LinkManager.ExpandDynamicLinks(string) transforms GUID references in RTE field values to friendly URLs. For example, to transform all references in the field named text in the context item: Sitecore.Data.Items.Item item = Sitecore.Context.Item; string before = item.Fields[FieldName].Value; string after = Sitecore.Links.LinkManager.ExpandDynamicLinks(before); To configure how Sitecore generates friendly URLs, edit the attributes of the /configuration/sitecore/linkManager element in web.config. The comments above this element document the purpose of each attribute. For example, to cause content item URLs for a multilingual site to always include the language code (/lang/page.aspx), set languageEmbedding to always. You could also configure this element to use a class other than the default Sitecore.Links.LinkProvider to generate URLs. 10/9/2008 Sysinternals PlugOne of my coworkers sent my team a link to Sysinternals ZoomIt, and something else had indicated that Microsoft has updated some of these tools recently, so this is probably worth a blog post. Sysinternals covers several great utilities. I think it's best to get the suite: http://technet.microsoft.com/en-us/sysinternals/0e18b180-9b7a-4c49-8120-c47c5a693683.aspx Of these, the utilities I have seen most useful for working with ASP.NET and other applications on Microsoft Windows include:
|
|
|