Articles by " Mark Ursino"

Sitecore Managed Sites as Virtual Folders

Apr 26, 2013 by     No Comments    Posted under: Sitecore

Sitecore’s native ability to manage multiple web properties is a highly-leveraged feature of the CMS. It supports this via different tree nodes and specific configuration to delineate among sites. This blog post covers several important considerations when managing sites with virtual folders.

Managing Multiple Sites in General

Sitecore supports multiple logical sites by having separate root paths in the content tree:

sites1

In addition to this, XML configuration in the <sites> section (preferably via the SiteDefinition.config patch) defines what sites map various host names to root content paths. Sites also define a context language, database, etc.

sites2

How Sitecore Resolves a Site

Sitecore resolves the correct site via a pipeline process in the httpRequestBegin pipeline. The SiteResolver sets the context site based on the following criteria:

  • Hostname
  • Virtual path
  • Port number

Sitecore processes the XML in sequence to find the first site that matches the above criteria. Since each site has a hostName attribute defined, Sitecore first matches against this. Next comes the virtualFolder which by default is simply “/” but may be changed.

How to Configure a Sitecore Site with a Virtual Folder

To configure a site as a virtual folder, define the virtualFolder path AND physicalFolder path to match the expected path.

sites3

If you do no define the physicalFolder in addition to the virtualFolder Sitecore may either not resolve the site correctly or may throw a YSOD saying a path is null:

sites4

Note: if you have configured the Link Provider to use the Display Name for URLs, these virtual and physical paths must actually match the display name, not the item name.

Additionally, you must define virtual folder sites above any non-virtual folder sites that share the same hostName. This is because Sitecore resolves the sites in sequence based on the configuration so you need more specific combinations of criteria first (e.g. host and path).

Sitecore Item and Field Names

Feb 7, 2013 by     2 Comments    Posted under: Sitecore

Sitecore’s constructs of item names and display names is useful for managing content and delivering content to end users in user-friendly ways. They can also be used to change the perceived names of template fields for a better user experience. John West recently published a blog post about the topic of Display Names, however I was already in the process of writing up this post, so here it is anyways. Read to on learn more about item names, display names, and how you can easily change the text labels for data template field names.

Item Names

Sitecore item names are — as you would expect — the names assigned to each Sitecore item. The names appear in the content tree and additionally translate to the resolved URL of the item by default.

item-name

item-url

You can determine the URL of an item by looking at the item’s path relative to the site’s start item:

item-path

Its typically best practice to name page items with hyphens between words for friendly URLs, instead of using spaces. Though this good for end users, it requires these item names in the tree to include hyphen as well, which can get annoying too. Luckily, Sitecore has a notion of another naming construct, display names.

Item Display Names

An item display name by default simply matches the item name, that is, until you fill in a value. Setting an item’s display name value will change the perceived name of the item in the content tree, but will preserve the original item name (likely with hyphens) for URLs. This allows the best of both worlds: friendly hyphenated URLs, and easy to read names in the content tree. It can also be automated.

item-and-display-names

Display Names and Language Specific URLs

Another nice advantage of being able to edit display names is that it allows a unique display name per language variant of an item. This is useful if an item has one perceived name in the tree for English and a separate name when looking at Spanish. This can be further used to actually render language-specific URLs based on the display name. By default, the Link Manager uses the item name to generate URLs, but you can simply swap the useDisplayName attribute to true if you want language-specific URLs on a multi-lingual site:

<add name="sitecore" type="Sitecore.Links.LinkProvider, Sitecore.Kernel" addAspxExtension="true" alwaysIncludeServerUrl="false" encodeNames="true" languageEmbedding="asNeeded" languageLocation="filePath" lowercaseUrls="false" shortenUrls="true" useDisplayName="true" />

Changing the Perceived Name of a Field

Though item names and display names are useful for regular content items, individual data template fields have their own ways to customize their names. There are two main ways to change the perceived name of a field:

  1. Change the “Title” field on the field item
  2. Fill in the Short Description on the field item

The most common way in the first approach — to set the Title field. In fact, most built-in fields do this as they typically start with “__” by the titles are changed to be more user-friendly. You can see this on the “__renderings” field which actually appears to be called “Renderings”:

field-title

The other approach is to set the Short Description on the field item. As you can see, by default the field name renders on the item:

sample-field

First, navigate to Configure > Help on the field item itself:

field-help-settings

Next, fill in the Short Description text which will render after the original field name:

field-tooltip

As you can see, this can be quite useful to help content editors with instructional text without changing the actual field name.

updated-item-field

Sitecore Avanced Database Crawler Occasionally Provides Null Results

Jan 20, 2013 by     2 Comments    Posted under: Sitecore

If you’ve ever used the Advanced Database Crawler as your toolkit to do some sophisticated searching and querying for Sitecore content, you may have noticed that sometimes your results may include null/empty results. This is a common issue that I’ve faced many times and I’ve tracked it down to the de-coupled nature of the search index and publishing to the Sitecore database. This blog post explains the issue and how you can resolve it.

The crux of this issue it that the process of rebuilding the index is not directly sync’d or timed to the publishing process. Alex Shyba has discussed this before when syncing your HTML cache clearer to the index build. Additionally, if you have an index that uses the master database or a location in the master database, it will incrementally edit the index frequently.

If you look at the SkinnyItem.cs class, there’s a GetItem() method to convert it into an Item. You can see it uses the database to get the item by its ID, language, and version number. Its possible that when you publish from master to web, you are publishing a new version # of an existing item and thus the new version exists in the web DB, but the index is not updated and references the old version. So, this GetItem() call would use the previous version # and the item would be null. One way to fix this is instead of calling that GetItem() method, just use your own code to get the latest version of that item from Sitecore, e.g.

Item item = Sitecore.Context.Database.GetItem(someSkinnyItem.ItemID);

Instead of

Item item = someSkinnyItem.GetItem();

If you have any other tips and tricks when using the Advanced Database Crawler and Lucene.NET with Sitecore, feel free to share in the comments!

Sitecore Search by Site with the Advanced Database Crawler

Dec 17, 2012 by     2 Comments    Posted under: Sitecore

Many Sitecore developers these days use the Advanced Database Crawler (ADC) as their interface into the Lucene.NET world. The ADC is a great tool because it builds on top of the Sitecore.Search namespace which in its own right wraps over Lucene.NET. Many Sitecore instances will contain multiple sites and share data across them. Sometimes its necessary to build a site-specific search mechanism that functionally works the same for each site, but ensures results are only for the given site. This blog post will go over two simple ways to accomplish this with the Advanced Database Crawler.

Location Filter

The easiest way to accomplish this task to filter results by a managed site is to use the LocationIds filter on the search parameter object. This location filter will only return SkinnyItem results that fall at or under the provided location. The LocationIds also happens to be a delimited list of GUIDs, so we can easily leverage this to filter results by site:

  1. Get the context site path
  2. Filter with the ADC using LocationIds by passing the home page’s or site root’s GUID

For example, here’s some basic code to do just that:


Item homeItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath);

if(homeItem != null)
{
  searchParams.LocationIds = homeItem.ID.ToString();
}

This example assumes your home page is below the site root path, which is common if you have some other data items for your site that are not pages.

Full Path Dynamic Field

Another way to do the same type of operation takes a bit more work and doesn’t necessarily yield any better results, however it’s good to have options! This approach requires to you define a dynamic field in your index for the full path of each item. If you’re using v1 of the ADC, this exists as the “_fullcontentpath” dynamic field. If you’re using the ADC v2, you’ll need to define it (grab it from here).

Once you configure that dynamic field, simply write some code to compare the skinny items from a search operation based on the full path vs. the context site’s start path. Here’s an example:


public static IEnumerable<SkinnyItem> FilterSkinnyItemsByContextSite(IEnumerable<SkinnyItem> items)
{
    return FilterSkinnyItemsBySite(items, Sitecore.Context.Site);
}

public static IEnumerable<SkinnyItem> FilterSkinnyItemsBySite(IEnumerable<SkinnyItem> items, Sitecore.Sites.SiteContext site)
{
    return FilterSkinnyItemsByRootPath(items, site.RootPath);
}

public static IEnumerable<SkinnyItem> FilterSkinnyItemsByRootPath(IEnumerable<SkinnyItem> items, string siteRootPath)
{
    
    // isolate the path query to a finite item path, not a prefix of a longer path
    // e.g. ensures a filter on /sitecore/content/brand as /sitecore/content/brand/ to avoid allowing /sitecore/content/brand2
    if (!siteRootPath.EndsWith("/"))
        siteRootPath = siteRootPath + "/";
    
    return items.Where(si => si.Fields["_fullcontentpath"].StartsWith(siteRootPath, StringComparison.InvariantCultureIgnoreCase));
}

As I said before, leveraging the LocationIds filter at the search-level is easier and more efficient as it won’t return unnecessary results. The second approach is good if you have existing search code that you don’t want to adjust too much and instead want to easily filter the results by site.

Sitecore Packager Error: Root Element is Missing

Jun 18, 2012 by     1 Comment     Posted under: Sitecore

I was recently installing and configuring an existing Sitecore 6.2 solution to do some development. When it came time to package up new templates, media, and content items, Sitecore threw an annoying packager error: “Package generation failed: Root element is missing..” Read on to learn how to fix this issue.

First, here’s a screenshot of the issue in the packager:

My first plan of attack was to check out the Sitecore logs to see what was going on. Surely I would find some sort of stack trace. My problem was that my logs were not being created! Enter my first problem…

Sitecore Logs Will Not Generate

As of late, I’ve been doing a lot of projects built on Sitecore 6.5, so going back to this Sitecore 6.2 instance threw this curve ball. I’m a major proponent of not even touching the standard web.config when you start a new project. I push everyone to adjust configurations with patch include config files instead. The obvious most-used one would be DataFolder.config. When I had setup this Sitecore instance, by nature I set the dataPath in the DataFolder.config. Now, when my logs weren’t being generated, I confirmed the data path was correct. Not only was it set correctly in the patch config, but a quick visit to /sitecore/admin/showconfig.aspx (one of those great admin pages) also rendered the data path that I expected. Perplexed, I decided to overcompensate and I gave the Everyone role on my local machine full read and write access to the data folder, just in case it was a file I/O error. Once again, no logs. Eventually, I found my way to Alex Shyba’s blog and decided to use his technique with Process Monitor to figure out what was going on at the file I/O level. I finally determined that Sitecore was actually writing out logs to the file system, but not to my data path! Well, actually it was my data path… my other data path. It turns out, even though the DataFolder.config file set the data path which I confirmed via my license file and the showconfig.aspx audit, Sitecore was writing logs to the data folder path set in the actual web.config file, which was a separate path on my machine. So, I finally corrected the issue by using the real data path directly in the web.config. Once that change was made, my logs showed up where I expected them. This however still did not solve my packager issue.

Enabling Access to the Windows Temp Folder

Since I was able to finally get Sitecore logs to write to the data folder, I analyzed them and found the main culprit to my packager woes. The application was trying to access the OS temp folder, c:\windows\temp\. The exact lines from the log were:

ManagedPoolThread #4 11:41:47 INFO  Job started: BuildPackage
ManagedPoolThread #4 11:41:47 ERROR OpenFileStream failed: C:\Windows\TEMP\tmp4F5D.tmp

Access to the temp folder was also a point in the blog post by Alex Shyba as well. I went ahead and gave the app pool identity read/write access to the folder. In my case, that didn’t fix the issue and I needed to give Everyone read/write access. From there, Sitecore’s packager was able to generate a package. It appears that in the process of generating the zip, it needs to access the temp folder during the creating process. I hope this helps anyone else with similar issues.

Custom Sitecore Clones Listing Ribbon Button

Jun 11, 2012 by     3 Comments    Posted under: Sitecore

This blog post is about a recent customization I made to the Sitecore content editor to easily show a listing of clones of an item. Part of what inspired me to build this is recent research I’ve been doing on significant clone use, as well as a recent Stack Overflow question, Can Sitecore Clones Become More Automatic. Essentially, I’ve built a custom dropdown button on the Configure tab which mimics the Links dropdown, except this one only shows Clones.

Creating Clones

First, let’s see how we create a clone. Find a content item that you want to clone, such as a shared data item:

On the Configure tab, click the Clone button to clone it:

Select a parent item for the new clone item:

Now you’ll see the clone item in the tree, separate from the original source item:

When you select the clone, you can see it was created from the source item:

The New Clones Dropdown

Now, when you go back to the source item, the new custom Clones dropdown will show all of the clones items created from that item. This can be an easy way to audit how many clones exists and where, for a given source item:

The Sitecore Package

This is available as a simple Sitecore package, just install it, no configuration needed: Clones Listing Ribbon Button Package (ZIP)

To uninstall the package:

  • Delete the file /bin/Sitecore.Sharedsource.ClonesListing.dll
  • Delete the folder /sitecore/shell/Applications/Content Manager/Galleries/Clones
  • Delete the item in the CORE database: /sitecore/content/Applications/Content Editor/Ribbons/Chunks/Item Clones/Clones

Source Code

The source code is available on Sitecore’s Shared Source directory: http://trac.sitecore.net/CustomClonesListing/

Sitecore Front-End Development Best Practices

May 31, 2012 by     6 Comments    Posted under: Sitecore

This article is intended to be a general guide on front-end development best practices for Sitecore solutions. There are a number of practices I’ve learned over the years that apply either because if the use of ASP.NET, or specifically because of how Sitecore operates. The best way to marry good front-end development with a Sitecore solution is for the front-end developer and Sitecore developer to communicate on the requirements. By knowing how pages will be built using modular Sitecore presentation components, a front-end developer can tailor HTML structure to meet the needs of content editors.

Use a `first` CSS Class Instead of a `last` CSS Class on Repeating Elements

When developing repeatable items, such as lists (e.g. ordered or unordered lists) or repetitive blocks (e.g. a consistently repeated set of divs), use a “first” class instead of a “last” class. This is because repetitive items can be developed in a Repeater (<asp:Repeater ...>) and its easier to see if you’re on the first item of a collection than the last item.

For example, say your repeater is bound to an IEnumerable<Item>. Applying the first class in the ItemDataBound method would be as simple as:

if(e.Item.ItemIndex == 0)
    // code to add "first" class

This could then yield:

<ul class="main-nav">
	<li class="first">Home</li>
	<li>Services</li>
	<li>Products</li>
	<li>Contact</li>
</ul>

Doing the opposite with a “last” class would be more expensive because you’d need to determine the count of the collection which can be expensive and compare the current item to that:

if(e.Item.ItemIndex == dataSourceCollection.Count())
    // code to add "last" class

That code could yield something like:

<ul class="main-nav">
	<li>Home</li>
	<li>Services</li>
	<li>Products</li>
	<li class="last">Contact</li>
</ul>

Use Classes Instead of IDs for CSS

Since ASP.NET controls use IDs, the rendered ID of an HTML element may get mangled by .NET’s Web Forms. For example, a Panel like this:

<asp:Panel ID="pnlLogin" runat="server">
  Login form here
</asp:Panel>

May get rendered to the front-end as something like this:

<div id="ctl00_pnlLogin">Login form here</div>

Instead of writing CSS to rely on the ID, which could change, its better to use CSS classes, even for elements that only occur once. This will guarantee the CSS will always apply.

Keep jQuery in an Isolated Scope

Some of the older versions of Sitecore ship with the Prototype JavaScript library in the global scope of the DOM using the $ variable. If you use jQuery, you likely conflict with Sitecore’s use of Prototype when in Page Edit mode. To always ensure you’re not writing jQuery that can break Sitecore’s page edit ribbon, either wrap your code in a self-executing anonymous function like so:

(function($){ //
  $(function(){
    // the DOM is ready, do jQuery stuff
  });
})(jQuery);

Or at the very minimum, make your outer functions use the verbose jQuery function:

jQuery(function($){
  // the DOM is ready, we can use $ in here now
})

Develop HTML and CSS so Modular Components Can Be Moved Around on Pages

It’s important for front-end engineers to understand the modularity of Sitecore. Each individual component or block on the page should be self contained, meaning the CSS and JS should only scope to the structure of that component. This will allow CMS users to easily move components around without them breaking based on their location on the page.

For example, if you’re defining a basic CTA module that is re-usable, this CSS would be bad:

.two-column-layout .sidebar .cta-module .title
{
  /* CSS here */
}

The CSS is too specific and assumes the module is within a certain part of a layout. Instead, de-couple the component from the overall layout of where in the page it will be; make it more self-contained and stand-alone:

.cta-module .title
{
  /* CSS here */
}

There are front-end development frameworks available that can help with this scaffolding too, such as Twitter’s Bootstrap.

Avoid Additional HTML in <p> Tags

Sitecore’s Rich Text Field renders paragraphs automatically in <p> tags. Try to not develop HTML that expects a specific structure within a <p> tag. For example, this is probably going to be hard for content editors unless you define Rich Text Editor snippets:


Lorem <span class="someClass">ipsum</span> dolor sit amet

On the other hand, the following example is fine because these are generic tags that the rich text editor will expose anyway:


Lorem ipsum <strong>doler</strong> sit <em>amet</em>.

The first approach is possible, however it requires additional configuration work on a developer to expose the extra class in the rich text editor.

Avoid Front-Loading HTML for AJAX Functionality if Possible

Sometimes when AJAX-like functionality is desired, there may be an inclination to front-load all of the content on the page, then hide content until it is needed. This can work in some cases, but its important to understand how the content may scale. You may have a small amount of content at first, so it will render fine, but as editors build up more content, this could really slow down the page loads on the front-end and cause flickers. Instead, its important for front-end and Sitecore developers to communicate an approach. For example, Sitecore can easily deliver content to JavaScript in various forms:

  • A Web Service (custom ASMX)
  • An AJAX-like presentation device, e.g. ajax=1
  • A Web Form (custom ASPX; non-layout)
  • An HTTP handler (custom ASHX)

The important part is to consider how content in Sitecore will scale and be delivered to the front-end, hopefully in batches.

Your Best Practices?

Do you have any front-end development best practices that I didn’t list? Of course you do! For any Sitecore developers out there, what other things have you noticed that make development much easier for you on the front-end? Leave your ideas in the comments below.

Getting the Sitecore Context Language Iso Code

May 29, 2012 by     No Comments    Posted under: Sitecore

Sitecore’s core functionality comes with the ability to localize content in multiple languages. This is done via the native language versioning of content items. Websites that leverage this multi-lingual capability typically provide a mechanism to switch between languages. Getting the context language that a user has selected is easy in Sitecore but it takes a bit more work to get the Iso code for the language. It’s nice to get the Iso code for the selected language so the language can be switched using Sitecore’s “sc_lang” query string key.

First, get the context language item. This is not merely the context language, but rather the context language definition item located under /sitecore/system/languages.

Use the LanguageManager to get the GUID of the Language item based on the context language. From this, you can get the actual language item from the context database:

ID contextLanguageId = LanguageManager.GetLanguageItemId(Sitecore.Context.Language, Sitecore.Context.Database);
Item contextLanguage = Sitecore.Context.Database.GetItem(contextLanguageId);

Next, you can get the “Regional Iso Code” field if one is set for the language. The out-of-the-box English language does not have a “Regional Iso Code” so fallback to the “Iso” field if necessary:

string iso = contextLanguage["Regional Iso Code"];
if (string.IsNullOrEmpty(iso))
{
    iso = contextLanguage["Iso"];
}

Now you’ll end up with something like “en” or “es-ES” for English and Spanish (Spain) respectively.

What can you use those Iso codes for? Good question. Sitecore provides a native way to switch the context language. Simply set the Iso code in the query string for the "sc_lang" query string key. This will change the context language. For example, you might want to create a language selector tool which given the languages defined in Sitecore switches the query string with the new Iso code (or Regional Iso Code). You can then use the context language’s Iso code to update your language selector tool to identify the current language.

Sitecore Gutter Icon to Indicate an Item is a Page

Mar 7, 2012 by     No Comments    Posted under: Sitecore

Sitecore’s flexibility allows developers to truly extend it beyond its features that come out of the box. One such extension point is creating a custom icon in the gutter of the content tree. Inspired by a post by Dan Solovay, I’ve decided to create my own custom gutter icon to indicate if a data item is a page.

The reason I decided to create this icon was to help identify to content editors which items in the tree are pages (and thus have URLs represented by their path) vs. which items are merely just data items to store data. The code is quite simple, and the icon and tooltip are overridable via two custom Sitecore settings.

Features:

  • Visually indicates what data items have a layout set.
  • Configurable icon and hover tooltip.
  • Clicking the icon opens the presentation details pop-up.

The Code


using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data.Items;
using Sitecore.Shell.Applications.ContentEditor.Gutters;

namespace CustomGutters
{
    public class PageGutterRenderer : GutterRenderer
    {

        protected override GutterIconDescriptor GetIconDescriptor(Item item)
        {
            if (string.IsNullOrEmpty(item[FieldIDs.LayoutField]))
            {
                return null;
            }

            string iconPath = Settings.GetSetting("PageGutter.Icon");

            if(string.IsNullOrEmpty(iconPath))
            {
                iconPath = "People/32x32/monitor2.png";
            }

            string iconTooltip = Settings.GetSetting("PageGutter.Tooltip");

            if (string.IsNullOrEmpty(iconTooltip))
            {
                iconTooltip = "The item is a page.";
            }

            GutterIconDescriptor gutterIconDescriptor = new GutterIconDescriptor
            {
                Icon = iconPath,
                Tooltip = iconTooltip,
                Click = string.Format("item:setlayoutdetails(id={0})", item.ID)
            };

            return gutterIconDescriptor;
        }

    }
}

Overrides

The few lines of code that there are do come with two optional overrides:

To change the icon used in the gutter, create a Sitecore setting named PageGutter.Icon with the path to a Sitecore icon (e.g. “People/32×32/monitor2.png”).

To change the tooltip on the icon, create a Sitecore setting named PageGutter.Tooltip with the desired text (e.g. “The item is a page.”).

Download

You can download the DLL for the compiled code and a Sitecore package which installs a single new gutter item in the core database.

Defensive Coding for Sitecore

Feb 26, 2012 by     1 Comment     Posted under: Sitecore

Inspired by a great post by Adam Weber about Sitecore Defensive Coding Practices, I’ve decided to throw down some of my favorite patterns and practices with Sitecore Development. If you have any of your own, share them in the comments!

Define a BaseSublayout Class

Define a BaseSublayout class to expose base-level functionality to any UserControl/Sublayout that you will have on your site, e.g. accessing a Data Source item. For example:


protected class PromoBox : BaseSublayout
{
	protected void Page_Load(object sender, EventArgs e)
	{
		// use any code exposed in the inherited class
	}
}

My experience with this: I started doing this when I realized the power of assigning data source items to a sublayout defined on an item. The code to get a datasource takes a few lines, so I abstracted it into a base class for reusability on any project. It turns out John West has had something even better on Shared Source for a while now, the Sublayout Parameter Helper.

Gracefully Degrade the Front-End

Note: This is not to be confused with the other graceful degradation pattern of front-end development, particularly with JavaScript. This is about accessing Sitecore data.

This is by far my favorite pattern that I use all the time. As developers, our job is to make the site render correctly per the content in Sitecore. That includes handling content editor errors that may occur, such as not filling out data or missing something. The concept is simple:

Set controls (ASP.NET controls, FieldRenderers, etc.) to be visible="false" by default, then prove the data to render exists and render the data. There are many sub-patterns you can go with here, depending on how you write your HTML and how the component works. For example, any sublayout I have that requires a DataSource is wrapped in a PlaceHolder with visible="false". My job as a developer is to ensure a data source is set and its of the right template type. Once confirmed, I turn the placeholder’s visibility on. This pattern ensures no widow HTML exists that is rendered without any real data.

Here’s a simple example:

<asp:PlaceHolder ID="TitleWithLinkPlaceholder" runat="server" Visible="false">
	<h4>
		<asp:Hyperlink ID="TitleWithLinkHyperlink" runat="server">
			<sc:Text ID="TitleWithLinkText" runat="server" Field="Title" />
		</asp:Hyperlink>
	</h4>
</asp:PlaceHolder>
bool linkSet = !String.IsNullOrEmpty(genericCallout.Link.Url);

if (!string.IsNullOrEmpty(genericCallout.Title.Text))
{
	if (linkSet) // With link
	{
		TitleWithLinkHyperlink.NavigateUrl = genericCallout.Link.Url;
		TitleWithLinkText.Item = genericCallout.InnerItem;
		TitleWithLinkPlaceholder.Visible = true;
	}
	//...

As you can see above, I assume the data is not in Sitecore by wrapping my HTML in a placeholder that is visible="false" then in C#, I see if the data exists and turn the controls on. In this simple example, it prevents a random <h4> tag from appears with nothing in it. Is it a lot of code for a little check like this? Yes. Is it worth it when not all fields in Sitecore are required and they affect the front-end? YES!

Use FieldRenders or at Least Trigger the renderField Pipeline

Many Sitecore solutions need to support Page Edit mode. In order for code to support this, the renderField pipeline needs to be triggered. Why? Because this pipeline injects JavaScript and JSON into the page in Page Edit mode to allow for dynamic control of the page. Triggering this can be done by using FieldRenderer controls on the front-end when possible, or if not possible, at least use the FieldRenderer via C#. Luckily, the Custom Item Generator (CIG) exposes easy access to the Rendered value of a field, so at least use this if you need to use regular .NET controls. (Note: the CIG is another useful pattern Adam covers in his post).

Use a Page Load Pattern with Try-Catch Blocks

Every sublayout I write follows the same pattern:

  1. The Page_Load method body contains a try-catch block.
  2. The try block calls a helper method to do the main work.
  3. If the sublayout takes a DataSource, a check occurs to ensure the item is set and is of the right template.
  4. The catch block logs the exception to the Sitecore log with the name of the specific sublayout.

Here’s an example where we use several of the patterns in this blog post (BaseSublayout to expose the DataSource, CIG items, Page_Load pattern):


protected void Page_Load(object sender, EventArgs e)
{
	try
	{
		if(DataSource != null && DataSource.TemplateId.Equals(PromoItem.TemplateId.ToString()))
		{
			LoadPromo(DataSource);
		}
	}
	catch(Exception ex)
	{
		Sitecore.Diagnostics.Log.Error("Promo Sublayout", ex, this);
	}
}

In the above code, the method LoadPromo(Item item) would actually bind the data to controls, etc.

My experience with this: I started doing this when I found it hard to determine where some null errors were occurring on sublayouts that didn’t have appropriate null checks. I started to follow the same pattern over and over and eventually tacked on the try-catch with Sitecore logging to make it easier to know when issues came up.

Avoid Hard-coded Paths and Instead Expose Global Item Links

To explain this approach, let me pose a scenario:

Say you’re building a search results page on your site to host the results sublayout for your global full text site search (using Lucene.NET, or Coveo, or Google Search Appliance, etc.). You have a textbox in your header that handles the search query and you push the query to the results page to actually search the site. How does your code for the textbox know which page to go to for the results?

  • Option 1: hard-code it (BLAH!)
  • Option 2: expose a global field in Sitecore to set the results page (FTW!)

In code, your search box query method would do something like this:


protected void btnSearch_Click(object sender, EventArgs e)
{

	// assumes Globals.GlobalItem is a static helper class to get the globals item
	ReferenceField searchPage = Globals.GlobalItem.Fields["Search Page"];
	var url = LinkManager.GetItemUrl(searchPage.TargetItem) + "?q=" + txtSearch;
	Response.Redirect(url);

}

Why is Option 2 better? If someone renames the search results page, say from search-results to just results then the global field in Sitecore would handle the change.

For Any Standard User Control in Use, Consider Statically Binding it as a Sublayout

If your code in layouts and sublayouts references other modular controls such as standard users controls, e.g. <uc:Promo runat="server"></uc:Promo> you should consider loading the user controls the Sitecore way. By changing a standard user control reference to a statically bound sublayout, you can leverage Sitecore’s HTML output cache to cache the rendered HTML per the business rules of the control. E.g. <sc:Sublayout Path="~/path/to/your/control.ascx" Cacheable="true" runat="server" />, then in C# you could perhaps set the VaryByParm property to alter the cache instances by your uniqueness of the control.

Leverage Utility Classes, Do Not Reinvent

Sitecore comes with a plethora of utility classes in many namespaces. Learn them and use them. They’re very handy and you can avoid writing code that may already exist (e.g. sanitizing an item name).

Categories