Experience Sitecore ! | All posts by martin

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

Sitecore.Kernel and Sitecore.MVC delivered via a NuGet library

Update (01 Aug 2015):  Here is the alternative approach answer and the solution for blog post:


I was always wondering, why Sitecore yet hasn't hosted Sitecore.Kernel and Sitecore.MVC on the NuGet?

This would have simplified our (developers') life in some way, as for today, we have to keep an additional /Libs folder and reference those 2 DLLs out of it (but couple others will serve good as well, for instance Lucene DLLs etc.). That could also simplify versioning and dependencies issues for us as soon as we still are using NuGet package management, so we now have 2 folders for references, highlighted below:


If they avoid doing that for licensing purposes - that seems quite strange as mentioned libraries are very little part of what Sitecore is, plus they may just supply obfuscated DLLs via NuGet and normal reflection-friendly via standard platform installation.

Here is what I have in my Libs folder and that I use and reference in small proof-of-concept project:


Dear Sitecore, if you read me, please consider using NuGET - today's industry standard for packaging and resolving dependencies .

Sitecore tips

1. Load Sitecore Desktop automatically and pre-select an item

I was wondering, if there is any opportunity to preload Sitecore Desktop automatically on Sitecore enter.

So annoyed of moving mouse across whole big monitor to the very top bottom, then select Content Editor from the menu, then wait till it loads an eventually as content tree loads - navigate to specific item! Even if it does not seem quite complex for a first glance - the number of times I do that per day makes me sadly thinking of wasted efforts for nothing...

Good news, everyone! There a convenient way of creating a shortcut that not only loads Content Editor, but also select any item you would like and loads its data right hand side. Here is the format of url:

http://your.host.name/sitecore/shell/Applications/Content%20Editor.aspx?fo={9CFF486B-9829-416E-8A39-408D3909B4FE}

where fo parameter stands for the item you want to pre-load.


2. If you use Google Chrome - install Chrome extensions for Sitecore


More details in my blog post by this link: Sitecore extensions for Google Chrome

Attach to IIS - debugging Sitecore with just one hotkey!

Debugging Sitecore in most cases requires you to open attach to a process menu in Visual Studio, then enable display for processes of all users, find appropriate IIS process and фееср to it. To much hassle, isn't it, especially taking into account that you may need to repeat that again and again hundreds / thousands times as a part of your work?

Ok, here's the solution to take your debugging pain off: Attach To IIS extension for Visual Studio. At the moment there are extensions for 2012 and 2013 version, as I haven't tried it in 2015 preview.





Download extensions for your Visual Studio version:

Attach_To_IIS_2012.vsix (58.6KB)

API - Useful IDisposable context switchers

Sitecore API provide several IDisposable context switchers, so I thought it's worth to review them in one place. Here they are:
  1. EditContext
  2. EventDisabler
  3. SecurityDisabler
  4. UserSwitcher
  5. LanguageSwitcher
  6. DatabaseSwitcher
  7. SiteContextSwitcher
  8. DatabaseCacheDisabler
  9. LockingDisabler
  10. LinkDisabler
1. EditContext
This is a very elegant way of editing an item in sitecore. What it does is automatically does item.Editing.BeginEdit(), item.Editing.EndEdit() as well as wraps that into try / catch block with item.Editing.CancelEdit() on Exception. You may map additional parameters into constructor to skip updating statistics (ie. last modified item's system field), suppress raising events (like item:save event) and also you may SecurityDisabler as a constructor parameter. 
// default usage
using (new EditContext(item))
{
    item.Fields["fieldname"] = "Some value to set";
}
// example of using with supressing events and avoiding statistics update
bool updateStatistics = false;  
bool silent = true;  
using (new EditContext(item, updateStatistics, silent))  
{  
    item.Fields["fieldname"] = "Some value to set";
}
// using EditContext and disable SecurityCheck together
using (new EditContext(iem, SecurityCheck.Disable))  
{  
    item.Fields["fieldname"] = "Some value to set";
}

2. EventDisabler
It gives exactly the same effect as if you were using Item.Editing.EndEdit(bool silent), passing true within silent. No events will be raised.
using (new EventDisabler())
{
    item.Editing.BeginEdit();
    // do edits
    item.Editing.EndEdit();
}

3. SecurityDisabler
Turns of permission check for operations performed within using block, regardless of if the user from the context can or cannot create / modify / move / rename /delete items. Beware as it may be insecure especially on CD instances. There is an alternative for this method - UserSwitcher (see below at 4). Here is an example of  SecurityDisabler usage:
using (new SecurityDisabler())
{
    subNode = homeItem.Add("SubNode", templateId);
    using (new EditContext(subNode))
    {
        subNode[fieldIdA] = "test";
        subNode[fieldIdB] = "testBBB";
    }
}

4. UserSwitcher
That is recommended practice (by Sitecore) to be used instead of SecurityDisabler. Everything you do is done in the context of a specific user. The user can be an Administrator and can do everything. Maybe you want to create a "service user" with only specific permissions or whatever, you can use this user to do what you want.
If you use the SecurityDisabler - Sitecore will not do any permission checks at all. In fact, the result will be the same as if you use the UserSwitcher() with an administrator, but you have no control over the context.
//User which is already created in Sitecore User Manager
 string testUser = @"sitecore\testuser"; 
if (Sitecore.Security.Accounts.User.Exists(testUser))
{
    //Getting Sitecore User Object with UserName
    Sitecore.Security.Accounts.User scUser = Sitecore.Security.Accounts.User.FromName(testUser, false); 
    //Switching Context User
     using (new Sitecore.Security.Accounts.UserSwitcher(scUser))
    {
        //Using EditContext to edit an Item
        using (new Sitecore.Data.Items.EditContext(home))
        {
            home["Text"] = "Modified Text from Code";
        }
    }
}

5. LanguageSwitcher
When working on multi-lingual website, you may have a need in retrieving the values in the language different than one you're having currently in the Context. Please consider the following code to achieve that:
var item = Sitecore.Context.Item;
using (new Sitecore.Globalization.LanguageSwitcher("fr"))
{
    item = myItem.Database.GetItem(item.ID);
}


6. DatabaseSwitcher
Sometimes you may need to do some database operations from the database
another than you currently have in Context, for instance you may have a specific task to look-up master database, or read some setting from core. Of course, such type of activities is fairly an exclusion from normal practice, so think well ahead. In any case, the following using switcher will be handy for that type of situations:
var master = Sitecore.Configuration.Factory.GetDatabase("master"))
using (new DatabaseSwitcher(master))
{
// now within this scope Sitecore.Context.Database is returning master database
}

7. SiteContextSwitcher
That one is use to substitute current site context. Usage is pretty easy:
var item = Sitecore.Context.Item;
using (new SiteContextSwitcher(SiteContextFactory.GetSiteContext("site_name_as_in_config")))
{
    // all the code here would run in the context of "site_name_as_in_config" site
}

8. DatabaseCacheDisabler
This one has become one of my favorites. When working with many items in background tasks etc., it will use the regular Items cache for fetching items, but on cache misses, it won’t put items loaded from the database into the items cache. This avoids the items cache from being polluted by for example a background task traversing multiple items. As the number of items grows in a solution, it’s important that the items being accessed by users are in the cache and aren’t outcompeted by one-time operations etc.
var item = Sitecore.Context.Item;
using(new DatabaseCacheDisabler())
{
  // your code here...
}
9. LockingDisabler
The __lock field isn’t updated also the locking events aren’t fired, so that switch effectively d
isables the item locking mechanism.

10. LinkDisabler
This is one is handy but use it with care as it disables updates of the Links Database. This may speed up item saves when doing changes to items from code that you know won’t change any links.

Hope you find this article helpful.

[Updatable] API snippets

This blog post is [Updatable] - I use it to store snippets at the same place just for a case.

I know for most of experienced Sitecore geeks my post may seem sort of obvious, however even you may find something interesting below or suggest me to add anything you consider important to append.

While working with Sitecore for years, I have met so many attempts to re-invent the wheel in sense of ugly implementation of functions that are part of Sitecore API, so I decided to write this post, placing most frequent but sometimes less known usages here within one post. So, here we go:

  1. Standard API - create, read, edit, save, rename,
  2. Media library
  3. Publishing
  4. Presentation / placeholders
  5. Indexing
  6. DMS / xDB
  7. Other


1. Standard API - create, read, edit, save, rename, move etc.


1.1. To start with the basics - how to get the item. 

Item item = Sitecore.Context.Database.GetItem("/sitecore/content/home/products/tv");

if (item != null)             
{                 
    // allways check for null before going forward
}
Avoid using explicit database by name like below, get database out of Context, rather than by name explicitly.

// incorrect, except when you need explicitly to get CM database.
var database = Database().GetDatabase("master")); 


1.2. Accessing the fields - multiple ways:

// Indexer on Item supports using a field's name, index, or ID
string value1 = someItem[fieldName];
// Get the value from the field
string value2 = someField.Value;
// Method 3: 
bool allowStandardValue = false;
string value3 = someField.GetValue(allowStandardValue);
bool allowStandardValue = false;
bool allowDefaultValue = false;
string value5 = someField.GetValue(allowStandardValue, allowDefaultValue);

The order usually follows: if actual value exists, it is returned, otherwise if the item is clone - it returns clone's value, if not - returns standard value, and in case standard value not set - the default value (empty string, otherwise - null).


1.3. Read all fields.

For the sake of performance, Sitecore will not give you all fields in the FieldCollection in the following code, only fields with explicit values on item level, including empty string.

Sitecore.Context.Item.Fields.ReadAll();


1.4. Checking if a Sitecore field has a value

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


1.5. When rendering fields from within the code, always use FieldRenderer class, in order to ensure that resulting output HTML  is Page Editor friendly.

FieldRenderer.Render(item, "field name")


1.6. Editing the item - straightforward way.

item.Editing.BeginEdit();
item.Fields["Title"].Value = "My New Title";
item.Editing.EndEdit();


1.7. More elegant way of editing the item, using IDisposable EditContext

using (new EditContext(item)) 
{ 
    item["Title"] = "My New Title"; 
}

More about EditContext and its options please read in API - IDisposable Usings article.


1.8. Miltilist field. I have evidenced several attempts of manipulation multi-list items by physically stored pipe-separated GUIDs, all clumsy and not best practice code with bad smell. Don't do that - use Sitecore API instead:

Sitecore.Data.Fields.MultilistField multilistField = Sitecore.Context.Item.Fields["myMultilistField"];
Sitecore.Data.Items.Item[] items = multilistField.GetItems();


1.10. Create item programmatically

Minimum code:
Item newItem = parent.Add(itemName, template); 
newItem.Editing.BeginEdit(); 
newItem.Fields["fieldName"].Value = "fieldValue"; 
newItem.Editing.EndEdit();
Full ready-to-use-code:
       public static void CreateItem()
        {
            // The SecurityDisabler overrides the current security model, allowing you
            // to access the item without any security. It's like the user being an administrator
            using ( new Sitecore.SecurityModel. SecurityDisabler())
            {
                // Get the master database
                Sitecore.Data. Database master = Sitecore.Data.Database.GetDatabase("master" );
                // Get the template to base the new item on
                TemplateItem template = master.GetItem("/sitecore/templates/Ecosystem/Concrete/MenuPage" );

                // Get the place in the site tree where the new item must be inserted
                Item parentItem = master.GetItem( "/sitecore/content/Ecosystem");

                // Add the item to the site tree
                Item newItem = parentItem.Add( "My New Page", template);

                // Set the new item in editing mode
                // Fields can only be updated when in editing mode
                // (It's like the begin tarnsaction on a database)
                newItem.Editing.BeginEdit();
                try
                {
                    // Assign values to the fields of the new item
                    newItem.Fields[ "Title"].Value = "My New Page - Title" ;
                    //newItem.Fields["Text"].Value = "Value2";

                    // End editing will write the new values back to the Sitecore
                    // database (It's like commit transaction of a database)
                    newItem.Editing.EndEdit();
                }
                catch (System. Exception ex)
                {
                    // The update failed, write a message to the log
                    //Sitecore.Diagnostics.Log.Error("Could not update item " + newItem.Paths.FullPath + ": " + ex.Message, this);

                    // Cancel the edit (not really needed, as Sitecore automatically aborts
                    // the transaction on exceptions, but it wont hurt your code)
                    newItem.Editing.CancelEdit();
                }
            }           
        }


1.11. Item propose name - will suggest valid item name according with current settings

ItemUtil.ProposeValidItemName("some-name");


1.12. Item has plenty of helpful methods, like CopyTo() and MoveTo() and others you may see in the context menu.

item.MoveTo (parentItem);



2. Media Library


2.1. Determining item type.

// usual content data item
Sitecore.Context.Item.Paths.IsContentItem;
// media item
Sitecore.Context.Item.Paths.IsMediaItem

2.2. The example below is anti-pattern. At the first glance it seems to be an elegant way of inline resolving media path right in MVC Razor view. So what is wrong with that?

 <img src="@MediaManager.GetMediaUrl(
Sitecore.Context.Database.GetItem(Html.Sitecore().CurrentRendering.DataSource))" />

First and most important - if datasource is not set (or set wrongly) then GetItem will return null being passed into GetMedaiUrl as the parameter, so it will fire NullReferenceException and the whole page won't load. You must validate parameters and do null-checks, so the best way out is to extract this logic into helper static (or extensions) class.

But the best option for sure is to pass this resolved URL into a view as a public property of strongly-typed viewmodel object.



3. Publishing


3.1. Publish item programmatically

private void PublishItem(Sitecore.Data.Items.Item item)
{
  // The publishOptions determine the source and target database,
  // the publish mode and language, and the publish date
  Sitecore.Publishing.PublishOptions publishOptions =
    new Sitecore.Publishing.PublishOptions(item.Database,
                                           Database.GetDatabase("web"),
                                           Sitecore.Publishing.PublishMode.SingleItem,
                                           item.Language,
                                           System.DateTime.Now);  // Create a publisher with the publishoptions
  Sitecore.Publishing.Publisher publisher = new Sitecore.Publishing.Publisher(publishOptions);

  // Choose where to publish from
  publisher.Options.RootItem = item;

  // Publish children as well?
  publisher.Options.Deep = true;

  // Do the publish!
  publisher.Publish();
}



4. Presentation / placeholders


4.1. Get count of renderings in a particular placeholder:

public static int GetRenderingsCount(string placeholderKey)
{
  return Sitecore.Context.Item.Visualization.GetRenderings(Sitecore.Context.Device, false).Count(p => p.Placeholder.Equals(placeholderKey));
}

4.2. Get position index of a rendering within a particular placeholder:

public static string GetComponentPositionOnPlaceHolder(System.Web.UI.Control webControl)
{
 if (webControl == null) return "0";
 var split = webControl.ID.Split('_');
 if (split.Length > 1)
 {
  int index = 0;
  if (int.TryParse(split.Last(), out index))
  {
   return (index + 1).ToString(CultureInfo.InvariantCulture);
  }
 }
 return "0";
}



5. Indexing


5.1. Simple Linq query

//Create a Search Context to the "index name" Index (could be SOLR/Lucene/something else)
using (var context = new ContentSearchManager.GetIndex("indexname").CreateSearchContext()) 
{
    //LINQ Query
    var query = context.GetQueryable.Where(i => i.Name.StartsWith("Something"));
}


5.2. Add single item to index

public static void AddItemToIndex(Item item, string indexName)
{
    var tempItem = (SitecoreIndexableItem)item;
    ContentSearchManager.GetIndex(indexName).Refresh(tempItem);
}
 
public static void UpdateItemInIndex(Item item, string indexName)
{
    var tempItem = (SitecoreIndexableItem)item;
    ContentSearchManager.GetIndex(indexName).Update(tempItem.UniqueId);
}
 
public static void DeleteItemInIndex(Item item, string indexName)
{
    var tempItem = (SitecoreIndexableItem)item;
    ContentSearchManager.GetIndex(indexName).Delete(tempItem.UniqueId);
 }


5.3. Queryable extensions

public static class QueryableExtensions
{
  public static IQueryable IsContentItem(this IQueryable query) where T : SearchResultItem
  {
    return query.IsDescendantOf(ItemIDs.ContentRoot);
  }
 
  public static IQueryable IsContextLanguage(this IQueryable query) where T : SearchResultItem
  {
    return query.Where(searchResultItem => searchResultItem.Language.Equals(Context.Language.Name));
  }
 
  public static IQueryable IsLatestVersion(this IQueryable query) where T : SearchResultItem
  {
    return query.Where(searchResultItem => searchResultItem["_latestversion"].Equals("1"));
  }
 
  public static IQueryable HasBaseTemplate(this IQueryable query, ID templateId) where T : SearchResultItem
  {
    var id = templateId.ToShortID().ToString().ToLowerInvariant();
    return query.Where(searchResultItem => searchResultItem["_basetemplates"].Contains(id));
  }
 
  public static IQueryable IsDescendantOf(this IQueryable query, ID parentId) where T : SearchResultItem
  {
    return query.Where(searchResultItem => searchResultItem.Paths.Any(ancestorId => ancestorId == parentId));
  }
}



6. DMS / xDB


6.1. DMS - hit the goal programmatically.

//Check that analytics are on...
if (Sitecore.Analytics.Tracker.IsActive &amp;&amp; Sitecore.Analytics.Tracker.CurrentPage != null)
{
  //Get the item goal.
  Item goalItem = Sitecore.Context.Database.GetItem(&amp;quot;{xxxxx-xxx-xxx-xxxxx}&amp;quot;);
  //Page event wrapper
  PageEventItem goal = new PageEventItem(goalItem);
  //Create the record that needs to  store the goal
   VisitorDataSet.PageEventsRow pageEventsRw = Sitecore.Analytics.Tracker.CurrentPage.Register(goal);
  //this is not mandatory
  pageEventsRw.Data = "custom text";
  Sitecore.Analytics.Tracker.Submit();
}


7. Other

7.1. Jumping between the sites of current Sitecore instance

Sitecore.Context.SetActiveSite(currentSiteName);


Case study: Collection database for Metropolitan Museum of Art (NYC)

This is my first significant Sitecore project ever, which has been completed two years ago.

The Metropolitan Museum of Art is the heavy-loaded web implementation for the iconic museum based in the largest US city – New York. It is been visited by 6.7 million people annually but the online attendance exceeds physical visitors 5 times, with 33 million interactions globally.

By 2010 the amount of the digital assets exceeded 900K and approached the value of 1M with a third of them being exposed publicly. The biggest problem however was that all the museum collections were kept in numerous incompatible source formats and origins. With dozens of collections and each of them managed by totally independent teams in a non-standardized approach, it was clearly a mess turning into a loss of maintainability.

Met chose Sitecore as the best digital experience platform suitable to centralize the authoring process for all editorial teams as well as improve online publishing and distribution in face of even growing online traffic.

I joined the project at a digital agency called Cogapp that provides services to the largest museums, galleries, and libraries as a contractor specializing in Sitecore and my particular scope was to create a centralized editable Sitecore-powered database for all the existing collections. I ended up creating the first single collection database the museum had ever for their artifacts with almost a million editable records!

Beyond the benefits mentioned above, there were lots of crucial improvements derived from my work:

  • Powerful search. Since every single collection item got standardized, it became possible to use semantic search against the whole collection. That alone came as an impressive improvement in visitors’ user experience, reducing the time for thousands of art professionals of reaching desired content.

  • Versioning and history audit. Since the database went live, it became possible to see all the changes done to every particular collection item back to the initial creation, see who made these changes, when and why, and with the ability to roll back to any previous version. Not just that, an approval workflow process was introduced to prevent faulty changes from going live before getting approved by a senior editor. Having a single “Source of Truth” for the data has fixed the “versioning hell” problem once and forever.

  • External data feeds. Powered by Sitecore, every single collection became exportable by external data feeds for all the Museum associates. They automatically get all the changes provided into a central database upon approval.

result

Instead of a one-time converting and migrating of the data, I created the Exporter Portal to ensure this process becomes reproducible and could be triggered by an individual editing team so that they do not need to roundtrips to the developers. Since all the previous content teams were mis-synchronized, it was impossible to force them all to start committing to the centralized database from day 1, not to say they had to undertake Sitecore training that was scattered over time. Exporter Portal knew all the original source formats each team used and was allowed to pull the delta into the centralized database. With the portal in place teams took could temporary work as before taking their time to familiarize themselves with a new system before they become ready to start using it exclusively – that prevented lots of spontaneous mistakes caused by lack of Sitecore training.