Browsing Sitecore

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).

Easily Sanitize a Sitecore Item Name in C#

Jan 23, 2012 by     2 Comments    Posted under: Sitecore

Say you’re creating a new Sitecore item programmatically in C#. The general approach is to take some piece of data from your original data point, e.g. an article in a feed, and sanitize it so your item name is valid in Sitecore. Let’s take the example of an article’s title. Most developers will strip out the invalid character’s they can think would cause issues in Sitecore, e.g. !,.:; etc…

Here’s an easier way to do it with Sitecore’s own configuration:

char[] invalidcharacters = Sitecore.Configuration.Settings.InvalidItemNameChars;
string sanitizedName = string.Concat(possibleName.Trim().Split(invalidcharacters));

Notice Sitecore.Configuration.Settings.InvalidItemNameChars gives you a character array of the characters defined in the web.config. From there, we just trim whitespace away from the name, split the string at each “invalid” character and then concatenate the split chunks.

For re-usability, here it is as an overloaded extension method:

public static string SanitizeToItemName(this string possibleName)
{
  return SanitizeToItemName(possibleName, Sitecore.Configuration.Settings.InvalidItemNameChars);
}

public static string SanitizeToItemName(this string possibleName, char[] invalidCharacters)
{
  return string.Concat(possibleName.Trim().Split(invalidCharacters));
}

UPDATE:

Mark van Aalst identified that the Sitecore API has a utility method that does something like this already: Sitecore.Data.Items.ItemUtil.ProposeValidItemName(name, defaultValue);. Thanks for the tip Mark!

Programmatically Add Controls to the HTML Head in Sitecore

Jan 19, 2012 by     No Comments    Posted under: Sitecore

I was recently working on a Sitecore module and realized I needed to add some HTML elements to the <head> tag of pages using the module. This would normally be something that can be done directly in every layout, but because this is a Sitecore module, I wanted to do it dynamically via C# through a pipeline so that it would only require a configuration patch to inject my code. The challenge I had was not implementing it via a pipeline processor, but rather programmatically getting access to the <head> tag. Luckily, John West had a great solution, and it was something directly available via the Sitecore API.

First, to accomplish this, register a processor for the renderLayout pipeline:

<renderLayout>
  <processor type="MyLibrary.Pipelines.RenderLayout.InsertHeadControls, MyLibrary">
  ...
</renderLayout>

Next, let’s look at the code:


namespace MyLibrary.Pipelines.RenderLayout
{
  public class InsertHeadControls
  {
    public void Process(RenderLayoutArgs args)
    {

      // no need to run our code in the shell
      if(Sitecore.Context.Site.Name == "shell")
        return;

      Control head = WebUtil.FindControlOfType(Sitecore.Context.Page.Page, typeof(HtmlHead));
      if(head != null)
      {
        head.Controls.Add(/* add whatever control you want */);
      }
    }
  }
}

The key here was to use WebUtil.FindControlOfType() to get the HtmlHead control via C#. From there you can ensure its not null and add controls dynamically to it as needed. Note: for the HtmlHead to not be null, I believe this requires a runat="server" on the <head> tag.

Prevent Sitecore Content Editor From Stripping Script Tags

Jan 10, 2012 by     6 Comments    Posted under: Sitecore

I was recently helping a colleague with writing some custom buttons in the Telerik Rich Text Editor. Our button injects JavaScript into the content of the field and we noticed some of the JavaScript was being stripped by Telerik. This has been an annoying issue for many versions of Sitecore. The solution is quite simple and requires a small change to a built-in Sitecore file.

Telerik’s documentation explains a content filter that can be applied to strip script tags. The goal of the custom code it to override the Telerik editor and disable the filter from being applied.

Open \Website\sitecore\shell\Controls\Rich Text Editor\EditorPage.aspx

The C# is compiled in the Sitecore.Client.dll assembly, so let’s add in our own C# code right into this page. Add in the following block of code to run before the normal Page_Load:

<script runat="server">

   protected override void OnLoad(EventArgs e)
   {
       Editor.DisableFilter(EditorFilters.RemoveScripts);
       base.OnLoad(e);
   }

</script>

Note: this was tested on Sitecore 6.4

Just consider the upgrade path here. Before you run an upgrade of Sitecore you’d want to backup this built-in file just in case.

Update (1/19/2012)

While working with a colleague (Mark Graber, Sitecore MVP) we determined a better way to do this via a configuration patch so you don’t need to edit the built-in Sitecore editor page.

Create a class that inherits Sitecore.Shell.Controls.RichTextEditor.EditorConfiguration. Next, override the SetupFilters() method and in there run the same code to remove the filter, like so:

protected override void SetupFilters()
{
  Editor.DisableFilter(EditorFilters.RemoveScripts);
  base.SetupFilters();
}

Now, register this new class in the config setting “HtmlEditor.DefaultConfigurationType” like so:

<setting name="HtmlEditor.DefaultConfigurationType" value="MyProject.EditorConfiguration, MyProject"/>

Update (2/16/2012)

It has been reported (in the comments) that there is still an issue that scripts are removed when you switch to HTML view of the RTE. I was only able to reproduce this issue in Internet Explorer but it does appear to be a problem still.

Additionally, it appears newer releases of Sitecore have the above solution baked in with a new setting called HtmlEditor.RemoveScripts which affects the built-in EditorConfiguration class based on the setting. My post was written on 1/10/2012 and Update 6 to Sitecore 6.4.1 was released on 1/13/2012 with this setting. So this should now be a built-in setting to Sitecore which will make it easier to configure out of the box!

Rendering Fully Qualified Sitecore URLs

Jan 6, 2012 by     No Comments    Posted under: Sitecore

Sitecore’s web.config contains many ways to extend and customize the application. One such configurable aspect is dynamic link resolution. This article is intended to explain how you can configure Sitecore to render fully qualified URLs (e.g. http://host/my/path/to/page.aspx) in links.

Make All Links Fully Qualified

Its really easy to configure Sitecore to make all links fully qualified. You can simply update the linkManager configuration and set alwaysIncludeServerUrl to true. Here’s an example:


<linkManager defaultProvider="sitecore">
  <providers>
    <clear />
    <add name="sitecore" type="Sitecore.Links.LinkProvider, Sitecore.Kernel" addAspxExtension="true" alwaysIncludeServerUrl="true" encodeNames="true" languageEmbedding="never" languageLocation="filePath" shortenUrls="true" useDisplayName="false" />
  </providers>
</linkManager>

Only Make Some Links Fully Qualified

If you don’t always want all links to be fully qualified, but rather have certain scenarios when they should be, you can create a custom link provider to handle this. Simply inherit Sitecore.Links.LinkProvider and override the GetItemUrl(...) method:

namespace CustomLibrary.Links
{
    public class CustomLinkProvider : Sitecore.Links.LinkProvider
    {

        public override string GetItemUrl(Sitecore.Data.Items.Item item, Sitecore.Links.UrlOptions options)
        {

            if (/* my condition of when to apply fully qualified links, e.g. a specific device maybe */)
            {

                options.AlwaysIncludeServerUrl = true;

            }

            return base.GetItemUrl(item, options);
        }

    }
}

After doing this, change the defaultProvider in the linkManager to your custom class like so:


<linkManager defaultProvider="custom">
  <providers>
    <clear />
    <add name="sitecore" type="Sitecore.Links.LinkProvider, Sitecore.Kernel" addAspxExtension="true" alwaysIncludeServerUrl="false" encodeNames="true" languageEmbedding="never" languageLocation="filePath" shortenUrls="true" useDisplayName="false" />
    <add name="custom" type="CustomLibrary.Links.CustomLinkProvider, CustomLibrary" addAspxExtension="true" alwaysIncludeServerUrl="false" encodeNames="true" languageEmbedding="never" languageLocation="filePath" shortenUrls="true" useDisplayName="false" />
  </providers>
</linkManager>

Scaling Sitecore Presentation Component Data Sources

Jan 4, 2012 by     4 Comments    Posted under: Sitecore

Sitecore presentation components come with some handy features to make them reusable, such as assigning a data source to define dynamic data. This post will explain how to scale your data sources so they can be moved or renamed within the content tree without affecting presentation.

The Issue (the Internal Link field)

The Data Source field on component rendering parameters is unfortunately set as an Internal Link field, so if you move or rename the target data source item, your presentation components that use the data source will break. This is because an Internal Link field (which you should always avoid) stores the target item via path, not GUID, so renaming or moving it will cause the path to be wrong.

A Simple Solution

This issue is quite annoying and can cause developers and/or architects to avoid using data sources and instead opt for Parameter Data Templates. The solution is to merely change the out-of-the-box Data Source field to be a Droptree field instead of an Internal Link field. Here’s how to do it.

First, find the template with the Data Source field on it. It happens to be on the Standard Rendering Parameters template (/sitecore/system/Layout/Rendering Parameters/Standard Rendering Parameters) which all presentation components use in the control properties.

Next, change the Data Source field to be a droptree.

Now, when you pick a data source, it will use a Droptree field and store the target item via GUID, so renaming or moving it will not break anything.

I’ve tested how Sitecore’s interface reacts to this change and Page Edit mode still works when assigning a data source item to a component.

A Going Live Checklist for Sitecore Websites

Dec 29, 2011 by     1 Comment     Posted under: Sitecore

This post is a summation of many different points to consider when deploying a Sitecore site to the public. Many of these are .NET tips that apply, but most are specific to Sitecore solutions. The checklist is broken down into two main categories: Security and Performance.

I. Security

1. Protect Admins Pages

Some of the older versions of Sitecore do not protect the admin pages under the /sitecore/admin/ folder. You should block public access to these by implementing security via IIS configuration. You can read how to do this in section 2.3 of the Security Hardening Guide (for 6.0 – 6.4).

2. Turn on Custom Errors

Remember to update your production web.config to <customErrors mode="RemoteOnly" />. This will allow to you have a friendly error message to your site visitors should an error occur.

3. Secure the Sitecore Backend

Don’t forget to reset the admin user’s password. It would be quite embarrassing to go live with “b” as your password.

4. Deploy Your Real License File

Ensure a live license file is deployed, not a temporary license or partner development license.

II. Performance

1. Configure Keep-Alive

Update your web.config to set the UrlAgent to the public URL of your site’s keep-alive page. By default the URL is not fully qualified and therefore a web request cannot be made to it, so just update it with your hostname. Having this enabled will ensure the application pool’s worker process does not shut down.

<agent type="Sitecore.Tasks.UrlAgent" method="Run" interval="01:00:00">
  <param desc="url">/sitecore/service/keepalive.aspx</param>
  <LogActivity>true</LogActivity>
</agent>

2. Turn Debug Mode Off

Remember to update your production web.config to <compilation debug="false">. This will result in higher performance of the application since extra debug info doesn’t need to be processed.

3. Regularly Rebuild Search Indexes

If you’re using Sitecore.Search, ensure search indexes are set to rebuild on the public database (e.g. web). If you don’t regularly rebuild your search indexes, they can become fragmented and can degrade performance. One way to rebuild them regularly is to create a page that when requested rebuilds the indexes. You can use Wget to automate executing this script.

4. Tune the Cache Sizes (Prefetch, Data, Item, and HTML Caches)

It’s important to tune your cache sizes instead of using the out-of-the-box values. If you’re not sure how the many layers of the Sitecore cache works, read the Caching Overview by Jens Mikkelsen. Sitecore recommends some initial values in Chapter 5 of the CMS Performance Tuning Guide. As the guide mentions, remember to modify the Prefetch cache config as many of the default values are unnecessary and will waste system resources.

5. Define Machine Keys to Load Balanced Servers

If your CD environment contains multiple load balanced servers, make sure you apply the same machine key to all instances. To understand more about what you need to do, refer to section 4.5 of the Scaling Guide.

6. Tweak Your Delivery Environment

If you want more tips, Rick Cabral from ISITE Design has published a good guide on ways to Tweak Your Sitecore Delivery Environment.

Your Tips

What are your tips or checklist items you refer to when you launch a Sitecore site? Leave them in the comments below.

How to Sort Sitecore Items in the Content Editor

Dec 14, 2011 by     1 Comment     Posted under: Sitecore

We’ve previously seen how to reset individual fields in Sitecore to their standard values, so today I’m going to show you how to re-order sub-items under a parent item. Many times you’d want to do this task if you’re creating a lot of items and would prefer them to be alphabetically ordered, or ordered by other criteria.

First, observe a folder of items ordered alphabetically. This is probably how you want them ordered, but let’s take a look at the sorting option available.

Right-click the parent item and go to Sorting > Subitems Sorting.

The pop-up allows you to define how to sort the items. There are many options in the dropdown to pick. In this example, Reverse has been selected to reverse the current order. The sorting option selected is previewed below in the main area.

To actually cause the items to sort using this sorting criteria, many people forget or don’t know that you need to push the Reset button to cause them to re-sort.

After hitting Reset, confirm you want to sort.

Next, click OK to close the sorting menu.

As you can see, the items are now sorted in reverse alphabetical order.

If you ever need to sort items back to their original alphabetical order, you can use the [Reset to Standard Value] sorting option.

As you can see, the items are back in alphabetical order:

Automate Sitecore Tasks with Wget

Dec 12, 2011 by     No Comments    Posted under: Sitecore

There are many blog posts out there about automating and scheduling Sitecore tasks. This post covers a scenario that I’ve used several times and just wanted to share. As John West mentions in All About Sitecore Scheduling Agents and Tasks, tasks defined in Sitecore and configured agents may not run on a site if the ASP.NET worker process has shut down due to inactivity to the site. This is often the case during off hours when you’d probably want to perform administrative tasks.

Overview

The scenario I often have is to rebuild the Lucene search indexes at off hours, usually around 3am, when the worker process can very well be shutdown due to inactivity. The process is simple: install Wget and configure a Windows scheduled task to call Wget to request a secret page on the site.

Task Configuration

  1. Download the Windows version of Wget and install it on the server.
  2. Create a new Windows Scheduled Task in the Control Panel.
  3. Navigate to the Wget program for the task to run.
  4. Once you create the task, right-click it and select Properties.
  5. Modify the path of the task to include the --spider parameter and the URL to your page to run. Below is a sample line for a task path.
"C:\Program Files\wget-1.11.4-1-bin\bin\wget.exe" --spider http://mysite.com/obfuscated-folder/special-task.aspx

This is just one of many ways to automate running something and have it happen at a specific time of the day. Here are some pros and cons I can think of for this approach:

Pros

  • Guaranteed to run at a specific time since it doesn’t rely on ticks in Sitecore
  • Simple to setup and won’t require a config change and therefore won’t recycle an existing app
  • Since a secret page is exposed via a public URL, you can manually run the code by hitting the page

Cons

  • Isolated from the Sitecore app, and thus possibly forgotten if migrating a server
  • Requires a specific page on the site to be requested, meaning a page with secret code would be exposed publicly (this can easily be fixed via permissions)

Additional Reading