TDS And Duplicate Items

April 18, 2013

From time to time we get feature requests for Team Development for Sitecore to support multiple sub-items with the same name as shown below.

- sitecore
    - content
        - home
            - article-1
            - article-1
            - article-2

This topic has been discussed internally at Hedgehog for the last few years and unfortunately it is a feature that we don’t plan on supporting. The reality is that we can’t find a good reason to ever have items at the same level with the same name. Examples would be:

  • Fields of a template with the same name: the field wouldn’t be accessible by its name since there are multiple
  • Templates with the same name: Again, using the Sitecore API you wouldn’t know which one you are getting
  • Content with the same name: There would be a single URL addressing two pieces of content. You wouldn’t know which one is served up.

There are a couple of cases where we hear requests for duplicate item support.

  1. The ‘Divider’ items in the ‘core’ database
  2. When there are issues with the actual items. An example of this would be somehow a template has multiple ‘__standard values’ items and the customer reports the problem. However, the problem in this case is actually in their Sitecore database and the extra item should be removed. Therefore, having lack of support for duplicate item names actually helped find a problem for them!

For times when people request support for duplicate item names in order to handle the ‘Divider’ issue we tell them to rename the dividers in the core database. The name of the ‘Divider’ item is irrelevant and if you rename them to Divider-1, Divider-2, etc… it will work as expected and TDS will not complain.

We have also worked with Sitecore about the ‘Divider’ issue and they said they will try to make the renames as part of a future release.

If you, or your colleagues, can come up with a good business reason to have duplicate item names in a Sitecore installation please let us know!

Dan Galvez and I had come up with an idea for a, sort of, Twitter based Sitecore Scrum. In essence, people would post on Twitter what they are doing today with the hashtag #SitecoreScrum. Interestingly, another Sitecore developer (Mike Reynolds) started this exact same thing the very next day with this tweet.  I’ve reached out to Mike and we are going to try and keep up with a daily #SitecoreScrum at 10am Eastern time. I don’t know what will come of this, but it can’t hurt knowing what others in our realm are doing with Sitecore. 

I realize actually committing and sticking to these daily tweets is tough, but I would appreciate any Sitecore developer with a Twitter account to do it; it shouldn’t take anyone too long to type 140 characters. 

If you use a multi-column Twitter client then take a moment and setup a column for the #SitecoreScrum search to keep tabs on one another! If the web interface is your thing, then setup a saved search. Alternatively, we can look to use a tool such as http://taghuddle.com/sitecorescrum to aggregate the hashtag.

Here is an ICS file you can add to your calendar to remind you! Twitter-Sitecore-Scrum.ics (3.43 kb)

When using Team Development for Sitecore (TDS) you may need to tell your source control system how to handle certain files. This post is mainly targeted to folks using Git as their source control system, but it could be a general guideline for people using any source control system that isn't integrated with Visual Studio.

Excluding Certain Files

TDS creates a few files on your disk that shouldn't be included with source control. These files are typically user specific files and/or build generated files. Here is a list of files and directories (with a wildcard character) that should be ignored.

  • *.scproj.user
  • T4RenderCache/
  • IconCache/ 
  • BuiltFiles_*.txt
  • Package_*/
Also worth noting is that TDS is configured with configuration specific 'Build Output Paths.' This means that when you build your TDS project "Debug," "Release," "[custom config]" directories are created. If you only have 'Debug' and 'Release' then there shouldn't be much of a problem if you are using Git since GitHub's default .gitignore file will exclude them. However, any custom configuration build output directories may be picked up as an 'uncommitted change'. I recommend manually changing your TDS Build Output Path to be ".\bin\[Config Name]" rather than the default ".\[Config Name]" to prevent custom config build outputs from accidentally being checked into Git.
 
Change this:

 

To this:

After you change your build output path, here are some lines you should add to your .gitignore when using a TDS project:

#############
## Hedgehog Development's Team Development for Sitecore
#############
IconCache/
T4RenderCache/
BuiltFiles_*.txt

Handling Line Endings

The Sitecore Serialization Format is rather strict especially with line endings. There may be instances where you have a Rich Text field that was serialized to have a mix of CRLF and LF. If you are using the default GitHub .gitattributes file you can run into issues. To get around this you can modify your .gitattributes file with these lines: (thanks to Nick Wessleman for discovering this issue)

 # Custom for TDS/Sitecore serialization 
*.item -text 

Disclaimer: This post was in a draft state for many, many, months. It was started and subsequently turned into my Sitecore Symposium 2012 talk. Now that SitecoreSym is over I am making this post and the associated code available.

We have started to see a rise in requests to create "single page applications (SPA)" and with MVC4 supporting this it confirms it isn't something to ignore. Similar to Single Page Apps, we have all seen a surge in mobile applications that require content. Both of these scenarios move rendering logic from the server to the client whether it be JavaScript, cocoa touch, etc... "This results in the role of the web server evolving into a pure data API or web service."[1Sitecore doesn't provide for a consumable content API out of the box and does require some plumbing to allow clients to pull content out. It was from these requests that we've dubbed "Sitecore Content as a Service." 

In order to use Sitecore content, in an SPA or mobile device, we need an easy-to-consume content service. The ultimate goal will be to have a clean REST API that is consumable by any potential client such as a Javascript MVC framework like Backbone.js or an iOS device. There are, however, a few server side things required to make this happen.

Sitecore has a robust API revolving around "Items" and "Fields". Sitecore's Item is a very heavy object with a lot of convenience methods and properties to things like 'Parent', 'Axes', 'Children', etc... A Sitecore Field is also very heavy with various versions for images, links, references, etc... Exposing these objects to clients wouldn't be ideal since they expose a lot of extras that are of no concern. Furthermore, trying to serialize these objects into JSON (the REST API serialization format of choice) would prove to be difficult if not impossible. 
 
So we recognized that Sitecore falls a little short with trying to get content out. Here is what we need:
  • “Sitecore Navigator”
  • POCO content models
  • Mapping framework
  • Efficient JSON Serialization
  • Service endpoint inside of Sitecore
"Sitecore Navigator"
The "Sitecore Navigator" is a trivial piece of the solution. It is simply a way by which we can get a Sitecore item with some unique identifier. This is almost as simple as Sitecore.Context.Database.GetItem(id);
 
POCO Content Models
Plain Old CLR Object (POCO) are objects unencumbered with inheritance or attributes needed for specific frameworks[2]. We want a set of these models, or classes, that represent our Sitecore templates. For each template we have we will have a corresponding .net class.
 
Mapping Framework
We need some method by which we can convert, or map, a Sitecore item into one of our POCO classes. Our weapon of choice is a convention based mapping framework. We will use reflection magic to determine what class to instantiate and populate based on a naming convention between Sitecore template and .net class.
 
JSON Serialization
We need to serialize our classes in order to server them over an API. JSON serialization is the way to go and ASP.NET Web API has it built in. Win!
 
Service Endpoint
In order for an external application to get Sitecore content out, the external application needs some endpoint to call. For our implementation we will use the newly released ASP.NET Web API. This allows us to define a route to a service controller. Our controller actions allow the applications to get content out.
 
The code has just been released on GitHub @ https://github.com/HedgehogDevelopment/sitecore-content-service

 

[1] https://en.wikipedia.org/wiki/Single_page_application

[2] http://en.wikipedia.org/wiki/Plain_Old_CLR_Object

 

Did you know that you can access special properties of a Sitecore item the same way you get a typical field's value? 

Supported 'special fields' are:

  • @id = Item.ID.ToString()
  • @key = Item.Key
  • @lang = Item.Language.ToString()
  • @mid = Item.BranchID.ToString()
  • @name = Item.Name
  • @tid = Item.TemplateID.ToString()
  • @ver = Item.Version.ToString()
Example:
Sitecore.Data.Items.Item item = Sitecore.Context.Database.GetItem("/sitecore/content");

string id = item["@id"];
string key = item["@key"];
string lang = item["@lang"];
string mid = item["@mid"];
string name = item["@name"];
string tid = item["@tid"];
string ver = item["@ver"];

Sitecore.Diagnostics.Assert.IsTrue(id == item.ID.ToString(), "No match");
Sitecore.Diagnostics.Assert.IsTrue(key == item.Key.ToString(), "No match");
Sitecore.Diagnostics.Assert.IsTrue(lang == item.Language.ToString(), "No match");
Sitecore.Diagnostics.Assert.IsTrue(mid == item.BranchId.ToString(), "No match");
Sitecore.Diagnostics.Assert.IsTrue(name == item.Name.ToString(), "No match");
Sitecore.Diagnostics.Assert.IsTrue(tid == item.TemplateID.ToString(), "No match");
Sitecore.Diagnostics.Assert.IsTrue(ver == item.Version.ToString(), "No match");

When trying to determine if a Sitecore field has a value there are two basic ways.

Sitecore.Context.Item.Fields["fieldName"].HasValue

or

Sitecore.Context.Item.Fields["fieldName"].Value != ""

There are a few major differences.

  1. HasValue will not include checking a clone's original value, Standard Values or Default Values
  2. HasValue will return true if the field value is an empty string
  3. Field.Value will return the clone's original value, Standard Value or Default Values if the field doesn't have a value
  4. If there is no value on the field, clone's original value, standard values, or default value Field.Value will return an empty string
Therefore, 
Sitecore.Context.Item.Fields["fieldName"].Value == null

will never evaluate to true!

Beware. Sitecore Bug 

By calling Field.HasValue you will cause the Field.ContainsStandardValues property to have an invalid value! This has been logged as bug #368493 with Sitecore. For more information see this post.

There are many ways to get the value of a Sitecore field and each is slightly different.

string fieldName = "__Icon";
Sitecore.Data.Items.Item someItem = Sitecore.Context.Database.GetItem("/sitecore/content");
Sitecore.Data.Fields.Field someField = someItem.Fields[fieldName]; // you could use field name, ID or index here

// assumptions:
// 1. The Item exists
// 2. The field exists and has a value on the item
// 3. This was written against Sitecore 6.4.1.101221
                
// Method 1: Indexer on Item supports using a field's name, index, or ID
string value1 = someItem[fieldName];

// Method 2: Get the value from the field
string value2 = someField.Value; // This is commonly seen as someItem.Fields[fieldName].Value

// Method 3: 
string value3 = someField.GetValue(true);

// Method 4: 
string value4 = someField.GetValue(false);

// Method 5: 
string value5 = someField.GetValue(true, true);

// Method 6: 
string value6 = someField.GetValue(false, true);

// Method 7: 
string value7 = someField.GetValue(false, false);

// Method 8: 
string value8 = someField.GetValue(true, false);

// Method 9: 
string value9 = someItem.InnerData.Fields[theField.ID]; 

 

Method 1

This method uses an indexer on the item. If the field doesn't exist on the item you will get an empty string. If the field does exist you will get the same result as method 2.

Method 2

You need to have a field to use this method! If you called method 2 by the more commonly used notation of someItem.Fields[fieldName].Value you can get a null reference exception when trying to access the Value. The first  value to be found will be returned. 

  1. The Value
  2. If the item is a clone, the clone source's value
  3. Standard Value
  4. The field's default value
  5. Empty String

Method 3 & Method 5

These two are functionally equivalent. This method gets the value of the field similar to method 2.

Methods 4 & Method 6

These two are functionally equivalent. This method gets the value of the field without checking a clone's source or the standard values. The first value to be found will be returned. 

  1. The Value
  2. The field's default value
  3. Empty String

Method 7 

This method gets the value of the field without checking a clone's source, standard values or default value! The first value to be found will be returned. 

  1. The Value
  2. Null

Method 8 

This method gets the value of the field without checking the default value! The first value to be found will be returned. 

  1. The Value
  2. If the item is a clone, the clone source's value
  3. Standard Value
  4. Null

Method 9

The FieldList object at ItemData.Fields on an item is the real container of field information for that item. The FieldCollection on Item.Fields is really a way into the FieldList on the ItemData. ItemData is also a much more raw form. Internally the FieldList is a HashList<ID, string> where the key is the field id and the value is the raw value of the field. Cloned Values, Standard Values and Default Values do not exist in the FieldList and using this method would return null where the field has no value.

Sitecore Bug 

By calling Field.GetValue(false), Field.GetValue(false, true), or Field.GetValue(false, false) you can cause the Field.ContainsStandardValues property to have an invalid value! This has been logged as bug #368493 with Sitecore. For more information see this post.

I had discovered a little bug revolving around the Sitecore.Data.Fields.Field.ContainsStandardValue property in Sitecore (at least 6.4.1.101221 through current). You cannot trust the value returned by Field.ContainsStandardValue if you have made any of the following calls:

  • Field.HasValue
  • Field.GetValue(false)
  • Field.GetValue(false, false)
  • Field.GetValue(false, true)

A way to get around the bug would be to immediately call field.GetValue(true, false) if you have made any of the previously listed calls. For example:

// does the field have a value?
bool hasValue = field.HasValue;

// fix bug 368493. 
field.GetValue(true, false);

This bug has been acknowledged by Sitecore and logged as #368493.

 

A common practice in all of my Sitecore sites is to have some sort of content fallback. What this means is that a field's value can come from somewhere other than the field itself, the source item (if it is a clone), or its standard values.

Content fallback is an excellent way to increase editor productivity and reduce content redundancy! Here are the three most common scenarios I run into with every project.

Ancestor Inheritance

One of the more common scenarios for ancestor inheritance is for things like the text that is to show up in the <title> tag of the HTML document. It is SEO best practice to have a unique title for every page. However, in some sites where there are thousands of pages, having an editor give each page a title may be unrealistic. The general practice we take with the sites we do is that if the "Title" field in null then we should use the value of the nearest set ancestor. This allows an editor to set the title at the 'home' node and guarantee that every page in the site will at least have something set for the <title> tag. This concept can be used for many different scenarios in almost any site!

Lateral Fallback

This is a fairly common technique where you may want a "MenuTitle" field to specify the text used in an <a> tag when a link to the item is being generated in a menu. However, in the event there is no "MenuTitle" then we should expect to see the value of the "Title" field.

Default Fallback

This type of fallback I typically use with Lateral Fallback. Furthering our Lateral Fallback example, lets suppose that we don't have a value for "MenuTitle" or the "Title" fields. In this case we simply want to have a value with the name of the item. In the event the item is renamed we should show the current name, not the name used at create time.

There are typically two places where this content fallback logic would live. If the site is using some sort of 'content model' framework such as Custom Item GeneratorCorePoint.DomainObjects or Glass Mapper then it would make sense to put the logic there. However, without such a framework the logic would be forced to live in the presentation layer. There are a few problems with these approaches.

  1. The fallback value isn't obvious to the editors. It doesn't show up in the content editor and they must know there is magic at hand at render time.
  2. Using the Page Editor can become (more) cumbersome.
  3. The fallback value is not readily available via standard API calls. The biggest issue is typically that the value isn't indexed for searching.
  4. Low level caching of the values doesn't happen. Caching is only available at the HTML level.

When Alex Shyba released his language fallback module I immediately envisioned a similar paradigm used for my content fallback scenarios! In my (not so) free time I was able to come up with the newly released Field Fallback Shared Source Module! This approach had one huge benefit and that is the editorial team would be able to see the 'fallback value' right there in the content editor! Furthering this it also had the fallback value in a cache thereby eliminating the need to calculate the fallback value multiple times.

The Field Fallback module will handle the three scenarios above and also provides for a fully pluggable architecture (pipeline based) to handle any other fallback scenarios you may need. This include language fallback! Part of the release includes a port of Alex Shyba's language fallback logic!

You can grab it from the Shared Source repository here - http://trac.sitecore.net/FieldFallback

I am scheduled to present this topic and demonstrate the module in an upcoming Sitecore Users Virtual Group. (After that date I will have the video here)

One of the major pain points when deploying Sitecore based web sites has always been configuration management. Sitecore has thousands of lines of configuration scattered across more than a dozen files. 

With Sitecore 6 we were given the ability to use 'patch' or 'include' files that greatly helped us manage changes in the Sitecore configuration section. While this was a huge step forward there was still a major problem with this. That problem being, we aren't able to patch, or transform, any configuration outside of the Sitecore section. 

One of the main goals of Team Development for Sitecore has always been to ease the deployment of your Sitecore based sites. When we shipped the first version of TDS for Visual Studio 2008 we provided a "File Replacements" feature. This feature let you manage files that may be specific to an environment. This feature worked well in that you were able to deploy environment specific configuration files automatically as part of your build process. The down side to using File Replacements for configuration files is that you needed to manage the entire file for each environment, but there were no alternatives at the time.

When Visual Studio 2010 was released we were given config transformations, but this feature has its flaws especially for Sitecore developers. The first major issue is that Visual Studio only supported adding a transform file to the web.config file. Second, in order to transform a file you needed to run it via web deploy (or packaging within Visual Studio). And, finally, many Sitecore developers still work in the web root and transforming a file "in place" would be a bad thing.

As a Sitecore developer using TDS we are able to use Sitecore include files to patch the Sitecore configs and we can leverage TDS file replacements to manage environment specific versions. If we wanted to use a config transform on the web.config file than we would have to use web deploy for our files as opposed to using TDS. It never felt right.

I'm very excited to say that with Version 4 of Team Development for Sitecore we are supporting config transformations as part of the build process! This means that you can use out of the box Visual Studio tooling to add a transform to your web.config file and when TDS builds your solution it will transform the web.config file and deploy or package the transformed file. The really exciting part is that you are able to leverage a Visual Studio add-in such as SlowCheetah to add transforms to all config files and TDS will transform them too!

Note:

We aren’t reliant on SlowCheetah to do the transformation at all. We would recommend that you use it in order to get the ability to add a transform to any config file and to preview the transformed file.