Configure Sitecore Web Forms for Marketers with a Standard Connection String

For anyone that has or is going to install Sitecore’s Web Forms for Marketers module, the module comes with it’s own patch config file as you can expect. Depending on the version of Sitecore you are using, it’s either called forms.config or Sitecore.Forms.config. One of the post-install steps of the package is to configure a data provider with a database connection, e.g.
<formsDataProvider
type="Sitecore.Forms.Data.DataProviders.WFMDataProvider,Sitecore.Forms.Core">
. The default configuration of this SQL-based provider is to define a connection string as the first string-based parameter below the provider, e.g.

<formsDataProvider type="Sitecore.Forms.Data.DataProviders.WFMDataProvider,Sitecore.Forms.Core">
  <param desc="connection string">Database=(database);Data Source=(server);user id=(user);password=(password);Connect Timeout=30</param>
</formsDataProvider>

This connection string is deeply embedded within this patch config and does not follow the standard connection string configuration in the ConnectionStrings.config file. Luckily, you can swap this out for a normal token-based connection string in ConnectionStrings.config.

If you de-compile the data provider and look at its constructor, you can see that when no string parameter is provided, it falls back to WebFormsContext.ConnectionString:

wfm1

That base connection string property looks for a standard connection string with a token of "wfm":

wfm2

So the simple way to follow the standard approach to defining the database in ConnectionStrings.config is to define a string with that "wfm" token and ensure there is no parameter to the data provider:

<add name="wfm" connectionString="user id=username;password=password;Data Source=(server);Database=(database)" />
<formsDataProvider type="Sitecore.Forms.Data.DataProviders.WFMDataProvider,Sitecore.Forms.Core">
</formsDataProvider>

Leave a comment if you have any other tips like this.

Posted in Sitecore

Sitecore Context Site Resolution

Sitecore’s API contains an easy way to get the context site in your code, however it has some pitfalls in the case of a multi-site solution with page editor and preview modes. Here is a simple solution to this challenge.

Standard API Context Site Resolution

The typical way to get a context site in Sitecore is simply via Sitecore.Context.Site. This works most of the time, but if you have a multi-site solution and use Page Editor or Preview mode, it may not determine the site the item lives within (due to co-habitating site content trees). Additionally, if you use the Presentation tab’s embedded Preview frame, it will assume the context site is either “website” or “shell” so you really need this solution.

Enhanced Context Site Resolution

Here is a sample method to get the context site. I won’t go into details of the code because it’s all commented, but at a high-level:

  1. Get the item being viewed (the hardest part) — page editor or multiple preview modes
  2. Get all sites
  3. Find the first site for which the item falls within the tree path

public static Sitecore.Sites.SiteContext GetContextSite()
{
	if (Sitecore.Context.PageMode.IsPageEditor || Sitecore.Context.PageMode.IsPreview)
	{
		// item ID for page editor and front-end preview mode
		string id = Sitecore.Web.WebUtil.GetQueryString("sc_itemid");

		// by default, get the item assuming Presentation Preview tool (embedded preview in shell)
		var item = Sitecore.Context.Item;

		// if a query string ID was found, get the item for page editor and front-end preview mode
		if (!string.IsNullOrEmpty(id))
		{
			item = Sitecore.Context.Database.GetItem(id);
		}

		// loop through all configured sites
		foreach (var site in Sitecore.Configuration.Factory.GetSiteInfoList())
		{
			// get this site's home page item
			var homePage = Sitecore.Context.Database.GetItem(site.RootPath + site.StartItem);

			// if the item lives within this site, this is our context site
			if (homePage != null && homePage.Axes.IsAncestorOf(item))
			{
				return Sitecore.Configuration.Factory.GetSite(site.Name);
			}
		}

		// fallback and assume context site
		return Sitecore.Context.Site;
	}
	else
	{
		// standard context site resolution via hostname, virtual/physical path, and port number
		return Sitecore.Context.Site;
	}
}

Posted in Sitecore

Unpublish Sitecore Content

Recently many (many…) people have asked me how to unpublish content in Sitecore. It’s really easy in fact.

Go to the Publish ribbon and select the Change button:

unpublish1

In the dialog, make the item as a whole unpublishable by deselecting the Publishable checkbox:

unpublish2

You will be told by the content editor what to expect:

unpublish3

This item is not publishable, which really means, you can instantiate the publish operation on it, but it will not show on the front-end. So your next step is to actually publish (I know, sounds wrong…) the item and it will disappear from the front-end but remain in the master database.

And there you have it, an easy way to “unpublish” content.

Posted in Sitecore

Dealing with Sitecore Template is Used by at Least One Item Warning

If you’ve ever worked on creating data templates in the Sitecore CMS only to later abandon them with a better replacement, you may come across an issue of existing items based on that template. Read on to learn a quick way to handle this.

First, here’s the issue you will see if you try to delete a template that has at least one item based on it:

delete1

Ugh oh, that’s not going to happen!

delete2

This simply means that another item exists based on this template and therefore you cannot delete it as Sitecore cannot really handle orphaned types well. The solution is to find the Referrers of that template itself to determine which items are based on it. From there you can go to those items and delete them.

delete3

Posted in Sitecore

Sitecore Managed Sites as Virtual Folders

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

Posted in Sitecore

Sitecore Item and Field Names

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

Posted in Sitecore

Sitecore Avanced Database Crawler Occasionally Provides Null Results

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!

Posted in Sitecore

Sitecore Search by Site with the Advanced Database Crawler

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.

Posted in Sitecore

Sitecore Packager Error: Root Element is Missing

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.

Posted in Sitecore

Custom Sitecore Clones Listing Ribbon Button

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/

Posted in Sitecore